import { Injectable, OnModuleInit } from '@nestjs/common'; import * as fs from 'fs'; import * as path from 'path'; import Handlebars from 'handlebars'; import { ContractViewModel } from './contract-view-model.builder'; @Injectable() export class ContractRendererService implements OnModuleInit { private readonly templatesDir = path.join(__dirname, 'templates'); private readonly compiled = new Map(); onModuleInit(): void { Handlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b); const partialsDir = path.join(this.templatesDir, '_partials'); if (fs.existsSync(partialsDir)) { for (const file of fs.readdirSync(partialsDir)) { if (!file.endsWith('.hbs')) continue; const name = file.replace(/\.hbs$/, ''); const content = fs.readFileSync(path.join(partialsDir, file), 'utf-8'); Handlebars.registerPartial(name, content); } } } render(view: ContractViewModel): string { const fileName = view.template.templateFile ?? 'generic.hbs'; const template = this.getCompiled(fileName); return template({ ...view, paymentArticle: view.pricing.currency === 'ETB' ? 'ETB' : 'USD', }); } private getCompiled(fileName: string): Handlebars.TemplateDelegate { const cached = this.compiled.get(fileName); if (cached) return cached; const filePath = path.join(this.templatesDir, fileName); const fallbackPath = path.join(this.templatesDir, 'generic.hbs'); const source = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : fs.readFileSync(fallbackPath, 'utf-8'); const compiled = Handlebars.compile(source); this.compiled.set(fileName, compiled); return compiled; } }