#7157 Yarn PnP Compatibility Error with @cloudflare/vitest-pool-workers
Yarn PnP virtual filesystem not supported. Module fallback uses fs.existsSync/readFileSync which fail for PnP zip archives. No PRs or fixes address this.
Keep open. Add PnP support via Node's findPnpApi in module-fallback.ts.
Analysis Report
Issue Review: cloudflare/workers-sdk#7157
Summary
Yarn PnP (Plug'n'Play) is incompatible with @cloudflare/vitest-pool-workers due to module resolution that assumes traditional node_modules directory structure.
Findings
- Created: 2024-11-02
- Updated: 2025-10-30
- Version: 0.5.24 (reported) -> 0.12.6 (current)
- Component: vitest-pool-workers
- Labels: bug
- Comments: 0
Key Evidence
No PRs or changelog entries reference this issue - searched for #7157, "yarn pnp", "PnP vitest-pool-workers" with no results
No PnP-specific handling in codebase - grep of
module-fallback.tsandconfig/index.tsshows no PnP/Yarn handlingHistorical context - Issue #420 (closed 2022) documented that Wrangler does not support Yarn PnP, with workaround to use
nodeLinker: node-modulesRoot cause identified in source code:
module-fallback.ts:258-281uses Vite'spluginContainer.resolveId()for module resolutionmodule-fallback.ts:269-271falls back to Node'srequire.resolve()for relative specifiers- Neither mechanism properly handles PnP's virtual filesystem where modules exist in
.yarn/cache/*.zipfiles - The error message shows Vite trying to load from physical path
/Users/xxx/@cloudflare/vitest-pool-workerswhich doesn't exist in PnP mode
Error analysis: The error
Failed to load url /path/to/project/@cloudflare/vitest-pool-workersindicates:- Vite's
loadAndTransformfunction (vite-npm-5.4.10in error stack) receives a filesystem path - In PnP mode, packages are stored in
.yarn/berry/cache/*.ziparchives, not in physical directories - The pool's module fallback service returns paths that PnP's virtual filesystem cannot resolve
- Vite's
Recommendation
Status: KEEP OPEN
Reasoning: This is a legitimate, unresolved compatibility issue. Yarn PnP represents a significant portion of the JavaScript ecosystem (~15% of projects per npm/yarn surveys). The issue has clear reproduction steps, a known workaround, and the root cause is identifiable in the codebase. No fixes have been merged since the issue was opened.
Action: Keep open and consider prioritizing based on user demand. The workaround (nodeLinker: node-modules or nodeLinker: pnpm) is documented and functional.
Root Cause Analysis
The issue stems from the module fallback service in vitest-pool-workers not supporting Yarn PnP's virtual filesystem.
Code References
Primary location: packages/vitest-pool-workers/src/pool/module-fallback.ts
viteResolve()function (lines 244-295):
async function viteResolve(
vite: ViteDevServer,
specifier: string,
referrer: string,
isRequire: boolean
): Promise<string> {
const resolved = await vite.pluginContainer.resolveId(specifier, referrer, {
ssr: true,
custom: { "node-resolve": { isRequire } },
});
// ... handles browser external and node: builtins
// For require() of relative specifiers, falls back to Node resolution:
if (isRequire && specifier[0] === ".") {
return require.resolve(specifier, { paths: [referrer] });
}
// ...
}
The vite.pluginContainer.resolveId() call doesn't automatically use PnP resolution unless Vite is configured with PnP support. The fallback to require.resolve() also doesn't work because it's called with paths that don't exist on disk in PnP mode.
maybeGetTargetFilePath()function (lines 209-221):
function maybeGetTargetFilePath(target: string): string | undefined {
if (isFile(target)) {
return target;
}
for (const extension of jsExtensions) {
const targetWithExtension = target + extension;
if (fs.existsSync(targetWithExtension)) {
return targetWithExtension;
}
}
// ...
}
This uses fs.existsSync() which returns false for PnP virtual paths.
- Pool index initialization (
packages/vitest-pool-workers/src/pool/index.ts:675):
service = handleModuleFallbackRequest.bind(undefined, ctx.vitenode.server);
The fallback service is bound to Vite's dev server but doesn't account for PnP resolution.
Why PnP Fails
In Yarn PnP:
- Packages are stored in
.yarn/cache/as zip files - Module resolution uses
.pnp.cjswhich patches Node's resolution - File paths like
/project/@cloudflare/vitest-pool-workersdon't physically exist - Standard
fs.existsSync()andfs.readFileSync()calls fail without PnP's patched require
Proposed Solution
There are two potential approaches:
Option A: Add PnP detection and use yarn-pnp-plugin (Recommended)
Add support for Vite's built-in PnP handling by ensuring the PnP plugin is loaded:
// In module-fallback.ts or config/index.ts
import { findPnpApi } from 'module';
function isPnPEnabled(): boolean {
try {
return findPnpApi(__filename) !== null;
} catch {
return false;
}
}
// In viteResolve or resolve function:
async function viteResolve(
vite: ViteDevServer,
specifier: string,
referrer: string,
isRequire: boolean
): Promise<string> {
// For PnP environments, use the PnP API directly for resolution
if (isPnPEnabled()) {
const pnpApi = findPnpApi(referrer);
if (pnpApi) {
try {
const resolved = pnpApi.resolveRequest(specifier, referrer);
if (resolved) {
return resolved;
}
} catch {
// Fall through to standard resolution
}
}
}
// Existing resolution logic...
const resolved = await vite.pluginContainer.resolveId(specifier, referrer, {
ssr: true,
custom: { "node-resolve": { isRequire } },
});
// ...
}
Option B: Document requirement for vite-plugin-yarn-pnp
Alternatively, require users to add PnP support via Vite config:
// vitest.config.ts
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
import { VitePluginYarnPnp } from "vite-plugin-yarn-pnp";
export default defineWorkersConfig({
plugins: [VitePluginYarnPnp()],
// ... rest of config
});
Recommended Changes
Files to modify:
packages/vitest-pool-workers/src/pool/module-fallback.ts- Add PnP detection using
findPnpApifrom Node'smodulebuiltin - Add PnP-aware resolution fallback before standard resolution
- Update file existence checks to use PnP API when available
- Add PnP detection using
packages/vitest-pool-workers/src/config/index.ts- Optionally: Auto-detect PnP and configure Vite accordingly
- Add PnP-related resolve conditions if needed
packages/vitest-pool-workers/package.json- No new dependencies needed (
findPnpApiis a Node.js builtin since v18.1.0)
- No new dependencies needed (
Implementation Difficulty
Difficulty: Medium
Justification:
- Pro: Node.js provides built-in PnP detection via
findPnpApi - Pro: The resolution logic is centralized in
module-fallback.ts - Pro: Clear error message pointing to the issue location
- Con: Need to understand both Vite's plugin resolution and PnP's resolution API
- Con: Testing requires setting up a Yarn PnP environment
- Con: Edge cases around virtual modules, zip archive access, and workerd's module system
- Con: May need changes to how file contents are read (PnP resolves to zip entries)
Testing Recommendations
Unit Tests:
- Mock
findPnpApito return a PnP API object - Verify
viteResolveuses PnP resolution when available - Test fallback behavior when PnP resolution fails
- Mock
Integration Tests:
- Create a test fixture with Yarn PnP enabled (
nodeLinker: pnpin.yarnrc.yml) - Verify basic test execution works
- Test importing npm packages from tests
- Test importing local modules with relative paths
- Create a test fixture with Yarn PnP enabled (
E2E Test:
- Clone the reproduction repo (https://github.com/ZL-Asica/KumoAuth)
- Remove the
nodeLinkerworkaround - Run
yarn vitest runand verify tests pass
Regression Tests:
- Ensure existing tests still pass with standard
node_modules - Test with pnpm's
node_modulesstructure - Test with npm's
node_modulesstructure
- Ensure existing tests still pass with standard
Suggested Comment
Thank you for reporting this issue. After investigation, we've confirmed that
@cloudflare/vitest-pool-workersdoes not currently support Yarn PnP's virtual filesystem due to how module resolution is implemented.Current Workaround: As you've discovered, setting
nodeLinker: node-modulesornodeLinker: pnpmin your.yarnrc.ymlwill resolve the issue.Root Cause: The pool's module fallback service uses filesystem operations (
fs.existsSync,fs.readFileSync) that don't work with PnP's virtual modules stored in zip archives. Supporting PnP would require using Node'sfindPnpApifor resolution.We're keeping this issue open as a feature request. If you'd like to contribute a fix, the main changes would be in
packages/vitest-pool-workers/src/pool/module-fallback.ts.
Notes & Feedback (0)
No notes yet.