#!/bin/sh # Make the wasm bundle's index.html work when it is served at a URL deeper than # "/", and verify it stayed that way. # # usage: tools/fix-bundle-depth.sh # # WHY THIS EXISTS # # Crafter.Build generates an index.html whose boot scripts are relative # (src="runtime.js?v=…"), and runtime.js in turn does fetch("variants.json"), # fetch("files.json"), one fetch per VFS entry, and fetches the .wasm named by # variants.json — all relative. Relative to the DOCUMENT, not to the module. # # That is correct when the document is "/". It is broken for every deeper path, # and this site has several: /demos/raytracer, /shop/, /legal/. A # document at /demos/raytracer sends the browser to /demos/runtime.js, which does # not exist, so Caddy's `try_files {path} /index.html` returns index.html — and # the browser refuses to execute a module served as text/html. The visible result # is four NS_ERROR_CORRUPTED_CONTENT errors and a dead page. # # The SSR path solves this itself (Views::RenderDocument emits on # any page that boots wasm). This script covers the OTHER path: the static shell # Caddy serves directly when the backend is down, where there is no SSR to help. # # Two changes, both idempotent: # 1. in , which fixes every relative fetch runtime.js # makes, since a bare relative fetch() resolves against the document base. # 2. Root the boot script srcs, which already handles but which is worth # doing anyway so the tags are correct even if the is ever dropped. # # The durable fix belongs upstream: runtime.js should resolve its own assets # against import.meta.url rather than the document. Then no bundle would care how # deep the page is. Until then, this. set -eu DIR="${1:-}" [ -n "$DIR" ] || { echo "usage: tools/fix-bundle-depth.sh " >&2; exit 1; } IDX="$DIR/index.html" [ -f "$IDX" ] || { echo "fix-bundle-depth: $IDX not found" >&2; exit 1; } TMP="$(mktemp)" trap 'rm -f "$TMP"' EXIT # 1. Root every relative script src. Anchored on `src="` immediately followed by # something that is not / : and : covers http:, https: and any other scheme, # / covers both already-rooted and protocol-relative //host. sed -E 's|(]*[[:space:]]src=")([^"/:][^"]*")|\1/\2|g' "$IDX" > "$TMP" # 2. Insert as the first thing in , unless one is present. # First, so it applies to everything after it — a only governs the # references that follow it. if ! grep -qi '/s||\n|' "$TMP" > "$TMP.b" \ && mv "$TMP.b" "$TMP" fi mv "$TMP" "$IDX" trap - EXIT # Verify rather than assume. A silent no-op here would ship the broken page. fail=0 if ! grep -qi '' "$IDX"; then echo "fix-bundle-depth: FAILED to insert into $IDX" >&2 fail=1 fi rel=$(grep -oE ']*[[:space:]]src="[^"/:][^"]*"' "$IDX" | wc -l) if [ "$rel" -ne 0 ]; then echo "fix-bundle-depth: $rel script src(s) are still relative in $IDX:" >&2 grep -oE ']*[[:space:]]src="[^"/:][^"]*"' "$IDX" >&2 fail=1 fi [ "$fail" -eq 0 ] || exit 1 n=$(grep -coE ']*[[:space:]]src="/' "$IDX" || true) echo "fix-bundle-depth: $IDX has and $n rooted script src(s)"