- Bump versions of @vitejs/plugin-react, @vitest/coverage-istanbul, @vitest/coverage-v8, and @vitest/ui to their beta releases. - Upgrade Vite and Vitest to their respective beta versions. - Adjust Vite configuration to disable code splitting for improved React initialization stability.
51 KiB
Major Dependency Upgrade Plan — ESLint v10, TypeScript 6.0, Vite 8
Date: 2026-03-12 Author: Planning Agent Status: Ready for Review Confidence Score: 82% (High for ESLint v10 + TS 6.0; Medium for Vite 8 — beta with Rolldown migration)
1. Executive Summary
This plan covers the upgrade of three major frontend toolchain dependencies in the Charon project:
| Dependency | Current Version | Target Version | Status | Risk |
|---|---|---|---|---|
| ESLint | ^9.39.3 <10.0.0 |
^10.0.0 |
Released | Medium — plugin compat gate |
| TypeScript | ^5.9.3 |
^6.0.0 |
Beta (Feb 11) / RC (Mar 6) | Medium — 17+ deprecations |
| Vite | ^7.3.1 |
8.0.0-beta.18 |
Beta (Dec 3, 2025) | High — beta, Rolldown replaces Rollup+esbuild |
Key Findings
-
ESLint v10 is released with a comprehensive migration guide. The primary blocker is a note in
lefthook.yml: "ESLint pinned at v9.x.x — do not upgrade until react-hooks plugin supports v10." Theeslint-plugin-react-hooks@7.0.1must be verified for ESLint v10 compatibility before proceeding. -
TypeScript 6.0 is real (Beta: Feb 11, 2026; RC: Mar 6, 2026). It is explicitly designed as a bridge release between TS 5.9 and the native Go-based TS 7.0. It introduces 17+ deprecations/breaking changes (new defaults for
strict,module,target,types,rootDir; removal ofoutFile, legacy module systems; deprecatedbaseUrl,moduleResolution: node). Charon's currenttsconfig.jsonis well-positioned — it already usesmoduleResolution: bundler,strict: true, andmodule: ESNext. The critical impact is thetypesdefault changing to[]. -
Vite 8 exists as
8.0.0-beta.18(announced Dec 3, 2025). The headline change is Rolldown replaces both Rollup and esbuild. JS transforms and minification now use Oxc; CSS minification uses Lightning CSS. Thebuild.rollupOptionsconfig key is deprecated in favor ofbuild.rolldownOptions, andoutput.manualChunks(object form) is removed. Charon'svite.config.tsusesrollupOptionswithinlineDynamicImports: true— both need migration. Ecosystem packages (@vitejs/plugin-react,vitest) require beta versions for Vite 8 compatibility.
Recommended Execution Order
PR-1: TypeScript 6.0 upgrade (fewer external dependencies, most self-contained)
PR-2: ESLint v10 upgrade (blocked on plugin compat verification)
PR-3: Vite 8 upgrade (beta — stacked on PR-1 + PR-2 branch)
2. Current Dependency Inventory
Root package.json (/projects/Charon/package.json)
| Package | Current Version | Category |
|---|---|---|
typescript |
^5.9.3 |
devDependency |
vite |
^7.3.1 |
devDependency |
@playwright/test |
^1.58.2 |
devDependency |
prettier |
^3.8.1 |
devDependency |
markdownlint-cli2 |
^0.21.0 |
devDependency |
Frontend package.json (/projects/Charon/frontend/package.json)
| Package | Current Version | Category |
|---|---|---|
typescript |
^5.9.3 |
devDependency |
vite |
^7.3.1 |
devDependency |
vitest |
^4.0.18 |
devDependency |
eslint |
^9.39.3 <10.0.0 |
devDependency |
@eslint/js |
^9.39.3 <10.0.0 |
devDependency |
@eslint/css |
^1.0.0 |
devDependency |
@eslint/json |
^1.1.0 |
devDependency |
@eslint/markdown |
^7.5.1 |
devDependency |
typescript-eslint |
^8.57.0 |
devDependency |
@typescript-eslint/eslint-plugin |
^8.57.0 |
devDependency |
@typescript-eslint/parser |
^8.57.0 |
devDependency |
@vitejs/plugin-react |
^5.1.4 |
devDependency |
@vitest/coverage-istanbul |
^4.0.18 |
devDependency |
@vitest/coverage-v8 |
^4.0.18 |
devDependency |
@vitest/eslint-plugin |
^1.6.10 |
devDependency |
react |
^19.2.4 |
dependency |
react-dom |
^19.2.4 |
dependency |
react-router-dom |
^7.13.1 |
dependency |
@tanstack/react-query |
^5.90.21 |
dependency |
ESLint Plugin Inventory (18 plugins)
| Plugin | Current Version | ESLint v10 Risk |
|---|---|---|
eslint-plugin-react-hooks |
^7.0.1 |
HIGH — explicit blocker in lefthook.yml |
eslint-plugin-react-compiler |
^19.1.0-rc.2 |
Medium — RC, check compat |
eslint-plugin-react-refresh |
^0.5.2 |
Low |
eslint-plugin-import-x |
^4.16.1 |
Low — modern fork |
eslint-plugin-jsx-a11y |
^6.10.2 |
Medium |
eslint-plugin-security |
^4.0.0 |
Low |
eslint-plugin-sonarjs |
^4.0.2 |
Low |
eslint-plugin-unicorn |
^63.0.0 |
Low — actively maintained |
eslint-plugin-promise |
^7.2.1 |
Low |
eslint-plugin-unused-imports |
^4.4.1 |
Low |
eslint-plugin-no-unsanitized |
^4.1.5 |
Medium |
eslint-plugin-testing-library |
^7.16.0 |
Low |
typescript-eslint |
^8.57.0 |
Low — tracks ESLint closely |
@vitest/eslint-plugin |
^1.6.10 |
Low |
@eslint/css |
^1.0.0 |
Low — official ESLint |
@eslint/json |
^1.1.0 |
Low — official ESLint |
@eslint/markdown |
^7.5.1 |
Low — official ESLint |
Config Files Affected
| File | Impact Area |
|---|---|
frontend/tsconfig.json |
TS 6.0 — types, lib, defaults |
frontend/tsconfig.node.json |
TS 6.0 — minor |
frontend/tsconfig.build.json |
TS 6.0 — extends base |
frontend/eslint.config.js |
ESLint v10 — plugin compat |
eslint.config.js (root) |
ESLint v10 — imports frontend config |
frontend/package.json |
All — version bumps |
package.json (root) |
TS + Vite version bumps |
lefthook.yml |
ESLint v10 — remove pin note |
Dockerfile |
Node.js version (already compatible) |
Infrastructure
- Node.js:
24.14.0-alpine(Dockerfile) — meets all upgrade requirements - No
.npmrcfile exists in the project - Go:
1.26.1(not affected by frontend upgrades)
3. Breaking Changes Analysis
3.1 ESLint v10 Breaking Changes
Source: ESLint v10 Migration Guide
| # | Breaking Change | Impact on Charon | Action Required |
|---|---|---|---|
| 1 | Node.js ≥ v20.19, v22.13, or v24 required | None — already on Node 24.14.0 | None |
| 2 | eslint:recommended updated — 3 new rules: no-unassigned-vars, no-useless-assignment, preserve-caught-error |
May flag new violations in codebase | Fix flagged code or disable rules |
| 3 | New config file lookup — searches from linted file, not cwd | Flat config already used; minor risk for monorepo patterns | Verify root config is found correctly |
| 4 | Old .eslintrc format completely removed |
None — already using flat config | None |
| 5 | JSX references now tracked — fixes no-unused-vars for JSX components |
Positive — fewer false positives | May surface new true positives |
| 6 | eslint-env comments reported as errors |
Search codebase for /* eslint-env */ |
Remove if found |
| 7 | Jiti ≥ v2.2.0 required | Check transitive dep version | May need explicit install |
| 8 | Removed deprecated context members — context.getScope(), context.getAncestors(), etc. |
Affects plugins, not our config directly | All 18 plugins must be compatible |
| 9 | Removed deprecated SourceCode methods |
Same — plugin concern | Plugin compat verification |
| 10 | Program AST node range spans entire source | Unlikely to affect us | None |
Critical Plugin Gate: The eslint-plugin-react-hooks compatibility with ESLint v10 must be verified. The lefthook.yml at line ~98 explicitly states: "NOTE: ESLint pinned at v9.x.x — do not upgrade until react-hooks plugin supports v10."
3.2 TypeScript 6.0 Breaking Changes
Source: TypeScript 6.0 Beta Announcement and 6.0 Deprecation List
Default Value Changes
| Setting | Old Default | New Default | Charon Current | Action |
|---|---|---|---|---|
strict |
false |
true |
true (explicit) |
None — already set |
module |
commonjs |
esnext |
ESNext (explicit) |
None — already set |
target |
es5 |
es2025 (floating) |
ES2022 (explicit) |
None — already set |
types |
["*"] (all @types) |
[] (none) |
Not set | ACTION: Add "types": [] |
rootDir |
inferred | . (tsconfig dir) |
Not set | Verify — no emit, noEmit: true |
noUncheckedSideEffectImports |
false |
true |
Not set | Verify no side-effect import issues |
libReplacement |
true |
false |
Not set | None — improves perf |
Deprecations (with ignoreDeprecations: "6.0" escape hatch)
| Deprecation | Charon Uses? | Impact |
|---|---|---|
target: es5 |
No (ES2022) |
None |
--outFile |
No | None |
--downlevelIteration |
No | None |
--moduleResolution node/node10 |
No (bundler) |
None |
--moduleResolution classic |
No | None |
--baseUrl |
No | None |
module: amd/umd/systemjs |
No (ESNext) |
None |
esModuleInterop: false |
Not explicitly set | None |
allowSyntheticDefaultImports: false |
Not set (true in tsconfig.node) |
None |
alwaysStrict: false |
Not set (strict: true covers) |
None |
Legacy module keyword for namespaces |
No | None |
asserts keyword on imports |
No | None |
no-default-lib directives |
No | None |
New Features Available
| Feature | Relevance |
|---|---|
import defer syntax |
Future use — deferred module evaluation |
--module node20 |
Not needed — using bundler |
es2025 target/lib |
Can update target from ES2022 to ES2025 |
| Temporal types | Available via esnext lib |
dom.iterable included in dom |
Can simplify lib array |
--stableTypeOrdering |
Useful for TS 7.0 migration prep |
| Expandable hovers | Editor UX improvement |
Map.getOrInsert / getOrInsertComputed |
Available via esnext lib |
RegExp.escape |
Available via es2025 lib |
#/ subpath imports |
Available for future module aliasing |
lib.d.ts Changes — ArrayBuffer/Buffer Breaking Change
TypeScript 5.9 introduced a behavioral change where ArrayBuffer is no longer a supertype of several TypedArray types. This may cause errors like:
error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'BufferSource'.
error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
Mitigation: Ensure @types/node is at latest version. This is a 5.9 → 6.0 carryover that must be verified.
3.3 Vite 8 Breaking Changes
Source: Vite 8 Beta Announcement and Migration from v7 Guide
Version: 8.0.0-beta.18 (dist-tag: beta, announced Dec 3, 2025)
Core Architecture Change: Rolldown Replaces Rollup + esbuild
Vite 8's defining change is replacing two bundlers (esbuild for dev transforms, Rollup for production builds) with a single Rust-based toolchain:
| Component | Vite 7 | Vite 8 | Impact on Charon |
|---|---|---|---|
| Bundler | Rollup | Rolldown (1.0.0-rc.8) |
rollupOptions → rolldownOptions |
| JS Transforms | esbuild | Oxc (@oxc-project/runtime@0.115.0) |
esbuild config key deprecated |
| JS Minification | esbuild | Oxc Minifier | Different minification assumptions |
| CSS Minification | esbuild | Lightning CSS (^1.31.1) |
Slightly different output, bundle size may change |
| Dep Optimization | esbuild | Rolldown | optimizeDeps.esbuildOptions deprecated |
Breaking Changes Impacting Charon
| # | Breaking Change | Impact on Charon | Action Required |
|---|---|---|---|
| 1 | Node.js ^20.19.0 || >=22.12.0 required |
None — already on Node 24.14.0 | None |
| 2 | build.rollupOptions deprecated → build.rolldownOptions |
HIGH — vite.config.ts uses rollupOptions |
Rename config key |
| 3 | output.manualChunks object form removed, function form deprecated |
HIGH — config sets manualChunks: undefined |
Remove or migrate to codeSplitting |
| 4 | output.inlineDynamicImports — supported in Rolldown but deprecated in favor of codeSplitting: false (rolldown docs) |
HIGH — config uses inlineDynamicImports: true as temporary workaround |
Migrate to codeSplitting: false; inlineDynamicImports works as fallback |
| 5 | Default browser targets updated (Chrome 107→111, Firefox 104→114, Safari 16.0→16.4) | Low — Charon doesn't set explicit build.target |
None — new defaults are fine |
| 6 | esbuild no longer a direct dependency | Low — Charon doesn't use esbuild config | None |
| 7 | Oxc Minifier replaces esbuild minifier | Low — different assumptions about source code | Test build output; verify no minification breakage |
| 8 | Lightning CSS for CSS minification | Low — may produce slightly different CSS output | Verify CSS output visually |
| 9 | Consistent CommonJS interop — default import behavior changes for CJS modules |
Medium — could affect CJS dependencies (axios, etc.) | Test all runtime imports |
| 10 | Module resolution format sniffing removed — browser/module field heuristic gone |
Low — modern packages use exports field |
Verify no resolution regressions |
| 11 | @vitejs/plugin-react 5.x does NOT support Vite 8 — requires 6.0.0-beta.0 |
HIGH — must upgrade plugin-react | Upgrade to @vitejs/plugin-react@6.0.0-beta.0 |
| 12 | Plugin-react 6.0 uses @rolldown/pluginutils instead of Rollup utils |
Low — internal plugin change | None — handled by plugin upgrade |
New Features Available
| Feature | Relevance to Charon |
|---|---|
Built-in tsconfig paths support (resolve.tsconfigPaths: true) |
Could replace manual alias config if needed |
emitDecoratorMetadata support |
Not needed — Charon doesn't use decorators |
| Performance: 10–30× faster production builds | Direct benefit — faster Docker builds and CI |
| Full Bundle Mode (upcoming) | Future — 3× faster dev server startup |
| Module-level persistent cache (upcoming) | Future — faster rebuilds |
Dockerfile Impact: Rollup Native Skip Flags
The current Dockerfile sets:
ENV npm_config_rollup_skip_nodejs_native=1 \
ROLLUP_SKIP_NODEJS_NATIVE=1
These env vars are Rollup-specific for cross-platform builds. With Vite 8, Rollup is replaced by Rolldown, which uses its own native bindings (@rolldown/binding-linux-x64-musl for Alpine). These env vars become no-ops but do not cause harm. Rolldown's native bindings are installed per-platform by npm's optionalDependencies mechanism — the same mechanism that works for the $BUILDPLATFORM Docker flag.
Action: Remove the Rollup skip flags from Dockerfile and verify cross-platform builds still work. Rolldown includes @rolldown/binding-linux-x64-musl which is exactly what Alpine requires.
4. Compatibility Matrix
ESLint v10 Plugin Compatibility Verification Matrix
Each plugin must be verified before the ESLint v10 upgrade. The agent performing PR-2 must run these checks:
# For each plugin, check peer dependency support
npm info eslint-plugin-react-hooks peerDependencies
npm info eslint-plugin-react-compiler peerDependencies
npm info eslint-plugin-jsx-a11y peerDependencies
npm info eslint-plugin-import-x peerDependencies
npm info eslint-plugin-security peerDependencies
npm info eslint-plugin-sonarjs peerDependencies
npm info eslint-plugin-unicorn peerDependencies
npm info eslint-plugin-promise peerDependencies
npm info eslint-plugin-unused-imports peerDependencies
npm info eslint-plugin-no-unsanitized peerDependencies
npm info eslint-plugin-testing-library peerDependencies
npm info eslint-plugin-react-refresh peerDependencies
npm info @vitest/eslint-plugin peerDependencies
npm info typescript-eslint peerDependencies
npm info @eslint/css peerDependencies
npm info @eslint/json peerDependencies
npm info @eslint/markdown peerDependencies
Decision Gate: If eslint-plugin-react-hooks does NOT support ESLint v10 in its peerDependencies, the ESLint v10 upgrade is BLOCKED. Do not use --legacy-peer-deps or --force as a workaround.
TypeScript 6.0 Ecosystem Compatibility
| Tool | TS 6.0 Compat | Notes |
|---|---|---|
typescript-eslint@8.57.0 |
Likely — tracks TS closely | Verify with npm install |
vite@7.3.1 |
Yes — Vite uses esbuild/swc, not tsc directly | Type-check is separate |
vitest@4.0.18 |
Yes — same reasoning | Type-check is separate |
@vitejs/plugin-react@5.1.4 |
Yes | No TS compiler dependency |
react@19.2.4 / @types/react |
Yes | Ensure @types/react latest |
@tanstack/react-query@5.90.21 |
Likely — popular library | TanStack already preparing for TS 6 |
knip@5.86.0 |
Verify | Uses TS programmatic API |
Node.js Compatibility
| Tool | Min Node.js | Charon Node.js | Status |
|---|---|---|---|
| ESLint v10 | 20.19 / 22.13 / 24+ | 24.14.0 | Compatible |
| TypeScript 6.0 | TBD (likely same as 5.9) | 24.14.0 | Compatible |
| Vite 7 | 20.19 / 22.12+ | 24.14.0 | Compatible |
| Vite 8 | 20.19 / 22.12+ | 24.14.0 | Compatible |
Vite 8 Ecosystem Compatibility Matrix
All Vite-related packages must be updated together. Stable releases do not support Vite 8.
| Package | Current Version | Vite 8 Compatible? | Required Version | Override Needed? |
|---|---|---|---|---|
vite |
^7.3.1 |
— | 8.0.0-beta.18 |
No — direct install |
@vitejs/plugin-react |
^5.1.4 |
No (5.x peer: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0) |
6.0.0-beta.0 (peer: vite: ^8.0.0 — verified via npm info) |
No — direct install |
vitest |
^4.0.18 |
No (deps: ^6.0.0 || ^7.0.0) |
4.1.0-beta.6 (deps: ^6.0.0 || ^7.0.0 || ^8.0.0-0) |
No — 4.1.0-beta.6 dep range includes Vite 8 |
@vitest/coverage-istanbul |
^4.0.18 |
No (peer: vitest: 4.0.18) |
4.1.0-beta.6 |
No — matches vitest beta |
@vitest/coverage-v8 |
^4.0.18 |
No (peer: vitest: 4.0.18) |
4.1.0-beta.6 |
No — matches vitest beta |
@vitest/ui |
^4.0.18 |
No (peer: vitest: 4.0.18) |
4.1.0-beta.6 |
No — matches vitest beta |
@vitest/eslint-plugin |
^1.6.10 |
Yes (peer: vitest: *) |
Keep current | No |
@bgotink/playwright-coverage |
^0.3.2 |
Yes (no Vite peer dep) | Keep current | No |
@playwright/test |
^1.58.2 |
Yes (no Vite peer dep) | Keep current | No |
Key constraints:
vitest@4.0.18hasvitein its dependencies (not peer deps) pinned to^6.0.0 || ^7.0.0— this will refuse Vite 8 unless overriddenvitest@4.1.0-beta.6extends this to^6.0.0 || ^7.0.0 || ^8.0.0-0— supports Vite 8 beta@vitejs/plugin-react@6.0.0-beta.0peers onvite: ^8.0.0(verified vianpm info). New optional peer deps:@rolldown/plugin-babelandbabel-plugin-react-compiler(both optional — not required)- All
@vitest/*packages at4.1.0-beta.6must be installed together (strict peer version matching:vitest: 4.1.0-beta.6) - Since
vitest@4.1.0-beta.6already includes^8.0.0-0in itsvitedependency range, and all@vitest/*packages peer to exactvitest: 4.1.0-beta.6, no npm overrides are needed when all packages are installed in lockstep at their beta versions
5. .npmrc Configuration
No .npmrc file currently exists in the project. No changes needed for these upgrades.
If plugin compatibility issues arise during ESLint v10 upgrade, do NOT create an .npmrc with legacy-peer-deps=true. Instead, wait for plugin updates or use granular overrides in package.json:
// package.json — ONLY if a specific plugin ships a fix before updating peerDeps
{
"overrides": {
"eslint-plugin-EXAMPLE": {
"eslint": "^10.0.0"
}
}
}
6. Dockerfile Changes
No Dockerfile changes required for ESLint v10 or TypeScript 6.0.
Vite 8 requires Dockerfile changes — the Rollup native skip flags become irrelevant:
# Set environment to bypass native binary requirement for cross-arch builds
- ENV npm_config_rollup_skip_nodejs_native=1 \
- ROLLUP_SKIP_NODEJS_NATIVE=1
+ # Vite 8 uses Rolldown (Rust native bindings, auto-resolved per platform)
+ # No skip flags needed — Rolldown's optionalDependencies handle cross-platform
Current Dockerfile state (frontend-builder stage):
FROM --platform=$BUILDPLATFORM node:24.14.0-alpine AS frontend-builder
# ...
ENV npm_config_rollup_skip_nodejs_native=1 \
ROLLUP_SKIP_NODEJS_NATIVE=1
RUN npm ci
COPY frontend/ ./
RUN npm run build
- Node.js 24.14.0 meets Vite 8's requirement (
^20.19.0 || >=22.12.0) npm ciwill install Rolldown's@rolldown/binding-linux-x64-muslautomatically on Alpine--platform=$BUILDPLATFORMensures native bindings match the build machine architecture- The
VITE_APP_VERSIONenv var and build output (dist/) remain unchanged - No new environment variables or build args needed
Future (Vite 8): If Vite 8 requires a higher Node.js, upgrade the base image at that time.
7. Config File Changes
7.1 TypeScript 6.0 — frontend/tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
+ // Consider upgrading to "ES2025" (TS 6.0 new target)
"useDefineForClassFields": true,
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "lib": ["ES2022", "DOM"],
+ // DOM.Iterable is now included in DOM as of TS 6.0
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
+
+ /* TS 6.0 — explicit types to override new default of [] */
+ "types": []
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
Key changes:
"types": []— Explicitly set to[]. Charon usesnoEmit: trueand doesn't rely on global@typespackages in the main tsconfig. All types come from explicit imports."lib"simplification — Remove"DOM.Iterable"since TS 6.0 includes it in"DOM"automatically."target"consideration — Can optionally upgrade fromES2022toES2025to accessRegExp.escapeand other ES2025 types natively. Not required.
7.2 TypeScript 6.0 — frontend/tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
- "strict": true
+ "strict": true,
+ "types": []
},
"include": ["vite.config.ts"]
}
Note: allowSyntheticDefaultImports is fine — TS 6.0 deprecates setting it to false, not true. Setting it to true remains valid.
7.3 ESLint v10 — frontend/package.json Version Caps
"devDependencies": {
- "eslint": "^9.39.3 <10.0.0",
+ "eslint": "^10.0.0",
- "@eslint/js": "^9.39.3 <10.0.0",
+ "@eslint/js": "^10.0.0",
// ... all other ESLint plugins may need version bumps
}
7.4 ESLint v10 — frontend/eslint.config.js
Likely no structural changes needed since Charon already uses flat config. Potential changes:
- Remove any
/* eslint-env */comments found in source files - Handle new
eslint:recommendedrules (no-unassigned-vars,no-useless-assignment,preserve-caught-error) - Verify
tseslint.config()wrapper compatibility
7.5 ESLint v10 — lefthook.yml
+ # NOTE: ESLint v10 is supported — plugin compatibility verified on [DATE]
- # NOTE: ESLint pinned at v9.x.x — do not upgrade until react-hooks plugin supports v10.
7.6 TypeScript 6.0 — package.json (Root + Frontend)
"devDependencies": {
- "typescript": "^5.9.3",
+ "typescript": "^6.0.0",
}
8. Phase-by-Phase Implementation Plan
Phase 1: Pre-Upgrade Verification (Both PRs)
Owner: Frontend_Dev agent (or whoever picks up the PR)
-
Snapshot current state:
cd /projects/Charon && npm run lint 2>&1 | tee /tmp/eslint-v9-baseline.log cd /projects/Charon/frontend && npx tsc --noEmit 2>&1 | tee /tmp/tsc-v5-baseline.log -
Verify ESLint plugin compatibility (PR-2 gate):
for plugin in eslint-plugin-react-hooks eslint-plugin-react-compiler \ eslint-plugin-jsx-a11y eslint-plugin-import-x eslint-plugin-security \ eslint-plugin-sonarjs eslint-plugin-unicorn eslint-plugin-promise \ eslint-plugin-unused-imports eslint-plugin-no-unsanitized \ eslint-plugin-testing-library eslint-plugin-react-refresh \ @vitest/eslint-plugin typescript-eslint @eslint/css @eslint/json @eslint/markdown; do echo "=== $plugin ===" && npm info "$plugin" peerDependencies 2>/dev/null done -
Search for
eslint-envcomments:grep -r "eslint-env" frontend/src/ --include="*.ts" --include="*.tsx" --include="*.js"
Phase 2: TypeScript 6.0 Upgrade (PR-1)
Scope: TypeScript version bump + tsconfig adjustments
-
Update
typescriptversion in bothpackage.jsonfiles:- Root:
^5.9.3→^6.0.0 - Frontend:
^5.9.3→^6.0.0
- Root:
-
Apply tsconfig changes (Section 7.1 and 7.2 above):
- Add
"types": []totsconfig.jsonandtsconfig.node.json - Remove
"DOM.Iterable"fromlibarray (now included in"DOM")
- Add
-
Run
npm installto update lock file -
Run type-check and fix any new errors:
cd frontend && npx tsc --noEmit -
Common expected issues:
- Missing types from
@types/*packages (solved by"types": []since we don't use globals) ArrayBuffer/Buffertype narrowing (from TS 5.9 lib.d.ts changes)- Type argument inference changes (may need explicit type annotations)
- Missing types from
-
Run full test suite:
cd frontend && npx vitest run -
Run Playwright E2E tests to verify build works:
# The Dockerfile builds with npm ci && npm run build # Verify: cd frontend && npx vite build
Phase 3: ESLint v10 Upgrade (PR-2)
Prerequisite: Phase 1 plugin verification passes. eslint-plugin-react-hooks must declare ESLint v10 support.
-
Remove version cap and update ESLint packages:
cd frontend npm install -D eslint@^10.0.0 @eslint/js@^10.0.0 -
Update any plugins that need version bumps for ESLint v10 compat
-
Run ESLint and compare against baseline:
cd /projects/Charon && npm run lint 2>&1 | tee /tmp/eslint-v10-output.log diff /tmp/eslint-v9-baseline.log /tmp/eslint-v10-output.log -
Address new violations from updated
eslint:recommended:no-unassigned-vars— variables declared but never assignedno-useless-assignment— assignments that are immediately overwrittenpreserve-caught-error— catch clause variables that are declared but unused
-
Remove any
/* eslint-env */comments found in Phase 1 -
Update
lefthook.yml— remove the ESLint v9 pin note -
Run full test suite to confirm no regressions
Phase 4: Integration Testing
-
Full lint + type-check:
cd /projects/Charon && npm run lint && cd frontend && npx tsc --noEmit -
Frontend build:
cd frontend && npx vite build -
Unit tests:
cd frontend && npx vitest run -
Playwright E2E tests (all browsers):
npx playwright test --project=chromium npx playwright test --project=firefox npx playwright test --project=webkit -
Docker build verification:
docker build -t charon:upgrade-test .
Phase 5: Vite 8 Upgrade (PR-3 — stacked commit on same branch)
Prerequisites: PR-1 (TypeScript 6.0) and PR-2 (ESLint v10) already committed on branch.
Scope: Vite ^7.3.1 → 8.0.0-beta.18, plugin-react ^5.1.4 → 6.0.0-beta.0, vitest ^4.0.18 → 4.1.0-beta.6, vite.config.ts migration, Dockerfile cleanup.
Step 1: Install Vite 8 and ecosystem packages
cd /projects/Charon/frontend
# Core Vite upgrade
npm install -D vite@8.0.0-beta.18
# Plugin-react upgrade (6.x required for Vite 8)
npm install -D @vitejs/plugin-react@6.0.0-beta.0
# Vitest + coverage upgrades (4.1.0-beta.6 supports Vite 8)
npm install -D vitest@4.1.0-beta.6 \
@vitest/coverage-istanbul@4.1.0-beta.6 \
@vitest/coverage-v8@4.1.0-beta.6 \
@vitest/ui@4.1.0-beta.6
Step 2: Update root package.json (direct version bump only — no overrides)
The root package.json only has vite as a direct devDependency (used by Playwright). It does not need overrides — just a version bump:
cd /projects/Charon
npm install -D vite@8.0.0-beta.18
Step 3: Verify peer dep resolution (overrides likely NOT needed)
With all packages at their Vite 8-compatible versions, overrides should not be necessary:
vitest@4.1.0-beta.6depends onvite: ^6.0.0 || ^7.0.0 || ^8.0.0-0— already includes Vite 8@vitejs/plugin-react@6.0.0-beta.0peers onvite: ^8.0.0— matches- All
@vitest/*@4.1.0-beta.6peer onvitest: 4.1.0-beta.6— matches when installed in lockstep
Run npm install and check for peer dep warnings. Only add overrides in frontend/package.json (following the established pattern from TS 6.0 and ESLint v10 phases) if specific transitive packages fail to resolve:
// frontend/package.json — ONLY if npm install reports unresolved peer deps
{
"overrides": {
// ... existing TS and ESLint overrides ...
// Add scoped overrides ONLY for the specific package that fails, e.g.:
// "some-transitive-package": { "vite": "8.0.0-beta.18" }
}
}
Do NOT add a top-level "vite": "8.0.0-beta.18" override — this forces every transitive Vite consumer to resolve to the beta, which is overly broad. If a broad override is truly needed after testing, add it with a comment explaining which transitive package requires it.
Step 4: Migrate vite.config.ts
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
build: {
outDir: 'dist',
sourcemap: true,
- // TEMPORARY: Disable code splitting to diagnose React initialization issue
- // If this works, the problem is module loading order in async chunks
chunkSizeWarningLimit: 2000,
- rollupOptions: {
- output: {
- // Disable code splitting - bundle everything into one file
- manualChunks: undefined,
- inlineDynamicImports: true
- }
- }
+ rolldownOptions: {
+ output: {
+ // Disable code splitting — single bundle for React init stability
+ // codeSplitting: false is the Rolldown-native approach
+ // (inlineDynamicImports is deprecated in Rolldown)
+ codeSplitting: false
+ }
+ }
}
})
Key changes:
rollupOptions→rolldownOptions(Rollup config key deprecated)manualChunks: undefinedremoved (object form no longer supported; was already a no-op sinceundefined)inlineDynamicImports: truereplaced withcodeSplitting: false— the Rolldown-native equivalent. Rolldown supportsinlineDynamicImportsbut marks it as deprecated in favor ofcodeSplitting: false.- The TEMPORARY comment is preserved in intent — this workaround may still be needed
Fallback if codeSplitting: false behaves differently than expected:
build: {
rolldownOptions: {
output: {
// Deprecated but still functional in Rolldown 1.0.0-rc.8
inlineDynamicImports: true
}
}
}
Step 5: Update Dockerfile
Remove the now-irrelevant Rollup native skip flags:
- ENV npm_config_rollup_skip_nodejs_native=1 \
- ROLLUP_SKIP_NODEJS_NATIVE=1
+ # Vite 8: Rolldown native bindings auto-resolved per platform via optionalDependencies
Step 6: Run npm install to regenerate lock file
cd /projects/Charon && npm install
cd /projects/Charon/frontend && npm install
Step 7: Verify builds and tests
# 1. Frontend build (most critical — tests Rolldown bundling)
cd /projects/Charon/frontend && npx vite build
# 2. Type-check (should be unaffected)
cd /projects/Charon/frontend && npx tsc --noEmit
# 3. Lint (should be unaffected)
cd /projects/Charon && npm run lint
# 4. Unit tests
cd /projects/Charon/frontend && npx vitest run
# 5. Docker build (tests Rolldown on Alpine/musl)
docker build -t charon:vite8-test .
# 6. Playwright E2E (tests the built app end-to-end)
cd /projects/Charon && npx playwright test --project=firefox
# 7. CJS interop smoke test (verify axios, react-hot-toast, react-hook-form)
# Run the app and manually verify pages that use CJS dependencies render correctly
# See Step 9 for detailed CJS interop verification checklist
Step 8: Verify build output
# Compare build output size and structure
ls -la frontend/dist/assets/
# Should still produce index-*.js, index-*.css
# With codeSplitting: false, should be a single JS bundle
Step 9: Verify CJS interop (Vite 8 behavior change)
Vite 8's consistent CJS interop may affect imports from CJS packages like axios and react-hot-toast. Explicitly verify these packages work at runtime:
# After Docker build or vite build + preview:
# 1. Verify axios API calls work (CJS package with __esModule flag)
# - Navigate to any page that makes API calls (e.g., Dashboard)
# - Check browser console for "default is not a function" errors
# 2. Verify react-hot-toast renders (CJS package)
# - Trigger a toast notification (e.g., save settings)
# - Check browser console for import errors
# 3. Verify react-hook-form works (CJS interop)
# - Open any form page, submit a form
If any runtime errors appear (e.g., default is not a function), use the temporary escape hatch:
// vite.config.ts — ONLY if CJS interop breaks
export default defineConfig({
legacy: {
inconsistentCjsInterop: true
}
})
Step 10: Update ARCHITECTURE.md
Update the Frontend technology stack table and directory structure to reflect current versions:
### Frontend
| Component | Technology | Version | Purpose |
- | **Build Tool** | Vite | 6.1.9 | Fast bundler and dev server |
+ | **Build Tool** | Vite | 8.0.0-beta.18 | Fast bundler and dev server |
- | **CSS Framework** | Tailwind CSS | 3.x | Utility-first CSS |
+ | **CSS Framework** | Tailwind CSS | 4.2.1 | Utility-first CSS |
- | **Unit Testing** | Vitest | 2.x | Fast unit test runner |
+ | **Unit Testing** | Vitest | 4.1.0-beta.6 | Fast unit test runner |
- | **E2E Testing** | Playwright | 1.50.x | Browser automation |
+ | **E2E Testing** | Playwright | 1.58.2 | Browser automation |
Also fix the directory structure reference:
- │ └── vite.config.js # Vite configuration
+ │ └── vite.config.ts # Vite configuration
9. Rollback Strategy
TypeScript 6.0 Rollback (PR-1)
-
Revert
package.jsonchanges (both root and frontend):- "typescript": "^6.0.0" + "typescript": "^5.9.3" -
Revert
tsconfig.jsonchanges (remove"types": [], restore"DOM.Iterable") -
Run
npm installto restore lock file -
Verify:
cd frontend && npx tsc --noEmit && npx vitest run
Risk: Low — TypeScript version is a devDependency only. No runtime impact. git revert of the PR commit is sufficient.
ESLint v10 Rollback (PR-2)
-
Revert
package.jsonchanges:- "eslint": "^10.0.0" + "eslint": "^9.39.3 <10.0.0" - "@eslint/js": "^10.0.0" + "@eslint/js": "^9.39.3 <10.0.0" -
Revert any plugin version bumps
-
Revert
lefthook.ymlcomment change -
Run
npm installto restore lock file -
Verify:
cd /projects/Charon && npm run lint
Risk: Low — ESLint is a devDependency only. Code changes (fixing new rule violations) are harmless to keep even if ESLint is rolled back.
Vite 8 Rollback (PR-3 commit)
-
Revert
viteversion in bothpackage.jsonfiles:- "vite": "8.0.0-beta.18" + "vite": "^7.3.1" -
Revert ecosystem packages in
frontend/package.json:- "@vitejs/plugin-react": "6.0.0-beta.0" + "@vitejs/plugin-react": "^5.1.4" - "vitest": "4.1.0-beta.6" + "vitest": "^4.0.18" - "@vitest/coverage-istanbul": "4.1.0-beta.6" + "@vitest/coverage-istanbul": "^4.0.18" - "@vitest/coverage-v8": "4.1.0-beta.6" + "@vitest/coverage-v8": "^4.0.18" - "@vitest/ui": "4.1.0-beta.6" + "@vitest/ui": "^4.0.18" -
Revert
vite.config.ts:rolldownOptions→rollupOptions, restoremanualChunks: undefined -
Revert Dockerfile: restore
ROLLUP_SKIP_NODEJS_NATIVE=1env vars -
Remove Vite 8 overrides from
frontend/package.json -
Run
npm installto restore lock file -
Verify:
cd frontend && npx vite build && npx vitest run
Risk: Medium — Vite 8 is a pre-release beta. More likely to need rollback than stable upgrades. Since this is a stacked commit on the same branch, git revert HEAD cleanly removes only the Vite 8 changes while preserving TS 6.0 and ESLint v10.
10. Testing Strategy
Automated Test Coverage
| Test Layer | Tool | What It Validates |
|---|---|---|
| Type checking | tsc --noEmit |
TS 6.0 compatibility, tsconfig changes |
| Linting | eslint |
ESLint v10 config + plugin compat |
| Unit tests | vitest run |
No runtime regressions from TS changes |
| E2E tests | Playwright (Chromium, Firefox, WebKit) | Full app build + functionality |
| Docker build | docker build |
Dockerfile still works with new deps |
| Pre-commit hooks | lefthook |
All hooks pass with new versions |
Specific Test Scenarios for TS 6.0
-
Build output verification:
cd frontend && npx vite build # Verify dist/ output is correct, no new warnings -
Type-check with
--stableTypeOrdering(prep for TS 7.0):cd frontend && npx tsc --noEmit --stableTypeOrdering # Note any differences — these will be real in TS 7.0 -
Verify no
@typesresolution issues:# With types: [], ensure no global type errors appear cd frontend && npx tsc --noEmit 2>&1 | grep "Cannot find"
Specific Test Scenarios for ESLint v10
-
Verify all 18 plugins load without errors:
cd /projects/Charon && npx eslint --print-config frontend/src/App.tsx | head -20 -
Count new violations vs baseline:
npx eslint frontend/src/ --format json 2>/dev/null | jq '.[] | .errorCount' | paste -sd+ | bc -
Verify config lookup works correctly in monorepo:
# Lint a file from the root — should find root eslint.config.js npx eslint frontend/src/App.tsx
11. Commit Slicing Strategy
Decision: 3 Stacked Commits on Single Branch
Trigger reasons:
- Cross-domain changes (TS and ESLint are independent tools)
- Risk isolation (if one breaks, the other can still merge)
- Review size (each PR is focused and reviewable)
- Plugin compatibility gate (ESLint v10 may be blocked)
PR-1: TypeScript 6.0 Upgrade
| Attribute | Detail |
|---|---|
| Scope | TypeScript ^5.9.3 → ^6.0.0, tsconfig changes, fix type errors |
| Files | package.json (root), frontend/package.json, package-lock.json, frontend/tsconfig.json, frontend/tsconfig.node.json, possibly source files with type fixes |
| Dependencies | None — can start immediately |
| Validation Gate | tsc --noEmit passes, vitest run passes, vite build succeeds, Docker build succeeds |
| Estimated Complexity | Medium — mostly defaults are already correct, types: [] is the main change |
| Rollback | git revert + npm install |
PR-2: ESLint v10 Upgrade
| Attribute | Detail |
|---|---|
| Scope | ESLint ^9.x → ^10.0.0, plugin updates, fix new violations, update lefthook |
| Files | frontend/package.json, package-lock.json, frontend/eslint.config.js (if needed), lefthook.yml, source files with new violations |
| Dependencies | BLOCKED until eslint-plugin-react-hooks declares ESLint v10 support |
| Validation Gate | npm run lint passes, all plugins load, no new unhandled violations |
| Estimated Complexity | Medium — depends on plugin ecosystem readiness |
| Rollback | git revert + npm install |
PR-3: Vite 8 Upgrade (stacked commit on same branch)
| Attribute | Detail |
|---|---|
| Scope | Vite 7→8, plugin-react 5→6, vitest 4.0→4.1-beta, vite.config.ts migration, Dockerfile cleanup |
| Files | package.json (root), frontend/package.json, package-lock.json, frontend/vite.config.ts, Dockerfile, ARCHITECTURE.md |
| Dependencies | PR-1 (TS 6.0) and PR-2 (ESLint v10) already committed on branch |
| Validation Gate | vite build succeeds with Rolldown, vitest run passes, Docker build succeeds, Playwright E2E passes |
| Estimated Complexity | High — beta software, bundler engine swap (Rollup→Rolldown), multiple ecosystem packages at beta versions |
| Rollback | git revert HEAD — cleanly removes only the Vite 8 commit |
npm Overrides for PR-3
No overrides expected when all packages are installed at their beta versions in lockstep:
vitest@4.1.0-beta.6deps includevite: ^8.0.0-0— resolves Vite 8 without override@vitest/*@4.1.0-beta.6peer onvitest: 4.1.0-beta.6— satisfied by direct install
If npm install fails, add scoped overrides in frontend/package.json only for the failing package. Do not add a broad "vite": "8.0.0-beta.18" override.
Contingency
- If TS 6.0 stable is delayed past RC, pin to
typescript@6.0.0-rctemporarily - If ESLint v10 plugin compat is blocked for >30 days, consider temporarily dropping the blocker plugin or using
--rulesdirworkaround - If a plugin is permanently abandoned, research replacement plugins
- If Vite 8 beta has blocking regressions,
git revertthe Vite 8 commit and wait for the next beta or stable release — TS 6.0 + ESLint v10 upgrades remain unaffected - If
vitest@4.1.0-beta.6fails tests, try pinningvitest@4.0.18with anoverridesentry for itsvitedependency (force it to accept^8.0.0-0) - If Rolldown's
codeSplitting: falsebehaves differently than expected, try the deprecatedinlineDynamicImports: trueas a fallback, or re-investigate the React initialization issue that motivated the workaround
12. Known Issues & Gotchas
ESLint v10
-
react-hooks plugin blocker —
lefthook.ymlexplicitly states the upgrade is blocked untileslint-plugin-react-hookssupports v10. This is the #1 risk. -
Config file lookup change — ESLint v10 finds config files starting from the linted file and walking up. In Charon's monorepo setup (root
eslint.config.jsimportsfrontend/eslint.config.js), verify the root config is still discovered when lintingfrontend/src/**. -
Jiti dependency — ESLint v10 requires
jiti >= v2.2.0for loading config files. This is typically a transitive dependency but may need explicit installation if conflicts arise. -
Plugin API breakage — Plugins that use deprecated
context.getScope(),context.getAncestors(),context.parserOptions, orcontext.parserPathwill break. All 18 plugins must be verified.
TypeScript 6.0
-
types: []default — This is the highest-impact change for Charon. Without explicitly setting"types", TS 6.0 will not auto-load any@types/*packages. Since Charon usesnoEmit: trueand explicit imports, this should be fine, but test thoroughly. -
TS 6.0 is a transition release — It is explicitly designed as a bridge to TS 7.0 (native Go port). Adopting TS 6.0 now prepares us for TS 7.0 later. The
ignoreDeprecations: "6.0"escape hatch exists if needed. -
typescript-eslintcompatibility — Iftypescript-eslint@8.57.0doesn't support TS 6.0, we may need to update it. Check for a release that adds TS 6.0 support. -
knipcompatibility —knip(^5.86.0) uses TS programmatic API internally. Verify it works with TS 6.0. -
ArrayBuffer/Buffer types — TS 5.9 changes to
lib.d.tsaroundArrayBuffernot being a supertype ofTypedArraymay surface with TS 6.0. Ensure@types/nodeis at latest. -
ts5to6migration tool — The experimental ts5to6 tool can automatically adjustbaseUrlandrootDir. Charon doesn't usebaseUrl, so this is of limited value, but worth knowing about.
Vite 8
-
Beta software —
8.0.0-beta.18is pre-release. Expect edge cases and undocumented behavior. File issues athttps://github.com/vitejs/rolldown-vite/issues. -
Rolldown bundler is RC, not stable — Vite 8 depends on
rolldown@1.0.0-rc.8. Rolldown is feature-complete but may have edge cases with complex chunk splitting configurations. -
codeSplitting: falsereplacesinlineDynamicImports: true—frontend/vite.config.tshas aTEMPORARYworkaround for a "React init issue". Rolldown supportsinlineDynamicImportsbut marks it as deprecated in favor ofcodeSplitting: false. The migration usescodeSplitting: falseas the primary approach;inlineDynamicImports: truecan be used as a deprecated fallback. -
Oxc Minifier assumptions differ from esbuild — The Oxc Minifier makes different assumptions about source code than esbuild. If runtime errors appear after build but not in dev, the minifier is the likely culprit. Use
build.minify: falsetemporarily to diagnose. -
CJS interop behavior change — Vite 8 changes how
defaultimports from CommonJS modules work. Packages likeaxios(CJS) may be affected. Thelegacy.inconsistentCjsInterop: trueescape hatch exists if needed. -
All ecosystem packages are beta —
@vitejs/plugin-react@6.0.0-beta.0,vitest@4.1.0-beta.6, and all@vitest/*packages are pre-release. They are tightly version-locked (e.g.,@vitest/coverage-v8peers to exactvitest: 4.1.0-beta.6). -
Plugin-react 6.0 API change — The new
@vitejs/plugin-react@6.0.0-beta.0uses@rolldown/pluginutilsinternally instead of@rollup/pluginutils. The public API (react()call in config) appears unchanged. New optional peer deps (@rolldown/plugin-babel,babel-plugin-react-compiler) are not required for Charon's usage. -
Lightning CSS may increase CSS bundle size — Lightning CSS produces slightly different output than esbuild's CSS minifier. Verify CSS output and check for visual regressions.
-
Cross-platform Docker builds — Rolldown uses native Rust bindings per platform (
@rolldown/binding-linux-x64-muslfor Alpine). The--platform=$BUILDPLATFORMDocker flag ensures the correct binding is installed. If cross-arch builds fail, verify the correct@rolldown/binding-*package is being resolved.
13. Risk Assessment
| Risk | Probability | Impact | Mitigation |
|---|---|---|---|
eslint-plugin-react-hooks doesn't support ESLint v10 |
Medium | High — blocks PR-2 entirely | Monitor npm for updates; check GitHub issues |
| Other ESLint plugins break on v10 | Low | Medium — individual plugins can be disabled | Verify all 18 plugins; have disable config ready |
TS 6.0 types: [] causes unexpected errors |
Medium | Low — easy to fix by adding types | Test with tsc --noEmit; add specific types |
typescript-eslint incompatible with TS 6.0 |
Low | Medium — blocks type-aware linting | Check releases; may need to update |
knip breaks with TS 6.0 |
Low | Low — knip is optional tooling |
Test separately; pin if needed |
| TS 6.0 stable delayed | Low | Low — RC already available | Use RC or pin beta |
| Vite 8 beta breaks production build | Medium | High — blocks Docker/deployment | Test vite build thoroughly; rollback with git revert |
| Rolldown CJS interop breaks runtime imports | Medium | Medium — runtime errors on CJS packages | Test all CJS deps (axios, etc.); use legacy.inconsistentCjsInterop escape |
| Oxc Minifier causes runtime errors | Low | High — minification bugs are subtle | Compare dev vs prod behavior; use build.minify: false to diagnose |
vitest@4.1.0-beta.6 incompatible with test suite |
Low | Medium — blocks unit test validation | Pin to 4.0.18 + override vite peer if needed |
@vitejs/plugin-react@6.0.0-beta.0 breaks React HMR |
Low | Medium — dev experience degraded | Rollback to 5.1.4 + Vite 7 if critical |
| Rolldown native binding fails on Alpine cross-build | Low | High — blocks Docker build entirely | Verify @rolldown/binding-linux-x64-musl resolves; fall back to non-cross-platform build |
| Lightning CSS produces visual CSS regressions | Low | Low — cosmetic issues only | Visual diff E2E screenshots |
| Docker build fails after upgrades | Low | Medium — blocks CI/deployment | Test Docker build in PR CI |
| Playwright E2E failures from TS changes | Very Low | High — blocks merge | Run full E2E suite before merge |
Overall Risk: MEDIUM-HIGH
- TypeScript 6.0 is well-characterized and Charon's tsconfig is well-aligned with the new defaults
- ESLint v10 is dependent on ecosystem readiness (plugin compatibility)
- Vite 8 is the highest-risk change — beta software with a complete bundler engine swap (Rollup→Rolldown). The saving grace is that all three upgrades are separate commits on the same branch, enabling surgical rollback of just the Vite 8 commit if needed
Acceptance Criteria
PR-1 (TypeScript 6.0)
typescriptupgraded to^6.0.0in root and frontendpackage.jsontsconfig.jsonupdated withtypes: []and simplifiedlibtsc --noEmitpasses with zero errorsvitest runpasses all testsvite buildproduces correct output- Docker build succeeds
- No new
ignoreDeprecationsusage (clean upgrade)
PR-2 (ESLint v10)
- Plugin compatibility verified for all 18 plugins
eslintand@eslint/jsupgraded to^10.0.0- Version cap (
<10.0.0) removed from both packages npm run lintpasses (new violations fixed)lefthook.ymlpin note removed/updated- All pre-commit hooks pass
PR-3 (Vite 8)
viteupgraded to8.0.0-beta.18in root and frontendpackage.json@vitejs/plugin-reactupgraded to6.0.0-beta.0vitestupgraded to4.1.0-beta.6with matching@vitest/*packagesvite.config.tsmigrated:rollupOptions→rolldownOptions,manualChunksremoved- npm overrides verified: no broad overrides needed (or scoped overrides added with justification)
- Dockerfile: Rollup native skip flags removed
vite buildproduces correct output with Rolldown bundlervitest runpasses all unit teststsc --noEmitstill passes (unchanged from PR-1)- Docker build succeeds with Rolldown on Alpine/musl
- Playwright E2E tests pass (all browsers)
- No CJS interop runtime errors (axios, react-hot-toast, etc.)
- CJS interop verified: axios API calls, react-hot-toast renders, react-hook-form submits work
- CSS output visually correct (Lightning CSS minification)
ARCHITECTURE.mdupdated: Vite 8.0.0-beta.18, Vitest 4.1.0-beta.6, Playwright 1.58.2, Tailwind CSS 4.2.1,vite.config.tsfilename- Pre-commit hooks pass (
lefthook)