Theme Framework How the LeaseLeads theme frontend works

The AssetLoader pipeline

How a CSS selector becomes a loaded, registered, running component.

On this page 4

Nothing ships that the page doesn't use. The AssetLoader pipeline is what enforces that: you hand it a list of { name, selector, loader } descriptors, and for each one it downloads the component's chunk only if the selector matches an element on the page, then registers and instantiates it. This is presence-gated lazy loading, and it runs over Vite's per-component code-splitting so every component is its own dynamically-imported chunk.

The descriptor

You describe a component with three things:

js
processor.convert({
  name: 'Intro',
  selector: '.intro',
  loader: () => import('../../modules/Intro/Intro.js'),
})

Each field maps to one job the pipeline does on your behalf — you supply the three values and never touch the machinery between them:

  • selector is the presence gate. The pipeline only downloads the chunk if document.querySelector(selector) finds an element. No match, no load.
  • loader is the dynamic import. It runs at most once; a component listed in several places on the page still downloads a single time.
  • name is the registration key — the class exported under name (exports[name]) is registered in Globals under it once the chunk resolves. Pass null for a style-only entry (below).

You queue as many descriptors as you like, then call .load() once to drain them together.

Note

Why it dedups so carefully. Resolving a chunk takes several turns of the event loop, and a sibling module may read the registry mid-flight. The pipeline registers a harmless placeholder under the name the moment loading starts, then swaps the real class in when the import resolves — so a consumer that looks up the component mid-load gets a benign no-op instead of a missing entry. You never see this: you just get the guarantee that one selector matched many times still loads and registers once.

Style-only entries (name: null)

Pass name: null and the pipeline runs the loader — so the chunk downloads and its imported CSS applies — but registers nothing and instantiates nothing:

js
processor.convert({
  name: null,                                     // no class to register
  selector: '.intro',
  loader: () => import('../../modules/Intro/Intro.js'),
})

This is the lever two-phase loading pulls on the critical path: get a module's JS and CSS ready while withholding the instantiation that would trigger reflow.

Note

A style-only entry (name: null) is not the same as a CSS-only component (an Instantiable with only a selector). The first runs a loader for its CSS and registers nothing; the second is a real class whose lifecycle happens to be empty.

Registering a component in a Global or host

You add a component to the pipeline by adding a convert(...) call — either to a Global's list (Crucial / Deferred) for a top-level Module, or, for a nested Component, by having the host mix the matching HasX trait whose bootXComponents() queues the same descriptor on the host's shared processor (see Sub-components & HasX). Either way the shape is identical: a name, a selector, a loader.