fix(test): auto-inline packages that use expect.extend() to fix module instance splitting#1113
Merged
fengmk2 merged 4 commits intovoidzero-dev:mainfrom Mar 23, 2026
Merged
fix(test): auto-inline packages that use expect.extend() to fix module instance splitting#1113fengmk2 merged 4 commits intovoidzero-dev:mainfrom
expect.extend() to fix module instance splitting#1113fengmk2 merged 4 commits intovoidzero-dev:mainfrom
Conversation
✅ Deploy Preview for viteplus-preview canceled.
|
Member
|
@kazupon I fork the reproduce repo to https://github.com/why-reproductions-are-required/vite-plus-jest-dom-repro, can you add it to e2e test case? Ensure that this validation logic will always exist in the future when we modify the patch solution or when vitest can support it later. |
Collaborator
Author
|
@fengmk2 |
fengmk2
approved these changes
Mar 23, 2026
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
Fixes
expect.extend()matchers from third-party packages (e.g.,@testing-library/jest-dom) not being registered on the test runner'sexpectinstance, causing errors likeInvalid Chai property: toBeInTheDocument.resolve #897
Root Cause
When third-party packages call
require('vitest').expect.extend(matchers)internally, the npm override (vitest→@voidzero-dev/vite-plus-test) causes Node.js to load a separate module instance with its ownchaiandexpect.Matchers are registered on this separate instance, not on the test runner's
expect.The root issue is the externalization behavior of vitest's module runner. By default, third-party packages in
node_modulesare externalized — loaded via Node.js nativerequire/import.When an externalized package calls
require('vitest'), Node.js resolves it through the npm override, producing a different module instance with a separatechai.Fix
Patch vitest's
ModuleRunnerTransformplugin during re-packaging (build.ts) to automatically add known affected packages toserver.deps.inlinein theconfigResolvedhook.This forces the Vite module runner to process these packages through its transform pipeline instead of externalizing them to Node.js.
Normal flow (plain vitest — no npm override)
sequenceDiagram participant Runner as Test Runner participant JestDOM as @testing-library/jest-dom participant NodeJS as Node.js require() participant Vitest as vitest module Runner->>Vitest: import vitest (cached by Node.js) Runner->>Runner: createExpect() → globalExpect Note over JestDOM: setupFiles execution JestDOM->>NodeJS: require('vitest') NodeJS-->>JestDOM: Same vitest module (cached) JestDOM->>Vitest: expect.extend(matchers) Note over Vitest: OK: Matchers registered on<br/>the same chai instance Runner->>Runner: expect(el).toBeInTheDocument() OK:Bug flow (vite-plus — npm override splits module instances)
sequenceDiagram participant Runner as Test Runner participant JestDOM as @testing-library/jest-dom participant NodeJS as Node.js require() participant VitestA as vitest (runner instance) participant VitestB as @voidzero-dev/vite-plus-test<br/>(override instance) Runner->>VitestA: import vitest Runner->>Runner: createExpect() with chai-A Note over JestDOM: setupFiles execution (externalized by default) JestDOM->>NodeJS: require('vitest') NodeJS->>NodeJS: npm override rewrites to<br/>@voidzero-dev/vite-plus-test NodeJS-->>JestDOM: Different module instance JestDOM->>VitestB: expect.extend(matchers) Note over VitestB: Matchers registered on chai-B Runner->>Runner: expect(el).toBeInTheDocument() Note over Runner: NG: chai-A has no matchers<br/>Invalid Chai propertyFix flow (with
server.deps.inlinepatch)sequenceDiagram participant Runner as Test Runner participant JestDOM as @testing-library/jest-dom participant ModRunner as Vite Module Runner participant Resolve as vitest:resolve-core participant Vitest as vitest (single instance) Runner->>Vitest: import vitest (cached) Runner->>Runner: createExpect() with chai Note over JestDOM: setupFiles execution (inlined by patch) JestDOM->>ModRunner: require('vitest') ModRunner->>ModRunner: Transform to __vite_ssr_import__ ModRunner->>Resolve: resolve 'vitest' Resolve-->>ModRunner: dist/index.js (same file) ModRunner-->>JestDOM: Same module instance (cached) JestDOM->>Vitest: expect.extend(matchers) Note over Vitest: OK: Matchers registered on<br/>the same chai instance Runner->>Runner: expect(el).toBeInTheDocument() OK:Auto-inlined packages
require('vitest').expect.extend(matchers)via./vitestsubpath@vitest/expectinternally withexpect.extend()expect.extend()on importThese three packages cover 99.5% of weekly downloads among all affected packages.
The list was determined by downloading npm tarballs of all known
expect.extendusing packages and inspecting their source code.