🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

vitest

Package Overview
Dependencies
Maintainers
5
Versions
476
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vitest - npm Package Compare versions

Comparing version
4.1.9
to
4.1.10
+203
dist/chunks/base.B6Opl8PE.js
import { runInThisContext } from 'node:vm';
import * as spyModule from '@vitest/spy';
import { r as resolveTestRunner, a as resolveSnapshotEnvironment, d as detectAsyncLeaks, s as setupChaiConfig } from './index.DXx9Dtk7.js';
import { l as loadEnvironment, e as emitModuleRunner, a as listenForErrors } from './init.k9zZ9sLh.js';
import { N as NativeModuleRunner } from './nativeModuleRunner.BIakptoF.js';
import { T as Traces } from './traces.DT5aQ62U.js';
import { V as VitestEvaluatedModules } from './evaluatedModules.Dg1zASAC.js';
import { s as startVitestModuleRunner, c as createNodeImportMeta } from './startVitestModuleRunner.DB-7oCpn.js';
import { performance as performance$1 } from 'node:perf_hooks';
import { startTests, collectTests } from '@vitest/runner';
import { s as setupCommonEnv, b as startCoverageInsideWorker, c as stopCoverageInsideWorker } from './setup-common.DYx3LtFI.js';
import { g as globalExpect, v as vi } from './test.DNmyFkvJ.js';
import { c as closeInspector } from './inspector.CvyFGlXm.js';
import { createRequire } from 'node:module';
import timers from 'node:timers';
import timersPromises from 'node:timers/promises';
import util from 'node:util';
import { KNOWN_ASSET_TYPES } from '@vitest/utils/constants';
import { i as index } from './index.DdgEv5B1.js';
import { g as getWorkerState, r as resetModules, p as provideWorkerState, a as getSafeWorkerState } from './utils.BX5Fg8C4.js';
// this should only be used in Node
let globalSetup = false;
async function setupGlobalEnv(config, environment) {
await setupCommonEnv(config);
Object.defineProperty(globalThis, "__vitest_index__", {
value: index,
enumerable: false
});
globalExpect.setState({ environment: environment.name });
if (globalSetup) return;
globalSetup = true;
if ((environment.viteEnvironment || environment.name) === "client") {
const _require = createRequire(import.meta.url);
// always mock "required" `css` files, because we cannot process them
_require.extensions[".css"] = resolveCss;
_require.extensions[".scss"] = resolveCss;
_require.extensions[".sass"] = resolveCss;
_require.extensions[".less"] = resolveCss;
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset;
});
process.env.SSR = "";
} else process.env.SSR = "1";
// @ts-expect-error not typed global for patched timers
globalThis.__vitest_required__ = {
util,
timers,
timersPromises
};
if (!config.disableConsoleIntercept) await setupConsoleLogSpy();
}
function resolveCss(mod) {
mod.exports = "";
}
function resolveAsset(mod, url) {
mod.exports = url;
}
async function setupConsoleLogSpy() {
const { createCustomConsole } = await import('./console.3WNpx0tS.js');
globalThis.console = createCustomConsole();
}
// browser shouldn't call this!
async function run(method, files, config, moduleRunner, environment, traces) {
const workerState = getWorkerState();
const [testRunner] = await Promise.all([
traces.$("vitest.runtime.runner", () => resolveTestRunner(config, moduleRunner, traces)),
traces.$("vitest.runtime.global_env", () => setupGlobalEnv(config, environment)),
traces.$("vitest.runtime.coverage.start", () => startCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate })),
traces.$("vitest.runtime.snapshot.environment", async () => {
if (!workerState.config.snapshotOptions.snapshotEnvironment) workerState.config.snapshotOptions.snapshotEnvironment = await resolveSnapshotEnvironment(config, moduleRunner);
})
]);
workerState.onCancel((reason) => {
closeInspector(config);
testRunner.cancel?.(reason);
});
workerState.durations.prepare = performance$1.now() - workerState.durations.prepare;
await traces.$(`vitest.test.runner.${method}`, async () => {
for (const file of files) {
if (config.isolate) {
moduleRunner.mocker?.reset();
resetModules(workerState.evaluatedModules, true);
}
workerState.filepath = file.filepath;
if (method === "run") {
const collectAsyncLeaks = config.detectAsyncLeaks ? detectAsyncLeaks(file.filepath, workerState.ctx.projectName) : void 0;
await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => startTests([file], testRunner));
const leaks = await collectAsyncLeaks?.();
if (leaks?.length) workerState.rpc.onAsyncLeaks(leaks);
} else await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => collectTests([file], testRunner));
// reset after tests, because user might call `vi.setConfig` in setupFile
vi.resetConfig();
// mocks should not affect different files
vi.restoreAllMocks();
}
});
await traces.$("vitest.runtime.coverage.stop", () => stopCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate }));
}
let _moduleRunner;
const evaluatedModules = new VitestEvaluatedModules();
const moduleExecutionInfo = /* @__PURE__ */ new Map();
async function startModuleRunner(options) {
if (_moduleRunner) return _moduleRunner;
process.exit = (code = process.exitCode || 0) => {
throw new Error(`process.exit unexpectedly called with "${code}"`);
};
const state = () => getSafeWorkerState() || options.state;
listenForErrors(state);
if (options.state.config.experimental.viteModuleRunner === false) {
const root = options.state.config.root;
let mocker;
if (options.state.config.experimental.nodeLoader !== false) {
// this additionally imports acorn/magic-string
const { NativeModuleMocker } = await import('./nativeModuleMocker.BkNfQMkH.js');
mocker = new NativeModuleMocker({
async resolveId(id, importer) {
// TODO: use import.meta.resolve instead
return state().rpc.resolve(id, importer, "__vitest__");
},
root,
moduleDirectories: state().config.deps.moduleDirectories || ["/node_modules/"],
traces: options.traces || new Traces({ enabled: false }),
getCurrentTestFilepath() {
return state().filepath;
},
spyModule
});
}
_moduleRunner = new NativeModuleRunner(root, mocker);
return _moduleRunner;
}
_moduleRunner = startVitestModuleRunner(options);
return _moduleRunner;
}
let _currentEnvironment;
let _environmentTime;
/** @experimental */
async function setupBaseEnvironment(context) {
if (context.config.experimental.viteModuleRunner === false) {
const { setupNodeLoaderHooks } = await import('./native.DPzPHdi5.js');
await setupNodeLoaderHooks(context);
}
const startTime = performance.now();
const { environment: { name: environmentName, options: environmentOptions }, rpc, config } = context;
// we could load @vite/env, but it would take ~8ms, while this takes ~0,02ms
if (context.config.serializedDefines) try {
runInThisContext(`(() =>{\n${context.config.serializedDefines}})()`, {
lineOffset: 1,
filename: "virtual:load-defines.js"
});
} catch (error) {
throw new Error(`Failed to load custom "defines": ${error.message}`);
}
const otel = context.traces;
const { environment, loader } = await loadEnvironment(environmentName, config.root, rpc, otel, context.config.experimental.viteModuleRunner);
_currentEnvironment = environment;
const env = await otel.$("vitest.runtime.environment.setup", { attributes: {
"vitest.environment": environment.name,
"vitest.environment.vite_environment": environment.viteEnvironment || environment.name
} }, () => environment.setup(globalThis, environmentOptions || config.environmentOptions || {}));
_environmentTime = performance.now() - startTime;
if (config.chaiConfig) setupChaiConfig(config.chaiConfig);
return async () => {
await otel.$("vitest.runtime.environment.teardown", () => env.teardown(globalThis));
await loader?.close();
};
}
/** @experimental */
async function runBaseTests(method, state, traces) {
const { ctx } = state;
state.environment = _currentEnvironment;
state.durations.environment = _environmentTime;
// state has new context, but we want to reuse existing ones
state.evaluatedModules = evaluatedModules;
state.moduleExecutionInfo = moduleExecutionInfo;
provideWorkerState(globalThis, state);
if (ctx.invalidates) ctx.invalidates.forEach((filepath) => {
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
ctx.files.forEach((i) => {
const filepath = i.filepath;
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
const moduleRunner = await startModuleRunner({
state,
evaluatedModules: state.evaluatedModules,
spyModule,
createImportMeta: createNodeImportMeta,
traces
});
emitModuleRunner(moduleRunner);
await run(method, ctx.files, ctx.config, moduleRunner, _currentEnvironment, traces);
}
export { runBaseTests as r, setupBaseEnvironment as s };

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import { i as init } from './init.k9zZ9sLh.js';
if (!process.send) throw new Error("Expected worker to be run in node:child_process");
// Store globals in case tests overwrite them
const processExit = process.exit.bind(process);
const processSend = process.send.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const processRemoveAllListeners = process.removeAllListeners.bind(process);
// Work-around for nodejs/node#55094
if (process.execArgv.some((execArg) => execArg.startsWith("--prof") || execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"))) processOn("SIGTERM", () => processExit());
processOn("error", onError);
function workerInit(options) {
const { runTests } = options;
init({
post: (v) => processSend(v),
on: (cb) => processOn("message", cb),
off: (cb) => processOff("message", cb),
teardown: () => {
processRemoveAllListeners("message");
processOff("error", onError);
},
runTests: (state, traces) => executeTests("run", state, traces),
collectTests: (state, traces) => executeTests("collect", state, traces),
setup: options.setup
});
async function executeTests(method, state, traces) {
try {
await runTests(method, state, traces);
} finally {
process.exit = processExit;
}
}
}
// Prevent leaving worker in loops where it tries to send message to closed main
// thread, errors, and tries to send the error.
function onError(error) {
if (error?.code === "ERR_IPC_CHANNEL_CLOSED" || error?.code === "EPIPE") processExit(1);
}
export { workerInit as w };
import { isMainThread, parentPort } from 'node:worker_threads';
import { i as init } from './init.k9zZ9sLh.js';
if (isMainThread || !parentPort) throw new Error("Expected worker to be run in node:worker_threads");
function workerInit(options) {
const { runTests } = options;
init({
post: (response) => parentPort.postMessage(response),
on: (callback) => parentPort.on("message", callback),
off: (callback) => parentPort.off("message", callback),
teardown: () => parentPort.removeAllListeners("message"),
runTests: async (state, traces) => runTests("run", state, traces),
collectTests: async (state, traces) => runTests("collect", state, traces),
setup: options.setup
});
}
export { workerInit as w };
import { readFileSync } from 'node:fs';
import { isBuiltin } from 'node:module';
import { pathToFileURL } from 'node:url';
import { resolve } from 'pathe';
import { ModuleRunner, EvaluatedModules } from 'vite/module-runner';
import { b as VitestTransport } from './startVitestModuleRunner.DB-7oCpn.js';
import { e as environments } from './index.DC7d2Pf8.js';
import { serializeValue } from '@vitest/utils/serialize';
import { serializeError } from '@vitest/utils/error';
import { disableDefaultColors } from 'tinyrainbow';
import { T as Traces } from './traces.DT5aQ62U.js';
import { o as onCancel, a as rpcDone, c as createRuntimeRpc } from './rpc.MzXet3jl.js';
import { createStackString, parseStacktrace } from '@vitest/utils/source-map';
import { s as setupInspect } from './inspector.CvyFGlXm.js';
import { V as VitestEvaluatedModules } from './evaluatedModules.Dg1zASAC.js';
import { E as EnvironmentTeardownError } from './utils.BX5Fg8C4.js';
function isBuiltinEnvironment(env) {
return env in environments;
}
const isWindows = process.platform === "win32";
const _loaders = /* @__PURE__ */ new Map();
function createEnvironmentLoader(root, rpc) {
const cachedLoader = _loaders.get(root);
if (!cachedLoader || cachedLoader.isClosed()) {
_loaders.delete(root);
const moduleRunner = new ModuleRunner({
hmr: false,
sourcemapInterceptor: "prepareStackTrace",
transport: new VitestTransport({
async fetchModule(id, importer, options) {
const result = await rpc.fetch(id, importer, "__vitest__", options);
if ("cached" in result) return {
code: readFileSync(result.tmp, "utf-8"),
...result
};
if (isWindows && "externalize" in result)
// TODO: vitest returns paths for external modules, but Vite returns file://
// https://github.com/vitejs/vite/pull/20449
result.externalize = isBuiltin(id) || /^(?:node:|data:|http:|https:|file:)/.test(id) ? result.externalize : pathToFileURL(result.externalize).toString();
return result;
},
async resolveId(id, importer) {
return rpc.resolve(id, importer, "__vitest__");
}
}, new EvaluatedModules(), /* @__PURE__ */ new WeakMap())
});
_loaders.set(root, moduleRunner);
}
return _loaders.get(root);
}
async function loadNativeEnvironment(name, root, traces) {
const packageId = name[0] === "." || name[0] === "/" ? pathToFileURL(resolve(root, name)).toString() : import.meta.resolve(`vitest-environment-${name}`, pathToFileURL(root).toString());
return resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => import(packageId)));
}
function resolveEnvironmentFromModule(name, packageId, pkg) {
if (!pkg || !pkg.default || typeof pkg.default !== "object") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "setup" or/and "setupVM" method.`);
const environment = pkg.default;
if (environment.transformMode != null && environment.transformMode !== "web" && environment.transformMode !== "ssr") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "transformMode" method equal to "ssr" or "web", received "${environment.transformMode}".`);
if (environment.transformMode) {
console.warn(`The Vitest environment ${environment.name} defines the "transformMode". This options was deprecated in Vitest 4 and will be removed in the next major version. Please, use "viteEnvironment" instead.`);
// keep for backwards compat
environment.viteEnvironment ??= environment.transformMode === "ssr" ? "ssr" : "client";
}
return environment;
}
async function loadEnvironment(name, root, rpc, traces, viteModuleRunner) {
if (isBuiltinEnvironment(name)) return { environment: environments[name] };
if (!viteModuleRunner) return { environment: await loadNativeEnvironment(name, root, traces) };
const loader = createEnvironmentLoader(root, rpc);
const packageId = name[0] === "." || name[0] === "/" ? resolve(root, name) : (await traces.$("vitest.runtime.environment.resolve", () => rpc.resolve(`vitest-environment-${name}`, void 0, "__vitest__")))?.id ?? resolve(root, name);
return {
environment: resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => loader.import(packageId))),
loader
};
}
const cleanupListeners = /* @__PURE__ */ new Set();
const moduleRunnerListeners = /* @__PURE__ */ new Set();
function onCleanup(cb) {
cleanupListeners.add(cb);
}
async function cleanup() {
await Promise.all([...cleanupListeners].map((l) => l()));
}
function onModuleRunner(cb) {
moduleRunnerListeners.add(cb);
}
function emitModuleRunner(moduleRunner) {
moduleRunnerListeners.forEach((l) => l(moduleRunner));
}
// Store globals in case tests overwrite them
const processListeners = process.listeners.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const dispose = [];
function listenForErrors(state) {
dispose.forEach((fn) => fn());
dispose.length = 0;
function catchError(err, type, event) {
const worker = state();
// if there is another listener, assume that it's handled by user code
// one is Vitest's own listener
if (processListeners(event).length > 1) return;
const error = serializeValue(err);
if (typeof error === "object" && error != null) {
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
}
state().rpc.onUnhandledError(error, type);
}
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException");
const unhandledRejection = (e) => catchError(e, "Unhandled Rejection", "unhandledRejection");
processOn("uncaughtException", uncaughtException);
processOn("unhandledRejection", unhandledRejection);
dispose.push(() => {
processOff("uncaughtException", uncaughtException);
processOff("unhandledRejection", unhandledRejection);
});
}
const resolvingModules = /* @__PURE__ */ new Set();
async function execute(method, ctx, worker, traces) {
const prepareStart = performance.now();
const cleanups = [setupInspect(ctx)];
// RPC is used to communicate between worker (be it a thread worker or child process or a custom implementation) and the main thread
const rpc = ctx.rpc;
try {
// do not close the RPC channel so that we can get the error messages sent to the main thread
cleanups.push(async () => {
await Promise.all(rpc.$rejectPendingCalls(({ method, reject }) => {
reject(new EnvironmentTeardownError(`[vitest-worker]: Closing rpc while "${method}" was pending`));
}));
});
const state = {
ctx,
evaluatedModules: new VitestEvaluatedModules(),
resolvingModules,
moduleExecutionInfo: /* @__PURE__ */ new Map(),
config: ctx.config,
environment: null,
durations: {
environment: 0,
prepare: prepareStart
},
rpc,
onCancel,
onCleanup: onCleanup,
providedContext: ctx.providedContext,
onFilterStackTrace(stack) {
return createStackString(parseStacktrace(stack));
},
metaEnv: createImportMetaEnvProxy()
};
const methodName = method === "collect" ? "collectTests" : "runTests";
if (!worker[methodName] || typeof worker[methodName] !== "function") throw new TypeError(`Test worker should expose "runTests" method. Received "${typeof worker.runTests}".`);
await worker[methodName](state, traces);
} finally {
await rpcDone().catch(() => {});
await Promise.all(cleanups.map((fn) => fn())).catch(() => {});
}
}
function run(ctx, worker, traces) {
return execute("run", ctx, worker, traces);
}
function collect(ctx, worker, traces) {
return execute("collect", ctx, worker, traces);
}
async function teardown() {
await cleanup();
}
const env = process.env;
function createImportMetaEnvProxy() {
// packages/vitest/src/node/plugins/index.ts:146
const booleanKeys = [
"DEV",
"PROD",
"SSR"
];
return new Proxy(env, {
get(_, key) {
if (typeof key !== "string") return;
if (booleanKeys.includes(key)) return !!process.env[key];
return process.env[key];
},
set(_, key, value) {
if (typeof key !== "string") return true;
if (booleanKeys.includes(key)) process.env[key] = value ? "1" : "";
else process.env[key] = value;
return true;
}
});
}
const __vitest_worker_response__ = true;
const memoryUsage = process.memoryUsage.bind(process);
let reportMemory = false;
let traces;
/** @experimental */
function init(worker) {
worker.on(onMessage);
if (worker.onModuleRunner) onModuleRunner(worker.onModuleRunner);
let runPromise;
let isRunning = false;
let workerTeardown;
let setupContext;
function send(response) {
worker.post(worker.serialize ? worker.serialize(response) : response);
}
async function onMessage(rawMessage) {
const message = worker.deserialize ? worker.deserialize(rawMessage) : rawMessage;
if (message?.__vitest_worker_request__ !== true) return;
switch (message.type) {
case "start": {
process.env.VITEST_POOL_ID = String(message.poolId);
process.env.VITEST_WORKER_ID = String(message.workerId);
reportMemory = message.options.reportMemory;
if (message.context.config.isAgent) disableDefaultColors();
traces ??= await new Traces({
enabled: message.traces.enabled,
sdkPath: message.traces.sdkPath
}).waitInit();
const { environment, config, pool } = message.context;
const context = traces.getContextFromCarrier(message.traces.otelCarrier);
// record telemetry as part of "start"
traces.recordInitSpan(context);
try {
setupContext = {
environment,
config,
pool,
rpc: createRuntimeRpc(worker),
projectName: config.name || "",
traces
};
workerTeardown = await traces.$("vitest.runtime.setup", { context }, () => worker.setup?.(setupContext));
send({
type: "started",
__vitest_worker_response__
});
} catch (error) {
send({
type: "started",
__vitest_worker_response__,
error: serializeError(error)
});
}
break;
}
case "run":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.run", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => run({
...setupContext,
...message.context
}, worker, traces).catch((error) => serializeError(error)));
send({
type: "testfileFinished",
__vitest_worker_response__,
error: await runPromise,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "collect":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.collect", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => collect({
...setupContext,
...message.context
}, worker, traces).catch((error) => serializeError(error)));
send({
type: "testfileFinished",
__vitest_worker_response__,
error: await runPromise,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "stop":
await runPromise;
try {
const context = traces.getContextFromCarrier(message.otelCarrier);
const error = await traces.$("vitest.runtime.teardown", { context }, async () => {
const error = await teardown().catch((error) => serializeError(error));
await workerTeardown?.();
return error;
});
await traces.finish();
send({
type: "stopped",
error,
__vitest_worker_response__
});
} catch (error) {
send({
type: "stopped",
error: serializeError(error),
__vitest_worker_response__
});
}
worker.teardown?.();
break;
}
}
}
function getFilesWithLocations(files) {
return files.flatMap((file) => {
if (!file.testLocations) return file.filepath;
return file.testLocations.map((location) => {
return `${file}:${location}`;
});
});
}
export { listenForErrors as a, emitModuleRunner as e, init as i, loadEnvironment as l };
import module$1, { isBuiltin } from 'node:module';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { automockModule, createManualModuleSource, collectModuleExports } from '@vitest/mocker/transforms';
import { cleanUrl, createDefer } from '@vitest/utils/helpers';
import { p as parse } from './acorn.B2iPLyUM.js';
import { isAbsolute } from 'pathe';
import { t as toBuiltin } from './modules.BJuCwlRJ.js';
import { B as BareModuleMocker, n as normalizeModuleId } from './startVitestModuleRunner.DB-7oCpn.js';
import 'node:fs';
import './utils.BX5Fg8C4.js';
import '@vitest/utils/timers';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import 'node:vm';
import 'vite/module-runner';
import './traces.DT5aQ62U.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
class NativeModuleMocker extends BareModuleMocker {
wrapDynamicImport(moduleFactory) {
if (typeof moduleFactory === "function") return new Promise((resolve, reject) => {
this.resolveMocks().finally(() => {
moduleFactory().then(resolve, reject);
});
});
return moduleFactory;
}
resolveMockedModule(url, parentURL) {
// don't mock modules inside of packages because there is
// a high chance that it uses `require` which is not mockable
// because we use top-level await in "manual" mocks.
// for the sake of consistency we don't support mocking anything at all
if (parentURL.includes("/node_modules/")) return;
const moduleId = normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url);
const mockedModule = this.getDependencyMock(moduleId);
if (!mockedModule) return;
if (mockedModule.type === "redirect") return {
url: pathToFileURL(mockedModule.redirect).toString(),
shortCircuit: true
};
if (mockedModule.type === "automock" || mockedModule.type === "autospy") return {
url: injectQuery(url, parentURL, `mock=${mockedModule.type}`),
shortCircuit: true
};
if (mockedModule.type === "manual") return {
url: injectQuery(url, parentURL, "mock=manual"),
shortCircuit: true
};
}
loadAutomock(url, result) {
const moduleId = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
let source;
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(moduleId);
const exports$1 = Object.keys(builtinModule);
source = `
import * as builtinModule from '${toBuiltin(moduleId)}?mock=actual'
${exports$1.map((key, index) => {
return `
const __${index} = builtinModule["${key}"]
export { __${index} as "${key}" }
`;
}).join("")}`;
} else source = result.source?.toString();
if (source == null) return;
const mockType = url.includes("mock=automock") ? "automock" : "autospy";
const transformedCode = transformCode(source, result.format || "module", moduleId);
try {
const ms = automockModule(transformedCode, mockType, (code) => parse(code, {
sourceType: "module",
ecmaVersion: "latest"
}), { id: moduleId });
return {
format: "module",
source: `${ms.toString()}\n//# sourceMappingURL=${genSourceMapUrl(ms.generateMap({
hires: "boundary",
source: moduleId
}))}`,
shortCircuit: true
};
} catch (cause) {
throw new Error(`Cannot automock '${url}' because it failed to parse.`, { cause });
}
}
loadManualMock(url, result) {
const moduleId = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
// should not be possible
if (this.getDependencyMock(moduleId)?.type !== "manual") {
console.warn(`Vitest detected unregistered manual mock ${moduleId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
return;
}
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(toBuiltin(moduleId));
return {
format: "module",
source: createManualModuleSource(moduleId, Object.keys(builtinModule)),
shortCircuit: true
};
}
if (!result.source) return;
const transformedCode = transformCode(result.source.toString(), result.format || "module", moduleId);
if (transformedCode == null) return;
const format = result.format?.startsWith("module") ? "module" : "commonjs";
try {
return {
format: "module",
source: createManualModuleSource(moduleId, collectModuleExports(moduleId, transformedCode, format)),
shortCircuit: true
};
} catch (cause) {
throw new Error(`Failed to mock '${url}'. See the cause for more information.`, { cause });
}
}
processedModules = /* @__PURE__ */ new Map();
checkCircularManualMock(url) {
const id = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
this.processedModules.set(id, (this.processedModules.get(id) ?? 0) + 1);
// the module is mocked and requested a second time, let's resolve
// the factory function that will redefine the exports later
if (this.originalModulePromises.has(id)) {
const factoryPromise = this.factoryPromises.get(id);
this.originalModulePromises.get(id)?.resolve({ __factoryPromise: factoryPromise });
}
}
originalModulePromises = /* @__PURE__ */ new Map();
factoryPromises = /* @__PURE__ */ new Map();
// potential performance improvement:
// store by URL, not ids, no need to call url.*to* methods and normalizeModuleId
getFactoryModule(id) {
const mock = this.getMockerRegistry().getById(id);
if (!mock || mock.type !== "manual") throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
const mockResult = mock.resolve();
if (mockResult instanceof Promise) {
// to avoid circular dependency, we resolve this function as {__factoryPromise} in `checkCircularManualMock`
// when it's requested the second time. then the exports are exposed as `undefined`,
// but later redefined when the promise is actually resolved
const promise = createDefer();
promise.finally(() => {
this.originalModulePromises.delete(id);
});
mockResult.then(promise.resolve, promise.reject).finally(() => {
this.factoryPromises.delete(id);
});
this.factoryPromises.set(id, mockResult);
this.originalModulePromises.set(id, promise);
// Node.js on windows processes all the files first, and then runs them
// unlike Node.js logic on Mac and Unix where it also runs the code while evaluating
// So on Linux/Mac this `if` won't be hit because `checkCircularManualMock` will resolve it
// And on Windows, the `checkCircularManualMock` will never have `originalModulePromises`
// because `getFactoryModule` is not called until the evaluation phase
// But if we track how many times the module was transformed,
// we can deduce when to return `__factoryPromise` to support circular modules
if ((this.processedModules.get(id) ?? 0) > 1) {
this.processedModules.set(id, (this.processedModules.get(id) ?? 1) - 1);
promise.resolve({ __factoryPromise: mockResult });
}
return promise;
}
return mockResult;
}
importActual(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "actual");
return import(url.toString());
}
importMock(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
// file is already mocked
if (resolvedId.includes("mock=")) return import(resolvedId);
const filename = fileURLToPath(resolvedId);
const external = !isAbsolute(filename) || this.isModuleDirectory(resolvedId) ? normalizeModuleId(rawId) : null;
// file is not mocked, automock or redirect it
const redirect = this.findMockRedirect(filename, external);
if (redirect) return import(pathToFileURL(redirect).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "automock");
return import(url.toString());
}
}
const replacePercentageRE = /%/g;
function injectQuery(url, importer, queryToInject) {
const { search, hash } = new URL(url.replace(replacePercentageRE, "%25"), importer);
return `${cleanUrl(url)}?${queryToInject}${search ? `&${search.slice(1)}` : ""}${hash ?? ""}`;
}
let __require;
function getBuiltinModule(moduleId) {
__require ??= module$1.createRequire(import.meta.url);
return __require(`${moduleId}?mock=actual`);
}
function genSourceMapUrl(map) {
if (typeof map !== "string") map = JSON.stringify(map);
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
}
function transformCode(code, format, filename) {
if (format.includes("typescript")) {
if (!module$1.stripTypeScriptTypes) throw new Error(`Cannot parse '${filename}' because "module.stripTypeScriptTypes" is not supported. Module mocking requires Node.js 22.15 or higher. This is NOT a bug of Vitest.`);
return module$1.stripTypeScriptTypes(code);
}
return code;
}
export { NativeModuleMocker };
import fs from 'node:fs';
import { splitFileAndPostfix as splitFileAndPostfix$1, isBareImport } from '@vitest/utils/helpers';
import { i as isBuiltin, a as isBrowserExternal, t as toBuiltin } from './modules.BJuCwlRJ.js';
import { E as EnvironmentTeardownError, a as getSafeWorkerState } from './utils.BX5Fg8C4.js';
import { pathToFileURL } from 'node:url';
import { normalize, join } from 'pathe';
import { distDir } from '../path.js';
import { VitestModuleEvaluator, unwrapId } from '../module-evaluator.js';
import { isAbsolute, resolve } from 'node:path';
import vm from 'node:vm';
import { MockerRegistry, mockObject, RedirectedModule, AutomockedModule } from '@vitest/mocker';
import { findMockRedirect } from '@vitest/mocker/redirect';
import * as viteModuleRunner from 'vite/module-runner';
import { T as Traces } from './traces.DT5aQ62U.js';
class BareModuleMocker {
static pendingIds = [];
spyModule;
primitives;
registries = /* @__PURE__ */ new Map();
mockContext = { callstack: null };
_otel;
constructor(options) {
this.options = options;
this._otel = options.traces;
this.primitives = {
Object,
Error,
Function,
RegExp,
Symbol: globalThis.Symbol,
Array,
Map
};
if (options.spyModule) this.spyModule = options.spyModule;
}
get root() {
return this.options.root;
}
get moduleDirectories() {
return this.options.moduleDirectories || [];
}
getMockerRegistry() {
const suite = this.getSuiteFilepath();
if (!this.registries.has(suite)) this.registries.set(suite, new MockerRegistry());
return this.registries.get(suite);
}
reset() {
this.registries.clear();
}
invalidateModuleById(_id) {
// implemented by mockers that control the module runner
}
isModuleDirectory(path) {
return this.moduleDirectories.some((dir) => path.includes(dir));
}
getSuiteFilepath() {
return this.options.getCurrentTestFilepath() || "global";
}
createError(message, codeFrame) {
const Error = this.primitives.Error;
const error = new Error(message);
Object.assign(error, { codeFrame });
return error;
}
async resolveId(rawId, importer) {
return this._otel.$("vitest.mocker.resolve_id", { attributes: {
"vitest.module.raw_id": rawId,
"vitest.module.importer": rawId
} }, async (span) => {
const result = await this.options.resolveId(rawId, importer);
if (!result) {
span.addEvent("could not resolve id, fallback to unresolved values");
const id = normalizeModuleId(rawId);
span.setAttributes({
"vitest.module.id": id,
"vitest.module.url": rawId,
"vitest.module.external": id,
"vitest.module.fallback": true
});
return {
id,
url: rawId,
external: id
};
}
// external is node_module or unresolved module
// for example, some people mock "vscode" and don't have it installed
const external = !isAbsolute(result.file) || this.isModuleDirectory(result.file) ? normalizeModuleId(rawId) : null;
const id = normalizeModuleId(result.id);
span.setAttributes({
"vitest.module.id": id,
"vitest.module.url": result.url,
"vitest.module.external": external ?? false
});
return {
...result,
id,
external
};
});
}
async resolveMocks() {
if (!BareModuleMocker.pendingIds.length) return;
const resolveMock = async (mock) => {
const { id, url, external } = await this.resolveId(mock.id, mock.importer);
if (mock.action === "unmock") this.unmockPath(id);
if (mock.action === "mock") this.mockPath(mock.id, id, url, external, mock.type, mock.factory);
};
// group consecutive mocks of the same action type together,
// resolve in parallel inside each group, but run groups sequentially
// to preserve mock/unmock ordering
const groups = groupByConsecutiveAction(BareModuleMocker.pendingIds);
for (const group of groups) await Promise.all(group.map(resolveMock));
BareModuleMocker.pendingIds = [];
}
// public method to avoid circular dependency
getMockContext() {
return this.mockContext;
}
// path used to store mocked dependencies
getMockPath(dep) {
return `mock:${dep}`;
}
getDependencyMock(id) {
return this.getMockerRegistry().getById(fixLeadingSlashes(id));
}
getDependencyMockByUrl(url) {
return this.getMockerRegistry().get(url);
}
findMockRedirect(mockPath, external) {
return findMockRedirect(this.root, mockPath, external);
}
mockObject(object, mockExportsOrModuleType, moduleType) {
let mockExports;
if (mockExportsOrModuleType === "automock" || mockExportsOrModuleType === "autospy") {
moduleType = mockExportsOrModuleType;
mockExports = void 0;
} else mockExports = mockExportsOrModuleType;
moduleType ??= "automock";
const createMockInstance = this.spyModule?.createMockInstance;
if (!createMockInstance) throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
return mockObject({
globalConstructors: this.primitives,
createMockInstance,
type: moduleType
}, object, mockExports);
}
unmockPath(id) {
this.getMockerRegistry().deleteById(id);
this.invalidateModuleById(id);
}
mockPath(originalId, id, url, external, mockType, factory) {
const registry = this.getMockerRegistry();
if (mockType === "manual") registry.register("manual", originalId, id, url, factory);
else if (mockType === "autospy") registry.register("autospy", originalId, id, url);
else {
const redirect = this.findMockRedirect(id, external);
if (redirect) registry.register("redirect", originalId, id, url, redirect);
else registry.register("automock", originalId, id, url);
}
// every time the mock is registered, we remove the previous one from the cache
this.invalidateModuleById(id);
}
async importActual(_rawId, _importer, _callstack) {
throw new Error(`importActual is not implemented`);
}
async importMock(_rawId, _importer, _callstack) {
throw new Error(`importMock is not implemented`);
}
queueMock(id, importer, factoryOrOptions) {
const mockType = getMockType(factoryOrOptions);
BareModuleMocker.pendingIds.push({
action: "mock",
id,
importer,
factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
type: mockType
});
}
queueUnmock(id, importer) {
BareModuleMocker.pendingIds.push({
action: "unmock",
id,
importer
});
}
}
function getMockType(factoryOrOptions) {
if (!factoryOrOptions) return "automock";
if (typeof factoryOrOptions === "function") return "manual";
return factoryOrOptions.spy ? "autospy" : "automock";
}
// unique id that is not available as "$bare_import" like "test"
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
const prefixedBuiltins = new Set([
"node:sea",
"node:sqlite",
"node:test",
"node:test/reporters"
]);
const isWindows$1 = process.platform === "win32";
// transform file url to id
// virtual:custom -> virtual:custom
// \0custom -> \0custom
// /root/id -> /id
// /root/id.js -> /id.js
// C:/root/id.js -> /id.js
// C:\root\id.js -> /id.js
// TODO: expose this in vite/module-runner
function normalizeModuleId(file) {
if (prefixedBuiltins.has(file)) return file;
// if it's not in the root, keep it as a path, not a URL
return slash(file).replace(/^\/@fs\//, isWindows$1 ? "" : "/").replace(/^node:/, "").replace(/^\/+/, "/").replace(/^file:\//, "/");
}
const windowsSlashRE = /\\/g;
function slash(p) {
return p.replace(windowsSlashRE, "/");
}
function groupByConsecutiveAction(mocks) {
const groups = [];
for (const mock of mocks) {
const last = groups.at(-1);
if (last?.[0].action === mock.action) last.push(mock);
else groups.push([mock]);
}
return groups;
}
const multipleSlashRe = /^\/+/;
// module-runner incorrectly replaces file:///path with `///path`
function fixLeadingSlashes(id) {
if (id.startsWith("//")) return id.replace(multipleSlashRe, "/");
return id;
}
// copied from vite
// https://github.com/vitejs/vite/blob/4417b4f305623b2850bd6ae6553834c017694672/packages/vite/src/shared/utils.ts
// https://github.com/vitejs/vite/blob/4417b4f305623b2850bd6ae6553834c017694672/packages/vite/src/node/utils.ts
const postfixRE = /[?#].*$/;
const trailingSeparatorRE = /[?&]$/;
function cleanUrl(url) {
return url.replace(postfixRE, "");
}
function splitFileAndPostfix(path) {
const file = cleanUrl(path);
return {
file,
postfix: path.slice(file.length)
};
}
function injectQuery(url, queryToInject) {
const { file, postfix } = splitFileAndPostfix(url);
return `${file}?${queryToInject}${postfix[0] === "?" ? `&${postfix.slice(1)}` : postfix}`;
}
function removeQuery(url, queryToRemove) {
return url.replace(new RegExp(`([?&])${queryToRemove}(?:&|$)`), "$1").replace(trailingSeparatorRE, "");
}
const spyModulePath = resolve(distDir, "spy.js");
class VitestMocker extends BareModuleMocker {
filterPublicKeys;
constructor(moduleRunner, options) {
super(options);
this.moduleRunner = moduleRunner;
this.options = options;
const context = this.options.context;
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
const Symbol = this.primitives.Symbol;
this.filterPublicKeys = [
"__esModule",
Symbol.asyncIterator,
Symbol.hasInstance,
Symbol.isConcatSpreadable,
Symbol.iterator,
Symbol.match,
Symbol.matchAll,
Symbol.replace,
Symbol.search,
Symbol.split,
Symbol.species,
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables
];
}
get evaluatedModules() {
return this.moduleRunner.evaluatedModules;
}
async initializeSpyModule() {
if (this.spyModule) return;
this.spyModule = await this.moduleRunner.import(spyModulePath);
}
reset() {
this.registries.clear();
}
invalidateModuleById(id) {
const mockId = this.getMockPath(id);
const node = this.evaluatedModules.getModuleById(mockId);
if (node) {
this.evaluatedModules.invalidateModule(node);
node.mockedExports = void 0;
}
}
ensureModule(id, url) {
const node = this.evaluatedModules.ensureModule(id, url);
// TODO
node.meta = {
id,
url,
code: "",
file: null,
invalidate: false
};
return node;
}
async callFunctionMock(id, url, mock) {
const node = this.ensureModule(id, url);
if (node.exports) return node.exports;
const exports$1 = await mock.resolve();
const moduleExports = new Proxy(exports$1, { get: (target, prop) => {
const val = target[prop];
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
if (prop === "then") {
if (target instanceof Promise) return target.then.bind(target);
} else if (!(prop in target)) {
if (this.filterPublicKeys.includes(prop)) return;
throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
`, `vi.mock(import("${mock.raw}"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})`);
}
return val;
} });
node.exports = moduleExports;
return moduleExports;
}
async importActual(rawId, importer, callstack) {
const { url } = await this.resolveId(rawId, importer);
const actualUrl = injectQuery(url, "_vitest_original");
const node = await this.moduleRunner.fetchModule(actualUrl, importer);
return await this.moduleRunner.cachedRequest(node.url, node, callstack || [importer], void 0, true);
}
async importMock(rawId, importer) {
const { id, url, external } = await this.resolveId(rawId, importer);
let mock = this.getDependencyMock(id);
if (!mock) {
const redirect = this.findMockRedirect(id, external);
if (redirect) mock = new RedirectedModule(rawId, id, rawId, redirect);
else mock = new AutomockedModule(rawId, id, rawId);
}
if (mock.type === "automock" || mock.type === "autospy") {
const node = await this.moduleRunner.fetchModule(url, importer);
const mod = await this.moduleRunner.cachedRequest(url, node, [importer], void 0, true);
const Object = this.primitives.Object;
return this.mockObject(mod, Object.create(Object.prototype), mock.type);
}
if (mock.type === "manual") return this.callFunctionMock(id, url, mock);
const node = await this.moduleRunner.fetchModule(mock.redirect);
return this.moduleRunner.cachedRequest(mock.redirect, node, [importer], void 0, true);
}
async requestWithMockedModule(url, evaluatedNode, callstack, mock) {
return this._otel.$("vitest.mocker.evaluate", async (span) => {
const mockId = this.getMockPath(evaluatedNode.id);
span.setAttributes({
"vitest.module.id": mockId,
"vitest.mock.type": mock.type,
"vitest.mock.id": mock.id,
"vitest.mock.url": mock.url,
"vitest.mock.raw": mock.raw
});
if (mock.type === "automock" || mock.type === "autospy") {
const cache = this.evaluatedModules.getModuleById(mockId);
if (cache && cache.mockedExports) return cache.mockedExports;
const Object = this.primitives.Object;
// we have to define a separate object that will copy all properties into itself
// and can't just use the same `exports` define automatically by Vite before the evaluator
const exports$1 = Object.create(null);
Object.defineProperty(exports$1, Symbol.toStringTag, {
value: "Module",
configurable: true,
writable: true
});
const node = this.ensureModule(mockId, this.getMockPath(evaluatedNode.url));
node.meta = evaluatedNode.meta;
node.file = evaluatedNode.file;
node.mockedExports = exports$1;
const mod = await this.moduleRunner.cachedRequest(url, node, callstack, void 0, true);
this.mockObject(mod, exports$1, mock.type);
return exports$1;
}
if (mock.type === "manual" && !callstack.includes(mockId) && !callstack.includes(url)) try {
callstack.push(mockId);
// this will not work if user does Promise.all(import(), import())
// we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
// maybe we should improve mock API in the future?
this.mockContext.callstack = callstack;
return await this.callFunctionMock(mockId, this.getMockPath(url), mock);
} finally {
this.mockContext.callstack = null;
const indexMock = callstack.indexOf(mockId);
callstack.splice(indexMock, 1);
}
else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) {
span.setAttribute("vitest.mock.redirect", mock.redirect);
return mock.redirect;
}
});
}
async mockedRequest(url, evaluatedNode, callstack) {
const mock = this.getDependencyMock(evaluatedNode.id);
if (!mock) return;
return this.requestWithMockedModule(url, evaluatedNode, callstack, mock);
}
}
class VitestTransport {
constructor(options, evaluatedModules, callstacks) {
this.options = options;
this.evaluatedModules = evaluatedModules;
this.callstacks = callstacks;
}
async invoke(event) {
if (event.type !== "custom") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support Vite HMR events.`) };
if (event.event !== "vite:invoke") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support ${event.event} event.`) };
const { name, data } = event.data;
if (name === "getBuiltins")
// we return an empty array here to avoid client-side builtin check,
// as we need builtins to go through `fetchModule`
return { result: [] };
if (name !== "fetchModule") return { error: /* @__PURE__ */ new Error(`Unknown method: ${name}. Expected "fetchModule".`) };
try {
return { result: await this.options.fetchModule(...data) };
} catch (cause) {
if (cause instanceof EnvironmentTeardownError) {
const [id, importer] = data;
let message = `Cannot load '${id}'${importer ? ` imported from ${importer}` : ""} after the environment was torn down. This is not a bug in Vitest.`;
const moduleNode = importer ? this.evaluatedModules.getModuleById(importer) : void 0;
const callstack = moduleNode ? this.callstacks.get(moduleNode) : void 0;
if (callstack) message += ` The last recorded callstack:\n- ${[
...callstack,
importer,
id
].reverse().join("\n- ")}`;
const error = new EnvironmentTeardownError(message);
if (cause.stack) error.stack = cause.stack.replace(cause.message, error.message);
return { error };
}
return { error: cause };
}
}
}
const createNodeImportMeta = (modulePath) => {
if (!viteModuleRunner.createDefaultImportMeta) throw new Error(`createNodeImportMeta is not supported in this version of Vite.`);
const defaultMeta = viteModuleRunner.createDefaultImportMeta(modulePath);
const href = defaultMeta.url;
const importMetaResolver = createImportMetaResolver();
return {
...defaultMeta,
main: false,
resolve(id, parent) {
return (importMetaResolver ?? defaultMeta.resolve)(id, parent ?? href);
}
};
};
function createImportMetaResolver() {
if (!import.meta.resolve) return;
return (specifier, importer) => import.meta.resolve(specifier, importer);
}
// @ts-expect-error overriding private method
class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
mocker;
moduleExecutionInfo;
_otel;
_callstacks;
constructor(vitestOptions) {
const options = vitestOptions;
const evaluatedModules = options.evaluatedModules;
const callstacks = /* @__PURE__ */ new WeakMap();
const transport = new VitestTransport(options.transport, evaluatedModules, callstacks);
super({
transport,
hmr: false,
evaluatedModules,
sourcemapInterceptor: "prepareStackTrace",
createImportMeta: vitestOptions.createImportMeta
}, options.evaluator);
this.vitestOptions = vitestOptions;
this._callstacks = callstacks;
this._otel = vitestOptions.traces || new Traces({ enabled: false });
this.moduleExecutionInfo = options.getWorkerState().moduleExecutionInfo;
this.mocker = options.mocker || new VitestMocker(this, {
spyModule: options.spyModule,
context: options.vm?.context,
traces: this._otel,
resolveId: options.transport.resolveId,
get root() {
return options.getWorkerState().config.root;
},
get moduleDirectories() {
return options.getWorkerState().config.deps.moduleDirectories || [];
},
getCurrentTestFilepath() {
return options.getWorkerState().filepath;
}
});
if (options.vm) options.vm.context.__vitest_mocker__ = this.mocker;
else Object.defineProperty(globalThis, "__vitest_mocker__", {
configurable: true,
writable: true,
value: this.mocker
});
}
/**
* Vite checks that the module has exports emulating the Node.js behaviour,
* but Vitest is more relaxed.
*
* We should keep the Vite behavior when there is a `strict` flag.
* @internal
*/
processImport(exports$1) {
return exports$1;
}
async import(rawId) {
const resolved = await this._otel.$("vitest.module.resolve_id", { attributes: { "vitest.module.raw_id": rawId } }, async (span) => {
const result = await this.vitestOptions.transport.resolveId(rawId);
if (result) span.setAttributes({
"vitest.module.url": result.url,
"vitest.module.file": result.file,
"vitest.module.id": result.id
});
return result;
});
return super.import(resolved ? resolved.url : rawId);
}
async fetchModule(url, importer) {
return await this.cachedModule(url, importer);
}
_cachedRequest(url, module, callstack = [], metadata) {
// @ts-expect-error "cachedRequest" is private
return super.cachedRequest(url, module, callstack, metadata);
}
/**
* @internal
*/
async cachedRequest(url, mod, callstack = [], metadata, ignoreMock = false) {
// Track for a better error message if dynamic import is not resolved properly
this._callstacks.set(mod, callstack);
if (ignoreMock) return this._cachedRequest(url, mod, callstack, metadata);
let mocked;
if (mod.meta && "mockedModule" in mod.meta) {
const mockedModule = mod.meta.mockedModule;
const mockId = this.mocker.getMockPath(mod.id);
// bypass mock and force "importActual" behavior when:
// - mock was removed by doUnmock (stale mockedModule in meta)
// - self-import: mock factory/file is importing the module it's mocking
const isStale = !this.mocker.getDependencyMock(mod.id);
const isSelfImport = callstack.includes(mockId) || callstack.includes(url) || "redirect" in mockedModule && callstack.includes(mockedModule.redirect);
if (isStale || isSelfImport) {
const node = await this.fetchModule(injectQuery(url, "_vitest_original"));
return this._cachedRequest(node.url, node, callstack, metadata);
}
mocked = await this.mocker.requestWithMockedModule(url, mod, callstack, mockedModule);
} else mocked = await this.mocker.mockedRequest(url, mod, callstack);
if (typeof mocked === "string") {
const node = await this.fetchModule(mocked);
return this._cachedRequest(mocked, node, callstack, metadata);
}
if (mocked != null && typeof mocked === "object") return mocked;
return this._cachedRequest(url, mod, callstack, metadata);
}
/** @internal */
_invalidateSubTreeById(ids, invalidated = /* @__PURE__ */ new Set()) {
for (const id of ids) {
if (invalidated.has(id)) continue;
const node = this.evaluatedModules.getModuleById(id);
if (!node) continue;
invalidated.add(id);
const subIds = Array.from(this.evaluatedModules.idToModuleMap).filter(([, mod]) => mod.importers.has(id)).map(([key]) => key);
if (subIds.length) this._invalidateSubTreeById(subIds, invalidated);
this.evaluatedModules.invalidateModule(node);
}
}
}
const bareVitestRegexp = /^@?vitest(?:\/|$)/;
const normalizedDistDir = normalize(distDir);
const relativeIds = {};
const externalizeMap = /* @__PURE__ */ new Map();
// all Vitest imports always need to be externalized
function getCachedVitestImport(id, state) {
if (id.startsWith("/@fs/") || id.startsWith("\\@fs\\")) id = id.slice(process.platform === "win32" ? 5 : 4);
if (externalizeMap.has(id)) return {
externalize: externalizeMap.get(id),
type: "module"
};
// always externalize Vitest because we import from there before running tests
// so we already have it cached by Node.js
const root = state().config.root;
const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
if (id.includes(distDir) || id.includes(normalizedDistDir)) {
const { file, postfix } = splitFileAndPostfix$1(id);
const externalize = id.startsWith("file://") ? id : `${pathToFileURL(file)}${postfix}`;
externalizeMap.set(id, externalize);
return {
externalize,
type: "module"
};
}
if (relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
const { file, postfix } = splitFileAndPostfix$1(id);
const externalize = `${pathToFileURL(join(root, file))}${postfix}`;
externalizeMap.set(id, externalize);
return {
externalize,
type: "module"
};
}
if (bareVitestRegexp.test(id)) {
externalizeMap.set(id, id);
return {
externalize: id,
type: "module"
};
}
return null;
}
const { readFileSync } = fs;
const VITEST_VM_CONTEXT_SYMBOL = "__vitest_vm_context__";
const cwd = process.cwd();
const isWindows = process.platform === "win32";
function startVitestModuleRunner(options) {
const traces = options.traces;
const state = () => getSafeWorkerState() || options.state;
const rpc = () => state().rpc;
const environment = () => {
const environment = state().environment;
return environment.viteEnvironment || environment.name;
};
const vm = options.context && options.externalModulesExecutor ? {
context: options.context,
externalModulesExecutor: options.externalModulesExecutor
} : void 0;
const evaluator = options.evaluator || new VitestModuleEvaluator(vm, {
traces,
evaluatedModules: options.evaluatedModules,
get moduleExecutionInfo() {
return state().moduleExecutionInfo;
},
get interopDefault() {
return state().config.deps.interopDefault;
},
getCurrentTestFilepath: () => state().filepath
});
const moduleRunner = new VitestModuleRunner({
spyModule: options.spyModule,
evaluatedModules: options.evaluatedModules,
evaluator,
traces,
mocker: options.mocker,
transport: {
async fetchModule(id, importer, options) {
const resolvingModules = state().resolvingModules;
if (isWindows) {
if (id[1] === ":") {
// The drive letter is different for whatever reason, we need to normalize it to CWD
if (id[0] !== cwd[0] && id[0].toUpperCase() === cwd[0].toUpperCase()) id = (cwd[0].toUpperCase() === cwd[0] ? id[0].toUpperCase() : id[0].toLowerCase()) + id.slice(1);
// always mark absolute windows paths, otherwise Vite will externalize it
id = `/@id/${id}`;
}
}
const vitest = getCachedVitestImport(id, state);
if (vitest) return vitest;
// strip _vitest_original query added by importActual so that
// the plugin pipeline sees the original import id (e.g. virtual modules's load hook)
const isImportActual = id.includes("_vitest_original");
if (isImportActual) id = removeQuery(id, "_vitest_original");
const rawId = unwrapId(id);
resolvingModules.add(rawId);
try {
if (VitestMocker.pendingIds.length) await moduleRunner.mocker.resolveMocks();
if (!isImportActual) {
const resolvedMock = moduleRunner.mocker.getDependencyMockByUrl(id);
if (resolvedMock?.type === "manual" || resolvedMock?.type === "redirect") return {
code: "",
file: null,
id: resolvedMock.id,
url: resolvedMock.url,
invalidate: false,
mockedModule: resolvedMock
};
}
if (isBuiltin(rawId)) return {
externalize: rawId,
type: "builtin"
};
if (isBrowserExternal(rawId)) return {
externalize: toBuiltin(rawId),
type: "builtin"
};
// if module is invalidated, the worker will be recreated,
// so cached is always true in a single worker
if (!isImportActual && options?.cached) return { cache: true };
const otelCarrier = traces?.getContextCarrier();
const result = await rpc().fetch(id, importer, environment(), options, otelCarrier);
if ("cached" in result) return {
code: readFileSync(result.tmp, "utf-8"),
...result
};
return result;
} catch (cause) {
// rethrow vite error if it cannot load the module because it's not resolved
if (typeof cause === "object" && cause != null && cause.code === "ERR_LOAD_URL" || typeof cause?.message === "string" && cause.message.includes("Failed to load url") || typeof cause?.message === "string" && cause.message.startsWith("Cannot find module '")) {
const error = new Error(`Cannot find ${isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from ${importer}` : ""}`, { cause });
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
throw cause;
} finally {
resolvingModules.delete(rawId);
}
},
resolveId(id, importer) {
return rpc().resolve(id, importer, environment());
}
},
getWorkerState: state,
vm,
createImportMeta: options.createImportMeta
});
return moduleRunner;
}
export { BareModuleMocker as B, VITEST_VM_CONTEXT_SYMBOL as V, VitestModuleRunner as a, VitestTransport as b, createNodeImportMeta as c, normalizeModuleId as n, startVitestModuleRunner as s };
import { fileURLToPath, pathToFileURL } from 'node:url';
import vm, { isContext, runInContext } from 'node:vm';
import { dirname, basename, extname, normalize, resolve } from 'pathe';
import { l as loadEnvironment, a as listenForErrors, e as emitModuleRunner } from './init.k9zZ9sLh.js';
import { distDir } from '../path.js';
import { createCustomConsole } from './console.3WNpx0tS.js';
import fs from 'node:fs';
import { createRequire, Module, isBuiltin } from 'node:module';
import { toArray, splitFileAndPostfix, isBareImport } from '@vitest/utils/helpers';
import { findNearestPackageData } from '@vitest/utils/resolver';
import { dirname as dirname$1 } from 'node:path';
import { CSS_LANGS_RE, KNOWN_ASSET_RE } from '@vitest/utils/constants';
import { getDefaultRequestStubs } from '../module-evaluator.js';
import { s as startVitestModuleRunner, V as VITEST_VM_CONTEXT_SYMBOL, c as createNodeImportMeta } from './startVitestModuleRunner.DB-7oCpn.js';
import { p as provideWorkerState } from './utils.BX5Fg8C4.js';
function interopCommonJsModule(interopDefault, mod) {
if (isPrimitive(mod) || Array.isArray(mod) || mod instanceof Promise) return {
keys: [],
moduleExports: {},
defaultExport: mod
};
if (interopDefault !== false && "__esModule" in mod && !isPrimitive(mod.default)) {
const defaultKets = Object.keys(mod.default);
const moduleKeys = Object.keys(mod);
const allKeys = new Set([...defaultKets, ...moduleKeys]);
allKeys.delete("default");
return {
keys: Array.from(allKeys),
moduleExports: new Proxy(mod, { get(mod, prop) {
return mod[prop] ?? mod.default?.[prop];
} }),
defaultExport: mod
};
}
return {
keys: Object.keys(mod).filter((key) => key !== "default"),
moduleExports: mod,
defaultExport: mod
};
}
function isPrimitive(obj) {
return !(obj != null && (typeof obj === "object" || typeof obj === "function"));
}
const SyntheticModule = vm.SyntheticModule;
const SourceTextModule = vm.SourceTextModule;
const _require = createRequire(import.meta.url);
const requiresCache = /* @__PURE__ */ new WeakMap();
class CommonjsExecutor {
context;
requireCache = /* @__PURE__ */ new Map();
publicRequireCache = this.createProxyCache();
moduleCache = /* @__PURE__ */ new Map();
builtinCache = Object.create(null);
extensions = Object.create(null);
fs;
Module;
interopDefault;
constructor(options) {
this.context = options.context;
this.fs = options.fileMap;
this.interopDefault = options.interopDefault;
const primitives = vm.runInContext("({ Object, Array, Error })", this.context);
// eslint-disable-next-line ts/no-this-alias
const executor = this;
this.Module = class Module$1 {
exports;
isPreloading = false;
id;
filename;
loaded;
parent;
children = [];
path;
paths = [];
constructor(id = "", parent) {
this.exports = primitives.Object.create(Object.prototype);
// in our case the path should always be resolved already
this.path = dirname(id);
this.id = id;
this.filename = id;
this.loaded = false;
this.parent = parent;
}
get require() {
const require = requiresCache.get(this);
if (require) return require;
const _require = Module$1.createRequire(this.id);
requiresCache.set(this, _require);
return _require;
}
static getSourceMapsSupport = () => ({
enabled: false,
nodeModules: false,
generatedCode: false
});
static setSourceMapsSupport = () => {
// noop
};
static register = () => {
throw new Error(`[vitest] "register" is not available when running in Vitest.`);
};
static registerHooks = () => {
throw new Error(`[vitest] "registerHooks" is not available when running in Vitest.`);
};
_compile(code, filename) {
const cjsModule = Module$1.wrap(code);
const script = new vm.Script(cjsModule, {
filename,
importModuleDynamically: options.importModuleDynamically
});
// @ts-expect-error mark script with current identifier
script.identifier = filename;
const fn = script.runInContext(executor.context);
const __dirname = dirname(filename);
executor.requireCache.set(filename, this);
try {
fn(this.exports, this.require, this, filename, __dirname);
return this.exports;
} finally {
this.loaded = true;
}
}
// exposed for external use, Node.js does the opposite
static _load = (request, parent, _isMain) => {
return Module$1.createRequire(parent?.filename ?? request)(request);
};
static wrap = (script) => {
return Module$1.wrapper[0] + script + Module$1.wrapper[1];
};
static wrapper = new primitives.Array("(function (exports, require, module, __filename, __dirname) { ", "\n});");
static builtinModules = Module.builtinModules;
static findSourceMap = Module.findSourceMap;
static SourceMap = Module.SourceMap;
static syncBuiltinESMExports = Module.syncBuiltinESMExports;
static _cache = executor.publicRequireCache;
static _extensions = executor.extensions;
static createRequire = (filename) => {
return executor.createRequire(filename);
};
static runMain = () => {
throw new primitives.Error("[vitest] \"runMain\" is not implemented.");
};
// @ts-expect-error not typed
static _resolveFilename = Module._resolveFilename;
// @ts-expect-error not typed
static _findPath = Module._findPath;
// @ts-expect-error not typed
static _initPaths = Module._initPaths;
// @ts-expect-error not typed
static _preloadModules = Module._preloadModules;
// @ts-expect-error not typed
static _resolveLookupPaths = Module._resolveLookupPaths;
// @ts-expect-error not typed
static globalPaths = Module.globalPaths;
static isBuiltin = Module.isBuiltin;
static constants = Module.constants;
static enableCompileCache = Module.enableCompileCache;
static getCompileCacheDir = Module.getCompileCacheDir;
static flushCompileCache = Module.flushCompileCache;
static stripTypeScriptTypes = Module.stripTypeScriptTypes;
static findPackageJSON = Module.findPackageJSON;
static Module = Module$1;
};
this.extensions[".js"] = this.requireJs;
this.extensions[".json"] = this.requireJson;
}
requireJs = (m, filename) => {
const content = this.fs.readFile(filename);
m._compile(content, filename);
};
requireJson = (m, filename) => {
const code = this.fs.readFile(filename);
m.exports = JSON.parse(code);
};
static cjsConditions;
static getCjsConditions() {
if (!CommonjsExecutor.cjsConditions) CommonjsExecutor.cjsConditions = parseCjsConditions(process.execArgv, process.env.NODE_OPTIONS);
return CommonjsExecutor.cjsConditions;
}
createRequire = (filename) => {
const _require = createRequire(filename);
const resolve = (id, options) => {
return _require.resolve(id, {
...options,
conditions: CommonjsExecutor.getCjsConditions()
});
};
const require = ((id) => {
const resolved = resolve(id);
if (extname(resolved) === ".node" || isBuiltin(resolved)) return this.requireCoreModule(resolved);
const module = new this.Module(resolved);
return this.loadCommonJSModule(module, resolved);
});
require.resolve = resolve;
require.resolve.paths = _require.resolve.paths;
Object.defineProperty(require, "extensions", {
get: () => this.extensions,
set: () => {},
configurable: true
});
require.main = void 0;
require.cache = this.publicRequireCache;
return require;
};
createProxyCache() {
return new Proxy(Object.create(null), {
defineProperty: () => true,
deleteProperty: () => true,
set: () => true,
get: (_, key) => this.requireCache.get(key),
has: (_, key) => this.requireCache.has(key),
ownKeys: () => Array.from(this.requireCache.keys()),
getOwnPropertyDescriptor() {
return {
configurable: true,
enumerable: true
};
}
});
}
// very naive implementation for Node.js require
loadCommonJSModule(module, filename) {
const cached = this.requireCache.get(filename);
if (cached) return cached.exports;
const extension = this.findLongestRegisteredExtension(filename);
(this.extensions[extension] || this.extensions[".js"])(module, filename);
return module.exports;
}
findLongestRegisteredExtension(filename) {
const name = basename(filename);
let currentExtension;
let index;
let startIndex = 0;
// eslint-disable-next-line no-cond-assign
while ((index = name.indexOf(".", startIndex)) !== -1) {
startIndex = index + 1;
if (index === 0) continue;
currentExtension = name.slice(index);
if (this.extensions[currentExtension]) return currentExtension;
}
return ".js";
}
getCoreSyntheticModule(identifier) {
if (this.moduleCache.has(identifier)) return this.moduleCache.get(identifier);
const exports$1 = this.require(identifier);
const keys = Object.keys(exports$1);
const module = new SyntheticModule([...keys, "default"], () => {
for (const key of keys) module.setExport(key, exports$1[key]);
module.setExport("default", exports$1);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
getCjsSyntheticModule(path, identifier) {
if (this.moduleCache.has(identifier)) return this.moduleCache.get(identifier);
const exports$1 = this.require(path);
// TODO: technically module should be parsed to find static exports, implement for strict mode in #2854
const { keys, moduleExports, defaultExport } = interopCommonJsModule(this.interopDefault, exports$1);
const module = new SyntheticModule([...keys, "default"], function() {
for (const key of keys) this.setExport(key, moduleExports[key]);
this.setExport("default", defaultExport);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
// TODO: use this in strict mode, when available in #2854
// private _getNamedCjsExports(path: string): Set<string> {
// const cachedNamedExports = this.cjsNamedExportsMap.get(path)
// if (cachedNamedExports) {
// return cachedNamedExports
// }
// if (extname(path) === '.node') {
// const moduleExports = this.require(path)
// const namedExports = new Set(Object.keys(moduleExports))
// this.cjsNamedExportsMap.set(path, namedExports)
// return namedExports
// }
// const code = this.fs.readFile(path)
// const { exports, reexports } = parseCjs(code, path)
// const namedExports = new Set(exports)
// this.cjsNamedExportsMap.set(path, namedExports)
// for (const reexport of reexports) {
// if (isNodeBuiltin(reexport)) {
// const exports = this.require(reexport)
// if (exports !== null && typeof exports === 'object') {
// for (const e of Object.keys(exports)) {
// namedExports.add(e)
// }
// }
// }
// else {
// const require = this.createRequire(path)
// const resolved = require.resolve(reexport)
// const exports = this._getNamedCjsExports(resolved)
// for (const e of exports) {
// namedExports.add(e)
// }
// }
// }
// return namedExports
// }
require(identifier) {
if (extname(identifier) === ".node" || isBuiltin(identifier)) return this.requireCoreModule(identifier);
const module = new this.Module(identifier);
return this.loadCommonJSModule(module, identifier);
}
requireCoreModule(identifier) {
const normalized = identifier.replace(/^node:/, "");
if (this.builtinCache[normalized]) return this.builtinCache[normalized].exports;
const moduleExports = _require(identifier);
if (identifier === "node:module" || identifier === "module") {
const module = new this.Module("/module.js");
module.exports = this.Module;
this.builtinCache[normalized] = module;
return module.exports;
}
this.builtinCache[normalized] = _require.cache[normalized];
// TODO: should we wrap module to rethrow context errors?
return moduleExports;
}
}
// The "module-sync" exports condition (added in Node 22.12/20.19 when
// require(esm) was unflagged) can resolve to ESM files that our CJS
// vm.Script executor cannot handle. We exclude it by passing explicit
// CJS conditions to require.resolve (Node 22.12+).
// Must be a Set because Node's internal resolver calls conditions.has().
// User-specified --conditions/-C flags are respected, except module-sync.
function parseCjsConditions(execArgv, nodeOptions) {
const conditions = [
"node",
"require",
"node-addons"
];
const args = [...execArgv, ...nodeOptions?.split(/\s+/) ?? []];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const eqMatch = arg.match(/^(?:--conditions|-C)=(.+)$/);
if (eqMatch) conditions.push(eqMatch[1]);
else if ((arg === "--conditions" || arg === "-C") && i + 1 < args.length) conditions.push(args[++i]);
}
return new Set(conditions.filter((c) => c !== "module-sync"));
}
const dataURIRegex = /^data:(?<mime>text\/javascript|application\/json|application\/wasm)(?:;(?<encoding>charset=utf-8|base64))?,(?<code>.*)$/;
class EsmExecutor {
moduleCache = /* @__PURE__ */ new Map();
esmLinkMap = /* @__PURE__ */ new WeakMap();
context;
#httpIp = IPnumber("127.0.0.0");
constructor(executor, options) {
this.executor = executor;
this.context = options.context;
}
async evaluateModule(m) {
if (m.status === "unlinked") this.esmLinkMap.set(m, m.link((identifier, referencer) => this.executor.resolveModule(identifier, referencer.identifier)));
await this.esmLinkMap.get(m);
if (m.status === "linked") await m.evaluate();
return m;
}
async createEsModule(fileURL, getCode) {
const cached = this.moduleCache.get(fileURL);
if (cached) return cached;
const promise = this.loadEsModule(fileURL, getCode);
this.moduleCache.set(fileURL, promise);
return promise;
}
async loadEsModule(fileURL, getCode) {
const code = await getCode();
// TODO: should not be allowed in strict mode, implement in #2854
if (fileURL.endsWith(".json")) {
const m = new SyntheticModule(["default"], function() {
const result = JSON.parse(code);
this.setExport("default", result);
});
this.moduleCache.set(fileURL, m);
return m;
}
const m = new SourceTextModule(code, {
identifier: fileURL,
context: this.context,
importModuleDynamically: this.executor.importModuleDynamically,
initializeImportMeta: (meta, mod) => {
meta.url = mod.identifier;
if (mod.identifier.startsWith("file:")) {
const filename = fileURLToPath(mod.identifier);
meta.filename = filename;
meta.dirname = dirname$1(filename);
}
meta.resolve = (specifier, importer) => {
return this.executor.resolve(specifier, importer != null ? importer.toString() : mod.identifier);
};
}
});
this.moduleCache.set(fileURL, m);
return m;
}
async createWebAssemblyModule(fileUrl, getCode) {
const cached = this.moduleCache.get(fileUrl);
if (cached) return cached;
const m = this.loadWebAssemblyModule(getCode(), fileUrl);
this.moduleCache.set(fileUrl, m);
return m;
}
async createNetworkModule(fileUrl) {
// https://nodejs.org/api/esm.html#https-and-http-imports
if (fileUrl.startsWith("http:")) {
const url = new URL(fileUrl);
if (url.hostname !== "localhost" && url.hostname !== "::1" && (IPnumber(url.hostname) & IPmask(8)) !== this.#httpIp) throw new Error(
// we don't know the importer, so it's undefined (the same happens in --pool=threads)
`import of '${fileUrl}' by undefined is not supported: http can only be used to load local resources (use https instead).`
);
}
return this.createEsModule(fileUrl, () => fetch(fileUrl).then((r) => r.text()));
}
async loadWebAssemblyModule(source, identifier) {
const cached = this.moduleCache.get(identifier);
if (cached) return cached;
const wasmModule = await WebAssembly.compile(source);
const exports$1 = WebAssembly.Module.exports(wasmModule);
const imports = WebAssembly.Module.imports(wasmModule);
const moduleLookup = {};
for (const { module } of imports) if (moduleLookup[module] === void 0) moduleLookup[module] = await this.executor.resolveModule(module, identifier);
const evaluateModule = (module) => this.evaluateModule(module);
return new SyntheticModule(exports$1.map(({ name }) => name), async function() {
const importsObject = {};
for (const { module, name } of imports) {
if (!importsObject[module]) importsObject[module] = {};
await evaluateModule(moduleLookup[module]);
importsObject[module][name] = moduleLookup[module].namespace[name];
}
const wasmInstance = new WebAssembly.Instance(wasmModule, importsObject);
for (const { name } of exports$1) this.setExport(name, wasmInstance.exports[name]);
}, {
context: this.context,
identifier
});
}
cacheModule(identifier, module) {
this.moduleCache.set(identifier, module);
}
resolveCachedModule(identifier) {
return this.moduleCache.get(identifier);
}
async createDataModule(identifier) {
const cached = this.moduleCache.get(identifier);
if (cached) return cached;
const match = identifier.match(dataURIRegex);
if (!match || !match.groups) throw new Error("Invalid data URI");
const mime = match.groups.mime;
const encoding = match.groups.encoding;
if (mime === "application/wasm") {
if (!encoding) throw new Error("Missing data URI encoding");
if (encoding !== "base64") throw new Error(`Invalid data URI encoding: ${encoding}`);
const module = this.loadWebAssemblyModule(Buffer.from(match.groups.code, "base64"), identifier);
this.moduleCache.set(identifier, module);
return module;
}
let code = match.groups.code;
if (!encoding || encoding === "charset=utf-8") code = decodeURIComponent(code);
else if (encoding === "base64") code = Buffer.from(code, "base64").toString();
else throw new Error(`Invalid data URI encoding: ${encoding}`);
if (mime === "application/json") {
const module = new SyntheticModule(["default"], function() {
const obj = JSON.parse(code);
this.setExport("default", obj);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
return this.createEsModule(identifier, () => code);
}
}
function IPnumber(address) {
const ip = address.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
if (ip) return (+ip[1] << 24) + (+ip[2] << 16) + (+ip[3] << 8) + +ip[4];
throw new Error(`Expected IP address, received ${address}`);
}
function IPmask(maskSize) {
return -1 << 32 - maskSize;
}
const CLIENT_ID = "/@vite/client";
const CLIENT_FILE = pathToFileURL(CLIENT_ID).href;
class ViteExecutor {
esm;
constructor(options) {
this.options = options;
this.esm = options.esmExecutor;
}
resolve = (identifier) => {
if (identifier === CLIENT_ID) return identifier;
};
get workerState() {
return this.options.context.__vitest_worker__;
}
async createViteModule(fileUrl) {
if (fileUrl === CLIENT_FILE || fileUrl === CLIENT_ID) return this.createViteClientModule();
const cached = this.esm.resolveCachedModule(fileUrl);
if (cached) return cached;
return this.esm.createEsModule(fileUrl, async () => {
try {
const result = await this.options.transform(fileUrl);
if (result.code) return result.code;
} catch (cause) {
// rethrow vite error if it cannot load the module because it's not resolved
if (typeof cause === "object" && cause.code === "ERR_LOAD_URL" || typeof cause?.message === "string" && cause.message.includes("Failed to load url")) {
const error = new Error(`Cannot find module '${fileUrl}'`, { cause });
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
}
throw new Error(`[vitest] Failed to transform ${fileUrl}. Does the file exist?`);
});
}
createViteClientModule() {
const identifier = CLIENT_ID;
const cached = this.esm.resolveCachedModule(identifier);
if (cached) return cached;
const stub = this.options.viteClientModule;
const moduleKeys = Object.keys(stub);
const module = new SyntheticModule(moduleKeys, function() {
moduleKeys.forEach((key) => {
this.setExport(key, stub[key]);
});
}, {
context: this.options.context,
identifier
});
this.esm.cacheModule(identifier, module);
return module;
}
canResolve = (fileUrl) => {
if (fileUrl === CLIENT_FILE) return true;
const config = this.workerState.config.deps?.web || {};
const [modulePath] = fileUrl.split("?");
if (config.transformCss && CSS_LANGS_RE.test(modulePath)) return true;
if (config.transformAssets && KNOWN_ASSET_RE.test(modulePath)) return true;
if (toArray(config.transformGlobPattern).some((pattern) => pattern.test(modulePath))) return true;
return false;
};
}
const { existsSync } = fs;
// always defined when we use vm pool
const nativeResolve = import.meta.resolve;
// TODO: improve Node.js strict mode support in #2854
class ExternalModulesExecutor {
cjs;
esm;
vite;
context;
fs;
resolvers = [];
#networkSupported = null;
constructor(options) {
this.options = options;
this.context = options.context;
this.fs = options.fileMap;
this.esm = new EsmExecutor(this, { context: this.context });
this.cjs = new CommonjsExecutor({
context: this.context,
importModuleDynamically: this.importModuleDynamically,
fileMap: options.fileMap,
interopDefault: options.interopDefault
});
this.vite = new ViteExecutor({
esmExecutor: this.esm,
context: this.context,
transform: options.transform,
viteClientModule: options.viteClientModule
});
this.resolvers = [this.vite.resolve];
}
async import(identifier) {
const module = await this.createModule(identifier);
await this.esm.evaluateModule(module);
return module.namespace;
}
require(identifier) {
return this.cjs.require(identifier);
}
createRequire(identifier) {
return this.cjs.createRequire(identifier);
}
// dynamic import can be used in both ESM and CJS, so we have it in the executor
importModuleDynamically = async (specifier, referencer) => {
const module = await this.resolveModule(specifier, referencer.identifier);
return await this.esm.evaluateModule(module);
};
resolveModule = async (specifier, referencer) => {
let identifier = this.resolve(specifier, referencer);
if (identifier instanceof Promise) identifier = await identifier;
return await this.createModule(identifier);
};
resolve(specifier, parent) {
for (const resolver of this.resolvers) {
const id = resolver(specifier, parent);
if (id) return id;
}
// import.meta.resolve can be asynchronous in older +18 Node versions
return nativeResolve(specifier, parent);
}
getModuleInformation(identifier) {
if (identifier.startsWith("data:")) return {
type: "data",
url: identifier,
path: identifier
};
const { file, postfix } = splitFileAndPostfix(identifier);
const extension = extname(file);
if (extension === ".node" || isBuiltin(identifier)) return {
type: "builtin",
url: identifier,
path: identifier
};
if (this.isNetworkSupported && (identifier.startsWith("http:") || identifier.startsWith("https:"))) return {
type: "network",
url: identifier,
path: identifier
};
const isFileUrl = identifier.startsWith("file://");
const pathUrl = isFileUrl ? fileURLToPath(file) : file;
const fileUrl = isFileUrl ? identifier : `${pathToFileURL(file)}${postfix}`;
let type;
if (this.vite.canResolve(fileUrl)) type = "vite";
else if (extension === ".mjs") type = "module";
else if (extension === ".cjs") type = "commonjs";
else if (extension === ".wasm")
// still experimental on NodeJS --experimental-wasm-modules
// cf. ESM_FILE_FORMAT(url) in https://nodejs.org/docs/latest-v20.x/api/esm.html#resolution-algorithm
type = "wasm";
else type = findNearestPackageData(normalize(pathUrl)).type === "module" ? "module" : "commonjs";
return {
type,
path: pathUrl,
url: fileUrl
};
}
createModule(identifier) {
const { type, url, path } = this.getModuleInformation(identifier);
// create ERR_MODULE_NOT_FOUND on our own since latest NodeJS's import.meta.resolve doesn't throw on non-existing namespace or path
// https://github.com/nodejs/node/pull/49038
if ((type === "module" || type === "commonjs" || type === "wasm") && !existsSync(path)) {
const error = /* @__PURE__ */ new Error(`Cannot find ${isBareImport(path) ? "package" : "module"} '${path}'`);
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
switch (type) {
case "data": return this.esm.createDataModule(identifier);
case "builtin": return this.cjs.getCoreSyntheticModule(identifier);
case "vite": return this.vite.createViteModule(url);
case "wasm": return this.esm.createWebAssemblyModule(url, () => this.fs.readBuffer(path));
case "module": return this.esm.createEsModule(url, () => this.fs.readFileAsync(path));
case "commonjs": return this.cjs.getCjsSyntheticModule(path, identifier);
case "network": return this.esm.createNetworkModule(url);
default: return type;
}
}
get isNetworkSupported() {
if (this.#networkSupported == null) if (process.execArgv.includes("--experimental-network-imports")) this.#networkSupported = true;
else if (process.env.NODE_OPTIONS?.includes("--experimental-network-imports")) this.#networkSupported = true;
else this.#networkSupported = false;
return this.#networkSupported;
}
}
const { promises, readFileSync } = fs;
class FileMap {
fsCache = /* @__PURE__ */ new Map();
fsBufferCache = /* @__PURE__ */ new Map();
async readFileAsync(path) {
const cached = this.fsCache.get(path);
if (cached != null) return cached;
const source = await promises.readFile(path, "utf-8");
this.fsCache.set(path, source);
return source;
}
readFile(path) {
const cached = this.fsCache.get(path);
if (cached != null) return cached;
const source = readFileSync(path, "utf-8");
this.fsCache.set(path, source);
return source;
}
readBuffer(path) {
const cached = this.fsBufferCache.get(path);
if (cached != null) return cached;
const buffer = readFileSync(path);
this.fsBufferCache.set(path, buffer);
return buffer;
}
}
const entryFile = pathToFileURL(resolve(distDir, "workers/runVmTests.js")).href;
const fileMap = new FileMap();
const packageCache = /* @__PURE__ */ new Map();
async function runVmTests(method, state, traces) {
const { ctx, rpc } = state;
const beforeEnvironmentTime = performance.now();
const { environment } = await loadEnvironment(ctx.environment.name, ctx.config.root, rpc, traces, true);
state.environment = environment;
if (!environment.setupVM) {
const envName = ctx.environment.name;
const packageId = envName[0] === "." ? envName : `vitest-environment-${envName}`;
throw new TypeError(`Environment "${ctx.environment.name}" is not a valid environment. Path "${packageId}" doesn't support vm environment because it doesn't provide "setupVM" method.`);
}
const vm = await traces.$("vitest.runtime.environment.setup", { attributes: {
"vitest.environment": environment.name,
"vitest.environment.vite_environment": environment.viteEnvironment || environment.name
} }, () => environment.setupVM(ctx.environment.options || ctx.config.environmentOptions || {}));
state.durations.environment = performance.now() - beforeEnvironmentTime;
process.env.VITEST_VM_POOL = "1";
if (!vm.getVmContext) throw new TypeError(`Environment ${environment.name} doesn't provide "getVmContext" method. It should return a context created by "vm.createContext" method.`);
const context = vm.getVmContext();
if (!isContext(context)) throw new TypeError(`Environment ${environment.name} doesn't provide a valid context. It should be created by "vm.createContext" method.`);
provideWorkerState(context, state);
// this is unfortunately needed for our own dependencies
// we need to find a way to not rely on this by default
// because browser doesn't provide these globals
context.process = process;
context.global = context;
context.console = state.config.disableConsoleIntercept ? console : createCustomConsole(state);
// TODO: don't hardcode setImmediate in fake timers defaults
context.setImmediate = setImmediate;
context.clearImmediate = clearImmediate;
const stubs = getDefaultRequestStubs(context);
const externalModulesExecutor = new ExternalModulesExecutor({
context,
fileMap,
packageCache,
transform: rpc.transform,
viteClientModule: stubs["/@vite/client"]
});
process.exit = (code = process.exitCode || 0) => {
throw new Error(`process.exit unexpectedly called with "${code}"`);
};
listenForErrors(() => state);
const moduleRunner = startVitestModuleRunner({
context,
evaluatedModules: state.evaluatedModules,
state,
externalModulesExecutor,
createImportMeta: createNodeImportMeta,
traces
});
emitModuleRunner(moduleRunner);
Object.defineProperty(context, VITEST_VM_CONTEXT_SYMBOL, {
value: {
context,
externalModulesExecutor
},
configurable: true,
enumerable: false,
writable: false
});
context.__vitest_mocker__ = moduleRunner.mocker;
if (ctx.config.serializedDefines) try {
runInContext(ctx.config.serializedDefines, context, { filename: "virtual:load-defines.js" });
} catch (error) {
throw new Error(`Failed to load custom "defines": ${error.message}`);
}
await moduleRunner.mocker.initializeSpyModule();
const { run } = await moduleRunner.import(entryFile);
try {
await run(method, ctx.files, ctx.config, moduleRunner, traces);
} finally {
await traces.$("vitest.runtime.environment.teardown", () => vm.teardown?.());
}
}
function setupVmWorker(context) {
if (context.config.experimental.viteModuleRunner === false) throw new Error(`Pool "${context.pool}" cannot run with "experimental.viteModuleRunner: false". Please, use "threads" or "forks" instead.`);
}
export { runVmTests as r, setupVmWorker as s };
+1
-1

@@ -1,2 +0,2 @@

import { c as createCLI } from './chunks/cac.D3xHeqeL.js';
import { c as createCLI } from './chunks/cac.DdICfEr1.js';
import '@vitest/utils/helpers';

@@ -3,0 +3,0 @@ import 'events';

import * as vite from 'vite';
import { resolveConfig as resolveConfig$1, mergeConfig } from 'vite';
export { esbuildVersion, isCSSRequest, isFileLoadingAllowed, parseAst, parseAstAsync, rollupVersion, version as viteVersion } from 'vite';
import { V as Vitest, a as VitestPlugin } from './chunks/cli-api.24X8XwN1.js';
export { F as ForksPoolWorker, G as GitNotFoundError, b as TestsNotFoundError, T as ThreadsPoolWorker, c as TypecheckPoolWorker, d as VitestPackageInstaller, e as VmForksPoolWorker, f as VmThreadsPoolWorker, g as createDebugger, h as createMethodsRPC, i as createViteLogger, j as createVitest, k as escapeTestName, l as experimental_getRunnerTask, m as getFilePoolName, n as isFileServingAllowed, o as isValidApiRequest, r as registerConsoleShortcuts, p as resolveFsAllow, s as startVitest } from './chunks/cli-api.24X8XwN1.js';
export { p as parseCLI } from './chunks/cac.D3xHeqeL.js';
import { V as Vitest, a as VitestPlugin } from './chunks/cli-api.BK8pd4xc.js';
export { F as ForksPoolWorker, G as GitNotFoundError, b as TestsNotFoundError, T as ThreadsPoolWorker, c as TypecheckPoolWorker, d as VitestPackageInstaller, e as VmForksPoolWorker, f as VmThreadsPoolWorker, g as createDebugger, h as createMethodsRPC, i as createViteLogger, j as createVitest, k as escapeTestName, l as experimental_getRunnerTask, m as getFilePoolName, n as isFileServingAllowed, o as isValidApiRequest, r as registerConsoleShortcuts, p as resolveFsAllow, s as startVitest } from './chunks/cli-api.BK8pd4xc.js';
export { p as parseCLI } from './chunks/cac.DdICfEr1.js';
import { r as resolveConfig$2 } from './chunks/coverage.DM_a_rWm.js';

@@ -8,0 +8,0 @@ export { B as BaseCoverageProvider, a as BaseSequencer, b as resolveApiServerConfig } from './chunks/coverage.DM_a_rWm.js';

import { VitestModuleEvaluator } from './module-evaluator.js';
import { V as VITEST_VM_CONTEXT_SYMBOL, s as startVitestModuleRunner, a as VitestModuleRunner } from './chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import { V as VITEST_VM_CONTEXT_SYMBOL, s as startVitestModuleRunner, a as VitestModuleRunner } from './chunks/startVitestModuleRunner.DB-7oCpn.js';
import { g as getWorkerState } from './chunks/utils.BX5Fg8C4.js';

@@ -4,0 +4,0 @@ export { e as builtinEnvironments, p as populateGlobal } from './chunks/index.DC7d2Pf8.js';

@@ -1,3 +0,3 @@

export { r as runBaseTests, s as setupEnvironment } from './chunks/base.BE3cXZj3.js';
export { i as init } from './chunks/init.d4hAcNdp.js';
export { r as runBaseTests, s as setupEnvironment } from './chunks/base.B6Opl8PE.js';
export { i as init } from './chunks/init.k9zZ9sLh.js';
import 'node:vm';

@@ -39,3 +39,3 @@ import '@vitest/spy';

import './chunks/evaluatedModules.Dg1zASAC.js';
import './chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import './chunks/startVitestModuleRunner.DB-7oCpn.js';
import './chunks/modules.BJuCwlRJ.js';

@@ -42,0 +42,0 @@ import './path.js';

@@ -1,3 +0,3 @@

import { r as runBaseTests, s as setupBaseEnvironment } from '../chunks/base.BE3cXZj3.js';
import { w as workerInit } from '../chunks/init-forks.B4VBaxRV.js';
import { r as runBaseTests, s as setupBaseEnvironment } from '../chunks/base.B6Opl8PE.js';
import { w as workerInit } from '../chunks/init-forks.H5ZuobOQ.js';
import 'node:vm';

@@ -25,3 +25,3 @@ import '@vitest/spy';

import '../chunks/_commonjsHelpers.D26ty3Ew.js';
import '../chunks/init.d4hAcNdp.js';
import '../chunks/init.k9zZ9sLh.js';
import 'node:fs';

@@ -31,3 +31,3 @@ import 'node:module';

import 'vite/module-runner';
import '../chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import '../chunks/startVitestModuleRunner.DB-7oCpn.js';
import '../chunks/modules.BJuCwlRJ.js';

@@ -34,0 +34,0 @@ import '../path.js';

@@ -1,3 +0,3 @@

import { s as setupBaseEnvironment, r as runBaseTests } from '../chunks/base.BE3cXZj3.js';
import { w as workerInit } from '../chunks/init-threads.C_ClEpAf.js';
import { s as setupBaseEnvironment, r as runBaseTests } from '../chunks/base.B6Opl8PE.js';
import { w as workerInit } from '../chunks/init-threads.6kl1khcL.js';
import 'node:vm';

@@ -25,3 +25,3 @@ import '@vitest/spy';

import '../chunks/_commonjsHelpers.D26ty3Ew.js';
import '../chunks/init.d4hAcNdp.js';
import '../chunks/init.k9zZ9sLh.js';
import 'node:fs';

@@ -31,3 +31,3 @@ import 'node:module';

import 'vite/module-runner';
import '../chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import '../chunks/startVitestModuleRunner.DB-7oCpn.js';
import '../chunks/modules.BJuCwlRJ.js';

@@ -34,0 +34,0 @@ import '../path.js';

@@ -1,4 +0,4 @@

import { w as workerInit } from '../chunks/init-forks.B4VBaxRV.js';
import { r as runVmTests, s as setupVmWorker } from '../chunks/vm.B2aHeaNw.js';
import '../chunks/init.d4hAcNdp.js';
import { w as workerInit } from '../chunks/init-forks.H5ZuobOQ.js';
import { r as runVmTests, s as setupVmWorker } from '../chunks/vm.CXMd5FHa.js';
import '../chunks/init.k9zZ9sLh.js';
import 'node:fs';

@@ -9,3 +9,3 @@ import 'node:module';

import 'vite/module-runner';
import '../chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import '../chunks/startVitestModuleRunner.DB-7oCpn.js';
import '@vitest/utils/helpers';

@@ -12,0 +12,0 @@ import '../chunks/modules.BJuCwlRJ.js';

@@ -1,5 +0,5 @@

import { w as workerInit } from '../chunks/init-threads.C_ClEpAf.js';
import { s as setupVmWorker, r as runVmTests } from '../chunks/vm.B2aHeaNw.js';
import { w as workerInit } from '../chunks/init-threads.6kl1khcL.js';
import { s as setupVmWorker, r as runVmTests } from '../chunks/vm.CXMd5FHa.js';
import 'node:worker_threads';
import '../chunks/init.d4hAcNdp.js';
import '../chunks/init.k9zZ9sLh.js';
import 'node:fs';

@@ -10,3 +10,3 @@ import 'node:module';

import 'vite/module-runner';
import '../chunks/startVitestModuleRunner.CCBQZ9DQ.js';
import '../chunks/startVitestModuleRunner.DB-7oCpn.js';
import '@vitest/utils/helpers';

@@ -13,0 +13,0 @@ import '../chunks/modules.BJuCwlRJ.js';

{
"name": "vitest",
"type": "module",
"version": "4.1.9",
"version": "4.1.10",
"description": "Next generation testing framework powered by Vite",

@@ -138,8 +138,8 @@ "author": "Anthony Fu <anthonyfu117@hotmail.com>",

"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
"@vitest/browser-playwright": "4.1.9",
"@vitest/browser-preview": "4.1.9",
"@vitest/browser-webdriverio": "4.1.9",
"@vitest/coverage-istanbul": "4.1.9",
"@vitest/coverage-v8": "4.1.9",
"@vitest/ui": "4.1.9"
"@vitest/browser-preview": "4.1.10",
"@vitest/coverage-istanbul": "4.1.10",
"@vitest/browser-playwright": "4.1.10",
"@vitest/browser-webdriverio": "4.1.10",
"@vitest/coverage-v8": "4.1.10",
"@vitest/ui": "4.1.10"
},

@@ -198,9 +198,9 @@ "peerDependenciesMeta": {

"why-is-node-running": "^2.3.0",
"@vitest/mocker": "4.1.9",
"@vitest/expect": "4.1.9",
"@vitest/pretty-format": "4.1.9",
"@vitest/runner": "4.1.9",
"@vitest/spy": "4.1.9",
"@vitest/snapshot": "4.1.9",
"@vitest/utils": "4.1.9"
"@vitest/expect": "4.1.10",
"@vitest/mocker": "4.1.10",
"@vitest/pretty-format": "4.1.10",
"@vitest/runner": "4.1.10",
"@vitest/spy": "4.1.10",
"@vitest/snapshot": "4.1.10",
"@vitest/utils": "4.1.10"
},

@@ -207,0 +207,0 @@ "devDependencies": {

import { runInThisContext } from 'node:vm';
import * as spyModule from '@vitest/spy';
import { r as resolveTestRunner, a as resolveSnapshotEnvironment, d as detectAsyncLeaks, s as setupChaiConfig } from './index.DXx9Dtk7.js';
import { l as loadEnvironment, e as emitModuleRunner, a as listenForErrors } from './init.d4hAcNdp.js';
import { N as NativeModuleRunner } from './nativeModuleRunner.BIakptoF.js';
import { T as Traces } from './traces.DT5aQ62U.js';
import { V as VitestEvaluatedModules } from './evaluatedModules.Dg1zASAC.js';
import { s as startVitestModuleRunner, c as createNodeImportMeta } from './startVitestModuleRunner.CCBQZ9DQ.js';
import { performance as performance$1 } from 'node:perf_hooks';
import { startTests, collectTests } from '@vitest/runner';
import { s as setupCommonEnv, b as startCoverageInsideWorker, c as stopCoverageInsideWorker } from './setup-common.DYx3LtFI.js';
import { g as globalExpect, v as vi } from './test.DNmyFkvJ.js';
import { c as closeInspector } from './inspector.CvyFGlXm.js';
import { createRequire } from 'node:module';
import timers from 'node:timers';
import timersPromises from 'node:timers/promises';
import util from 'node:util';
import { KNOWN_ASSET_TYPES } from '@vitest/utils/constants';
import { i as index } from './index.DdgEv5B1.js';
import { g as getWorkerState, r as resetModules, p as provideWorkerState, a as getSafeWorkerState } from './utils.BX5Fg8C4.js';
// this should only be used in Node
let globalSetup = false;
async function setupGlobalEnv(config, environment) {
await setupCommonEnv(config);
Object.defineProperty(globalThis, "__vitest_index__", {
value: index,
enumerable: false
});
globalExpect.setState({ environment: environment.name });
if (globalSetup) return;
globalSetup = true;
if ((environment.viteEnvironment || environment.name) === "client") {
const _require = createRequire(import.meta.url);
// always mock "required" `css` files, because we cannot process them
_require.extensions[".css"] = resolveCss;
_require.extensions[".scss"] = resolveCss;
_require.extensions[".sass"] = resolveCss;
_require.extensions[".less"] = resolveCss;
// since we are using Vite, we can assume how these will be resolved
KNOWN_ASSET_TYPES.forEach((type) => {
_require.extensions[`.${type}`] = resolveAsset;
});
process.env.SSR = "";
} else process.env.SSR = "1";
// @ts-expect-error not typed global for patched timers
globalThis.__vitest_required__ = {
util,
timers,
timersPromises
};
if (!config.disableConsoleIntercept) await setupConsoleLogSpy();
}
function resolveCss(mod) {
mod.exports = "";
}
function resolveAsset(mod, url) {
mod.exports = url;
}
async function setupConsoleLogSpy() {
const { createCustomConsole } = await import('./console.3WNpx0tS.js');
globalThis.console = createCustomConsole();
}
// browser shouldn't call this!
async function run(method, files, config, moduleRunner, environment, traces) {
const workerState = getWorkerState();
const [testRunner] = await Promise.all([
traces.$("vitest.runtime.runner", () => resolveTestRunner(config, moduleRunner, traces)),
traces.$("vitest.runtime.global_env", () => setupGlobalEnv(config, environment)),
traces.$("vitest.runtime.coverage.start", () => startCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate })),
traces.$("vitest.runtime.snapshot.environment", async () => {
if (!workerState.config.snapshotOptions.snapshotEnvironment) workerState.config.snapshotOptions.snapshotEnvironment = await resolveSnapshotEnvironment(config, moduleRunner);
})
]);
workerState.onCancel((reason) => {
closeInspector(config);
testRunner.cancel?.(reason);
});
workerState.durations.prepare = performance$1.now() - workerState.durations.prepare;
await traces.$(`vitest.test.runner.${method}`, async () => {
for (const file of files) {
if (config.isolate) {
moduleRunner.mocker?.reset();
resetModules(workerState.evaluatedModules, true);
}
workerState.filepath = file.filepath;
if (method === "run") {
const collectAsyncLeaks = config.detectAsyncLeaks ? detectAsyncLeaks(file.filepath, workerState.ctx.projectName) : void 0;
await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => startTests([file], testRunner));
const leaks = await collectAsyncLeaks?.();
if (leaks?.length) workerState.rpc.onAsyncLeaks(leaks);
} else await traces.$(`vitest.test.runner.${method}.module`, { attributes: { "code.file.path": file.filepath } }, () => collectTests([file], testRunner));
// reset after tests, because user might call `vi.setConfig` in setupFile
vi.resetConfig();
// mocks should not affect different files
vi.restoreAllMocks();
}
});
await traces.$("vitest.runtime.coverage.stop", () => stopCoverageInsideWorker(config.coverage, moduleRunner, { isolate: config.isolate }));
}
let _moduleRunner;
const evaluatedModules = new VitestEvaluatedModules();
const moduleExecutionInfo = /* @__PURE__ */ new Map();
async function startModuleRunner(options) {
if (_moduleRunner) return _moduleRunner;
process.exit = (code = process.exitCode || 0) => {
throw new Error(`process.exit unexpectedly called with "${code}"`);
};
const state = () => getSafeWorkerState() || options.state;
listenForErrors(state);
if (options.state.config.experimental.viteModuleRunner === false) {
const root = options.state.config.root;
let mocker;
if (options.state.config.experimental.nodeLoader !== false) {
// this additionally imports acorn/magic-string
const { NativeModuleMocker } = await import('./nativeModuleMocker.BLzfffNb.js');
mocker = new NativeModuleMocker({
async resolveId(id, importer) {
// TODO: use import.meta.resolve instead
return state().rpc.resolve(id, importer, "__vitest__");
},
root,
moduleDirectories: state().config.deps.moduleDirectories || ["/node_modules/"],
traces: options.traces || new Traces({ enabled: false }),
getCurrentTestFilepath() {
return state().filepath;
},
spyModule
});
}
_moduleRunner = new NativeModuleRunner(root, mocker);
return _moduleRunner;
}
_moduleRunner = startVitestModuleRunner(options);
return _moduleRunner;
}
let _currentEnvironment;
let _environmentTime;
/** @experimental */
async function setupBaseEnvironment(context) {
if (context.config.experimental.viteModuleRunner === false) {
const { setupNodeLoaderHooks } = await import('./native.DPzPHdi5.js');
await setupNodeLoaderHooks(context);
}
const startTime = performance.now();
const { environment: { name: environmentName, options: environmentOptions }, rpc, config } = context;
// we could load @vite/env, but it would take ~8ms, while this takes ~0,02ms
if (context.config.serializedDefines) try {
runInThisContext(`(() =>{\n${context.config.serializedDefines}})()`, {
lineOffset: 1,
filename: "virtual:load-defines.js"
});
} catch (error) {
throw new Error(`Failed to load custom "defines": ${error.message}`);
}
const otel = context.traces;
const { environment, loader } = await loadEnvironment(environmentName, config.root, rpc, otel, context.config.experimental.viteModuleRunner);
_currentEnvironment = environment;
const env = await otel.$("vitest.runtime.environment.setup", { attributes: {
"vitest.environment": environment.name,
"vitest.environment.vite_environment": environment.viteEnvironment || environment.name
} }, () => environment.setup(globalThis, environmentOptions || config.environmentOptions || {}));
_environmentTime = performance.now() - startTime;
if (config.chaiConfig) setupChaiConfig(config.chaiConfig);
return async () => {
await otel.$("vitest.runtime.environment.teardown", () => env.teardown(globalThis));
await loader?.close();
};
}
/** @experimental */
async function runBaseTests(method, state, traces) {
const { ctx } = state;
state.environment = _currentEnvironment;
state.durations.environment = _environmentTime;
// state has new context, but we want to reuse existing ones
state.evaluatedModules = evaluatedModules;
state.moduleExecutionInfo = moduleExecutionInfo;
provideWorkerState(globalThis, state);
if (ctx.invalidates) ctx.invalidates.forEach((filepath) => {
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
ctx.files.forEach((i) => {
const filepath = i.filepath;
(state.evaluatedModules.fileToModulesMap.get(filepath) || []).forEach((module) => {
state.evaluatedModules.invalidateModule(module);
});
});
const moduleRunner = await startModuleRunner({
state,
evaluatedModules: state.evaluatedModules,
spyModule,
createImportMeta: createNodeImportMeta,
traces
});
emitModuleRunner(moduleRunner);
await run(method, ctx.files, ctx.config, moduleRunner, _currentEnvironment, traces);
}
export { runBaseTests as r, setupBaseEnvironment as s };

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import { i as init } from './init.d4hAcNdp.js';
if (!process.send) throw new Error("Expected worker to be run in node:child_process");
// Store globals in case tests overwrite them
const processExit = process.exit.bind(process);
const processSend = process.send.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const processRemoveAllListeners = process.removeAllListeners.bind(process);
// Work-around for nodejs/node#55094
if (process.execArgv.some((execArg) => execArg.startsWith("--prof") || execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"))) processOn("SIGTERM", () => processExit());
processOn("error", onError);
function workerInit(options) {
const { runTests } = options;
init({
post: (v) => processSend(v),
on: (cb) => processOn("message", cb),
off: (cb) => processOff("message", cb),
teardown: () => {
processRemoveAllListeners("message");
processOff("error", onError);
},
runTests: (state, traces) => executeTests("run", state, traces),
collectTests: (state, traces) => executeTests("collect", state, traces),
setup: options.setup
});
async function executeTests(method, state, traces) {
try {
await runTests(method, state, traces);
} finally {
process.exit = processExit;
}
}
}
// Prevent leaving worker in loops where it tries to send message to closed main
// thread, errors, and tries to send the error.
function onError(error) {
if (error?.code === "ERR_IPC_CHANNEL_CLOSED" || error?.code === "EPIPE") processExit(1);
}
export { workerInit as w };
import { isMainThread, parentPort } from 'node:worker_threads';
import { i as init } from './init.d4hAcNdp.js';
if (isMainThread || !parentPort) throw new Error("Expected worker to be run in node:worker_threads");
function workerInit(options) {
const { runTests } = options;
init({
post: (response) => parentPort.postMessage(response),
on: (callback) => parentPort.on("message", callback),
off: (callback) => parentPort.off("message", callback),
teardown: () => parentPort.removeAllListeners("message"),
runTests: async (state, traces) => runTests("run", state, traces),
collectTests: async (state, traces) => runTests("collect", state, traces),
setup: options.setup
});
}
export { workerInit as w };
import { readFileSync } from 'node:fs';
import { isBuiltin } from 'node:module';
import { pathToFileURL } from 'node:url';
import { resolve } from 'pathe';
import { ModuleRunner, EvaluatedModules } from 'vite/module-runner';
import { b as VitestTransport } from './startVitestModuleRunner.CCBQZ9DQ.js';
import { e as environments } from './index.DC7d2Pf8.js';
import { serializeValue } from '@vitest/utils/serialize';
import { serializeError } from '@vitest/utils/error';
import { disableDefaultColors } from 'tinyrainbow';
import { T as Traces } from './traces.DT5aQ62U.js';
import { o as onCancel, a as rpcDone, c as createRuntimeRpc } from './rpc.MzXet3jl.js';
import { createStackString, parseStacktrace } from '@vitest/utils/source-map';
import { s as setupInspect } from './inspector.CvyFGlXm.js';
import { V as VitestEvaluatedModules } from './evaluatedModules.Dg1zASAC.js';
import { E as EnvironmentTeardownError } from './utils.BX5Fg8C4.js';
function isBuiltinEnvironment(env) {
return env in environments;
}
const isWindows = process.platform === "win32";
const _loaders = /* @__PURE__ */ new Map();
function createEnvironmentLoader(root, rpc) {
const cachedLoader = _loaders.get(root);
if (!cachedLoader || cachedLoader.isClosed()) {
_loaders.delete(root);
const moduleRunner = new ModuleRunner({
hmr: false,
sourcemapInterceptor: "prepareStackTrace",
transport: new VitestTransport({
async fetchModule(id, importer, options) {
const result = await rpc.fetch(id, importer, "__vitest__", options);
if ("cached" in result) return {
code: readFileSync(result.tmp, "utf-8"),
...result
};
if (isWindows && "externalize" in result)
// TODO: vitest returns paths for external modules, but Vite returns file://
// https://github.com/vitejs/vite/pull/20449
result.externalize = isBuiltin(id) || /^(?:node:|data:|http:|https:|file:)/.test(id) ? result.externalize : pathToFileURL(result.externalize).toString();
return result;
},
async resolveId(id, importer) {
return rpc.resolve(id, importer, "__vitest__");
}
}, new EvaluatedModules(), /* @__PURE__ */ new WeakMap())
});
_loaders.set(root, moduleRunner);
}
return _loaders.get(root);
}
async function loadNativeEnvironment(name, root, traces) {
const packageId = name[0] === "." || name[0] === "/" ? pathToFileURL(resolve(root, name)).toString() : import.meta.resolve(`vitest-environment-${name}`, pathToFileURL(root).toString());
return resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => import(packageId)));
}
function resolveEnvironmentFromModule(name, packageId, pkg) {
if (!pkg || !pkg.default || typeof pkg.default !== "object") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "setup" or/and "setupVM" method.`);
const environment = pkg.default;
if (environment.transformMode != null && environment.transformMode !== "web" && environment.transformMode !== "ssr") throw new TypeError(`Environment "${name}" is not a valid environment. Path "${packageId}" should export default object with a "transformMode" method equal to "ssr" or "web", received "${environment.transformMode}".`);
if (environment.transformMode) {
console.warn(`The Vitest environment ${environment.name} defines the "transformMode". This options was deprecated in Vitest 4 and will be removed in the next major version. Please, use "viteEnvironment" instead.`);
// keep for backwards compat
environment.viteEnvironment ??= environment.transformMode === "ssr" ? "ssr" : "client";
}
return environment;
}
async function loadEnvironment(name, root, rpc, traces, viteModuleRunner) {
if (isBuiltinEnvironment(name)) return { environment: environments[name] };
if (!viteModuleRunner) return { environment: await loadNativeEnvironment(name, root, traces) };
const loader = createEnvironmentLoader(root, rpc);
const packageId = name[0] === "." || name[0] === "/" ? resolve(root, name) : (await traces.$("vitest.runtime.environment.resolve", () => rpc.resolve(`vitest-environment-${name}`, void 0, "__vitest__")))?.id ?? resolve(root, name);
return {
environment: resolveEnvironmentFromModule(name, packageId, await traces.$("vitest.runtime.environment.import", () => loader.import(packageId))),
loader
};
}
const cleanupListeners = /* @__PURE__ */ new Set();
const moduleRunnerListeners = /* @__PURE__ */ new Set();
function onCleanup(cb) {
cleanupListeners.add(cb);
}
async function cleanup() {
await Promise.all([...cleanupListeners].map((l) => l()));
}
function onModuleRunner(cb) {
moduleRunnerListeners.add(cb);
}
function emitModuleRunner(moduleRunner) {
moduleRunnerListeners.forEach((l) => l(moduleRunner));
}
// Store globals in case tests overwrite them
const processListeners = process.listeners.bind(process);
const processOn = process.on.bind(process);
const processOff = process.off.bind(process);
const dispose = [];
function listenForErrors(state) {
dispose.forEach((fn) => fn());
dispose.length = 0;
function catchError(err, type, event) {
const worker = state();
// if there is another listener, assume that it's handled by user code
// one is Vitest's own listener
if (processListeners(event).length > 1) return;
const error = serializeValue(err);
if (typeof error === "object" && error != null) {
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
}
state().rpc.onUnhandledError(error, type);
}
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException");
const unhandledRejection = (e) => catchError(e, "Unhandled Rejection", "unhandledRejection");
processOn("uncaughtException", uncaughtException);
processOn("unhandledRejection", unhandledRejection);
dispose.push(() => {
processOff("uncaughtException", uncaughtException);
processOff("unhandledRejection", unhandledRejection);
});
}
const resolvingModules = /* @__PURE__ */ new Set();
async function execute(method, ctx, worker, traces) {
const prepareStart = performance.now();
const cleanups = [setupInspect(ctx)];
// RPC is used to communicate between worker (be it a thread worker or child process or a custom implementation) and the main thread
const rpc = ctx.rpc;
try {
// do not close the RPC channel so that we can get the error messages sent to the main thread
cleanups.push(async () => {
await Promise.all(rpc.$rejectPendingCalls(({ method, reject }) => {
reject(new EnvironmentTeardownError(`[vitest-worker]: Closing rpc while "${method}" was pending`));
}));
});
const state = {
ctx,
evaluatedModules: new VitestEvaluatedModules(),
resolvingModules,
moduleExecutionInfo: /* @__PURE__ */ new Map(),
config: ctx.config,
environment: null,
durations: {
environment: 0,
prepare: prepareStart
},
rpc,
onCancel,
onCleanup: onCleanup,
providedContext: ctx.providedContext,
onFilterStackTrace(stack) {
return createStackString(parseStacktrace(stack));
},
metaEnv: createImportMetaEnvProxy()
};
const methodName = method === "collect" ? "collectTests" : "runTests";
if (!worker[methodName] || typeof worker[methodName] !== "function") throw new TypeError(`Test worker should expose "runTests" method. Received "${typeof worker.runTests}".`);
await worker[methodName](state, traces);
} finally {
await rpcDone().catch(() => {});
await Promise.all(cleanups.map((fn) => fn())).catch(() => {});
}
}
function run(ctx, worker, traces) {
return execute("run", ctx, worker, traces);
}
function collect(ctx, worker, traces) {
return execute("collect", ctx, worker, traces);
}
async function teardown() {
await cleanup();
}
const env = process.env;
function createImportMetaEnvProxy() {
// packages/vitest/src/node/plugins/index.ts:146
const booleanKeys = [
"DEV",
"PROD",
"SSR"
];
return new Proxy(env, {
get(_, key) {
if (typeof key !== "string") return;
if (booleanKeys.includes(key)) return !!process.env[key];
return process.env[key];
},
set(_, key, value) {
if (typeof key !== "string") return true;
if (booleanKeys.includes(key)) process.env[key] = value ? "1" : "";
else process.env[key] = value;
return true;
}
});
}
const __vitest_worker_response__ = true;
const memoryUsage = process.memoryUsage.bind(process);
let reportMemory = false;
let traces;
/** @experimental */
function init(worker) {
worker.on(onMessage);
if (worker.onModuleRunner) onModuleRunner(worker.onModuleRunner);
let runPromise;
let isRunning = false;
let workerTeardown;
let setupContext;
function send(response) {
worker.post(worker.serialize ? worker.serialize(response) : response);
}
async function onMessage(rawMessage) {
const message = worker.deserialize ? worker.deserialize(rawMessage) : rawMessage;
if (message?.__vitest_worker_request__ !== true) return;
switch (message.type) {
case "start": {
process.env.VITEST_POOL_ID = String(message.poolId);
process.env.VITEST_WORKER_ID = String(message.workerId);
reportMemory = message.options.reportMemory;
if (message.context.config.isAgent) disableDefaultColors();
traces ??= await new Traces({
enabled: message.traces.enabled,
sdkPath: message.traces.sdkPath
}).waitInit();
const { environment, config, pool } = message.context;
const context = traces.getContextFromCarrier(message.traces.otelCarrier);
// record telemetry as part of "start"
traces.recordInitSpan(context);
try {
setupContext = {
environment,
config,
pool,
rpc: createRuntimeRpc(worker),
projectName: config.name || "",
traces
};
workerTeardown = await traces.$("vitest.runtime.setup", { context }, () => worker.setup?.(setupContext));
send({
type: "started",
__vitest_worker_response__
});
} catch (error) {
send({
type: "started",
__vitest_worker_response__,
error: serializeError(error)
});
}
break;
}
case "run":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.run", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => run({
...setupContext,
...message.context
}, worker, traces).catch((error) => serializeError(error)));
send({
type: "testfileFinished",
__vitest_worker_response__,
error: await runPromise,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "collect":
// Prevent concurrent execution if worker is already running
if (isRunning) {
send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(/* @__PURE__ */ new Error("[vitest-worker]: Worker is already running tests"))
});
return;
}
try {
process.env.VITEST_WORKER_ID = String(message.context.workerId);
} catch (error) {
return send({
type: "testfileFinished",
__vitest_worker_response__,
error: serializeError(error),
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
}
isRunning = true;
try {
const tracesContext = traces.getContextFromCarrier(message.otelCarrier);
runPromise = traces.$("vitest.runtime.collect", {
context: tracesContext,
attributes: {
"vitest.worker.specifications": traces.isEnabled() ? getFilesWithLocations(message.context.files) : [],
"vitest.worker.id": message.context.workerId
}
}, () => collect({
...setupContext,
...message.context
}, worker, traces).catch((error) => serializeError(error)));
send({
type: "testfileFinished",
__vitest_worker_response__,
error: await runPromise,
usedMemory: reportMemory ? memoryUsage().heapUsed : void 0
});
} finally {
runPromise = void 0;
isRunning = false;
}
break;
case "stop":
await runPromise;
try {
const context = traces.getContextFromCarrier(message.otelCarrier);
const error = await traces.$("vitest.runtime.teardown", { context }, async () => {
const error = await teardown().catch((error) => serializeError(error));
await workerTeardown?.();
return error;
});
await traces.finish();
send({
type: "stopped",
error,
__vitest_worker_response__
});
} catch (error) {
send({
type: "stopped",
error: serializeError(error),
__vitest_worker_response__
});
}
worker.teardown?.();
break;
}
}
}
function getFilesWithLocations(files) {
return files.flatMap((file) => {
if (!file.testLocations) return file.filepath;
return file.testLocations.map((location) => {
return `${file}:${location}`;
});
});
}
export { listenForErrors as a, emitModuleRunner as e, init as i, loadEnvironment as l };
import module$1, { isBuiltin } from 'node:module';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { automockModule, createManualModuleSource, collectModuleExports } from '@vitest/mocker/transforms';
import { cleanUrl, createDefer } from '@vitest/utils/helpers';
import { p as parse } from './acorn.B2iPLyUM.js';
import { isAbsolute } from 'pathe';
import { t as toBuiltin } from './modules.BJuCwlRJ.js';
import { B as BareModuleMocker, n as normalizeModuleId } from './startVitestModuleRunner.CCBQZ9DQ.js';
import 'node:fs';
import './utils.BX5Fg8C4.js';
import '@vitest/utils/timers';
import '../path.js';
import 'node:path';
import '../module-evaluator.js';
import 'node:vm';
import 'vite/module-runner';
import './traces.DT5aQ62U.js';
import '@vitest/mocker';
import '@vitest/mocker/redirect';
class NativeModuleMocker extends BareModuleMocker {
wrapDynamicImport(moduleFactory) {
if (typeof moduleFactory === "function") return new Promise((resolve, reject) => {
this.resolveMocks().finally(() => {
moduleFactory().then(resolve, reject);
});
});
return moduleFactory;
}
resolveMockedModule(url, parentURL) {
// don't mock modules inside of packages because there is
// a high chance that it uses `require` which is not mockable
// because we use top-level await in "manual" mocks.
// for the sake of consistency we don't support mocking anything at all
if (parentURL.includes("/node_modules/")) return;
const moduleId = normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url);
const mockedModule = this.getDependencyMock(moduleId);
if (!mockedModule) return;
if (mockedModule.type === "redirect") return {
url: pathToFileURL(mockedModule.redirect).toString(),
shortCircuit: true
};
if (mockedModule.type === "automock" || mockedModule.type === "autospy") return {
url: injectQuery(url, parentURL, `mock=${mockedModule.type}`),
shortCircuit: true
};
if (mockedModule.type === "manual") return {
url: injectQuery(url, parentURL, "mock=manual"),
shortCircuit: true
};
}
loadAutomock(url, result) {
const moduleId = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
let source;
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(moduleId);
const exports$1 = Object.keys(builtinModule);
source = `
import * as builtinModule from '${toBuiltin(moduleId)}?mock=actual'
${exports$1.map((key, index) => {
return `
const __${index} = builtinModule["${key}"]
export { __${index} as "${key}" }
`;
}).join("")}`;
} else source = result.source?.toString();
if (source == null) return;
const mockType = url.includes("mock=automock") ? "automock" : "autospy";
const transformedCode = transformCode(source, result.format || "module", moduleId);
try {
const ms = automockModule(transformedCode, mockType, (code) => parse(code, {
sourceType: "module",
ecmaVersion: "latest"
}), { id: moduleId });
return {
format: "module",
source: `${ms.toString()}\n//# sourceMappingURL=${genSourceMapUrl(ms.generateMap({
hires: "boundary",
source: moduleId
}))}`,
shortCircuit: true
};
} catch (cause) {
throw new Error(`Cannot automock '${url}' because it failed to parse.`, { cause });
}
}
loadManualMock(url, result) {
const moduleId = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
// should not be possible
if (this.getDependencyMock(moduleId)?.type !== "manual") {
console.warn(`Vitest detected unregistered manual mock ${moduleId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
return;
}
if (isBuiltin(moduleId)) {
const builtinModule = getBuiltinModule(toBuiltin(moduleId));
return {
format: "module",
source: createManualModuleSource(moduleId, Object.keys(builtinModule)),
shortCircuit: true
};
}
if (!result.source) return;
const transformedCode = transformCode(result.source.toString(), result.format || "module", moduleId);
if (transformedCode == null) return;
const format = result.format?.startsWith("module") ? "module" : "commonjs";
try {
return {
format: "module",
source: createManualModuleSource(moduleId, collectModuleExports(moduleId, transformedCode, format)),
shortCircuit: true
};
} catch (cause) {
throw new Error(`Failed to mock '${url}'. See the cause for more information.`, { cause });
}
}
processedModules = /* @__PURE__ */ new Map();
checkCircularManualMock(url) {
const id = cleanUrl(normalizeModuleId(url.startsWith("file://") ? fileURLToPath(url) : url));
this.processedModules.set(id, (this.processedModules.get(id) ?? 0) + 1);
// the module is mocked and requested a second time, let's resolve
// the factory function that will redefine the exports later
if (this.originalModulePromises.has(id)) {
const factoryPromise = this.factoryPromises.get(id);
this.originalModulePromises.get(id)?.resolve({ __factoryPromise: factoryPromise });
}
}
originalModulePromises = /* @__PURE__ */ new Map();
factoryPromises = /* @__PURE__ */ new Map();
// potential performance improvement:
// store by URL, not ids, no need to call url.*to* methods and normalizeModuleId
getFactoryModule(id) {
const mock = this.getMockerRegistry().getById(id);
if (!mock || mock.type !== "manual") throw new Error(`Mock ${id} wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.`);
const mockResult = mock.resolve();
if (mockResult instanceof Promise) {
// to avoid circular dependency, we resolve this function as {__factoryPromise} in `checkCircularManualMock`
// when it's requested the second time. then the exports are exposed as `undefined`,
// but later redefined when the promise is actually resolved
const promise = createDefer();
promise.finally(() => {
this.originalModulePromises.delete(id);
});
mockResult.then(promise.resolve, promise.reject).finally(() => {
this.factoryPromises.delete(id);
});
this.factoryPromises.set(id, mockResult);
this.originalModulePromises.set(id, promise);
// Node.js on windows processes all the files first, and then runs them
// unlike Node.js logic on Mac and Unix where it also runs the code while evaluating
// So on Linux/Mac this `if` won't be hit because `checkCircularManualMock` will resolve it
// And on Windows, the `checkCircularManualMock` will never have `originalModulePromises`
// because `getFactoryModule` is not called until the evaluation phase
// But if we track how many times the module was transformed,
// we can deduce when to return `__factoryPromise` to support circular modules
if ((this.processedModules.get(id) ?? 0) > 1) {
this.processedModules.set(id, (this.processedModules.get(id) ?? 1) - 1);
promise.resolve({ __factoryPromise: mockResult });
}
return promise;
}
return mockResult;
}
importActual(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "actual");
return import(url.toString());
}
importMock(rawId, importer) {
const resolvedId = import.meta.resolve(rawId, pathToFileURL(importer).toString());
// file is already mocked
if (resolvedId.includes("mock=")) return import(resolvedId);
const filename = fileURLToPath(resolvedId);
const external = !isAbsolute(filename) || this.isModuleDirectory(resolvedId) ? normalizeModuleId(rawId) : null;
// file is not mocked, automock or redirect it
const redirect = this.findMockRedirect(filename, external);
if (redirect) return import(pathToFileURL(redirect).toString());
const url = new URL(resolvedId);
url.searchParams.set("mock", "automock");
return import(url.toString());
}
}
const replacePercentageRE = /%/g;
function injectQuery(url, importer, queryToInject) {
const { search, hash } = new URL(url.replace(replacePercentageRE, "%25"), importer);
return `${cleanUrl(url)}?${queryToInject}${search ? `&${search.slice(1)}` : ""}${hash ?? ""}`;
}
let __require;
function getBuiltinModule(moduleId) {
__require ??= module$1.createRequire(import.meta.url);
return __require(`${moduleId}?mock=actual`);
}
function genSourceMapUrl(map) {
if (typeof map !== "string") map = JSON.stringify(map);
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
}
function transformCode(code, format, filename) {
if (format.includes("typescript")) {
if (!module$1.stripTypeScriptTypes) throw new Error(`Cannot parse '${filename}' because "module.stripTypeScriptTypes" is not supported. Module mocking requires Node.js 22.15 or higher. This is NOT a bug of Vitest.`);
return module$1.stripTypeScriptTypes(code);
}
return code;
}
export { NativeModuleMocker };
import fs from 'node:fs';
import { isBareImport } from '@vitest/utils/helpers';
import { i as isBuiltin, a as isBrowserExternal, t as toBuiltin } from './modules.BJuCwlRJ.js';
import { E as EnvironmentTeardownError, a as getSafeWorkerState } from './utils.BX5Fg8C4.js';
import { pathToFileURL } from 'node:url';
import { normalize, join } from 'pathe';
import { distDir } from '../path.js';
import { VitestModuleEvaluator, unwrapId } from '../module-evaluator.js';
import { isAbsolute, resolve } from 'node:path';
import vm from 'node:vm';
import { MockerRegistry, mockObject, RedirectedModule, AutomockedModule } from '@vitest/mocker';
import { findMockRedirect } from '@vitest/mocker/redirect';
import * as viteModuleRunner from 'vite/module-runner';
import { T as Traces } from './traces.DT5aQ62U.js';
class BareModuleMocker {
static pendingIds = [];
spyModule;
primitives;
registries = /* @__PURE__ */ new Map();
mockContext = { callstack: null };
_otel;
constructor(options) {
this.options = options;
this._otel = options.traces;
this.primitives = {
Object,
Error,
Function,
RegExp,
Symbol: globalThis.Symbol,
Array,
Map
};
if (options.spyModule) this.spyModule = options.spyModule;
}
get root() {
return this.options.root;
}
get moduleDirectories() {
return this.options.moduleDirectories || [];
}
getMockerRegistry() {
const suite = this.getSuiteFilepath();
if (!this.registries.has(suite)) this.registries.set(suite, new MockerRegistry());
return this.registries.get(suite);
}
reset() {
this.registries.clear();
}
invalidateModuleById(_id) {
// implemented by mockers that control the module runner
}
isModuleDirectory(path) {
return this.moduleDirectories.some((dir) => path.includes(dir));
}
getSuiteFilepath() {
return this.options.getCurrentTestFilepath() || "global";
}
createError(message, codeFrame) {
const Error = this.primitives.Error;
const error = new Error(message);
Object.assign(error, { codeFrame });
return error;
}
async resolveId(rawId, importer) {
return this._otel.$("vitest.mocker.resolve_id", { attributes: {
"vitest.module.raw_id": rawId,
"vitest.module.importer": rawId
} }, async (span) => {
const result = await this.options.resolveId(rawId, importer);
if (!result) {
span.addEvent("could not resolve id, fallback to unresolved values");
const id = normalizeModuleId(rawId);
span.setAttributes({
"vitest.module.id": id,
"vitest.module.url": rawId,
"vitest.module.external": id,
"vitest.module.fallback": true
});
return {
id,
url: rawId,
external: id
};
}
// external is node_module or unresolved module
// for example, some people mock "vscode" and don't have it installed
const external = !isAbsolute(result.file) || this.isModuleDirectory(result.file) ? normalizeModuleId(rawId) : null;
const id = normalizeModuleId(result.id);
span.setAttributes({
"vitest.module.id": id,
"vitest.module.url": result.url,
"vitest.module.external": external ?? false
});
return {
...result,
id,
external
};
});
}
async resolveMocks() {
if (!BareModuleMocker.pendingIds.length) return;
const resolveMock = async (mock) => {
const { id, url, external } = await this.resolveId(mock.id, mock.importer);
if (mock.action === "unmock") this.unmockPath(id);
if (mock.action === "mock") this.mockPath(mock.id, id, url, external, mock.type, mock.factory);
};
// group consecutive mocks of the same action type together,
// resolve in parallel inside each group, but run groups sequentially
// to preserve mock/unmock ordering
const groups = groupByConsecutiveAction(BareModuleMocker.pendingIds);
for (const group of groups) await Promise.all(group.map(resolveMock));
BareModuleMocker.pendingIds = [];
}
// public method to avoid circular dependency
getMockContext() {
return this.mockContext;
}
// path used to store mocked dependencies
getMockPath(dep) {
return `mock:${dep}`;
}
getDependencyMock(id) {
return this.getMockerRegistry().getById(fixLeadingSlashes(id));
}
getDependencyMockByUrl(url) {
return this.getMockerRegistry().get(url);
}
findMockRedirect(mockPath, external) {
return findMockRedirect(this.root, mockPath, external);
}
mockObject(object, mockExportsOrModuleType, moduleType) {
let mockExports;
if (mockExportsOrModuleType === "automock" || mockExportsOrModuleType === "autospy") {
moduleType = mockExportsOrModuleType;
mockExports = void 0;
} else mockExports = mockExportsOrModuleType;
moduleType ??= "automock";
const createMockInstance = this.spyModule?.createMockInstance;
if (!createMockInstance) throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
return mockObject({
globalConstructors: this.primitives,
createMockInstance,
type: moduleType
}, object, mockExports);
}
unmockPath(id) {
this.getMockerRegistry().deleteById(id);
this.invalidateModuleById(id);
}
mockPath(originalId, id, url, external, mockType, factory) {
const registry = this.getMockerRegistry();
if (mockType === "manual") registry.register("manual", originalId, id, url, factory);
else if (mockType === "autospy") registry.register("autospy", originalId, id, url);
else {
const redirect = this.findMockRedirect(id, external);
if (redirect) registry.register("redirect", originalId, id, url, redirect);
else registry.register("automock", originalId, id, url);
}
// every time the mock is registered, we remove the previous one from the cache
this.invalidateModuleById(id);
}
async importActual(_rawId, _importer, _callstack) {
throw new Error(`importActual is not implemented`);
}
async importMock(_rawId, _importer, _callstack) {
throw new Error(`importMock is not implemented`);
}
queueMock(id, importer, factoryOrOptions) {
const mockType = getMockType(factoryOrOptions);
BareModuleMocker.pendingIds.push({
action: "mock",
id,
importer,
factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
type: mockType
});
}
queueUnmock(id, importer) {
BareModuleMocker.pendingIds.push({
action: "unmock",
id,
importer
});
}
}
function getMockType(factoryOrOptions) {
if (!factoryOrOptions) return "automock";
if (typeof factoryOrOptions === "function") return "manual";
return factoryOrOptions.spy ? "autospy" : "automock";
}
// unique id that is not available as "$bare_import" like "test"
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
const prefixedBuiltins = new Set([
"node:sea",
"node:sqlite",
"node:test",
"node:test/reporters"
]);
const isWindows$1 = process.platform === "win32";
// transform file url to id
// virtual:custom -> virtual:custom
// \0custom -> \0custom
// /root/id -> /id
// /root/id.js -> /id.js
// C:/root/id.js -> /id.js
// C:\root\id.js -> /id.js
// TODO: expose this in vite/module-runner
function normalizeModuleId(file) {
if (prefixedBuiltins.has(file)) return file;
// if it's not in the root, keep it as a path, not a URL
return slash(file).replace(/^\/@fs\//, isWindows$1 ? "" : "/").replace(/^node:/, "").replace(/^\/+/, "/").replace(/^file:\//, "/");
}
const windowsSlashRE = /\\/g;
function slash(p) {
return p.replace(windowsSlashRE, "/");
}
function groupByConsecutiveAction(mocks) {
const groups = [];
for (const mock of mocks) {
const last = groups.at(-1);
if (last?.[0].action === mock.action) last.push(mock);
else groups.push([mock]);
}
return groups;
}
const multipleSlashRe = /^\/+/;
// module-runner incorrectly replaces file:///path with `///path`
function fixLeadingSlashes(id) {
if (id.startsWith("//")) return id.replace(multipleSlashRe, "/");
return id;
}
// copied from vite
// https://github.com/vitejs/vite/blob/4417b4f305623b2850bd6ae6553834c017694672/packages/vite/src/shared/utils.ts
// https://github.com/vitejs/vite/blob/4417b4f305623b2850bd6ae6553834c017694672/packages/vite/src/node/utils.ts
const postfixRE = /[?#].*$/;
const trailingSeparatorRE = /[?&]$/;
function cleanUrl(url) {
return url.replace(postfixRE, "");
}
function splitFileAndPostfix(path) {
const file = cleanUrl(path);
return {
file,
postfix: path.slice(file.length)
};
}
function injectQuery(url, queryToInject) {
const { file, postfix } = splitFileAndPostfix(url);
return `${file}?${queryToInject}${postfix[0] === "?" ? `&${postfix.slice(1)}` : postfix}`;
}
function removeQuery(url, queryToRemove) {
return url.replace(new RegExp(`([?&])${queryToRemove}(?:&|$)`), "$1").replace(trailingSeparatorRE, "");
}
const spyModulePath = resolve(distDir, "spy.js");
class VitestMocker extends BareModuleMocker {
filterPublicKeys;
constructor(moduleRunner, options) {
super(options);
this.moduleRunner = moduleRunner;
this.options = options;
const context = this.options.context;
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
const Symbol = this.primitives.Symbol;
this.filterPublicKeys = [
"__esModule",
Symbol.asyncIterator,
Symbol.hasInstance,
Symbol.isConcatSpreadable,
Symbol.iterator,
Symbol.match,
Symbol.matchAll,
Symbol.replace,
Symbol.search,
Symbol.split,
Symbol.species,
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables
];
}
get evaluatedModules() {
return this.moduleRunner.evaluatedModules;
}
async initializeSpyModule() {
if (this.spyModule) return;
this.spyModule = await this.moduleRunner.import(spyModulePath);
}
reset() {
this.registries.clear();
}
invalidateModuleById(id) {
const mockId = this.getMockPath(id);
const node = this.evaluatedModules.getModuleById(mockId);
if (node) {
this.evaluatedModules.invalidateModule(node);
node.mockedExports = void 0;
}
}
ensureModule(id, url) {
const node = this.evaluatedModules.ensureModule(id, url);
// TODO
node.meta = {
id,
url,
code: "",
file: null,
invalidate: false
};
return node;
}
async callFunctionMock(id, url, mock) {
const node = this.ensureModule(id, url);
if (node.exports) return node.exports;
const exports$1 = await mock.resolve();
const moduleExports = new Proxy(exports$1, { get: (target, prop) => {
const val = target[prop];
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
if (prop === "then") {
if (target instanceof Promise) return target.then.bind(target);
} else if (!(prop in target)) {
if (this.filterPublicKeys.includes(prop)) return;
throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
`, `vi.mock(import("${mock.raw}"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})`);
}
return val;
} });
node.exports = moduleExports;
return moduleExports;
}
async importActual(rawId, importer, callstack) {
const { url } = await this.resolveId(rawId, importer);
const actualUrl = injectQuery(url, "_vitest_original");
const node = await this.moduleRunner.fetchModule(actualUrl, importer);
return await this.moduleRunner.cachedRequest(node.url, node, callstack || [importer], void 0, true);
}
async importMock(rawId, importer) {
const { id, url, external } = await this.resolveId(rawId, importer);
let mock = this.getDependencyMock(id);
if (!mock) {
const redirect = this.findMockRedirect(id, external);
if (redirect) mock = new RedirectedModule(rawId, id, rawId, redirect);
else mock = new AutomockedModule(rawId, id, rawId);
}
if (mock.type === "automock" || mock.type === "autospy") {
const node = await this.moduleRunner.fetchModule(url, importer);
const mod = await this.moduleRunner.cachedRequest(url, node, [importer], void 0, true);
const Object = this.primitives.Object;
return this.mockObject(mod, Object.create(Object.prototype), mock.type);
}
if (mock.type === "manual") return this.callFunctionMock(id, url, mock);
const node = await this.moduleRunner.fetchModule(mock.redirect);
return this.moduleRunner.cachedRequest(mock.redirect, node, [importer], void 0, true);
}
async requestWithMockedModule(url, evaluatedNode, callstack, mock) {
return this._otel.$("vitest.mocker.evaluate", async (span) => {
const mockId = this.getMockPath(evaluatedNode.id);
span.setAttributes({
"vitest.module.id": mockId,
"vitest.mock.type": mock.type,
"vitest.mock.id": mock.id,
"vitest.mock.url": mock.url,
"vitest.mock.raw": mock.raw
});
if (mock.type === "automock" || mock.type === "autospy") {
const cache = this.evaluatedModules.getModuleById(mockId);
if (cache && cache.mockedExports) return cache.mockedExports;
const Object = this.primitives.Object;
// we have to define a separate object that will copy all properties into itself
// and can't just use the same `exports` define automatically by Vite before the evaluator
const exports$1 = Object.create(null);
Object.defineProperty(exports$1, Symbol.toStringTag, {
value: "Module",
configurable: true,
writable: true
});
const node = this.ensureModule(mockId, this.getMockPath(evaluatedNode.url));
node.meta = evaluatedNode.meta;
node.file = evaluatedNode.file;
node.mockedExports = exports$1;
const mod = await this.moduleRunner.cachedRequest(url, node, callstack, void 0, true);
this.mockObject(mod, exports$1, mock.type);
return exports$1;
}
if (mock.type === "manual" && !callstack.includes(mockId) && !callstack.includes(url)) try {
callstack.push(mockId);
// this will not work if user does Promise.all(import(), import())
// we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
// maybe we should improve mock API in the future?
this.mockContext.callstack = callstack;
return await this.callFunctionMock(mockId, this.getMockPath(url), mock);
} finally {
this.mockContext.callstack = null;
const indexMock = callstack.indexOf(mockId);
callstack.splice(indexMock, 1);
}
else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) {
span.setAttribute("vitest.mock.redirect", mock.redirect);
return mock.redirect;
}
});
}
async mockedRequest(url, evaluatedNode, callstack) {
const mock = this.getDependencyMock(evaluatedNode.id);
if (!mock) return;
return this.requestWithMockedModule(url, evaluatedNode, callstack, mock);
}
}
class VitestTransport {
constructor(options, evaluatedModules, callstacks) {
this.options = options;
this.evaluatedModules = evaluatedModules;
this.callstacks = callstacks;
}
async invoke(event) {
if (event.type !== "custom") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support Vite HMR events.`) };
if (event.event !== "vite:invoke") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support ${event.event} event.`) };
const { name, data } = event.data;
if (name === "getBuiltins")
// we return an empty array here to avoid client-side builtin check,
// as we need builtins to go through `fetchModule`
return { result: [] };
if (name !== "fetchModule") return { error: /* @__PURE__ */ new Error(`Unknown method: ${name}. Expected "fetchModule".`) };
try {
return { result: await this.options.fetchModule(...data) };
} catch (cause) {
if (cause instanceof EnvironmentTeardownError) {
const [id, importer] = data;
let message = `Cannot load '${id}'${importer ? ` imported from ${importer}` : ""} after the environment was torn down. This is not a bug in Vitest.`;
const moduleNode = importer ? this.evaluatedModules.getModuleById(importer) : void 0;
const callstack = moduleNode ? this.callstacks.get(moduleNode) : void 0;
if (callstack) message += ` The last recorded callstack:\n- ${[
...callstack,
importer,
id
].reverse().join("\n- ")}`;
const error = new EnvironmentTeardownError(message);
if (cause.stack) error.stack = cause.stack.replace(cause.message, error.message);
return { error };
}
return { error: cause };
}
}
}
const createNodeImportMeta = (modulePath) => {
if (!viteModuleRunner.createDefaultImportMeta) throw new Error(`createNodeImportMeta is not supported in this version of Vite.`);
const defaultMeta = viteModuleRunner.createDefaultImportMeta(modulePath);
const href = defaultMeta.url;
const importMetaResolver = createImportMetaResolver();
return {
...defaultMeta,
main: false,
resolve(id, parent) {
return (importMetaResolver ?? defaultMeta.resolve)(id, parent ?? href);
}
};
};
function createImportMetaResolver() {
if (!import.meta.resolve) return;
return (specifier, importer) => import.meta.resolve(specifier, importer);
}
// @ts-expect-error overriding private method
class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
mocker;
moduleExecutionInfo;
_otel;
_callstacks;
constructor(vitestOptions) {
const options = vitestOptions;
const evaluatedModules = options.evaluatedModules;
const callstacks = /* @__PURE__ */ new WeakMap();
const transport = new VitestTransport(options.transport, evaluatedModules, callstacks);
super({
transport,
hmr: false,
evaluatedModules,
sourcemapInterceptor: "prepareStackTrace",
createImportMeta: vitestOptions.createImportMeta
}, options.evaluator);
this.vitestOptions = vitestOptions;
this._callstacks = callstacks;
this._otel = vitestOptions.traces || new Traces({ enabled: false });
this.moduleExecutionInfo = options.getWorkerState().moduleExecutionInfo;
this.mocker = options.mocker || new VitestMocker(this, {
spyModule: options.spyModule,
context: options.vm?.context,
traces: this._otel,
resolveId: options.transport.resolveId,
get root() {
return options.getWorkerState().config.root;
},
get moduleDirectories() {
return options.getWorkerState().config.deps.moduleDirectories || [];
},
getCurrentTestFilepath() {
return options.getWorkerState().filepath;
}
});
if (options.vm) options.vm.context.__vitest_mocker__ = this.mocker;
else Object.defineProperty(globalThis, "__vitest_mocker__", {
configurable: true,
writable: true,
value: this.mocker
});
}
/**
* Vite checks that the module has exports emulating the Node.js behaviour,
* but Vitest is more relaxed.
*
* We should keep the Vite behavior when there is a `strict` flag.
* @internal
*/
processImport(exports$1) {
return exports$1;
}
async import(rawId) {
const resolved = await this._otel.$("vitest.module.resolve_id", { attributes: { "vitest.module.raw_id": rawId } }, async (span) => {
const result = await this.vitestOptions.transport.resolveId(rawId);
if (result) span.setAttributes({
"vitest.module.url": result.url,
"vitest.module.file": result.file,
"vitest.module.id": result.id
});
return result;
});
return super.import(resolved ? resolved.url : rawId);
}
async fetchModule(url, importer) {
return await this.cachedModule(url, importer);
}
_cachedRequest(url, module, callstack = [], metadata) {
// @ts-expect-error "cachedRequest" is private
return super.cachedRequest(url, module, callstack, metadata);
}
/**
* @internal
*/
async cachedRequest(url, mod, callstack = [], metadata, ignoreMock = false) {
// Track for a better error message if dynamic import is not resolved properly
this._callstacks.set(mod, callstack);
if (ignoreMock) return this._cachedRequest(url, mod, callstack, metadata);
let mocked;
if (mod.meta && "mockedModule" in mod.meta) {
const mockedModule = mod.meta.mockedModule;
const mockId = this.mocker.getMockPath(mod.id);
// bypass mock and force "importActual" behavior when:
// - mock was removed by doUnmock (stale mockedModule in meta)
// - self-import: mock factory/file is importing the module it's mocking
const isStale = !this.mocker.getDependencyMock(mod.id);
const isSelfImport = callstack.includes(mockId) || callstack.includes(url) || "redirect" in mockedModule && callstack.includes(mockedModule.redirect);
if (isStale || isSelfImport) {
const node = await this.fetchModule(injectQuery(url, "_vitest_original"));
return this._cachedRequest(node.url, node, callstack, metadata);
}
mocked = await this.mocker.requestWithMockedModule(url, mod, callstack, mockedModule);
} else mocked = await this.mocker.mockedRequest(url, mod, callstack);
if (typeof mocked === "string") {
const node = await this.fetchModule(mocked);
return this._cachedRequest(mocked, node, callstack, metadata);
}
if (mocked != null && typeof mocked === "object") return mocked;
return this._cachedRequest(url, mod, callstack, metadata);
}
/** @internal */
_invalidateSubTreeById(ids, invalidated = /* @__PURE__ */ new Set()) {
for (const id of ids) {
if (invalidated.has(id)) continue;
const node = this.evaluatedModules.getModuleById(id);
if (!node) continue;
invalidated.add(id);
const subIds = Array.from(this.evaluatedModules.idToModuleMap).filter(([, mod]) => mod.importers.has(id)).map(([key]) => key);
if (subIds.length) this._invalidateSubTreeById(subIds, invalidated);
this.evaluatedModules.invalidateModule(node);
}
}
}
const bareVitestRegexp = /^@?vitest(?:\/|$)/;
const normalizedDistDir = normalize(distDir);
const relativeIds = {};
const externalizeMap = /* @__PURE__ */ new Map();
// all Vitest imports always need to be externalized
function getCachedVitestImport(id, state) {
if (id.startsWith("/@fs/") || id.startsWith("\\@fs\\")) id = id.slice(process.platform === "win32" ? 5 : 4);
if (externalizeMap.has(id)) return {
externalize: externalizeMap.get(id),
type: "module"
};
// always externalize Vitest because we import from there before running tests
// so we already have it cached by Node.js
const root = state().config.root;
const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
if (id.includes(distDir) || id.includes(normalizedDistDir)) {
const externalize = id.startsWith("file://") ? id : pathToFileURL(id).toString();
externalizeMap.set(id, externalize);
return {
externalize,
type: "module"
};
}
if (relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
const externalize = pathToFileURL(join(root, id)).toString();
externalizeMap.set(id, externalize);
return {
externalize,
type: "module"
};
}
if (bareVitestRegexp.test(id)) {
externalizeMap.set(id, id);
return {
externalize: id,
type: "module"
};
}
return null;
}
const { readFileSync } = fs;
const VITEST_VM_CONTEXT_SYMBOL = "__vitest_vm_context__";
const cwd = process.cwd();
const isWindows = process.platform === "win32";
function startVitestModuleRunner(options) {
const traces = options.traces;
const state = () => getSafeWorkerState() || options.state;
const rpc = () => state().rpc;
const environment = () => {
const environment = state().environment;
return environment.viteEnvironment || environment.name;
};
const vm = options.context && options.externalModulesExecutor ? {
context: options.context,
externalModulesExecutor: options.externalModulesExecutor
} : void 0;
const evaluator = options.evaluator || new VitestModuleEvaluator(vm, {
traces,
evaluatedModules: options.evaluatedModules,
get moduleExecutionInfo() {
return state().moduleExecutionInfo;
},
get interopDefault() {
return state().config.deps.interopDefault;
},
getCurrentTestFilepath: () => state().filepath
});
const moduleRunner = new VitestModuleRunner({
spyModule: options.spyModule,
evaluatedModules: options.evaluatedModules,
evaluator,
traces,
mocker: options.mocker,
transport: {
async fetchModule(id, importer, options) {
const resolvingModules = state().resolvingModules;
if (isWindows) {
if (id[1] === ":") {
// The drive letter is different for whatever reason, we need to normalize it to CWD
if (id[0] !== cwd[0] && id[0].toUpperCase() === cwd[0].toUpperCase()) id = (cwd[0].toUpperCase() === cwd[0] ? id[0].toUpperCase() : id[0].toLowerCase()) + id.slice(1);
// always mark absolute windows paths, otherwise Vite will externalize it
id = `/@id/${id}`;
}
}
const vitest = getCachedVitestImport(id, state);
if (vitest) return vitest;
// strip _vitest_original query added by importActual so that
// the plugin pipeline sees the original import id (e.g. virtual modules's load hook)
const isImportActual = id.includes("_vitest_original");
if (isImportActual) id = removeQuery(id, "_vitest_original");
const rawId = unwrapId(id);
resolvingModules.add(rawId);
try {
if (VitestMocker.pendingIds.length) await moduleRunner.mocker.resolveMocks();
if (!isImportActual) {
const resolvedMock = moduleRunner.mocker.getDependencyMockByUrl(id);
if (resolvedMock?.type === "manual" || resolvedMock?.type === "redirect") return {
code: "",
file: null,
id: resolvedMock.id,
url: resolvedMock.url,
invalidate: false,
mockedModule: resolvedMock
};
}
if (isBuiltin(rawId)) return {
externalize: rawId,
type: "builtin"
};
if (isBrowserExternal(rawId)) return {
externalize: toBuiltin(rawId),
type: "builtin"
};
// if module is invalidated, the worker will be recreated,
// so cached is always true in a single worker
if (!isImportActual && options?.cached) return { cache: true };
const otelCarrier = traces?.getContextCarrier();
const result = await rpc().fetch(id, importer, environment(), options, otelCarrier);
if ("cached" in result) return {
code: readFileSync(result.tmp, "utf-8"),
...result
};
return result;
} catch (cause) {
// rethrow vite error if it cannot load the module because it's not resolved
if (typeof cause === "object" && cause != null && cause.code === "ERR_LOAD_URL" || typeof cause?.message === "string" && cause.message.includes("Failed to load url") || typeof cause?.message === "string" && cause.message.startsWith("Cannot find module '")) {
const error = new Error(`Cannot find ${isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from ${importer}` : ""}`, { cause });
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
throw cause;
} finally {
resolvingModules.delete(rawId);
}
},
resolveId(id, importer) {
return rpc().resolve(id, importer, environment());
}
},
getWorkerState: state,
vm,
createImportMeta: options.createImportMeta
});
return moduleRunner;
}
export { BareModuleMocker as B, VITEST_VM_CONTEXT_SYMBOL as V, VitestModuleRunner as a, VitestTransport as b, createNodeImportMeta as c, normalizeModuleId as n, startVitestModuleRunner as s };
import { fileURLToPath, pathToFileURL } from 'node:url';
import vm, { isContext, runInContext } from 'node:vm';
import { dirname, basename, extname, normalize, resolve } from 'pathe';
import { l as loadEnvironment, a as listenForErrors, e as emitModuleRunner } from './init.d4hAcNdp.js';
import { distDir } from '../path.js';
import { createCustomConsole } from './console.3WNpx0tS.js';
import fs from 'node:fs';
import { createRequire, Module, isBuiltin } from 'node:module';
import { toArray, splitFileAndPostfix, isBareImport } from '@vitest/utils/helpers';
import { findNearestPackageData } from '@vitest/utils/resolver';
import { dirname as dirname$1 } from 'node:path';
import { CSS_LANGS_RE, KNOWN_ASSET_RE } from '@vitest/utils/constants';
import { getDefaultRequestStubs } from '../module-evaluator.js';
import { s as startVitestModuleRunner, V as VITEST_VM_CONTEXT_SYMBOL, c as createNodeImportMeta } from './startVitestModuleRunner.CCBQZ9DQ.js';
import { p as provideWorkerState } from './utils.BX5Fg8C4.js';
function interopCommonJsModule(interopDefault, mod) {
if (isPrimitive(mod) || Array.isArray(mod) || mod instanceof Promise) return {
keys: [],
moduleExports: {},
defaultExport: mod
};
if (interopDefault !== false && "__esModule" in mod && !isPrimitive(mod.default)) {
const defaultKets = Object.keys(mod.default);
const moduleKeys = Object.keys(mod);
const allKeys = new Set([...defaultKets, ...moduleKeys]);
allKeys.delete("default");
return {
keys: Array.from(allKeys),
moduleExports: new Proxy(mod, { get(mod, prop) {
return mod[prop] ?? mod.default?.[prop];
} }),
defaultExport: mod
};
}
return {
keys: Object.keys(mod).filter((key) => key !== "default"),
moduleExports: mod,
defaultExport: mod
};
}
function isPrimitive(obj) {
return !(obj != null && (typeof obj === "object" || typeof obj === "function"));
}
const SyntheticModule = vm.SyntheticModule;
const SourceTextModule = vm.SourceTextModule;
const _require = createRequire(import.meta.url);
const requiresCache = /* @__PURE__ */ new WeakMap();
class CommonjsExecutor {
context;
requireCache = /* @__PURE__ */ new Map();
publicRequireCache = this.createProxyCache();
moduleCache = /* @__PURE__ */ new Map();
builtinCache = Object.create(null);
extensions = Object.create(null);
fs;
Module;
interopDefault;
constructor(options) {
this.context = options.context;
this.fs = options.fileMap;
this.interopDefault = options.interopDefault;
const primitives = vm.runInContext("({ Object, Array, Error })", this.context);
// eslint-disable-next-line ts/no-this-alias
const executor = this;
this.Module = class Module$1 {
exports;
isPreloading = false;
id;
filename;
loaded;
parent;
children = [];
path;
paths = [];
constructor(id = "", parent) {
this.exports = primitives.Object.create(Object.prototype);
// in our case the path should always be resolved already
this.path = dirname(id);
this.id = id;
this.filename = id;
this.loaded = false;
this.parent = parent;
}
get require() {
const require = requiresCache.get(this);
if (require) return require;
const _require = Module$1.createRequire(this.id);
requiresCache.set(this, _require);
return _require;
}
static getSourceMapsSupport = () => ({
enabled: false,
nodeModules: false,
generatedCode: false
});
static setSourceMapsSupport = () => {
// noop
};
static register = () => {
throw new Error(`[vitest] "register" is not available when running in Vitest.`);
};
static registerHooks = () => {
throw new Error(`[vitest] "registerHooks" is not available when running in Vitest.`);
};
_compile(code, filename) {
const cjsModule = Module$1.wrap(code);
const script = new vm.Script(cjsModule, {
filename,
importModuleDynamically: options.importModuleDynamically
});
// @ts-expect-error mark script with current identifier
script.identifier = filename;
const fn = script.runInContext(executor.context);
const __dirname = dirname(filename);
executor.requireCache.set(filename, this);
try {
fn(this.exports, this.require, this, filename, __dirname);
return this.exports;
} finally {
this.loaded = true;
}
}
// exposed for external use, Node.js does the opposite
static _load = (request, parent, _isMain) => {
return Module$1.createRequire(parent?.filename ?? request)(request);
};
static wrap = (script) => {
return Module$1.wrapper[0] + script + Module$1.wrapper[1];
};
static wrapper = new primitives.Array("(function (exports, require, module, __filename, __dirname) { ", "\n});");
static builtinModules = Module.builtinModules;
static findSourceMap = Module.findSourceMap;
static SourceMap = Module.SourceMap;
static syncBuiltinESMExports = Module.syncBuiltinESMExports;
static _cache = executor.publicRequireCache;
static _extensions = executor.extensions;
static createRequire = (filename) => {
return executor.createRequire(filename);
};
static runMain = () => {
throw new primitives.Error("[vitest] \"runMain\" is not implemented.");
};
// @ts-expect-error not typed
static _resolveFilename = Module._resolveFilename;
// @ts-expect-error not typed
static _findPath = Module._findPath;
// @ts-expect-error not typed
static _initPaths = Module._initPaths;
// @ts-expect-error not typed
static _preloadModules = Module._preloadModules;
// @ts-expect-error not typed
static _resolveLookupPaths = Module._resolveLookupPaths;
// @ts-expect-error not typed
static globalPaths = Module.globalPaths;
static isBuiltin = Module.isBuiltin;
static constants = Module.constants;
static enableCompileCache = Module.enableCompileCache;
static getCompileCacheDir = Module.getCompileCacheDir;
static flushCompileCache = Module.flushCompileCache;
static stripTypeScriptTypes = Module.stripTypeScriptTypes;
static findPackageJSON = Module.findPackageJSON;
static Module = Module$1;
};
this.extensions[".js"] = this.requireJs;
this.extensions[".json"] = this.requireJson;
}
requireJs = (m, filename) => {
const content = this.fs.readFile(filename);
m._compile(content, filename);
};
requireJson = (m, filename) => {
const code = this.fs.readFile(filename);
m.exports = JSON.parse(code);
};
static cjsConditions;
static getCjsConditions() {
if (!CommonjsExecutor.cjsConditions) CommonjsExecutor.cjsConditions = parseCjsConditions(process.execArgv, process.env.NODE_OPTIONS);
return CommonjsExecutor.cjsConditions;
}
createRequire = (filename) => {
const _require = createRequire(filename);
const resolve = (id, options) => {
return _require.resolve(id, {
...options,
conditions: CommonjsExecutor.getCjsConditions()
});
};
const require = ((id) => {
const resolved = resolve(id);
if (extname(resolved) === ".node" || isBuiltin(resolved)) return this.requireCoreModule(resolved);
const module = new this.Module(resolved);
return this.loadCommonJSModule(module, resolved);
});
require.resolve = resolve;
require.resolve.paths = _require.resolve.paths;
Object.defineProperty(require, "extensions", {
get: () => this.extensions,
set: () => {},
configurable: true
});
require.main = void 0;
require.cache = this.publicRequireCache;
return require;
};
createProxyCache() {
return new Proxy(Object.create(null), {
defineProperty: () => true,
deleteProperty: () => true,
set: () => true,
get: (_, key) => this.requireCache.get(key),
has: (_, key) => this.requireCache.has(key),
ownKeys: () => Array.from(this.requireCache.keys()),
getOwnPropertyDescriptor() {
return {
configurable: true,
enumerable: true
};
}
});
}
// very naive implementation for Node.js require
loadCommonJSModule(module, filename) {
const cached = this.requireCache.get(filename);
if (cached) return cached.exports;
const extension = this.findLongestRegisteredExtension(filename);
(this.extensions[extension] || this.extensions[".js"])(module, filename);
return module.exports;
}
findLongestRegisteredExtension(filename) {
const name = basename(filename);
let currentExtension;
let index;
let startIndex = 0;
// eslint-disable-next-line no-cond-assign
while ((index = name.indexOf(".", startIndex)) !== -1) {
startIndex = index + 1;
if (index === 0) continue;
currentExtension = name.slice(index);
if (this.extensions[currentExtension]) return currentExtension;
}
return ".js";
}
getCoreSyntheticModule(identifier) {
if (this.moduleCache.has(identifier)) return this.moduleCache.get(identifier);
const exports$1 = this.require(identifier);
const keys = Object.keys(exports$1);
const module = new SyntheticModule([...keys, "default"], () => {
for (const key of keys) module.setExport(key, exports$1[key]);
module.setExport("default", exports$1);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
getCjsSyntheticModule(path, identifier) {
if (this.moduleCache.has(identifier)) return this.moduleCache.get(identifier);
const exports$1 = this.require(path);
// TODO: technically module should be parsed to find static exports, implement for strict mode in #2854
const { keys, moduleExports, defaultExport } = interopCommonJsModule(this.interopDefault, exports$1);
const module = new SyntheticModule([...keys, "default"], function() {
for (const key of keys) this.setExport(key, moduleExports[key]);
this.setExport("default", defaultExport);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
// TODO: use this in strict mode, when available in #2854
// private _getNamedCjsExports(path: string): Set<string> {
// const cachedNamedExports = this.cjsNamedExportsMap.get(path)
// if (cachedNamedExports) {
// return cachedNamedExports
// }
// if (extname(path) === '.node') {
// const moduleExports = this.require(path)
// const namedExports = new Set(Object.keys(moduleExports))
// this.cjsNamedExportsMap.set(path, namedExports)
// return namedExports
// }
// const code = this.fs.readFile(path)
// const { exports, reexports } = parseCjs(code, path)
// const namedExports = new Set(exports)
// this.cjsNamedExportsMap.set(path, namedExports)
// for (const reexport of reexports) {
// if (isNodeBuiltin(reexport)) {
// const exports = this.require(reexport)
// if (exports !== null && typeof exports === 'object') {
// for (const e of Object.keys(exports)) {
// namedExports.add(e)
// }
// }
// }
// else {
// const require = this.createRequire(path)
// const resolved = require.resolve(reexport)
// const exports = this._getNamedCjsExports(resolved)
// for (const e of exports) {
// namedExports.add(e)
// }
// }
// }
// return namedExports
// }
require(identifier) {
if (extname(identifier) === ".node" || isBuiltin(identifier)) return this.requireCoreModule(identifier);
const module = new this.Module(identifier);
return this.loadCommonJSModule(module, identifier);
}
requireCoreModule(identifier) {
const normalized = identifier.replace(/^node:/, "");
if (this.builtinCache[normalized]) return this.builtinCache[normalized].exports;
const moduleExports = _require(identifier);
if (identifier === "node:module" || identifier === "module") {
const module = new this.Module("/module.js");
module.exports = this.Module;
this.builtinCache[normalized] = module;
return module.exports;
}
this.builtinCache[normalized] = _require.cache[normalized];
// TODO: should we wrap module to rethrow context errors?
return moduleExports;
}
}
// The "module-sync" exports condition (added in Node 22.12/20.19 when
// require(esm) was unflagged) can resolve to ESM files that our CJS
// vm.Script executor cannot handle. We exclude it by passing explicit
// CJS conditions to require.resolve (Node 22.12+).
// Must be a Set because Node's internal resolver calls conditions.has().
// User-specified --conditions/-C flags are respected, except module-sync.
function parseCjsConditions(execArgv, nodeOptions) {
const conditions = [
"node",
"require",
"node-addons"
];
const args = [...execArgv, ...nodeOptions?.split(/\s+/) ?? []];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const eqMatch = arg.match(/^(?:--conditions|-C)=(.+)$/);
if (eqMatch) conditions.push(eqMatch[1]);
else if ((arg === "--conditions" || arg === "-C") && i + 1 < args.length) conditions.push(args[++i]);
}
return new Set(conditions.filter((c) => c !== "module-sync"));
}
const dataURIRegex = /^data:(?<mime>text\/javascript|application\/json|application\/wasm)(?:;(?<encoding>charset=utf-8|base64))?,(?<code>.*)$/;
class EsmExecutor {
moduleCache = /* @__PURE__ */ new Map();
esmLinkMap = /* @__PURE__ */ new WeakMap();
context;
#httpIp = IPnumber("127.0.0.0");
constructor(executor, options) {
this.executor = executor;
this.context = options.context;
}
async evaluateModule(m) {
if (m.status === "unlinked") this.esmLinkMap.set(m, m.link((identifier, referencer) => this.executor.resolveModule(identifier, referencer.identifier)));
await this.esmLinkMap.get(m);
if (m.status === "linked") await m.evaluate();
return m;
}
async createEsModule(fileURL, getCode) {
const cached = this.moduleCache.get(fileURL);
if (cached) return cached;
const promise = this.loadEsModule(fileURL, getCode);
this.moduleCache.set(fileURL, promise);
return promise;
}
async loadEsModule(fileURL, getCode) {
const code = await getCode();
// TODO: should not be allowed in strict mode, implement in #2854
if (fileURL.endsWith(".json")) {
const m = new SyntheticModule(["default"], function() {
const result = JSON.parse(code);
this.setExport("default", result);
});
this.moduleCache.set(fileURL, m);
return m;
}
const m = new SourceTextModule(code, {
identifier: fileURL,
context: this.context,
importModuleDynamically: this.executor.importModuleDynamically,
initializeImportMeta: (meta, mod) => {
meta.url = mod.identifier;
if (mod.identifier.startsWith("file:")) {
const filename = fileURLToPath(mod.identifier);
meta.filename = filename;
meta.dirname = dirname$1(filename);
}
meta.resolve = (specifier, importer) => {
return this.executor.resolve(specifier, importer != null ? importer.toString() : mod.identifier);
};
}
});
this.moduleCache.set(fileURL, m);
return m;
}
async createWebAssemblyModule(fileUrl, getCode) {
const cached = this.moduleCache.get(fileUrl);
if (cached) return cached;
const m = this.loadWebAssemblyModule(getCode(), fileUrl);
this.moduleCache.set(fileUrl, m);
return m;
}
async createNetworkModule(fileUrl) {
// https://nodejs.org/api/esm.html#https-and-http-imports
if (fileUrl.startsWith("http:")) {
const url = new URL(fileUrl);
if (url.hostname !== "localhost" && url.hostname !== "::1" && (IPnumber(url.hostname) & IPmask(8)) !== this.#httpIp) throw new Error(
// we don't know the importer, so it's undefined (the same happens in --pool=threads)
`import of '${fileUrl}' by undefined is not supported: http can only be used to load local resources (use https instead).`
);
}
return this.createEsModule(fileUrl, () => fetch(fileUrl).then((r) => r.text()));
}
async loadWebAssemblyModule(source, identifier) {
const cached = this.moduleCache.get(identifier);
if (cached) return cached;
const wasmModule = await WebAssembly.compile(source);
const exports$1 = WebAssembly.Module.exports(wasmModule);
const imports = WebAssembly.Module.imports(wasmModule);
const moduleLookup = {};
for (const { module } of imports) if (moduleLookup[module] === void 0) moduleLookup[module] = await this.executor.resolveModule(module, identifier);
const evaluateModule = (module) => this.evaluateModule(module);
return new SyntheticModule(exports$1.map(({ name }) => name), async function() {
const importsObject = {};
for (const { module, name } of imports) {
if (!importsObject[module]) importsObject[module] = {};
await evaluateModule(moduleLookup[module]);
importsObject[module][name] = moduleLookup[module].namespace[name];
}
const wasmInstance = new WebAssembly.Instance(wasmModule, importsObject);
for (const { name } of exports$1) this.setExport(name, wasmInstance.exports[name]);
}, {
context: this.context,
identifier
});
}
cacheModule(identifier, module) {
this.moduleCache.set(identifier, module);
}
resolveCachedModule(identifier) {
return this.moduleCache.get(identifier);
}
async createDataModule(identifier) {
const cached = this.moduleCache.get(identifier);
if (cached) return cached;
const match = identifier.match(dataURIRegex);
if (!match || !match.groups) throw new Error("Invalid data URI");
const mime = match.groups.mime;
const encoding = match.groups.encoding;
if (mime === "application/wasm") {
if (!encoding) throw new Error("Missing data URI encoding");
if (encoding !== "base64") throw new Error(`Invalid data URI encoding: ${encoding}`);
const module = this.loadWebAssemblyModule(Buffer.from(match.groups.code, "base64"), identifier);
this.moduleCache.set(identifier, module);
return module;
}
let code = match.groups.code;
if (!encoding || encoding === "charset=utf-8") code = decodeURIComponent(code);
else if (encoding === "base64") code = Buffer.from(code, "base64").toString();
else throw new Error(`Invalid data URI encoding: ${encoding}`);
if (mime === "application/json") {
const module = new SyntheticModule(["default"], function() {
const obj = JSON.parse(code);
this.setExport("default", obj);
}, {
context: this.context,
identifier
});
this.moduleCache.set(identifier, module);
return module;
}
return this.createEsModule(identifier, () => code);
}
}
function IPnumber(address) {
const ip = address.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
if (ip) return (+ip[1] << 24) + (+ip[2] << 16) + (+ip[3] << 8) + +ip[4];
throw new Error(`Expected IP address, received ${address}`);
}
function IPmask(maskSize) {
return -1 << 32 - maskSize;
}
const CLIENT_ID = "/@vite/client";
const CLIENT_FILE = pathToFileURL(CLIENT_ID).href;
class ViteExecutor {
esm;
constructor(options) {
this.options = options;
this.esm = options.esmExecutor;
}
resolve = (identifier) => {
if (identifier === CLIENT_ID) return identifier;
};
get workerState() {
return this.options.context.__vitest_worker__;
}
async createViteModule(fileUrl) {
if (fileUrl === CLIENT_FILE || fileUrl === CLIENT_ID) return this.createViteClientModule();
const cached = this.esm.resolveCachedModule(fileUrl);
if (cached) return cached;
return this.esm.createEsModule(fileUrl, async () => {
try {
const result = await this.options.transform(fileUrl);
if (result.code) return result.code;
} catch (cause) {
// rethrow vite error if it cannot load the module because it's not resolved
if (typeof cause === "object" && cause.code === "ERR_LOAD_URL" || typeof cause?.message === "string" && cause.message.includes("Failed to load url")) {
const error = new Error(`Cannot find module '${fileUrl}'`, { cause });
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
}
throw new Error(`[vitest] Failed to transform ${fileUrl}. Does the file exist?`);
});
}
createViteClientModule() {
const identifier = CLIENT_ID;
const cached = this.esm.resolveCachedModule(identifier);
if (cached) return cached;
const stub = this.options.viteClientModule;
const moduleKeys = Object.keys(stub);
const module = new SyntheticModule(moduleKeys, function() {
moduleKeys.forEach((key) => {
this.setExport(key, stub[key]);
});
}, {
context: this.options.context,
identifier
});
this.esm.cacheModule(identifier, module);
return module;
}
canResolve = (fileUrl) => {
if (fileUrl === CLIENT_FILE) return true;
const config = this.workerState.config.deps?.web || {};
const [modulePath] = fileUrl.split("?");
if (config.transformCss && CSS_LANGS_RE.test(modulePath)) return true;
if (config.transformAssets && KNOWN_ASSET_RE.test(modulePath)) return true;
if (toArray(config.transformGlobPattern).some((pattern) => pattern.test(modulePath))) return true;
return false;
};
}
const { existsSync } = fs;
// always defined when we use vm pool
const nativeResolve = import.meta.resolve;
// TODO: improve Node.js strict mode support in #2854
class ExternalModulesExecutor {
cjs;
esm;
vite;
context;
fs;
resolvers = [];
#networkSupported = null;
constructor(options) {
this.options = options;
this.context = options.context;
this.fs = options.fileMap;
this.esm = new EsmExecutor(this, { context: this.context });
this.cjs = new CommonjsExecutor({
context: this.context,
importModuleDynamically: this.importModuleDynamically,
fileMap: options.fileMap,
interopDefault: options.interopDefault
});
this.vite = new ViteExecutor({
esmExecutor: this.esm,
context: this.context,
transform: options.transform,
viteClientModule: options.viteClientModule
});
this.resolvers = [this.vite.resolve];
}
async import(identifier) {
const module = await this.createModule(identifier);
await this.esm.evaluateModule(module);
return module.namespace;
}
require(identifier) {
return this.cjs.require(identifier);
}
createRequire(identifier) {
return this.cjs.createRequire(identifier);
}
// dynamic import can be used in both ESM and CJS, so we have it in the executor
importModuleDynamically = async (specifier, referencer) => {
const module = await this.resolveModule(specifier, referencer.identifier);
return await this.esm.evaluateModule(module);
};
resolveModule = async (specifier, referencer) => {
let identifier = this.resolve(specifier, referencer);
if (identifier instanceof Promise) identifier = await identifier;
return await this.createModule(identifier);
};
resolve(specifier, parent) {
for (const resolver of this.resolvers) {
const id = resolver(specifier, parent);
if (id) return id;
}
// import.meta.resolve can be asynchronous in older +18 Node versions
return nativeResolve(specifier, parent);
}
getModuleInformation(identifier) {
if (identifier.startsWith("data:")) return {
type: "data",
url: identifier,
path: identifier
};
const { file, postfix } = splitFileAndPostfix(identifier);
const extension = extname(file);
if (extension === ".node" || isBuiltin(identifier)) return {
type: "builtin",
url: identifier,
path: identifier
};
if (this.isNetworkSupported && (identifier.startsWith("http:") || identifier.startsWith("https:"))) return {
type: "network",
url: identifier,
path: identifier
};
const isFileUrl = identifier.startsWith("file://");
const pathUrl = isFileUrl ? fileURLToPath(file) : file;
const fileUrl = isFileUrl ? identifier : `${pathToFileURL(file)}${postfix}`;
let type;
if (this.vite.canResolve(fileUrl)) type = "vite";
else if (extension === ".mjs") type = "module";
else if (extension === ".cjs") type = "commonjs";
else if (extension === ".wasm")
// still experimental on NodeJS --experimental-wasm-modules
// cf. ESM_FILE_FORMAT(url) in https://nodejs.org/docs/latest-v20.x/api/esm.html#resolution-algorithm
type = "wasm";
else type = findNearestPackageData(normalize(pathUrl)).type === "module" ? "module" : "commonjs";
return {
type,
path: pathUrl,
url: fileUrl
};
}
createModule(identifier) {
const { type, url, path } = this.getModuleInformation(identifier);
// create ERR_MODULE_NOT_FOUND on our own since latest NodeJS's import.meta.resolve doesn't throw on non-existing namespace or path
// https://github.com/nodejs/node/pull/49038
if ((type === "module" || type === "commonjs" || type === "wasm") && !existsSync(path)) {
const error = /* @__PURE__ */ new Error(`Cannot find ${isBareImport(path) ? "package" : "module"} '${path}'`);
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
switch (type) {
case "data": return this.esm.createDataModule(identifier);
case "builtin": return this.cjs.getCoreSyntheticModule(identifier);
case "vite": return this.vite.createViteModule(url);
case "wasm": return this.esm.createWebAssemblyModule(url, () => this.fs.readBuffer(path));
case "module": return this.esm.createEsModule(url, () => this.fs.readFileAsync(path));
case "commonjs": return this.cjs.getCjsSyntheticModule(path, identifier);
case "network": return this.esm.createNetworkModule(url);
default: return type;
}
}
get isNetworkSupported() {
if (this.#networkSupported == null) if (process.execArgv.includes("--experimental-network-imports")) this.#networkSupported = true;
else if (process.env.NODE_OPTIONS?.includes("--experimental-network-imports")) this.#networkSupported = true;
else this.#networkSupported = false;
return this.#networkSupported;
}
}
const { promises, readFileSync } = fs;
class FileMap {
fsCache = /* @__PURE__ */ new Map();
fsBufferCache = /* @__PURE__ */ new Map();
async readFileAsync(path) {
const cached = this.fsCache.get(path);
if (cached != null) return cached;
const source = await promises.readFile(path, "utf-8");
this.fsCache.set(path, source);
return source;
}
readFile(path) {
const cached = this.fsCache.get(path);
if (cached != null) return cached;
const source = readFileSync(path, "utf-8");
this.fsCache.set(path, source);
return source;
}
readBuffer(path) {
const cached = this.fsBufferCache.get(path);
if (cached != null) return cached;
const buffer = readFileSync(path);
this.fsBufferCache.set(path, buffer);
return buffer;
}
}
const entryFile = pathToFileURL(resolve(distDir, "workers/runVmTests.js")).href;
const fileMap = new FileMap();
const packageCache = /* @__PURE__ */ new Map();
async function runVmTests(method, state, traces) {
const { ctx, rpc } = state;
const beforeEnvironmentTime = performance.now();
const { environment } = await loadEnvironment(ctx.environment.name, ctx.config.root, rpc, traces, true);
state.environment = environment;
if (!environment.setupVM) {
const envName = ctx.environment.name;
const packageId = envName[0] === "." ? envName : `vitest-environment-${envName}`;
throw new TypeError(`Environment "${ctx.environment.name}" is not a valid environment. Path "${packageId}" doesn't support vm environment because it doesn't provide "setupVM" method.`);
}
const vm = await traces.$("vitest.runtime.environment.setup", { attributes: {
"vitest.environment": environment.name,
"vitest.environment.vite_environment": environment.viteEnvironment || environment.name
} }, () => environment.setupVM(ctx.environment.options || ctx.config.environmentOptions || {}));
state.durations.environment = performance.now() - beforeEnvironmentTime;
process.env.VITEST_VM_POOL = "1";
if (!vm.getVmContext) throw new TypeError(`Environment ${environment.name} doesn't provide "getVmContext" method. It should return a context created by "vm.createContext" method.`);
const context = vm.getVmContext();
if (!isContext(context)) throw new TypeError(`Environment ${environment.name} doesn't provide a valid context. It should be created by "vm.createContext" method.`);
provideWorkerState(context, state);
// this is unfortunately needed for our own dependencies
// we need to find a way to not rely on this by default
// because browser doesn't provide these globals
context.process = process;
context.global = context;
context.console = state.config.disableConsoleIntercept ? console : createCustomConsole(state);
// TODO: don't hardcode setImmediate in fake timers defaults
context.setImmediate = setImmediate;
context.clearImmediate = clearImmediate;
const stubs = getDefaultRequestStubs(context);
const externalModulesExecutor = new ExternalModulesExecutor({
context,
fileMap,
packageCache,
transform: rpc.transform,
viteClientModule: stubs["/@vite/client"]
});
process.exit = (code = process.exitCode || 0) => {
throw new Error(`process.exit unexpectedly called with "${code}"`);
};
listenForErrors(() => state);
const moduleRunner = startVitestModuleRunner({
context,
evaluatedModules: state.evaluatedModules,
state,
externalModulesExecutor,
createImportMeta: createNodeImportMeta,
traces
});
emitModuleRunner(moduleRunner);
Object.defineProperty(context, VITEST_VM_CONTEXT_SYMBOL, {
value: {
context,
externalModulesExecutor
},
configurable: true,
enumerable: false,
writable: false
});
context.__vitest_mocker__ = moduleRunner.mocker;
if (ctx.config.serializedDefines) try {
runInContext(ctx.config.serializedDefines, context, { filename: "virtual:load-defines.js" });
} catch (error) {
throw new Error(`Failed to load custom "defines": ${error.message}`);
}
await moduleRunner.mocker.initializeSpyModule();
const { run } = await moduleRunner.import(entryFile);
try {
await run(method, ctx.files, ctx.config, moduleRunner, traces);
} finally {
await traces.$("vitest.runtime.environment.teardown", () => vm.teardown?.());
}
}
function setupVmWorker(context) {
if (context.config.experimental.viteModuleRunner === false) throw new Error(`Pool "${context.pool}" cannot run with "experimental.viteModuleRunner: false". Please, use "threads" or "forks" instead.`);
}
export { runVmTests as r, setupVmWorker as s };