UNPKG

ecto

Version:

Modern Template Consolidation Engine for EJS, Markdown, Pug, Nunjucks, Liquid, and Handlebars

403 lines (402 loc) 16.4 kB
import { Cacheable, CacheableMemory } from "cacheable"; import { Hookified, HookifiedOptions } from "hookified"; //#region src/engine-interface.d.ts type EngineInterface = { names: string[]; engine: any; opts?: Record<string, unknown>; rootTemplatePath?: string; render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; }; //#endregion //#region src/engine-map.d.ts declare class EngineMap { private readonly _mappings; set(name: string, extensions: string[]): void; delete(name: string): void; deleteExtension(name: string, extension: string): void; get(name: string): string[] | undefined; getName(extension: string): string | undefined; } //#endregion //#region src/base-engine.d.ts declare class BaseEngine { names: string[]; opts?: Record<string, unknown>; engine: any; rootTemplatePath?: string; private _extensions; getExtensions(): string[]; setExtensions(extensions: string[]): void; deleteExtension(name: string): void; } //#endregion //#region src/engines/ejs.d.ts declare class EJS extends BaseEngine implements EngineInterface { constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; } //#endregion //#region src/engines/handlebars.d.ts declare class Handlebars extends BaseEngine implements EngineInterface { partialsPath: string[]; constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; initPartials(): void; registerPartials(partialsPath: string): boolean; } //#endregion //#region src/engines/liquid.d.ts declare class Liquid extends BaseEngine implements EngineInterface { constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; } //#endregion //#region src/engines/markdown.d.ts declare class Markdown extends BaseEngine implements EngineInterface { constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; } //#endregion //#region src/engines/nunjucks.d.ts declare class Nunjucks extends BaseEngine implements EngineInterface { constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; } //#endregion //#region src/engines/pug.d.ts declare class Pug extends BaseEngine implements EngineInterface { constructor(options?: Record<string, unknown>); render(source: string, data?: Record<string, unknown>): Promise<string>; renderSync(source: string, data?: Record<string, unknown>): string; } //#endregion //#region src/ecto.d.ts type EctoOptions = { /** * The default engine to use. This can be 'ejs', 'markdown', 'pug', 'nunjucks', 'handlebars', 'liquid' * @default 'ejs' * @type {string} */ defaultEngine?: string; /** * The engine options to pass to each engine * @type {Record<string, Record<string, unknown>>} * @default {} * @example * { * nunjucks: { * autoescape: true * }, * markdown: { * html: true * } * } */ engineOptions?: Record<string, Record<string, unknown>>; /** * Caching for async rendered templates. If set to true, it will use the default cacheable options. * If set to Cacheable instantce, it will use the provided cacheable instance. * @type {boolean | Cacheable} * @default false */ cache?: boolean | Cacheable; /** * If set to true, it will cache the rendered templates synchronously when running renderSync. * If set to CacheableMemory instance, it will use the provided cacheable memory instance. * @type {boolean | CacheableMemory} * @default false */ cacheSync?: boolean | CacheableMemory; } & HookifiedOptions; /** * Context passed to beforeRender and beforeRenderSync hooks */ type RenderContext = { source: string; data?: Record<string, unknown>; engineName: string; rootTemplatePath?: string; filePathOutput?: string; cached: boolean; }; /** * Result passed to afterRender and afterRenderSync hooks */ type RenderResult = { result: string; context: RenderContext; }; declare enum EctoEvents { cacheHit = "cacheHit", cacheMiss = "cacheMiss", warn = "warn", error = "error", beforeRender = "beforeRender", afterRender = "afterRender", beforeRenderSync = "beforeRenderSync", afterRenderSync = "afterRenderSync" } declare class Ecto extends Hookified { private readonly _mapping; private readonly _engines; private _cache; private _cacheSync; private _defaultEngine; private readonly _ejs; private readonly _markdown; private readonly _pug; private readonly _nunjucks; private readonly _handlebars; private readonly _liquid; /** * Ecto constructor * @param {EctoOptions} [options] - The options for the ecto engine */ constructor(options?: EctoOptions); /** * Get the default engine * @returns {string} - the engine name such as 'ejs', 'markdown', 'pug', 'nunjucks', 'handlebars', 'liquid' */ get defaultEngine(): string; /** * Set the default engine * @param {string} value the engine name such as 'ejs', 'markdown', 'pug', 'nunjucks', 'handlebars', 'liquid' */ set defaultEngine(value: string); /** * Get the cacheable instance * @returns {Cacheable | undefined} - The cacheable instance or undefined if caching is disabled */ get cache(): Cacheable | undefined; /** * Set the cacheable instance * @param {Cacheable | undefined} value - The cacheable instance to set. If set to undefined, caching will be disabled. */ set cache(value: Cacheable | undefined); /** * Get the cacheable memory instance * @returns {CacheableMemory | undefined} - The cacheable memory instance or undefined if caching is disabled */ get cacheSync(): CacheableMemory | undefined; /** * Set the cacheable memory instance * @param {CacheableMemory | undefined} value - The cacheable memory instance to set. If set to undefined, caching will be disabled. */ set cacheSync(value: CacheableMemory | undefined); /** * Get the Engine Mappings. This is used to map file extensions to engines * @returns {EngineMap} */ get mappings(): EngineMap; /** * Get the EJS Engine * @returns {EJS} */ get ejs(): EJS; /** * Get the Markdown Engine * @returns {Markdown} */ get markdown(): Markdown; /** * Get the Pug Engine * @returns {Pug} */ get pug(): Pug; /** * Get the Nunjucks Engine * @returns {Nunjucks} */ get nunjucks(): Nunjucks; /** * Get the Handlebars Engine * @returns {Handlebars} */ get handlebars(): Handlebars; /** * Get the Liquid Engine * @returns {Liquid} */ get liquid(): Liquid; /** * Asynchronously render a template source with data using the specified engine * @param {string} source - The template source string to render * @param {Record<string, unknown>} [data] - Data object to pass to the template engine * @param {string} [engineName] - Name of the engine to use (e.g., 'ejs', 'pug'). Defaults to defaultEngine * @param {string} [rootTemplatePath] - Root directory path for template includes/partials resolution * @param {string} [filePathOutput] - Optional file path to write the rendered output to * @returns {Promise<string>} The rendered template output as a string * @example * const result = await ecto.render('<%= name %>', { name: 'World' }, 'ejs'); */ render(source: string, data?: Record<string, unknown>, engineName?: string, rootTemplatePath?: string, filePathOutput?: string): Promise<string>; /** * Synchronously render a template source with data using the specified engine * @param {string} source - The template source string to render * @param {Record<string, unknown>} [data] - Data object to pass to the template engine * @param {string} [engineName] - Name of the engine to use (e.g., 'ejs', 'pug'). Defaults to defaultEngine * @param {string} [rootTemplatePath] - Root directory path for template includes/partials resolution * @param {string} [filePathOutput] - Optional file path to write the rendered output to * @returns {string} The rendered template output as a string * @example * const result = ecto.renderSync('<%= name %>', { name: 'World' }, 'ejs'); */ renderSync(source: string, data?: Record<string, unknown>, engineName?: string, rootTemplatePath?: string, filePathOutput?: string): string; /** * Asynchronously render a template from a file path * @param {string} filePath - Path to the template file to render * @param {Record<string, unknown>} [data] - Data object to pass to the template engine * @param {string} [rootTemplatePath] - Root directory for template includes. Defaults to file's directory * @param {string} [filePathOutput] - Optional file path to write the rendered output to * @param {string} [engineName] - Engine to use. If not specified, determined from file extension * @returns {Promise<string>} The rendered template output as a string * @example * const result = await ecto.renderFromFile('./templates/index.ejs', { title: 'Home' }); */ renderFromFile(filePath: string, data?: Record<string, unknown>, rootTemplatePath?: string, filePathOutput?: string, engineName?: string): Promise<string>; /** * Synchronously render a template from a file path * @param {string} filePath - Path to the template file to render * @param {Record<string, unknown>} [data] - Data object to pass to the template engine * @param {string} [rootTemplatePath] - Root directory for template includes. Defaults to file's directory * @param {string} [filePathOutput] - Optional file path to write the rendered output to * @param {string} [engineName] - Engine to use. If not specified, determined from file extension * @returns {string} The rendered template output as a string * @example * const result = ecto.renderFromFileSync('./templates/index.ejs', { title: 'Home' }); */ renderFromFileSync(filePath: string, data?: Record<string, unknown>, rootTemplatePath?: string, filePathOutput?: string, engineName?: string): string; /** * Asynchronously ensure that the directory path for a file exists, creating it if necessary * @param {string} path - The full file path (directories will be extracted from this) * @returns {Promise<void>} * @example * await ecto.ensureFilePath('/path/to/file.txt'); */ ensureFilePath(path: string): Promise<void>; /** * Synchronously ensure that the directory path for a file exists, creating it if necessary * @param {string} path - The full file path (directories will be extracted from this) * @returns {void} * @example * ecto.ensureFilePathSync('/path/to/file.txt'); */ ensureFilePathSync(path: string): void; /** * Determine the appropriate template engine based on a file's extension * @param {string} filePath - The file path to analyze * @returns {string} The engine name (e.g., 'ejs', 'markdown', 'pug', 'nunjucks', 'handlebars', 'liquid') * @example * const engine = ecto.getEngineByFilePath('template.ejs'); // Returns 'ejs' */ getEngineByFilePath(filePath: string): string; /** * Asynchronously find a template file in a directory by name, regardless of extension * @param {string} path - Directory path to search in * @param {string} templateName - Template name without extension * @returns {Promise<string>} Full path to the found template file, or empty string if not found * @example * const templatePath = await ecto.findTemplateWithoutExtension('./templates', 'index'); */ findTemplateWithoutExtension(path: string, templateName: string): Promise<string>; /** * Synchronously find a template file in a directory by name, regardless of extension * @param {string} path - Directory path to search in * @param {string} templateName - Template name without extension * @returns {string} Full path to the found template file, or empty string if not found * @example * const templatePath = ecto.findTemplateWithoutExtensionSync('./templates', 'index'); */ findTemplateWithoutExtensionSync(path: string, templateName: string): string; /** * Check if the given engine name is valid and registered in Ecto * @param {string} [engineName] - The engine name to validate * @returns {boolean} True if the engine is valid and registered, false otherwise * @example * const isValid = ecto.isValidEngine('ejs'); // Returns true */ isValidEngine(engineName?: string): boolean; /** * Detect the template engine from a template string by analyzing its syntax * @param {string} source - The template source string to analyze * @returns {string} The detected engine name ('ejs', 'markdown', 'pug', 'nunjucks', 'handlebars', 'liquid') or the default engine * @example * const engine = ecto.detectEngine('<%= name %>'); // Returns 'ejs' * const engine2 = ecto.detectEngine('{{name}}'); // Returns 'handlebars' or 'liquid' * const engine3 = ecto.detectEngine('# Heading'); // Returns 'markdown' * const engine4 = ecto.detectEngine('plain text'); // Returns defaultEngine (e.g., 'ejs') */ detectEngine(source: string): string; /** * Register all engine mappings between engine names and file extensions * @returns {void} * @private */ registerEngineMappings(): void; /** * Get the render engine instance by name * @param {string} engineName - The name of the engine to retrieve * @returns {EngineInterface} The engine instance (defaults to EJS if not found) * @example * const engine = ecto.getRenderEngine('pug'); */ getRenderEngine(engineName: string): EngineInterface; /** * Check if the source content contains front matter (YAML metadata) * @param {string} source - The source content to check * @returns {boolean} True if front matter is present, false otherwise * @example * const hasFM = ecto.hasFrontMatter('---\ntitle: Test\n---\nContent'); */ hasFrontMatter(source: string): boolean; /** * Extract front matter data from the source content * @param {string} source - The source content containing front matter * @returns {Record<string, unknown>} Parsed front matter as an object * @example * const data = ecto.getFrontMatter('---\ntitle: Test\n---\nContent'); */ getFrontMatter(source: string): Record<string, unknown>; /** * Set or replace front matter in the source content * @param {string} source - The source content * @param {Record<string, unknown>} data - The front matter data to set * @returns {string} The source content with updated front matter * @example * const updated = ecto.setFrontMatter('Content', { title: 'New Title' }); */ setFrontMatter(source: string, data: Record<string, unknown>): string; /** * Remove front matter from the source content, returning only the body * @param {string} source - The source content with front matter * @returns {string} The source content without front matter * @example * const body = ecto.removeFrontMatter('---\ntitle: Test\n---\nContent'); */ removeFrontMatter(source: string): string; /** * Write content to a file asynchronously, creating directories if needed * @private * @param {string} [filePath] - The path to write the file to * @param {string} [source] - The content to write to the file * @returns {Promise<void>} */ private writeFile; /** * Write content to a file synchronously, creating directories if needed * @private * @param {string} [filePath] - The path to write the file to * @param {string} [source] - The content to write to the file * @returns {void} */ private writeFileSync; } //#endregion export { type BaseEngine, EJS, Ecto, EctoEvents, EctoOptions, type EngineInterface, Handlebars, Liquid, Markdown, Nunjucks, Pug, RenderContext, RenderResult };