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