Asset registry
A feature declares the stylesheets and scripts it ships, and the public shell renders every declared asset, deduplicated and in a fixed order. The page’s CSS and JS are composed from the installed features, with no <link> or <script> tag written into a hook or a template.
How it works
- A feature ships the file under its blueprint’s
assets/folder, which the blueprint’s asset route serves, and registers it frominit_feature(app). - The registry,
splent_framework.assets.asset_registry, keeps every registration of the app and hands the theme the URLs in order. - The set of assets follows the set of installed features, so there is no product-level list of stylesheets to keep.
register_asset
The slider ships slider.css and slider.js like this.
from splent_framework.blueprints.base_blueprint import create_blueprint
from splent_framework.services.service_locator import register_service
from splent_io.splent_feature_slider.services import SliderService
slider_bp = create_blueprint(__name__)
def init_feature(app):
from splent_framework.assets.asset_registry import register_asset
register_service(app, "SliderService", SliderService)
register_asset("css", "slider.assets", order=100, subfolder="css", filename="slider.css")
register_asset("js", "slider.assets", order=100, subfolder="js", filename="slider.js")
| Parameter | Required | Description |
|---|---|---|
kind |
yes | "css" or "js". |
endpoint |
yes | The blueprint’s asset route, for instance "slider.assets". |
order |
no | Load order, lower first. Defaults to 100. See Order. |
shell |
no | Keyword only. The shell that loads it, "public" (the default), "app" or "both". See Assets per shell. |
**params |
no | url_for parameters for the asset route, such as subfolder="css" and filename="slider.css". |
A registration is idempotent. The registry keys it by (kind, endpoint, params), so registering the same asset twice emits one tag. The first registration keeps its order, and registering the asset again for the other shell makes it serve both.
Two companion functions exist, and you rarely call them yourself.
get_assets(kind, shell="public")returns the URLs of one kind registered for that shell and those registered for"both", deduplicated and ordered by(order, endpoint).shellis keyword only and takes"public"or"app", the shell rendering the page. Each carries a?v=<file mtime>suffix, so an edited stylesheet is fetched again.url_foralready adds that suffix to every asset URL, and the registry leaves such a URL as it is. A versioned asset is cached by the browser for a year (see Versioned assets).url_forruns when it is called, so call it from a template, inside a request. An asset whose route cannot be resolved is skipped without an error. It is a Jinja global, and the theme uses it.clear_assets()empties the registry. The framework calls it per app (see Cleared per app).
Order
order decides the cascade.
| Layer | order |
Loads |
|---|---|---|
| Theme base | about 0 | first, lowest priority |
| Features | about 100 | in the middle |
| Skins | about 200 | last, highest priority |
A skin registers its stylesheet, skin_<name>.css (for instance skin_arcade.css), with order=200, so it loads after every feature’s CSS and can override it. Features keep the default 100.
In the theme
The public shell, public_base.html in the theme, renders the CSS in the <head>.
{% for href in get_assets('css') %}<link rel="stylesheet" href="{{ href }}">{% endfor %}
The JS goes just before </body>, deferred.
{% for src in get_assets('js') %}<script defer src="{{ src }}"></script>{% endfor %}
Cleared per app
The framework’s FeatureManager calls clear_assets() in register_features(), before it loads a product’s features, as it does for the navigation registry. The registry holds only the assets of this product’s installed features, so a product emits only the CSS and JS it uses.
See also
- Navigation registry. The same pattern for the main menu
- Admin-configurable settings. Declare a feature’s settings and get an admin panel
hooks.pyand template hooks. Inject HTML into layout slots