vitest 3.2.0-beta.2 → 3.2.0

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.
Files changed (72) hide show
  1. package/LICENSE.md +29 -0
  2. package/dist/browser.d.ts +3 -3
  3. package/dist/browser.js +2 -2
  4. package/dist/chunks/{base.DwtwORaC.js → base.Cg0miDlQ.js} +11 -14
  5. package/dist/chunks/{benchmark.BoF7jW0Q.js → benchmark.CYdenmiT.js} +4 -6
  6. package/dist/chunks/{cac.I9MLYfT-.js → cac.6rXCxFY1.js} +76 -143
  7. package/dist/chunks/{cli-api.d6IK1pnk.js → cli-api.Cej3MBjA.js} +1460 -1344
  8. package/dist/chunks/{config.d.UqE-KR0o.d.ts → config.d.D2ROskhv.d.ts} +2 -0
  9. package/dist/chunks/{console.K1NMVOSc.js → console.CtFJOzRO.js} +25 -45
  10. package/dist/chunks/{constants.BZZyIeIE.js → constants.DnKduX2e.js} +1 -0
  11. package/dist/chunks/{coverage.0iPg4Wrz.js → coverage.DVF1vEu8.js} +4 -12
  12. package/dist/chunks/{coverage.OGU09Jbh.js → coverage.EIiagJJP.js} +578 -993
  13. package/dist/chunks/{creator.DGAdZ4Hj.js → creator.GK6I-cL4.js} +39 -83
  14. package/dist/chunks/date.Bq6ZW5rf.js +73 -0
  15. package/dist/chunks/{defaults.DSxsTG0h.js → defaults.B7q_naMc.js} +2 -1
  16. package/dist/chunks/{env.Dq0hM4Xv.js → env.D4Lgay0q.js} +1 -1
  17. package/dist/chunks/{environment.d.D8YDy2v5.d.ts → environment.d.cL3nLXbE.d.ts} +1 -0
  18. package/dist/chunks/{execute.JlGHLJZT.js → execute.B7h3T_Hc.js} +126 -217
  19. package/dist/chunks/{git.DXfdBEfR.js → git.BVQ8w_Sw.js} +1 -3
  20. package/dist/chunks/{global.d.BPa1eL3O.d.ts → global.d.MAmajcmJ.d.ts} +5 -1
  21. package/dist/chunks/{globals.CpxW8ccg.js → globals.DEHgCU4V.js} +7 -6
  22. package/dist/chunks/{index.CV36oG_L.js → index.BZ0g1JD2.js} +430 -625
  23. package/dist/chunks/{index.DswW_LEs.js → index.BbB8_kAK.js} +25 -24
  24. package/dist/chunks/{index.CmC5OK9L.js → index.CIyJn3t1.js} +38 -82
  25. package/dist/chunks/{index.CfXMNXHg.js → index.CdQS2e2Q.js} +4 -2
  26. package/dist/chunks/{index.DFXFpH3w.js → index.CmSc2RE5.js} +85 -105
  27. package/dist/chunks/index.D3XRDfWc.js +213 -0
  28. package/dist/chunks/{inspector.DbDkSkFn.js → inspector.C914Efll.js} +4 -1
  29. package/dist/chunks/{node.3xsWotC9.js → node.fjCdwEIl.js} +1 -1
  30. package/dist/chunks/{reporters.d.CLC9rhKy.d.ts → reporters.d.C1ogPriE.d.ts} +47 -9
  31. package/dist/chunks/{rpc.D9_013TY.js → rpc.Iovn4oWe.js} +10 -19
  32. package/dist/chunks/{runBaseTests.Dn2vyej_.js → runBaseTests.Dd85QTll.js} +27 -31
  33. package/dist/chunks/{setup-common.CYo3Y0dD.js → setup-common.Dd054P77.js} +16 -42
  34. package/dist/chunks/{typechecker.DnTrplSJ.js → typechecker.DRKU1-1g.js} +163 -186
  35. package/dist/chunks/{utils.BfxieIyZ.js → utils.CAioKnHs.js} +9 -14
  36. package/dist/chunks/{utils.CgTj3MsC.js → utils.XdZDrNZV.js} +6 -13
  37. package/dist/chunks/{vi.BFR5YIgu.js → vi.bdSIJ99Y.js} +137 -263
  38. package/dist/chunks/{vite.d.CBZ3M_ru.d.ts → vite.d.DqE4-hhK.d.ts} +3 -1
  39. package/dist/chunks/{vm.C1HHjtNS.js → vm.BThCzidc.js} +164 -212
  40. package/dist/chunks/{worker.d.D5Xdi-Zr.d.ts → worker.d.DvqK5Vmu.d.ts} +1 -1
  41. package/dist/chunks/{worker.d.CoCI7hzP.d.ts → worker.d.tQu2eJQy.d.ts} +5 -3
  42. package/dist/cli.js +5 -5
  43. package/dist/config.cjs +3 -1
  44. package/dist/config.d.ts +7 -6
  45. package/dist/config.js +3 -3
  46. package/dist/coverage.d.ts +4 -4
  47. package/dist/coverage.js +7 -7
  48. package/dist/environments.d.ts +6 -2
  49. package/dist/environments.js +1 -1
  50. package/dist/execute.d.ts +9 -3
  51. package/dist/execute.js +1 -1
  52. package/dist/index.d.ts +28 -15
  53. package/dist/index.js +5 -5
  54. package/dist/node.d.ts +18 -10
  55. package/dist/node.js +17 -17
  56. package/dist/reporters.d.ts +4 -4
  57. package/dist/reporters.js +4 -4
  58. package/dist/runners.d.ts +6 -3
  59. package/dist/runners.js +59 -80
  60. package/dist/snapshot.js +2 -2
  61. package/dist/suite.js +2 -2
  62. package/dist/worker.js +39 -41
  63. package/dist/workers/forks.js +6 -4
  64. package/dist/workers/runVmTests.js +20 -21
  65. package/dist/workers/threads.js +4 -4
  66. package/dist/workers/vmForks.js +6 -6
  67. package/dist/workers/vmThreads.js +6 -6
  68. package/dist/workers.d.ts +4 -4
  69. package/dist/workers.js +10 -10
  70. package/package.json +21 -19
  71. package/dist/chunks/date.CDOsz-HY.js +0 -53
  72. package/dist/chunks/index.CK1YOQaa.js +0 -143
@@ -13,9 +13,7 @@ import { highlight } from '@vitest/utils';
13
13
 
14
14
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
15
15
  function normalizeWindowsPath(input = "") {
16
- if (!input) {
17
- return input;
18
- }
16
+ if (!input) return input;
19
17
  return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
20
18
  }
21
19
  const _UNC_REGEX = /^[/\\]{2}/;
@@ -23,30 +21,20 @@ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
23
21
  const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
24
22
  const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
25
23
  const normalize = function(path) {
26
- if (path.length === 0) {
27
- return ".";
28
- }
24
+ if (path.length === 0) return ".";
29
25
  path = normalizeWindowsPath(path);
30
26
  const isUNCPath = path.match(_UNC_REGEX);
31
27
  const isPathAbsolute = isAbsolute(path);
32
28
  const trailingSeparator = path[path.length - 1] === "/";
33
29
  path = normalizeString(path, !isPathAbsolute);
34
30
  if (path.length === 0) {
35
- if (isPathAbsolute) {
36
- return "/";
37
- }
31
+ if (isPathAbsolute) return "/";
38
32
  return trailingSeparator ? "./" : ".";
39
33
  }
40
- if (trailingSeparator) {
41
- path += "/";
42
- }
43
- if (_DRIVE_LETTER_RE.test(path)) {
44
- path += "/";
45
- }
34
+ if (trailingSeparator) path += "/";
35
+ if (_DRIVE_LETTER_RE.test(path)) path += "/";
46
36
  if (isUNCPath) {
47
- if (!isPathAbsolute) {
48
- return `//./${path}`;
49
- }
37
+ if (!isPathAbsolute) return `//./${path}`;
50
38
  return `//${path}`;
51
39
  }
52
40
  return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
@@ -54,28 +42,19 @@ const normalize = function(path) {
54
42
  const join = function(...segments) {
55
43
  let path = "";
56
44
  for (const seg of segments) {
57
- if (!seg) {
58
- continue;
59
- }
45
+ if (!seg) continue;
60
46
  if (path.length > 0) {
61
47
  const pathTrailing = path[path.length - 1] === "/";
62
48
  const segLeading = seg[0] === "/";
63
49
  const both = pathTrailing && segLeading;
64
- if (both) {
65
- path += seg.slice(1);
66
- } else {
67
- path += pathTrailing || segLeading ? seg : `/${seg}`;
68
- }
69
- } else {
70
- path += seg;
71
- }
50
+ if (both) path += seg.slice(1);
51
+ else path += pathTrailing || segLeading ? seg : `/${seg}`;
52
+ } else path += seg;
72
53
  }
73
54
  return normalize(path);
74
55
  };
75
56
  function cwd() {
76
- if (typeof process !== "undefined" && typeof process.cwd === "function") {
77
- return process.cwd().replace(/\\/g, "/");
78
- }
57
+ if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
79
58
  return "/";
80
59
  }
81
60
  const resolve = function(...arguments_) {
@@ -84,16 +63,12 @@ const resolve = function(...arguments_) {
84
63
  let resolvedAbsolute = false;
85
64
  for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
86
65
  const path = index >= 0 ? arguments_[index] : cwd();
87
- if (!path || path.length === 0) {
88
- continue;
89
- }
66
+ if (!path || path.length === 0) continue;
90
67
  resolvedPath = `${path}/${resolvedPath}`;
91
68
  resolvedAbsolute = isAbsolute(path);
92
69
  }
93
70
  resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
94
- if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
95
- return `/${resolvedPath}`;
96
- }
71
+ if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
97
72
  return resolvedPath.length > 0 ? resolvedPath : ".";
98
73
  };
99
74
  function normalizeString(path, allowAboveRoot) {
@@ -103,13 +78,9 @@ function normalizeString(path, allowAboveRoot) {
103
78
  let dots = 0;
104
79
  let char = null;
105
80
  for (let index = 0; index <= path.length; ++index) {
106
- if (index < path.length) {
107
- char = path[index];
108
- } else if (char === "/") {
109
- break;
110
- } else {
111
- char = "/";
112
- }
81
+ if (index < path.length) char = path[index];
82
+ else if (char === "/") break;
83
+ else char = "/";
113
84
  if (char === "/") {
114
85
  if (lastSlash === index - 1 || dots === 1);
115
86
  else if (dots === 2) {
@@ -139,20 +110,14 @@ function normalizeString(path, allowAboveRoot) {
139
110
  lastSegmentLength = 2;
140
111
  }
141
112
  } else {
142
- if (res.length > 0) {
143
- res += `/${path.slice(lastSlash + 1, index)}`;
144
- } else {
145
- res = path.slice(lastSlash + 1, index);
146
- }
113
+ if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
114
+ else res = path.slice(lastSlash + 1, index);
147
115
  lastSegmentLength = index - lastSlash - 1;
148
116
  }
149
117
  lastSlash = index;
150
118
  dots = 0;
151
- } else if (char === "." && dots !== -1) {
152
- ++dots;
153
- } else {
154
- dots = -1;
155
- }
119
+ } else if (char === "." && dots !== -1) ++dots;
120
+ else dots = -1;
156
121
  }
157
122
  return res;
158
123
  }
@@ -166,9 +131,7 @@ const extname = function(p) {
166
131
  };
167
132
  const dirname = function(p) {
168
133
  const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
169
- if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
170
- segments[0] += "/";
171
- }
134
+ if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
172
135
  return segments.join("/") || (isAbsolute(p) ? "/" : ".");
173
136
  };
174
137
  const basename = function(p, extension) {
@@ -187,12 +150,12 @@ const basename = function(p, extension) {
187
150
  const { existsSync, readdirSync, statSync } = fs;
188
151
  function findMockRedirect(root, mockPath, external) {
189
152
  const path = external || mockPath;
153
+ // it's a node_module alias
154
+ // all mocks should be inside <root>/__mocks__
190
155
  if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
191
156
  const mockDirname = dirname(path);
192
157
  const mockFolder = join(root, "__mocks__", mockDirname);
193
- if (!existsSync(mockFolder)) {
194
- return null;
195
- }
158
+ if (!existsSync(mockFolder)) return null;
196
159
  const baseOriginal = basename(path);
197
160
  function findFile(mockFolder, baseOriginal) {
198
161
  const files = readdirSync(mockFolder);
@@ -200,13 +163,12 @@ function findMockRedirect(root, mockPath, external) {
200
163
  const baseFile = basename(file, extname(file));
201
164
  if (baseFile === baseOriginal) {
202
165
  const path = resolve(mockFolder, file);
203
- if (statSync(path).isFile()) {
204
- return path;
205
- } else {
166
+ // if the same name, return the file
167
+ if (statSync(path).isFile()) return path;
168
+ else {
169
+ // find folder/index.{js,ts}
206
170
  const indexFile = findFile(path, "index");
207
- if (indexFile) {
208
- return indexFile;
209
- }
171
+ if (indexFile) return indexFile;
210
172
  }
211
173
  }
212
174
  }
@@ -235,6 +197,7 @@ const builtins = new Set([
235
197
  "util/types",
236
198
  "wasi"
237
199
  ]);
200
+ // https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
238
201
  const prefixedBuiltins = new Set([
239
202
  "node:sea",
240
203
  "node:sqlite",
@@ -243,9 +206,7 @@ const prefixedBuiltins = new Set([
243
206
  ]);
244
207
  const NODE_BUILTIN_NAMESPACE = "node:";
245
208
  function isNodeBuiltin(id) {
246
- if (prefixedBuiltins.has(id)) {
247
- return true;
248
- }
209
+ if (prefixedBuiltins.has(id)) return true;
249
210
  return builtins.has(id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(NODE_BUILTIN_NAMESPACE.length) : id);
250
211
  }
251
212
 
@@ -255,24 +216,21 @@ class VitestMocker {
255
216
  spyModule;
256
217
  primitives;
257
218
  filterPublicKeys;
258
- registries = new Map();
219
+ registries = /* @__PURE__ */ new Map();
259
220
  mockContext = { callstack: null };
260
221
  constructor(executor) {
261
222
  this.executor = executor;
262
223
  const context = this.executor.options.context;
263
- if (context) {
264
- this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
265
- } else {
266
- this.primitives = {
267
- Object,
268
- Error,
269
- Function,
270
- RegExp,
271
- Symbol: globalThis.Symbol,
272
- Array,
273
- Map
274
- };
275
- }
224
+ if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
225
+ else this.primitives = {
226
+ Object,
227
+ Error,
228
+ Function,
229
+ RegExp,
230
+ Symbol: globalThis.Symbol,
231
+ Array,
232
+ Map
233
+ };
276
234
  const Symbol = this.primitives.Symbol;
277
235
  this.filterPublicKeys = [
278
236
  "__esModule",
@@ -305,9 +263,7 @@ class VitestMocker {
305
263
  }
306
264
  getMockerRegistry() {
307
265
  const suite = this.getSuiteFilepath();
308
- if (!this.registries.has(suite)) {
309
- this.registries.set(suite, new MockerRegistry());
310
- }
266
+ if (!this.registries.has(suite)) this.registries.set(suite, new MockerRegistry());
311
267
  return this.registries.get(suite);
312
268
  }
313
269
  reset() {
@@ -315,9 +271,7 @@ class VitestMocker {
315
271
  }
316
272
  deleteCachedItem(id) {
317
273
  const mockId = this.getMockPath(id);
318
- if (this.moduleCache.has(mockId)) {
319
- this.moduleCache.delete(mockId);
320
- }
274
+ if (this.moduleCache.has(mockId)) this.moduleCache.delete(mockId);
321
275
  }
322
276
  isModuleDirectory(path) {
323
277
  return this.moduleDirectories.some((dir) => path.includes(dir));
@@ -337,14 +291,15 @@ class VitestMocker {
337
291
  try {
338
292
  [id, fsPath] = await this.executor.originalResolveUrl(rawId, importer);
339
293
  } catch (error) {
294
+ // it's allowed to mock unresolved modules
340
295
  if (error.code === "ERR_MODULE_NOT_FOUND") {
341
296
  const { id: unresolvedId } = error[Symbol.for("vitest.error.not_found.data")];
342
297
  id = unresolvedId;
343
298
  fsPath = unresolvedId;
344
- } else {
345
- throw error;
346
- }
299
+ } else throw error;
347
300
  }
301
+ // external is node_module or unresolved module
302
+ // for example, some people mock "vscode" and don't have it installed
348
303
  const external = !isAbsolute$1(fsPath) || this.isModuleDirectory(fsPath) ? rawId : null;
349
304
  return {
350
305
  id,
@@ -353,37 +308,28 @@ class VitestMocker {
353
308
  };
354
309
  }
355
310
  async resolveMocks() {
356
- if (!VitestMocker.pendingIds.length) {
357
- return;
358
- }
311
+ if (!VitestMocker.pendingIds.length) return;
359
312
  await Promise.all(VitestMocker.pendingIds.map(async (mock) => {
360
313
  const { fsPath, external } = await this.resolvePath(mock.id, mock.importer);
361
- if (mock.action === "unmock") {
362
- this.unmockPath(fsPath);
363
- }
364
- if (mock.action === "mock") {
365
- this.mockPath(mock.id, fsPath, external, mock.type, mock.factory);
366
- }
314
+ if (mock.action === "unmock") this.unmockPath(fsPath);
315
+ if (mock.action === "mock") this.mockPath(mock.id, fsPath, external, mock.type, mock.factory);
367
316
  }));
368
317
  VitestMocker.pendingIds = [];
369
318
  }
370
319
  async callFunctionMock(dep, mock) {
371
320
  const cached = this.moduleCache.get(dep)?.exports;
372
- if (cached) {
373
- return cached;
374
- }
321
+ if (cached) return cached;
375
322
  const exports = await mock.resolve();
376
323
  const moduleExports = new Proxy(exports, { get: (target, prop) => {
377
324
  const val = target[prop];
325
+ // 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
378
326
  if (prop === "then") {
379
- if (target instanceof Promise) {
380
- return target.then.bind(target);
381
- }
327
+ if (target instanceof Promise) return target.then.bind(target);
382
328
  } else if (!(prop in target)) {
383
- if (this.filterPublicKeys.includes(prop)) {
384
- return undefined;
385
- }
386
- throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. ` + "Did you forget to return it from \"vi.mock\"?" + "\nIf you need to partially mock a module, you can use \"importOriginal\" helper inside:\n", highlight(`vi.mock(import("${mock.raw}"), async (importOriginal) => {
329
+ if (this.filterPublicKeys.includes(prop)) return void 0;
330
+ throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
331
+ If you need to partially mock a module, you can use "importOriginal" helper inside:
332
+ `, highlight(`vi.mock(import("${mock.raw}"), async (importOriginal) => {
387
333
  const actual = await importOriginal()
388
334
  return {
389
335
  ...actual,
@@ -396,9 +342,11 @@ class VitestMocker {
396
342
  this.moduleCache.set(dep, { exports: moduleExports });
397
343
  return moduleExports;
398
344
  }
345
+ // public method to avoid circular dependency
399
346
  getMockContext() {
400
347
  return this.mockContext;
401
348
  }
349
+ // path used to store mocked dependencies
402
350
  getMockPath(dep) {
403
351
  return `mock:${dep}`;
404
352
  }
@@ -414,9 +362,7 @@ class VitestMocker {
414
362
  }
415
363
  mockObject(object, mockExports = {}, behavior = "automock") {
416
364
  const spyOn = this.spyModule?.spyOn;
417
- if (!spyOn) {
418
- throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
419
- }
365
+ if (!spyOn) throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
420
366
  return mockObject({
421
367
  globalConstructors: this.primitives,
422
368
  spyOn,
@@ -432,18 +378,14 @@ class VitestMocker {
432
378
  mockPath(originalId, path, external, mockType, factory) {
433
379
  const registry = this.getMockerRegistry();
434
380
  const id = this.normalizePath(path);
435
- if (mockType === "manual") {
436
- registry.register("manual", originalId, id, id, factory);
437
- } else if (mockType === "autospy") {
438
- registry.register("autospy", originalId, id, id);
439
- } else {
381
+ if (mockType === "manual") registry.register("manual", originalId, id, id, factory);
382
+ else if (mockType === "autospy") registry.register("autospy", originalId, id, id);
383
+ else {
440
384
  const redirect = this.resolveMockPath(id, external);
441
- if (redirect) {
442
- registry.register("redirect", originalId, id, id, redirect);
443
- } else {
444
- registry.register("automock", originalId, id, id);
445
- }
385
+ if (redirect) registry.register("redirect", originalId, id, id, redirect);
386
+ else registry.register("automock", originalId, id, id);
446
387
  }
388
+ // every time the mock is registered, we remove the previous one from the cache
447
389
  this.deleteCachedItem(id);
448
390
  }
449
391
  async importActual(rawId, importer, callstack) {
@@ -457,52 +399,44 @@ class VitestMocker {
457
399
  let mock = this.getDependencyMock(normalizedId);
458
400
  if (!mock) {
459
401
  const redirect = this.resolveMockPath(normalizedId, external);
460
- if (redirect) {
461
- mock = new RedirectedModule(rawId, normalizedId, normalizedId, redirect);
462
- } else {
463
- mock = new AutomockedModule(rawId, normalizedId, normalizedId);
464
- }
402
+ if (redirect) mock = new RedirectedModule(rawId, normalizedId, normalizedId, redirect);
403
+ else mock = new AutomockedModule(rawId, normalizedId, normalizedId);
465
404
  }
466
405
  if (mock.type === "automock" || mock.type === "autospy") {
467
406
  const mod = await this.executor.cachedRequest(id, fsPath, [importee]);
468
407
  return this.mockObject(mod, {}, mock.type);
469
408
  }
470
- if (mock.type === "manual") {
471
- return this.callFunctionMock(fsPath, mock);
472
- }
409
+ if (mock.type === "manual") return this.callFunctionMock(fsPath, mock);
473
410
  return this.executor.dependencyRequest(mock.redirect, mock.redirect, [importee]);
474
411
  }
475
412
  async requestWithMock(url, callstack) {
476
413
  const id = this.normalizePath(url);
477
414
  const mock = this.getDependencyMock(id);
478
- if (!mock) {
479
- return;
480
- }
415
+ if (!mock) return;
481
416
  const mockPath = this.getMockPath(id);
482
417
  if (mock.type === "automock" || mock.type === "autospy") {
483
418
  const cache = this.moduleCache.get(mockPath);
484
- if (cache.exports) {
485
- return cache.exports;
486
- }
419
+ if (cache.exports) return cache.exports;
487
420
  const exports = {};
421
+ // Assign the empty exports object early to allow for cycles to work. The object will be filled by mockObject()
488
422
  this.moduleCache.set(mockPath, { exports });
489
423
  const mod = await this.executor.directRequest(url, url, callstack);
490
424
  this.mockObject(mod, exports, mock.type);
491
425
  return exports;
492
426
  }
493
- if (mock.type === "manual" && !callstack.includes(mockPath) && !callstack.includes(url)) {
494
- try {
495
- callstack.push(mockPath);
496
- this.mockContext.callstack = callstack;
497
- return await this.callFunctionMock(mockPath, mock);
498
- } finally {
499
- this.mockContext.callstack = null;
500
- const indexMock = callstack.indexOf(mockPath);
501
- callstack.splice(indexMock, 1);
502
- }
503
- } else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) {
504
- return mock.redirect;
427
+ if (mock.type === "manual" && !callstack.includes(mockPath) && !callstack.includes(url)) try {
428
+ callstack.push(mockPath);
429
+ // this will not work if user does Promise.all(import(), import())
430
+ // we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
431
+ // maybe we should improve mock API in the future?
432
+ this.mockContext.callstack = callstack;
433
+ return await this.callFunctionMock(mockPath, mock);
434
+ } finally {
435
+ this.mockContext.callstack = null;
436
+ const indexMock = callstack.indexOf(mockPath);
437
+ callstack.splice(indexMock, 1);
505
438
  }
439
+ else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) return mock.redirect;
506
440
  }
507
441
  queueMock(id, importer, factoryOrOptions) {
508
442
  const mockType = getMockType(factoryOrOptions);
@@ -510,7 +444,7 @@ class VitestMocker {
510
444
  action: "mock",
511
445
  id,
512
446
  importer,
513
- factory: typeof factoryOrOptions === "function" ? factoryOrOptions : undefined,
447
+ factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
514
448
  type: mockType
515
449
  });
516
450
  }
@@ -523,12 +457,8 @@ class VitestMocker {
523
457
  }
524
458
  }
525
459
  function getMockType(factoryOrOptions) {
526
- if (!factoryOrOptions) {
527
- return "automock";
528
- }
529
- if (typeof factoryOrOptions === "function") {
530
- return "manual";
531
- }
460
+ if (!factoryOrOptions) return "automock";
461
+ if (typeof factoryOrOptions === "function") return "manual";
532
462
  return factoryOrOptions.spy ? "autospy" : "automock";
533
463
  }
534
464
 
@@ -540,7 +470,7 @@ async function createVitestExecutor(options) {
540
470
  await runner.mocker.initializeSpyModule();
541
471
  return runner;
542
472
  }
543
- const externalizeMap = new Map();
473
+ const externalizeMap = /* @__PURE__ */ new Map();
544
474
  const bareVitestRegexp = /^@?vitest(?:\/|$)/;
545
475
  const dispose = [];
546
476
  function listenForErrors(state) {
@@ -549,15 +479,13 @@ function listenForErrors(state) {
549
479
  function catchError(err, type, event) {
550
480
  const worker = state();
551
481
  const listeners = process.listeners(event);
552
- if (listeners.length > 1) {
553
- return;
554
- }
482
+ // if there is another listener, assume that it's handled by user code
483
+ // one is Vitest's own listener
484
+ if (listeners.length > 1) return;
555
485
  const error = processError(err);
556
486
  if (!isPrimitive(error)) {
557
- error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : undefined;
558
- if (worker.filepath) {
559
- error.VITEST_TEST_PATH = worker.filepath;
560
- }
487
+ error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
488
+ if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
561
489
  error.VITEST_AFTER_ENV_TEARDOWN = worker.environmentTeardownRun;
562
490
  }
563
491
  state().rpc.onUnhandledError(error, type);
@@ -573,9 +501,9 @@ function listenForErrors(state) {
573
501
  }
574
502
  const relativeIds = {};
575
503
  function getVitestImport(id, state) {
576
- if (externalizeMap.has(id)) {
577
- return { externalize: externalizeMap.get(id) };
578
- }
504
+ if (externalizeMap.has(id)) return { externalize: externalizeMap.get(id) };
505
+ // always externalize Vitest because we import from there before running tests
506
+ // so we already have it cached by Node.js
579
507
  const root = state().config.root;
580
508
  const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
581
509
  if (id.includes(distDir) || id.includes(normalizedDistDir) || relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
@@ -603,9 +531,7 @@ async function startVitestExecutor(options) {
603
531
  return await createVitestExecutor({
604
532
  async fetchModule(id) {
605
533
  const vitest = getVitestImport(id, state);
606
- if (vitest) {
607
- return vitest;
608
- }
534
+ if (vitest) return vitest;
609
535
  const result = await rpc().fetch(id, getTransformMode());
610
536
  if (result.id && !result.externalize) {
611
537
  const code = readFileSync(result.id, "utf-8");
@@ -638,9 +564,7 @@ async function startVitestExecutor(options) {
638
564
  });
639
565
  }
640
566
  function updateStyle(id, css) {
641
- if (typeof document === "undefined") {
642
- return;
643
- }
567
+ if (typeof document === "undefined") return;
644
568
  const element = document.querySelector(`[data-vite-dev-id="${id}"]`);
645
569
  if (element) {
646
570
  element.textContent = css;
@@ -654,13 +578,9 @@ function updateStyle(id, css) {
654
578
  head?.appendChild(style);
655
579
  }
656
580
  function removeStyle(id) {
657
- if (typeof document === "undefined") {
658
- return;
659
- }
581
+ if (typeof document === "undefined") return;
660
582
  const sheet = document.querySelector(`[data-vite-dev-id="${id}"]`);
661
- if (sheet) {
662
- document.head.removeChild(sheet);
663
- }
583
+ if (sheet) document.head.removeChild(sheet);
664
584
  }
665
585
  function getDefaultRequestStubs(context) {
666
586
  if (!context) {
@@ -705,36 +625,31 @@ class VitestExecutor extends ViteNodeRunner {
705
625
  } else if (options.externalModulesExecutor) {
706
626
  this.primitives = vm.runInContext("({ Object, Reflect, Symbol })", options.context);
707
627
  this.externalModules = options.externalModulesExecutor;
708
- } else {
709
- throw new Error("When context is provided, externalModulesExecutor must be provided as well.");
710
- }
628
+ } else throw new Error("When context is provided, externalModulesExecutor must be provided as well.");
711
629
  }
712
630
  getContextPrimitives() {
713
631
  return this.primitives;
714
632
  }
715
633
  get state() {
634
+ // @ts-expect-error injected untyped global
716
635
  return globalThis.__vitest_worker__ || this.options.state;
717
636
  }
718
637
  get moduleExecutionInfo() {
719
638
  return this.options.moduleExecutionInfo;
720
639
  }
721
640
  shouldResolveId(id, _importee) {
722
- if (isInternalRequest(id) || id.startsWith("data:")) {
723
- return false;
724
- }
641
+ if (isInternalRequest(id) || id.startsWith("data:")) return false;
725
642
  const transformMode = this.state.environment?.transformMode ?? "ssr";
643
+ // do not try and resolve node builtins in Node
644
+ // import('url') returns Node internal even if 'url' package is installed
726
645
  return transformMode === "ssr" ? !isNodeBuiltin$1(id) : !id.startsWith("node:");
727
646
  }
728
647
  async originalResolveUrl(id, importer) {
729
648
  return super.resolveUrl(id, importer);
730
649
  }
731
650
  async resolveUrl(id, importer) {
732
- if (VitestMocker.pendingIds.length) {
733
- await this.mocker.resolveMocks();
734
- }
735
- if (importer && importer.startsWith("mock:")) {
736
- importer = importer.slice(5);
737
- }
651
+ if (VitestMocker.pendingIds.length) await this.mocker.resolveMocks();
652
+ if (importer && importer.startsWith("mock:")) importer = importer.slice(5);
738
653
  try {
739
654
  return await super.resolveUrl(id, importer);
740
655
  } catch (error) {
@@ -742,18 +657,15 @@ class VitestExecutor extends ViteNodeRunner {
742
657
  const { id } = error[Symbol.for("vitest.error.not_found.data")];
743
658
  const path = this.mocker.normalizePath(id);
744
659
  const mock = this.mocker.getDependencyMock(path);
745
- if (mock !== undefined) {
746
- return [id, id];
747
- }
660
+ if (mock !== void 0) return [id, id];
748
661
  }
749
662
  throw error;
750
663
  }
751
664
  }
752
665
  async runModule(context, transformed) {
753
666
  const vmContext = this.options.context;
754
- if (!vmContext || !this.externalModules) {
755
- return super.runModule(context, transformed);
756
- }
667
+ if (!vmContext || !this.externalModules) return super.runModule(context, transformed);
668
+ // add 'use strict' since ESM enables it by default
757
669
  const codeDefinition = `'use strict';async (${Object.keys(context).join(",")})=>{{`;
758
670
  const code = `${codeDefinition}${transformed}\n}}`;
759
671
  const options = {
@@ -761,37 +673,34 @@ class VitestExecutor extends ViteNodeRunner {
761
673
  lineOffset: 0,
762
674
  columnOffset: -codeDefinition.length
763
675
  };
764
- this.options.moduleExecutionInfo?.set(options.filename, { startOffset: codeDefinition.length });
765
- const fn = vm.runInContext(code, vmContext, {
766
- ...options,
767
- importModuleDynamically: this.externalModules.importModuleDynamically
768
- });
769
- await fn(...Object.values(context));
676
+ const finishModuleExecutionInfo = this.startCalculateModuleExecutionInfo(options.filename, codeDefinition.length);
677
+ try {
678
+ const fn = vm.runInContext(code, vmContext, {
679
+ ...options,
680
+ importModuleDynamically: this.externalModules.importModuleDynamically
681
+ });
682
+ await fn(...Object.values(context));
683
+ } finally {
684
+ this.options.moduleExecutionInfo?.set(options.filename, finishModuleExecutionInfo());
685
+ }
770
686
  }
771
687
  async importExternalModule(path) {
772
- if (this.externalModules) {
773
- return this.externalModules.import(path);
774
- }
688
+ if (this.externalModules) return this.externalModules.import(path);
775
689
  return super.importExternalModule(path);
776
690
  }
777
691
  async dependencyRequest(id, fsPath, callstack) {
778
692
  const mocked = await this.mocker.requestWithMock(fsPath, callstack);
779
- if (typeof mocked === "string") {
780
- return super.dependencyRequest(mocked, mocked, callstack);
781
- }
782
- if (mocked && typeof mocked === "object") {
783
- return mocked;
784
- }
693
+ if (typeof mocked === "string") return super.dependencyRequest(mocked, mocked, callstack);
694
+ if (mocked && typeof mocked === "object") return mocked;
785
695
  return super.dependencyRequest(id, fsPath, callstack);
786
696
  }
787
697
  prepareContext(context) {
698
+ // support `import.meta.vitest` for test entry
788
699
  if (this.state.filepath && normalize$1(this.state.filepath) === normalize$1(context.__filename)) {
789
700
  const globalNamespace = this.options.context || globalThis;
790
701
  Object.defineProperty(context.__vite_ssr_import_meta__, "vitest", { get: () => globalNamespace.__vitest_index__ });
791
702
  }
792
- if (this.options.context && this.externalModules) {
793
- context.require = this.externalModules.createRequire(context.__filename);
794
- }
703
+ if (this.options.context && this.externalModules) context.require = this.externalModules.createRequire(context.__filename);
795
704
  return context;
796
705
  }
797
706
  }
@@ -18,9 +18,7 @@ class VitestGit {
18
18
  }
19
19
  async findChangedFiles(options) {
20
20
  const root = await this.getRoot(this.cwd);
21
- if (!root) {
22
- return null;
23
- }
21
+ if (!root) return null;
24
22
  this.root = root;
25
23
  const changedSince = options.changedSince;
26
24
  if (typeof changedSince === "string") {