The three-level template lookup: site overlay → theme → framework.
Three levels, never more (Hugo's specificity matrix is the cautionary
tale), and always printable via cherry theme.which. Overlays are keyed
per-theme (themes/<theme-name>/templates/…) so swapping themes never
silently applies overrides written for a different theme.
Summary
Functions
The full lookup chain for a template: {level, path, exists?} in
priority order. The first existing entry wins.
The .html.heex twin of an .html.eex template path.
Where a site's overlay file for this theme + template would live, or
nil when the theme is site-local.
Resolves a template to the winning {level, path}.
Whether this theme is loaded from inside the site itself.
Functions
@spec chain(Cherry.Site.t(), Cherry.Theme.t(), atom()) :: [ {atom(), Path.t(), boolean()} ]
The full lookup chain for a template: {level, path, exists?} in
priority order. The first existing entry wins.
Each level offers two candidates — <name>.html.heex, then
<name>.html.eex — so a HEEx rewrite wins over the EEx original at
the same level, and a site overlay in either language beats the theme.
The .html.heex twin of an .html.eex template path.
@spec overlay_path(Cherry.Site.t(), Cherry.Theme.t(), atom()) :: Path.t() | nil
Where a site's overlay file for this theme + template would live, or
nil when the theme is site-local.
An overlay only means something relative to an installed theme. A
theme that lives inside the site — what cherry gen.theme writes, and
what theme: "themes/mine" selects — owns its templates outright, so
the overlay path would land on the theme's own files and report every
one of them as untracked drift.
@spec resolve(Cherry.Site.t(), Cherry.Theme.t(), atom()) :: {:ok, {atom(), Path.t()}} | {:error, String.t()}
Resolves a template to the winning {level, path}.
@spec site_local?(Cherry.Site.t(), Cherry.Theme.t()) :: boolean()
Whether this theme is loaded from inside the site itself.