TemplatePreprocessor
Preprocesses Crinja templates at load time to cut per-page render cost:
- Includes are inlined into the template source (recursively, with cycle detection): every {% include %} costs a context creation and template resolution on every render.
- Subtrees that only reference build-constant site variables are evaluated once against the constants and replaced by fixed text ("folding"). Constants are per language (site title, nav items, ...), so folded templates are cached per {env, template, lang} plus a hash of the inlined source, so a template edit (e.g. in auto mode) re-folds instead of serving a stale cached template.
Folding is deliberately conservative: only if-tags, for-tags whose collection and body are constant, print statements and fixed text fold; expressions are limited to literals, constant lookups, member and index access, operators and a whitelist of pure filters; and any evaluation error leaves the subtree dynamic. A Template is bound to the env that parsed it, so folded templates are cached per environment as well (the env pool is bounded by cpu_count).
Constants
Tags whose rendering has no side effects on the context.
Filters that are safe to evaluate at load time. Anything not listed (in particular functions like shell) keeps its subtree dynamic.
Keywords (and words like filter names) that may legally appear in a foldable tag's arguments without naming a runtime variable
Class methods
The folded template for name in lang, loading the source through the loader of env. The returned Template belongs to env: render it via render_with, not Template#render.
Render a (possibly folded) template with bindings in scope.
The scope is pushed onto the template's OWN environment, never a caller-supplied one: Crinja's Template#render(io, env) ignores its env argument and evaluates against template.env (the env that parsed it). Templates are cached per environment, so normally the fiber's pooled env and the template's env are the same object; but when the pool is exhausted every Templates.environment call hands out a fresh transient env, and pushing the bindings onto one env while rendering against another made every variable undefined. Pages then silently rendered without their title, description or canonical link (a few percent of pages on every parallel build of a site whose declaration phase also renders, see EnvCache).