UNPKG

@mdfriday/foundry

Version:

The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.

361 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemplateEngine = void 0; exports.newTemplateEngine = newTemplateEngine; const type_1 = require("../type"); const info_1 = require("../vo/info"); const info_2 = require("../vo/info"); const log_1 = require("../../../../pkg/log"); const paths_1 = require("../../../domain/paths"); // Create domain-specific logger for template operations const log = (0, log_1.getDomainLogger)('template', { component: 'template-engine' }); /** * Template entity - main template engine with separated template management * TypeScript version of Go's Template struct */ class TemplateEngine { constructor(executor, lookup, parser, templateNamespace, partialNamespace, shortcodeNamespace, fs) { this.executor = executor; this.lookup = lookup; this.parser = parser; this.templateNamespace = templateNamespace; this.partialNamespace = partialNamespace; this.shortcodeNamespace = shortcodeNamespace; this.fs = fs; } /** * Mark template engine as ready */ async markReady() { try { await this.parser.markReady(); } catch (error) { throw new type_1.TemplateError(`Failed to mark template engine ready: ${error.message}`, 'TEMPLATE_ENGINE_READY_FAILED'); } } /** * Get regular template by name with baseof dependency support */ async getTemplate(name) { // First try to find template with no dependencies const [tmpl, found, err] = this.lookup.findTemplate(name, this.templateNamespace); if (err) { return [null, false, err]; } if (found && tmpl) { return [tmpl, true, null]; } // Try to find template with dependencies const [overlay, base, hasDependent] = this.lookup.findDependentInfo(name); if (hasDependent && overlay) { try { const [ts, parseFound, parseErr] = await this.parser.parseOverlap(overlay, base || overlay, this.lookup.newTemplateLookup(this.templateNamespace)); if (parseErr) { return [null, false, parseErr]; } if (parseFound && ts) { return [ts.template, true, null]; } } catch (error) { return [null, false, error]; } } return [null, false, null]; } /** * Get partial template by name (simple lookup) */ async getPartial(name) { // Ensure name includes partials prefix if not already present let partialName = name.startsWith(type_1.PARTIALS_PREFIX) ? name : `${type_1.PARTIALS_PREFIX}${name}`; partialName = name.endsWith(".html") ? partialName : `${partialName}.html`; return this.lookup.findPartial(partialName, this.partialNamespace); } /** * Get shortcode template by name */ async getShortcode(name) { return this.lookup.findShortcode(name, this.shortcodeNamespace); } /** * Get template by name (unified method that routes to appropriate namespace) */ async get(name) { const templateType = (0, info_2.resolveTemplateType)(name); switch (templateType) { case type_1.TemplateType.TypeShortcode: return this.getShortcode(name); case type_1.TemplateType.TypePartial: return this.getPartial(name); default: return this.getTemplate(name); } } /** * Find first available template from a list of template names by priority * Returns the first template found, its name, whether it was found, and any error */ async findFirst(names) { if (!names || names.length === 0) { return [null, null, false, null]; } for (const name of names) { try { const [tmpl, found, err] = await this.get(name); if (err) { // Log error but continue to try next template continue; } if (found && tmpl) { return [tmpl, name, true, null]; } } catch (error) { log.error("Error finding template:", error); } } return [null, null, false, null]; } /** * Check if a shortcode exists * @param name - The shortcode name * @returns true if the shortcode exists, false otherwise */ hasShortcode(name) { return this.shortcodeNamespace.hasShortcode(name); } /** * Get all available shortcode names * @returns Array of shortcode names */ getShortcodeNames() { return this.shortcodeNamespace.getShortcodeNames(); } /** * Get shortcode count * @returns Number of loaded shortcodes */ getShortcodeCount() { return this.getShortcodeNames().length; } /** * Load templates from file system (unified for all template types) */ async loadTemplates() { const walker = async (filePath, fi) => { if (fi.isDir()) { return; } // Convert to relative path const name = filePath.startsWith(paths_1.PATH_CONSTANTS.SYSTEM_PATH_SEPARATOR) ? filePath.substring(1) : filePath; const normalizedName = paths_1.PATH_CONSTANTS.normalizePath(name); try { await this.addTemplateFileInfo(normalizedName, fi); } catch (error) { throw new type_1.TemplateError(`Failed to add template ${normalizedName}: ${error.message}`, 'LOAD_TEMPLATE_FAILED'); } }; try { await this.fs.walk('', { walkFn: walker, }, {}); } catch (error) { // Check if it's a "not exist" error if (!error.message?.includes('ENOENT') && !error.message?.includes('no such file')) { throw error; } // If directory doesn't exist, that's okay - just continue } } /** * Add template from file info (routes to appropriate namespace) */ async addTemplateFileInfo(name, fim) { try { const tinfo = await (0, info_1.loadTemplate)(name, fim); await this.addTemplate(tinfo.name, tinfo); } catch (error) { throw new type_1.TemplateError(`Failed to load template info for ${name}: ${error.message}`, 'LOAD_TEMPLATE_INFO_FAILED'); } } /** * Add template to appropriate namespace based on type */ async addTemplate(name, tinfo) { try { // Check if it's a base template if (this.lookup.getBaseOf().isBaseTemplatePath(name)) { this.lookup.getBaseOf().addBaseOf(name, tinfo); return; } // Check if it needs a base template if (this.lookup.getBaseOf().needsBaseOf(name, tinfo.template)) { this.lookup.getBaseOf().addNeedsBaseOf(name, tinfo); return; } // Determine template type and add to appropriate namespace const templateType = (0, info_2.resolveTemplateType)(name); // Parse single template (no dependencies) const state = await this.parser.parse(tinfo); switch (templateType) { case type_1.TemplateType.TypeShortcode: this.shortcodeNamespace.addShortcodeTemplate(name, state); break; case type_1.TemplateType.TypePartial: this.partialNamespace.addPartialTemplate(name, state); break; default: this.templateNamespace.addTemplate(name, state); break; } } catch (error) { throw new type_1.TemplateError(`Failed to add template ${name}: ${error.message}`, 'ADD_TEMPLATE_FAILED'); } } /** * Execute template with data */ async execute(templateName, data) { const [tmpl, found, err] = await this.get(templateName); if (err) { throw err; } if (!found || !tmpl) { throw new type_1.TemplateError(`Template not found: ${templateName}`, 'TEMPLATE_NOT_FOUND'); } return await this.executor.execute(tmpl, data); } async executeRaw(templateName, rawContent, data) { const tmpl = await this.parser.parseWithLock(templateName, rawContent); if (!tmpl) { throw new type_1.TemplateError(`Raw Template parse error: ${templateName}`, 'TEMPLATE_PARSE_ERROR'); } return await this.executor.execute(tmpl, data); } /** * Execute shortcode template with data */ async executeShortcode(shortcodeName, data) { const tmpl = this.shortcodeNamespace.getShortcode(shortcodeName); if (!tmpl) { throw new type_1.TemplateError(`Shortcode template '${shortcodeName}' not found`, 'SHORTCODE_NOT_FOUND'); } try { return await this.executor.execute(tmpl, data); } catch (error) { throw new type_1.TemplateError(`Error executing shortcode template '${shortcodeName}': ${error.message}`, 'SHORTCODE_EXECUTION_FAILED'); } } /** * Execute template safely */ async executeSafely(templateName, data) { try { const result = await this.execute(templateName, data); return { result, error: null }; } catch (error) { if (error instanceof type_1.TemplateError) { return { result: null, error }; } return { result: null, error: new type_1.TemplateError(`Execute safely failed: ${error.message}`, 'EXECUTE_SAFELY_FAILED') }; } } /** * Execute shortcode safely */ async executeShortcodeSafely(shortcodeName, data) { try { const result = await this.executeShortcode(shortcodeName, data); return { result, error: null }; } catch (error) { if (error instanceof type_1.TemplateError) { return { result: null, error }; } return { result: null, error: new type_1.TemplateError(`Execute shortcode safely failed: ${error.message}`, 'EXECUTE_SHORTCODE_SAFELY_FAILED') }; } } /** * Check if template exists */ async hasTemplate(name) { const [, found] = await this.get(name); return found; } /** * Get all template names */ getTemplateNames() { return this.templateNamespace.getTemplateNames(); } /** * Get all partial template names */ getPartialTemplateNames() { return this.partialNamespace.getTemplateNames(); } /** * Get all template names from all namespaces */ getAllTemplateNames() { return [ ...this.templateNamespace.getTemplateNames(), ...this.partialNamespace.getTemplateNames(), ...this.shortcodeNamespace.getTemplateNames() ]; } /** * Get partial namespace (for internal use by partial function) */ getPartialNamespace() { return this.partialNamespace; } /** * Get shortcode namespace (for internal use) */ getShortcodeNamespace() { return this.shortcodeNamespace; } /** * Get template namespace (for internal use) */ getTemplateNamespace() { return this.templateNamespace; } /** * Clear all templates including shortcodes */ clear() { this.templateNamespace.clear(); this.lookup.getBaseOf().clear(); } /** * Get template by pattern */ getTemplatesByPattern(pattern) { const templates = this.templateNamespace.getTemplatesByPattern(pattern); return templates.map(t => t.info.name); } } exports.TemplateEngine = TemplateEngine; /** * Create a new TemplateEngine instance */ function newTemplateEngine(executor, lookup, parser, templateNamespace, partialNamespace, shortcodeNamespace, fs) { return new TemplateEngine(executor, lookup, parser, templateNamespace, partialNamespace, shortcodeNamespace, fs); } //# sourceMappingURL=template.js.map