11# ADR 0007: Migrate to Vite+ (Full Vite Toolchain)
22
33** Status** : Partially Implemented
4- ** Last Updated** : 2026-04-01
4+ ** Last Updated** : 2026-04-02
55
66---
77
@@ -17,13 +17,13 @@ Prettier, and Jest with a single dependency that has built-in TypeScript support
1717
1818## Context & Problem
1919
20- ### Current State (after PRs A, B, C1, C2, D, E)
20+ ### Current State (after PRs A, B, C1, C2, D, E, F-plexus )
2121
2222| Concern | ` packages/jaeger-ui ` | ` packages/plexus ` |
2323| ---------------- | ---------------------------------- | ---------------------------------- |
2424| Dev server | Vite 8 | n/a |
2525| Production build | Vite 8 (Rolldown engine) | ✅ dropped |
26- | Testing | Jest 30 + ` babel-jest ` | Jest 30 + ` babel-jest ` |
26+ | Testing | Jest 30 + ` babel-jest ` | ✅ Vitest 4 |
2727| TypeScript | ` tsc ` (type-check only, 1 config) | ` tsc ` (noEmit, source only) |
2828| Linting | ✅ Oxlint (via ` vp lint ` ) | ✅ Oxlint (via ` vp lint ` ) |
2929| Formatting | ✅ Oxfmt (via ` vp fmt ` ) | ✅ Oxfmt (via ` vp fmt ` ) |
@@ -76,8 +76,7 @@ Migrate the monorepo to Vite+ in phases:
76764 . ✅ ** Replace Prettier with Oxfmt** — ` prettier ` removed; ` oxfmt --migrate=prettier ` migrated the config.
77775 . ✅ ** Upgrade TypeScript** — upgraded to 6.0.2; ` moduleResolution ` switched to ` "bundler" ` .
78786 . ✅ ** Consolidate jaeger-ui tsconfigs** — ` tsconfig.lint.json ` deleted; ` tsconfig.json ` is the single config.
79- 7 . ** Replace Jest + Babel with Vitest** — deferred; significant migration effort, but the correct
80- long-term direction.
79+ 7 . 🔶 ** Replace Jest + Babel with Vitest** — plexus done ([ #3690 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3690 ) ); jaeger-ui pending.
8180
8281---
8382
@@ -254,7 +253,7 @@ Upgraded TypeScript from 5.9.3 to 6.0.2. Two fixes required:
254253 ` "exports" ` field in ` package.json ` . ` "bundler" ` accurately reflects how Vite resolves modules.
255254- ` packages/plexus/tsconfig.json ` : added ` "types": ["jest"] ` because TypeScript 6.0 tightened
256255 ` @types ` auto-discovery under ` moduleResolution: "bundler" ` — Jest globals were no longer found
257- automatically.
256+ automatically. (Later changed to ` "types": ["vitest/globals"] ` in PR F when plexus migrated to Vitest.)
258257
259258---
260259
@@ -304,15 +303,54 @@ both of which are unaffected by switching the lint/test tooling.
304303
305304### 8. Both Packages — Replace Jest + Babel with Vitest (PR F)
306305
307- ** Deferred.** The migration is the correct long-term direction but carries significant effort:
308-
309- - ` jest.fn() ` → ` vi.fn() ` across the test suite
310- - ` @testing-library/jest-dom ` import path changes
311- - ` importMetaTransform ` Babel plugin in ` test/babel-transform.js ` is eliminated (Vitest runs native ESM)
312- - Snapshot files may need regeneration
313- - See Unknowns 3–6 for the investigation plan
314-
315- Both packages continue to use Jest 30 + ` babel-jest ` until PR F is ready.
306+ ** Partially done.** ` packages/plexus ` migrated to Vitest in [ #3690 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3690 ) .
307+ ` packages/jaeger-ui ` remains on Jest + Babel and is the next step.
308+
309+ #### plexus migration (✅ complete in [ #3690 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3690 ) )
310+
311+ - Added ` vitest ` , ` @vitest/coverage-v8 ` , ` jsdom ` to devDependencies; removed ` jest ` , ` babel-jest ` ,
312+ ` jest-environment-jsdom ` , all ` @babel/* ` packages, ` @types/jest ` .
313+ - Added ` packages/plexus/vitest.config.ts ` with ` environment: 'jsdom' ` , ` globals: true ` ,
314+ ` moduleNameMapper ` for CSS, and coverage settings.
315+ - Replaced ` packages/plexus/test/jest-per-test-setup.js ` with ` test/vitest-setup.ts ` :
316+ - Imports ` @testing-library/jest-dom/vitest ` for matcher compatibility.
317+ - Sets ` (global as any).jest = vi ` so existing ` jest.fn() ` / ` jest.clearAllMocks() ` calls in test
318+ files continue to work ** without rewriting them** .
319+ - ` jest.mock( ` calls (4 test files + demo) renamed to ` vi.mock( ` — the one API that cannot be aliased
320+ because Vitest's transform hoists ` vi.mock( ` patterns statically before imports, whereas ` jest.mock( `
321+ is left in place and never hoisted.
322+ - Six test files renamed ` .test.js ` → ` .test.jsx ` so Vite/esbuild parses their JSX syntax correctly.
323+ - Mock factory functions updated for Vitest ESM:
324+ - All default-exported modules must be returned as ` { default: MockComponent } ` inside factory bodies.
325+ - ` jest.requireActual() ` replaced with ` async vi.importActual() ` (async in Vitest).
326+ - Constructor mocks passed to ` vi.fn() ` must use regular ` function ` expressions, not arrow functions
327+ (Vitest 4.x rejects arrow functions called with ` new ` ).
328+ - ` vi.fn() ` (not the ` jest ` alias) must be used inside ` vi.mock() ` factory bodies — the alias is not
329+ available in hoisted factory scope.
330+ - Deleted ` test/babel-transform.js ` and ` test/generic-file-transform.js ` (no longer needed).
331+
332+ #### Lessons learned (applicable to jaeger-ui migration)
333+
334+ | Situation | Vitest behaviour | Action required |
335+ | -----------| -----------------| -----------------|
336+ | ` jest.fn() ` , ` jest.clearAllMocks() ` , etc. | Not available by default | Alias: ` global.jest = vi ` in setup file |
337+ | ` jest.mock() ` / ` jest.unmock() ` | Not hoisted by transform | Rename to ` vi.mock() ` / ` vi.unmock() ` |
338+ | ` jest.requireActual(mod) ` | Synchronous, CommonJS | Replace with ` await vi.importActual(mod) ` (async factory) |
339+ | Default-export mocks | Factory can return component directly | Must return ` { default: MockComponent } ` |
340+ | Constructor mocks via ` mockImplementation ` | Arrow functions fail with ` new ` | Use regular ` function() { return {...}; } ` |
341+ | ` vi.fn() ` inside ` vi.mock() ` factory | ` jest ` alias not in scope | Use ` vi.fn() ` explicitly |
342+ | JSX in ` .js ` test files | Vite/esbuild skips JSX transform for ` .js ` | Rename to ` .jsx ` |
343+ | ` require() ` inside test scope | Unavailable in ESM | Use top-level ES ` import ` |
344+
345+ #### jaeger-ui migration (pending)
346+
347+ The same strategy applies but at much larger scale (226 test files, ~ 2600 tests). Additional concerns:
348+
349+ - ` packages/jaeger-ui/test/jest-per-test-setup.js ` mocks browser APIs (` ResizeObserver ` , ` matchMedia ` ,
350+ ` requestAnimationFrame ` ) with ` jest.fn() ` — covered by the ` global.jest = vi ` alias.
351+ - ` importMetaTransform ` Babel plugin in ` test/babel-transform.js ` is eliminated (Vitest runs native ESM).
352+ - Snapshot files may need regeneration — see Unknown 3.
353+ - ` transformIgnorePatterns ` for ESM packages (` redux-actions ` , ` d3-* ` ) become unnecessary — see Unknown 5.
316354
317355---
318356
@@ -341,7 +379,7 @@ transpilation, so the tsconfig value has no effect on builds or the dev server.
341379
342380---
343381
344- ### Unknown 2: Oxlint rule coverage
382+ ### ✅ Unknown 2: Oxlint rule coverage
345383
346384** Risk** : Oxlint may not have direct equivalents for all currently active ESLint rules.
347385
@@ -382,6 +420,9 @@ transpilation, so the tsconfig value has no effect on builds or the dev server.
382420
383421### Unknown 3: Vitest snapshot compatibility
384422
423+ ** Status** : Not yet validated for ` jaeger-ui ` . Plexus has no snapshot tests, so this unknown remains
424+ open for the ` jaeger-ui ` migration.
425+
385426** Risk** : Vitest's snapshot serializer may produce subtly different output for component snapshots compared
386427to Jest's ` jest-snapshot ` . Existing ` .snap ` files may need to be regenerated even if functionally equivalent.
387428
@@ -397,16 +438,23 @@ semantic regressions. After update, all snapshot tests pass.
397438
398439### Unknown 4: Vitest jsdom API coverage for existing test setup
399440
441+ ** Status** : Partially resolved via plexus migration. The ` global.jest = vi ` alias strategy works —
442+ confirmed in plexus where ` jest.fn() ` / ` jest.clearAllMocks() ` calls continue to work without renaming.
443+ ` @testing-library/jest-dom/vitest ` import path is confirmed correct. Remaining jaeger-ui-specific
444+ concerns (browser API mocks, ` process.env.TZ ` , globalSetup isolation) still need validation.
445+
400446** Risk** : The ` packages/jaeger-ui/test/jest-per-test-setup.js ` mocks several browser APIs (` ResizeObserver ` ,
401- ` MessageChannel ` , ` matchMedia ` , ` requestAnimationFrame ` ) using ` jest.fn() ` . Under Vitest these need to be
402- replaced with ` vi.fn() ` . The ` @testing-library/jest-dom ` matchers need the Vitest-compatible import path.
447+ ` MessageChannel ` , ` matchMedia ` , ` requestAnimationFrame ` ) using ` jest.fn() ` . Under Vitest these will be
448+ covered by the ` global.jest = vi ` alias. The ` @testing-library/jest-dom ` matchers need the
449+ Vitest-compatible import path (` @testing-library/jest-dom/vitest ` ).
403450
404451Additionally, the ` globalSetup ` mechanism differs: Jest's ` globalSetup ` runs in the Node context before
405452workers; Vitest's ` globalSetup ` does the same but the process isolation model differs.
406453
407454** Experiment** :
408- 1 . Port the setup file to ` vi.* ` APIs and run a representative subset of tests (e.g., Ant Design component
409- tests, Redux-connected components) to confirm that mocks behave identically.
455+ 1 . Port the setup file to use ` global.jest = vi ` alias (not a full ` vi.* ` rewrite) and run a representative
456+ subset of tests (e.g., Ant Design component tests, Redux-connected components) to confirm that mocks
457+ behave identically.
4104582 . Verify ` process.env.TZ = 'UTC' ` propagates correctly under Vitest's globalSetup.
411459
412460** Success criteria** : All previously-passing tests pass under Vitest without mock-related failures.
@@ -415,6 +463,10 @@ workers; Vitest's `globalSetup` does the same but the process isolation model di
415463
416464### Unknown 5: ` transformIgnorePatterns ` equivalents in Vitest
417465
466+ ** Status** : Confirmed non-issue for plexus (no ESM-only ` node_modules ` deps). Jaeger-ui still needs
467+ validation for ` redux-actions ` , ` d3-zoom ` , ` d3-selection ` , but the expectation remains that Vitest's
468+ native ESM pipeline makes ` transformIgnorePatterns ` unnecessary.
469+
418470** Risk** : Jest required explicit ` transformIgnorePatterns ` to process ESM-only packages in ` node_modules `
419471(` redux-actions ` , ` d3-zoom ` , ` d3-selection ` , ` @jaegertracing/plexus ` ). Vitest's Vite transform pipeline
420472handles ESM natively, so these patterns should be unnecessary. However, some packages may have non-standard
@@ -432,6 +484,8 @@ Babel workaround for `redux-actions` is confirmed unnecessary.
432484
433485### Unknown 6: ` @vitejs/plugin-legacy ` interaction with Vitest
434486
487+ ** Status** : Not yet validated. Applies only to ` jaeger-ui ` .
488+
435489** Risk** : ` packages/jaeger-ui/vite.config.mts ` currently uses ` @vitejs/plugin-legacy ` to emit a legacy
436490browser bundle. Vitest ignores ` transformIndexHtml ` hooks so the plugin should be harmless, but this needs
437491confirming.
@@ -476,15 +530,15 @@ confirm no errors or unexpected HTML injection.
476530| ✅ C2 | Replace Prettier with Oxfmt ([ #3686 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3686 ) ) | None | Done |
477531| ✅ D | Upgrade TypeScript to 6.0.2 ([ #3688 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3688 ) ) | None | Done |
478532| ✅ E | Consolidate ` jaeger-ui ` tsconfigs; remove ` main ` from plexus package.json ([ #3689 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3689 ) ) | Unknown 1 | Done |
479- | F | Migrate Jest → Vitest in both packages; remove Babel test deps | Unknowns 3, 4, 5, 6 | Deferred |
533+ | 🔶 F | Migrate Jest → Vitest in both packages; remove Babel test deps ( [ # 3690 ] ( https://github.com/jaegertracing/jaeger-ui/pull/3690 ) plexus ✅, jaeger-ui pending) | Unknowns 3, 4, 5, 6 | Partial |
480534| G | Update CLAUDE.md, README, CI workflows | None | After F |
481535
482536### Investigation strategy
483537
484538- ** Unknown 2** (Oxlint rules): ✅ Resolved — parallel run completed; rule coverage confirmed; ESLint removed.
485539- ** Unknown 1** (tsconfig): ✅ Resolved — tsconfig files merged; ` tsc-lint ` and Vite build both pass.
486- - ** Unknowns 3–6** (Vitest): Validate together in a single throwaway branch by porting the test setup for
487- one package and running the full suite .
540+ - ** Unknowns 3–6** (Vitest): Partially validated via plexus migration (PR F / # 3690 ). Unknowns 4 and 5
541+ confirmed non-issues for plexus. Unknowns 3 and 6 remain open for jaeger-ui (snapshots and plugin-legacy) .
488542
489543---
490544
0 commit comments