The HMR client script was dead in the browser. HMR_CLIENT_JS is a TypeScript template literal, so the regex [ \t\r\n] inside it was expanded into real control characters, producing a regex literal containing a raw newline — a syntax error that took the whole script down with "Invalid regular expression: missing /". It now uses \s, and a test asserts the emitted client parses and holds no control characters inside regex literals; that test fails if the bug is reintroduced. HMR also corrupted CSP nonces. A document's nonce is fixed at load, but morph copied attributes from freshly fetched HTML, overwriting the live nonce with one the browser will not honour. syncAttrs now leaves nonce alone, and nodes moved across are re-stamped with the live nonce. Islands vanished on every HMR update: morph puts the server placeholder back over the mounted island. The island runtime now remounts on wrnexus:hmr-updated. Remounting swaps the container for a bare clone — re-rendering the existing root is a no-op once HMR has wiped the DOM externally, and unmounting throws asynchronously because the nodes React wants to remove are already gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
/**
|
|
* The island bootstrap served at `/__wrnexus/islands.js`.
|
|
*
|
|
* Mirrors the `@wrnexus/csr` pattern: this file is only ever fetched when a
|
|
* `data-wrn-island` marker is present, so island-free pages download nothing —
|
|
* including React.
|
|
*/
|
|
export function getIslandRuntime(development = false): string {
|
|
return `
|
|
(function () {
|
|
function loader(name) {
|
|
return import("/__wrnexus/island/" + encodeURIComponent(name) + ".js");
|
|
}
|
|
|
|
function boot() {
|
|
if (!document.querySelector("[data-wrn-island]")) return;
|
|
import("/__wrnexus/island/runtime.js").then(function (runtime) {
|
|
runtime.mountIslands(document, { loader: loader, development: ${development} });
|
|
window.__wrnexusUnmountIslands = function (root) {
|
|
runtime.unmountIslands(root || document);
|
|
};
|
|
window.__wrnexusRemountIslands = function (root) {
|
|
return runtime.remountIslands(root || document, {
|
|
loader: loader,
|
|
development: ${development}
|
|
});
|
|
};
|
|
// HMR morphs the server placeholder back over the mounted island, which
|
|
// discards the React tree. Remount once the DOM has settled.
|
|
window.addEventListener("wrnexus:hmr-updated", function () {
|
|
window.__wrnexusRemountIslands();
|
|
});
|
|
}).catch(function (error) {
|
|
console.error("[wrnexus] failed to load the island runtime", error);
|
|
});
|
|
}
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", boot);
|
|
} else {
|
|
boot();
|
|
}
|
|
})();
|
|
`;
|
|
}
|