Skip to content

chore(deps): bump vitest 3.2.7 to 4.1.10 on the pinned vite 6 - #180

Merged
tyler-rich merged 1 commit into
devfrom
claude/vitest-upgrade-4-1-10-4g8xg5
Aug 9, 2026
Merged

chore(deps): bump vitest 3.2.7 to 4.1.10 on the pinned vite 6#180
tyler-rich merged 1 commit into
devfrom
claude/vitest-upgrade-4-1-10-4g8xg5

Conversation

@tyler-rich

@tyler-rich tyler-rich commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Step 5 of the eight-step frontend toolchain sweep in
docs/upgrades/frontend-toolchain-86.md. vitest is the only package bumped.
jsdom stays at 26.1.0 (step 6) and vite at 6.4.3 (step 7).

See docs/ARCHIVE.md § Deviations for the full record of this step.

Note on formatting: this body writes TypeScript generics with a space after the
opening bracket — Mock< Procedure > rather than the usual spelling. That is not a
typo. This environment's GitHub ingress strips anything bracket-plus-letter as an HTML
tag, including inside code fences, which silently deleted every generic argument from
the first version of this description. The space defeats the tag heuristic. Read them
as ordinary generics.

Pre-checks, run before anything was edited

  • Step 5's row names vitest alone — "Moves | vitest only (stays on the pinned
    vite@6.4.3)". jsdom is step 6's sole member, so the 5-Vitest / 6-jsdom / 7-Vite order is intact
    and there was nothing to escalate.

  • 4.1.10 is still current. vitest's dist-tags.latest is 4.1.10; the only published version
    beyond it is 5.0.0-beta.7 on the beta tag. No deviation from the document's target was needed.

  • The Vite peer range was re-read at the published manifest, because if Vitest 4 required a Vite
    major the ordering would be wrong. It does not:

    Field on vitest@4.1.10 Value
    peerDependencies.vite ^6.0.0 || ^7.0.0 || ^8.0.0
    peerDependenciesMeta.vite.optional false (a required peer)
    dependencies.vite same range
    engines.node ^20.0.0 || ^22.0.0 || >=24.0.0

    The pinned vite@6.4.3 satisfies it, and CI's Node 24 satisfies the engine floor. No ci.yml or
    Dockerfile change needed.

Config changes

frontend/vite.config.ts needed no edit — as the Step 5 row predicts, and confirmed against the
shipped 4.1.10 tarball rather than the migration guide:

Claim Checked at Verdict
the declare module "vite" block still augments UserConfig with a test key dist/config.d.ts:33 holds — the vitest/config triple-slash reference plus defineConfig from 'vite' still types test
project configs accept extends?: string | true dist/chunks/reporters.d.*.d.ts:3614 holds — vite.config.ts:32,40 are fine
projects is the current spelling same file, :2859 holds — the workspace to projects rename is inert

The rest of v4's breaking surface was checked against the repo, not assumed inert: no coverage,
poolOptions, maxThreads/maxForks, minWorkers, reporters, deps.*,
environmentMatchGlobs/poolMatchGlobs, css or restoreMocks key in the config; zero snapshot
files, so the shadow-root printing change has nothing to act on; no test-options-as-third-argument
call sites. The narrowed default exclude was measured on both versions (v3's five patterns
down to v4's node_modules + .git) and collects nothing new, because both projects' include
globs are confined to src/**.

The one thing the Step 5 row did not predict

The row prices breakage as "Low" and names npm test as what verifies it. npm test was green on
the first run. npm run build was not — its tsc -b half failed at
src/components/settings/OidcLinkCard.test.tsx(65,65) with TS2345: argument of type
Mock< Procedure | Constructable > is not assignable to parameter of type
(data: any, unused: string, url?: string | URL | null | undefined) => void.

Vitest 4 widened vi.fn's type-parameter constraint from Procedure to
Procedure | Constructable — the change that lets vi.spyOn mock constructors. ReturnType< T >
instantiates a generic at its constraint, not its default, so the alias
ReturnType< typeof vi.fn > silently moved from Mock< Procedure > to
Mock< Procedure | Constructable >, whose call signature is a union carrying a construct-only
branch. That no longer matches a plain call signature, and OidcLinkCard.test.tsx used the alias
for a mock passed to .mockImplementation() on a History.replaceState spy — the one position in
the suite demanding an exact signature.

Fixed at that single site by typing the mock against the real method signature
(Mock< typeof window.history.replaceState >), which is more accurate than the alias it replaces.
No as, no any, no @ts-expect-error, and no autofix, in bulk or individually. The generic
form of vi.fn erases to a plain vi.fn() call, so nothing at runtime changed.

The sibling ReturnType< typeof vi.fn > at line 49 still compiles — its mock is only ever asserted
on — and was deliberately left alone rather than swept up.

One semantic change reaches the suite, measured rather than argued

Vitest 4's vi.restoreAllMocks() restores only vi.spyOn spies; Vitest 3's also reset plain
vi.fn() implementations. Measured with a standalone probe run under both versions:

vi.fn() implementation survives restoreAllMocks() vi.spyOn spy restored
vitest@3.2.7 no yes
vitest@4.1.10 yes yes

api/client.test.tsx is structurally inert (no vi.spyOn, no vi.mock factory, no module-level
vi.fn()). OidcLinkCard.test.tsx is the one file that combines a vi.mock factory's vi.fn()s
with restoreAllMocks() in afterEach. Instrumenting the real suite shows the carryover is real
and provably inert: startOidcLink and unlinkOidcIdentity now retain implementations from the
test that sets each one onward, but record zero calls in every later test, and all ten tests set
getOidcLinkStatus's own resolved value before rendering. No test passes for a different reason
than it did on 3.2.7.
The instrumentation was removed before this PR.

Lockfile

346 to 338 packages, every movement attributed to a requirer by parsing both lockfiles and
resolving requirers, not by eyeballing the diff:

  • 2 addedobug@2.1.4 (direct dep of vitest@4.1.10), @standard-schema/spec@1.1.0
    (required by @vitest/expect@4.1.10).
  • 10 removedvite-node + its private cac (replaced by Vite's Module Runner); tinypool
    (v4 removed Tinypool outright); tinyspy; strip-literal + nested js-tokens; and
    check-error, deep-eql, loupe, pathval — the chai-5 subtree orphaned by the move to chai 6.
    Each checked to have no surviving requirer.
  • 13 version bumps — the seven @vitest/* packages, vitest itself, and its closure moving in
    step: chai 5.3.3 to 6.2.2, es-module-lexer 1.7.0 to 2.3.1, std-env 3.10.0 to 4.2.0,
    tinyexec 0.3.2 to 1.3.0, tinyrainbow 2.0.0 to 3.1.1.

Nothing moved that is not vitest or required by it. The one entry that could have been a
problem — es-module-lexer crossing a major, since a single hoisted copy would serve Vite too — was
checked specifically: its only requirer before was vite-node@3.2.4 at ^1.7.0, and after is
vitest at ^2.0.0. Vite bundles its own and declares no dependency on the package. vite reads
6.4.3 and jsdom 26.1.0 in the resolved tree.

lockfileVersion stays 3 and the diff is +131/−209, with no whole-file re-normalisation,
because the lockfile was written with npm 11.19.0 installed into a scratch prefix to match CI's
Node 24 rather than the sandbox's Node 22 / npm 10.9.7. npm ci was then run through that same
npm 11 and the lockfile SHA-256 re-verified unchanged.

Suites

Both sides measured here, each from a clean install.

before (3.2.7) after (4.1.10)
npm run lint clean (11.6 s) clean (9.5 s)
npm run format:check clean clean
npm test 80 tests / 22 files 80 tests / 22 files
npm run build 7,035 modules to index-Vvdzytcz.js 645.14 kB / index-D2wHtcHV.css 201.38 kB identical, same content hashes
npm audit 0 vulnerabilities 0 vulnerabilities

The after-run is from a fresh rm -rf node_modules followed by npm ci. The count comparison was
made per test, not per total
— matching totals could hide a renamed or re-parented test. Both runs
were captured with --reporter=json, reduced to sorted file :: full test name :: status triples,
and the two lists diff empty.

Baseline note: the document's Step 5 row says "expect 21 files / 79 tests". That pair
predates ScanDetailPage.scanIdReset.test.tsx landing on dev; 80/22 is current, as the
step-4 archive entry already records. The row's "17 jsdom tests" is also wrong — the jsdom
project is 18 .test.tsx files carrying 59 tests, against 4 .test.ts files carrying 21 under
Node. Both are flagged in the archive entry rather than edited into the sequence document, since
correcting that document is a maintainer call.

Deliberately not done

No package other than vitest moved — jsdom and vite were not touched, which is the point
of the step boundary. No lint finding was autofixed, in bulk or individually (there were none); the
one type error was fixed by hand at a single site. No production source file changed. No step 6 or
later work was started. docs/upgrades/frontend-toolchain-86.md and docs/ROADMAP.md were not
edited. main was not touched.

Step 5 of the frontend toolchain sweep in
docs/upgrades/frontend-toolchain-86.md. `vitest` is the only package
bumped; jsdom stays at 26.1.0 (step 6) and vite at 6.4.3 (step 7).

vitest@4.1.10 peers `vite: "^6.0.0 || ^7.0.0 || ^8.0.0"` as a required
peer, re-read at the published manifest, so Vitest 4 needs no Vite major
and the 5/6/7 ordering holds. 4.1.10 is still `dist-tags.latest`.

vite.config.ts needed no edit, confirmed against the shipped 4.1.10
tarball: `declare module "vite"` still augments UserConfig with `test`,
`extends?: string | true` survives, and `projects` is already the current
spelling. Nothing else in v4's breaking surface has a consumer here.

One type error had to be fixed, in test code. Vitest 4 widened `vi.fn`'s
type-parameter constraint to `Procedure | Constructable`, so the alias
`ReturnType<typeof vi.fn>` — which instantiates at the constraint, not
the default — no longer satisfies a plain call signature, and `tsc -b`
failed where such a mock is passed to `.mockImplementation()` on a
`History.replaceState` spy. Typed against the real method signature at
that one site; no autofix.

`vi.restoreAllMocks()` also changed meaning (v4 restores only `vi.spyOn`
spies), which reaches OidcLinkCard.test.tsx. Measured on both versions
and instrumented in the real suite: the carryover is real and inert, and
no test passes for a different reason than it did on 3.2.7.

Suites from a fresh `rm -rf node_modules && npm ci`: lint clean,
format:check clean, 80 tests across 22 files — identical to the baseline
per test name and status — audit 0, and a build whose emitted assets
carry the same content hashes as the baseline.

See docs/ARCHIVE.md § Deviations for the full record of this step.
@tyler-rich tyler-rich changed the title chore(deps): bump vitest 3.2.7 -> 4.1.10 on the pinned vite 6 chore(deps): bump vitest 3.2.7 to 4.1.10 on the pinned vite 6 Aug 9, 2026
@tyler-rich
tyler-rich merged commit 306556d into dev Aug 9, 2026
8 checks passed
@tyler-rich
tyler-rich deleted the claude/vitest-upgrade-4-1-10-4g8xg5 branch August 9, 2026 14:04
tyler-rich added a commit that referenced this pull request Aug 9, 2026
…r 8 (#189)

Three Track A edits in docs/ROADMAP.md, each gated on a live check made
this session rather than on a prior session's claim:

- Strike the "Frontend tooling majors from Dependabot #86" item as Done
  2026-08-09, naming all eight sweep PRs (#171, #174, #177, #179, #180,
  #183, #185, #187) and both deliberately-excluded packages (TypeScript 7
  and @types/node 26). All eight squash commits re-confirmed present on
  dev before writing the bullet.

- Strike the GHSA-qwww-vcr4-c8h2 re-cut request: the advisory has already
  been re-cut upstream to >= 7.12.0, < 7.18.2 plus >= 8.0.0, < 8.3.0,
  with the 8.x range untouched. Verified against the record in
  github/advisory-database and independently against the npm registry's
  advisory endpoint, which returns nothing for 7.18.2.

- Reword react-router 7 -> 8 as blocked on a React 19 decision rather
  than on a tooling bump, and move it out of the tooling-majors grouping
  into Longer-term / speculative. react-router@8.3.0 peers react and
  react-dom at >=19.2.7; this repo pins both at 18.3.1.

See docs/ARCHIVE.md section 14 for the dated record of what was verified
for each.
tyler-rich added a commit that referenced this pull request Aug 9, 2026
…a current reason (#193)

Comment-only change to the npm /frontend entry. No ignore rule, group,
schedule, target-branch or any other key is added, removed or modified.

The paragraph's instruction — leave the frontend tooling majors unignored
so Dependabot keeps surfacing them — is still correct. Its stated reason
was not: it called them "the deferred #86 sweep" and said to surface them
"until that PR is done". The sweep completed 2026-08-09 across #171,
#174, #177, #179, #180, #183, #185 and #187, and docs/ROADMAP.md marks
the item Done (verified before writing). So the comment had begun
arguing against its own instruction — a reader who checked the roadmap
would find the work finished and reasonably conclude the exemption had
expired, whose obvious next move is to add the typescript ignore, which
is the one thing that must not happen.

Rewritten so the instruction rests on two current reasons. The tooling
majors stay unignored as ordinary policy: we want to see them, evaluate
them and land them, which is exactly what the sweep did (pointing at
docs/upgrades/frontend-toolchain-86.md rather than restating it).
typescript stays unignored for a sharper reason: TypeScript 7 is wanted,
the blocker is entirely upstream, and the regenerating Dependabot PR
proposing 7.x IS the notification that tells us when typescript-eslint
ships support — an ignore would suppress the signal while changing
nothing about the blocker. The re-check command is inlined so the next
reader can test it without a session, along with the expected shape of
support (a new typescript-eslint MAJOR built against TS 7's ./unstable/*
API, not a point-release range widen).

Verified by parsing rather than by reading: both versions load with
yaml.safe_load to byte-identical canonical JSON (SHA-256 07e71c6c on each
side), and every changed raw line matches ^[+-]\s*#.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant