vitest-dev / vitest-dev/vitest

in `globalSetup`, wrong module version served in pnpm workspaces

Open
#10,028 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

p3-minor-bug
Dominant language
TypeScript
Stars
17.1k
Forks
2k
Avg merge
1d 22h
Merged PRs (30d)
94

Description

Describe the bug

When a pnpm workspace has multiple versions of the same dependency (e.g., @azure/core-lro@2.7.2 from a published npm package and @azure/core-lro@3.x from a workspace link), vitest serves the wrong version to some importers, causing:

SyntaxError: [vite] The requested module '@azure/core-lro' does not provide an export named 'deserializeState'

Root cause: The configEnvironment hook in vitest's VitestResolver plugin returns early for the __vitest__ environment (line: if (name === "__vitest_vm__" || name === "__vitest__") return;). This skips setting resolve.noExternal = true for the main test environment. Without this, Vite's import analysis plugin treats bare specifiers of node_modules packages as "external" and does not resolve them during transform, leaving raw bare specifiers in the transformed code.

This triggers a cascade of bugs:

  1. Vite's createIsExternal caches externalization decisions by bare specifier (the processedIds map), ignoring the importer. Once @azure/core-lro is deemed external for one importer, ALL importers get the same result.
  2. Vite's module runner caches modules by URL (urlToIdModuleMap). The bare specifier @azure/core-lro maps to whichever version was resolved first (2.7.2), and all subsequent importers get that cached module.
  3. The { cache: true } protocol between client and server doesn't carry module identity. The server resolves the bare specifier to the correct version (3.x), sees it was already transformed, and returns { cache: true }. The client interprets this as "use what you have" and serves the wrong version (2.7.2).

Workaround: Add resolve.dedupe: ["@azure/core-lro"] to vitest.config.ts. This forces Vite's import analysis to resolve the bare specifier from the project root, producing a consistent /@fs/... URL that bypasses all three cache issues.

Reproduction

https://github.com/Azure/azure-sdk-for-js

git clone https://github.com/Azure/azure-sdk-for-js.git
cd azure-sdk-for-js
git checkout cd25924f9c263e012f50a447a770ac68d703d2a4
pnpm install
cd sdk/batch/batch
npx vitest run --no-coverage test/files.spec.ts

Expected: Tests pass (or fail for non-resolution reasons)
Actual: SyntaxError: The requested module '@azure/core-lro' does not provide an export named 'deserializeState'

Steps to reproduce

The dependency tree that triggers this:

  • @azure/batch (test target) depends on:
    • @azure/arm-batch (workspace link) → @azure/core-lro workspace (3.x, has deserializeState)
    • @azure/arm-resources@5.2.0 (published npm) → @azure/core-lro@2.7.2 (no deserializeState)

The arm-resources package uses ESM syntax without "type": "module" in package.json, so vitest's shouldExternalize returns false for its files — they're transformed by Vite. During transform, Vite's import analysis encounters import { ... } from "@azure/core-lro":

  1. shouldExternalize(environment, "@azure/core-lro", "arm-resources/deployments.js") → calls createIsExternal → resolves to 2.7.2 (in node_modules) → canExternalizeFile → true → bare specifier NOT resolved → cached in processedIds["@azure/core-lro"] = true
  2. shouldExternalize(environment, "@azure/core-lro", "arm-batch/pollingHelpers.js")processedIds.has("@azure/core-lro") → returns cached truebare specifier NOT resolved (even though this importer should get 3.x)
  3. Module runner receives __vite_ssr_import__("@azure/core-lro") from both importers → first resolution (2.7.2) cached in urlToIdModuleMap["@azure/core-lro"] → second importer gets stale 2.7.2 → crash
System Info
vitest/4.1.2 linux-x64 node-v22.22.1
vite/7.3.1
pnpm/10.33.0
Used Package Manager

pnpm

Validations

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the VitestResolver plugin's configEnvironment hook and reproduce the issue with the provided Azure SDK checkout and npx vitest run --no-coverage test/files.spec.ts. Trace how the vitest environment sets resolution options and verify that importers using different dependency versions resolve and serve the correct module without the export error.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, vite
Domain
testing, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.