From 8868a57a7a8caa65301d7d757cec0ab7bb8104d5 Mon Sep 17 00:00:00 2001 From: Shironex Date: Mon, 31 Aug 2026 20:08:41 +0200 Subject: [PATCH] build(deps): move the app build to vite 8.2.2 (Rolldown) + plugin-react 5.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR B of the vite/vitest stack. PR A left `node_modules/vite` at 7.3.6, so the production build and dev server were still the untested surface; this moves them. Vite 8 is a four-engine swap, not just the dep-optimizer swap the migration plan described: Rolldown replaces Rollup for the production bundle, Oxc replaces esbuild for JS transform and minification, and Lightning CSS replaces esbuild for CSS minification. No config edits were required because the repo configures none of the renamed options (no rollupOptions/manualChunks/esbuild:/worker:/lib build) — not because the API was unchanged. The bump de-duplicates the tree rather than adding to it: the nested `astro/vite` and `vitest/vite` 8.2.2 copies collapse into the single hoisted root, so the lockfile delta is four entries removed and none added, and PR A's `optimizeDeps.esbuildOptions` deprecation warning goes away. All three `optimizeDeps` F3 guards were left byte-identical and remain sufficient under Rolldown's scanner. `test:web` ran twice with a cold `.vite` cache: 517 files / 2953 tests green both times, zero `optimized dependencies changed`. `vite build` drops 3.81s -> 715ms; dist grows 0.75% (Lightning CSS lowering) while the main JS chunk sheds 60.5 kB, and all 322 asset/inter-chunk references resolve with `base: './'` intact for Tauri's custom protocol. Storybook is not a blocker: @storybook/react-vite and @storybook/builder-vite 10.5.10 both declare vite ^5 || ^6 || ^7 || ^8, as does every other vite-peer consumer in the tree. Still owed: a GUI `bun run desktop` smoke test of the Tauri WebView window. --- apps/web/package.json | 4 +- bun.lock | 14 +-- docs/migrations/2026-08-31-vite.md | 132 +++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 13 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index e7e1177e..a9cb215d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -46,7 +46,7 @@ "@tailwindcss/vite": "^4.1.0", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^5.2.0", "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "@vitest/coverage-istanbul": "^4.1.11", @@ -54,7 +54,7 @@ "storybook": "^10.5.5", "tailwindcss": "^4.1.0", "typescript": "^6.0.3", - "vite": "^7.0.0", + "vite": "^8.2.2", "vitest": "^4.1.11", "vitest-browser-react": "^1.0.0" } diff --git a/bun.lock b/bun.lock index aa8ca1b3..005c7c66 100644 --- a/bun.lock +++ b/bun.lock @@ -83,7 +83,7 @@ "@tailwindcss/vite": "^4.1.0", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", - "@vitejs/plugin-react": "^5.0.0", + "@vitejs/plugin-react": "^5.2.0", "@vitest/browser": "^4.1.11", "@vitest/browser-playwright": "^4.1.11", "@vitest/coverage-istanbul": "^4.1.11", @@ -91,7 +91,7 @@ "storybook": "^10.5.5", "tailwindcss": "^4.1.0", "typescript": "^6.0.3", - "vite": "^7.0.0", + "vite": "^8.2.2", "vitest": "^4.1.11", "vitest-browser-react": "^1.0.0", }, @@ -2204,7 +2204,7 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], + "vite": ["vite@8.2.2", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q=="], "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], @@ -2302,8 +2302,6 @@ "astro/magic-string": ["magic-string@1.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g=="], - "astro/vite": ["vite@8.2.2", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q=="], - "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], @@ -2352,8 +2350,6 @@ "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "vitest/vite": ["vite@8.2.2", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q=="], - "@astrojs/markdown-satteri/satteri/@bruits/satteri-darwin-arm64": ["@bruits/satteri-darwin-arm64@0.10.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA=="], "@astrojs/markdown-satteri/satteri/@bruits/satteri-darwin-x64": ["@bruits/satteri-darwin-x64@0.10.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q=="], @@ -2404,8 +2400,6 @@ "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "astro/vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "eslint-plugin-jsx-a11y/minimatch/brace-expansion": ["brace-expansion@1.1.18", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw=="], @@ -2474,8 +2468,6 @@ "unstorage/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], - "vitest/vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "eslint-plugin-jsx-a11y/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "storybook/@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], diff --git a/docs/migrations/2026-08-31-vite.md b/docs/migrations/2026-08-31-vite.md index f4eb9e7c..14a514cf 100644 --- a/docs/migrations/2026-08-31-vite.md +++ b/docs/migrations/2026-08-31-vite.md @@ -268,3 +268,135 @@ Not listed among BC-A1's call sites. `@vitest/browser@4` dropped its `./provider - `.gitignore`: Vitest 4 writes failure screenshots to a new `**/.vitest-attachments/` directory (alongside the already-ignored `**/__screenshots__/`). Ignored, so test runs don't leave untracked binaries. - BC-A2's 36-file `@vitest/browser/context` → `vitest/browser` sweep was included. It required an `eslint --fix` pass afterwards: the new specifier sorts differently under `simple-import-sort`, tripping the lint gate in all 36 files. - BC-A3 and BC-A4 were confirmed correct — no `invocationCallOrder` or automock fixes were needed. + +--- + +## POST-IMPLEMENTATION CORRECTIONS — PR B, as actually landed (2026-08-31) + +Landing PR B surfaced five more errors in the plan body, one of them a material understatement of the +migration's own scope. **Read this alongside the PR A corrections above.** + +### CORRECTION 6 (understated scope): Vite 8 is a FOUR-engine swap, not just the dep optimizer + +BC-B1 describes Vite 8 as replacing "esbuild with Rolldown for dependency pre-bundling/scanning" and +calls the `optimizeDeps` scanner "THE highest-risk item in this whole migration". That is one of four +engine swaps, and not the one that touches the shipped artifact. Vite 8 actually replaces: + +| Concern | Vite 7 | Vite 8 | +|---|---|---| +| Production bundler | **Rollup** | **Rolldown** | +| JS transform | esbuild | Oxc | +| JS minifier | esbuild | Oxc Minifier | +| CSS minifier | esbuild | **Lightning CSS** | +| Dep pre-bundling | esbuild | Rolldown *(the only one PR A already exercised)* | + +Confirmed by the installed package's own dependency set: `vite@7.3.6` depends on `esbuild` + `rollup`; +`vite@8.2.2` depends on `rolldown` + `lightningcss`, and demotes `esbuild` to an **optional peer**. + +Renamed/removed config surfaces that come with it: `build.rollupOptions` → `build.rolldownOptions`, +`worker.rollupOptions` → `worker.rolldownOptions`, the `esbuild:` config option → `oxc:` (with +`esbuild.jsx: 'automatic'` becoming `oxc.jsx: { runtime: 'automatic' }`), removal of the `manualChunks` +object form, removal of `build.rollupOptions.watch.chokidar`, and dropped `system`/`amd` output formats. + +**Why PR B still needed zero config edits:** this repo configures *none* of them. Grepped the whole tree — +no `rollupOptions`, no `manualChunks`, no `esbuild:`/`esbuildOptions`, no `worker:`, no `import.meta.hot`, +no lib/formats build. The correct justification for "no config-shape edits required" is *"the repo uses +none of the renamed options"*, **not** BC-B1's *"the API is unchanged"* — the API did change, this repo +just never touched the parts that moved. + +### CORRECTION 7: PR A did NOT pre-pay the Rolldown risk for the build path + +CORRECTION 1 concludes "the Rolldown risk is already paid and measured". That is true **only for the +dep-optimizer in the test path**. PR A left `node_modules/vite` at 7.3.6, so the production build kept +running Rollup + esbuild-minify + esbuild-CSS-minify the entire time. Rollup→Rolldown bundling, Oxc +minification and Lightning CSS all land for the first time **here, in PR B**. The app build and dev +server were genuinely the new risk surface; the test runner was not. + +### CORRECTION 8: `@vitejs/plugin-react` was ALREADY on 5.2.0 before this PR + +BC-B3 presents `5.0.0 → 5.2.0` as a version move. The manifest range was `^5.0.0`, which had already +semver-resolved to `5.2.0` on the parent branch (verified on disk pre-change). The `package.json` edit is +a **floor tightening only** — it documents the vite-8-capable minimum and changes nothing about what is +installed. The lockfile shows no resolved-version change for this package. + +### CORRECTION 9 (the plan predicted the opposite): vite 8 DE-DUPLICATES the tree + +CORRECTION 1 documents the three-copy split PR A created (root 7.3.6 + `vitest/vite` 8.2.2 + +`astro/vite` 8.2.2). Bumping the app to 8.2.2 collapses all three into the single hoisted root copy. +The lockfile delta for this entire PR is **four entries removed, zero added**: + +``` +- "astro/vite" - "astro/vite/fsevents" +- "vitest/vite" - "vitest/vite/fsevents" +``` + +There is now exactly **one** `vite` in the tree. Installed package count drops accordingly (884 with a +clean `--frozen-lockfile` install; all 11 `@nightcore/*` workspace links present, hoisted linker intact, +`e2e:ring3 --prove` green). So PR B is net-simplifying, not additive — and the "vitest carries its own +vite 8 either way" caveat in CORRECTION 1 stops being true the moment PR B lands. + +### CORRECTION 10: PR A's `esbuildOptions` deprecation warning is RESOLVED by PR B + +The warning CORRECTION 1 quotes (`vite:react-babel` setting the deprecated `optimizeDeps.esbuildOptions`) +came from plugin-react targeting a vite-7 API while vitest ran vite 8. With the app on vite 8 too, +plugin-react 5.2.0 emits the `rolldownOptions` form instead. **Zero occurrences** across both cold-cache +`test:web` runs and the production build. PR B cleans up a warning PR A introduced. + +### CORRECTION 11: rollback + +CORRECTION 1 already voided the plan's original rollback claim. Completing it: reverting PR B does not +reach a clean state either — it restores the **three-copy** vite tree (root 7.3.6 + two nested 8.2.2) +and re-introduces the `esbuildOptions` deprecation warning. Reverting PR A and PR B together is the only +route back to a single-vite-7 tree. + +### Measured results (PR B) + +**Storybook × vite 8 — admitted, not a blocker.** Every vite-peer consumer in the tree accepts `^8.0.0`: +`@storybook/react-vite@10.5.10` and `@storybook/builder-vite@10.5.10` both declare +`^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0`; also `@tailwindcss/vite@4.3.3` (`^5.2.0 || ^6 || ^7 || ^8`), +`@vitejs/plugin-react@5.2.0`, `@joshwooding/vite-plugin-react-docgen-typescript@0.7.0`, `@vitest/mocker`, +`vitefu`, and `vitest@4.1.11`. `astro@7.2.9` requires `^8.0.13` — satisfied by the now-shared 8.2.2. + +**`optimizeDeps` guards needed NO adjustment.** All three locations +(`vite.config.ts` has none; `vitest.config.ts` both projects; `.storybook/main.ts`) were left byte-identical. +The include lists tuned against esbuild's scanner remain sufficient under Rolldown's — now confirmed for +the build path as well as the test path. + +**F3 cold-cache trap did not reproduce.** `bun run test:web` ×2, each preceded by +`rm -rf apps/web/node_modules/.vite`: 517 files / 2953 tests passed both times, **zero** occurrences of +`optimized dependencies changed`. `test:stories` (207 files / 918 tests) likewise clean. + +**Production build is green and materially faster.** `vite build` 3.81s → **715ms** (~5.3×). + +| | vite 7.3.6 | vite 8.2.2 | Δ | +|---|---|---|---| +| dist files | 43 | 53 | +10 (finer code-splitting) | +| dist total | 3,163,690 B | 3,187,272 B | **+0.75%** | +| main JS chunk | 751.94 kB (gzip 229.63) | 691.43 kB (gzip 205.81) | **−60.5 kB / −23.8 kB gzip** | +| main CSS | 109,089 B | 115,465 B | +5.8% (Lightning CSS syntax lowering, documented) | +| CSS files / SVG / html | 2 / 1 / 1 | 2 / 1 / 1 | unchanged | + +Output integrity verified, not assumed: all 21 assets referenced by `index.html` plus **301** inter-chunk +relative references resolve on disk; **zero** absolute `/assets` paths, so `base: './'` still satisfies +Tauri's custom-protocol requirement; `@layer`, `prefers-reduced-motion` and the `--nc-*` custom properties +all survive Lightning CSS minification. Chunk filenames became semantic (`terminal-*`, `settings-*`, +`council-*`, `prreview-*`) where Rollup emitted repeated `index-*`, and Vite 8 additionally emits +`` hints Vite 7 did not. + +**Two new advisory warnings** appear from Rolldown, both pre-existing code conditions rather than +regressions — `[INEFFECTIVE_DYNAMIC_IMPORT]` for `@tauri-apps/plugin-dialog` and `src/components/board/index.ts`, +each dynamically imported somewhere but also statically imported elsewhere, so the dynamic import cannot +split them into their own chunk. Harmless today; a cheap code-splitting win if anyone wants it. The +chunk-size advisory also now points at `build.rolldownOptions.output.codeSplitting` instead of +`manualChunks`. + +**CJS interop (the "Consistent CommonJS Interop" break) is a non-issue here.** Only two default-import +sites exist in `apps/web/src`: `dompurify` and `shiki/**/*.mjs`. `dompurify` ships a `module`/`exports` +ESM build so Vite resolves ESM and the CJS rule never applies; the shiki imports are literal `.mjs`. +No default import of `react`/`react-dom` exists anywhere (only `import type`). + +**Dev-server path smoke-tested non-interactively.** `vite --port 5173 --strictPort` (the exact +`beforeDevCommand` Tauri's `devUrl` polls) reports `VITE v8.2.2 ready in 163 ms`, serves `/` at 200 with +plugin-react's Babel Fast-Refresh preamble injected, transforms `/src/main.tsx` at 200, and serves +`/@vite/client` at 200. **Still owed: a GUI `bun run desktop` run** to confirm the Tauri WebView window +itself renders — that needs an interactive session and was not attempted.