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>
55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
import { I18N_JS_HREF } from "@wrnexus/i18n";
|
|
import type { ClientRuntimeDefinition } from "@wrnexus/plugin";
|
|
import { THEME_JS_HREF } from "@wrnexus/styles";
|
|
import type { RenderScript } from "@wrnexus/ssr";
|
|
import { runtimeScriptsForMarkup } from "./plugin-assets.ts";
|
|
|
|
/** Select browser runtimes from capabilities present in rendered markup. */
|
|
export function collectScripts(
|
|
body: string,
|
|
clientRuntimes: readonly ClientRuntimeDefinition[] = [],
|
|
navigation: { mode?: "auto" | "client" | "document" } = {},
|
|
): RenderScript[] {
|
|
const scripts: RenderScript[] = [];
|
|
if (
|
|
/\bdata-scope=/.test(body) ||
|
|
/\bdata-wrnexus-csr=/.test(body) ||
|
|
/\bdata-wrn-client-template=/.test(body) ||
|
|
/\bdata-wrn-async=/.test(body)
|
|
) {
|
|
scripts.push("/__wrnexus/reactive.js");
|
|
}
|
|
// Islands ship their own bootstrap, not the reactive runtime — a page whose
|
|
// only interactivity is an island must not pull in WRNexus's client runtime.
|
|
if (/\bdata-wrn-island=/.test(body)) scripts.push("/__wrnexus/islands.js");
|
|
if (/\bdata-wrn-action=/.test(body)) scripts.push("/__wrnexus/actions.js");
|
|
if (
|
|
/\bdata-wrn-theme-(toggle|set)\b/.test(body) ||
|
|
/\bdata-wrn-accent-(set|clear)\b/.test(body)
|
|
) {
|
|
scripts.push(THEME_JS_HREF);
|
|
}
|
|
if (/\bdata-schema="[A-Za-z0-9_-]+"/.test(body)) {
|
|
scripts.push("/__wrnexus/schemas.js", "/__wrnexus/validate.js");
|
|
}
|
|
if (/\bdata-wrn-lang-set\b/.test(body) || /\bselect[^>]*\bdata-wrn-lang\b/.test(body)) {
|
|
scripts.push(I18N_JS_HREF);
|
|
}
|
|
if (/\bdata-room=/.test(body)) scripts.push("/__wrnexus/realtime.js");
|
|
if (/\bdata-uploader\b/.test(body)) scripts.push("/__wrnexus/uploader.js");
|
|
scripts.push(...runtimeScriptsForMarkup(body, clientRuntimes));
|
|
|
|
const mode = navigation.mode ?? "auto";
|
|
if (mode === "client" || (mode === "auto" && scripts.length > 0)) {
|
|
scripts.unshift("/__wrnexus/nav.js");
|
|
}
|
|
return scripts;
|
|
}
|
|
|
|
/** Whether rendered markup needs the Capacitor/native browser bridge. */
|
|
export function usesMobileRuntime(body: string): boolean {
|
|
return /\b(?:data-mobile-[\w-]+|data-native-(?:browser|mobile|only|requires|unsupported|options)|data-on-wrnexus-(?:browser|mobile)-[\w-]+)\b/.test(
|
|
body,
|
|
);
|
|
}
|