vitest 4.0.6 → 4.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/chunks/base.BgTO2qAg.js +156 -0
- package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
- package/dist/chunks/{browser.d.ScGeWTou.d.ts → browser.d.DTTM2PTh.d.ts} +1 -1
- package/dist/chunks/{cac.BBqWH4nd.js → cac.CfkWq8Qy.js} +117 -43
- package/dist/chunks/{cli-api.UL3SwFUb.js → cli-api.BQ-bjcRi.js} +1870 -847
- package/dist/chunks/console.Cf-YriPC.js +146 -0
- package/dist/chunks/{coverage.DuCn_Tmx.js → coverage.NVjCOln1.js} +281 -103
- package/dist/chunks/{creator.cqqifzG7.js → creator.fzVyoMf3.js} +74 -30
- package/dist/chunks/{date.-jtEtIeV.js → date.Bq6ZW5rf.js} +17 -6
- package/dist/chunks/{git.BFNcloKD.js → git.Bm2pzPAa.js} +3 -3
- package/dist/chunks/{global.d.DdOkMiVb.d.ts → global.d.DVdCfKp5.d.ts} +1 -1
- package/dist/chunks/{globals.BGT_RUsD.js → globals.DOh96BiR.js} +5 -5
- package/dist/chunks/{resolveSnapshotEnvironment.BZzLjzkh.js → index.BY4-tcno.js} +42 -25
- package/dist/chunks/{index.Bgo3tNWt.js → index.DAL392Ss.js} +40 -15
- package/dist/chunks/{index.RwjEGCQ0.js → index.DIFZf73e.js} +2 -2
- package/dist/chunks/{index.DV0mQLEO.js → index.DfKyPFVi.js} +195 -64
- package/dist/chunks/{index.BL8Hg4Uk.js → index.kotH7DY7.js} +837 -380
- package/dist/chunks/{index.CpdwpN7L.js → index.op2Re5rn.js} +22 -12
- package/dist/chunks/{init-forks.CSGFj9zN.js → init-forks.2hx7cf78.js} +16 -5
- package/dist/chunks/{init-threads.CIJLeFO8.js → init-threads.Cm4OCIWA.js} +3 -2
- package/dist/chunks/{init.DUeOfNO9.js → init.DMDG-idf.js} +124 -54
- package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
- package/dist/chunks/{moduleRunner.d.TP-w6tIQ.d.ts → moduleRunner.d.CzOZ_4wC.d.ts} +1 -1
- package/dist/chunks/{node.BwAWWjHZ.js → node.Ce0vMQM7.js} +1 -1
- package/dist/chunks/{plugin.d.lctzD3Wk.d.ts → plugin.d.D4RrtywJ.d.ts} +1 -1
- package/dist/chunks/{reporters.d.PEs0tXod.d.ts → reporters.d.Da1D1VbQ.d.ts} +19 -9
- package/dist/chunks/rpc.BUV7uWKJ.js +76 -0
- package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.LGjNSzXp.js} +20 -8
- package/dist/chunks/{startModuleRunner.Di-EZqh0.js → startModuleRunner.BOmUtLIO.js} +228 -105
- package/dist/chunks/{test.CnspO-X4.js → test.ClrAtjMv.js} +48 -22
- package/dist/chunks/{utils.CG9h5ccR.js → utils.DvEY5TfP.js} +14 -5
- package/dist/chunks/{vi.BZvkKVkM.js → vi.Bgcdy3bQ.js} +261 -111
- package/dist/chunks/{vm.Co_lR2NL.js → vm.BIkCDs68.js} +177 -70
- package/dist/chunks/{worker.d.B4Hthdvt.d.ts → worker.d.DadbA89M.d.ts} +52 -6
- package/dist/cli.js +2 -2
- package/dist/config.d.ts +5 -5
- package/dist/coverage.d.ts +3 -3
- package/dist/coverage.js +1 -1
- package/dist/environments.js +2 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/module-evaluator.d.ts +2 -2
- package/dist/module-evaluator.js +85 -35
- package/dist/module-runner.js +2 -2
- package/dist/node.d.ts +7 -7
- package/dist/node.js +16 -12
- package/dist/reporters.d.ts +3 -3
- package/dist/reporters.js +2 -2
- package/dist/runners.js +7 -7
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +27 -27
- package/dist/workers/forks.js +34 -31
- package/dist/workers/runVmTests.js +41 -22
- package/dist/workers/threads.js +34 -31
- package/dist/workers/vmForks.js +14 -14
- package/dist/workers/vmThreads.js +14 -14
- package/package.json +20 -20
- package/dist/chunks/base.BAf_bYeI.js +0 -128
- package/dist/chunks/console.CTJL2nuH.js +0 -115
- package/dist/chunks/rpc.Dv1Jt3i2.js +0 -66
|
@@ -11,24 +11,62 @@ import vm from 'node:vm';
|
|
|
11
11
|
import { MockerRegistry, mockObject, RedirectedModule, AutomockedModule } from '@vitest/mocker';
|
|
12
12
|
import * as viteModuleRunner from 'vite/module-runner';
|
|
13
13
|
|
|
14
|
+
class VitestTransport {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
async invoke(event) {
|
|
19
|
+
if (event.type !== "custom") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support Vite HMR events.`) };
|
|
20
|
+
if (event.event !== "vite:invoke") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support ${event.event} event.`) };
|
|
21
|
+
const { name, data } = event.data;
|
|
22
|
+
if (name === "getBuiltins")
|
|
23
|
+
// we return an empty array here to avoid client-side builtin check,
|
|
24
|
+
// as we need builtins to go through `fetchModule`
|
|
25
|
+
return { result: [] };
|
|
26
|
+
if (name !== "fetchModule") return { error: /* @__PURE__ */ new Error(`Unknown method: ${name}. Expected "fetchModule".`) };
|
|
27
|
+
try {
|
|
28
|
+
return { result: await this.options.fetchModule(...data) };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return { error };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
14
35
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
15
36
|
function normalizeWindowsPath(input = "") {
|
|
16
|
-
|
|
37
|
+
if (!input) return input;
|
|
38
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
17
39
|
}
|
|
18
|
-
const _UNC_REGEX = /^[/\\]{2}
|
|
40
|
+
const _UNC_REGEX = /^[/\\]{2}/;
|
|
41
|
+
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
42
|
+
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
43
|
+
const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
|
|
44
|
+
const normalize = function(path) {
|
|
19
45
|
if (path.length === 0) return ".";
|
|
20
46
|
path = normalizeWindowsPath(path);
|
|
21
|
-
const isUNCPath = path.match(_UNC_REGEX)
|
|
22
|
-
|
|
47
|
+
const isUNCPath = path.match(_UNC_REGEX);
|
|
48
|
+
const isPathAbsolute = isAbsolute(path);
|
|
49
|
+
const trailingSeparator = path[path.length - 1] === "/";
|
|
50
|
+
path = normalizeString(path, !isPathAbsolute);
|
|
51
|
+
if (path.length === 0) {
|
|
52
|
+
if (isPathAbsolute) return "/";
|
|
53
|
+
return trailingSeparator ? "./" : ".";
|
|
54
|
+
}
|
|
23
55
|
if (trailingSeparator) path += "/";
|
|
24
56
|
if (_DRIVE_LETTER_RE.test(path)) path += "/";
|
|
25
|
-
|
|
26
|
-
|
|
57
|
+
if (isUNCPath) {
|
|
58
|
+
if (!isPathAbsolute) return `//./${path}`;
|
|
59
|
+
return `//${path}`;
|
|
60
|
+
}
|
|
61
|
+
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
|
|
62
|
+
};
|
|
63
|
+
const join = function(...segments) {
|
|
27
64
|
let path = "";
|
|
28
65
|
for (const seg of segments) {
|
|
29
66
|
if (!seg) continue;
|
|
30
67
|
if (path.length > 0) {
|
|
31
|
-
const pathTrailing = path[path.length - 1] === "/"
|
|
68
|
+
const pathTrailing = path[path.length - 1] === "/";
|
|
69
|
+
const segLeading = seg[0] === "/";
|
|
32
70
|
if (pathTrailing && segLeading) path += seg.slice(1);
|
|
33
71
|
else path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
34
72
|
} else path += seg;
|
|
@@ -36,19 +74,29 @@ const _UNC_REGEX = /^[/\\]{2}/, _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\
|
|
|
36
74
|
return normalize(path);
|
|
37
75
|
};
|
|
38
76
|
function cwd$1() {
|
|
39
|
-
|
|
77
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
78
|
+
return "/";
|
|
40
79
|
}
|
|
41
80
|
const resolve = function(...arguments_) {
|
|
42
81
|
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
43
|
-
let resolvedPath = ""
|
|
82
|
+
let resolvedPath = "";
|
|
83
|
+
let resolvedAbsolute = false;
|
|
44
84
|
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
45
85
|
const path = index >= 0 ? arguments_[index] : cwd$1();
|
|
46
|
-
!path || path.length === 0
|
|
86
|
+
if (!path || path.length === 0) continue;
|
|
87
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
88
|
+
resolvedAbsolute = isAbsolute(path);
|
|
47
89
|
}
|
|
48
|
-
|
|
90
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
91
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
|
|
92
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
49
93
|
};
|
|
50
94
|
function normalizeString(path, allowAboveRoot) {
|
|
51
|
-
let res = ""
|
|
95
|
+
let res = "";
|
|
96
|
+
let lastSegmentLength = 0;
|
|
97
|
+
let lastSlash = -1;
|
|
98
|
+
let dots = 0;
|
|
99
|
+
let char = null;
|
|
52
100
|
for (let index = 0; index <= path.length; ++index) {
|
|
53
101
|
if (index < path.length) char = path[index];
|
|
54
102
|
else if (char === "/") break;
|
|
@@ -59,22 +107,35 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
59
107
|
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
60
108
|
if (res.length > 2) {
|
|
61
109
|
const lastSlashIndex = res.lastIndexOf("/");
|
|
62
|
-
if (lastSlashIndex === -1)
|
|
63
|
-
|
|
64
|
-
|
|
110
|
+
if (lastSlashIndex === -1) {
|
|
111
|
+
res = "";
|
|
112
|
+
lastSegmentLength = 0;
|
|
113
|
+
} else {
|
|
114
|
+
res = res.slice(0, lastSlashIndex);
|
|
115
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
116
|
+
}
|
|
117
|
+
lastSlash = index;
|
|
118
|
+
dots = 0;
|
|
65
119
|
continue;
|
|
66
120
|
} else if (res.length > 0) {
|
|
67
|
-
res = ""
|
|
121
|
+
res = "";
|
|
122
|
+
lastSegmentLength = 0;
|
|
123
|
+
lastSlash = index;
|
|
124
|
+
dots = 0;
|
|
68
125
|
continue;
|
|
69
126
|
}
|
|
70
127
|
}
|
|
71
|
-
if (allowAboveRoot)
|
|
128
|
+
if (allowAboveRoot) {
|
|
129
|
+
res += res.length > 0 ? "/.." : "..";
|
|
130
|
+
lastSegmentLength = 2;
|
|
131
|
+
}
|
|
72
132
|
} else {
|
|
73
133
|
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
74
134
|
else res = path.slice(lastSlash + 1, index);
|
|
75
135
|
lastSegmentLength = index - lastSlash - 1;
|
|
76
136
|
}
|
|
77
|
-
lastSlash = index
|
|
137
|
+
lastSlash = index;
|
|
138
|
+
dots = 0;
|
|
78
139
|
} else if (char === "." && dots !== -1) ++dots;
|
|
79
140
|
else dots = -1;
|
|
80
141
|
}
|
|
@@ -82,15 +143,18 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
82
143
|
}
|
|
83
144
|
const isAbsolute = function(p) {
|
|
84
145
|
return _IS_ABSOLUTE_RE.test(p);
|
|
85
|
-
}
|
|
146
|
+
};
|
|
147
|
+
const extname = function(p) {
|
|
86
148
|
if (p === "..") return "";
|
|
87
149
|
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
|
|
88
150
|
return match && match[1] || "";
|
|
89
|
-
}
|
|
151
|
+
};
|
|
152
|
+
const dirname = function(p) {
|
|
90
153
|
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
91
154
|
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
|
|
92
155
|
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
93
|
-
}
|
|
156
|
+
};
|
|
157
|
+
const basename = function(p, extension) {
|
|
94
158
|
const segments = normalizeWindowsPath(p).split("/");
|
|
95
159
|
let lastSegment = "";
|
|
96
160
|
for (let i = segments.length - 1; i >= 0; i--) {
|
|
@@ -109,7 +173,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
109
173
|
// it's a node_module alias
|
|
110
174
|
// all mocks should be inside <root>/__mocks__
|
|
111
175
|
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
|
|
112
|
-
const
|
|
176
|
+
const mockFolder = join(root, "__mocks__", dirname(path));
|
|
113
177
|
if (!existsSync(mockFolder)) return null;
|
|
114
178
|
const baseOriginal = basename(path);
|
|
115
179
|
function findFile(mockFolder, baseOriginal) {
|
|
@@ -118,7 +182,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
118
182
|
const path = resolve(mockFolder, file);
|
|
119
183
|
// if the same name, return the file
|
|
120
184
|
if (statSync(path).isFile()) return path;
|
|
121
|
-
{
|
|
185
|
+
else {
|
|
122
186
|
// find folder/index.{js,ts}
|
|
123
187
|
const indexFile = findFile(path, "index");
|
|
124
188
|
if (indexFile) return indexFile;
|
|
@@ -128,7 +192,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
128
192
|
}
|
|
129
193
|
return findFile(mockFolder, baseOriginal);
|
|
130
194
|
}
|
|
131
|
-
const
|
|
195
|
+
const fullPath = resolve(dirname(path), "__mocks__", basename(path));
|
|
132
196
|
return existsSync(fullPath) ? fullPath : null;
|
|
133
197
|
}
|
|
134
198
|
const builtins = new Set([
|
|
@@ -146,14 +210,20 @@ const builtins = new Set([
|
|
|
146
210
|
"timers/promises",
|
|
147
211
|
"util/types",
|
|
148
212
|
"wasi"
|
|
149
|
-
])
|
|
213
|
+
]);
|
|
214
|
+
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
215
|
+
const prefixedBuiltins$1 = new Set([
|
|
150
216
|
"node:sea",
|
|
151
217
|
"node:sqlite",
|
|
152
218
|
"node:test",
|
|
153
219
|
"node:test/reporters"
|
|
154
|
-
])
|
|
220
|
+
]);
|
|
221
|
+
const NODE_BUILTIN_NAMESPACE = "node:";
|
|
155
222
|
function isNodeBuiltin(id) {
|
|
156
|
-
|
|
223
|
+
// Added in v18.6.0
|
|
224
|
+
if (nodeModule.isBuiltin) return nodeModule.isBuiltin(id);
|
|
225
|
+
if (prefixedBuiltins$1.has(id)) return true;
|
|
226
|
+
return builtins.has(id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(5) : id);
|
|
157
227
|
}
|
|
158
228
|
|
|
159
229
|
const spyModulePath = resolve$1(distDir, "spy.js");
|
|
@@ -165,7 +235,8 @@ class VitestMocker {
|
|
|
165
235
|
registries = /* @__PURE__ */ new Map();
|
|
166
236
|
mockContext = { callstack: null };
|
|
167
237
|
constructor(moduleRunner, options) {
|
|
168
|
-
this.moduleRunner = moduleRunner
|
|
238
|
+
this.moduleRunner = moduleRunner;
|
|
239
|
+
this.options = options;
|
|
169
240
|
const context = this.options.context;
|
|
170
241
|
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
|
|
171
242
|
else this.primitives = {
|
|
@@ -206,7 +277,8 @@ class VitestMocker {
|
|
|
206
277
|
return this.options.moduleDirectories || [];
|
|
207
278
|
}
|
|
208
279
|
async initializeSpyModule() {
|
|
209
|
-
this.spyModule
|
|
280
|
+
if (this.spyModule) return;
|
|
281
|
+
this.spyModule = await this.moduleRunner.import(spyModulePath);
|
|
210
282
|
}
|
|
211
283
|
getMockerRegistry() {
|
|
212
284
|
const suite = this.getSuiteFilepath();
|
|
@@ -217,8 +289,12 @@ class VitestMocker {
|
|
|
217
289
|
this.registries.clear();
|
|
218
290
|
}
|
|
219
291
|
invalidateModuleById(id) {
|
|
220
|
-
const mockId = this.getMockPath(id)
|
|
221
|
-
|
|
292
|
+
const mockId = this.getMockPath(id);
|
|
293
|
+
const node = this.evaluatedModules.getModuleById(mockId);
|
|
294
|
+
if (node) {
|
|
295
|
+
this.evaluatedModules.invalidateModule(node);
|
|
296
|
+
node.mockedExports = void 0;
|
|
297
|
+
}
|
|
222
298
|
}
|
|
223
299
|
isModuleDirectory(path) {
|
|
224
300
|
return this.moduleDirectories.some((dir) => path.includes(dir));
|
|
@@ -227,8 +303,10 @@ class VitestMocker {
|
|
|
227
303
|
return this.options.getCurrentTestFilepath() || "global";
|
|
228
304
|
}
|
|
229
305
|
createError(message, codeFrame) {
|
|
230
|
-
const Error = this.primitives.Error
|
|
231
|
-
|
|
306
|
+
const Error = this.primitives.Error;
|
|
307
|
+
const error = new Error(message);
|
|
308
|
+
Object.assign(error, { codeFrame });
|
|
309
|
+
return error;
|
|
232
310
|
}
|
|
233
311
|
async resolveId(rawId, importer) {
|
|
234
312
|
const result = await this.options.resolveId(rawId, importer);
|
|
@@ -250,26 +328,31 @@ class VitestMocker {
|
|
|
250
328
|
};
|
|
251
329
|
}
|
|
252
330
|
async resolveMocks() {
|
|
253
|
-
|
|
331
|
+
if (!VitestMocker.pendingIds.length) return;
|
|
332
|
+
await Promise.all(VitestMocker.pendingIds.map(async (mock) => {
|
|
254
333
|
const { id, url, external } = await this.resolveId(mock.id, mock.importer);
|
|
255
334
|
if (mock.action === "unmock") this.unmockPath(id);
|
|
256
335
|
if (mock.action === "mock") this.mockPath(mock.id, id, url, external, mock.type, mock.factory);
|
|
257
|
-
}))
|
|
336
|
+
}));
|
|
337
|
+
VitestMocker.pendingIds = [];
|
|
258
338
|
}
|
|
259
339
|
ensureModule(id, url) {
|
|
260
340
|
const node = this.evaluatedModules.ensureModule(id, url);
|
|
261
|
-
|
|
341
|
+
// TODO
|
|
342
|
+
node.meta = {
|
|
262
343
|
id,
|
|
263
344
|
url,
|
|
264
345
|
code: "",
|
|
265
346
|
file: null,
|
|
266
347
|
invalidate: false
|
|
267
|
-
}
|
|
348
|
+
};
|
|
349
|
+
return node;
|
|
268
350
|
}
|
|
269
351
|
async callFunctionMock(id, url, mock) {
|
|
270
352
|
const node = this.ensureModule(id, url);
|
|
271
353
|
if (node.exports) return node.exports;
|
|
272
|
-
const exports = await mock.resolve()
|
|
354
|
+
const exports = await mock.resolve();
|
|
355
|
+
const moduleExports = new Proxy(exports, { get: (target, prop) => {
|
|
273
356
|
const val = target[prop];
|
|
274
357
|
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
|
|
275
358
|
if (prop === "then") {
|
|
@@ -288,7 +371,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
288
371
|
}
|
|
289
372
|
return val;
|
|
290
373
|
} });
|
|
291
|
-
|
|
374
|
+
node.exports = moduleExports;
|
|
375
|
+
return moduleExports;
|
|
292
376
|
}
|
|
293
377
|
// public method to avoid circular dependency
|
|
294
378
|
getMockContext() {
|
|
@@ -314,7 +398,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
314
398
|
}, object, mockExports);
|
|
315
399
|
}
|
|
316
400
|
unmockPath(id) {
|
|
317
|
-
this.getMockerRegistry().deleteById(id)
|
|
401
|
+
this.getMockerRegistry().deleteById(id);
|
|
402
|
+
this.invalidateModuleById(id);
|
|
318
403
|
}
|
|
319
404
|
mockPath(originalId, id, url, external, mockType, factory) {
|
|
320
405
|
const registry = this.getMockerRegistry();
|
|
@@ -329,7 +414,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
329
414
|
this.invalidateModuleById(id);
|
|
330
415
|
}
|
|
331
416
|
async importActual(rawId, importer, callstack) {
|
|
332
|
-
const { url } = await this.resolveId(rawId, importer)
|
|
417
|
+
const { url } = await this.resolveId(rawId, importer);
|
|
418
|
+
const node = await this.moduleRunner.fetchModule(url, importer);
|
|
333
419
|
return await this.moduleRunner.cachedRequest(node.url, node, callstack || [importer], void 0, true);
|
|
334
420
|
}
|
|
335
421
|
async importMock(rawId, importer) {
|
|
@@ -341,7 +427,9 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
341
427
|
else mock = new AutomockedModule(rawId, id, rawId);
|
|
342
428
|
}
|
|
343
429
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
344
|
-
const node = await this.moduleRunner.fetchModule(url, importer)
|
|
430
|
+
const node = await this.moduleRunner.fetchModule(url, importer);
|
|
431
|
+
const mod = await this.moduleRunner.cachedRequest(url, node, [importer], void 0, true);
|
|
432
|
+
const Object = this.primitives.Object;
|
|
345
433
|
return this.mockObject(mod, Object.create(Object.prototype), mock.type);
|
|
346
434
|
}
|
|
347
435
|
if (mock.type === "manual") return this.callFunctionMock(id, url, mock);
|
|
@@ -353,19 +441,30 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
353
441
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
354
442
|
const cache = this.evaluatedModules.getModuleById(mockId);
|
|
355
443
|
if (cache && cache.mockedExports) return cache.mockedExports;
|
|
356
|
-
const Object = this.primitives.Object
|
|
444
|
+
const Object = this.primitives.Object;
|
|
445
|
+
// we have to define a separate object that will copy all properties into itself
|
|
446
|
+
// and can't just use the same `exports` define automatically by Vite before the evaluator
|
|
447
|
+
const exports = Object.create(null);
|
|
357
448
|
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
358
449
|
value: "Module",
|
|
359
450
|
configurable: true,
|
|
360
451
|
writable: true
|
|
361
452
|
});
|
|
362
453
|
const node = this.ensureModule(mockId, this.getMockPath(evaluatedNode.url));
|
|
363
|
-
node.meta = evaluatedNode.meta
|
|
454
|
+
node.meta = evaluatedNode.meta;
|
|
455
|
+
node.file = evaluatedNode.file;
|
|
456
|
+
node.mockedExports = exports;
|
|
364
457
|
const mod = await this.moduleRunner.cachedRequest(url, node, callstack, void 0, true);
|
|
365
|
-
|
|
458
|
+
this.mockObject(mod, exports, mock.type);
|
|
459
|
+
return exports;
|
|
366
460
|
}
|
|
367
461
|
if (mock.type === "manual" && !callstack.includes(mockId) && !callstack.includes(url)) try {
|
|
368
|
-
|
|
462
|
+
callstack.push(mockId);
|
|
463
|
+
// this will not work if user does Promise.all(import(), import())
|
|
464
|
+
// we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
|
|
465
|
+
// maybe we should improve mock API in the future?
|
|
466
|
+
this.mockContext.callstack = callstack;
|
|
467
|
+
return await this.callFunctionMock(mockId, this.getMockPath(url), mock);
|
|
369
468
|
} finally {
|
|
370
469
|
this.mockContext.callstack = null;
|
|
371
470
|
const indexMock = callstack.indexOf(mockId);
|
|
@@ -375,7 +474,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
375
474
|
}
|
|
376
475
|
async mockedRequest(url, evaluatedNode, callstack) {
|
|
377
476
|
const mock = this.getDependencyMock(evaluatedNode.id);
|
|
378
|
-
if (mock) return
|
|
477
|
+
if (!mock) return;
|
|
478
|
+
return this.requestWithMockedModule(url, evaluatedNode, callstack, mock);
|
|
379
479
|
}
|
|
380
480
|
queueMock(id, importer, factoryOrOptions) {
|
|
381
481
|
const mockType = getMockType(factoryOrOptions);
|
|
@@ -396,7 +496,9 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
396
496
|
}
|
|
397
497
|
}
|
|
398
498
|
function getMockType(factoryOrOptions) {
|
|
399
|
-
|
|
499
|
+
if (!factoryOrOptions) return "automock";
|
|
500
|
+
if (typeof factoryOrOptions === "function") return "manual";
|
|
501
|
+
return factoryOrOptions.spy ? "autospy" : "automock";
|
|
400
502
|
}
|
|
401
503
|
// unique id that is not available as "$bare_import" like "test"
|
|
402
504
|
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
@@ -405,7 +507,8 @@ const prefixedBuiltins = new Set([
|
|
|
405
507
|
"node:sqlite",
|
|
406
508
|
"node:test",
|
|
407
509
|
"node:test/reporters"
|
|
408
|
-
])
|
|
510
|
+
]);
|
|
511
|
+
const isWindows$1 = process.platform === "win32";
|
|
409
512
|
// transform file url to id
|
|
410
513
|
// virtual:custom -> virtual:custom
|
|
411
514
|
// \0custom -> \0custom
|
|
@@ -415,8 +518,9 @@ const prefixedBuiltins = new Set([
|
|
|
415
518
|
// C:\root\id.js -> /id.js
|
|
416
519
|
// TODO: expose this in vite/module-runner
|
|
417
520
|
function normalizeModuleId(file) {
|
|
521
|
+
if (prefixedBuiltins.has(file)) return file;
|
|
418
522
|
// if it's not in the root, keep it as a path, not a URL
|
|
419
|
-
return
|
|
523
|
+
return slash(file).replace(/^\/@fs\//, isWindows$1 ? "" : "/").replace(/^node:/, "").replace(/^\/+/, "/").replace(/^file:\//, "/");
|
|
420
524
|
}
|
|
421
525
|
const windowsSlashRE = /\\/g;
|
|
422
526
|
function slash(p) {
|
|
@@ -425,33 +529,15 @@ function slash(p) {
|
|
|
425
529
|
const multipleSlashRe = /^\/+/;
|
|
426
530
|
// module-runner incorrectly replaces file:///path with `///path`
|
|
427
531
|
function fixLeadingSlashes(id) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
class VitestTransport {
|
|
432
|
-
constructor(options) {
|
|
433
|
-
this.options = options;
|
|
434
|
-
}
|
|
435
|
-
async invoke(event) {
|
|
436
|
-
if (event.type !== "custom") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support Vite HMR events.`) };
|
|
437
|
-
if (event.event !== "vite:invoke") return { error: /* @__PURE__ */ new Error(`Vitest Module Runner doesn't support ${event.event} event.`) };
|
|
438
|
-
const { name, data } = event.data;
|
|
439
|
-
if (name === "getBuiltins")
|
|
440
|
-
// we return an empty array here to avoid client-side builtin check,
|
|
441
|
-
// as we need builtins to go through `fetchModule`
|
|
442
|
-
return { result: [] };
|
|
443
|
-
if (name !== "fetchModule") return { error: /* @__PURE__ */ new Error(`Unknown method: ${name}. Expected "fetchModule".`) };
|
|
444
|
-
try {
|
|
445
|
-
return { result: await this.options.fetchModule(...data) };
|
|
446
|
-
} catch (error) {
|
|
447
|
-
return { error };
|
|
448
|
-
}
|
|
449
|
-
}
|
|
532
|
+
if (id.startsWith("//")) return id.replace(multipleSlashRe, "/");
|
|
533
|
+
return id;
|
|
450
534
|
}
|
|
451
535
|
|
|
452
536
|
const createNodeImportMeta = (modulePath) => {
|
|
453
537
|
if (!viteModuleRunner.createDefaultImportMeta) throw new Error(`createNodeImportMeta is not supported in this version of Vite.`);
|
|
454
|
-
const defaultMeta = viteModuleRunner.createDefaultImportMeta(modulePath)
|
|
538
|
+
const defaultMeta = viteModuleRunner.createDefaultImportMeta(modulePath);
|
|
539
|
+
const href = defaultMeta.url;
|
|
540
|
+
const importMetaResolver = createImportMetaResolver();
|
|
455
541
|
return {
|
|
456
542
|
...defaultMeta,
|
|
457
543
|
main: false,
|
|
@@ -461,21 +547,27 @@ const createNodeImportMeta = (modulePath) => {
|
|
|
461
547
|
};
|
|
462
548
|
};
|
|
463
549
|
function createImportMetaResolver() {
|
|
464
|
-
if (import.meta.resolve) return
|
|
550
|
+
if (!import.meta.resolve) return;
|
|
551
|
+
return (specifier, importer) => import.meta.resolve(specifier, importer);
|
|
465
552
|
}
|
|
466
553
|
// @ts-expect-error overriding private method
|
|
467
554
|
class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
|
|
468
555
|
mocker;
|
|
469
556
|
moduleExecutionInfo;
|
|
470
557
|
constructor(vitestOptions) {
|
|
471
|
-
const options = vitestOptions
|
|
472
|
-
|
|
558
|
+
const options = vitestOptions;
|
|
559
|
+
const transport = new VitestTransport(options.transport);
|
|
560
|
+
const evaluatedModules = options.evaluatedModules;
|
|
561
|
+
super({
|
|
473
562
|
transport,
|
|
474
563
|
hmr: false,
|
|
475
564
|
evaluatedModules,
|
|
476
565
|
sourcemapInterceptor: "prepareStackTrace",
|
|
477
566
|
createImportMeta: vitestOptions.createImportMeta
|
|
478
|
-
}, options.evaluator)
|
|
567
|
+
}, options.evaluator);
|
|
568
|
+
this.vitestOptions = vitestOptions;
|
|
569
|
+
this.moduleExecutionInfo = options.getWorkerState().moduleExecutionInfo;
|
|
570
|
+
this.mocker = options.mocker || new VitestMocker(this, {
|
|
479
571
|
spyModule: options.spyModule,
|
|
480
572
|
context: options.vm?.context,
|
|
481
573
|
resolveId: options.transport.resolveId,
|
|
@@ -488,7 +580,8 @@ class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
|
|
|
488
580
|
getCurrentTestFilepath() {
|
|
489
581
|
return options.getWorkerState().filepath;
|
|
490
582
|
}
|
|
491
|
-
})
|
|
583
|
+
});
|
|
584
|
+
if (options.vm) options.vm.context.__vitest_mocker__ = this.mocker;
|
|
492
585
|
else Object.defineProperty(globalThis, "__vitest_mocker__", {
|
|
493
586
|
configurable: true,
|
|
494
587
|
writable: true,
|
|
@@ -528,7 +621,8 @@ class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
|
|
|
528
621
|
const node = await this.fetchModule(mocked);
|
|
529
622
|
return this._cachedRequest(mocked, node, callstack, metadata);
|
|
530
623
|
}
|
|
531
|
-
|
|
624
|
+
if (mocked != null && typeof mocked === "object") return mocked;
|
|
625
|
+
return this._cachedRequest(url, mod, callstack, metadata);
|
|
532
626
|
}
|
|
533
627
|
/** @internal */
|
|
534
628
|
_invalidateSubTreeById(ids, invalidated = /* @__PURE__ */ new Set()) {
|
|
@@ -544,7 +638,10 @@ class VitestModuleRunner extends viteModuleRunner.ModuleRunner {
|
|
|
544
638
|
}
|
|
545
639
|
}
|
|
546
640
|
|
|
547
|
-
const bareVitestRegexp = /^@?vitest(?:\/|$)
|
|
641
|
+
const bareVitestRegexp = /^@?vitest(?:\/|$)/;
|
|
642
|
+
const normalizedDistDir = normalize$1(distDir);
|
|
643
|
+
const relativeIds = {};
|
|
644
|
+
const externalizeMap = /* @__PURE__ */ new Map();
|
|
548
645
|
// all Vitest imports always need to be externalized
|
|
549
646
|
function getCachedVitestImport(id, state) {
|
|
550
647
|
if (id.startsWith("/@fs/") || id.startsWith("\\@fs\\")) id = id.slice(process.platform === "win32" ? 5 : 4);
|
|
@@ -554,63 +651,86 @@ function getCachedVitestImport(id, state) {
|
|
|
554
651
|
};
|
|
555
652
|
// always externalize Vitest because we import from there before running tests
|
|
556
653
|
// so we already have it cached by Node.js
|
|
557
|
-
const root = state().config.root
|
|
654
|
+
const root = state().config.root;
|
|
655
|
+
const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
|
|
558
656
|
if (id.includes(distDir) || id.includes(normalizedDistDir)) {
|
|
559
657
|
const externalize = id.startsWith("file://") ? id : pathToFileURL(id).toString();
|
|
560
|
-
|
|
658
|
+
externalizeMap.set(id, externalize);
|
|
659
|
+
return {
|
|
561
660
|
externalize,
|
|
562
661
|
type: "module"
|
|
563
662
|
};
|
|
564
663
|
}
|
|
565
664
|
if (relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
|
|
566
|
-
const
|
|
567
|
-
|
|
665
|
+
const externalize = pathToFileURL(join$1(root, id)).toString();
|
|
666
|
+
externalizeMap.set(id, externalize);
|
|
667
|
+
return {
|
|
568
668
|
externalize,
|
|
569
669
|
type: "module"
|
|
570
670
|
};
|
|
571
671
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
672
|
+
if (bareVitestRegexp.test(id)) {
|
|
673
|
+
externalizeMap.set(id, id);
|
|
674
|
+
return {
|
|
675
|
+
externalize: id,
|
|
676
|
+
type: "module"
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
576
680
|
}
|
|
577
681
|
|
|
682
|
+
// Store globals in case tests overwrite them
|
|
683
|
+
const processListeners = process.listeners.bind(process);
|
|
684
|
+
const processOn = process.on.bind(process);
|
|
685
|
+
const processOff = process.off.bind(process);
|
|
578
686
|
const dispose = [];
|
|
579
687
|
function listenForErrors(state) {
|
|
580
|
-
dispose.forEach((fn) => fn())
|
|
688
|
+
dispose.forEach((fn) => fn());
|
|
689
|
+
dispose.length = 0;
|
|
581
690
|
function catchError(err, type, event) {
|
|
582
691
|
const worker = state();
|
|
583
692
|
// if there is another listener, assume that it's handled by user code
|
|
584
693
|
// one is Vitest's own listener
|
|
585
|
-
if (
|
|
694
|
+
if (processListeners(event).length > 1) return;
|
|
586
695
|
const error = serializeValue(err);
|
|
587
696
|
if (typeof error === "object" && error != null) {
|
|
588
|
-
|
|
589
|
-
error.
|
|
697
|
+
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
|
|
698
|
+
if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
|
|
590
699
|
}
|
|
591
700
|
state().rpc.onUnhandledError(error, type);
|
|
592
701
|
}
|
|
593
|
-
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException")
|
|
594
|
-
|
|
595
|
-
|
|
702
|
+
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException");
|
|
703
|
+
const unhandledRejection = (e) => catchError(e, "Unhandled Rejection", "unhandledRejection");
|
|
704
|
+
processOn("uncaughtException", uncaughtException);
|
|
705
|
+
processOn("unhandledRejection", unhandledRejection);
|
|
706
|
+
dispose.push(() => {
|
|
707
|
+
processOff("uncaughtException", uncaughtException);
|
|
708
|
+
processOff("unhandledRejection", unhandledRejection);
|
|
596
709
|
});
|
|
597
710
|
}
|
|
598
711
|
|
|
599
|
-
const { readFileSync } = fs
|
|
712
|
+
const { readFileSync } = fs;
|
|
713
|
+
const browserExternalId = "__vite-browser-external";
|
|
714
|
+
const browserExternalLength = 24;
|
|
600
715
|
const VITEST_VM_CONTEXT_SYMBOL = "__vitest_vm_context__";
|
|
601
|
-
const cwd = process.cwd()
|
|
716
|
+
const cwd = process.cwd();
|
|
717
|
+
const isWindows = process.platform === "win32";
|
|
602
718
|
function startVitestModuleRunner(options) {
|
|
603
|
-
const state = () => globalThis.__vitest_worker__ || options.state
|
|
719
|
+
const state = () => globalThis.__vitest_worker__ || options.state;
|
|
720
|
+
const rpc = () => state().rpc;
|
|
604
721
|
process.exit = (code = process.exitCode || 0) => {
|
|
605
722
|
throw new Error(`process.exit unexpectedly called with "${code}"`);
|
|
606
|
-
}
|
|
723
|
+
};
|
|
724
|
+
listenForErrors(state);
|
|
607
725
|
const environment = () => {
|
|
608
726
|
const environment = state().environment;
|
|
609
727
|
return environment.viteEnvironment || environment.name;
|
|
610
|
-
}
|
|
728
|
+
};
|
|
729
|
+
const vm = options.context && options.externalModulesExecutor ? {
|
|
611
730
|
context: options.context,
|
|
612
731
|
externalModulesExecutor: options.externalModulesExecutor
|
|
613
|
-
} : void 0
|
|
732
|
+
} : void 0;
|
|
733
|
+
const evaluator = options.evaluator || new VitestModuleEvaluator(vm, {
|
|
614
734
|
get moduleExecutionInfo() {
|
|
615
735
|
return state().moduleExecutionInfo;
|
|
616
736
|
},
|
|
@@ -618,7 +738,8 @@ function startVitestModuleRunner(options) {
|
|
|
618
738
|
return state().config.deps.interopDefault;
|
|
619
739
|
},
|
|
620
740
|
getCurrentTestFilepath: () => state().filepath
|
|
621
|
-
})
|
|
741
|
+
});
|
|
742
|
+
const moduleRunner = new VitestModuleRunner({
|
|
622
743
|
spyModule: options.spyModule,
|
|
623
744
|
evaluatedModules: options.evaluatedModules,
|
|
624
745
|
evaluator,
|
|
@@ -654,15 +775,17 @@ function startVitestModuleRunner(options) {
|
|
|
654
775
|
type: "builtin"
|
|
655
776
|
};
|
|
656
777
|
const result = await rpc().fetch(id, importer, environment(), options);
|
|
657
|
-
|
|
778
|
+
if ("cached" in result) return {
|
|
658
779
|
code: readFileSync(result.tmp, "utf-8"),
|
|
659
780
|
...result
|
|
660
|
-
}
|
|
781
|
+
};
|
|
782
|
+
return result;
|
|
661
783
|
} catch (cause) {
|
|
662
784
|
// rethrow vite error if it cannot load the module because it's not resolved
|
|
663
785
|
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 '")) {
|
|
664
786
|
const error = new Error(`Cannot find ${isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from '${importer}'` : ""}`, { cause });
|
|
665
|
-
|
|
787
|
+
error.code = "ERR_MODULE_NOT_FOUND";
|
|
788
|
+
throw error;
|
|
666
789
|
}
|
|
667
790
|
throw cause;
|
|
668
791
|
} finally {
|