The island pieces existed but nothing connected .wrn compilation to island
emission. Now:
- codegen emits a data-wrn-island placeholder for component tags bound to
.tsx imports, keeping .wrn components on the normal mount path
- the dev pipeline and static build resolve island imports, thread the
names into codegen, and build the bundles
- collectScripts adds /__wrnexus/islands.js only when island markup is
present, so island-free pages still ship nothing
- island routes classify as static-interactive via hasIslands
Three bugs found by driving a real page in the browser:
1. The mount runtime was never built anywhere, so the bootstrap 404'd and
no island mounted.
2. Building the runtime separately from the islands gave each its own copy
of React: "Cannot read properties of null (reading 'useState')". The
runtime is now an entrypoint of the same build so React stays in one
shared chunk. The existing single-React test only compared bundles
within one build and could not see across build boundaries.
3. Island props arrived as attribute strings, so start={3} was "3" and
incrementing produced "31" then "311". Props now follow JSX semantics:
{…} parses as JSON, quoted values stay strings, and a runtime
expression is a WRN-ISLAND-PROPS build error rather than a silent
wrong value.
island-codegen.ts no longer imports @wrnexus/core. Compiler modules are
bundled into the Node-only VS Code extension, which contains no other
packages, so a runtime import of core broke the editor compiler; the two
helpers are implemented locally and the core dependency is dropped again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
57 lines
1.9 KiB
TypeScript
57 lines
1.9 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { parse } from "@wrnexus/syntax";
|
|
import { analyzeRuntimeRequirements, routeNeedsIslands } from "../src/analysis.ts";
|
|
|
|
test("a route with an island import needs client JavaScript", () => {
|
|
expect(
|
|
routeNeedsIslands([
|
|
{ declaration: { source: "./a" } as any, resolved: "/app/a.ts" },
|
|
{ declaration: { source: "./Chart" } as any, resolved: "/app/Chart.tsx", kind: "island" },
|
|
]),
|
|
).toBe(true);
|
|
});
|
|
|
|
test("a route with no island imports stays zero-JS", () => {
|
|
expect(
|
|
routeNeedsIslands([{ declaration: { source: "./a" } as any, resolved: "/app/a.ts" }]),
|
|
).toBe(false);
|
|
});
|
|
|
|
test("an empty import list stays zero-JS", () => {
|
|
expect(routeNeedsIslands([])).toBe(false);
|
|
});
|
|
|
|
test("an unresolved import does not count as an island", () => {
|
|
expect(
|
|
routeNeedsIslands([
|
|
{
|
|
declaration: { source: "./missing" } as any,
|
|
diagnostic: { code: "WRN-IMPORT-NOT-FOUND", message: "nope", severity: "warning" },
|
|
},
|
|
]),
|
|
).toBe(false);
|
|
});
|
|
|
|
test("an island promotes a static route to static-interactive", () => {
|
|
const source = `page Home { view { <div>hello</div> } }`;
|
|
const ast = parse(source);
|
|
|
|
const plain = analyzeRuntimeRequirements(ast);
|
|
expect(plain.kind).toBe("static");
|
|
expect(plain.needsIslandRuntime).toBe(false);
|
|
|
|
const withIsland = analyzeRuntimeRequirements(ast, { hasIslands: true });
|
|
expect(withIsland.kind).toBe("static-interactive");
|
|
expect(withIsland.needsIslandRuntime).toBe(true);
|
|
expect(withIsland.reasons).toContain("react island");
|
|
});
|
|
|
|
test("an island does not turn on the WRNexus reactive runtime", () => {
|
|
const ast = parse(`page Home { view { <div>hello</div> } }`);
|
|
const withIsland = analyzeRuntimeRequirements(ast, { hasIslands: true });
|
|
|
|
// Islands ship the island runtime, not WRNexus's own client runtime.
|
|
expect(withIsland.needsClientRuntime).toBe(false);
|
|
expect(withIsland.needsIslandRuntime).toBe(true);
|
|
});
|