`
* - Loops: `
{{item.name}}`
*
* @example
* // Basic usage
* import { compileTemplate } from './m';
*
* const { content, render } = compileTemplate(`
*
*
{{title}}
*
{{description}}
*
* `);
*
* render({ title: 'Hello', description: 'World' });
* document.body.appendChild(content);
*
* @example
* // With pipes and functions
* import { createPipeRegistry } from '../pipes';
*
* const pipeRegistry = createPipeRegistry();
* const { content, render } = compileTemplate(`
*
{{user.name | uppercase}}
*
{{formatDate(user.createdAt)}}
* `, { strict: false, pipeRegistry });
*
* render(
* { user: { name: 'john', createdAt: new Date() } },
* { formatDate: (d) => d.toLocaleDateString() }
* );
*
* @example
* // With loops and conditionals
* const { content, render } = compileTemplate(`
*
* -
* {{item.name}}: {{item.price | currency}}
*
*
* `);
*
* render({ items: [
* { name: 'Apple', price: 1.5, visible: true },
* { name: 'Hidden', price: 0, visible: false }
* ]});
*/
import { PipeRegistry } from '../pipes';
/**
* Configuration options for the template engine.
*
* @example
* const config: EngineConfig = {
* strict: true,
* onError: (msg) => console.error(msg),
* pipeRegistry: createPipeRegistry()
* };
*/
export interface EngineConfig {
/** When true, throws errors for missing paths/functions. When false, returns empty string. */
strict: boolean;
/** Optional callback invoked when errors occur, receives formatted error message. */
onError?: (msg: string) => void;
/** Custom pipe registry for transformations. Defaults to built-in pipes. */
pipeRegistry?: PipeRegistry;
}
export type Path = string;
export type TemplateValue = string | number | boolean | null | undefined;
/**
* Data context object passed to render function.
* Contains the data values that expressions resolve against.
*
* @example
* const ctx: Context = {
* user: { name: 'John', age: 30 },
* items: ['a', 'b', 'c'],
* isActive: true
* };
*
* @internal
*/
export interface Context {
[key: string]: ContextValue;
}
/**
* Functions context object passed as second argument to render.
* Contains callable functions that can be invoked from templates.
*
* @example
* const fns: FunctionsContext = {
* formatDate: (d) => d.toLocaleDateString(),
* add: (a, b) => a + b,
* greet: (name) => `Hello, ${name}!`
* };
*
* @internal
*/
export interface FunctionsContext {
[key: string]: (...args: any[]) => any;
}
export type ContextValue = TemplateValue | any[] | Context | ((...args: any[]) => any);
export type Getter = (ctx: Context, path: Path, debugInfo?: string) => TemplateValue;
export type Setter = (ctx: Context, fns?: FunctionsContext) => void;
export type Patcher = (node: Node, get: Getter, config: EngineConfig) => Setter | void;
export type ExpressionFn = (ctx: Context, fns?: FunctionsContext) => TemplateValue;
/**
* Result of compiling a template.
* Contains the DOM content and a render function for updating it with data.
*/
export interface CompiledTemplate {
/** The compiled DOM element containing the template structure. */
content: DocumentFragment | HTMLElement;
/**
* Updates the DOM with the provided data context.
* Memoized: only re-renders when context object reference changes.
* @param ctx - Data context with values for template expressions
* @param fns - Optional functions context for callable expressions
*/
render: (ctx: Context, fns?: FunctionsContext) => void;
}
/**
* Compiles an HTML template string into a reusable render function.
*
* The template supports mustache-style expressions `{{expression}}` for:
* - Path resolution: `{{user.name}}`, `{{items[0].title}}`
* - Pipes: `{{value | uppercase}}`, `{{price | currency}}`
* - Function calls: `{{formatDate(createdAt)}}`, `{{add(a, b)}}`
*
* Directive attributes for control flow:
* - `if="condition"` - Renders element only when condition is truthy
* - `unless="condition"` - Renders element only when condition is falsy
* - `loop="item in items"` - Repeats element for each array item
* - `r-
="handler(args)"` - Calls a function from the functions context
* on the named DOM event (`r-click`, `r-change`, `r-keypress`, ...);
* arguments resolve against the current data context
*
* @param templateStr - HTML template string with mustache expressions
* @param config - Optional engine configuration
* @returns Compiled template with content and render function
*
* @example
* // Simple data binding
* const { content, render } = compileTemplate('{{title}}
');
* render({ title: 'Hello World' });
* document.body.appendChild(content);
*
* @example
* // Re-rendering with new data
* const { content, render } = compileTemplate('Count: {{count}}');
* render({ count: 0 });
* render({ count: 1 }); // DOM updates automatically
*
* @example
* // With strict mode and error handling
* const { render } = compileTemplate('{{missing}}', {
* strict: true,
* onError: (msg) => console.error(msg)
* });
* render({}); // Throws error for missing path
*
* @example
* // Event handling in loops with r-
* const tpl = compileTemplate(`
*
* `);
*
* // The handler is looked up in the functions context passed to render.
* tpl.render(
* { rows: [{ id: 1, name: 'Apple' }] },
* { removeRow: (row) => console.log('remove', row.id) }
* );
* document.body.appendChild(tpl.content);
*/
export declare function compileTemplate(templateStr: string, config?: EngineConfig): CompiledTemplate;