This commit is contained in:
David Arranz 2026-02-13 17:11:46 +01:00
parent aaf7d22374
commit 8d6b53e431
7 changed files with 164 additions and 5 deletions

View File

@ -31,8 +31,8 @@ export class FastReportTemplateResolver {
return this.resolveJoin([module, "templates", companySlug, languageCode]);
}
// <root>/templates/<companySlug>/<module>/<languageCode>
return this.resolveJoin([companySlug, module, languageCode]);
// <root>/templates/<module>/<languageCode>
return this.resolveJoin([module, languageCode]);
}
/** Resuelve una ruta de recurso relativa al directorio de plantilla */

View File

@ -0,0 +1,97 @@
import { existsSync, readFileSync } from "node:fs";
import Handlebars from "handlebars";
import { lookup } from "mime-types";
import { RendererTemplateResolver } from "./renderer-template-resolver-SOBRA";
export class HandlebarsTemplateResolver extends RendererTemplateResolver {
protected readonly hbs = Handlebars.create();
protected registered = false;
protected readonly assetCache = new Map<string, string>();
/**
* Registra el helper "asset".
*
* - Si el fichero termina en .b64 se asume que el contenido ya es base64
* - Si no se lee binario y se convierte a base64
*/
protected registerAssetHelper(templateDir: string) {
// Si ya está registrado, no hacer nada
if (this.registered) return;
this.hbs.registerHelper("asset", (resource: string) => {
const assetPath = this.resolveAssetPath(templateDir, resource);
const cacheKey = `${assetPath}`;
// 1) Caché en memoria
const cached = this.assetCache.get(cacheKey);
if (cached) {
return cached;
}
if (!existsSync(assetPath)) {
throw new Error(`Asset not found: ${assetPath}`);
}
// 3) Modo "base64"
const isPreencoded = assetPath.endsWith(".b64");
let base64: string;
let mimeType: string;
let value: string;
if (isPreencoded) {
// Fichero ya contiene el base64 en texto plano
base64 = readFileSync(assetPath, "utf8").trim();
// Para el MIME usamos el nombre "original" sin .b64
const mimeLookupPath = assetPath.replace(/\.b64$/, "");
mimeType = (lookup(mimeLookupPath) || "application/octet-stream") as string;
value = `data:${mimeType};base64,${base64}`;
} else {
// Fichero normal
// Si es un CSS no se convierte y se incrusta
const isCSS = assetPath.endsWith(".css");
if (isCSS) {
const buffer = readFileSync(assetPath);
value = buffer.toString();
} else {
// En otro caso, se transforma a Base64
const buffer = readFileSync(assetPath);
mimeType = (lookup(assetPath) || "application/octet-stream") as string;
base64 = buffer.toString("base64");
value = `data:${mimeType};base64,${base64}`;
}
}
this.assetCache.set(cacheKey, value);
return value;
});
this.registered = true;
}
/** Compilación directa desde string (sin resolución de rutas) */
public compile(templateSource: string) {
return this.hbs.compile(templateSource);
}
/** Localiza → lee → registra helpers → compila */
public compileTemplate(
module: string,
companySlug: string,
templateName: string
): Handlebars.TemplateDelegate {
// 1) Directorio de plantillas
const templateDir = this.resolveTemplateDirectory(module, companySlug);
const templatePath = this.resolveTemplatePath(module, companySlug, templateName); // 2) Path completo del template
const source = this.readTemplateFile(templatePath); // Contenido
this.registerAssetHelper(templateDir);
// 5) Compilar
return this.compile(source);
}
}

View File

@ -0,0 +1,61 @@
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import type { IRendererTemplateResolver } from "../../../application";
import { FastReportTemplateNotFoundError } from "./fastreport";
/**
* Resuelve rutas de plantillas para desarrollo y producción.
*/
export abstract class RendererTemplateResolver implements IRendererTemplateResolver {
constructor(protected readonly rootPath: string) {}
/** Une partes de ruta relativas al rootPath */
protected resolveJoin(parts: string[]): string {
return join(this.rootPath, ...parts);
}
/**
* Devuelve el directorio donde residen las plantillas de un módulo/empresa
* según el entorno (dev/prod).
*/
protected resolveTemplateDirectory(module: string, companySlug: string): string {
const isDev = process.env.NODE_ENV === "development";
if (isDev) {
// <root>/<module>/templates/<companySlug>/
return this.resolveJoin([module, "templates", companySlug]);
}
// <root>/templates/<module>/<companySlug>/
//return this.resolveJoin(["templates", module, companySlug]);
return this.resolveJoin([module]);
}
/** Resuelve una ruta de recurso relativa al directorio de plantilla */
protected resolveAssetPath(templateDir: string, relative: string): string {
return join(templateDir, relative);
}
/**
* Devuelve la ruta absoluta del fichero de plantilla.
*/
public resolveTemplatePath(module: string, companySlug: string, templateName: string): string {
const dir = this.resolveTemplateDirectory(module, companySlug);
const filePath = this.resolveAssetPath(dir, templateName);
if (!existsSync(filePath)) {
throw new FastReportTemplateNotFoundError(
`Template not found: module=${module} company=${companySlug} name=${templateName}`
);
}
return filePath;
}
/** Lee el contenido de un fichero plantilla */
protected readTemplateFile(templatePath: string): string {
return readFileSync(templatePath, "utf8");
}
}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_VERSION="1.2.8"
SCRIPT_VERSION="1.2.9"
# =====================================================
# FACTUGES Build Script
@ -194,6 +194,7 @@ if [[ "$MODE" == "api" || "$MODE" == "all" ]]; then
# Recopilar plantillas
${SCRIPT_DIR}/build-templates.sh ${COMPANY}
TEMPLATES_DIR="${PROJECT_DIR}/out/${COMPANY}/templates"
echo "📑 Plantillas ${TEMPLATES_DIR}"
cd "${PROJECT_DIR}"
echo "🐳 Construyendo imagen Docker..."

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_VERSION="0.0.2"
SCRIPT_VERSION="0.0.3"
# =====================================================
# TEMPLATES Build Script
@ -54,7 +54,7 @@ rm -rf "${TARGET_DIR:?}/"*
for module in "$SOURCE_DIR"/*; do
if [ -d "$module/templates/$COMPANY" ]; then
module_name=$(basename "$module")
echo "→ Copying templates for module: $module_name"
echo "📑 → Copying templates for module: $module_name"
mkdir -p "$TARGET_DIR/$module_name"
cp -Rv "$module/templates/$COMPANY/." "$TARGET_DIR/$module_name/"