fix: correct feature-report prose, restore update test coverage, warn on sub-0.8.0 upgrades

- generate-complete-framework-report.mjs no longer claims legacyEmit/
  legacyEventProps/legacyComponentDiscovery/stringLayouts/
  functions.legacyDefaultRuntime are usable compatibility flags; they were
  removed before the first public release and a config setting them is now
  rejected. Regenerated docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md.
- Restored the update.test.ts coverage lost in the sub-0.8.0 migration
  cleanup: a 0.8.x fixture now asserts refreshFrameworkFiles' still-live
  behaviour (public/llms.txt and CLAUDE.md creation) and that
  pkg.wrnexus.version stays at its old value after an unverified update.
  The .gitignore refresh and build/start/production script backfill were
  themselves removed as part of dropping the sub-0.8.0 migrations that
  implemented them, so there is nothing left to cover for those two.
- updateApp now logs a clear warning when the detected project version is
  below 0.8.0, naming the version and stating that automated migration from
  below 0.8.0 is no longer supported, without failing the command (a
  marker-less app, like examples/basic-app, is benign and must still
  upgrade cleanly).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 02:19:23 +05:30
co-authored by Claude Opus 5
parent abebbcf8af
commit d069f5ddd7
4 changed files with 91 additions and 8 deletions
+4 -4
View File
@@ -119,11 +119,11 @@ Legacy API binding remains valid: `ssr { api users GET /api/users { return users
| Legacy/earlier approach | Current approach | Compatibility/status |
| ------------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Compiler-owned/internal parsing imports | Canonical `@wrnexus/syntax` lexer/parser/AST/diagnostics | Compiler re-exports remain for compatibility; direct internals are deprecated. |
| Implicit component discovery everywhere | Explicit imports and generated contracts | Upgraded apps can retain `legacyComponentDiscovery`; unresolved symbols are reported rather than guessed. |
| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts remain behind compatibility configuration. |
| Implicit component discovery everywhere | Explicit imports and generated contracts | Unresolved symbols are reported rather than guessed; there is no compatibility opt-out. |
| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts are no longer supported; the config key that toggled them was removed. |
| `@event changed = function` in props | `outputs { changed(payload: Type) }` | v0.6 migration converts declarations; ambiguous payloads become `unknown`. |
| `$emit("changed", value)` and `event.detail` | `output.changed(value)` and direct `payload` | Static cases auto-migrated; dynamic emit names require review. |
| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; compatibility default can preserve ambiguous behavior. |
| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; the legacy default runtime that preserved ambiguous behavior was removed. |
| Manually copied CAPTCHA JS/script tags | Package-discovered client runtime/assets | v0.4 removes tags and archives old assets under `.wrnexus/legacy-assets/0.4.0`. |
| Package components/routes/assets wired manually | Automatic package/plugin discovery and contribution registry | Current CLI/build/dev server inspect and consume contributions. |
| Only scalar/quoted dynamic props | Native arrays/objects and JSX-style unquoted expressions | Quoted expression attributes remain supported. |
@@ -137,7 +137,7 @@ Legacy API binding remains valid: `ssr { api users GET /api/users { return users
| Per-component/global style placement inconsistencies | `style {}` promoted to document head | Current pipeline supports CSP, HMR and CSR navigation. |
| Manually maintained API/component knowledge | Generated public API and component references plus validation gates | `check:public-api`, generated-type checks, UI visual contract and package audits detect drift. |
Compatibility flags visible in generated/upgraded config include `legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime`. New projects default legacy flags off; migration-created configs may enable them to preserve behavior until source modernization is complete.
`legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime` (and the other pre-1.0 compatibility keys) were removed before the first public release; a config that still sets any of them is rejected by validation rather than honored.
## 7. Diagnostics, security, and correctness guarantees
+6
View File
@@ -950,6 +950,12 @@ export function updateApp(
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>;
const from = appVersion(appRoot, pkg);
const log = (m: string) => console.log(` ${m}`);
if (cmp(from, "0.8.0") < 0) {
log(
`⚠ detected version ${from}: automated migration from below 0.8.0 is no longer supported. ` +
`Only 0.8.x+ migrations will run; review the project manually before relying on this update.`,
);
}
const report =
options.report ??
({
+77
View File
@@ -111,6 +111,33 @@ test("update verification still blocks real doctor errors", () => {
).toEqual([fatal]);
});
test("updateApp refreshes framework-owned files without marking an unverified update complete", () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-update-unverified-"));
mkdirSync(join(root, "app", "pages"), { recursive: true });
writeFileSync(
join(root, "package.json"),
JSON.stringify({
name: "current-app",
dependencies: { "@wrnexus/core": "0.8.5" },
wrnexus: { version: "0.8.5" },
}),
);
try {
const result = updateApp(root, "0.8.6", false);
expect(result).not.toBeNull();
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
expect(pkg.dependencies["@wrnexus/core"]).toBe("^0.8.6");
// updateApp only bumps dependency versions; the `wrnexus.version` marker is
// set later, once install/build/doctor verification actually succeeds.
expect(pkg.wrnexus.version).toBe("0.8.5");
expect(readFileSync(join(root, "public", "llms.txt"), "utf8")).toContain("# WrNexus");
expect(readFileSync(join(root, "CLAUDE.md"), "utf8").length).toBeGreaterThan(0);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("0.8 migration modernizes every WRN source with imports and a review report", () => {
const root = mkdtempSync(join(tmpdir(), "wrnexus-update-current-source-"));
mkdirSync(join(root, "app", "components"), { recursive: true });
@@ -163,3 +190,53 @@ test("0.8 migration modernizes every WRN source with imports and a review report
rmSync(root, { recursive: true, force: true });
}
});
test("updateApp warns when a marker-less project is below 0.8.0, and stays quiet at 0.8.x", () => {
const belowRoot = mkdtempSync(join(tmpdir(), "wrnexus-update-below-"));
const currentRoot = mkdtempSync(join(tmpdir(), "wrnexus-update-current-"));
try {
// No `wrnexus.version` marker and no installed @wrnexus/cli resolves to
// "0.0.0" — the common, benign case (e.g. examples/basic-app).
writeFileSync(
join(belowRoot, "package.json"),
JSON.stringify({ name: "marker-less-app", dependencies: {} }),
);
writeFileSync(
join(currentRoot, "package.json"),
JSON.stringify({
name: "current-app",
dependencies: { "@wrnexus/core": "^0.8.5" },
wrnexus: { version: "0.8.5" },
}),
);
const originalLog = console.log;
const capture = (): string[] => {
const logs: string[] = [];
console.log = (...args: unknown[]) => {
logs.push(args.join(" "));
};
return logs;
};
let belowLogs: string[];
let currentLogs: string[];
try {
belowLogs = capture();
updateApp(belowRoot, "0.8.9", true);
currentLogs = capture();
updateApp(currentRoot, "0.8.9", true);
} finally {
console.log = originalLog;
}
expect(belowLogs.some((l) => l.includes("no longer supported") && l.includes("0.0.0"))).toBe(
true,
);
expect(currentLogs.some((l) => l.includes("no longer supported"))).toBe(false);
} finally {
rmSync(belowRoot, { recursive: true, force: true });
rmSync(currentRoot, { recursive: true, force: true });
}
});
@@ -174,11 +174,11 @@ add(
"| Legacy/earlier approach | Current approach | Compatibility/status |",
"| --- | --- | --- |",
"| Compiler-owned/internal parsing imports | Canonical `@wrnexus/syntax` lexer/parser/AST/diagnostics | Compiler re-exports remain for compatibility; direct internals are deprecated. |",
"| Implicit component discovery everywhere | Explicit imports and generated contracts | Upgraded apps can retain `legacyComponentDiscovery`; unresolved symbols are reported rather than guessed. |",
'| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts remain behind compatibility configuration. |',
"| Implicit component discovery everywhere | Explicit imports and generated contracts | Unresolved symbols are reported rather than guessed; there is no compatibility opt-out. |",
'| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts are no longer supported; the config key that toggled them was removed. |',
"| `@event changed = function` in props | `outputs { changed(payload: Type) }` | v0.6 migration converts declarations; ambiguous payloads become `unknown`. |",
'| `$emit("changed", value)` and `event.detail` | `output.changed(value)` and direct `payload` | Static cases auto-migrated; dynamic emit names require review. |',
"| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; compatibility default can preserve ambiguous behavior. |",
"| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; the legacy default runtime that preserved ambiguous behavior was removed. |",
"| Manually copied CAPTCHA JS/script tags | Package-discovered client runtime/assets | v0.4 removes tags and archives old assets under `.wrnexus/legacy-assets/0.4.0`. |",
"| Package components/routes/assets wired manually | Automatic package/plugin discovery and contribution registry | Current CLI/build/dev server inspect and consume contributions. |",
"| Only scalar/quoted dynamic props | Native arrays/objects and JSX-style unquoted expressions | Quoted expression attributes remain supported. |",
@@ -192,7 +192,7 @@ add(
"| Per-component/global style placement inconsistencies | `style {}` promoted to document head | Current pipeline supports CSP, HMR and CSR navigation. |",
"| Manually maintained API/component knowledge | Generated public API and component references plus validation gates | `check:public-api`, generated-type checks, UI visual contract and package audits detect drift. |",
"",
"Compatibility flags visible in generated/upgraded config include `legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime`. New projects default legacy flags off; migration-created configs may enable them to preserve behavior until source modernization is complete.",
"`legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime` (and the other pre-1.0 compatibility keys) were removed before the first public release; a config that still sets any of them is rejected by validation rather than honored.",
"",
"## 7. Diagnostics, security, and correctness guarantees",
"",