UEA-Prodem

This commit is contained in:
2026-06-10 12:38:42 -03:00
parent 3f33154e16
commit c41625e542
9352 changed files with 1128292 additions and 14752 deletions
+6
View File
@@ -0,0 +1,6 @@
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
}
export { commonjsGlobal as c, getDefaultExportFromCjs as g };
+5958
View File
File diff suppressed because it is too large Load Diff
+203
View File
@@ -0,0 +1,203 @@
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.D98-gwRW.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.bRl2_oI_.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.BRN2oBJd.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 };
+40
View File
@@ -0,0 +1,40 @@
import { getCurrentSuite } from '@vitest/runner';
import { createChainable } from '@vitest/runner/utils';
import { noop } from '@vitest/utils/helpers';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
const benchFns = /* @__PURE__ */ new WeakMap();
const benchOptsMap = /* @__PURE__ */ new WeakMap();
function getBenchOptions(key) {
return benchOptsMap.get(key);
}
function getBenchFn(key) {
return benchFns.get(key);
}
const bench = createBenchmark(function(name, fn = noop, options = {}) {
if (getWorkerState().config.mode !== "benchmark") throw new Error("`bench()` is only available in benchmark mode.");
const task = getCurrentSuite().task(formatName(name), {
...this,
meta: { benchmark: true }
});
benchFns.set(task, fn);
benchOptsMap.set(task, options);
// vitest runner sets mode to `todo` if handler is not passed down
// but we store handler separately
if (!this.todo && task.mode === "todo") task.mode = "run";
});
function createBenchmark(fn) {
const benchmark = createChainable([
"skip",
"only",
"todo"
], fn);
benchmark.skipIf = (condition) => condition ? benchmark.skip : benchmark;
benchmark.runIf = (condition) => condition ? benchmark : benchmark.skip;
return benchmark;
}
function formatName(name) {
return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
}
export { getBenchOptions as a, bench as b, getBenchFn as g };
+24
View File
@@ -0,0 +1,24 @@
import { Test } from '@vitest/runner';
import { ChainableFunction } from '@vitest/runner/utils';
import { TaskResult, Bench, Options } from 'tinybench';
interface Benchmark extends Test {
meta: {
benchmark: true;
result?: TaskResult;
};
}
interface BenchmarkResult extends TaskResult {
name: string;
rank: number;
sampleCount: number;
median: number;
}
type BenchFunction = (this: Bench) => Promise<void> | void;
type ChainableBenchmarkAPI = ChainableFunction<"skip" | "only" | "todo", (name: string | Function, fn?: BenchFunction, options?: Options) => void>;
type BenchmarkAPI = ChainableBenchmarkAPI & {
skipIf: (condition: any) => ChainableBenchmarkAPI;
runIf: (condition: any) => ChainableBenchmarkAPI;
};
export type { BenchmarkResult as B, BenchFunction as a, Benchmark as b, BenchmarkAPI as c };
+62
View File
@@ -0,0 +1,62 @@
import { FileSpecification } from '@vitest/runner';
import { O as OTELCarrier } from './traces.d.D2T_R8rx.js';
import { T as TestExecutionMethod } from './worker.d.ZpHpO4yb.js';
type SerializedTestSpecification = [project: {
name: string | undefined;
root: string;
}, file: string, options: {
pool: string;
testLines?: number[] | undefined;
testIds?: string[] | undefined;
testNamePattern?: RegExp | undefined;
testTagsFilter?: string[] | undefined;
}];
interface ModuleDefinitionLocation {
line: number;
column: number;
}
interface SourceModuleLocations {
modules: ModuleDefinitionDiagnostic[];
untracked: ModuleDefinitionDiagnostic[];
}
interface ModuleDefinitionDiagnostic {
start: ModuleDefinitionLocation;
end: ModuleDefinitionLocation;
startIndex: number;
endIndex: number;
rawUrl: string;
resolvedUrl: string;
resolvedId: string;
}
interface ModuleDefinitionDurationsDiagnostic extends ModuleDefinitionDiagnostic {
selfTime: number;
totalTime: number;
transformTime?: number;
external?: boolean;
importer?: string;
}
interface UntrackedModuleDefinitionDiagnostic {
url: string;
resolvedId: string;
resolvedUrl: string;
selfTime: number;
totalTime: number;
transformTime?: number;
external?: boolean;
importer?: string;
}
interface SourceModuleDiagnostic {
modules: ModuleDefinitionDurationsDiagnostic[];
untrackedModules: UntrackedModuleDefinitionDiagnostic[];
}
interface BrowserTesterOptions {
method: TestExecutionMethod;
files: FileSpecification[];
providedContext: string;
otelCarrier?: OTELCarrier;
}
export type { BrowserTesterOptions as B, ModuleDefinitionDurationsDiagnostic as M, SerializedTestSpecification as S, UntrackedModuleDefinitionDiagnostic as U, ModuleDefinitionDiagnostic as a, ModuleDefinitionLocation as b, SourceModuleDiagnostic as c, SourceModuleLocations as d };
+2392
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+232
View File
@@ -0,0 +1,232 @@
import { PrettyFormatOptions } from '@vitest/pretty-format';
import { SequenceHooks, SequenceSetupFiles, SerializableRetry, TestTagDefinition } from '@vitest/runner';
import { SnapshotUpdateState, SnapshotEnvironment } from '@vitest/snapshot';
import { SerializedDiffOptions } from '@vitest/utils/diff';
import { L as LabelColor } from './traces.d.D2T_R8rx.js';
/**
* Names of clock methods that may be faked by install.
*/
type FakeMethod =
| "setTimeout"
| "clearTimeout"
| "setImmediate"
| "clearImmediate"
| "setInterval"
| "clearInterval"
| "Date"
| "nextTick"
| "hrtime"
| "requestAnimationFrame"
| "cancelAnimationFrame"
| "requestIdleCallback"
| "cancelIdleCallback"
| "performance"
| "queueMicrotask";
interface FakeTimerInstallOpts {
/**
* Installs fake timers with the specified unix epoch (default: 0)
*/
now?: number | Date | undefined;
/**
* An array with names of global methods and APIs to fake. By default, `@sinonjs/fake-timers` does not replace `nextTick()` and `queueMicrotask()`.
* For instance, `FakeTimers.install({ toFake: ['setTimeout', 'nextTick'] })` will fake only `setTimeout()` and `nextTick()`
*/
toFake?: FakeMethod[] | undefined;
/**
* The maximum number of timers that will be run when calling runAll() (default: 1000)
*/
loopLimit?: number | undefined;
/**
* Tells @sinonjs/fake-timers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by
* 20ms for every 20ms change in the real system time) (default: false)
*/
shouldAdvanceTime?: boolean | undefined;
/**
* Relevant only when using with shouldAdvanceTime: true. increment mocked time by advanceTimeDelta ms every advanceTimeDelta ms change
* in the real system time (default: 20)
*/
advanceTimeDelta?: number | undefined;
/**
* Tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by
* default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers. (default: false)
*/
shouldClearNativeTimers?: boolean | undefined;
/**
* Tells FakeTimers to not throw an error when faking a timer that does not exist in the global object. (default: false)
*/
ignoreMissingTimers?: boolean | undefined;
}
/**
* Config that tests have access to.
*/
interface SerializedConfig {
name: string | undefined;
color?: LabelColor;
globals: boolean;
base: string | undefined;
snapshotEnvironment?: string;
disableConsoleIntercept: boolean | undefined;
runner: string | undefined;
isolate: boolean;
maxWorkers: number;
mode: "test" | "benchmark";
bail: number | undefined;
environmentOptions?: Record<string, any>;
root: string;
setupFiles: string[];
passWithNoTests: boolean;
testNamePattern: RegExp | undefined;
allowOnly: boolean;
testTimeout: number;
hookTimeout: number;
clearMocks: boolean;
mockReset: boolean;
restoreMocks: boolean;
unstubGlobals: boolean;
unstubEnvs: boolean;
fakeTimers: FakeTimerInstallOpts;
maxConcurrency: number;
defines: Record<string, any>;
expect: {
requireAssertions?: boolean;
poll?: {
timeout?: number;
interval?: number;
};
};
printConsoleTrace: boolean | undefined;
sequence: {
shuffle?: boolean;
concurrent?: boolean;
seed: number;
hooks: SequenceHooks;
setupFiles: SequenceSetupFiles;
};
deps: {
web: {
transformAssets?: boolean;
transformCss?: boolean;
transformGlobPattern?: RegExp | RegExp[];
};
optimizer: Record<string, {
enabled: boolean;
}>;
interopDefault: boolean | undefined;
moduleDirectories: string[] | undefined;
};
snapshotOptions: {
updateSnapshot: SnapshotUpdateState;
expand: boolean | undefined;
snapshotFormat: PrettyFormatOptions | undefined;
/**
* only exists for tests, not available in the main process
*/
snapshotEnvironment: SnapshotEnvironment;
};
pool: string;
snapshotSerializers: string[];
chaiConfig: {
includeStack?: boolean;
showDiff?: boolean;
truncateThreshold?: number;
} | undefined;
api: {
allowExec: boolean | undefined;
allowWrite: boolean | undefined;
};
diff: string | SerializedDiffOptions | undefined;
retry: SerializableRetry;
includeTaskLocation: boolean | undefined;
inspect: boolean | string | undefined;
inspectBrk: boolean | string | undefined;
inspector: {
enabled?: boolean;
port?: number;
host?: string;
waitForDebugger?: boolean;
};
watch: boolean;
env: Record<string, any>;
browser: {
name: string;
headless: boolean;
isolate: boolean;
fileParallelism: boolean;
ui: boolean;
viewport: {
width: number;
height: number;
};
locators: {
testIdAttribute: string;
exact: boolean;
};
screenshotFailures: boolean;
providerOptions: {
actionTimeout?: number;
};
trace: BrowserTraceViewMode;
trackUnhandledErrors: boolean;
detailsPanelPosition: "right" | "bottom";
};
standalone: boolean;
logHeapUsage: boolean | undefined;
detectAsyncLeaks: boolean;
coverage: SerializedCoverageConfig;
benchmark: {
includeSamples: boolean;
} | undefined;
serializedDefines: string;
experimental: {
fsModuleCache: boolean;
importDurations: {
print: boolean | "on-warn";
limit: number;
failOnDanger: boolean;
thresholds: {
warn: number;
danger: number;
};
};
viteModuleRunner: boolean;
nodeLoader: boolean;
openTelemetry: {
enabled: boolean;
sdkPath?: string;
browserSdkPath?: string;
} | undefined;
};
tags: TestTagDefinition[];
tagsFilter: string[] | undefined;
strictTags: boolean;
slowTestThreshold: number | undefined;
isAgent: boolean;
}
interface SerializedCoverageConfig {
provider: "istanbul" | "v8" | "custom" | undefined;
reportsDirectory: string;
htmlDir: string | undefined;
enabled: boolean;
customProviderModule: string | undefined;
}
interface SerializedRootConfig extends SerializedConfig {
projects: SerializedConfig[];
}
type RuntimeConfig = Pick<SerializedConfig, "allowOnly" | "testTimeout" | "hookTimeout" | "clearMocks" | "mockReset" | "restoreMocks" | "fakeTimers" | "maxConcurrency" | "expect" | "printConsoleTrace"> & {
sequence?: {
hooks?: SequenceHooks;
};
};
type RuntimeOptions = Partial<RuntimeConfig>;
type BrowserTraceViewMode = "on" | "off" | "on-first-retry" | "on-all-retries" | "retain-on-failure";
export type { BrowserTraceViewMode as B, FakeTimerInstallOpts as F, RuntimeOptions as R, SerializedConfig as S, SerializedCoverageConfig as a, SerializedRootConfig as b, RuntimeConfig as c };
+147
View File
@@ -0,0 +1,147 @@
import { Console } from 'node:console';
import { relative } from 'node:path';
import { Writable } from 'node:stream';
import { getSafeTimers } from '@vitest/utils/timers';
import c from 'tinyrainbow';
import { R as RealDate } from './rpc.MzXet3jl.js';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
import './index.Chj8NDwU.js';
const UNKNOWN_TEST_ID = "__vitest__unknown_test__";
function getTaskIdByStack(root) {
const stack = (/* @__PURE__ */ new Error("STACK_TRACE_ERROR")).stack?.split("\n");
if (!stack) return UNKNOWN_TEST_ID;
const index = stack.findIndex((line) => line.includes("at Console.value"));
const line = index === -1 ? null : stack[index + 2];
if (!line) return UNKNOWN_TEST_ID;
const filepath = line.match(/at\s(.*)\s?/)?.[1];
if (filepath) return relative(root, filepath);
return UNKNOWN_TEST_ID;
}
function createCustomConsole(defaultState) {
const stdoutBuffer = /* @__PURE__ */ new Map();
const stderrBuffer = /* @__PURE__ */ new Map();
const timers = /* @__PURE__ */ new Map();
const { queueMicrotask } = getSafeTimers();
function queueCancelableMicrotask(callback) {
let canceled = false;
queueMicrotask(() => {
if (!canceled) callback();
});
return () => {
canceled = true;
};
}
const state = () => defaultState || getWorkerState();
// group sync console.log calls with micro task
function schedule(taskId) {
const timer = timers.get(taskId);
const { stdoutTime, stderrTime } = timer;
timer.cancel?.();
timer.cancel = queueCancelableMicrotask(() => {
if (stderrTime < stdoutTime) {
sendStderr(taskId);
sendStdout(taskId);
} else {
sendStdout(taskId);
sendStderr(taskId);
}
});
}
function sendStdout(taskId) {
sendBuffer("stdout", taskId);
}
function sendStderr(taskId) {
sendBuffer("stderr", taskId);
}
function sendBuffer(type, taskId) {
const buffers = type === "stdout" ? stdoutBuffer : stderrBuffer;
const buffer = buffers.get(taskId);
if (!buffer) return;
if (state().config.printConsoleTrace) buffer.forEach(([buffer, origin]) => {
sendLog(type, taskId, String(buffer), buffer.length, origin);
});
else sendLog(type, taskId, buffer.map((i) => String(i[0])).join(""), buffer.length);
const timer = timers.get(taskId);
buffers.delete(taskId);
if (type === "stderr") timer.stderrTime = 0;
else timer.stdoutTime = 0;
}
function sendLog(type, taskId, content, size, origin) {
const timer = timers.get(taskId);
const time = type === "stderr" ? timer.stderrTime : timer.stdoutTime;
state().rpc.onUserConsoleLog({
type,
content: content || "<empty line>",
taskId,
time: time || RealDate.now(),
size,
origin
});
}
return new Console({
stdout: new Writable({ write(data, encoding, callback) {
const s = state();
const id = s?.current?.id || s?.current?.suite?.id || s.current?.file.id || getTaskIdByStack(s.config.root);
let timer = timers.get(id);
if (timer) timer.stdoutTime = timer.stdoutTime || RealDate.now();
else {
timer = {
stdoutTime: RealDate.now(),
stderrTime: RealDate.now()
};
timers.set(id, timer);
}
let buffer = stdoutBuffer.get(id);
if (!buffer) {
buffer = [];
stdoutBuffer.set(id, buffer);
}
if (state().config.printConsoleTrace) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = limit + 6;
const trace = (/* @__PURE__ */ new Error("STACK_TRACE")).stack?.split("\n").slice(7).join("\n");
Error.stackTraceLimit = limit;
buffer.push([data, trace]);
} else buffer.push([data, void 0]);
schedule(id);
callback();
} }),
stderr: new Writable({ write(data, encoding, callback) {
const s = state();
const id = s?.current?.id || s?.current?.suite?.id || s.current?.file.id || getTaskIdByStack(s.config.root);
let timer = timers.get(id);
if (timer) timer.stderrTime = timer.stderrTime || RealDate.now();
else {
timer = {
stderrTime: RealDate.now(),
stdoutTime: RealDate.now()
};
timers.set(id, timer);
}
let buffer = stderrBuffer.get(id);
if (!buffer) {
buffer = [];
stderrBuffer.set(id, buffer);
}
if (state().config.printConsoleTrace) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = limit + 6;
const stack = (/* @__PURE__ */ new Error("STACK_TRACE")).stack?.split("\n");
Error.stackTraceLimit = limit;
if (stack?.some((line) => line.includes("at Console.trace"))) buffer.push([data, void 0]);
else {
const trace = stack?.slice(7).join("\n");
Error.stackTraceLimit = limit;
buffer.push([data, trace]);
}
} else buffer.push([data, void 0]);
schedule(id);
callback();
} }),
colorMode: c.isColorSupported,
groupIndentation: 2
});
}
export { UNKNOWN_TEST_ID, createCustomConsole };
+38
View File
@@ -0,0 +1,38 @@
// if changed, update also jsdocs and docs
const defaultPort = 51204;
const defaultBrowserPort = 63315;
const defaultInspectPort = 9229;
const API_PATH = "/__vitest_api__";
const CONFIG_NAMES = ["vitest.config", "vite.config"];
const CONFIG_EXTENSIONS = [
".ts",
".mts",
".cts",
".js",
".mjs",
".cjs"
];
const configFiles = CONFIG_NAMES.flatMap((name) => CONFIG_EXTENSIONS.map((ext) => name + ext));
const globalApis = [
"suite",
"test",
"describe",
"it",
"chai",
"expect",
"assert",
"expectTypeOf",
"assertType",
"vitest",
"vi",
"beforeAll",
"afterAll",
"beforeEach",
"afterEach",
"onTestFinished",
"onTestFailed",
"aroundEach",
"aroundAll"
];
export { API_PATH as A, defaultInspectPort as a, defaultPort as b, configFiles as c, defaultBrowserPort as d, globalApis as g };
+28
View File
@@ -0,0 +1,28 @@
const CoverageProviderMap = {
v8: "@vitest/coverage-v8",
istanbul: "@vitest/coverage-istanbul"
};
async function resolveCoverageProviderModule(options, loader) {
if (!options?.enabled || !options.provider) return null;
const provider = options.provider;
if (provider === "v8" || provider === "istanbul") {
let builtInModule = CoverageProviderMap[provider];
if (provider === "v8" && loader.isBrowser) builtInModule += "/browser";
const { default: coverageModule } = loader.isBrowser ? await loader.import(builtInModule) : await import(
/* @vite-ignore */
builtInModule
);
if (!coverageModule) throw new Error(`Failed to load ${CoverageProviderMap[provider]}. Default export is missing.`);
return coverageModule;
}
let customProviderModule;
try {
customProviderModule = await loader.import(options.customProviderModule);
} catch (error) {
throw new Error(`Failed to load custom CoverageProviderModule from ${options.customProviderModule}`, { cause: error });
}
if (customProviderModule.default == null) throw new Error(`Custom CoverageProviderModule loaded from ${options.customProviderModule} was not the default export`);
return customProviderModule.default;
}
export { CoverageProviderMap as C, resolveCoverageProviderModule as r };
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
interface RuntimeCoverageModuleLoader {
import: (id: string) => Promise<{
default: RuntimeCoverageProviderModule;
}>;
isBrowser?: boolean;
moduleExecutionInfo?: Map<string, {
startOffset: number;
}>;
}
interface RuntimeCoverageProviderModule {
/**
* Factory for creating a new coverage provider
*/
getProvider: () => any;
/**
* Executed before tests are run in the worker thread.
*/
startCoverage?: (runtimeOptions: {
isolate: boolean;
}) => unknown | Promise<unknown>;
/**
* Executed on after each run in the worker thread. Possible to return a payload passed to the provider
*/
takeCoverage?: (runtimeOptions?: {
moduleExecutionInfo?: Map<string, {
startOffset: number;
}>;
}) => unknown | Promise<unknown>;
/**
* Executed after all tests have been run in the worker thread.
*/
stopCoverage?: (runtimeOptions: {
isolate: boolean;
}) => unknown | Promise<unknown>;
}
export type { RuntimeCoverageModuleLoader as R, RuntimeCoverageProviderModule as a };
+704
View File
@@ -0,0 +1,704 @@
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
import { mkdir, writeFile } from 'node:fs/promises';
import { resolve, dirname, relative } from 'node:path';
import { detectPackageManager, installPackage } from './index.CMESou6r.js';
import { p as prompt, a as any } from './index.og1WyBLx.js';
import { x } from 'tinyexec';
import c from 'tinyrainbow';
import { c as configFiles } from './constants.CPYnjOGj.js';
import 'node:process';
import 'node:module';
import 'node:url';
import './_commonjsHelpers.D26ty3Ew.js';
import 'readline';
import 'events';
const jsxExample = {
name: "HelloWorld.jsx",
js: `
export default function HelloWorld({ name }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from '@testing-library/jsx'
import HelloWorld from './HelloWorld.<EXT>x'
test('renders name', async () => {
const { getByText } = await render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const vueExample = {
name: "HelloWorld.vue",
js: `
<script setup>
defineProps({
name: String
})
<\/script>
<template>
<div>
<h1>Hello {{ name }}!</h1>
</div>
</template>
`,
ts: `
<script setup lang="ts">
defineProps<{
name: string
}>()
<\/script>
<template>
<div>
<h1>Hello {{ name }}!</h1>
</div>
</template>
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-vue'
import HelloWorld from './HelloWorld.vue'
test('renders name', async () => {
const { getByText } = render(HelloWorld, {
props: { name: 'Vitest' },
})
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const svelteExample = {
name: "HelloWorld.svelte",
js: `
<script>
export let name
<\/script>
<h1>Hello {name}!</h1>
`,
ts: `
<script lang="ts">
export let name: string
<\/script>
<h1>Hello {name}!</h1>
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-svelte'
import HelloWorld from './HelloWorld.svelte'
test('renders name', async () => {
const { getByText } = render(HelloWorld, { name: 'Vitest' })
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const markoExample = {
name: "HelloWorld.marko",
js: `
class {
onCreate() {
this.state = { name: null }
}
}
<h1>Hello \${state.name}!</h1>
`,
ts: `
export interface Input {
name: string
}
<h1>Hello \${input.name}!</h1>
`,
test: `
import { expect, test } from 'vitest'
import { render } from '@marko/testing-library'
import HelloWorld from './HelloWorld.svelte'
test('renders name', async () => {
const { getByText } = await render(HelloWorld, { name: 'Vitest' })
const element = getByText('Hello Vitest!')
expect(element).toBeInTheDocument()
})
`
};
const litExample = {
name: "HelloWorld.js",
js: `
import { html, LitElement } from 'lit'
export class HelloWorld extends LitElement {
static properties = {
name: { type: String },
}
constructor() {
super()
this.name = 'World'
}
render() {
return html\`<h1>Hello \${this.name}!</h1>\`
}
}
customElements.define('hello-world', HelloWorld)
`,
ts: `
import { html, LitElement } from 'lit'
import { customElement, property } from 'lit/decorators.js'
@customElement('hello-world')
export class HelloWorld extends LitElement {
@property({ type: String })
name = 'World'
render() {
return html\`<h1>Hello \${this.name}!</h1>\`
}
}
declare global {
interface HTMLElementTagNameMap {
'hello-world': HelloWorld
}
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-lit'
import { html } from 'lit'
import './HelloWorld.js'
test('renders name', async () => {
const screen = render(html\`<hello-world name="Vitest"></hello-world>\`)
const element = screen.getByText('Hello Vitest!')
await expect.element(element).toBeInTheDocument()
})
`
};
const qwikExample = {
name: "HelloWorld.jsx",
js: `
import { component$ } from '@builder.io/qwik'
export default component$(({ name }) => {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
})
`,
ts: `
import { component$ } from '@builder.io/qwik'
export default component$(({ name }: { name: string }) => {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
})
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-qwik'
import HelloWorld from './HelloWorld.tsx'
test('renders name', async () => {
const { getByText } = render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const preactExample = {
name: "HelloWorld.jsx",
js: `
export default function HelloWorld({ name }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
)
}
`,
test: `
import { expect, test } from 'vitest'
import { render } from 'vitest-browser-preact'
import HelloWorld from './HelloWorld.<EXT>x'
test('renders name', async () => {
const { getByText } = render(<HelloWorld name="Vitest" />)
await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`
};
const vanillaExample = {
name: "HelloWorld.js",
js: `
export default function HelloWorld({ name }) {
const parent = document.createElement('div')
const h1 = document.createElement('h1')
h1.textContent = 'Hello ' + name + '!'
parent.appendChild(h1)
return parent
}
`,
ts: `
export default function HelloWorld({ name }: { name: string }): HTMLDivElement {
const parent = document.createElement('div')
const h1 = document.createElement('h1')
h1.textContent = 'Hello ' + name + '!'
parent.appendChild(h1)
return parent
}
`,
test: `
import { expect, test } from 'vitest'
import { getByText } from '@testing-library/dom'
import HelloWorld from './HelloWorld.js'
test('renders name', () => {
const parent = HelloWorld({ name: 'Vitest' })
document.body.appendChild(parent)
const element = getByText(parent, 'Hello Vitest!')
expect(element).toBeInTheDocument()
})
`
};
function getExampleTest(framework) {
switch (framework) {
case "solid": return {
...jsxExample,
test: jsxExample.test.replace("@testing-library/jsx", `@testing-library/${framework}`)
};
case "preact": return preactExample;
case "react": return {
...jsxExample,
test: jsxExample.test.replace("@testing-library/jsx", `vitest-browser-${framework}`)
};
case "vue": return vueExample;
case "svelte": return svelteExample;
case "lit": return litExample;
case "marko": return markoExample;
case "qwik": return qwikExample;
default: return vanillaExample;
}
}
async function generateExampleFiles(framework, lang) {
const example = getExampleTest(framework);
let fileName = example.name;
const folder = resolve(process.cwd(), "vitest-example");
const fileContent = example[lang];
if (!existsSync(folder)) await mkdir(folder, { recursive: true });
const isJSX = fileName.endsWith(".jsx");
if (isJSX && lang === "ts") fileName = fileName.replace(".jsx", ".tsx");
else if (fileName.endsWith(".js") && lang === "ts") fileName = fileName.replace(".js", ".ts");
example.test = example.test.replace("<EXT>", lang);
const filePath = resolve(folder, fileName);
const testPath = resolve(folder, `HelloWorld.test.${isJSX ? `${lang}x` : lang}`);
writeFileSync(filePath, fileContent.trimStart(), "utf-8");
writeFileSync(testPath, example.test.trimStart(), "utf-8");
return testPath;
}
// eslint-disable-next-line no-console
const log = console.log;
function getProviderOptions() {
return Object.entries({
playwright: "Playwright relies on Chrome DevTools protocol. Read more: https://playwright.dev",
webdriverio: "WebdriverIO uses WebDriver protocol. Read more: https://webdriver.io",
preview: "Preview is useful to quickly run your tests in the browser, but not suitable for CI."
}).map(([provider, description]) => {
return {
title: provider,
description,
value: provider
};
});
}
function getBrowserNames(provider) {
switch (provider) {
case "webdriverio": return [
"chrome",
"firefox",
"edge",
"safari"
];
case "playwright": return [
"chromium",
"firefox",
"webkit"
];
case "preview": return [
"chrome",
"firefox",
"safari"
];
}
}
function getFramework() {
return [
{
title: "vanilla",
value: "vanilla",
description: "No framework, just plain JavaScript or TypeScript."
},
{
title: "vue",
value: "vue",
description: "\"The Progressive JavaScript Framework\""
},
{
title: "svelte",
value: "svelte",
description: "\"Svelte: cybernetically enhanced web apps\""
},
{
title: "react",
value: "react",
description: "\"The library for web and native user interfaces\""
},
{
title: "lit",
value: "lit",
description: "\"A simple library for building fast, lightweight web components.\""
},
{
title: "preact",
value: "preact",
description: "\"Fast 3kB alternative to React with the same modern API\""
},
{
title: "solid",
value: "solid",
description: "\"Simple and performant reactivity for building user interfaces\""
},
{
title: "marko",
value: "marko",
description: "\"A declarative, HTML-based language that makes building web apps fun\""
},
{
title: "qwik",
value: "qwik",
description: "\"Instantly interactive web apps at scale\""
}
];
}
function getFrameworkTestPackage(framework) {
switch (framework) {
case "vanilla": return null;
case "vue": return "vitest-browser-vue";
case "svelte": return "vitest-browser-svelte";
case "react": return "vitest-browser-react";
case "lit": return "vitest-browser-lit";
case "preact": return "vitest-browser-preact";
case "solid": return "@solidjs/testing-library";
case "marko": return "@marko/testing-library";
case "qwik": return "vitest-browser-qwik";
}
throw new Error(`Unsupported framework: ${framework}`);
}
function getFrameworkPluginPackage(framework) {
switch (framework) {
case "vue": return "@vitejs/plugin-vue";
case "svelte": return "@sveltejs/vite-plugin-svelte";
case "react": return "@vitejs/plugin-react";
case "preact": return "@preact/preset-vite";
case "solid": return "vite-plugin-solid";
case "marko": return "@marko/vite";
case "qwik": return "@builder.io/qwik/optimizer";
}
return null;
}
function getLanguageOptions() {
return [{
title: "TypeScript",
description: "Use TypeScript.",
value: "ts"
}, {
title: "JavaScript",
description: "Use plain JavaScript.",
value: "js"
}];
}
async function installPackages(pkgManager, packages) {
if (!packages.length) {
log(c.green("✔"), c.bold("All packages are already installed."));
return;
}
log(c.cyan("◼"), c.bold("Installing packages..."));
log(c.cyan("◼"), packages.join(", "));
log();
await installPackage(packages, {
dev: true,
packageManager: pkgManager ?? void 0
});
}
function readPkgJson(path) {
if (!existsSync(path)) return null;
const content = readFileSync(path, "utf-8");
return JSON.parse(content);
}
function getPossibleDefaults(dependencies) {
return {
lang: "ts",
provider: getPossibleProvider(dependencies),
framework: getPossibleFramework(dependencies)
};
}
function getPossibleFramework(dependencies) {
if (dependencies.vue || dependencies["vue-tsc"] || dependencies["@vue/reactivity"]) return "vue";
if (dependencies.react || dependencies["react-dom"]) return "react";
if (dependencies.svelte || dependencies["@sveltejs/kit"]) return "svelte";
if (dependencies.lit || dependencies["lit-html"]) return "lit";
if (dependencies.preact) return "preact";
if (dependencies["solid-js"] || dependencies["@solidjs/start"]) return "solid";
if (dependencies.marko) return "marko";
if (dependencies["@builder.io/qwik"] || dependencies["@qwik.dev/core"]) return "qwik";
return "vanilla";
}
function getPossibleProvider(dependencies) {
if (dependencies.webdriverio || dependencies["@wdio/cli"] || dependencies["@wdio/config"]) return "webdriverio";
// playwright is the default recommendation
return "playwright";
}
function getProviderDocsLink(provider) {
switch (provider) {
case "playwright": return "https://vitest.dev/config/browser/playwright";
case "webdriverio": return "https://vitest.dev/config/browser/webdriverio";
}
}
function sort(choices, value) {
const index = choices.findIndex((i) => i.value === value);
if (index === -1) return choices;
return [choices.splice(index, 1)[0], ...choices];
}
function fail() {
process.exitCode = 1;
}
function getFrameworkImportInfo(framework) {
switch (framework) {
case "svelte": return {
importName: "svelte",
isNamedExport: true
};
case "qwik": return {
importName: "qwikVite",
isNamedExport: true
};
default: return {
importName: framework,
isNamedExport: false
};
}
}
async function generateFrameworkConfigFile(options) {
const { importName, isNamedExport } = getFrameworkImportInfo(options.framework);
const frameworkImport = isNamedExport ? `import { ${importName} } from '${options.frameworkPlugin}'` : `import ${importName} from '${options.frameworkPlugin}'`;
const configContent = [
`import { defineConfig } from 'vitest/config'`,
`import { ${options.provider} } from '@vitest/browser-${options.provider}'`,
options.frameworkPlugin ? frameworkImport : null,
``,
"export default defineConfig({",
options.frameworkPlugin ? ` plugins: [${importName}()],` : null,
` test: {`,
` browser: {`,
` enabled: true,`,
` provider: ${options.provider}(),`,
options.provider !== "preview" && ` // ${getProviderDocsLink(options.provider)}`,
` instances: [`,
...options.browsers.map((browser) => ` { browser: '${browser}' },`),
` ],`,
` },`,
` },`,
`})`,
""
].filter((t) => typeof t === "string").join("\n");
await writeFile(options.configPath, configContent);
}
async function updatePkgJsonScripts(pkgJsonPath, vitestScript) {
if (!existsSync(pkgJsonPath)) {
const pkg = { scripts: { "test:browser": vitestScript } };
await writeFile(pkgJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
} else {
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
pkg.scripts = pkg.scripts || {};
pkg.scripts["test:browser"] = vitestScript;
await writeFile(pkgJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
}
log(c.green("✔"), "Added \"test:browser\" script to your package.json.");
}
function getRunScript(pkgManager) {
switch (pkgManager) {
case "yarn@berry":
case "yarn": return "yarn test:browser";
case "pnpm@6":
case "pnpm": return "pnpm test:browser";
case "bun": return "bun test:browser";
default: return "npm run test:browser";
}
}
function getPlaywrightRunArgs(pkgManager) {
switch (pkgManager) {
case "yarn@berry":
case "yarn": return ["yarn", "exec"];
case "pnpm@6":
case "pnpm": return ["pnpx"];
case "bun": return ["bunx"];
default: return ["npx"];
}
}
async function create() {
log(c.cyan("◼"), "This utility will help you set up a browser testing environment.\n");
const pkgJsonPath = resolve(process.cwd(), "package.json");
const pkg = readPkgJson(pkgJsonPath) || {};
const dependencies = {
...pkg.dependencies,
...pkg.devDependencies
};
const defaults = getPossibleDefaults(dependencies);
const { lang } = await prompt({
type: "select",
name: "lang",
message: "Choose a language for your tests",
choices: sort(getLanguageOptions(), defaults?.lang)
});
if (!lang) return fail();
const { provider } = await prompt({
type: "select",
name: "provider",
message: "Choose a browser provider. Vitest will use its API to control the testing environment",
choices: sort(getProviderOptions(), defaults?.provider)
});
if (!provider) return fail();
const { browsers } = await prompt({
type: "multiselect",
name: "browsers",
message: "Choose a browser",
choices: getBrowserNames(provider).map((browser) => ({
title: browser,
value: browser
}))
});
if (!provider) return fail();
const { framework } = await prompt({
type: "select",
name: "framework",
message: "Choose your framework",
choices: sort(getFramework(), defaults?.framework)
});
if (!framework) return fail();
let installPlaywright = false;
if (provider === "playwright") ({installPlaywright} = await prompt({
type: "confirm",
name: "installPlaywright",
message: `Install Playwright browsers (can be done manually via 'pnpm exec playwright install')?`
}));
if (installPlaywright == null) return fail();
const dependenciesToInstall = [`@vitest/browser-${provider}`];
const frameworkPackage = getFrameworkTestPackage(framework);
if (frameworkPackage) dependenciesToInstall.push(frameworkPackage);
const frameworkPlugin = getFrameworkPluginPackage(framework);
if (frameworkPlugin) dependenciesToInstall.push(frameworkPlugin);
const pkgManager = await detectPackageManager();
log();
await installPackages(pkgManager, dependenciesToInstall.filter((pkg) => !dependencies[pkg]));
const rootConfig = any(configFiles, { cwd: process.cwd() });
let scriptCommand = "vitest";
log();
if (rootConfig) {
const configPath = resolve(dirname(rootConfig), `vitest.browser.config.${lang}`);
scriptCommand = `vitest --config=${relative(process.cwd(), configPath)}`;
await generateFrameworkConfigFile({
configPath,
framework,
frameworkPlugin,
provider,
browsers
});
log(
c.green("✔"),
"Created a new config file for browser tests:",
c.bold(relative(process.cwd(), configPath)),
// TODO: Can we modify the config ourselves?
"\nSince you already have a Vitest config file, it is recommended to copy the contents of the new file ",
"into your existing config located at ",
c.bold(relative(process.cwd(), rootConfig))
);
} else {
const configPath = resolve(process.cwd(), `vitest.config.${lang}`);
await generateFrameworkConfigFile({
configPath,
framework,
frameworkPlugin,
provider,
browsers
});
log(c.green("✔"), "Created a config file for browser tests:", c.bold(relative(process.cwd(), configPath)));
}
log();
await updatePkgJsonScripts(pkgJsonPath, scriptCommand);
if (installPlaywright) {
log();
const [command, ...args] = getPlaywrightRunArgs(pkgManager);
const allArgs = [
...args,
"playwright",
"install",
"--with-deps"
];
log(c.cyan("◼"), `Installing Playwright dependencies with \`${c.bold(command)} ${c.bold(allArgs.join(" "))}\`...`);
log();
await x(command, allArgs, { nodeOptions: { stdio: [
"pipe",
"inherit",
"inherit"
] } });
}
log();
const exampleTestFile = await generateExampleFiles(framework, lang);
log(c.green("✔"), "Created example test file in", c.bold(relative(process.cwd(), exampleTestFile)));
log(c.dim(" You can safely delete this file once you have written your own tests."));
log();
log(c.cyan("◼"), "All done! Run your tests with", c.bold(getRunScript(pkgManager)));
}
export { create };
+83
View File
@@ -0,0 +1,83 @@
import nodeos__default from 'node:os';
import './env.D4Lgay0q.js';
import { isCI, isAgent } from 'std-env';
const defaultInclude = ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
const defaultExclude = ["**/node_modules/**", "**/.git/**"];
const benchmarkConfigDefaults = {
include: ["**/*.{bench,benchmark}.?(c|m)[jt]s?(x)"],
exclude: defaultExclude,
includeSource: [],
reporters: ["default"],
includeSamples: false
};
// These are the generic defaults for coverage. Providers may also set some provider specific defaults.
const coverageConfigDefaults = {
provider: "v8",
enabled: false,
clean: true,
cleanOnRerun: true,
reportsDirectory: "./coverage",
exclude: [],
reportOnFailure: false,
reporter: [
"text",
"html",
"clover",
"json"
],
allowExternal: false,
excludeAfterRemap: false,
processingConcurrency: Math.min(20, nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length),
ignoreClassMethods: [],
skipFull: false,
watermarks: {
statements: [50, 80],
functions: [50, 80],
branches: [50, 80],
lines: [50, 80]
}
};
const fakeTimersDefaults = {
loopLimit: 1e4,
shouldClearNativeTimers: true
};
const configDefaults = Object.freeze({
allowOnly: !isCI,
isolate: true,
watch: !isCI && process.stdin.isTTY && !isAgent,
globals: false,
environment: "node",
clearMocks: false,
restoreMocks: false,
mockReset: false,
unstubGlobals: false,
unstubEnvs: false,
include: defaultInclude,
exclude: defaultExclude,
teardownTimeout: 1e4,
forceRerunTriggers: ["**/package.json/**", "**/{vitest,vite}.config.*/**"],
update: false,
reporters: [],
silent: false,
hideSkippedTests: false,
api: false,
ui: false,
uiBase: "/__vitest__/",
open: !isCI,
css: { include: [] },
coverage: coverageConfigDefaults,
fakeTimers: fakeTimersDefaults,
maxConcurrency: 5,
dangerouslyIgnoreUnhandledErrors: false,
typecheck: {
checker: "tsc",
include: ["**/*.{test,spec}-d.?(c|m)[jt]s?(x)"],
exclude: defaultExclude
},
slowTestThreshold: 300,
disableConsoleIntercept: false,
detectAsyncLeaks: false
});
export { coverageConfigDefaults as a, defaultInclude as b, configDefaults as c, defaultExclude as d, benchmarkConfigDefaults as e };
+8
View File
@@ -0,0 +1,8 @@
import { isCI } from 'std-env';
const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
const isWindows = (isNode || isDeno) && process.platform === "win32";
const isTTY = (isNode || isDeno) && process.stdout?.isTTY && !isCI;
export { isWindows as a, isTTY as i };
+29
View File
@@ -0,0 +1,29 @@
import { Awaitable } from '@vitest/utils';
interface EnvironmentReturn {
teardown: (global: any) => Awaitable<void>;
}
interface VmEnvironmentReturn {
getVmContext: () => {
[key: string]: any;
};
teardown: () => Awaitable<void>;
}
interface Environment {
name: string;
/**
* @deprecated use `viteEnvironment` instead. Uses `name` by default
*/
transformMode?: "web" | "ssr";
/**
* Environment initiated by the Vite server. It is usually available
* as `vite.server.environments.${name}`.
*
* By default, fallbacks to `name`.
*/
viteEnvironment?: "client" | "ssr" | ({} & string);
setupVM?: (options: Record<string, any>) => Awaitable<VmEnvironmentReturn>;
setup: (global: any, options: Record<string, any>) => Awaitable<EnvironmentReturn>;
}
export type { Environment as E, VmEnvironmentReturn as V, EnvironmentReturn as a };
+17
View File
@@ -0,0 +1,17 @@
import { dirname, resolve } from 'pathe';
import { EvaluatedModules } from 'vite/module-runner';
// TODO: this is not needed in Vite 7.2+
class VitestEvaluatedModules extends EvaluatedModules {
getModuleSourceMapById(id) {
const map = super.getModuleSourceMapById(id);
if (map != null && !("_patched" in map)) {
map._patched = true;
const dir = dirname(map.url);
map.resolvedSources = (map.map.sources || []).map((s) => resolve(dir, s || ""));
}
return map;
}
}
export { VitestEvaluatedModules as V };
+7
View File
@@ -0,0 +1,7 @@
import { EvaluatedModules } from 'vite/module-runner';
declare class VitestEvaluatedModules extends EvaluatedModules {
getModuleSourceMapById(id: string): any;
}
export { VitestEvaluatedModules as V };
+100
View File
@@ -0,0 +1,100 @@
import { PromisifyAssertion, Tester, ExpectStatic } from '@vitest/expect';
import { Plugin } from '@vitest/pretty-format';
import { Test } from '@vitest/runner';
import { SnapshotState } from '@vitest/snapshot';
import { B as BenchmarkResult } from './benchmark.d.DAaHLpsq.js';
import { U as UserConsoleLog } from './traces.d.D2T_R8rx.js';
interface SnapshotMatcher<T> {
<U extends { [P in keyof T] : any }>(snapshot: Partial<U>, hint?: string): void;
(hint?: string): void;
}
interface InlineSnapshotMatcher<T> {
<U extends { [P in keyof T] : any }>(properties: Partial<U>, snapshot?: string, hint?: string): void;
(hint?: string): void;
}
declare module "@vitest/expect" {
interface MatcherState {
environment: string;
snapshotState: SnapshotState;
task?: Readonly<Test>;
}
interface ExpectPollOptions {
interval?: number;
timeout?: number;
message?: string;
}
interface ExpectStatic {
assert: Chai.AssertStatic;
unreachable: (message?: string) => never;
soft: <T>(actual: T, message?: string) => Assertion<T>;
poll: <T>(actual: () => T, options?: ExpectPollOptions) => PromisifyAssertion<Awaited<T>>;
addEqualityTesters: (testers: Array<Tester>) => void;
assertions: (expected: number) => void;
hasAssertions: () => void;
addSnapshotSerializer: (plugin: Plugin) => void;
}
interface Assertion<T> {
matchSnapshot: SnapshotMatcher<T>;
toMatchSnapshot: SnapshotMatcher<T>;
toMatchInlineSnapshot: InlineSnapshotMatcher<T>;
/**
* Checks that an error thrown by a function matches a previously recorded snapshot.
*
* @param hint - Optional custom error message.
*
* @example
* expect(functionWithError).toThrowErrorMatchingSnapshot();
*/
toThrowErrorMatchingSnapshot: (hint?: string) => void;
/**
* Checks that an error thrown by a function matches an inline snapshot within the test file.
* Useful for keeping snapshots close to the test code.
*
* @param snapshot - Optional inline snapshot string to match.
* @param hint - Optional custom error message.
*
* @example
* const throwError = () => { throw new Error('Error occurred') };
* expect(throwError).toThrowErrorMatchingInlineSnapshot(`"Error occurred"`);
*/
toThrowErrorMatchingInlineSnapshot: (snapshot?: string, hint?: string) => void;
/**
* Compares the received value to a snapshot saved in a specified file.
* Useful for cases where snapshot content is large or needs to be shared across tests.
*
* @param filepath - Path to the snapshot file.
* @param hint - Optional custom error message.
*
* @example
* await expect(largeData).toMatchFileSnapshot('path/to/snapshot.json');
*/
toMatchFileSnapshot: (filepath: string, hint?: string) => Promise<void>;
}
}
declare module "@vitest/runner" {
interface TestContext {
/**
* `expect` instance bound to the current test.
*
* This API is useful for running snapshot tests concurrently because global expect cannot track them.
*/
readonly expect: ExpectStatic;
/** @internal */
_local: boolean;
}
interface TaskMeta {
typecheck?: boolean;
benchmark?: boolean;
}
interface File {
prepareDuration?: number;
environmentLoad?: number;
}
interface TaskBase {
logs?: UserConsoleLog[];
}
interface TaskResult {
benchmark?: BenchmarkResult;
}
}
+31
View File
@@ -0,0 +1,31 @@
import { g as globalApis } from './constants.CPYnjOGj.js';
import { i as index } from './index.DdgEv5B1.js';
import './test.DNmyFkvJ.js';
import '@vitest/runner';
import '@vitest/utils/helpers';
import '@vitest/utils/timers';
import './benchmark.CX_oY03V.js';
import '@vitest/runner/utils';
import './utils.BX5Fg8C4.js';
import '@vitest/expect';
import '@vitest/utils/error';
import 'pathe';
import '@vitest/spy';
import '@vitest/utils/offset';
import '@vitest/utils/source-map';
import './_commonjsHelpers.D26ty3Ew.js';
import './rpc.MzXet3jl.js';
import './index.Chj8NDwU.js';
import '@vitest/snapshot';
import './evaluatedModules.Dg1zASAC.js';
import 'vite/module-runner';
import 'expect-type';
function registerApiGlobally() {
globalApis.forEach((api) => {
// @ts-expect-error I know what I am doing :P
globalThis[api] = index[api];
});
}
export { registerApiGlobally };
+2338
View File
File diff suppressed because it is too large Load Diff
+238
View File
@@ -0,0 +1,238 @@
import process from 'node:process';
import fs from 'node:fs/promises';
import path, { resolve } from 'node:path';
import { existsSync } from 'node:fs';
import { x } from 'tinyexec';
const AGENTS = [
"npm",
"yarn",
"yarn@berry",
"pnpm",
"pnpm@6",
"bun",
"deno"
];
const LOCKS = {
"bun.lock": "bun",
"bun.lockb": "bun",
"deno.lock": "deno",
"pnpm-lock.yaml": "pnpm",
"pnpm-workspace.yaml": "pnpm",
"yarn.lock": "yarn",
"package-lock.json": "npm",
"npm-shrinkwrap.json": "npm"
};
const INSTALL_METADATA = {
"node_modules/.deno/": "deno",
"node_modules/.pnpm/": "pnpm",
"node_modules/.yarn-state.yml": "yarn",
// yarn v2+ (node-modules)
"node_modules/.yarn_integrity": "yarn",
// yarn v1
"node_modules/.package-lock.json": "npm",
".pnp.cjs": "yarn",
// yarn v3+ (pnp)
".pnp.js": "yarn",
// yarn v2 (pnp)
"bun.lock": "bun",
"bun.lockb": "bun"
};
async function pathExists(path2, type) {
try {
const stat = await fs.stat(path2);
return type === "file" ? stat.isFile() : stat.isDirectory();
} catch {
return false;
}
}
function* lookup(cwd = process.cwd()) {
let directory = path.resolve(cwd);
const { root } = path.parse(directory);
while (directory && directory !== root) {
yield directory;
directory = path.dirname(directory);
}
}
async function parsePackageJson(filepath, onUnknown) {
return !filepath || !pathExists(filepath, "file") ? null : await handlePackageManager(filepath, onUnknown);
}
async function detect(options = {}) {
const {
cwd,
strategies = ["lockfile", "packageManager-field", "devEngines-field"],
onUnknown
} = options;
let stopDir;
if (typeof options.stopDir === "string") {
const resolved = path.resolve(options.stopDir);
stopDir = (dir) => dir === resolved;
} else {
stopDir = options.stopDir;
}
for (const directory of lookup(cwd)) {
for (const strategy of strategies) {
switch (strategy) {
case "lockfile": {
for (const lock of Object.keys(LOCKS)) {
if (await pathExists(path.join(directory, lock), "file")) {
const name = LOCKS[lock];
const result = await parsePackageJson(path.join(directory, "package.json"), onUnknown);
if (result)
return result;
else
return { name, agent: name };
}
}
break;
}
case "packageManager-field":
case "devEngines-field": {
const result = await parsePackageJson(path.join(directory, "package.json"), onUnknown);
if (result)
return result;
break;
}
case "install-metadata": {
for (const metadata of Object.keys(INSTALL_METADATA)) {
const fileOrDir = metadata.endsWith("/") ? "dir" : "file";
if (await pathExists(path.join(directory, metadata), fileOrDir)) {
const name = INSTALL_METADATA[metadata];
const agent = name === "yarn" ? isMetadataYarnClassic(metadata) ? "yarn" : "yarn@berry" : name;
return { name, agent };
}
}
break;
}
}
}
if (stopDir?.(directory))
break;
}
return null;
}
function getNameAndVer(pkg) {
const handelVer = (version) => version?.match(/\d+(\.\d+){0,2}/)?.[0] ?? version;
if (typeof pkg.packageManager === "string") {
const [name, ver] = pkg.packageManager.replace(/^\^/, "").split("@");
return { name, ver: handelVer(ver) };
}
if (typeof pkg.devEngines?.packageManager?.name === "string") {
return {
name: pkg.devEngines.packageManager.name,
ver: handelVer(pkg.devEngines.packageManager.version)
};
}
return void 0;
}
async function handlePackageManager(filepath, onUnknown) {
try {
const pkg = JSON.parse(await fs.readFile(filepath, "utf8"));
let agent;
const nameAndVer = getNameAndVer(pkg);
if (nameAndVer) {
const name = nameAndVer.name;
const ver = nameAndVer.ver;
let version = ver;
if (name === "yarn" && ver && Number.parseInt(ver) > 1) {
agent = "yarn@berry";
version = "berry";
return { name, agent, version };
} else if (name === "pnpm" && ver && Number.parseInt(ver) < 7) {
agent = "pnpm@6";
return { name, agent, version };
} else if (AGENTS.includes(name)) {
agent = name;
return { name, agent, version };
} else {
return onUnknown?.(pkg.packageManager) ?? null;
}
}
} catch {
}
return null;
}
function isMetadataYarnClassic(metadataPath) {
return metadataPath.endsWith(".yarn_integrity");
}
// src/detect.ts
async function detectPackageManager(cwd = process.cwd()) {
const result = await detect({
cwd,
onUnknown(packageManager) {
console.warn("[@antfu/install-pkg] Unknown packageManager:", packageManager);
return void 0;
}
});
return result?.agent || null;
}
async function installPackage(names, options = {}) {
const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || "npm";
const [agent] = detectedAgent.split("@");
if (!Array.isArray(names))
names = [names];
const args = (typeof options.additionalArgs === "function" ? options.additionalArgs(agent, detectedAgent) : options.additionalArgs) || [];
if (options.preferOffline) {
if (detectedAgent === "yarn@berry")
args.unshift("--cached");
else
args.unshift("--prefer-offline");
}
if (agent === "pnpm") {
args.unshift(
/**
* Prevent pnpm from removing installed devDeps while `NODE_ENV` is `production`
* @see https://pnpm.io/cli/install#--prod--p
*/
"--prod=false"
);
if (existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) {
args.unshift("-w");
}
}
return x(
agent,
[
agent === "yarn" ? "add" : "install",
options.dev ? "-D" : "",
...args,
...names
].filter(Boolean),
{
nodeOptions: {
stdio: options.silent ? "ignore" : "inherit",
cwd: options.cwd
},
throwOnError: true
}
);
}
async function uninstallPackage(names, options = {}) {
const detectedAgent = options.packageManager || await detectPackageManager(options.cwd) || "npm";
const [agent] = detectedAgent.split("@");
if (!Array.isArray(names))
names = [names];
const args = options.additionalArgs || [];
if (agent === "pnpm" && existsSync(resolve(options.cwd ?? process.cwd(), "pnpm-workspace.yaml")))
args.unshift("-w");
return x(
agent,
[
agent === "yarn" ? "remove" : "uninstall",
options.dev ? "-D" : "",
...args,
...names
].filter(Boolean),
{
nodeOptions: {
stdio: options.silent ? "ignore" : "inherit",
cwd: options.cwd
},
throwOnError: true
}
);
}
export { detectPackageManager, installPackage, uninstallPackage };
+206
View File
@@ -0,0 +1,206 @@
//#region src/messages.ts
const TYPE_REQUEST = "q";
const TYPE_RESPONSE = "s";
//#endregion
//#region src/utils.ts
function createPromiseWithResolvers() {
let resolve;
let reject;
return {
promise: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
resolve,
reject
};
}
const random = Math.random.bind(Math);
const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
function nanoid(size = 21) {
let id = "";
let i = size;
while (i--) id += urlAlphabet[random() * 64 | 0];
return id;
}
//#endregion
//#region src/main.ts
const DEFAULT_TIMEOUT = 6e4;
const defaultSerialize = (i) => i;
const defaultDeserialize = defaultSerialize;
const { clearTimeout, setTimeout } = globalThis;
function createBirpc($functions, options) {
const { post, on, off = () => {}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT, proxify = true } = options;
let $closed = false;
const _rpcPromiseMap = /* @__PURE__ */ new Map();
let _promiseInit;
let rpc;
async function _call(method, args, event, optional) {
if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
const req = {
m: method,
a: args,
t: TYPE_REQUEST
};
if (optional) req.o = true;
const send = async (_req) => post(serialize(_req));
if (event) {
await send(req);
return;
}
if (_promiseInit) try {
await _promiseInit;
} finally {
_promiseInit = void 0;
}
let { promise, resolve, reject } = createPromiseWithResolvers();
const id = nanoid();
req.i = id;
let timeoutId;
async function handler(newReq = req) {
if (timeout >= 0) {
timeoutId = setTimeout(() => {
try {
if (options.onTimeoutError?.call(rpc, method, args) !== true) throw new Error(`[birpc] timeout on calling "${method}"`);
} catch (e) {
reject(e);
}
_rpcPromiseMap.delete(id);
}, timeout);
if (typeof timeoutId === "object") timeoutId = timeoutId.unref?.();
}
_rpcPromiseMap.set(id, {
resolve,
reject,
timeoutId,
method
});
await send(newReq);
return promise;
}
try {
if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
else await handler();
} catch (e) {
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
return;
} finally {
clearTimeout(timeoutId);
_rpcPromiseMap.delete(id);
}
return promise;
}
const builtinMethods = {
$call: (method, ...args) => _call(method, args, false),
$callOptional: (method, ...args) => _call(method, args, false, true),
$callEvent: (method, ...args) => _call(method, args, true),
$callRaw: (options$1) => _call(options$1.method, options$1.args, options$1.event, options$1.optional),
$rejectPendingCalls,
get $closed() {
return $closed;
},
get $meta() {
return options.meta;
},
$close,
$functions
};
if (proxify) rpc = new Proxy({}, { get(_, method) {
if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
if (method === "then" && !eventNames.includes("then") && !("then" in $functions)) return void 0;
const sendEvent = (...args) => _call(method, args, true);
if (eventNames.includes(method)) {
sendEvent.asEvent = sendEvent;
return sendEvent;
}
const sendCall = (...args) => _call(method, args, false);
sendCall.asEvent = sendEvent;
return sendCall;
} });
else rpc = builtinMethods;
function $close(customError) {
$closed = true;
_rpcPromiseMap.forEach(({ reject, method }) => {
const error = /* @__PURE__ */ new Error(`[birpc] rpc is closed, cannot call "${method}"`);
if (customError) {
customError.cause ??= error;
return reject(customError);
}
reject(error);
});
_rpcPromiseMap.clear();
off(onMessage);
}
function $rejectPendingCalls(handler) {
const handlerResults = Array.from(_rpcPromiseMap.values()).map(({ method, reject }) => {
if (!handler) return reject(/* @__PURE__ */ new Error(`[birpc]: rejected pending call "${method}".`));
return handler({
method,
reject
});
});
_rpcPromiseMap.clear();
return handlerResults;
}
async function onMessage(data, ...extra) {
let msg;
try {
msg = deserialize(data);
} catch (e) {
if (options.onGeneralError?.call(rpc, e) !== true) throw e;
return;
}
if (msg.t === TYPE_REQUEST) {
const { m: method, a: args, o: optional } = msg;
let result, error;
let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
if (optional) fn ||= () => void 0;
if (!fn) error = /* @__PURE__ */ new Error(`[birpc] function "${method}" not found`);
else try {
result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
} catch (e) {
error = e;
}
if (msg.i) {
if (error && options.onFunctionError) {
if (options.onFunctionError.call(rpc, error, method, args) === true) return;
}
if (!error) try {
await post(serialize({
t: TYPE_RESPONSE,
i: msg.i,
r: result
}), ...extra);
return;
} catch (e) {
error = e;
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
}
try {
await post(serialize({
t: TYPE_RESPONSE,
i: msg.i,
e: error
}), ...extra);
} catch (e) {
if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
}
}
} else {
const { i: ack, r: result, e: error } = msg;
const promise = _rpcPromiseMap.get(ack);
if (promise) {
clearTimeout(promise.timeoutId);
if (error) promise.reject(error);
else promise.resolve(result);
}
_rpcPromiseMap.delete(ack);
}
}
_promiseInit = on(onMessage);
return rpc;
}
export { createBirpc as c };
+729
View File
@@ -0,0 +1,729 @@
import { URL as URL$1 } from 'node:url';
import { Console } from 'node:console';
// SEE https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/interfaces.js
const LIVING_KEYS = [
"DOMException",
"EventTarget",
"NamedNodeMap",
"Node",
"Attr",
"Element",
"DocumentFragment",
"DOMImplementation",
"Document",
"XMLDocument",
"CharacterData",
"Text",
"CDATASection",
"ProcessingInstruction",
"Comment",
"DocumentType",
"NodeList",
"RadioNodeList",
"HTMLCollection",
"HTMLOptionsCollection",
"DOMStringMap",
"DOMTokenList",
"StyleSheetList",
"HTMLElement",
"HTMLHeadElement",
"HTMLTitleElement",
"HTMLBaseElement",
"HTMLLinkElement",
"HTMLMetaElement",
"HTMLStyleElement",
"HTMLBodyElement",
"HTMLHeadingElement",
"HTMLParagraphElement",
"HTMLHRElement",
"HTMLPreElement",
"HTMLUListElement",
"HTMLOListElement",
"HTMLLIElement",
"HTMLMenuElement",
"HTMLDListElement",
"HTMLDivElement",
"HTMLAnchorElement",
"HTMLAreaElement",
"HTMLBRElement",
"HTMLButtonElement",
"HTMLCanvasElement",
"HTMLDataElement",
"HTMLDataListElement",
"HTMLDetailsElement",
"HTMLDialogElement",
"HTMLDirectoryElement",
"HTMLFieldSetElement",
"HTMLFontElement",
"HTMLFormElement",
"HTMLHtmlElement",
"HTMLImageElement",
"HTMLInputElement",
"HTMLLabelElement",
"HTMLLegendElement",
"HTMLMapElement",
"HTMLMarqueeElement",
"HTMLMediaElement",
"HTMLMeterElement",
"HTMLModElement",
"HTMLOptGroupElement",
"HTMLOptionElement",
"HTMLOutputElement",
"HTMLPictureElement",
"HTMLProgressElement",
"HTMLQuoteElement",
"HTMLScriptElement",
"HTMLSelectElement",
"HTMLSlotElement",
"HTMLSourceElement",
"HTMLSpanElement",
"HTMLTableCaptionElement",
"HTMLTableCellElement",
"HTMLTableColElement",
"HTMLTableElement",
"HTMLTimeElement",
"HTMLTableRowElement",
"HTMLTableSectionElement",
"HTMLTemplateElement",
"HTMLTextAreaElement",
"HTMLUnknownElement",
"HTMLFrameElement",
"HTMLFrameSetElement",
"HTMLIFrameElement",
"HTMLEmbedElement",
"HTMLObjectElement",
"HTMLParamElement",
"HTMLVideoElement",
"HTMLAudioElement",
"HTMLTrackElement",
"HTMLFormControlsCollection",
"SVGElement",
"SVGGraphicsElement",
"SVGSVGElement",
"SVGTitleElement",
"SVGAnimatedString",
"SVGNumber",
"SVGStringList",
"Event",
"CloseEvent",
"CustomEvent",
"MessageEvent",
"ErrorEvent",
"HashChangeEvent",
"PopStateEvent",
"StorageEvent",
"ProgressEvent",
"PageTransitionEvent",
"SubmitEvent",
"UIEvent",
"FocusEvent",
"InputEvent",
"MouseEvent",
"KeyboardEvent",
"TouchEvent",
"CompositionEvent",
"WheelEvent",
"BarProp",
"External",
"Location",
"History",
"Screen",
"Crypto",
"Performance",
"Navigator",
"PluginArray",
"MimeTypeArray",
"Plugin",
"MimeType",
"FileReader",
"FormData",
"Blob",
"File",
"FileList",
"ValidityState",
"DOMParser",
"XMLSerializer",
"XMLHttpRequestEventTarget",
"XMLHttpRequestUpload",
"XMLHttpRequest",
"WebSocket",
"NodeFilter",
"NodeIterator",
"TreeWalker",
"AbstractRange",
"Range",
"StaticRange",
"Selection",
"Storage",
"CustomElementRegistry",
"ShadowRoot",
"MutationObserver",
"MutationRecord",
"Uint8Array",
"Uint16Array",
"Uint32Array",
"Uint8ClampedArray",
"Int8Array",
"Int16Array",
"Int32Array",
"Float32Array",
"Float64Array",
"ArrayBuffer",
"DOMRectReadOnly",
"DOMRect",
"Image",
"Audio",
"Option",
"CSS"
];
const OTHER_KEYS = [
"addEventListener",
"alert",
"blur",
"cancelAnimationFrame",
"close",
"confirm",
"createPopup",
"dispatchEvent",
"document",
"focus",
"frames",
"getComputedStyle",
"history",
"innerHeight",
"innerWidth",
"length",
"location",
"matchMedia",
"moveBy",
"moveTo",
"name",
"navigator",
"open",
"outerHeight",
"outerWidth",
"pageXOffset",
"pageYOffset",
"parent",
"postMessage",
"print",
"prompt",
"removeEventListener",
"requestAnimationFrame",
"resizeBy",
"resizeTo",
"screen",
"screenLeft",
"screenTop",
"screenX",
"screenY",
"scroll",
"scrollBy",
"scrollLeft",
"scrollTo",
"scrollTop",
"scrollX",
"scrollY",
"self",
"stop",
"top",
"Window",
"window"
];
const KEYS = LIVING_KEYS.concat(OTHER_KEYS);
const skipKeys = [
"window",
"self",
"top",
"parent"
];
function getWindowKeys(global, win, additionalKeys = []) {
const keysArray = [...additionalKeys, ...KEYS];
return new Set(keysArray.concat(Object.getOwnPropertyNames(win)).filter((k) => {
if (skipKeys.includes(k)) return false;
if (k in global) return keysArray.includes(k);
return true;
}));
}
function isClassLikeName(name) {
return name[0] === name[0].toUpperCase();
}
function populateGlobal(global, win, options = {}) {
const { bindFunctions = false } = options;
const keys = getWindowKeys(global, win, options.additionalKeys);
const originals = /* @__PURE__ */ new Map();
const overriddenKeys = new Set([...KEYS, ...options.additionalKeys || []]);
const overrideObject = /* @__PURE__ */ new Map();
for (const key of keys) {
const boundFunction = bindFunctions && typeof win[key] === "function" && !isClassLikeName(key) && win[key].bind(win);
if (overriddenKeys.has(key) && key in global) originals.set(key, global[key]);
Object.defineProperty(global, key, {
get() {
if (overrideObject.has(key)) return overrideObject.get(key);
if (boundFunction) return boundFunction;
return win[key];
},
set(v) {
overrideObject.set(key, v);
},
configurable: true
});
}
global.window = global;
global.self = global;
global.top = global;
global.parent = global;
if (global.global) global.global = global;
// rewrite defaultView to reference the same global context
if (global.document && global.document.defaultView) Object.defineProperty(global.document, "defaultView", {
get: () => global,
enumerable: true,
configurable: true
});
skipKeys.forEach((k) => keys.add(k));
return {
keys,
skipKeys,
originals
};
}
var edge = {
name: "edge-runtime",
viteEnvironment: "ssr",
async setupVM() {
const { EdgeVM } = await import('@edge-runtime/vm');
const vm = new EdgeVM({ extend: (context) => {
context.global = context;
context.Buffer = Buffer;
return context;
} });
return {
getVmContext() {
return vm.context;
},
teardown() {
// nothing to teardown
}
};
},
async setup(global) {
const { EdgeVM } = await import('@edge-runtime/vm');
const { keys, originals } = populateGlobal(global, new EdgeVM({ extend: (context) => {
context.global = context;
context.Buffer = Buffer;
KEYS.forEach((key) => {
if (key in global) context[key] = global[key];
});
return context;
} }).context, { bindFunctions: true });
return { teardown(global) {
keys.forEach((key) => delete global[key]);
originals.forEach((v, k) => global[k] = v);
} };
}
};
async function teardownWindow(win) {
if (win.close && win.happyDOM.abort) {
await win.happyDOM.abort();
win.close();
} else win.happyDOM.cancelAsync();
}
var happy = {
name: "happy-dom",
viteEnvironment: "client",
async setupVM({ happyDOM = {} }) {
const { Window } = await import('happy-dom');
let win = new Window({
...happyDOM,
console: console && globalThis.console ? globalThis.console : void 0,
url: happyDOM.url || "http://localhost:3000",
settings: {
...happyDOM.settings,
disableErrorCapturing: true
}
});
// TODO: browser doesn't expose Buffer, but a lot of dependencies use it
win.Buffer = Buffer;
// inject structuredClone if it exists
if (typeof structuredClone !== "undefined" && !win.structuredClone) win.structuredClone = structuredClone;
return {
getVmContext() {
return win;
},
async teardown() {
await teardownWindow(win);
win = void 0;
}
};
},
async setup(global, { happyDOM = {} }) {
// happy-dom v3 introduced a breaking change to Window, but
// provides GlobalWindow as a way to use previous behaviour
const { Window, GlobalWindow } = await import('happy-dom');
const win = new (GlobalWindow || Window)({
...happyDOM,
console: console && global.console ? global.console : void 0,
url: happyDOM.url || "http://localhost:3000",
settings: {
...happyDOM.settings,
disableErrorCapturing: true
}
});
const { keys, originals } = populateGlobal(global, win, {
bindFunctions: true,
additionalKeys: [
"Request",
"Response",
"MessagePort",
"fetch",
"Headers",
"AbortController",
"AbortSignal",
"URL",
"URLSearchParams",
"FormData"
]
});
return { async teardown(global) {
await teardownWindow(win);
keys.forEach((key) => delete global[key]);
originals.forEach((v, k) => global[k] = v);
} };
}
};
function catchWindowErrors(window) {
let userErrorListenerCount = 0;
function throwUnhandlerError(e) {
if (userErrorListenerCount === 0 && e.error != null) {
e.preventDefault();
process.emit("uncaughtException", e.error);
}
}
const addEventListener = window.addEventListener.bind(window);
const removeEventListener = window.removeEventListener.bind(window);
window.addEventListener("error", throwUnhandlerError);
window.addEventListener = function(...args) {
if (args[0] === "error") userErrorListenerCount++;
return addEventListener.apply(this, args);
};
window.removeEventListener = function(...args) {
if (args[0] === "error" && userErrorListenerCount) userErrorListenerCount--;
return removeEventListener.apply(this, args);
};
return function clearErrorHandlers() {
window.removeEventListener("error", throwUnhandlerError);
};
}
let NodeFormData_;
let NodeBlob_;
let NodeRequest_;
var jsdom = {
name: "jsdom",
viteEnvironment: "client",
async setupVM({ jsdom = {} }) {
// delay initialization because it takes ~1s
NodeFormData_ = globalThis.FormData;
NodeBlob_ = globalThis.Blob;
NodeRequest_ = globalThis.Request;
const { CookieJar, JSDOM, ResourceLoader, VirtualConsole } = await import('jsdom');
const { html = "<!DOCTYPE html>", userAgent, url = "http://localhost:3000", contentType = "text/html", pretendToBeVisual = true, includeNodeLocations = false, runScripts = "dangerously", resources, console = false, cookieJar = false, ...restOptions } = jsdom;
let virtualConsole;
if (console && globalThis.console) {
virtualConsole = new VirtualConsole();
// jsdom <27
if ("sendTo" in virtualConsole) virtualConsole.sendTo(globalThis.console);
else virtualConsole.forwardTo(globalThis.console);
}
let dom = new JSDOM(html, {
pretendToBeVisual,
resources: resources ?? (userAgent ? new ResourceLoader({ userAgent }) : void 0),
runScripts,
url,
virtualConsole,
cookieJar: cookieJar ? new CookieJar() : void 0,
includeNodeLocations,
contentType,
userAgent,
...restOptions
});
const clearAddEventListenerPatch = patchAddEventListener(dom.window);
const clearWindowErrors = catchWindowErrors(dom.window);
const utils = createCompatUtils(dom.window);
// TODO: browser doesn't expose Buffer, but a lot of dependencies use it
dom.window.Buffer = Buffer;
dom.window.jsdom = dom;
dom.window.Request = createCompatRequest(utils);
dom.window.URL = createJSDOMCompatURL(utils);
for (const name of [
"structuredClone",
"BroadcastChannel",
"MessageChannel",
"MessagePort",
"TextEncoder",
"TextDecoder"
]) {
const value = globalThis[name];
if (typeof value !== "undefined" && typeof dom.window[name] === "undefined") dom.window[name] = value;
}
for (const name of [
"fetch",
"Response",
"Headers",
"AbortController",
"AbortSignal",
"URLSearchParams"
]) {
const value = globalThis[name];
if (typeof value !== "undefined") dom.window[name] = value;
}
return {
getVmContext() {
return dom.getInternalVMContext();
},
teardown() {
clearAddEventListenerPatch();
clearWindowErrors();
dom.window.close();
dom = void 0;
}
};
},
async setup(global, { jsdom = {} }) {
// delay initialization because it takes ~1s
NodeFormData_ = globalThis.FormData;
NodeBlob_ = globalThis.Blob;
NodeRequest_ = globalThis.Request;
const { CookieJar, JSDOM, ResourceLoader, VirtualConsole } = await import('jsdom');
const { html = "<!DOCTYPE html>", userAgent, url = "http://localhost:3000", contentType = "text/html", pretendToBeVisual = true, includeNodeLocations = false, runScripts = "dangerously", resources, console = false, cookieJar = false, ...restOptions } = jsdom;
let virtualConsole;
if (console && globalThis.console) {
virtualConsole = new VirtualConsole();
// jsdom <27
if ("sendTo" in virtualConsole) virtualConsole.sendTo(globalThis.console);
else virtualConsole.forwardTo(globalThis.console);
}
const dom = new JSDOM(html, {
pretendToBeVisual,
resources: resources ?? (userAgent ? new ResourceLoader({ userAgent }) : void 0),
runScripts,
url,
virtualConsole,
cookieJar: cookieJar ? new CookieJar() : void 0,
includeNodeLocations,
contentType,
userAgent,
...restOptions
});
const clearAddEventListenerPatch = patchAddEventListener(dom.window);
const { keys, originals } = populateGlobal(global, dom.window, { bindFunctions: true });
const clearWindowErrors = catchWindowErrors(global);
const utils = createCompatUtils(dom.window);
global.jsdom = dom;
global.Request = createCompatRequest(utils);
global.URL = createJSDOMCompatURL(utils);
return { teardown(global) {
clearAddEventListenerPatch();
clearWindowErrors();
dom.window.close();
delete global.jsdom;
keys.forEach((key) => delete global[key]);
originals.forEach((v, k) => global[k] = v);
} };
}
};
function createCompatRequest(utils) {
class Request extends NodeRequest_ {
constructor(...args) {
const [input, init] = args;
if (init?.body != null) {
const compatInit = { ...init };
if (init.body instanceof utils.window.Blob) compatInit.body = utils.makeCompatBlob(init.body);
if (init.body instanceof utils.window.FormData) compatInit.body = utils.makeCompatFormData(init.body);
super(input, compatInit);
} else super(...args);
}
static [Symbol.hasInstance](instance) {
return instance instanceof NodeRequest_;
}
}
return Request;
}
function createJSDOMCompatURL(utils) {
class URL extends URL$1 {
static createObjectURL(blob) {
if (blob instanceof utils.window.Blob) {
const compatBlob = utils.makeCompatBlob(blob);
return URL$1.createObjectURL(compatBlob);
}
return URL$1.createObjectURL(blob);
}
static [Symbol.hasInstance](instance) {
return instance instanceof URL$1;
}
}
return URL;
}
function createCompatUtils(window) {
// this returns a hidden Symbol(impl)
// this is cursed, and jsdom should just implement fetch API itself
const implSymbol = Object.getOwnPropertySymbols(Object.getOwnPropertyDescriptors(new window.Blob()))[0];
const utils = {
window,
makeCompatFormData(formData) {
const nodeFormData = new NodeFormData_();
formData.forEach((value, key) => {
if (value instanceof window.Blob) nodeFormData.append(key, utils.makeCompatBlob(value));
else nodeFormData.append(key, value);
});
return nodeFormData;
},
makeCompatBlob(blob) {
const buffer = blob[implSymbol]._buffer;
return new NodeBlob_([buffer], { type: blob.type });
}
};
return utils;
}
function patchAddEventListener(window) {
const abortControllers = /* @__PURE__ */ new WeakMap();
const JSDOMAbortSignal = window.AbortSignal;
const JSDOMAbortController = window.AbortController;
const originalAddEventListener = window.EventTarget.prototype.addEventListener;
function getJsdomAbortController(signal) {
if (!abortControllers.has(signal)) {
const jsdomAbortController = new JSDOMAbortController();
signal.addEventListener("abort", () => {
jsdomAbortController.abort(signal.reason);
});
abortControllers.set(signal, jsdomAbortController);
}
return abortControllers.get(signal);
}
window.EventTarget.prototype.addEventListener = function addEventListener(type, callback, options) {
if (typeof options === "object" && options?.signal != null) {
const { signal, ...otherOptions } = options;
// - this happens because AbortSignal is provided by Node.js,
// but jsdom APIs require jsdom's AbortSignal, while Node APIs
// (like fetch and Request) require a Node.js AbortSignal
// - disable narrow typing with "as any" because we need it later
if (!(signal instanceof JSDOMAbortSignal)) {
const jsdomCompatOptions = Object.create(null);
Object.assign(jsdomCompatOptions, otherOptions);
jsdomCompatOptions.signal = getJsdomAbortController(signal).signal;
return originalAddEventListener.call(this, type, callback, jsdomCompatOptions);
}
}
return originalAddEventListener.call(this, type, callback, options);
};
return () => {
window.EventTarget.prototype.addEventListener = originalAddEventListener;
};
}
// some globals we do not want, either because deprecated or we set it ourselves
const denyList = new Set([
"GLOBAL",
"root",
"global",
"Buffer",
"ArrayBuffer",
"Uint8Array"
]);
const nodeGlobals = /* @__PURE__ */ new Map();
function populateNodeGlobals() {
if (nodeGlobals.size !== 0) return;
const names = Object.getOwnPropertyNames(globalThis);
const length = names.length;
for (let i = 0; i < length; i++) {
const globalName = names[i];
if (!denyList.has(globalName)) {
const descriptor = Object.getOwnPropertyDescriptor(globalThis, globalName);
if (!descriptor) throw new Error(`No property descriptor for ${globalName}, this is a bug in Vitest.`);
nodeGlobals.set(globalName, descriptor);
}
}
}
var node = {
name: "node",
viteEnvironment: "ssr",
async setupVM() {
populateNodeGlobals();
const vm = await import('node:vm');
let context = vm.createContext();
let global = vm.runInContext("this", context);
const contextGlobals = new Set(Object.getOwnPropertyNames(global));
for (const [nodeGlobalsKey, descriptor] of nodeGlobals) if (!contextGlobals.has(nodeGlobalsKey)) if (descriptor.configurable) Object.defineProperty(global, nodeGlobalsKey, {
configurable: true,
enumerable: descriptor.enumerable,
get() {
// @ts-expect-error: no index signature
const val = globalThis[nodeGlobalsKey];
// override lazy getter
Object.defineProperty(global, nodeGlobalsKey, {
configurable: true,
enumerable: descriptor.enumerable,
value: val,
writable: descriptor.writable === true || nodeGlobalsKey === "performance"
});
return val;
},
set(val) {
// override lazy getter
Object.defineProperty(global, nodeGlobalsKey, {
configurable: true,
enumerable: descriptor.enumerable,
value: val,
writable: true
});
}
});
else if ("value" in descriptor) Object.defineProperty(global, nodeGlobalsKey, {
configurable: false,
enumerable: descriptor.enumerable,
value: descriptor.value,
writable: descriptor.writable
});
else Object.defineProperty(global, nodeGlobalsKey, {
configurable: false,
enumerable: descriptor.enumerable,
get: descriptor.get,
set: descriptor.set
});
global.global = global;
global.Buffer = Buffer;
global.ArrayBuffer = ArrayBuffer;
// TextEncoder (global or via 'util') references a Uint8Array constructor
// different than the global one used by users in tests. This makes sure the
// same constructor is referenced by both.
global.Uint8Array = Uint8Array;
return {
getVmContext() {
return context;
},
teardown() {
context = void 0;
global = void 0;
}
};
},
async setup(global) {
global.console.Console = Console;
return { teardown(global) {
delete global.console.Console;
} };
}
};
const environments = {
node,
jsdom,
"happy-dom": happy,
"edge-runtime": edge
};
export { environments as e, populateGlobal as p };
+195
View File
@@ -0,0 +1,195 @@
import { chai } from '@vitest/expect';
import { createHook } from 'node:async_hooks';
import { l as loadDiffConfig, a as loadSnapshotSerializers, t as takeCoverageInsideWorker } from './setup-common.DYx3LtFI.js';
import { r as rpc } from './rpc.MzXet3jl.js';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
import { T as TestRunner, N as NodeBenchmarkRunner } from './test.DNmyFkvJ.js';
function setupChaiConfig(config) {
Object.assign(chai.config, config);
}
async function resolveSnapshotEnvironment(config, moduleRunner) {
if (!config.snapshotEnvironment) {
const { VitestNodeSnapshotEnvironment } = await import('./node.COQbm6gK.js');
return new VitestNodeSnapshotEnvironment();
}
const mod = await moduleRunner.import(config.snapshotEnvironment);
if (typeof mod.default !== "object" || !mod.default) throw new Error("Snapshot environment module must have a default export object with a shape of `SnapshotEnvironment`");
return mod.default;
}
const IGNORED_TYPES = new Set([
"DNSCHANNEL",
"ELDHISTOGRAM",
"PerformanceObserver",
"RANDOMBYTESREQUEST",
"SIGNREQUEST",
"STREAM_END_OF_STREAM",
"TCPWRAP",
"TIMERWRAP",
"TLSWRAP",
"ZLIB"
]);
function detectAsyncLeaks(testFile, projectName) {
const resources = /* @__PURE__ */ new Map();
const hook = createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (IGNORED_TYPES.has(type)) return;
let stack = "";
const limit = Error.stackTraceLimit;
// VitestModuleEvaluator's async wrapper of node:vm causes out-of-bound stack traces, simply skip it.
// Crash fixed in https://github.com/vitejs/vite/pull/21585
try {
Error.stackTraceLimit = 100;
stack = (/* @__PURE__ */ new Error("VITEST_DETECT_ASYNC_LEAKS")).stack || "";
} catch {
return;
} finally {
Error.stackTraceLimit = limit;
}
if (!stack.includes(testFile)) {
const trigger = resources.get(triggerAsyncId);
if (!trigger) return;
stack = trigger.stack;
}
let isActive = isActiveDefault;
if ("hasRef" in resource) {
const ref = new WeakRef(resource);
isActive = () => ref.deref()?.hasRef() ?? false;
}
resources.set(asyncId, {
type,
stack,
projectName,
filename: testFile,
isActive
});
},
destroy(asyncId) {
if (resources.get(asyncId)?.type !== "PROMISE") resources.delete(asyncId);
},
promiseResolve(asyncId) {
resources.delete(asyncId);
}
});
hook.enable();
return async function collect() {
await Promise.resolve(setImmediate);
hook.disable();
const leaks = [];
for (const resource of resources.values()) if (resource.isActive()) leaks.push({
stack: resource.stack,
type: resource.type,
filename: resource.filename,
projectName: resource.projectName
});
resources.clear();
return leaks;
};
}
function isActiveDefault() {
return true;
}
async function getTestRunnerConstructor(config, moduleRunner) {
if (!config.runner) return config.mode === "test" ? TestRunner : NodeBenchmarkRunner;
const mod = await moduleRunner.import(config.runner);
if (!mod.default && typeof mod.default !== "function") throw new Error(`Runner must export a default function, but got ${typeof mod.default} imported from ${config.runner}`);
return mod.default;
}
async function resolveTestRunner(config, moduleRunner, traces) {
const testRunner = new (await (getTestRunnerConstructor(config, moduleRunner)))(config);
// inject private executor to every runner
Object.defineProperty(testRunner, "moduleRunner", {
value: moduleRunner,
enumerable: false,
configurable: false
});
if (!testRunner.config) testRunner.config = config;
if (!testRunner.importFile) throw new Error("Runner must implement \"importFile\" method.");
if ("__setTraces" in testRunner) testRunner.__setTraces(traces);
const [diffOptions] = await Promise.all([loadDiffConfig(config, moduleRunner), loadSnapshotSerializers(config, moduleRunner)]);
testRunner.config.diffOptions = diffOptions;
// patch some methods, so custom runners don't need to call RPC
const originalOnTaskUpdate = testRunner.onTaskUpdate;
testRunner.onTaskUpdate = async (task, events) => {
const p = rpc().onTaskUpdate(task, events);
await originalOnTaskUpdate?.call(testRunner, task, events);
return p;
};
// patch some methods, so custom runners don't need to call RPC
const originalOnTestAnnotate = testRunner.onTestAnnotate;
testRunner.onTestAnnotate = async (test, annotation) => {
const p = rpc().onTaskArtifactRecord(test.id, {
type: "internal:annotation",
location: annotation.location,
annotation
});
const overriddenResult = await originalOnTestAnnotate?.call(testRunner, test, annotation);
const vitestResult = await p;
return overriddenResult || vitestResult.annotation;
};
const originalOnTestArtifactRecord = testRunner.onTestArtifactRecord;
testRunner.onTestArtifactRecord = async (test, artifact) => {
const p = rpc().onTaskArtifactRecord(test.id, artifact);
const overriddenResult = await originalOnTestArtifactRecord?.call(testRunner, test, artifact);
const vitestResult = await p;
return overriddenResult || vitestResult;
};
const originalOnCollectStart = testRunner.onCollectStart;
testRunner.onCollectStart = async (file) => {
await rpc().onQueued(file);
await originalOnCollectStart?.call(testRunner, file);
};
const originalOnCollected = testRunner.onCollected;
testRunner.onCollected = async (files) => {
const state = getWorkerState();
files.forEach((file) => {
file.prepareDuration = state.durations.prepare;
file.environmentLoad = state.durations.environment;
// should be collected only for a single test file in a batch
state.durations.prepare = 0;
state.durations.environment = 0;
});
// Strip function conditions from retry config before sending via RPC
// Functions cannot be cloned by structured clone algorithm
const sanitizeRetryConditions = (task) => {
if (task.retry && typeof task.retry === "object" && typeof task.retry.condition === "function")
// Remove function condition - it can't be serialized
task.retry = {
...task.retry,
condition: void 0
};
if (task.tasks) task.tasks.forEach(sanitizeRetryConditions);
};
files.forEach(sanitizeRetryConditions);
rpc().onCollected(files);
await originalOnCollected?.call(testRunner, files);
};
const originalOnAfterRun = testRunner.onAfterRunFiles;
testRunner.onAfterRunFiles = async (files) => {
const state = getWorkerState();
const coverage = await takeCoverageInsideWorker(config.coverage, moduleRunner);
if (coverage) rpc().onAfterSuiteRun({
coverage,
testFiles: files.map((file) => file.name).sort(),
environment: state.environment.viteEnvironment || state.environment.name,
projectName: state.ctx.projectName
});
await originalOnAfterRun?.call(testRunner, files);
};
const originalOnAfterRunTask = testRunner.onAfterRunTask;
testRunner.onAfterRunTask = async (test) => {
if (config.bail && test.result?.state === "fail") {
if (1 + await rpc().getCountOfFailedTests() >= config.bail) {
rpc().onCancel("test-failure");
testRunner.cancel?.("test-failure");
}
}
await originalOnAfterRunTask?.call(testRunner, test);
};
return testRunner;
}
export { resolveSnapshotEnvironment as a, detectAsyncLeaks as d, resolveTestRunner as r, setupChaiConfig as s };
+42
View File
@@ -0,0 +1,42 @@
import { v as vi, N as NodeBenchmarkRunner, S as Snapshots, T as TestRunner, a as assert, c as createExpect, g as globalExpect, i as inject, s as should, b as vitest } from './test.DNmyFkvJ.js';
import { b as bench } from './benchmark.CX_oY03V.js';
import { V as VitestEvaluatedModules } from './evaluatedModules.Dg1zASAC.js';
import { expectTypeOf } from 'expect-type';
import { afterAll, afterEach, aroundAll, aroundEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, recordArtifact, suite, test } from '@vitest/runner';
import { chai } from '@vitest/expect';
const assertType = function assertType() {};
var index = /*#__PURE__*/Object.freeze({
__proto__: null,
BenchmarkRunner: NodeBenchmarkRunner,
EvaluatedModules: VitestEvaluatedModules,
Snapshots: Snapshots,
TestRunner: TestRunner,
afterAll: afterAll,
afterEach: afterEach,
aroundAll: aroundAll,
aroundEach: aroundEach,
assert: assert,
assertType: assertType,
beforeAll: beforeAll,
beforeEach: beforeEach,
bench: bench,
chai: chai,
createExpect: createExpect,
describe: describe,
expect: globalExpect,
expectTypeOf: expectTypeOf,
inject: inject,
it: it,
onTestFailed: onTestFailed,
onTestFinished: onTestFinished,
recordArtifact: recordArtifact,
should: should,
suite: suite,
test: test,
vi: vi,
vitest: vitest
});
export { assertType as a, index as i };
+4255
View File
File diff suppressed because it is too large Load Diff
+6358
View File
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
import { i as init } from './init.D98-gwRW.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 };
+18
View File
@@ -0,0 +1,18 @@
import { isMainThread, parentPort } from 'node:worker_threads';
import { i as init } from './init.D98-gwRW.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 };
+375
View File
@@ -0,0 +1,375 @@
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.bRl2_oI_.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 };
+53
View File
@@ -0,0 +1,53 @@
import { createRequire } from 'node:module';
import { pathToFileURL } from 'node:url';
const __require = createRequire(import.meta.url);
let inspector;
let session;
/**
* Enables debugging inside `worker_threads` and `child_process`.
* Should be called as early as possible when worker/process has been set up.
*/
function setupInspect(ctx) {
const config = ctx.config;
const isEnabled = config.inspector.enabled;
if (isEnabled) {
inspector = __require("node:inspector");
if (!(inspector.url() !== void 0)) {
inspector.open(config.inspector.port, config.inspector.host, config.inspector.waitForDebugger);
if (config.inspectBrk) {
const firstTestFile = typeof ctx.files[0] === "string" ? ctx.files[0] : ctx.files[0].filepath;
// Stop at first test file
if (firstTestFile) {
session = new inspector.Session();
session.connect();
session.post("Debugger.enable");
session.post("Debugger.setBreakpointByUrl", {
lineNumber: 0,
url: pathToFileURL(firstTestFile)
});
}
}
}
}
const keepOpen = shouldKeepOpen(config);
return function cleanup() {
if (isEnabled && !keepOpen && inspector) {
inspector.close();
session?.disconnect();
}
};
}
function closeInspector(config) {
const keepOpen = shouldKeepOpen(config);
if (inspector && !keepOpen) {
inspector.close();
session?.disconnect();
}
}
function shouldKeepOpen(config) {
// In watch mode the inspector can persist re-runs if isolation is disabled and a single worker is used
return config.watch && config.isolate === false && config.maxWorkers === 1;
}
export { closeInspector as c, setupInspect as s };
+36
View File
@@ -0,0 +1,36 @@
import { builtinModules } from 'node:module';
// copied from vite
// https://github.com/vitejs/vite/blob/814120f2ad387ca3d1e16c7dd403b04ca4b97f75/packages/vite/src/node/utils.ts#L106
// Supported by Node, Deno, Bun
const NODE_BUILTIN_NAMESPACE = "node:";
// Supported by Deno
const NPM_BUILTIN_NAMESPACE = "npm:";
// Supported by Bun
const BUN_BUILTIN_NAMESPACE = "bun:";
// Some runtimes like Bun injects namespaced modules here, which is not a node builtin
const nodeBuiltins = builtinModules.filter((id) => !id.includes(":"));
const { bun: isBun, deno: isDeno } = process.versions;
// TODO: Use `isBuiltin` from `node:module`, but Deno doesn't support it
function isBuiltin(id) {
if (isDeno && id.startsWith(NPM_BUILTIN_NAMESPACE)) return true;
if (isBun && id.startsWith(BUN_BUILTIN_NAMESPACE)) return true;
return isNodeBuiltin(id);
}
function isNodeBuiltin(id) {
if (id.startsWith(NODE_BUILTIN_NAMESPACE)) return true;
return nodeBuiltins.includes(id);
}
const browserExternalId = "__vite-browser-external";
const browserExternalLength = 24;
function isBrowserExternal(id) {
return id.startsWith(browserExternalId);
}
function toBuiltin(id) {
if (id.startsWith(browserExternalId)) id = id.slice(browserExternalLength);
if (id.startsWith(NPM_BUILTIN_NAMESPACE) || id.startsWith(BUN_BUILTIN_NAMESPACE) || id.startsWith(NODE_BUILTIN_NAMESPACE)) return id;
if (isDeno || isBun) return id;
return `node:${id}`;
}
export { isBrowserExternal as a, isBuiltin as i, toBuiltin as t };
+148
View File
@@ -0,0 +1,148 @@
import module$1, { isBuiltin } from 'node:module';
import { fileURLToPath } from 'node:url';
import { MessageChannel } from 'node:worker_threads';
import { initSyntaxLexers, hoistMocks } from '@vitest/mocker/transforms';
import { cleanUrl } from '@vitest/utils/helpers';
import { p as parse } from './acorn.B2iPLyUM.js';
import MagicString from 'magic-string';
import { resolve } from 'pathe';
import c from 'tinyrainbow';
import { distDir } from '../path.js';
import { t as toBuiltin } from './modules.BJuCwlRJ.js';
import 'node:path';
const NOW_LENGTH = Date.now().toString().length;
const REGEXP_VITEST = new RegExp(`%3Fvitest=\\d{${NOW_LENGTH}}`);
const REGEXP_MOCK_ACTUAL = /\?mock=actual/;
async function setupNodeLoaderHooks(worker) {
if (module$1.setSourceMapsSupport) module$1.setSourceMapsSupport(true);
else if (process.setSourceMapsEnabled) process.setSourceMapsEnabled(true);
if (worker.config.experimental.nodeLoader !== false) await initSyntaxLexers();
if (typeof module$1.registerHooks === "function") module$1.registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier.includes("mock=actual")) {
// url is already resolved by `importActual`
const moduleId = specifier.replace(REGEXP_MOCK_ACTUAL, "");
const builtin = isBuiltin(moduleId);
return {
url: builtin ? toBuiltin(moduleId) : moduleId,
format: builtin ? "builtin" : void 0,
shortCircuit: true
};
}
const isVitest = specifier.includes("%3Fvitest=");
const result = nextResolve(isVitest ? specifier.replace(REGEXP_VITEST, "") : specifier, context);
// avoid tracking /node_modules/ module graph for performance reasons
if (context.parentURL && result.url && !result.url.includes("/node_modules/")) worker.rpc.ensureModuleGraphEntry(result.url, context.parentURL).catch(() => {
// ignore errors
});
// this is require for in-source tests to be invalidated if
// one of the files already imported it in --maxWorkers=1 --no-isolate
if (isVitest) result.url = `${result.url}?vitest=${Date.now()}`;
if (worker.config.experimental.nodeLoader === false || !context.parentURL || result.url.includes(distDir) || context.parentURL?.toString().includes(distDir)) return result;
const mockedResult = getNativeMocker()?.resolveMockedModule(result.url, context.parentURL);
if (mockedResult != null) return mockedResult;
return result;
},
load: worker.config.experimental.nodeLoader === false ? void 0 : createLoadHook()
});
else if (module$1.register) {
if (worker.config.experimental.nodeLoader !== false) console.warn(`${c.bgYellow(" WARNING ")} "module.registerHooks" is not supported in Node.js ${process.version}. This means that some features like module mocking or in-source testing are not supported. Upgrade your Node.js version to at least 22.15 or disable "experimental.nodeLoader" flag manually.\n`);
const { port1, port2 } = new MessageChannel();
port1.unref();
port2.unref();
port1.on("message", (data) => {
if (!data || typeof data !== "object") return;
switch (data.event) {
case "register-module-graph-entry": {
const { url, parentURL } = data;
worker.rpc.ensureModuleGraphEntry(url, parentURL);
return;
}
default: console.error("Unknown message event:", data.event);
}
});
/** Registers {@link file://./../nodejsWorkerLoader.ts} */
module$1.register("#nodejs-worker-loader", {
parentURL: import.meta.url,
data: { port: port2 },
transferList: [port2]
});
} else if (!process.versions.deno && !process.versions.bun) console.warn("\"module.registerHooks\" and \"module.register\" are not supported. Some Vitest features may not work. Please, use Node.js 18.19.0 or higher.");
}
function replaceInSourceMarker(url, source, ms) {
const re = /import\.meta\.vitest/g;
let match;
let overridden = false;
// eslint-disable-next-line no-cond-assign
while (match = re.exec(source)) {
const { index, "0": code } = match;
overridden = true;
// should it support process.vitest for CJS modules?
ms().overwrite(index, index + code.length, "IMPORT_META_TEST()");
}
if (overridden) {
const filename = resolve(fileURLToPath(url));
// appending instead of prepending because functions are hoisted and we don't change the offset
ms().append(`;\nfunction IMPORT_META_TEST() { return typeof __vitest_worker__ !== 'undefined' && __vitest_worker__.filepath === "${filename.replace(/"/g, "\\\"")}" ? __vitest_index__ : undefined; }`);
}
}
const ignoreFormats = new Set([
"addon",
"builtin",
"wasm"
]);
function createLoadHook(_worker) {
return (url, context, nextLoad) => {
const result = url.includes("mock=") && isBuiltin(cleanUrl(url)) ? { format: "commonjs" } : nextLoad(url, context);
if (result.format && ignoreFormats.has(result.format) || url.includes(distDir)) return result;
const mocker = getNativeMocker();
mocker?.checkCircularManualMock(url);
if (url.includes("mock=automock") || url.includes("mock=autospy")) {
const automockedResult = mocker?.loadAutomock(url, result);
if (automockedResult != null) return automockedResult;
return result;
}
if (url.includes("mock=manual")) {
const mockedResult = mocker?.loadManualMock(url, result);
if (mockedResult != null) return mockedResult;
return result;
}
// ignore non-vitest modules for performance reasons,
// vi.hoisted and vi.mock won't work outside of test files or setup files
if (!result.source || !url.includes("vitest=")) return result;
const filename = url.startsWith("file://") ? fileURLToPath(url) : url;
const source = result.source.toString();
const transformedCode = result.format?.includes("typescript") ? module$1.stripTypeScriptTypes(source) : source;
let _ms;
const ms = () => _ms || (_ms = new MagicString(source));
if (source.includes("import.meta.vitest")) replaceInSourceMarker(url, source, ms);
hoistMocks(transformedCode, filename, (code) => parse(code, {
ecmaVersion: "latest",
sourceType: result.format === "module" || result.format === "module-typescript" || result.format === "typescript" ? "module" : "script"
}), {
magicString: ms,
globalThisAccessor: "\"__vitest_mocker__\""
});
let code;
if (_ms) code = `${_ms.toString()}\n//# sourceMappingURL=${genSourceMapUrl(_ms.generateMap({
hires: "boundary",
source: filename
}))}`;
else code = source;
return {
format: result.format,
shortCircuit: true,
source: code
};
};
}
function genSourceMapUrl(map) {
if (typeof map !== "string") map = JSON.stringify(map);
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
}
function getNativeMocker() {
return typeof __vitest_mocker__ !== "undefined" ? __vitest_mocker__ : void 0;
}
export { setupNodeLoaderHooks };
+206
View File
@@ -0,0 +1,206 @@
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.bRl2_oI_.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 };
+36
View File
@@ -0,0 +1,36 @@
import { pathToFileURL } from 'node:url';
import { r as resolveModule } from './index.BCY_7LL2.js';
import { resolve } from 'pathe';
import { ModuleRunner } from 'vite/module-runner';
class NativeModuleRunner extends ModuleRunner {
/**
* @internal
*/
mocker;
constructor(root, mocker) {
super({
hmr: false,
sourcemapInterceptor: false,
transport: { invoke() {
throw new Error("Unexpected `invoke`");
} }
});
this.root = root;
this.mocker = mocker;
if (mocker) Object.defineProperty(globalThis, "__vitest_mocker__", {
configurable: true,
writable: true,
value: mocker
});
}
async import(moduleId) {
const path = resolveModule(moduleId, { paths: [this.root] }) ?? resolve(this.root, moduleId);
// resolveModule doesn't keep the query params, so we need to add them back
let queryParams = "";
if (moduleId.includes("?") && !path.includes("?")) queryParams = moduleId.slice(moduleId.indexOf("?"));
return import(pathToFileURL(path + queryParams).toString());
}
}
export { NativeModuleRunner as N };
+14
View File
@@ -0,0 +1,14 @@
import { NodeSnapshotEnvironment } from '@vitest/snapshot/environment';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
import '@vitest/utils/timers';
class VitestNodeSnapshotEnvironment extends NodeSnapshotEnvironment {
getHeader() {
return `// Vitest Snapshot v${this.getVersion()}, https://vitest.dev/guide/snapshot.html`;
}
resolvePath(filepath) {
return getWorkerState().rpc.resolveSnapshotPath(filepath);
}
}
export { VitestNodeSnapshotEnvironment };
+38
View File
@@ -0,0 +1,38 @@
import { DevEnvironment } from 'vite';
import { V as Vitest, T as TestProject, a as TestProjectConfiguration } from './reporters.d.CtLUhkkA.js';
/**
* Generate a unique cache identifier.
*
* Return `false` to disable caching of the file.
* @experimental
*/
interface CacheKeyIdGenerator {
(context: CacheKeyIdGeneratorContext): string | undefined | null | false;
}
/**
* @experimental
*/
interface CacheKeyIdGeneratorContext {
environment: DevEnvironment;
id: string;
sourceCode: string;
}
interface VitestPluginContext {
vitest: Vitest;
project: TestProject;
injectTestProjects: (config: TestProjectConfiguration | TestProjectConfiguration[]) => Promise<TestProject[]>;
/**
* Define a generator that will be applied before hashing the cache key.
*
* Use this to make sure Vitest generates correct hash. It is a good idea
* to define this function if your plugin can be registered with different options.
*
* This is called only if `experimental.fsModuleCache` is defined.
* @experimental
*/
experimental_defineCacheKeyGenerator: (callback: CacheKeyIdGenerator) => void;
}
export type { CacheKeyIdGenerator as C, VitestPluginContext as V, CacheKeyIdGeneratorContext as a };
File diff suppressed because it is too large Load Diff
+144
View File
@@ -0,0 +1,144 @@
import { getSafeTimers } from '@vitest/utils/timers';
import { c as createBirpc } from './index.Chj8NDwU.js';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
/* Ported from https://github.com/boblauer/MockDate/blob/master/src/mockdate.ts */
/*
The MIT License (MIT)
Copyright (c) 2014 Bob Lauer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const RealDate = Date;
let now = null;
class MockDate extends RealDate {
constructor(y, m, d, h, M, s, ms) {
super();
let date;
switch (arguments.length) {
case 0:
if (now !== null) date = new RealDate(now.valueOf());
else date = new RealDate();
break;
case 1:
date = new RealDate(y);
break;
default:
d = typeof d === "undefined" ? 1 : d;
h = h || 0;
M = M || 0;
s = s || 0;
ms = ms || 0;
date = new RealDate(y, m, d, h, M, s, ms);
break;
}
Object.setPrototypeOf(date, MockDate.prototype);
return date;
}
}
MockDate.UTC = RealDate.UTC;
MockDate.now = function() {
return new MockDate().valueOf();
};
MockDate.parse = function(dateString) {
return RealDate.parse(dateString);
};
MockDate.toString = function() {
return RealDate.toString();
};
function mockDate(date) {
const dateObj = new RealDate(date.valueOf());
if (Number.isNaN(dateObj.getTime())) throw new TypeError(`mockdate: The time set is an invalid date: ${date}`);
// @ts-expect-error global
globalThis.Date = MockDate;
now = dateObj.valueOf();
}
function resetDate() {
globalThis.Date = RealDate;
}
const { get } = Reflect;
function withSafeTimers(fn) {
const { setTimeout, clearTimeout, nextTick, setImmediate, clearImmediate } = getSafeTimers();
const currentSetTimeout = globalThis.setTimeout;
const currentClearTimeout = globalThis.clearTimeout;
const currentSetImmediate = globalThis.setImmediate;
const currentClearImmediate = globalThis.clearImmediate;
const currentNextTick = globalThis.process?.nextTick;
try {
globalThis.setTimeout = setTimeout;
globalThis.clearTimeout = clearTimeout;
if (setImmediate) globalThis.setImmediate = setImmediate;
if (clearImmediate) globalThis.clearImmediate = clearImmediate;
if (globalThis.process && nextTick) globalThis.process.nextTick = nextTick;
return fn();
} finally {
globalThis.setTimeout = currentSetTimeout;
globalThis.clearTimeout = currentClearTimeout;
globalThis.setImmediate = currentSetImmediate;
globalThis.clearImmediate = currentClearImmediate;
if (globalThis.process && nextTick) nextTick(() => {
globalThis.process.nextTick = currentNextTick;
});
}
}
const promises = /* @__PURE__ */ new Set();
async function rpcDone() {
if (!promises.size) return;
const awaitable = Array.from(promises);
return Promise.all(awaitable);
}
const onCancelCallbacks = [];
function onCancel(callback) {
onCancelCallbacks.push(callback);
}
function createRuntimeRpc(options) {
return createSafeRpc(createBirpc({ async onCancel(reason) {
await Promise.all(onCancelCallbacks.map((fn) => fn(reason)));
} }, {
eventNames: ["onCancel"],
timeout: -1,
...options
}));
}
function createSafeRpc(rpc) {
return new Proxy(rpc, { get(target, p, handler) {
// keep $rejectPendingCalls as sync function
if (p === "$rejectPendingCalls") return rpc.$rejectPendingCalls;
const sendCall = get(target, p, handler);
const safeSendCall = (...args) => withSafeTimers(async () => {
const result = sendCall(...args);
promises.add(result);
try {
return await result;
} finally {
promises.delete(result);
}
});
safeSendCall.asEvent = sendCall.asEvent;
return safeSendCall;
} });
}
function rpc() {
const { rpc } = getWorkerState();
return rpc;
}
export { RealDate as R, rpcDone as a, resetDate as b, createRuntimeRpc as c, mockDate as m, onCancel as o, rpc as r };
+30
View File
@@ -0,0 +1,30 @@
import { File, TestArtifact, TaskResultPack, TaskEventPack, CancelReason } from '@vitest/runner';
import { SnapshotResult } from '@vitest/snapshot';
import { FetchFunctionOptions, FetchResult } from 'vite/module-runner';
import { O as OTELCarrier, F as FetchCachedFileSystemResult, R as ResolveFunctionResult, U as UserConsoleLog, A as AsyncLeak, a as AfterSuiteRunMeta } from './traces.d.D2T_R8rx.js';
interface RuntimeRPC {
fetch: (id: string, importer: string | undefined, environment: string, options?: FetchFunctionOptions, otelCarrier?: OTELCarrier) => Promise<FetchResult | FetchCachedFileSystemResult>;
resolve: (id: string, importer: string | undefined, environment: string) => Promise<ResolveFunctionResult | null>;
transform: (id: string) => Promise<{
code?: string;
}>;
onUserConsoleLog: (log: UserConsoleLog) => void;
onUnhandledError: (err: unknown, type: string) => void;
onAsyncLeaks: (leak: AsyncLeak[]) => void;
onQueued: (file: File) => void;
onCollected: (files: File[]) => Promise<void>;
onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void;
onTaskArtifactRecord: <Artifact extends TestArtifact>(testId: string, artifact: Artifact) => Promise<Artifact>;
onTaskUpdate: (pack: TaskResultPack[], events: TaskEventPack[]) => Promise<void>;
onCancel: (reason: CancelReason) => void;
getCountOfFailedTests: () => number;
snapshotSaved: (snapshot: SnapshotResult) => void;
resolveSnapshotPath: (testPath: string) => string;
ensureModuleGraphEntry: (id: string, importer: string) => void;
}
interface RunnerRPC {
onCancel: (reason: CancelReason) => void;
}
export type { RuntimeRPC as R, RunnerRPC as a };
+60
View File
@@ -0,0 +1,60 @@
import { r as resolveCoverageProviderModule } from './coverage.CTzCuANN.js';
import { addSerializer } from '@vitest/snapshot';
import { setSafeTimers } from '@vitest/utils/timers';
import { g as getWorkerState } from './utils.BX5Fg8C4.js';
async function startCoverageInsideWorker(options, loader, runtimeOptions) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.startCoverage?.(runtimeOptions);
return null;
}
async function takeCoverageInsideWorker(options, loader) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.takeCoverage?.({ moduleExecutionInfo: loader.moduleExecutionInfo });
return null;
}
async function stopCoverageInsideWorker(options, loader, runtimeOptions) {
const coverageModule = await resolveCoverageProviderModule(options, loader);
if (coverageModule) return coverageModule.stopCoverage?.(runtimeOptions);
return null;
}
let globalSetup = false;
async function setupCommonEnv(config) {
setupDefines(config);
setupEnv(config.env);
if (globalSetup) return;
globalSetup = true;
setSafeTimers();
if (config.globals) (await import('./globals.Dj1TGiMC.js')).registerApiGlobally();
}
function setupDefines(config) {
for (const key in config.defines) globalThis[key] = config.defines[key];
}
function setupEnv(env) {
const state = getWorkerState();
// same boolean-to-string assignment as VitestPlugin.configResolved
const { PROD, DEV, ...restEnvs } = env;
state.metaEnv.PROD = PROD;
state.metaEnv.DEV = DEV;
for (const key in restEnvs) state.metaEnv[key] = env[key];
}
async function loadDiffConfig(config, moduleRunner) {
if (typeof config.diff === "object") return config.diff;
if (typeof config.diff !== "string") return;
const diffModule = await moduleRunner.import(config.diff);
if (diffModule && typeof diffModule.default === "object" && diffModule.default != null) return diffModule.default;
else throw new Error(`invalid diff config file ${config.diff}. Must have a default export with config object`);
}
async function loadSnapshotSerializers(config, moduleRunner) {
const files = config.snapshotSerializers;
(await Promise.all(files.map(async (file) => {
const mo = await moduleRunner.import(file);
if (!mo || typeof mo.default !== "object" || mo.default === null) throw new Error(`invalid snapshot serializer file ${file}. Must export a default object`);
const config = mo.default;
if (typeof config.test !== "function" || typeof config.serialize !== "function" && typeof config.print !== "function") throw new TypeError(`invalid snapshot serializer in ${file}. Must have a 'test' method along with either a 'serialize' or 'print' method.`);
return config;
}))).forEach((serializer) => addSerializer(serializer));
}
export { loadSnapshotSerializers as a, startCoverageInsideWorker as b, stopCoverageInsideWorker as c, loadDiffConfig as l, setupCommonEnv as s, takeCoverageInsideWorker as t };
+735
View File
@@ -0,0 +1,735 @@
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/src/shared/utils.ts
const postfixRE = /[?#].*$/;
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}(?=[&#]|$)`), "").replace(/\?$/, "");
}
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 };
+10
View File
@@ -0,0 +1,10 @@
import { Test } from '@vitest/runner';
import { a as BenchFunction, c as BenchmarkAPI } from './benchmark.d.DAaHLpsq.js';
import { Options } from 'tinybench';
import '@vitest/runner/utils';
declare function getBenchOptions(key: Test): Options;
declare function getBenchFn(key: Test): BenchFunction;
declare const bench: BenchmarkAPI;
export { getBenchOptions as a, bench as b, getBenchFn as g };
+4430
View File
File diff suppressed because it is too large Load Diff
+217
View File
@@ -0,0 +1,217 @@
class Traces {
/**
* otel stands for OpenTelemetry
*/
#otel = null;
#sdk = null;
#init = null;
#noopSpan = createNoopSpan();
#noopContext = createNoopContext();
#initStartTime = performance.now();
#initEndTime = 0;
#initRecorded = false;
constructor(options) {
if (options.enabled) {
const apiInit = import('@opentelemetry/api').then((api) => {
this.#otel = {
tracer: api.trace.getTracer(options.tracerName || "vitest"),
context: api.context,
propagation: api.propagation,
trace: api.trace,
SpanKind: api.SpanKind,
SpanStatusCode: api.SpanStatusCode
};
}).catch(() => {
throw new Error(`"@opentelemetry/api" is not installed locally. Make sure you have setup OpenTelemetry instrumentation: https://vitest.dev/guide/open-telemetry`);
});
const sdkInit = (options.sdkPath ? import(
/* @vite-ignore */
options.sdkPath
) : Promise.resolve()).catch((cause) => {
throw new Error(`Failed to import custom OpenTelemetry SDK script (${options.sdkPath}): ${cause.message}`);
});
this.#init = Promise.all([sdkInit, apiInit]).then(([sdk]) => {
if (sdk != null) {
if (sdk.default != null && typeof sdk.default === "object" && typeof sdk.default.shutdown === "function") this.#sdk = sdk.default;
else if (options.watchMode !== true && process.env.VITEST_MODE !== "watch") console.warn(`OpenTelemetry instrumentation module (${options.sdkPath}) does not have a default export with a "shutdown" method. Vitest won't be able to ensure that all traces are processed in time. Try running Vitest in watch mode instead.`);
}
}).finally(() => {
this.#initEndTime = performance.now();
this.#init = null;
});
}
}
isEnabled() {
return !!this.#otel;
}
/**
* @internal
*/
async waitInit() {
if (this.#init) await this.#init;
return this;
}
/**
* @internal
*/
recordInitSpan(context) {
if (this.#initRecorded) return;
this.#initRecorded = true;
this.startSpan("vitest.runtime.traces", { startTime: this.#initStartTime }, context).end(this.#initEndTime);
}
/**
* @internal
*/
startContextSpan(name, currentContext) {
if (!this.#otel) return {
span: this.#noopSpan,
context: this.#noopContext
};
const activeContext = currentContext || this.#otel.context.active();
const span = this.#otel.tracer.startSpan(name, {}, activeContext);
return {
span,
context: this.#otel.trace.setSpan(activeContext, span)
};
}
/**
* @internal
*/
getContextFromCarrier(carrier) {
if (!this.#otel) return this.#noopContext;
const activeContext = this.#otel.context.active();
if (!carrier) return activeContext;
return this.#otel.propagation.extract(activeContext, carrier);
}
/**
* @internal
*/
getContextFromEnv(env) {
if (!this.#otel) return this.#noopContext;
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/env-carriers.md
// some tools sets only `TRACEPARENT` but not `TRACESTATE`
const carrier = {};
if (typeof env.TRACEPARENT === "string") carrier.traceparent = env.TRACEPARENT;
if (typeof env.TRACESTATE === "string") carrier.tracestate = env.TRACESTATE;
return this.getContextFromCarrier(carrier);
}
/**
* @internal
*/
getContextCarrier(context) {
if (!this.#otel) return;
const carrier = {};
this.#otel.propagation.inject(context || this.#otel.context.active(), carrier);
return carrier;
}
#callActiveSpan(span, callback) {
const otel = this.#otel;
let result;
try {
result = callback(span);
if (result instanceof Promise) return result.catch((error) => {
span.recordException({
name: error.name,
message: error.message,
stack: error.stack
});
span.setStatus({ code: otel.SpanStatusCode.ERROR });
throw error;
}).finally(() => span.end());
return result;
} catch (error) {
if (error instanceof Error) {
span.recordException({
name: error.name,
message: error.message,
stack: error.stack
});
span.setStatus({ code: otel.SpanStatusCode.ERROR });
}
throw error;
} finally {
// end sync callback
if (!(result instanceof Promise)) span.end();
}
}
/**
* @internal
*/
$(name, optionsOrFn, fn) {
const callback = typeof optionsOrFn === "function" ? optionsOrFn : fn;
if (!this.#otel) return callback(this.#noopSpan);
const otel = this.#otel;
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
const context = options.context;
if (context) return otel.tracer.startActiveSpan(name, options, context, (span) => this.#callActiveSpan(span, callback));
return otel.tracer.startActiveSpan(name, options, (span) => this.#callActiveSpan(span, callback));
}
/**
* @internal
*/
startSpan(name, options, context) {
if (!this.#otel) return this.#noopSpan;
const { tracer } = this.#otel;
return tracer.startSpan(name, options, context);
}
// On browser mode, async context is not automatically propagated,
// so we manually bind the `$` calls to the provided context.
// TODO: this doesn't bind to user land's `@optelemetry/api` calls
/**
* @internal
*/
bind(context) {
if (!this.#otel) return;
const original = this.$.__original ?? this.$;
this.$ = this.#otel.context.bind(context, original);
this.$.__original = original;
}
/**
* @internal
*/
async finish() {
await this.#sdk?.shutdown();
}
/**
* @internal
*/
async flush() {
await this.#sdk?.forceFlush?.();
}
}
function noopSpan() {
return this;
}
function createNoopSpan() {
return {
setAttribute: noopSpan,
setStatus: noopSpan,
addEvent: noopSpan,
addLink: noopSpan,
addLinks: noopSpan,
setAttributes: noopSpan,
updateName: noopSpan,
end: () => {},
isRecording: () => false,
recordException: noopSpan,
spanContext() {
return {
spanId: "",
traceFlags: 0,
traceId: ""
};
}
};
}
function noopContext() {
return this;
}
function createNoopContext() {
return {
getValue: noopContext,
setValue: noopContext,
deleteValue: noopContext
};
}
export { Traces as T };
+60
View File
@@ -0,0 +1,60 @@
interface AfterSuiteRunMeta {
coverage?: unknown;
testFiles: string[];
environment: string;
projectName?: string;
}
interface UserConsoleLog {
content: string;
origin?: string;
browser?: boolean;
type: "stdout" | "stderr";
taskId?: string;
time: number;
size: number;
}
interface ModuleGraphData {
graph: Record<string, string[]>;
externalized: string[];
inlined: string[];
}
interface ProvidedContext {}
interface ResolveFunctionResult {
id: string;
file: string;
url: string;
}
interface FetchCachedFileSystemResult {
cached: true;
tmp: string;
id: string;
file: string | null;
url: string;
invalidate: boolean;
}
type LabelColor = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white";
interface AsyncLeak {
filename: string;
projectName: string;
stack: string;
type: string;
}
interface OTELCarrier {
traceparent?: string;
tracestate?: string;
}
interface TracesOptions {
enabled: boolean;
watchMode?: boolean;
sdkPath?: string;
tracerName?: string;
}
declare class Traces {
#private;
constructor(options: TracesOptions);
isEnabled(): boolean;
}
export { Traces as T };
export type { AsyncLeak as A, FetchCachedFileSystemResult as F, LabelColor as L, ModuleGraphData as M, OTELCarrier as O, ProvidedContext as P, ResolveFunctionResult as R, UserConsoleLog as U, AfterSuiteRunMeta as a };
+189
View File
@@ -0,0 +1,189 @@
import { stripVTControlCharacters } from 'node:util';
import { slash } from '@vitest/utils/helpers';
import { isAbsolute, relative, dirname, basename } from 'pathe';
import c from 'tinyrainbow';
const F_RIGHT = "→";
const F_DOWN = "↓";
const F_DOWN_RIGHT = "↳";
const F_POINTER = "";
const F_DOT = "·";
const F_CHECK = "✓";
const F_CROSS = "×";
const F_LONG_DASH = "⎯";
const F_TODO = "□";
const F_TREE_NODE_MIDDLE = "├──";
const F_TREE_NODE_END = "└──";
const pointer = c.yellow(F_POINTER);
const skipped = c.dim(c.gray(F_DOWN));
const todo = c.dim(c.gray(F_TODO));
const benchmarkPass = c.green(F_DOT);
const testPass = c.green(F_CHECK);
const taskFail = c.red(F_CROSS);
const suiteFail = c.red(F_POINTER);
const pending = c.gray("·");
const separator = c.dim(" > ");
const labelDefaultColors = [
c.bgYellow,
c.bgCyan,
c.bgGreen,
c.bgMagenta
];
function getCols(delta = 0) {
let length = process.stdout?.columns;
if (!length || Number.isNaN(length)) length = 30;
return Math.max(length + delta, 0);
}
function errorBanner(message) {
return divider(c.bold(c.bgRed(` ${message} `)), null, null, c.red);
}
function divider(text, left, right, color) {
const cols = getCols();
const c = color || ((text) => text);
if (text) {
const textLength = stripVTControlCharacters(text).length;
if (left == null && right != null) left = cols - textLength - right;
else {
left = left ?? Math.floor((cols - textLength) / 2);
right = cols - textLength - left;
}
left = Math.max(0, left);
right = Math.max(0, right);
return `${c(F_LONG_DASH.repeat(left))}${text}${c(F_LONG_DASH.repeat(right))}`;
}
return F_LONG_DASH.repeat(cols);
}
function formatTestPath(root, path) {
if (isAbsolute(path)) path = relative(root, path);
const dir = dirname(path);
const ext = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)?.[0] || "";
const base = basename(path, ext);
return slash(c.dim(`${dir}/`) + c.bold(base)) + c.dim(ext);
}
function renderSnapshotSummary(rootDir, snapshots) {
const summary = [];
if (snapshots.added) summary.push(c.bold(c.green(`${snapshots.added} written`)));
if (snapshots.unmatched) summary.push(c.bold(c.red(`${snapshots.unmatched} failed`)));
if (snapshots.updated) summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
else summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
const [head, ...tail] = snapshots.filesRemovedList;
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
tail.forEach((key) => {
summary.push(` ${c.gray(F_DOT)} ${formatTestPath(rootDir, key)}`);
});
}
if (snapshots.unchecked) {
if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.unchecked} removed`)));
else summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
uncheckedFile.keys.forEach((key) => summary.push(` ${c.gray(F_DOT)} ${key}`));
});
}
return summary;
}
function countTestErrors(tasks) {
return tasks.reduce((c, i) => c + (i.result?.errors?.length || 0), 0);
}
function getStateString(tasks, name = "tests", showTotal = true) {
if (tasks.length === 0) return c.dim(`no ${name}`);
const passed = tasks.reduce((acc, i) => {
// Exclude expected failures from passed count
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc;
return i.result?.state === "pass" ? acc + 1 : acc;
}, 0);
const failed = tasks.reduce((acc, i) => i.result?.state === "fail" ? acc + 1 : acc, 0);
const skipped = tasks.reduce((acc, i) => i.mode === "skip" ? acc + 1 : acc, 0);
const todo = tasks.reduce((acc, i) => i.mode === "todo" ? acc + 1 : acc, 0);
const expectedFail = tasks.reduce((acc, i) => {
// Count tests that are marked as .fails and passed (which means they failed as expected)
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc + 1;
return acc;
}, 0);
return [
failed ? c.bold(c.red(`${failed} failed`)) : null,
passed ? c.bold(c.green(`${passed} passed`)) : null,
expectedFail ? c.cyan(`${expectedFail} expected fail`) : null,
skipped ? c.yellow(`${skipped} skipped`) : null,
todo ? c.gray(`${todo} todo`) : null
].filter(Boolean).join(c.dim(" | ")) + (showTotal ? c.gray(` (${tasks.length})`) : "");
}
function getStateSymbol(task) {
if (task.mode === "todo") return todo;
if (task.mode === "skip") return skipped;
if (!task.result) return pending;
if (task.result.state === "run" || task.result.state === "queued") {
if (task.type === "suite") return pointer;
}
if (task.result.state === "pass") return task.meta?.benchmark ? benchmarkPass : testPass;
if (task.result.state === "fail") return task.type === "suite" ? suiteFail : taskFail;
return " ";
}
function formatTimeString(date) {
return date.toTimeString().split(" ")[0];
}
function formatTime(time) {
if (time > 1e3) return `${(time / 1e3).toFixed(2)}s`;
return `${Math.round(time)}ms`;
}
function formatProjectName(project, suffix = " ") {
if (!project?.name) return "";
if (!c.isColorSupported) return `|${project.name}|${suffix}`;
let background = project.color && c[`bg${capitalize(project.color)}`];
if (!background) background = labelDefaultColors[project.name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0) % labelDefaultColors.length];
return c.black(background(` ${project.name} `)) + suffix;
}
function withLabel(color, label, message) {
const bgColor = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;
return `${c.bold(c.black(c[bgColor](` ${label} `)))} ${message ? c[color](message) : ""}`;
}
function padSummaryTitle(str) {
return c.dim(`${str.padStart(11)} `);
}
function truncateString(text, maxLength) {
const plainText = stripVTControlCharacters(text);
if (plainText.length <= maxLength) return text;
return `${plainText.slice(0, maxLength - 1)}`;
}
function capitalize(text) {
return `${text[0].toUpperCase()}${text.slice(1)}`;
}
/**
* Returns the singular or plural form of a word based on the count.
*/
function noun(count, singular, plural) {
if (count === 1) return singular;
return plural;
}
var utils = /*#__PURE__*/Object.freeze({
__proto__: null,
benchmarkPass: benchmarkPass,
countTestErrors: countTestErrors,
divider: divider,
errorBanner: errorBanner,
formatProjectName: formatProjectName,
formatTestPath: formatTestPath,
formatTime: formatTime,
formatTimeString: formatTimeString,
getStateString: getStateString,
getStateSymbol: getStateSymbol,
noun: noun,
padSummaryTitle: padSummaryTitle,
pending: pending,
pointer: pointer,
renderSnapshotSummary: renderSnapshotSummary,
separator: separator,
skipped: skipped,
suiteFail: suiteFail,
taskFail: taskFail,
testPass: testPass,
todo: todo,
truncateString: truncateString,
withLabel: withLabel
});
export { F_POINTER as F, taskFail as a, F_CHECK as b, F_DOWN_RIGHT as c, divider as d, errorBanner as e, formatTimeString as f, formatProjectName as g, getStateSymbol as h, getStateString as i, formatTime as j, countTestErrors as k, F_TREE_NODE_END as l, F_TREE_NODE_MIDDLE as m, noun as n, F_RIGHT as o, padSummaryTitle as p, renderSnapshotSummary as r, separator as s, truncateString as t, utils as u, withLabel as w };
+59
View File
@@ -0,0 +1,59 @@
import { getSafeTimers } from '@vitest/utils/timers';
const NAME_WORKER_STATE = "__vitest_worker__";
class EnvironmentTeardownError extends Error {
name = "EnvironmentTeardownError";
}
function getWorkerState() {
// @ts-expect-error untyped global
const workerState = globalThis[NAME_WORKER_STATE];
if (!workerState) throw new Error("Vitest failed to access its internal state.\n\nOne of the following is possible:\n- \"vitest\" is imported directly without running \"vitest\" command\n- \"vitest\" is imported inside \"globalSetup\" (to fix this, use \"setupFiles\" instead, because \"globalSetup\" runs in a different context)\n- \"vitest\" is imported inside Vite / Vitest config file\n- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues\n");
return workerState;
}
function getSafeWorkerState() {
// @ts-expect-error untyped global
return globalThis[NAME_WORKER_STATE];
}
function provideWorkerState(context, state) {
Object.defineProperty(context, NAME_WORKER_STATE, {
value: state,
configurable: true,
writable: true,
enumerable: false
});
return state;
}
function isChildProcess() {
return typeof process !== "undefined" && !!process.send;
}
function resetModules(modules, resetMocks = false) {
const skipPaths = [
/\/vitest\/dist\//,
/vitest-virtual-\w+\/dist/,
/@vitest\/dist/,
...!resetMocks ? [/^mock:/] : []
];
modules.idToModuleMap.forEach((node, path) => {
if (skipPaths.some((re) => re.test(path))) return;
node.promise = void 0;
node.exports = void 0;
node.evaluated = false;
node.importers.clear();
});
}
function waitNextTick() {
const { setTimeout } = getSafeTimers();
return new Promise((resolve) => setTimeout(resolve, 0));
}
async function waitForImportsToResolve() {
await waitNextTick();
const state = getWorkerState();
const promises = [];
const resolvingCount = state.resolvingModules.size;
for (const [_, mod] of state.evaluatedModules.idToModuleMap) if (mod.promise && !mod.evaluated) promises.push(mod.promise);
if (!promises.length && !resolvingCount) return;
await Promise.allSettled(promises);
await waitForImportsToResolve();
}
export { EnvironmentTeardownError as E, getSafeWorkerState as a, getWorkerState as g, isChildProcess as i, provideWorkerState as p, resetModules as r, waitForImportsToResolve as w };
+785
View File
@@ -0,0 +1,785 @@
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.D98-gwRW.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.bRl2_oI_.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 };
+255
View File
@@ -0,0 +1,255 @@
import { FileSpecification, Task, CancelReason } from '@vitest/runner';
import { EvaluatedModules } from 'vite/module-runner';
import { S as SerializedConfig } from './config.d.A1h_Y6Jt.js';
import { E as Environment } from './environment.d.CrsxCzP1.js';
import { R as RuntimeRPC, a as RunnerRPC } from './rpc.d.B_8sPU0w.js';
//#region src/messages.d.ts
declare const TYPE_REQUEST: "q";
interface RpcRequest {
/**
* Type
*/
t: typeof TYPE_REQUEST;
/**
* ID
*/
i?: string;
/**
* Method
*/
m: string;
/**
* Arguments
*/
a: any[];
/**
* Optional
*/
o?: boolean;
}
//#endregion
//#region src/utils.d.ts
type ArgumentsType<T> = T extends ((...args: infer A) => any) ? A : never;
type ReturnType<T> = T extends ((...args: any) => infer R) ? R : never;
type Thenable<T> = T | PromiseLike<T>;
//#endregion
//#region src/main.d.ts
type PromisifyFn<T> = ReturnType<T> extends Promise<any> ? T : (...args: ArgumentsType<T>) => Promise<Awaited<ReturnType<T>>>;
type BirpcResolver<This> = (this: This, name: string, resolved: (...args: unknown[]) => unknown) => Thenable<((...args: any[]) => any) | undefined>;
interface ChannelOptions {
/**
* Function to post raw message
*/
post: (data: any, ...extras: any[]) => Thenable<any>;
/**
* Listener to receive raw message
*/
on: (fn: (data: any, ...extras: any[]) => void) => Thenable<any>;
/**
* Clear the listener when `$close` is called
*/
off?: (fn: (data: any, ...extras: any[]) => void) => Thenable<any>;
/**
* Custom function to serialize data
*
* by default it passes the data as-is
*/
serialize?: (data: any) => any;
/**
* Custom function to deserialize data
*
* by default it passes the data as-is
*/
deserialize?: (data: any) => any;
/**
* Call the methods with the RPC context or the original functions object
*/
bind?: 'rpc' | 'functions';
/**
* Custom meta data to attached to the RPC instance's `$meta` property
*/
meta?: any;
}
interface EventOptions<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> {
/**
* Names of remote functions that do not need response.
*/
eventNames?: (keyof RemoteFunctions)[];
/**
* Maximum timeout for waiting for response, in milliseconds.
*
* @default 60_000
*/
timeout?: number;
/**
* Whether to proxy the remote functions.
*
* When `proxify` is false, calling the remote function
* with `rpc.$call('method', ...args)` instead of `rpc.method(...args)`
* explicitly is required.
*
* @default true
*/
proxify?: Proxify;
/**
* Custom resolver to resolve function to be called
*
* For advanced use cases only
*/
resolver?: BirpcResolver<BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>>;
/**
* Hook triggered before an event is sent to the remote
*
* @param req - Request parameters
* @param next - Function to continue the request
* @param resolve - Function to resolve the response directly
*/
onRequest?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, req: RpcRequest, next: (req?: RpcRequest) => Promise<any>, resolve: (res: any) => void) => void | Promise<void>;
/**
* Custom error handler for errors occurred in local functions being called
*
* @returns `true` to prevent the error from being thrown
*/
onFunctionError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, error: Error, functionName: string, args: any[]) => boolean | void;
/**
* Custom error handler for errors occurred during serialization or messsaging
*
* @returns `true` to prevent the error from being thrown
*/
onGeneralError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, error: Error, functionName?: string, args?: any[]) => boolean | void;
/**
* Custom error handler for timeouts
*
* @returns `true` to prevent the error from being thrown
*/
onTimeoutError?: (this: BirpcReturn<RemoteFunctions, LocalFunctions, Proxify>, functionName: string, args: any[]) => boolean | void;
}
type BirpcOptions<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> = EventOptions<RemoteFunctions, LocalFunctions, Proxify> & ChannelOptions;
type BirpcFn<T> = PromisifyFn<T> & {
/**
* Send event without asking for response
*/
asEvent: (...args: ArgumentsType<T>) => Promise<void>;
};
interface BirpcReturnBuiltin<RemoteFunctions, LocalFunctions = Record<string, unknown>> {
/**
* Raw functions object
*/
$functions: LocalFunctions;
/**
* Whether the RPC is closed
*/
readonly $closed: boolean;
/**
* Custom meta data attached to the RPC instance
*/
readonly $meta: any;
/**
* Close the RPC connection
*/
$close: (error?: Error) => void;
/**
* Reject pending calls
*/
$rejectPendingCalls: (handler?: PendingCallHandler) => Promise<void>[];
/**
* Call the remote function and wait for the result.
* An alternative to directly calling the function
*/
$call: <K$1 extends keyof RemoteFunctions>(method: K$1, ...args: ArgumentsType<RemoteFunctions[K$1]>) => Promise<Awaited<ReturnType<RemoteFunctions[K$1]>>>;
/**
* Same as `$call`, but returns `undefined` if the function is not defined on the remote side.
*/
$callOptional: <K$1 extends keyof RemoteFunctions>(method: K$1, ...args: ArgumentsType<RemoteFunctions[K$1]>) => Promise<Awaited<ReturnType<RemoteFunctions[K$1]> | undefined>>;
/**
* Send event without asking for response
*/
$callEvent: <K$1 extends keyof RemoteFunctions>(method: K$1, ...args: ArgumentsType<RemoteFunctions[K$1]>) => Promise<void>;
/**
* Call the remote function with the raw options.
*/
$callRaw: (options: {
method: string;
args: unknown[];
event?: boolean;
optional?: boolean;
}) => Promise<Awaited<ReturnType<any>>[]>;
}
type ProxifiedRemoteFunctions<RemoteFunctions extends object = Record<string, unknown>> = { [K in keyof RemoteFunctions]: BirpcFn<RemoteFunctions[K]> };
type BirpcReturn<RemoteFunctions extends object = Record<string, unknown>, LocalFunctions extends object = Record<string, unknown>, Proxify extends boolean = true> = Proxify extends true ? ProxifiedRemoteFunctions<RemoteFunctions> & BirpcReturnBuiltin<RemoteFunctions, LocalFunctions> : BirpcReturnBuiltin<RemoteFunctions, LocalFunctions>;
type PendingCallHandler = (options: Pick<PromiseEntry, 'method' | 'reject'>) => void | Promise<void>;
interface PromiseEntry {
resolve: (arg: any) => void;
reject: (error: any) => void;
method: string;
timeoutId?: ReturnType<typeof setTimeout>;
}
declare const setTimeout: typeof globalThis.setTimeout;
type WorkerRPC = BirpcReturn<RuntimeRPC, RunnerRPC>;
interface ContextTestEnvironment {
name: string;
options: Record<string, any> | null;
}
interface WorkerTestEnvironment {
name: string;
options: Record<string, any> | null;
}
type TestExecutionMethod = "run" | "collect";
interface WorkerExecuteContext {
files: FileSpecification[];
providedContext: Record<string, any>;
invalidates?: string[];
environment: ContextTestEnvironment;
/** Exposed to test runner as `VITEST_WORKER_ID`. Value is unique per each isolated worker. */
workerId: number;
}
interface ContextRPC {
pool: string;
config: SerializedConfig;
projectName: string;
environment: WorkerTestEnvironment;
rpc: WorkerRPC;
files: FileSpecification[];
providedContext: Record<string, any>;
invalidates?: string[];
/** Exposed to test runner as `VITEST_WORKER_ID`. Value is unique per each isolated worker. */
workerId: number;
}
interface WorkerSetupContext {
environment: WorkerTestEnvironment;
pool: string;
config: SerializedConfig;
projectName: string;
rpc: WorkerRPC;
}
interface WorkerGlobalState {
ctx: ContextRPC;
config: SerializedConfig;
rpc: WorkerRPC;
current?: Task;
filepath?: string;
metaEnv: {
[key: string]: any;
BASE_URL: string;
MODE: string;
DEV: boolean;
PROD: boolean;
SSR: boolean;
};
environment: Environment;
evaluatedModules: EvaluatedModules;
resolvingModules: Set<string>;
moduleExecutionInfo: Map<string, any>;
onCancel: (listener: (reason: CancelReason) => unknown) => void;
onCleanup: (listener: () => unknown) => void;
providedContext: Record<string, any>;
durations: {
environment: number;
prepare: number;
};
onFilterStackTrace?: (trace: string) => string;
}
export type { BirpcOptions as B, ContextRPC as C, TestExecutionMethod as T, WorkerGlobalState as W, WorkerSetupContext as a, BirpcReturn as b, ContextTestEnvironment as c, WorkerExecuteContext as d, WorkerTestEnvironment as e };