diff --git a/package.json b/package.json index c1b400a..87072ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-mailing", - "version": "0.0.9", + "version": "0.0.10", "description": "Template-based email system with scheduling and job processing for PayloadCMS", "type": "module", "main": "dist/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51f267c..5db65df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ importers: .: dependencies: - liquidjs: - specifier: ^10.19.0 - version: 10.21.1 nodemailer: specifier: ^6.9.8 version: 6.10.1 @@ -117,6 +114,13 @@ importers: vitest: specifier: ^3.1.2 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.1)(sass@1.77.4)(tsx@4.20.3) + optionalDependencies: + liquidjs: + specifier: ^10.19.0 + version: 10.21.1 + mustache: + specifier: ^4.2.0 + version: 4.2.0 packages: @@ -4190,6 +4194,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -8533,7 +8541,8 @@ snapshots: colorette@2.0.20: {} - commander@10.0.1: {} + commander@10.0.1: + optional: true commander@2.20.3: {} @@ -10033,6 +10042,7 @@ snapshots: liquidjs@10.21.1: dependencies: commander: 10.0.1 + optional: true locate-path@5.0.0: dependencies: @@ -10421,6 +10431,9 @@ snapshots: ms@2.1.3: {} + mustache@4.2.0: + optional: true + nanoid@3.3.11: {} napi-postinstall@0.3.3: {} diff --git a/src/services/MailingService.ts b/src/services/MailingService.ts index 83df0d1..1765770 100644 --- a/src/services/MailingService.ts +++ b/src/services/MailingService.ts @@ -73,18 +73,22 @@ export class MailingService implements IMailingService { const engine = this.config.templateEngine || 'liquidjs' if (engine === 'liquidjs') { - this.initializeLiquidJS() + // LiquidJS will be initialized lazily on first use + this.liquid = null } else if (engine === 'mustache') { - // Mustache doesn't need initialization, we'll use it directly in renderTemplate + // Mustache will be loaded dynamically on first use this.liquid = null } else if (engine === 'simple') { this.liquid = null } } - private initializeLiquidJS(): void { + private async initializeLiquidJS(): Promise { + if (this.liquid) return // Already initialized + try { - const { Liquid: LiquidEngine } = require('liquidjs') + const liquidModule = await Function('return import("liquidjs")')() as any + const { Liquid: LiquidEngine } = liquidModule this.liquid = new LiquidEngine() // Register custom filters (equivalent to Handlebars helpers) @@ -396,24 +400,27 @@ export class MailingService implements IMailingService { const engine = this.config.templateEngine || 'liquidjs' - // Use LiquidJS if available and configured - if (engine === 'liquidjs' && this.liquid) { + // Use LiquidJS if configured + if (engine === 'liquidjs') { try { - return await this.liquid.parseAndRender(template, variables) + await this.initializeLiquidJS() + if (this.liquid) { + return await this.liquid.parseAndRender(template, variables) + } } catch (error) { console.error('LiquidJS template rendering error:', error) - return template } } // Use Mustache if configured if (engine === 'mustache') { try { - const Mustache = require('mustache') - return Mustache.render(template, variables) + const mustacheResult = await this.renderWithMustache(template, variables) + if (mustacheResult !== null) { + return mustacheResult + } } catch (error) { console.warn('Mustache not available. Falling back to simple variable replacement. Install mustache package.') - return this.simpleVariableReplacement(template, variables) } } @@ -421,6 +428,17 @@ export class MailingService implements IMailingService { return this.simpleVariableReplacement(template, variables) } + private async renderWithMustache(template: string, variables: Record): Promise { + try { + // Dynamic import with proper typing + const mustacheModule = await Function('return import("mustache")')() as any + const Mustache = mustacheModule.default || mustacheModule + return Mustache.render(template, variables) + } catch (error) { + return null + } + } + private simpleVariableReplacement(template: string, variables: Record): string { return template.replace(/\{\{(\w+)\}\}/g, (match, key) => { const value = variables[key]