UNPKG

nestjs-mailable

Version:

A comprehensive NestJS mail package with design patterns for email handling, templating, and multi-provider support

112 lines (96 loc) 3.86 kB
import { Injectable } from '@nestjs/common'; import { BaseTemplateEngine, ensurePackageAvailable } from './base.engine'; import { TemplateConfiguration } from '../interfaces/mail.interface'; // eslint-disable-next-line @typescript-eslint/no-explicit-any let HandlebarsLib: any; try { HandlebarsLib = ensurePackageAvailable('handlebars'); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_error) { // Handlebars not available - error will be thrown in constructor } @Injectable() export class HandlebarsTemplateEngine extends BaseTemplateEngine { // eslint-disable-next-line @typescript-eslint/no-explicit-any private compiledTemplates = new Map<string, any>(); private isConfigured = false; // eslint-disable-next-line @typescript-eslint/no-explicit-any private handlebars: any; constructor(templateDir: string, mainFile: string, config?: TemplateConfiguration) { super(templateDir, mainFile, 'hbs'); if (!HandlebarsLib) { throw new Error('Handlebars template engine is not available. Please install it with: npm install handlebars'); } // Create a new Handlebars instance for this template engine this.handlebars = HandlebarsLib.create(); if (config) { this.configureEngine(config); } } private configureEngine(config: TemplateConfiguration): void { if (this.isConfigured) return; // Register helpers if (config.options?.helpers) { Object.entries(config.options.helpers).forEach(([name, helper]) => { this.registerHelper(name, helper); }); } // Register partials if (config.partials) { Object.entries(config.partials).forEach(([name, partialPath]) => { this.registerPartialFromFile(name, partialPath); }); } this.isConfigured = true; } async render(template: string, context: Record<string, unknown>): Promise<string> { try { let compiledTemplate = this.compiledTemplates.get(template); if (!compiledTemplate) { const templateSource = await this.loadTemplate(template); compiledTemplate = this.handlebars.compile(templateSource); this.compiledTemplates.set(template, compiledTemplate); } return compiledTemplate(context); } catch (error) { throw new Error(`Failed to render Handlebars template '${template}': ${(error as Error).message}`); } } async compile(source: string): Promise<(context: Record<string, unknown>) => string> { try { const compiledTemplate = this.handlebars.compile(source); return (context: Record<string, unknown>) => compiledTemplate(context); } catch (error) { throw new Error(`Failed to compile Handlebars template: ${(error as Error).message}`); } } registerHelper(name: string, helper: (...args: unknown[]) => unknown): void { if (this.handlebars) { // Wrap helper with error handling const safeHelper = (...args: unknown[]): unknown => { try { return helper(...args); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_error) { // Helper error - silently return fallback value return args[0] || ''; // Return the first argument or empty string as fallback } }; this.handlebars.registerHelper(name, safeHelper); } } registerPartial(name: string, partial: string): void { if (this.handlebars) { this.handlebars.registerPartial(name, partial); } } async registerPartialFromFile(name: string, partialPath: string): Promise<void> { try { const partialContent = await this.loadTemplate(partialPath); this.registerPartial(name, partialContent); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_error) { // Failed to load partial - silently continue } } }