UNPKG

@ackplus/nest-dynamic-templates

Version:

A powerful and flexible dynamic template rendering library for NestJS applications with support for Nunjucks, Handlebars, EJS, Pug, MJML, Markdown, and more.

242 lines 10.7 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemplateLayoutService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const template_layout_entity_1 = require("../entities/template-layout.entity"); const template_engine_registry_1 = require("./template-engine.registry"); const template_errors_1 = require("../errors/template.errors"); const diagnose_1 = require("../errors/diagnose"); let TemplateLayoutService = class TemplateLayoutService { layoutRepository; engineRegistry; constructor(layoutRepository, engineRegistry) { this.layoutRepository = layoutRepository; this.engineRegistry = engineRegistry; } async render(renderDto) { const { name, scopeId, locale, context } = renderDto; const scope = renderDto.scope || 'system'; const layout = await this.findTemplateLayout(name, scope, scopeId, locale); if (!layout) { throw new template_errors_1.TemplateNotFoundError({ templateName: name, scope, scopeId, locale, kind: 'layout' }); } if (!layout.content) { throw new template_errors_1.TemplateInputError(`Layout "${name}" has no content to render.`); } const ctx = context || {}; const meta = (stage) => ({ stage, engine: layout.engine, language: layout.language, templateName: name, scope, scopeId, locale, }); let content = layout.content; if (layout.engine) { try { content = await this.engineRegistry.getTemplateEngine(layout.engine).render(content, ctx); } catch (error) { throw this.asRenderError(error, layout.content, ctx, meta('template-engine')); } } if (layout.language) { try { content = await this.engineRegistry.getLanguageEngine(layout.language).render(content, ctx); } catch (error) { throw this.asRenderError(error, content, ctx, meta('language-engine')); } } return { content }; } async renderContent(input) { const { content, language, engine, context } = input; const ctx = context || {}; if (!content) { throw new template_errors_1.TemplateInputError('`content` is required for layout rendering.'); } let rendered = content; if (engine) { try { rendered = await this.engineRegistry.getTemplateEngine(engine).render(content, ctx); } catch (error) { throw this.asRenderError(error, content, ctx, { stage: 'template-engine', engine, language }); } } if (language) { try { rendered = await this.engineRegistry.getLanguageEngine(language).render(rendered, ctx); } catch (error) { throw this.asRenderError(error, rendered, ctx, { stage: 'language-engine', engine, language }); } } return rendered; } asRenderError(error, source, context, meta) { if ((0, template_errors_1.isTemplateError)(error)) return error; return (0, diagnose_1.diagnoseRenderError)(error, source, context, meta); } async getTemplateLayouts(filter = {}) { const { scope, scopeId, type, locale, excludeNames = [] } = filter; const where = {}; if (type) where.type = type; if (locale) where.locale = locale; if (excludeNames.length > 0) where.name = (0, typeorm_2.Not)((0, typeorm_2.In)(excludeNames)); const systemTemplates = await this.layoutRepository.find({ where: { ...where, scope: 'system', scopeId: (0, typeorm_2.IsNull)() }, }); if (scope === 'system') { return systemTemplates; } const templates = await this.layoutRepository.find({ where: { ...where, scope: (0, typeorm_2.Equal)(scope), scopeId: scopeId }, order: { createdAt: 'DESC' }, }); const templateMap = new Map(); for (const template of systemTemplates) { templateMap.set(`${template.type}/${template.name}/${template.locale}`, template); } for (const template of templates) { templateMap.set(`${template.type}/${template.name}/${template.locale}`, template); } return Array.from(templateMap.values()); } async getTemplateLayoutById(id) { return this.layoutRepository.findOne({ where: { id } }); } async findTemplateLayout(name, scope, scopeId, locale) { const locales = (locale ? [locale, 'en'] : ['en']).filter(Boolean); for (const currentLocale of locales) { const template = await this.layoutRepository.findOne({ where: { name, scope, scopeId: scope === 'system' ? (0, typeorm_2.IsNull)() : (0, typeorm_2.Equal)(scopeId), locale: currentLocale, isActive: true, }, }); if (template) return template; } if (scope !== 'system') { for (const currentLocale of locales) { const template = await this.layoutRepository.findOne({ where: { name, scope: 'system', scopeId: (0, typeorm_2.IsNull)(), locale: currentLocale, isActive: true, }, }); if (template) return template; } } return null; } async createTemplateLayout(data) { if (data.scope !== 'system') { throw new template_errors_1.TemplateForbiddenError('Only system layouts can be created directly.'); } const existingTemplate = await this.layoutRepository.findOne({ where: { name: data.name, scope: 'system', scopeId: (0, typeorm_2.IsNull)(), locale: data.locale }, }); if (existingTemplate) { throw new template_errors_1.TemplateConflictError(`A system layout "${data.name}" already exists for locale "${data.locale ?? 'en'}".`); } const template = this.layoutRepository.create({ ...data, scopeId: undefined }); return this.layoutRepository.save(template); } async overwriteSystemTemplateLayout(templateId, updates) { let template = await this.layoutRepository.findOne({ where: { id: templateId } }); if (!template) { throw new template_errors_1.TemplateNotFoundError({ templateName: templateId, kind: 'layout' }); } if (template.scope === 'system') { if (!updates.scope) { throw new template_errors_1.TemplateInputError('`scope` is required when overwriting a system layout.'); } const existingTemplate = await this.layoutRepository.findOne({ where: { name: template.name, locale: template.locale, scope: updates.scope, scopeId: updates.scopeId, }, }); if (existingTemplate) { template = existingTemplate; } else { template = this.layoutRepository.create({ ...template, id: undefined, createdAt: undefined, updatedAt: undefined, scope: updates.scope, scopeId: updates.scopeId, }); } } template = this.layoutRepository.merge(template, updates); await this.layoutRepository.save(template); return template; } async updateTemplateLayout(id, updates, canUpdateSystemTemplate = false) { let template = await this.layoutRepository.findOne({ where: { id } }); if (!template) { throw new template_errors_1.TemplateNotFoundError({ templateName: id, kind: 'layout' }); } if (template.scope === 'system' && !canUpdateSystemTemplate) { if (updates.scope) { return this.overwriteSystemTemplateLayout(id, updates); } throw new template_errors_1.TemplateForbiddenError('Cannot update a system layout directly.'); } template = this.layoutRepository.merge(template, updates); return this.layoutRepository.save(template); } async deleteTemplateLayout(id, canDeleteSystemTemplate = false) { const template = await this.layoutRepository.findOne({ where: { id } }); if (!template) { throw new template_errors_1.TemplateNotFoundError({ templateName: id, kind: 'layout' }); } if (template.scope === 'system' && !canDeleteSystemTemplate) { throw new template_errors_1.TemplateForbiddenError('Cannot delete a system layout.'); } await this.layoutRepository.remove(template); } }; exports.TemplateLayoutService = TemplateLayoutService; exports.TemplateLayoutService = TemplateLayoutService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(template_layout_entity_1.NestDynamicTemplateLayout)), __metadata("design:paramtypes", [typeorm_2.Repository, template_engine_registry_1.TemplateEngineRegistryService]) ], TemplateLayoutService); //# sourceMappingURL=template-layout.service.js.map