chore(deps): vite 4 → 8 + Rolldown, vitest 4, snapshot-matcher fixes#1
Open
dnplkndll wants to merge 10 commits into
Open
chore(deps): vite 4 → 8 + Rolldown, vitest 4, snapshot-matcher fixes#1dnplkndll wants to merge 10 commits into
dnplkndll wants to merge 10 commits into
Conversation
Bumps vite ^4 → ^5 across replicad, replicad-evaluator, studio, and replicad-app-example. Tags along: vite-plugin-dts ^3 → ^4, vite-plugin-pwa ^0.16 → ^0.21, @vitejs/plugin-react 3.0.1 → ^4.3.4, vitest ^0.28 → ^1.6. vite.config files untouched.
Bumps vite ^6 → ^7, @vitejs/plugin-react ^4 → ^5, vite-plugin-pwa ^0.21 → ^1, vitest ^1 → ^2. Adds workbox-build/workbox-window ^7.4.1 peers required by vite-plugin-pwa 1.x. Vite 7 floor is Node 20.19/22.12 — .nvmrc bumped 20.12.1 → 20.19.0.
Vite 8 ships with Rolldown as the default bundler — replacing esbuild (dev transform) and Rollup (production) with a single Rust toolchain. Plugin API stays Rollup-compatible; vite.config files unchanged across all four major bumps. Studio build wall: 19.6s → 3.3s (~6× faster). replicad lib build: ~2.4s → 1.7s.
45183e6 to
b1ce543
Compare
Earlier upgrade dropped the original 'v' prefix when bumping to 20.19.0. Pure cosmetic — both forms work with nvm.
…rors Three react versions live in the workspace: 18.2.0 (replicad-app-example pins exact), 18.3.1 (studio uses ^), 19.1.0 (transitive via docusaurus). Under Vite 8 + Rolldown's dep optimizer, studio's pre-bundled chunks for mobx-react and @devbookhq/splitter were resolving react against a different instance than the app's, producing 'Invalid hook call. You might have more than one copy of React in the same app' at runtime when mounting the Workbench tree. resolve.dedupe forces both 'react' and 'react-dom' to a single instance across the bundle. Prod build is unaffected (Rolldown already collapses duplicates); the fix is dev-mode-load-time.
vitest 2 and 3 depend on their own copy of Vite rather than taking it as a peer, so vite@8 + vitest@2 resolved two Vite instances (8.0.12 for the packages, 5.4.21 nested under vitest/vite-node). vitest 4 declares Vite as a peer (^6 || ^7 || ^8), collapsing the tree to a single vite@8.0.12. Same failure class as the dual-React instance fixed earlier in studio. Folded into this PR (rather than a follow-up) so Vite 8 never lands with a nested Vite 5. Claude-Session: https://claude.ai/code/session_01A1L7NpgecJPis1mJzxHKDb
Two defects surfaced by the vitest 4 upgrade, both of which let the SVG
suite pass while asserting nothing:
- The snapshot filename was derived from `currentTestName`, which in
vitest 2 carried a leading "<relative-path> > " segment that vitest 4
dropped. Every committed baseline was silently orphaned. The
identifier now strips any file-path prefix, so it depends only on the
test's own name and survives the version change (the snapshot dir is
already scoped per test file, so the prefix was always redundant).
- A missing baseline was auto-written and reported as a pass in normal
`vitest run`; only `--ci` failed it. Combined with the rename above,
the suite reported all-green while regenerating baselines from scratch.
A missing baseline is now a failure unless the run explicitly records
with `-u`, so a renamed or deleted golden file fails loudly.
Claude-Session: https://claude.ai/code/session_01A1L7NpgecJPis1mJzxHKDb
The test shuffled its triangles with `.sort(() => Math.random() - 0.4)` before fusing. fuseAll is commutative so the geometry was identical every run, but toSVG() emits subpaths in input order, so the serialized SVG differed every run while the shape did not — an exact-string snapshot of that is flaky by construction (it passed historically only by luck). Use a fixed non-identity permutation (reverse) so the test still exercises fuseAll on scrambled input while producing a reproducible snapshot. Verified deterministic across three fresh processes. Claude-Session: https://claude.ai/code/session_01A1L7NpgecJPis1mJzxHKDb
The matcher identifier change renames every baseline, so the committed
golden files are re-recorded to match. Breakdown:
- 26 files are pure renames — byte-identical content, only the filename
changed (git detects them as R100). Confirms those tests' output is
unchanged; the rename is purely the vitest-4 identifier fix.
- "build from triangles" content changed because its input order is now
fixed rather than randomised (see prior commit).
- Four "offset vase" baselines re-recorded. These were stale: the vase
is a spline, and offsetting a spline is an iterative approximation
(blueprints/offset.ts) whose control points are sensitive to the
toolchain they were recorded on. The old baselines (recorded 2026-03)
no longer reproduce — coordinates drift at the 4th–5th significant
figure while the geometry is identical (viewBox byte-for-byte equal,
coords agree to 3 dp). Arc/curve output is unaffected: "fuses two
circles" (arc commands) re-records byte-identically, so curve math did
not shift — only the spline-offset approximation.
- One dead baseline removed: "offset vase, with offset -5" had no
corresponding case (the test.each covers -1/1/5/10), a leftover from a
removed parameter.
Note: offset-of-spline snapshots remain toolchain-sensitive. If this suite
ever gains CI, those four cases should assert a geometric invariant rather
than an exact SVG string.
Claude-Session: https://claude.ai/code/session_01A1L7NpgecJPis1mJzxHKDb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step-wise upgrade of Vite (4 → 5 → 6 → 7 → 8) across the four Vite-using packages in the monorepo (
replicad,replicad-evaluator,studio,replicad-app-example). Vite 8 ships with Rolldown as the default bundler — replacing both esbuild (dev transform) and Rollup (production) with a single Rust toolchain. Plugin API stays Rollup-compatible;vite.configfiles needed no behavior changes across all four majors.Direct analog to
ledoent/cad-plmPR sgenoud#43.What changed
Dependency bumps (
packages/*/package.json)vite^4 → ^8 (4 packages).@vitejs/plugin-react3.0.1 / ^4.3.4 → ^6.0.1 (studio,replicad-app-example).vite-plugin-pwa^0.16.7 → ^1.0.3 (studio).vite-plugin-dts^3.5.2 → ^4.5.0 (replicad,replicad-evaluator).vitest^0.28.3 → ^4.1.10 (replicad,replicad-evaluator,replicad-cli) — see the vitest section below.workbox-window^7.3.0 → ^7.4.1 + newworkbox-build^7.4.1 —vite-plugin-pwa1.x peers.Toolchain
.nvmrcv20.12.1 → v20.19.0 — Vite 7+ Node floor.Studio dev-mode fix
packages/studio/vite.config.jsgainsresolve.dedupe: ["react", "react-dom"]. Three React versions live in the workspace (18.2.0, 18.3.1, 19.1.0 — pinned byreplicad-app-example, used bystudio, and pulled transitively byreplicad-docs/docusaurus respectively). Under Vite 8's dep optimizer,studio's pre-bundled chunks formobx-reactand@devbookhq/splitterwere resolving react against a different instance than the app's, producingInvalid hook call. You might have more than one copy of React in the same appat runtime when mounting the Workbench tree. Dedupe is the standard remedy and is a dev-mode-only concern (Rolldown collapses duplicates in prod).Each upgrade major lives in its own commit so any regression bisects to the exact bump. The dedupe fix is a separate commit on top so the review pattern matches.
Build perf (local, identical machine)
pnpm prepare-packageswallpnpm --filter studio buildwallpnpm --filter replicad-app-example buildwallindex-*.js(gzip)replicad.js(lib, raw)Studio's headline 6.5× win is the most visible: production-mode Workbench/index bundles plus PWA manifest generation, with code-splitting that Rolldown produces more aggressively than Rollup did (Monaco workers + Workbench load lazily as separate chunks). Dev startup wasn't measured here; upstream reports ~3× faster.
Surfaces that did NOT need changes
replicad-opencascadejsandreplicad_single.wasmload unchanged.worker.format: "es") carries across.@rollup/browser+rollup-plugin-external-globalsas runtime deps ofreplicad-evaluatorfor in-browser script bundling — orthogonal to the build-time bundler, unaffected.vite-plugin-dtswithrollupTypes: true— uses API Extractor independently of Vite/Rolldown.@rollup/plugin-commonjs,-node-resolve,-json,-terserinpackages/replicad/package.json— Rollup-compatible plugin API in Rolldown means these stay as-is.The chunk-size advisory now reads
build.rolldownOptionsinstead ofrollupOptions— informational, expected.vitest 2 → 4 (folded into this PR)
The initial pass bumped
vitestonly to 2.x, which left the dependency tree carrying two Vite copies:vite@8.0.12for the packages andvite@5.4.21nested undervitest/vite-node, because vitest 2 and 3 depend on their own Vite rather than taking it as a peer. That is the same dual-instance class as the studio React fix above, one layer down. vitest 4 declares Vite as a peer (^6 || ^7 || ^8), so the tree collapses to a singlevite@8.0.12. It's folded in here rather than shipped as a follow-up so Vite 8 never lands alongside a stale nested Vite 5.The upgrade surfaced two defects in the repo's custom SVG snapshot matcher (
packages/replicad/__tests__/toMatchSVGSnapshot.ts), both of which let the SVG suite go green while asserting nothing:currentTestName, which vitest 2 prefixed with the relative test path and vitest 4 does not. Every committed baseline was silently renamed out from under the suite. The identifier now strips any file-path prefix and depends only on the test's own name (the snapshot dir is already per-file, so the prefix was always redundant).vitest run; only--cifailed it. Combined with the rename, the suite reported all-green while regenerating every baseline from scratch. A missing baseline is now a hard failure unless the run explicitly records with-u.One genuinely flaky test fixed.
build from trianglesshuffled its input withMath.random()before fusing. The fused geometry is identical regardless of order, buttoSVG()serializes subpaths in input order, so the exact-string snapshot varied every run — it passed historically only by luck. Input order is now a fixed permutation; verified deterministic across three fresh processes.Baseline re-record. The identifier change renames every baseline, so the golden files are re-recorded: 26 are pure renames (byte-identical, git
R100),build from trianglesreflects its new fixed input, and fouroffset vasebaselines were stale — offsetting a spline is an iterative approximation whose control points are toolchain-sensitive, so those coordinates drift at the 4th–5th significant figure while the geometry is identical (viewBox byte-for-byte equal, coords agree to 3 dp). Arc/curve output is unaffected:fuses two circles(arc commands) re-records byte-identically, confirming curve math didn't shift — only the spline-offset approximation. One dead baseline (offset vase, with offset -5, no matching case) was removed.Rolldown surfaces a couple of new informational warnings during the studio build:
evaluse in Monaco editor — known upstream behavior.BatchedMeshimport missing fromthree@0.155.0(viathree-mesh-bvh) — pre-existing; would resolve on a three.js bump.Verification
pnpm installclean across all four major bumps.pnpm prepare-packagesclean at every step.pnpm --filter studio buildclean; service worker generates.vite@8.0.12in the tree. Full replicad suite verified deterministic across repeated fresh processes.http://localhost:5555/workbench:replicad-vite8-broken.pngbelow.main— editor, toolbar, language dropdown, etc. — seereplicad-vite8-fixed.pngbelow. Both are byte-identical to amainbaseline screenshot (replicad-main-ok.png); the remainingWebGL context could not be createdconsole error is a headless-browser artifact unrelated to the upgrade (it's present onmaintoo).Visual evidence
mainbaseline (pre-upgrade)Out of scope
packages/replicad-threejs-helper— uses standalonerollup@3.10.1(not Vite). Independent migration; deferred..github/workflows/publish-studio.yml) currently usespnpm/action-setup@v2 version: 8with ourpnpm@9.13.2lockfile andactions/setup-node@v3 node-version: 20. Pre-existing and tangentially related; left to a follow-up.rolldownOptions.output.advancedChunksfor the studio bundle — defaults are fine.