Skip to content

Theme loading and the asset pipeline

How the loader walks themes, strips metadata blocks, and builds the asset pipeline.

Updated View as Markdown

theme/loader.rs walks a themes/ directory and produces fully-populated ThemeManifest + ThemeAssetPipeline pairs, one pass per .jinja file that registers the template with MiniJinja, extracts the schema, extracts stylesheets, and extracts JavaScript.

The walk

walk_themes(themes_dir, env):

  1. Reads each subdirectory of themes_dir, the directory name is the slug.
  2. Loads theme.json into a ThemeManifest (theme/manifest.rs) and validates its settings.
  3. Registers every .jinja file under layout/, nodes/, templates/, and snippets/ (recursively) with MiniJinja under "{slug}/{dir}/{relpath}".
  4. Scans nodes/ for {% schema %} blocks, deserializing each into a NodeSchema and inserting it into manifest.nodes keyed by file stem. Invalid schema or settings → engine startup fails with the theme and node named.
  5. Populates style_ownership (which CSS keys each rendered type pulls in, including its accepts/slot child types), then pre-computes combined_css / combined_js.

Stripping metadata blocks

Before MiniJinja compiles a template, strip_metadata_blocks removes every {% schema %}{% endschema %}, {% stylesheet %}{% endstylesheet %}, and {% javascript %}{% endjavascript %} pair, this is what stops MiniJinja’s “unknown statement” rejection. extract_role_tag separately pulls a leading @editor:section or @editor:any marker off the source (the role also lands in the schema if the JSON has one).

The asset pipeline

pub struct ThemeAssetPipeline {
    pub section_styles: HashMap<String, String>,   // style key -> raw CSS
    pub section_scripts: HashMap<String, String>,  // style key -> raw JS
    pub style_ownership: HashMap<String, Vec<String>>, // type -> CSS keys
    pub combined_css: String,
    pub combined_js: String,
}

Style keys: __layout__ for files in layout/, otherwise the file stem (nodes/hero.jinjahero). Because snippets are registered too, a snippet’s stylesheet block would be keyed by the snippet’s stem.

page_css(rendered_types), per-page subsetting

  1. __layout__ CSS first.
  2. For each rendered node type: its own stylesheet, plus the stylesheets of every accepts / slot child type (via style_ownership), deduplicated.
  3. Returns the concatenated subset, the page emits it as one <style> block in <head>.

Combined output

build_combined_css concatenates all styles in sorted-key order; build_combined_js concatenates every script block wrapped in an IIFE ((function() { … })();). The engine content-hashes these into combined.{hash}.css / .js for CDN-cached asset serving.

The manifest

theme/manifest.rs, the parsed theme.json:

pub struct ThemeManifest {
    pub name: String,
    pub version: String,
    pub settings: Vec<SettingDefinition>,          // global settings schema
    pub nodes: HashMap<String, NodeSchema>,        // from nodes/*.jinja schemas
    pub presets: HashMap<String, PageConfig>,      // default page trees
    pub section_groups: HashMap<String, SectionGroupSchema>,
}

SectionGroupSchema { name, max_sections, allowed_sections, preset } models the @root:header-group / @root:footer-group entries.

Template names

Every registered template is addressable by namespaced name:

File Template name
themes/default/layout/theme.jinja default/layout/theme.jinja
themes/default/nodes/hero.jinja default/nodes/hero.jinja
themes/default/templates/account/order.jinja default/templates/account/order.jinja
themes/default/snippets/ui/button.jinja default/snippets/ui/button.jinja

Node bodies resolve dynamically as {slug}/nodes/{node_type}.jinja; page templates via resolve_template_path (see Template resolution).

Asset processing

asset/process.rs::process_assets(env, theme_dir, output_dir, settings_ctx) renders *.jinja files found in assets/ through MiniJinja (stripping the .jinja suffix in the output), copies everything else verbatim, and skips subdirectories, no-op when assets/ is missing. The CLI build command uses this to pre-render theme assets into dist/.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close