Skip to content

chore(core): run workspace package unit tests with vitest - #36896

Draft
FrozenPandaz wants to merge 8 commits into
masterfrom
chore/workspace-vitest
Draft

chore(core): run workspace package unit tests with vitest#36896
FrozenPandaz wants to merge 8 commits into
masterfrom
chore/workspace-vitest

Conversation

@FrozenPandaz

Copy link
Copy Markdown
Contributor

Current Behavior

The @nx/workspace package's 420 unit tests run with Jest through the shared jest.preset.js: pinned to maxWorkers: 1, on top of a custom resolver (scripts/patched-jest-resolver.js), the workspace-wide guards in scripts/unit-test-setup.js, and CJS shims for ESM-only dependencies. A full nx test workspace takes 7m53s.

Expected Behavior

nx test workspace runs the same 420 tests with Vitest 4, inferred through the @nx/vitest plugin, in ~17.5s (~27x faster). Test count, suite count, and every recorded snapshot value are unchanged; the suite is stable over repeated runs. Other packages are unaffected and still infer Jest.

Follows #36754, which moved packages/nx. Two pieces of that migration turned out to be specific to nx — the one package that barely imports its siblings — so this PR adds the shared machinery every other package will need:

  • scripts/vitest-nx-source-resolver.mts replaces scripts/patched-jest-resolver.js. resolve.conditions: ['@nx/nx-source'] is not sufficient on its own: node_modules/nx and node_modules/@nx/* are the published tarballs (dist only), and their exports maps advertise @nx/nx-source entries pointing at source files the tarball does not ship. The plugin maps nx / @nx/* through the local packages/<pkg>/package.json instead, with a file fallback for deep imports no exports entry covers.
  • scripts/vitest-setup.mts ports scripts/unit-test-setup.js (jest-only, jest.doMock) and adds what vitest's execution model requires:
    • the same source mapping on Module._resolveFilename, so node's require and vite's module graph agree — without it a lazy require('@nx/js') fails outright under --conditions=@nx/nx-source;
    • the graph mocks repeated on the CJS channel via Module._load. Generators reach graph builders through lazy require(), which vi.mock cannot see; unmocked, createProjectGraphAsync takes project-graph.lock and deadlocks the worker with no output and no test timeout;
    • NX_ISOLATE_PLUGINS=false, so plugin isolation does not spawn worker subprocesses that are never torn down (two packages/nx specs already carry this note);
    • NX_WORKSPACE_ROOT_PATH under tmp/unit/<pid>, per worker. The jest resolver set a single tmp/unit as a side effect; with parallel workers one shared root makes every worker queue on the same lock.
  • The @clack/prompts shim is kept. It is not only ESM interop: the real library drives a synchronous prompt, so a generator that asks a question blocks the worker forever.

Spec changes are the usual codemod (jest.*vi.*, async mock factories with vi.importActual, xdescribedescribe.skip, vitest type imports) plus vi.mock('child_process', { spy: true }) where a frozen ESM namespace was previously spied on directly.

Snapshots are rekeyed, not rewritten: vitest joins describe and test names with ' > ' where jest used a space, so every key reads as new. All 176 recorded values were diffed against the jest originals after normalizing the separator and match exactly. The one shape change is toThrowErrorMatchingInlineSnapshot, which vitest records as [Error: msg] rather than "msg".

Verification

  • nx test workspace — 420 tests (417 passed, 3 skipped), matching jest exactly; stable across four consecutive runs.
  • nx run-many -t build,lint -p workspace — green.
  • devkit run as a canary — 563 tests, unchanged. A full nx affected is not meaningful here: a new file under scripts/ marks all 94 projects affected, and nothing jest reads was modified (jest.preset.js, scripts/unit-test-setup.js, and scripts/patched-jest-resolver.js are untouched).

Follow-ups, not in this PR

  • packages/nx/src/internal-testing-utils/mock-project-graph.ts logs a vitest deprecation warning ("vi.mock call is not at the top level ... will become an error in a future version") because of its dual-runner if (typeof vi !== 'undefined') branch. Pre-existing; the nx suite hits it too.
  • scripts/jest-mocks/ now serves vitest as well and wants renaming to scripts/test-mocks/ once a second package migrates.
  • Each worker leaves a tmp/unit/<pid> directory behind (gitignored). packages/devkit/jest-setup-nx-workspace-data-dir.js has the mkdtemp + rmSync-on-exit pattern if we want them cleaned up.

Related Issue(s)

N/A


View Polygraph session ↗

@netlify

netlify Bot commented Sep 2, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit bf949c4
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6aa1d4f30b138c0007d30a09
😎 Deploy Preview https://deploy-preview-36896--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Sep 2, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit bf949c4
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6aa1d4f34407ae0008f289fa
😎 Deploy Preview https://deploy-preview-36896--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud

nx-cloud Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit bf949c4

Command Status Duration Result
nx affected --targets=lint,oxlint,test,build,e2... ✅ Succeeded 56m 32s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 4s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 1m 4s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 20s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-09-09 22:53:00 UTC

Ports the jest preset's guards (project-graph and workspace-context mocks, workspaceRoot pinned under tmp/unit) and the patched jest resolver's source resolution to vitest, shared under scripts/ for the next package to reuse.
Mechanical jest.* to vi.* rename, async mock factories with vi.importActual, xdescribe to describe.skip, and vitest type imports.
Vitest joins describe and test names with ' > '; the recorded values are byte-identical.
Written from PR #36754 and corrected against the packages/workspace migration.
Vite resolves the shared setup at the workspace root through the root solution tsconfig, reading every referenced project's tsconfig.json, and prettier resolves .editorconfig from the repo. Both affect the result, so declare them rather than exclude.
At the workspace root, vite resolved the setup against the root solution tsconfig and walked its references, reading all 114 project tsconfigs on every run. A leaf tsconfig beside the files cuts that to one, so the workspace:test inputs no longer have to name them.
…igration

Paths moved to tools/vitest, and adds what the sandbox reports taught: where
the shared setup must live, how to attribute a violation from the report's
process tree, and that oxlint is a separate target from lint.
@FrozenPandaz
FrozenPandaz force-pushed the chore/workspace-vitest branch from 857e2b9 to bf949c4 Compare September 9, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant