Skip to content

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.

Terminal window
node tools/inline.mjs apps/<name>/index.html dist/<name>.html
  1. Resolve the module graph recursively from each <script type="module" src> entry — static import/export from, bare side-effect imports, and dynamic import() of relative specifiers. lib/met-stats.mjs is followed like any other module.
  2. 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 a data: module would resolve against the data: base and break.
  3. Emit one import map with each module as data:text/javascript;base64,…, plus a bootstrap <script type="module">import "metal:<entry>"</script>.
  4. Inline stylesheets as <style> and local images as base64 data: URIs.
  5. Leave runtime fetch('./data/*.json') alone — under file:// those fail by design, and each app falls back to an inlined .js data 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.

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).

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.