import { afterAll, expect, test } from "bun:test"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { runBuild } from "../src/build.ts"; const scratchRoot = join(import.meta.dir, ".tmp-plugin-runtime-production"); mkdirSync(scratchRoot, { recursive: true }); afterAll(() => rmSync(scratchRoot, { recursive: true, force: true })); test("production renders and serves a content-addressed plugin runtime", async () => { const root = mkdtempSync(join(scratchRoot, "app-")); mkdirSync(join(root, "app", "pages"), { recursive: true }); mkdirSync(join(root, "app", "components"), { recursive: true }); writeFileSync(join(root, "package.json"), JSON.stringify({ name: "plugin-runtime-fixture" })); writeFileSync( join(root, "wrnexus.config.ts"), `import { definePlugin } from "@wrnexus/plugin"; export default { plugins: [definePlugin({ name: "runtime-fixture", clientRuntimes: [{ id: "runtime-fixture", source: "window.__runtimeFixture = true;", type: "script", bundle: false, }], })], }; `, ); writeFileSync( join(root, "app", "components", "RuntimeProbe.wrn"), `component RuntimeProbe { view {
Runtime fixture
} }\n`, ); writeFileSync( join(root, "app", "pages", "index.wrn"), `import RuntimeProbe from "../components/RuntimeProbe.wrn"\npage Home { view { } }\n`, ); writeFileSync( join(root, "app", "pages", "[id].wrn"), `page Dynamic { functions { client function selectPanel() { console.log('panel-ready') } } view { } }`, ); await runBuild(root); const proc = Bun.spawn({ cmd: ["bun", join(root, "dist", "server.js")], env: { ...process.env, PORT: "0" }, stdout: "pipe", stderr: "pipe", cwd: root, }); let port: number | undefined; try { const reader = proc.stdout.getReader(); const decoder = new TextDecoder(); let output = ""; const giveUp = setTimeout(() => proc.kill(), 20_000); try { while (port === undefined) { const { value, done } = await reader.read(); if (done) break; output += decoder.decode(value, { stream: true }); const match = /listening on http:\/\/[^:]+:(\d+)/.exec(output); if (match) port = Number(match[1]); } } finally { clearTimeout(giveUp); reader.releaseLock(); } if (port === undefined) { throw new Error( `production server failed to start: ${await new Response(proc.stderr).text()}`, ); } const page = await fetch(`http://127.0.0.1:${port}/`); expect(page.status).toBe(200); const dynamic = await fetch(`http://127.0.0.1:${port}/example`); const dynamicHtml = await dynamic.text(); const modulePath = dynamicHtml.match(/\/__wrnexus\/client\/[^"\s<>]+\.mjs/)?.[0]; expect(modulePath).toMatch(/^\/__wrnexus\/client\/[A-Za-z0-9._-]+\.mjs$/); expect((await fetch(`http://127.0.0.1:${port}${modulePath}`)).status).toBe(200); const html = await page.text(); const assetPath = html.match(/(\/__wrnexus\/assets\/runtime-fixture\.[a-f0-9]+\.js)/)?.[1]; expect(assetPath).toBeTruthy(); const asset = await fetch(`http://127.0.0.1:${port}${assetPath}`); expect(asset.status).toBe(200); expect(await asset.text()).toContain("window.__runtimeFixture = true"); const lazyAsset = await fetch(`http://127.0.0.1:${port}/__wrnexus/assets/runtime-fixture.js`); expect(lazyAsset.status).toBe(200); expect(await lazyAsset.text()).toContain("window.__runtimeFixture = true"); expect(lazyAsset.headers.get("cache-control") ?? "").not.toContain("immutable"); } finally { proc.kill(); await proc.exited; } }, 30_000);