vitest 4.0.7 → 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.D3GxgUMI.js → base.BgTO2qAg.js} +71 -36
- package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
- package/dist/chunks/{browser.d.-LKfRopd.d.ts → browser.d.DTTM2PTh.d.ts} +1 -1
- package/dist/chunks/{cac.G9DAn-c7.js → cac.CfkWq8Qy.js} +115 -42
- package/dist/chunks/{cli-api.Csks4as1.js → cli-api.BQ-bjcRi.js} +1857 -839
- package/dist/chunks/console.Cf-YriPC.js +146 -0
- package/dist/chunks/{coverage.C2LA1DSL.js → coverage.NVjCOln1.js} +273 -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.DxtanrNO.d.ts → global.d.DVdCfKp5.d.ts} +1 -1
- package/dist/chunks/{globals.BGT_RUsD.js → globals.DOh96BiR.js} +5 -5
- package/dist/chunks/{index.DEPqWSIZ.js → index.BY4-tcno.js} +33 -16
- 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.CWIFvlX5.js → index.DfKyPFVi.js} +159 -54
- package/dist/chunks/{index.CVpyv-Zg.js → index.kotH7DY7.js} +832 -373
- package/dist/chunks/{index.jMQYiEWE.js → index.op2Re5rn.js} +22 -12
- package/dist/chunks/{init-forks.IU-xQ2_X.js → init-forks.2hx7cf78.js} +14 -4
- package/dist/chunks/{init-threads.C_NWvZkU.js → init-threads.Cm4OCIWA.js} +1 -1
- package/dist/chunks/{init.fmH9J833.js → init.DMDG-idf.js} +53 -30
- package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
- package/dist/chunks/{moduleRunner.d.DEkTotCv.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.Cpes8Bt6.d.ts → plugin.d.D4RrtywJ.d.ts} +1 -1
- package/dist/chunks/{reporters.d.CSNcMDxF.d.ts → reporters.d.Da1D1VbQ.d.ts} +6 -5
- package/dist/chunks/{rpc.D38ahn14.js → rpc.BUV7uWKJ.js} +20 -7
- package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.LGjNSzXp.js} +20 -8
- package/dist/chunks/{startModuleRunner.Cn7hCL7D.js → startModuleRunner.BOmUtLIO.js} +206 -83
- package/dist/chunks/{test.B6aJd6T3.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.BL7_zzOr.js → vm.BIkCDs68.js} +177 -71
- package/dist/chunks/{worker.d.D25zYZ7N.d.ts → worker.d.DadbA89M.d.ts} +30 -2
- 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 +1 -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 +1 -1
- package/dist/worker.js +15 -15
- package/dist/workers/forks.js +16 -16
- package/dist/workers/runVmTests.js +41 -22
- package/dist/workers/threads.js +16 -16
- package/dist/workers/vmForks.js +11 -11
- package/dist/workers/vmThreads.js +11 -11
- package/package.json +20 -20
- package/dist/chunks/console.CTJL2nuH.js +0 -115
|
@@ -34,22 +34,39 @@ class VitestTransport {
|
|
|
34
34
|
|
|
35
35
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
36
36
|
function normalizeWindowsPath(input = "") {
|
|
37
|
-
|
|
37
|
+
if (!input) return input;
|
|
38
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
38
39
|
}
|
|
39
|
-
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) {
|
|
40
45
|
if (path.length === 0) return ".";
|
|
41
46
|
path = normalizeWindowsPath(path);
|
|
42
|
-
const isUNCPath = path.match(_UNC_REGEX)
|
|
43
|
-
|
|
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
|
+
}
|
|
44
55
|
if (trailingSeparator) path += "/";
|
|
45
56
|
if (_DRIVE_LETTER_RE.test(path)) path += "/";
|
|
46
|
-
|
|
47
|
-
|
|
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) {
|
|
48
64
|
let path = "";
|
|
49
65
|
for (const seg of segments) {
|
|
50
66
|
if (!seg) continue;
|
|
51
67
|
if (path.length > 0) {
|
|
52
|
-
const pathTrailing = path[path.length - 1] === "/"
|
|
68
|
+
const pathTrailing = path[path.length - 1] === "/";
|
|
69
|
+
const segLeading = seg[0] === "/";
|
|
53
70
|
if (pathTrailing && segLeading) path += seg.slice(1);
|
|
54
71
|
else path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
55
72
|
} else path += seg;
|
|
@@ -57,19 +74,29 @@ const _UNC_REGEX = /^[/\\]{2}/, _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\
|
|
|
57
74
|
return normalize(path);
|
|
58
75
|
};
|
|
59
76
|
function cwd$1() {
|
|
60
|
-
|
|
77
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
78
|
+
return "/";
|
|
61
79
|
}
|
|
62
80
|
const resolve = function(...arguments_) {
|
|
63
81
|
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
64
|
-
let resolvedPath = ""
|
|
82
|
+
let resolvedPath = "";
|
|
83
|
+
let resolvedAbsolute = false;
|
|
65
84
|
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
66
85
|
const path = index >= 0 ? arguments_[index] : cwd$1();
|
|
67
|
-
!path || path.length === 0
|
|
86
|
+
if (!path || path.length === 0) continue;
|
|
87
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
88
|
+
resolvedAbsolute = isAbsolute(path);
|
|
68
89
|
}
|
|
69
|
-
|
|
90
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
91
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
|
|
92
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
70
93
|
};
|
|
71
94
|
function normalizeString(path, allowAboveRoot) {
|
|
72
|
-
let res = ""
|
|
95
|
+
let res = "";
|
|
96
|
+
let lastSegmentLength = 0;
|
|
97
|
+
let lastSlash = -1;
|
|
98
|
+
let dots = 0;
|
|
99
|
+
let char = null;
|
|
73
100
|
for (let index = 0; index <= path.length; ++index) {
|
|
74
101
|
if (index < path.length) char = path[index];
|
|
75
102
|
else if (char === "/") break;
|
|
@@ -80,22 +107,35 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
80
107
|
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
81
108
|
if (res.length > 2) {
|
|
82
109
|
const lastSlashIndex = res.lastIndexOf("/");
|
|
83
|
-
if (lastSlashIndex === -1)
|
|
84
|
-
|
|
85
|
-
|
|
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;
|
|
86
119
|
continue;
|
|
87
120
|
} else if (res.length > 0) {
|
|
88
|
-
res = ""
|
|
121
|
+
res = "";
|
|
122
|
+
lastSegmentLength = 0;
|
|
123
|
+
lastSlash = index;
|
|
124
|
+
dots = 0;
|
|
89
125
|
continue;
|
|
90
126
|
}
|
|
91
127
|
}
|
|
92
|
-
if (allowAboveRoot)
|
|
128
|
+
if (allowAboveRoot) {
|
|
129
|
+
res += res.length > 0 ? "/.." : "..";
|
|
130
|
+
lastSegmentLength = 2;
|
|
131
|
+
}
|
|
93
132
|
} else {
|
|
94
133
|
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
95
134
|
else res = path.slice(lastSlash + 1, index);
|
|
96
135
|
lastSegmentLength = index - lastSlash - 1;
|
|
97
136
|
}
|
|
98
|
-
lastSlash = index
|
|
137
|
+
lastSlash = index;
|
|
138
|
+
dots = 0;
|
|
99
139
|
} else if (char === "." && dots !== -1) ++dots;
|
|
100
140
|
else dots = -1;
|
|
101
141
|
}
|
|
@@ -103,15 +143,18 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
103
143
|
}
|
|
104
144
|
const isAbsolute = function(p) {
|
|
105
145
|
return _IS_ABSOLUTE_RE.test(p);
|
|
106
|
-
}
|
|
146
|
+
};
|
|
147
|
+
const extname = function(p) {
|
|
107
148
|
if (p === "..") return "";
|
|
108
149
|
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
|
|
109
150
|
return match && match[1] || "";
|
|
110
|
-
}
|
|
151
|
+
};
|
|
152
|
+
const dirname = function(p) {
|
|
111
153
|
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
112
154
|
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
|
|
113
155
|
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
114
|
-
}
|
|
156
|
+
};
|
|
157
|
+
const basename = function(p, extension) {
|
|
115
158
|
const segments = normalizeWindowsPath(p).split("/");
|
|
116
159
|
let lastSegment = "";
|
|
117
160
|
for (let i = segments.length - 1; i >= 0; i--) {
|
|
@@ -130,7 +173,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
130
173
|
// it's a node_module alias
|
|
131
174
|
// all mocks should be inside <root>/__mocks__
|
|
132
175
|
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
|
|
133
|
-
const
|
|
176
|
+
const mockFolder = join(root, "__mocks__", dirname(path));
|
|
134
177
|
if (!existsSync(mockFolder)) return null;
|
|
135
178
|
const baseOriginal = basename(path);
|
|
136
179
|
function findFile(mockFolder, baseOriginal) {
|
|
@@ -139,7 +182,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
139
182
|
const path = resolve(mockFolder, file);
|
|
140
183
|
// if the same name, return the file
|
|
141
184
|
if (statSync(path).isFile()) return path;
|
|
142
|
-
{
|
|
185
|
+
else {
|
|
143
186
|
// find folder/index.{js,ts}
|
|
144
187
|
const indexFile = findFile(path, "index");
|
|
145
188
|
if (indexFile) return indexFile;
|
|
@@ -149,7 +192,7 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
149
192
|
}
|
|
150
193
|
return findFile(mockFolder, baseOriginal);
|
|
151
194
|
}
|
|
152
|
-
const
|
|
195
|
+
const fullPath = resolve(dirname(path), "__mocks__", basename(path));
|
|
153
196
|
return existsSync(fullPath) ? fullPath : null;
|
|
154
197
|
}
|
|
155
198
|
const builtins = new Set([
|
|
@@ -167,14 +210,20 @@ const builtins = new Set([
|
|
|
167
210
|
"timers/promises",
|
|
168
211
|
"util/types",
|
|
169
212
|
"wasi"
|
|
170
|
-
])
|
|
213
|
+
]);
|
|
214
|
+
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
215
|
+
const prefixedBuiltins$1 = new Set([
|
|
171
216
|
"node:sea",
|
|
172
217
|
"node:sqlite",
|
|
173
218
|
"node:test",
|
|
174
219
|
"node:test/reporters"
|
|
175
|
-
])
|
|
220
|
+
]);
|
|
221
|
+
const NODE_BUILTIN_NAMESPACE = "node:";
|
|
176
222
|
function isNodeBuiltin(id) {
|
|
177
|
-
|
|
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);
|
|
178
227
|
}
|
|
179
228
|
|
|
180
229
|
const spyModulePath = resolve$1(distDir, "spy.js");
|
|
@@ -186,7 +235,8 @@ class VitestMocker {
|
|
|
186
235
|
registries = /* @__PURE__ */ new Map();
|
|
187
236
|
mockContext = { callstack: null };
|
|
188
237
|
constructor(moduleRunner, options) {
|
|
189
|
-
this.moduleRunner = moduleRunner
|
|
238
|
+
this.moduleRunner = moduleRunner;
|
|
239
|
+
this.options = options;
|
|
190
240
|
const context = this.options.context;
|
|
191
241
|
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
|
|
192
242
|
else this.primitives = {
|
|
@@ -227,7 +277,8 @@ class VitestMocker {
|
|
|
227
277
|
return this.options.moduleDirectories || [];
|
|
228
278
|
}
|
|
229
279
|
async initializeSpyModule() {
|
|
230
|
-
this.spyModule
|
|
280
|
+
if (this.spyModule) return;
|
|
281
|
+
this.spyModule = await this.moduleRunner.import(spyModulePath);
|
|
231
282
|
}
|
|
232
283
|
getMockerRegistry() {
|
|
233
284
|
const suite = this.getSuiteFilepath();
|
|
@@ -238,8 +289,12 @@ class VitestMocker {
|
|
|
238
289
|
this.registries.clear();
|
|
239
290
|
}
|
|
240
291
|
invalidateModuleById(id) {
|
|
241
|
-
const mockId = this.getMockPath(id)
|
|
242
|
-
|
|
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
|
+
}
|
|
243
298
|
}
|
|
244
299
|
isModuleDirectory(path) {
|
|
245
300
|
return this.moduleDirectories.some((dir) => path.includes(dir));
|
|
@@ -248,8 +303,10 @@ class VitestMocker {
|
|
|
248
303
|
return this.options.getCurrentTestFilepath() || "global";
|
|
249
304
|
}
|
|
250
305
|
createError(message, codeFrame) {
|
|
251
|
-
const Error = this.primitives.Error
|
|
252
|
-
|
|
306
|
+
const Error = this.primitives.Error;
|
|
307
|
+
const error = new Error(message);
|
|
308
|
+
Object.assign(error, { codeFrame });
|
|
309
|
+
return error;
|
|
253
310
|
}
|
|
254
311
|
async resolveId(rawId, importer) {
|
|
255
312
|
const result = await this.options.resolveId(rawId, importer);
|
|
@@ -271,26 +328,31 @@ class VitestMocker {
|
|
|
271
328
|
};
|
|
272
329
|
}
|
|
273
330
|
async resolveMocks() {
|
|
274
|
-
|
|
331
|
+
if (!VitestMocker.pendingIds.length) return;
|
|
332
|
+
await Promise.all(VitestMocker.pendingIds.map(async (mock) => {
|
|
275
333
|
const { id, url, external } = await this.resolveId(mock.id, mock.importer);
|
|
276
334
|
if (mock.action === "unmock") this.unmockPath(id);
|
|
277
335
|
if (mock.action === "mock") this.mockPath(mock.id, id, url, external, mock.type, mock.factory);
|
|
278
|
-
}))
|
|
336
|
+
}));
|
|
337
|
+
VitestMocker.pendingIds = [];
|
|
279
338
|
}
|
|
280
339
|
ensureModule(id, url) {
|
|
281
340
|
const node = this.evaluatedModules.ensureModule(id, url);
|
|
282
|
-
|
|
341
|
+
// TODO
|
|
342
|
+
node.meta = {
|
|
283
343
|
id,
|
|
284
344
|
url,
|
|
285
345
|
code: "",
|
|
286
346
|
file: null,
|
|
287
347
|
invalidate: false
|
|
288
|
-
}
|
|
348
|
+
};
|
|
349
|
+
return node;
|
|
289
350
|
}
|
|
290
351
|
async callFunctionMock(id, url, mock) {
|
|
291
352
|
const node = this.ensureModule(id, url);
|
|
292
353
|
if (node.exports) return node.exports;
|
|
293
|
-
const exports = await mock.resolve()
|
|
354
|
+
const exports = await mock.resolve();
|
|
355
|
+
const moduleExports = new Proxy(exports, { get: (target, prop) => {
|
|
294
356
|
const val = target[prop];
|
|
295
357
|
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
|
|
296
358
|
if (prop === "then") {
|
|
@@ -309,7 +371,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
309
371
|
}
|
|
310
372
|
return val;
|
|
311
373
|
} });
|
|
312
|
-
|
|
374
|
+
node.exports = moduleExports;
|
|
375
|
+
return moduleExports;
|
|
313
376
|
}
|
|
314
377
|
// public method to avoid circular dependency
|
|
315
378
|
getMockContext() {
|
|
@@ -335,7 +398,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
335
398
|
}, object, mockExports);
|
|
336
399
|
}
|
|
337
400
|
unmockPath(id) {
|
|
338
|
-
this.getMockerRegistry().deleteById(id)
|
|
401
|
+
this.getMockerRegistry().deleteById(id);
|
|
402
|
+
this.invalidateModuleById(id);
|
|
339
403
|
}
|
|
340
404
|
mockPath(originalId, id, url, external, mockType, factory) {
|
|
341
405
|
const registry = this.getMockerRegistry();
|
|
@@ -350,7 +414,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
350
414
|
this.invalidateModuleById(id);
|
|
351
415
|
}
|
|
352
416
|
async importActual(rawId, importer, callstack) {
|
|
353
|
-
const { url } = await this.resolveId(rawId, importer)
|
|
417
|
+
const { url } = await this.resolveId(rawId, importer);
|
|
418
|
+
const node = await this.moduleRunner.fetchModule(url, importer);
|
|
354
419
|
return await this.moduleRunner.cachedRequest(node.url, node, callstack || [importer], void 0, true);
|
|
355
420
|
}
|
|
356
421
|
async importMock(rawId, importer) {
|
|
@@ -362,7 +427,9 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
362
427
|
else mock = new AutomockedModule(rawId, id, rawId);
|
|
363
428
|
}
|
|
364
429
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
365
|
-
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;
|
|
366
433
|
return this.mockObject(mod, Object.create(Object.prototype), mock.type);
|
|
367
434
|
}
|
|
368
435
|
if (mock.type === "manual") return this.callFunctionMock(id, url, mock);
|
|
@@ -374,19 +441,30 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
374
441
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
375
442
|
const cache = this.evaluatedModules.getModuleById(mockId);
|
|
376
443
|
if (cache && cache.mockedExports) return cache.mockedExports;
|
|
377
|
-
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);
|
|
378
448
|
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
379
449
|
value: "Module",
|
|
380
450
|
configurable: true,
|
|
381
451
|
writable: true
|
|
382
452
|
});
|
|
383
453
|
const node = this.ensureModule(mockId, this.getMockPath(evaluatedNode.url));
|
|
384
|
-
node.meta = evaluatedNode.meta
|
|
454
|
+
node.meta = evaluatedNode.meta;
|
|
455
|
+
node.file = evaluatedNode.file;
|
|
456
|
+
node.mockedExports = exports;
|
|
385
457
|
const mod = await this.moduleRunner.cachedRequest(url, node, callstack, void 0, true);
|
|
386
|
-
|
|
458
|
+
this.mockObject(mod, exports, mock.type);
|
|
459
|
+
return exports;
|
|
387
460
|
}
|
|
388
461
|
if (mock.type === "manual" && !callstack.includes(mockId) && !callstack.includes(url)) try {
|
|
389
|
-
|
|
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);
|
|
390
468
|
} finally {
|
|
391
469
|
this.mockContext.callstack = null;
|
|
392
470
|
const indexMock = callstack.indexOf(mockId);
|
|
@@ -396,7 +474,8 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
396
474
|
}
|
|
397
475
|
async mockedRequest(url, evaluatedNode, callstack) {
|
|
398
476
|
const mock = this.getDependencyMock(evaluatedNode.id);
|
|
399
|
-
if (mock) return
|
|
477
|
+
if (!mock) return;
|
|
478
|
+
return this.requestWithMockedModule(url, evaluatedNode, callstack, mock);
|
|
400
479
|
}
|
|
401
480
|
queueMock(id, importer, factoryOrOptions) {
|
|
402
481
|
const mockType = getMockType(factoryOrOptions);
|
|
@@ -417,7 +496,9 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
417
496
|
}
|
|
418
497
|
}
|
|
419
498
|
function getMockType(factoryOrOptions) {
|
|
420
|
-
|
|
499
|
+
if (!factoryOrOptions) return "automock";
|
|
500
|
+
if (typeof factoryOrOptions === "function") return "manual";
|
|
501
|
+
return factoryOrOptions.spy ? "autospy" : "automock";
|
|
421
502
|
}
|
|
422
503
|
// unique id that is not available as "$bare_import" like "test"
|
|
423
504
|
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
@@ -426,7 +507,8 @@ const prefixedBuiltins = new Set([
|
|
|
426
507
|
"node:sqlite",
|
|
427
508
|
"node:test",
|
|
428
509
|
"node:test/reporters"
|
|
429
|
-
])
|
|
510
|
+
]);
|
|
511
|
+
const isWindows$1 = process.platform === "win32";
|
|
430
512
|
// transform file url to id
|
|
431
513
|
// virtual:custom -> virtual:custom
|
|
432
514
|
// \0custom -> \0custom
|
|
@@ -436,8 +518,9 @@ const prefixedBuiltins = new Set([
|
|
|
436
518
|
// C:\root\id.js -> /id.js
|
|
437
519
|
// TODO: expose this in vite/module-runner
|
|
438
520
|
function normalizeModuleId(file) {
|
|
521
|
+
if (prefixedBuiltins.has(file)) return file;
|
|
439
522
|
// if it's not in the root, keep it as a path, not a URL
|
|
440
|
-
return
|
|
523
|
+
return slash(file).replace(/^\/@fs\//, isWindows$1 ? "" : "/").replace(/^node:/, "").replace(/^\/+/, "/").replace(/^file:\//, "/");
|
|
441
524
|
}
|
|
442
525
|
const windowsSlashRE = /\\/g;
|
|
443
526
|
function slash(p) {
|
|
@@ -446,12 +529,15 @@ function slash(p) {
|
|
|
446
529
|
const multipleSlashRe = /^\/+/;
|
|
447
530
|
// module-runner incorrectly replaces file:///path with `///path`
|
|
448
531
|
function fixLeadingSlashes(id) {
|
|
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,31 +651,42 @@ 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
|
|
|
578
682
|
// Store globals in case tests overwrite them
|
|
579
|
-
const processListeners = process.listeners.bind(process)
|
|
683
|
+
const processListeners = process.listeners.bind(process);
|
|
684
|
+
const processOn = process.on.bind(process);
|
|
685
|
+
const processOff = process.off.bind(process);
|
|
686
|
+
const dispose = [];
|
|
580
687
|
function listenForErrors(state) {
|
|
581
|
-
dispose.forEach((fn) => fn())
|
|
688
|
+
dispose.forEach((fn) => fn());
|
|
689
|
+
dispose.length = 0;
|
|
582
690
|
function catchError(err, type, event) {
|
|
583
691
|
const worker = state();
|
|
584
692
|
// if there is another listener, assume that it's handled by user code
|
|
@@ -586,31 +694,43 @@ function listenForErrors(state) {
|
|
|
586
694
|
if (processListeners(event).length > 1) return;
|
|
587
695
|
const error = serializeValue(err);
|
|
588
696
|
if (typeof error === "object" && error != null) {
|
|
589
|
-
|
|
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 {
|