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) #

0.1.0

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) #

0.1.0
{
  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?) #

0.1.0

Convenience renderer that lazily binds a default engine instance to /views 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.