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>
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { parse } from "@wrnexus/syntax";
|
|
import { generate } from "../src/codegen.ts";
|
|
|
|
const SOURCE = `page Home {
|
|
view {
|
|
<Chart title="Revenue" client:visible />
|
|
<Card>plain</Card>
|
|
}
|
|
}`;
|
|
|
|
test("an island tag emits an island marker instead of a component mount", () => {
|
|
const out = generate(parse(SOURCE), { islands: new Set(["Chart"]) });
|
|
|
|
expect(out).toContain("data-wrn-island=");
|
|
expect(out).toContain("Chart");
|
|
expect(out).toContain('data-wrn-island-strategy="visible"');
|
|
// Non-island components still mount the normal way.
|
|
expect(out).toContain('data-component="Card"');
|
|
});
|
|
|
|
test("without the island set the same tag stays a normal component", () => {
|
|
const out = generate(parse(SOURCE));
|
|
|
|
expect(out).not.toContain("data-wrn-island=");
|
|
expect(out).toContain('data-component="Chart"');
|
|
});
|
|
|
|
test("island props are serialized into the marker", () => {
|
|
const out = generate(parse(SOURCE), { islands: new Set(["Chart"]) });
|
|
expect(out).toContain("Revenue");
|
|
});
|
|
|
|
test("numeric and boolean island props keep their types through the marker", () => {
|
|
const source = `page Home {
|
|
view { <Chart start={3} live={true} title="Revenue" /> }
|
|
}`;
|
|
const out = generate(parse(source), { islands: new Set(["Chart"]) });
|
|
|
|
expect(out).toContain(""start":3");
|
|
expect(out).toContain(""live":true");
|
|
expect(out).toContain(""title":"Revenue"");
|
|
});
|
|
|
|
test("a runtime expression prop fails the build with WRN-ISLAND-PROPS", () => {
|
|
const source = `page Home {
|
|
state count = 1
|
|
view { <Chart value={count} /> }
|
|
}`;
|
|
expect(() => generate(parse(source), { islands: new Set(["Chart"]) })).toThrow(
|
|
/WRN-ISLAND-PROPS/,
|
|
);
|
|
});
|