/** * ES-module emitter for the optional view compiler. * * Turns a {@link CompiledView} into an importable side-effect module that * registers the precompiled expressions at startup. The emitted code contains * no `with` and no `new Function()`, so importing it is safe under a strict CSP. * * @module bquery/view/compiler */ import { compileViews } from './compile'; import type { CompiledView, CompileOptions, CompileStats } from './types'; /** Default module specifier the emitted code imports the runtime hook from. */ export const DEFAULT_IMPORT_SPECIFIER = '@bquery/bquery/view'; /** Options for {@link emitModule} / {@link compileToModule}. */ export type EmitOptions = CompileOptions & { /** * Module specifier to import `registerCompiledExpressions` from. Defaults to * `'@bquery/bquery/view'`; override it for monorepos or custom aliases. */ importSpecifier?: string; }; /** * Emits an importable ES module from an already-compiled view. */ export const emitModule = ( compiled: CompiledView, importSpecifier: string = DEFAULT_IMPORT_SPECIFIER ): string => { const entries = Object.keys(compiled.expressions); const lines = entries.map( (expression) => ` ${JSON.stringify(expression)}: ${compiled.expressions[expression]},` ); const header = '// Auto-generated by @bquery/bquery/view/compiler. Do not edit by hand.\n' + `// Compiled ${compiled.stats.compiled}/${compiled.stats.total} expression(s); ` + `${compiled.stats.skipped.length} fall back to the runtime evaluator.\n`; if (entries.length === 0) { // Nothing compiled — emit a valid no-op module so the build step never // produces a broken import. return `${header}export {};\n`; } return ( `${header}import { registerCompiledExpressions } from ${JSON.stringify(importSpecifier)};\n\n` + `registerCompiledExpressions({\n${lines.join('\n')}\n});\n` ); }; /** * Compiles a template and emits an importable ES module in one step. * * @returns The module source and the compile statistics (so a build tool can * report or assert on coverage). * * @example * ```ts * import { compileToModule } from '@bquery/bquery/view/compiler'; * * const { code, stats } = compileToModule('

'); * // write `code` next to your template and import it before mounting * ``` */ export const compileToModule = ( template: string, options: EmitOptions = {} ): { code: string; stats: CompileStats } => { const compiled = compileViews(template, options); return { code: emitModule(compiled, options.importSpecifier), stats: compiled.stats, }; };