Files
WRNexusJS/packages/cli/test/workspace.test.ts
T
ClintchizandClaude Opus 5 17aa3b98eb feat(islands): wire islands end to end
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>
2026-08-18 16:16:03 +05:30

119 lines
4.7 KiB
TypeScript

import { expect, test } from "bun:test";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { currentCliVersion } from "../src/update-notifier.ts";
import { scaffoldFrameworkRange } from "../src/create.ts";
import {
addWorkspaceApp,
insertWorkspaceApp,
resolveWorkspaceConfig,
workspaceFiles,
workspaceMigrationTargets,
} from "../src/workspace.ts";
test("workspace environments preserve runtime and HMR policy", () => {
const resolved = resolveWorkspaceConfig(
{
environments: {
staging: {
protocol: "https",
rootDomain: "staging.example.com",
port: 443,
runtime: "development",
hmr: false,
build: false,
migrate: false,
profile: "uat",
},
},
apps: [{ name: "web", dir: "apps/web", subdomain: "www" }],
},
"staging",
);
expect(resolved.config.runtime).toBe("development");
expect(resolved.config.hmr).toBe(false);
expect(resolved.config.profile).toBe("uat");
expect(resolved.apps[0]?.publicOrigin).toBe("https://www.staging.example.com");
});
test("workspace templates pin the CLI and use compatible independent package ranges", () => {
const files = workspaceFiles("acme");
const rootPackage = JSON.parse(files["package.json"]!);
const sharedPackage = JSON.parse(files["packages/shared/package.json"]!);
const version = currentCliVersion();
expect(rootPackage.devDependencies["@wrnexus/cli"]).toBe(version);
expect(sharedPackage.dependencies["@wrnexus/pubsub"]).toBe(scaffoldFrameworkRange);
expect(files["README.md"]).toContain("http://127.0.0.1:3000");
expect(files["README.md"]).toContain("internal gateway targets");
expect(JSON.parse(files["package.json"]!).scripts.production).toBe("wrnexus production");
expect(JSON.parse(files["package.json"]!).scripts.check).toContain("typecheck");
expect(JSON.parse(files["package.json"]!).scripts.check).toContain("format:check");
expect(files["wrnexus.workspace.ts"]).toContain('runtime: "development"');
expect(files["wrnexus.workspace.ts"]).toContain("hmr: false");
expect(files[".env.example"]).toContain("REDIS_URL");
expect(files["eslint.config.js"]).toContain("typescript-eslint");
expect(files["tsconfig.json"]).toContain('"strict": true');
expect(files[".vscode/extensions.json"]).toContain("wrnexus.wrnexus");
});
test("production workspace detects default and named SQL migrations", () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-production-migrations-"));
try {
mkdirSync(join(root, "app", "db", "migrations"), { recursive: true });
mkdirSync(join(root, "app", "db", "analytics", "migrations"), { recursive: true });
mkdirSync(join(root, "app", "db", "empty", "migrations"), { recursive: true });
writeFileSync(join(root, "app", "db", "migrations", "0001_init.sql"), "select 1;");
writeFileSync(join(root, "app", "db", "analytics", "migrations", "0001_init.sql"), "select 1;");
expect(workspaceMigrationTargets(root)).toEqual([null, "analytics"]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("insertWorkspaceApp handles nested arrays and comments", () => {
const source = `const config = {
apps: [
{ name: "web", dir: "apps/web", domains: ["localhost"] },
{ name: "admin", dir: "apps/admin", domains: ["admin.localhost"], auth: { allowIps: ["::1"] } }, // ]
],
};
export default config;
`;
const result = insertWorkspaceApp(source, {
name: "reports",
dir: "apps/reports",
domains: ["reports.localhost"],
});
expect(result).toContain(
'{ name: "reports", dir: "apps/reports", domains: ["reports.localhost"] },',
);
expect(result.indexOf('name: "reports"')).toBeLessThan(result.indexOf("\n ],"));
});
test("workspace add scaffolds and registers an app", async () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-workspace-add-"));
writeFileSync(
join(root, "wrnexus.workspace.ts"),
'export default { apps: [{ name: "web", dir: "apps/web", domains: ["localhost"] }] };\n',
);
try {
await addWorkspaceApp(root, "reports", ["--domain=reports.localhost"]);
const manifest = JSON.parse(
readFileSync(join(root, "apps", "reports", "package.json"), "utf8"),
);
const config = readFileSync(join(root, "wrnexus.workspace.ts"), "utf8");
expect(manifest.name).toBe("reports");
expect(manifest.wrnexus.version).toBe(currentCliVersion());
expect(config).toContain('name: "reports"');
expect(config).toContain('domains: ["reports.localhost"]');
expect(existsSync(join(root, "apps", "reports", "public", "llms.txt"))).toBe(true);
} finally {
rmSync(root, { recursive: true, force: true });
}
});