diff --git a/packages/dev-server/src/assets.ts b/packages/dev-server/src/assets.ts index f0d94f85..d364b9a3 100644 --- a/packages/dev-server/src/assets.ts +++ b/packages/dev-server/src/assets.ts @@ -34,6 +34,7 @@ import type { AssetServer } from "./runtime.ts"; import { servePublicAsset } from "./public.ts"; import { servePluginAsset, type ServedPluginAsset } from "./plugin-assets.ts"; import { serveIslandArtifact, serveWrnBrowserArtifact } from "./pipeline.ts"; +import { HMR_CLIENT_HREF, HMR_CLIENT_JS } from "./runtime.ts"; /** Style inputs the dev asset server needs to build `/__wrnexus/styles.css`. */ export interface DevStyles { @@ -99,6 +100,7 @@ export function createDevAssetServer( if (pathname.startsWith("/__wrnexus/island/")) { return serveIslandArtifact(pathname) ?? new Response("Not Found", { status: 404 }); } + if (pathname === HMR_CLIENT_HREF) return jsResponse(HMR_CLIENT_JS); if (pathname === "/__wrnexus/islands.js") return jsResponse(getIslandRuntime(true)); if (pathname === "/__wrnexus/reactive.js") return jsResponse(getReactiveRuntime(true)); if (pathname === "/__wrnexus/controllers.js") diff --git a/packages/dev-server/src/runtime.ts b/packages/dev-server/src/runtime.ts index 22bfcf18..740d5823 100644 --- a/packages/dev-server/src/runtime.ts +++ b/packages/dev-server/src/runtime.ts @@ -878,8 +878,19 @@ function randomNonce(): string { } /** The dev HMR client as a nonce-tagged inline script (strict-CSP friendly). */ -function hmrClientTag(nonce: string): string { - return ``; +/** Path the dev asset server publishes the HMR client on. */ +export const HMR_CLIENT_HREF = "/__wrnexus/hmr-client.js"; + +/** + * The HMR client is served as an external module rather than inlined. + * + * A document's CSP nonce is fixed at load, so an inline script arriving from a + * later response — which is exactly what an HMR reload produces — can never + * carry a nonce this document accepts. An external file is covered by + * script-src 'self' and needs no nonce at all. + */ +function hmrClientTag(_nonce: string): string { + return ``; } /** 403 for a rejected cross-site WebSocket handshake. */