class

Hwaro::Core::Build::TemplateDeps

Inherits Reference < Object

Constants

FROM_ARG_RE = /\A\s*(?:"([^"]+)"|'([^']+)')\s+import\s+.+\z/m
IMPORT_ARG_RE = /\A\s*(?:"([^"]+)"|'([^']+)')\s+as\s+\w+\s*(?:with(?:out)?\s+context\s*)?\z/
INCLUDE_ARG_RE = /\A\s*(?:"([^"]+)"|'([^']+)')\s*(?:ignore\s+missing\s*)?(?:with(?:out)?\s+context\s*)?\z/

Per-tag argument shapes where the template name is a plain string literal. The literal must account for the ENTIRE argument (modulo the tag's own keywords) — a leading literal in an expression like {% include "partials/" ~ name %} must NOT count as static.

REFERENCE_TAG_RE = /\{%-?\s*(extends|include|import|from)\b\s*(.+?)\s*-?%\}/m

{% extends "base.html" %}, {% include 'partials/head.html' %}, {% import "macros.html" as m %}, {% from "macros.html" import x %}

\b\s* (not \s+) after the keyword: Crinja happily parses {% include"x.html" %} with no space, and a tag this regex fails to see entirely never reaches the safe dynamic? fallback — it just silently drops the dependency edge and the included template's edits stop re-rendering its dependents during serve.

Constructors

new(templates : Hash(String, String), template_paths : Hash(String, String) = {} of String => String)

template_paths maps each snapshot name to the source file that won its slot ("partials/nav" => "templates/partials/nav.html"); it's what detects shadowed-variant references. Callers without a snapshot (tests) may omit it — shadow detection is then skipped.

Source

Instance methods

closure(name : String) : Set(String)

Transitive dependency set of a template, including itself. Unknown references stay in the set: they hash as "absent", so a template appearing later changes the closure hash and re-renders.

Source
closure_hash(entry_template : String, shortcode_templates : Set(String) = Set(String).new) : String

Stable fingerprint of everything that influences a page's rendered template output: the entry template's closure plus the closures of every shortcode template the page content invokes and of every render-hook template. The hook registry fingerprint the caller folds in covers only the hook sources themselves, so a partial a hook {% include %}s left --cache pages stale when it was edited.

Memoized: sites have a handful of distinct (entry template, shortcode set) combinations but compute this once or twice per page on cached builds. The memo lives for this instance's lifetime, which is exactly one template (re)load — invalidation is automatic.

Source
dependents_closure(changed : Set(String)) : Set(String)

All templates whose closure intersects changed — i.e. every template that (transitively) extends, includes, or imports one of the changed templates, plus the changed templates themselves.

Source
dynamic?

True when any reference could not be resolved statically — the graph is incomplete and callers must treat every template change as affecting every page.

Source
reaches_dynamic?(name : String) : Bool

True when name's closure holds a reference the scanner could not resolve, so what name reaches is unknown. Narrower than dynamic?: a computed include in a template name never pulls in leaves its closure complete.

Source
shortcode_names

Bare names of user shortcode templates (templates/shortcodes/*).

Source
shortcodes_used_in(text : String) : Set(String)

Templates the page's content references via shortcode calls.

Source
snapshot_escaping_refs

Literal references the snapshot loader cannot serve — ./-prefixed names, names that keep a non-template extension (never loadable by the snapshot glob), and exact-extension refs to a shadowed variant (foo.j2 while foo.html won the snapshot slot). The render reads these files from DISK via the loader fallback, so their edits are invisible to both the snapshot templates hash and this graph. The graph is marked dynamic whenever this set is non-empty; the builder additionally folds these files' disk contents into the global templates checksum so editing one invalidates the cache (see fold_snapshot_escaping_refs in phases/initialize.cr).

Source