IMPROVE: Clean up async initialization pattern

- Remove unnecessary initializeTemplateEngine() from constructor
- Rename initializeLiquidJS() to ensureLiquidJSInitialized() for clarity
- Make template engine loading truly lazy (only on first template render)
- Eliminate potential timing issues with constructor async calls
- Improve code clarity and maintainability

Now template engines are only loaded when actually needed for rendering.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-13 18:08:10 +02:00
parent 5acf7d52f6
commit cfc3ce5a7e
2 changed files with 16 additions and 37 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@xtr-dev/payload-mailing", "name": "@xtr-dev/payload-mailing",
"version": "0.0.11", "version": "0.0.12",
"description": "Template-based email system with scheduling and job processing for PayloadCMS", "description": "Template-based email system with scheduling and job processing for PayloadCMS",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -1,9 +1,9 @@
import { Payload } from 'payload' import { Payload } from 'payload'
import { Liquid } from 'liquidjs' import { Liquid } from 'liquidjs'
import nodemailer, { Transporter } from 'nodemailer' import nodemailer, { Transporter } from 'nodemailer'
import { import {
MailingPluginConfig, MailingPluginConfig,
SendEmailOptions, SendEmailOptions,
MailingService as IMailingService, MailingService as IMailingService,
EmailTemplate, EmailTemplate,
QueuedEmail, QueuedEmail,
@@ -23,15 +23,14 @@ export class MailingService implements IMailingService {
constructor(payload: Payload, config: MailingPluginConfig) { constructor(payload: Payload, config: MailingPluginConfig) {
this.payload = payload this.payload = payload
this.config = config this.config = config
const templatesConfig = config.collections?.templates const templatesConfig = config.collections?.templates
this.templatesCollection = typeof templatesConfig === 'string' ? templatesConfig : 'email-templates' this.templatesCollection = typeof templatesConfig === 'string' ? templatesConfig : 'email-templates'
const emailsConfig = config.collections?.emails const emailsConfig = config.collections?.emails
this.emailsCollection = typeof emailsConfig === 'string' ? emailsConfig : 'emails' this.emailsCollection = typeof emailsConfig === 'string' ? emailsConfig : 'emails'
this.initializeTransporter() this.initializeTransporter()
this.initializeTemplateEngine()
} }
private initializeTransporter(): void { private initializeTransporter(): void {
@@ -63,31 +62,11 @@ export class MailingService implements IMailingService {
return fromEmail || '' return fromEmail || ''
} }
private initializeTemplateEngine(): void { private async ensureLiquidJSInitialized(): Promise<void> {
// Skip initialization if custom template renderer is provided
if (this.config.templateRenderer) {
return
}
// Use specified template engine or default to 'liquidjs'
const engine = this.config.templateEngine || 'liquidjs'
if (engine === 'liquidjs') {
// LiquidJS will be initialized lazily on first use
this.liquid = null
} else if (engine === 'mustache') {
// Mustache will be loaded dynamically on first use
this.liquid = null
} else if (engine === 'simple') {
this.liquid = null
}
}
private async initializeLiquidJS(): Promise<void> {
if (this.liquid !== null) return // Already initialized or failed if (this.liquid !== null) return // Already initialized or failed
try { try {
const liquidModule = await import('liquidjs') as any const liquidModule = await import('liquidjs')
const { Liquid: LiquidEngine } = liquidModule const { Liquid: LiquidEngine } = liquidModule
this.liquid = new LiquidEngine() this.liquid = new LiquidEngine()
@@ -135,7 +114,7 @@ export class MailingService implements IMailingService {
}) })
await this.processEmailItem(emailId) await this.processEmailItem(emailId)
return emailId return emailId
} }
@@ -147,7 +126,7 @@ export class MailingService implements IMailingService {
if (options.templateSlug) { if (options.templateSlug) {
const template = await this.getTemplateBySlug(options.templateSlug) const template = await this.getTemplateBySlug(options.templateSlug)
if (template) { if (template) {
templateId = template.id templateId = template.id
const variables = options.variables || {} const variables = options.variables || {}
@@ -195,7 +174,7 @@ export class MailingService implements IMailingService {
async processEmails(): Promise<void> { async processEmails(): Promise<void> {
const currentTime = new Date().toISOString() const currentTime = new Date().toISOString()
const { docs: pendingEmails } = await this.payload.find({ const { docs: pendingEmails } = await this.payload.find({
collection: this.emailsCollection as any, collection: this.emailsCollection as any,
where: { where: {
@@ -379,7 +358,7 @@ export class MailingService implements IMailingService {
}, },
limit: 1, limit: 1,
}) })
return docs.length > 0 ? docs[0] as EmailTemplate : null return docs.length > 0 ? docs[0] as EmailTemplate : null
} catch (error) { } catch (error) {
console.error(`Template with slug '${templateSlug}' not found:`, error) console.error(`Template with slug '${templateSlug}' not found:`, error)
@@ -403,7 +382,7 @@ export class MailingService implements IMailingService {
// Use LiquidJS if configured // Use LiquidJS if configured
if (engine === 'liquidjs') { if (engine === 'liquidjs') {
try { try {
await this.initializeLiquidJS() await this.ensureLiquidJSInitialized()
if (this.liquid && typeof this.liquid !== 'boolean') { if (this.liquid && typeof this.liquid !== 'boolean') {
return await this.liquid.parseAndRender(template, variables) return await this.liquid.parseAndRender(template, variables)
} }
@@ -430,7 +409,7 @@ export class MailingService implements IMailingService {
private async renderWithMustache(template: string, variables: Record<string, any>): Promise<string | null> { private async renderWithMustache(template: string, variables: Record<string, any>): Promise<string | null> {
try { try {
const mustacheModule = await import('mustache') as any const mustacheModule = await import('mustache')
const Mustache = mustacheModule.default || mustacheModule const Mustache = mustacheModule.default || mustacheModule
return Mustache.render(template, variables) return Mustache.render(template, variables)
} catch (error) { } catch (error) {
@@ -461,4 +440,4 @@ export class MailingService implements IMailingService {
return { html, text } return { html, text }
} }
} }