Single-file builds
Browsers block ES-module imports over file:// (CORS origin: null), so the served apps
need HTTP. The single-file builds solve the “just double-click it” case:
dist/<name>.html is one self-contained file — modules, styles, images, and fallback data
all inlined.
node tools/inline.mjs apps/<name>/index.html dist/<name>.htmlHow the inliner works
Section titled “How the inliner works”- Resolve the module graph recursively from each
<script type="module" src>entry — staticimport/export from, bare side-effect imports, and dynamicimport()of relative specifiers.lib/met-stats.mjsis followed like any other module. - Assign bare keys (
metal:0,metal:1, …) and rewrite every relative specifier to its key. This is the crucial trick: bare specifiers resolve through the page’s import map regardless of the importing module’s base URL, whereas relative specifiers inside adata:module would resolve against thedata:base and break. - Emit one import map with each module as
data:text/javascript;base64,…, plus a bootstrap<script type="module">import "metal:<entry>"</script>. - Inline stylesheets as
<style>and local images as base64data:URIs. - Leave runtime
fetch('./data/*.json')alone — underfile://those fail by design, and each app falls back to an inlined.jsdata module that is in the module graph.
Every build self-verifies: no external src/href, no raw relative imports outside data:
payloads, no http(s):// outside base64, importmap parses, every module references only
bare metal: keys. Failures exit 1.
The dist inventory
Section titled “The dist inventory”Nine apps build to dist/ (total ~2.7 MB after the logo/data trimming pass). Two apps have
no build on purpose: the Real-Data Demo (streaming is the point) and WebGPU FSS (requires a
WebGPU browser).
When to rebuild
Section titled “When to rebuild”dist/ is committed as a shipped artifact and not rebuilt automatically. Rebuild an
app’s single file after changing its source or shared lib code. Note the deliberate
exception: navigation-only changes (e.g. the logo→home link) are not propagated into
dist/ — a home link is meaningless in an offline single file.
