-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Describe the bug
When using the vmThreads pool, Vitest's host-thread require() matches the module-sync exports condition but then fails to load the resulting .mjs file, throwing Cannot use import statement outside a module.
This is related to but distinct from #7692 — that issue covers Vite's ESM resolver not picking up module-sync. This issue is about vmThreads' custom require() implementation, which has its own condition matching that bypasses Vite's resolve config entirely.
Reproduction
Any package using module-sync in its exports map will trigger this. The simplest case is async-function (v1.0.0), which has:
{
"exports": {
".": [
{
"module-sync": "./require.mjs",
"import": "./index.mjs",
"default": "./index.js"
},
"./index.js"
]
}
}require.mjs is a valid ESM file designed for Node 22.12+'s native require() (which can load ESM synchronously). vmThreads' custom require() resolves to this file via the module-sync condition, then tries to parse it as CJS and fails.
vitest.config.ts:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
pool: "vmThreads",
},
});Error:
SyntaxError: Cannot use import statement outside a module
❯ node_modules/async-function/require.mjs:1
The forks pool does not have this problem — it uses Node's native require() which handles module-sync correctly.
What's been tried
resolve.conditions: ["module-sync"]— No effect. This only controls Vite's ESM resolver, not vmThreads' hostrequire().poolOptions.vmThreads.execArgv: ["--experimental-require-module"]— No effect. The flag applies to Node's native module loader, but vmThreads builds its ownrequire()using V8'svmAPI, bypassing it entirely.- Yarn patches converting
require.mjsto CJS — Works, but is a workaround.
Expected behavior
vmThreads' custom require() should either:
- Not advertise the
module-synccondition if it cannot load ESM, or - Handle ESM when
module-syncresolves to an.mjsfile
Affected packages
This affects any package using module-sync exports. In practice, the most common are ljharb's ecosystem packages:
async-function(viais-async-function→which-builtin-type→reflect.getprototypeof)generator-function(viais-generator-function→which-builtin-type→reflect.getprototypeof)async-generator-function(viais-async-generator-function→which-builtin-type→reflect.getprototypeof)
These are transitive dependencies — they're pulled in by common packages like es-abstract and deep-equal.
See also: ljharb/async-function#1
System Info
System:
OS: macOS
Node: v22.x
Package:
vitest: 4.0.18
Config:
pool: vmThreadsUsed Package Manager
yarn
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- The provided reproduction is a minimal reproducible example of the bug.