Template
Server-side HTML template engine. Handlebars-flavoured tag syntax ({{ expr }} HTML-escaped, {{{ expr }}} raw, {{> name }} partials, {% extends "layout" %} / {% block name %} inheritance, {% if %} / {% for %} directives) parsed into a small AST and walked at render time against an operator-supplied data scope.
No eval, no dynamic Function constructor, no vm.runInThisContext — the expression grammar is a fixed recursive-descent Pratt parser and member access is restricted to own properties (the parser refuses foo.constructor / foo.__proto__ walks out of the data scope). Custom helpers are operator-provided functions in the data scope (e.g. {{ helpers.formatDate(d) }}); when opts.sandbox === true each helper source string is wrapped through b.sandbox.run so helper code runs in a worker-thread isolate with timeout + byte cap.
precompileAll() walks viewsDir at boot, parsing every .html file so template syntax errors fail the deploy rather than the first user request. Compiled ASTs are cached unless cache: false is set on the engine — operators turn caching off for live-reload workflows.
b.template.escapeHtml(value) #
HTML-entity escapes the five attack-relevant characters (&, <, >, ", '). Non-string inputs are coerced via String(value); null and undefined become the empty string. Used internally by {{ expr }} interpolation; exported because operators occasionally reach for the same escape from non-template paths (form-error rendering, CSV-cell-as-HTML pre-escape).
b.template.escapeHtml("");
// → "<script>alert(1)</script>"
b.template.escapeHtml(null); // → ""
b.template.escapeHtml(42); // → "42"
b.template.create(opts) #
{
viewsDir: string, // optional — directory of .html templates; omit for string-only (renderString) use
cache: boolean, // default true; set false for live-reload
escapeHtml: function (value) → string, // override the default 5-character HTML escape
sandbox: boolean, // when true, sandboxHelpers run through b.sandbox.run
sandboxHelpers: Object, // map of helperName → JS source executed inside the sandbox
sandboxOpts: { timeoutMs, maxBytes, allowed },
}
Builds an engine instance. With opts.viewsDir the returned object exposes render(viewName, data?) for one-shot rendering, compile(viewName) for AST-only access (caches under viewName), precompileAll() for boot-time validation of every .html file under viewsDir, and reset() to drop the AST cache (useful in live-reload workflows).
viewsDir is optional: an engine created without it serves from a source STRING via renderString(source, data?, opts?) and compileString(source, opts?) — the read-only / serverless path with no disk read. {% extends %} and {{> partial}} in a string source resolve through opts.resolve(name) -> string (without it, an extends throws and a missing partial inlines empty). The file-backed render/compile/precompileAll refuse when no viewsDir is configured.
View names are resolved against viewsDir; names containing .. or NUL are refused, and resolved paths outside viewsDir throw. Layout-extends and partial-inclusion recursion are bounded at depth 16 to defend against accidental cycles.
var engine = b.template.create({ viewsDir: "./views" });
engine.precompileAll(); // fail boot on syntax errors
var html = engine.render("dashboard", { user: { name: "Ada" } });
// → "Hello Ada
"
b.template.render(viewName, data?) #
Convenience renderer that lazily binds a default engine instance to on first call, then dispatches to its render(). Operators with custom view directories (multi-tenant apps, non-cwd-rooted deploys) call b.template.create({ viewsDir }) instead and keep the engine in their app scope.
// Project layout: ./views/welcome.html
var html = b.template.render("welcome", { name: "Ada" });
// → "Welcome, Ada
"
Last updated 2026-08-08T16:39:49.652Z by seeder.