import path from 'path';
import fs from 'fs/promises'; // Para leer archivos de forma asíncrona
import mustache from 'mustache'; // Importa el motor de plantillas Mustache
/**
* Define la estructura del contenido renderizado que devolverá `RenderManager.render`.
*/
export interface RenderedContent {
/**
* El contenido HTML renderizado de la plantilla.
* @type {string}
*/
html: string;
/**
* El contenido opcional de texto plano de la plantilla, si se genera.
* @type {string}
* @optional
*/
text?: string;
}
/**
* Gestiona la renderización de plantillas de correo electrónico utilizando el motor Mustache.
* Esta clase es una utilidad estática para procesar archivos de plantilla y convertirlos en HTML.
*/
export class RenderManager {
/**
* Renderiza una plantilla Mustache con los datos proporcionados.
* Si se provee una expresión regular, intentará extraer contenido específico de la plantilla
* antes de la renderización (ej., el cuerpo de un HTML).
*
* @template T - El tipo de los datos que se inyectarán en la plantilla.
* @param {string} templatesBasePath - La ruta base donde se encuentran todas las plantillas (ej., `./src/templates`).
* @param {string} templateName - El nombre del archivo de la plantilla (sin extensión, ej., 'verificacion').
* @param {T} data - Los datos que se usarán para rellenar los marcadores de posición en la plantilla.
* @param {RegExp} [regex] - Una expresión regular opcional (ej., `/
]*>([\s\S]*?)<\/body>/`)
* para pre-procesar el contenido de la plantilla antes de la renderización con Mustache.
* @returns {Promise} Un objeto que contiene el contenido HTML renderizado.
* @throws {Error} Si la plantilla no se encuentra, hay un error al leerla o al renderizarla.
*/
public static async render(
templatesBasePath: string,
templateName: string,
data: T,
regex?: RegExp
): Promise {
// Construye la ruta completa al archivo de plantilla, asumiendo una extensión .mustache.
const templateFilePath = path.join(templatesBasePath, `${templateName}.mustache`);
let templateContent: string;
try {
// Lee el contenido de la plantilla de forma asíncrona.
templateContent = await fs.readFile(templateFilePath, 'utf-8');
} catch (error: any) {
if (error.code === 'ENOENT') {
throw new Error(`Plantilla no encontrada: "${templateName}" en "${templateFilePath}".`);
}
throw new Error(`Error al leer la plantilla "${templateName}": ${error.message}`);
}
try {
// Si se proporciona una expresión regular, intenta extraer una parte del contenido.
let matchs: RegExpMatchArray | null = null;
if (regex && (matchs = templateContent.match(regex)) && matchs.length > 0) {
const [_, content] = matchs;
templateContent = content.trim(); // Usa la primera captura y elimina espacios en blanco.
}
// Renderiza la plantilla con los datos usando Mustache.
const renderedHtml = mustache.render(templateContent, data);
// TODO: Añadir lógica para generar una versión de texto plano si es necesario,
// quizás leyendo un archivo .txt correspondiente o usando una librería como 'html-to-text'.
return { html: renderedHtml };
} catch (error: any) {
throw new Error(`Error al renderizar la plantilla "${templateName}": ${error.message}`);
}
}
}