vitest 4.0.0-beta.8 → 4.0.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 (85) hide show
  1. package/LICENSE.md +86 -102
  2. package/browser/context.d.ts +7 -0
  3. package/browser/context.js +20 -0
  4. package/dist/browser.d.ts +26 -9
  5. package/dist/browser.js +17 -7
  6. package/dist/chunks/base.CtHM3ryk.js +128 -0
  7. package/dist/chunks/{benchmark.UW6Ezvxy.js → benchmark.DHKMYAts.js} +2 -2
  8. package/dist/chunks/{browser.d.DOMmqJQx.d.ts → browser.d.B9iJzZyn.d.ts} +3 -3
  9. package/dist/chunks/{cac.By1HvRIk.js → cac.B99MQg-w.js} +47 -91
  10. package/dist/chunks/{cli-api.C-JHgQgp.js → cli-api.PwHwIMss.js} +1544 -310
  11. package/dist/chunks/{config.d._GBBbReY.d.ts → config.d.u2CUDWwS.d.ts} +6 -19
  12. package/dist/chunks/{console.B0quX7yH.js → console.CTJL2nuH.js} +4 -6
  13. package/dist/chunks/{coverage.DarITf6U.js → coverage.FU3w4IrQ.js} +128 -1142
  14. package/dist/chunks/{creator.KEg6n5IC.js → creator.DucAaYBz.js} +10 -37
  15. package/dist/chunks/{defaults.CXFFjsi8.js → defaults.BOqNVLsY.js} +0 -1
  16. package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
  17. package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
  18. package/dist/chunks/{global.d.K6uBQHzY.d.ts → global.d.BgJSTpgQ.d.ts} +2 -17
  19. package/dist/chunks/{globals.lgsmH00r.js → globals.BGT_RUsD.js} +12 -9
  20. package/dist/chunks/{index.BuwjkI-q.js → index.BdSLhLDZ.js} +3 -3
  21. package/dist/chunks/{index.DfviD7lX.js → index.CbWINfS7.js} +49 -21
  22. package/dist/chunks/{index.AzwzFtyi.js → index.CcRZ6fUh.js} +1493 -114
  23. package/dist/chunks/{index.X0nbfr6-.js → index.Dc3xnDvT.js} +48 -289
  24. package/dist/chunks/{index.AR8aAkCC.js → index.RwjEGCQ0.js} +7 -8
  25. package/dist/chunks/init-forks.DSafeltJ.js +54 -0
  26. package/dist/chunks/init-threads.SUtZ-067.js +17 -0
  27. package/dist/chunks/init.B2EESLQM.js +213 -0
  28. package/dist/chunks/{inspector.CvQD-Nie.js → inspector.DLZxSeU3.js} +2 -6
  29. package/dist/chunks/{moduleRunner.d.CX4DuqOx.d.ts → moduleRunner.d.YtNsMIoJ.d.ts} +12 -14
  30. package/dist/chunks/{node.BOqcT2jW.js → node.BwAWWjHZ.js} +3 -4
  31. package/dist/chunks/{plugin.d.CHe6slQs.d.ts → plugin.d.DQU1R5px.d.ts} +1 -1
  32. package/dist/chunks/{reporters.d.37tJQ2uV.d.ts → reporters.d.BMKt7f6I.d.ts} +1066 -1030
  33. package/dist/chunks/{index.CsFXYRkW.js → resolveSnapshotEnvironment.DJJKMKxb.js} +18 -24
  34. package/dist/chunks/{rpc.RpPylpp0.js → rpc.cD77ENhU.js} +13 -14
  35. package/dist/chunks/{setup-common.hLGRxhC8.js → setup-common.DR1sucx6.js} +8 -8
  36. package/dist/chunks/{startModuleRunner.C8TW8zTN.js → startModuleRunner.C2tTvmF9.js} +131 -110
  37. package/dist/chunks/test.C3RPt8JR.js +214 -0
  38. package/dist/chunks/{utils.C7__0Iv5.js → utils.CG9h5ccR.js} +3 -15
  39. package/dist/chunks/{vi.BfdOiD4j.js → vi.BZvkKVkM.js} +73 -176
  40. package/dist/chunks/{vm.BHBje7cC.js → vm.DBeOXrP9.js} +29 -33
  41. package/dist/chunks/{worker.d.DYlqbejz.d.ts → worker.d.BFk-vvBU.d.ts} +42 -6
  42. package/dist/cli.js +12 -11
  43. package/dist/config.cjs +0 -1
  44. package/dist/config.d.ts +12 -14
  45. package/dist/config.js +1 -1
  46. package/dist/coverage.d.ts +8 -7
  47. package/dist/coverage.js +3 -14
  48. package/dist/environments.d.ts +3 -6
  49. package/dist/environments.js +1 -1
  50. package/dist/index.d.ts +24 -30
  51. package/dist/index.js +12 -11
  52. package/dist/module-evaluator.d.ts +6 -4
  53. package/dist/module-evaluator.js +14 -16
  54. package/dist/module-runner.js +5 -5
  55. package/dist/node.d.ts +83 -27
  56. package/dist/node.js +23 -20
  57. package/dist/reporters.d.ts +11 -10
  58. package/dist/reporters.js +12 -11
  59. package/dist/runners.d.ts +1 -1
  60. package/dist/runners.js +14 -216
  61. package/dist/snapshot.js +3 -3
  62. package/dist/suite.js +4 -3
  63. package/dist/worker.d.ts +26 -0
  64. package/dist/worker.js +45 -166
  65. package/dist/workers/forks.js +41 -35
  66. package/dist/workers/runVmTests.js +25 -22
  67. package/dist/workers/threads.js +41 -23
  68. package/dist/workers/vmForks.js +26 -39
  69. package/dist/workers/vmThreads.js +26 -29
  70. package/package.json +48 -35
  71. package/worker.d.ts +1 -0
  72. package/browser.d.ts +0 -1
  73. package/dist/chunks/base.BXI97p6t.js +0 -39
  74. package/dist/chunks/environment.d.2fYMoz3o.d.ts +0 -66
  75. package/dist/chunks/moduleTransport.I-bgQy0S.js +0 -19
  76. package/dist/chunks/resolver.Bx6lE0iq.js +0 -119
  77. package/dist/chunks/runBaseTests.D6sfuWBM.js +0 -99
  78. package/dist/chunks/typechecker.DSo_maXz.js +0 -762
  79. package/dist/chunks/utils.C2YI6McM.js +0 -52
  80. package/dist/chunks/worker.d.BKu8cnnX.d.ts +0 -8
  81. package/dist/workers.d.ts +0 -38
  82. package/dist/workers.js +0 -31
  83. package/execute.d.ts +0 -1
  84. package/utils.d.ts +0 -1
  85. package/workers.d.ts +0 -1
@@ -1,18 +1,19 @@
1
- import fs, { promises, existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
2
- import { relative, resolve, dirname, extname, normalize, join, basename, isAbsolute } from 'pathe';
1
+ import fs, { promises as promises$1, existsSync, mkdirSync, readFileSync, statSync, readdirSync, writeFileSync } from 'node:fs';
2
+ import { relative, resolve, dirname, join, extname, normalize, basename, isAbsolute } from 'pathe';
3
3
  import { C as CoverageProviderMap } from './coverage.D_JHT54q.js';
4
4
  import path, { resolve as resolve$1 } from 'node:path';
5
- import { noop, createDefer, slash, highlight, toArray, cleanUrl, deepMerge, KNOWN_ASSET_RE, nanoid, deepClone, isPrimitive, notNullish } from '@vitest/utils';
6
- import { f as findUp, p as prompt } from './index.X0nbfr6-.js';
5
+ import { noop, createDefer, slash, isExternalUrl, unwrapId, nanoid, withTrailingSlash, cleanUrl, wrapId, toArray, deepMerge, deepClone, isPrimitive, notNullish } from '@vitest/utils/helpers';
6
+ import { a as any, p as prompt } from './index.Dc3xnDvT.js';
7
7
  import * as vite from 'vite';
8
- import { parseAst, searchForWorkspaceRoot, version, mergeConfig, createServer } from 'vite';
8
+ import { parseAst, fetchModule, version, searchForWorkspaceRoot, mergeConfig, createServer } from 'vite';
9
9
  import { A as API_PATH, c as configFiles, d as defaultBrowserPort, a as defaultPort } from './constants.D_Q9UYh-.js';
10
+ import * as nodeos from 'node:os';
10
11
  import nodeos__default, { tmpdir } from 'node:os';
11
- import { generateHash as generateHash$1, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, hasFailed, getTasks, getTests } from '@vitest/runner/utils';
12
+ import { generateHash as generateHash$1, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, getTasks, isTestCase } from '@vitest/runner/utils';
12
13
  import { SnapshotManager } from '@vitest/snapshot/manager';
13
- import { v as version$1 } from './cac.By1HvRIk.js';
14
+ import { v as version$1 } from './cac.B99MQg-w.js';
14
15
  import { c as createBirpc } from './index.Bgo3tNWt.js';
15
- import { p as parse, s as stringify, d as printError, f as formatProjectName, w as withLabel, e as errorBanner, h as divider, i as generateCodeFrame, R as ReportersMap, B as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.AzwzFtyi.js';
16
+ import { p as parse, d as stringify, e as TraceMap, o as originalPositionFor, h as ancestor, i as printError, f as formatProjectName, w as withLabel, j as errorBanner, k as divider, l as Typechecker, m as generateCodeFrame, n as createDefinesScript, R as ReportersMap, B as BlobReporter, r as readBlobs, q as convertTasksToEvents, H as HangingProcessReporter } from './index.CcRZ6fUh.js';
16
17
  import require$$0$3 from 'events';
17
18
  import require$$1$1 from 'https';
18
19
  import require$$2 from 'http';
@@ -25,29 +26,35 @@ import require$$0 from 'zlib';
25
26
  import require$$0$1 from 'buffer';
26
27
  import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
27
28
  import crypto, { createHash } from 'node:crypto';
28
- import { distDir, rootDir } from '../path.js';
29
- import { h as hash, d as createFetchModuleFunction, n as normalizeResolvedIdToUrl, R as RandomSequencer, i as isPackageExists, g as getFilePoolName, e as isBrowserEnabled, r as resolveConfig, f as groupBy, j as getCoverageProvider, k as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.DarITf6U.js';
30
- import { b as ancestor, c as convertTasksToEvents } from './typechecker.DSo_maXz.js';
31
- import { TraceMap, originalPositionFor, parseErrorStacktrace } from '@vitest/utils/source-map';
29
+ import { rootDir, distDir } from '../path.js';
32
30
  import createDebug from 'debug';
31
+ import { h as hash, R as RandomSequencer, i as isPackageExists, c as isBrowserEnabled, r as resolveConfig, g as getCoverageProvider, a as resolveApiServerConfig } from './coverage.FU3w4IrQ.js';
32
+ import { readFile, writeFile, rename, stat, unlink, rm, mkdir, copyFile } from 'node:fs/promises';
33
33
  import { VitestModuleEvaluator } from '#module-evaluator';
34
34
  import { ModuleRunner } from 'vite/module-runner';
35
35
  import { Console } from 'node:console';
36
36
  import c from 'tinyrainbow';
37
+ import { highlight } from '@vitest/utils/highlight';
37
38
  import { createRequire, builtinModules, isBuiltin } from 'node:module';
38
- import url, { pathToFileURL } from 'node:url';
39
+ import url, { fileURLToPath, pathToFileURL } from 'node:url';
39
40
  import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
40
- import { rm, mkdir, copyFile } from 'node:fs/promises';
41
+ import { isatty } from 'node:tty';
42
+ import EventEmitter$1, { EventEmitter } from 'node:events';
43
+ import { fork } from 'node:child_process';
44
+ import v8 from 'node:v8';
45
+ import { Worker } from 'node:worker_threads';
41
46
  import pm from 'picomatch';
42
47
  import { glob, isDynamicPattern } from 'tinyglobby';
43
48
  import MagicString from 'magic-string';
44
49
  import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
45
- import { c as configDefaults } from './defaults.CXFFjsi8.js';
46
- import { f as findNearestPackageData } from './resolver.Bx6lE0iq.js';
50
+ import { c as configDefaults } from './defaults.BOqNVLsY.js';
51
+ import { KNOWN_ASSET_RE } from '@vitest/utils/constants';
52
+ import { findNearestPackageData } from '@vitest/utils/resolver';
47
53
  import * as esModuleLexer from 'es-module-lexer';
48
- import { a as BenchmarkReportsMap } from './index.BuwjkI-q.js';
54
+ import { a as BenchmarkReportsMap } from './index.BdSLhLDZ.js';
49
55
  import assert$1 from 'node:assert';
50
- import { serializeError as serializeError$1 } from '@vitest/utils/error';
56
+ import { serializeValue } from '@vitest/utils/serialize';
57
+ import { parseErrorStacktrace } from '@vitest/utils/source-map';
51
58
  import readline from 'node:readline';
52
59
  import { stripVTControlCharacters } from 'node:util';
53
60
 
@@ -5020,7 +5027,7 @@ var WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServerExport
5020
5027
  async function getModuleGraph(ctx, projectName, id, browser = false) {
5021
5028
  const graph = {}, externalized = /* @__PURE__ */ new Set(), inlined = /* @__PURE__ */ new Set(), project = ctx.getProjectByName(projectName);
5022
5029
  async function get(mod, seen = /* @__PURE__ */ new Map()) {
5023
- if (!mod || !mod.id || mod.id === "\0@vitest/browser/context") return;
5030
+ if (!mod || !mod.id || mod.id === "\0vitest/browser") return;
5024
5031
  if (seen.has(mod)) return seen.get(mod);
5025
5032
  let id = clearId(mod.id);
5026
5033
  seen.set(mod, id);
@@ -5081,8 +5088,8 @@ catch {}
5081
5088
  }
5082
5089
 
5083
5090
  function setup(ctx, _server) {
5084
- const wss = new WebSocketServer({ noServer: true }), clients = /* @__PURE__ */ new Map(), server = _server || ctx.vite;
5085
- server.httpServer?.on("upgrade", (request, socket, head) => {
5091
+ const wss = new WebSocketServer({ noServer: true }), clients = /* @__PURE__ */ new Map();
5092
+ (_server || ctx.vite).httpServer?.on("upgrade", (request, socket, head) => {
5086
5093
  if (!request.url) return;
5087
5094
  const { pathname } = new URL(request.url, "http://localhost");
5088
5095
  if (pathname === API_PATH) {
@@ -5107,11 +5114,11 @@ function setup(ctx, _server) {
5107
5114
  return ctx.state.getPaths();
5108
5115
  },
5109
5116
  async readTestFile(id) {
5110
- return !ctx.state.filesMap.has(id) || !existsSync(id) ? null : promises.readFile(id, "utf-8");
5117
+ return !ctx.state.filesMap.has(id) || !existsSync(id) ? null : promises$1.readFile(id, "utf-8");
5111
5118
  },
5112
5119
  async saveTestFile(id, content) {
5113
5120
  if (!ctx.state.filesMap.has(id) || !existsSync(id)) throw new Error(`Test file "${id}" was not registered, so it cannot be updated using the API.`);
5114
- return promises.writeFile(id, content, "utf-8");
5121
+ return promises$1.writeFile(id, content, "utf-8");
5115
5122
  },
5116
5123
  async rerun(files, resetTestNamePattern) {
5117
5124
  await ctx.rerunFiles(files, void 0, true, resetTestNamePattern);
@@ -5132,7 +5139,7 @@ function setup(ctx, _server) {
5132
5139
  const project = ctx.getProjectByName(projectName), result = browser ? await project.browser.vite.transformRequest(id) : await project.vite.transformRequest(id);
5133
5140
  if (result) {
5134
5141
  try {
5135
- result.source = result.source || await promises.readFile(id, "utf-8");
5142
+ result.source = result.source || await promises$1.readFile(id, "utf-8");
5136
5143
  } catch {}
5137
5144
  return result;
5138
5145
  }
@@ -5148,8 +5155,7 @@ function setup(ctx, _server) {
5148
5155
  return ctx.state.getUnhandledErrors();
5149
5156
  },
5150
5157
  async getTestFiles() {
5151
- const spec = await ctx.globTestSpecifications();
5152
- return spec.map((spec) => [
5158
+ return (await ctx.globTestSpecifications()).map((spec) => [
5153
5159
  {
5154
5160
  name: spec.project.config.name,
5155
5161
  root: spec.project.config.root
@@ -5229,16 +5235,39 @@ var setup$1 = /*#__PURE__*/Object.freeze({
5229
5235
  setup: setup
5230
5236
  });
5231
5237
 
5238
+ function groupBy(collection, iteratee) {
5239
+ return collection.reduce((acc, item) => {
5240
+ const key = iteratee(item);
5241
+ return acc[key] ||= [], acc[key].push(item), acc;
5242
+ }, {});
5243
+ }
5244
+ function stdout() {
5245
+ // @ts-expect-error Node.js maps process.stdout to console._stdout
5246
+ // eslint-disable-next-line no-console
5247
+ return console._stdout || process.stdout;
5248
+ }
5249
+ function escapeRegExp(s) {
5250
+ // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
5251
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5252
+ }
5253
+ function wildcardPatternToRegExp(pattern) {
5254
+ const negated = pattern[0] === "!";
5255
+ if (negated) pattern = pattern.slice(1);
5256
+ let regexp = `${pattern.split("*").map(escapeRegExp).join(".*")}$`;
5257
+ if (negated) regexp = `(?!${regexp})`;
5258
+ return new RegExp(`^${regexp}`, "i");
5259
+ }
5260
+
5232
5261
  function createDebugger(namespace) {
5233
5262
  const debug = createDebug(namespace);
5234
5263
  if (debug.enabled) return debug;
5235
5264
  }
5236
5265
 
5237
- const debug = createDebugger("vitest:ast-collect-info"), verbose = createDebugger("vitest:ast-collect-verbose");
5266
+ const debug$1 = createDebugger("vitest:ast-collect-info"), verbose = createDebugger("vitest:ast-collect-verbose");
5238
5267
  function astParseFile(filepath, code) {
5239
5268
  const ast = parseAst(code);
5240
5269
  if (verbose) verbose("Collecting", filepath, code);
5241
- else debug?.("Collecting", filepath);
5270
+ else debug$1?.("Collecting", filepath);
5242
5271
  const definitions = [], getName = (callee) => {
5243
5272
  if (!callee) return null;
5244
5273
  if (callee.type === "Identifier") return callee.name;
@@ -5302,7 +5331,7 @@ function astParseFile(filepath, code) {
5302
5331
  const property = callee.tag?.property?.name;
5303
5332
  isDynamicEach = property === "each" || property === "for";
5304
5333
  }
5305
- debug?.("Found", name, message, `(${mode})`), definitions.push({
5334
+ debug$1?.("Found", name, message, `(${mode})`), definitions.push({
5306
5335
  start,
5307
5336
  end,
5308
5337
  name: message,
@@ -5338,17 +5367,13 @@ function createFailedFileTask(project, filepath, error) {
5338
5367
  return file.file = file, file;
5339
5368
  }
5340
5369
  function serializeError(ctx, error) {
5341
- if ("errors" in error && "pluginCode" in error) {
5342
- const errors = error.errors.map((e) => {
5343
- return {
5344
- name: error.name,
5345
- message: e.text,
5346
- stack: e.location ? `${error.name}: ${e.text}\n at ${relative(ctx.config.root, e.location.file)}:${e.location.line}:${e.location.column}` : ""
5347
- };
5348
- });
5349
- return errors;
5350
- }
5351
- return [{
5370
+ return "errors" in error && "pluginCode" in error ? error.errors.map((e) => {
5371
+ return {
5372
+ name: error.name,
5373
+ message: e.text,
5374
+ stack: e.location ? `${error.name}: ${e.text}\n at ${relative(ctx.config.root, e.location.file)}:${e.location.line}:${e.location.column}` : ""
5375
+ };
5376
+ }) : [{
5352
5377
  name: error.name,
5353
5378
  stack: error.stack,
5354
5379
  message: error.message
@@ -5390,8 +5415,8 @@ function createFileTask(testFilepath, code, requestMap, options) {
5390
5415
  column: processedLocation.column
5391
5416
  });
5392
5417
  if (originalLocation.column != null) verbose?.(`Found location for`, definition.type, definition.name, `${processedLocation.line}:${processedLocation.column}`, "->", `${originalLocation.line}:${originalLocation.column}`), location = originalLocation;
5393
- else debug?.("Cannot find original location for", definition.type, definition.name, `${processedLocation.column}:${processedLocation.line}`);
5394
- } else debug?.("Cannot find original location for", definition.type, definition.name, `${definition.start}`);
5418
+ else debug$1?.("Cannot find original location for", definition.type, definition.name, `${processedLocation.column}:${processedLocation.line}`);
5419
+ } else debug$1?.("Cannot find original location for", definition.type, definition.name, `${definition.start}`);
5395
5420
  if (definition.type === "suite") {
5396
5421
  const task = {
5397
5422
  type: definition.type,
@@ -5446,7 +5471,7 @@ async function astCollectTests(project, filepath) {
5446
5471
  allowOnly: project.config.allowOnly,
5447
5472
  testNamePattern: project.config.testNamePattern,
5448
5473
  pool: project.browser ? "browser" : project.config.pool
5449
- }) : (debug?.("Cannot parse", testFilepath, "(vite didn't return anything)"), createFailedFileTask(project, filepath, /* @__PURE__ */ new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`)));
5474
+ }) : (debug$1?.("Cannot parse", testFilepath, "(vite didn't return anything)"), createFailedFileTask(project, filepath, /* @__PURE__ */ new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`)));
5450
5475
  }
5451
5476
  async function transformSSR(project, filepath) {
5452
5477
  const request = await project.vite.transformRequest(filepath, { ssr: false });
@@ -5611,18 +5636,197 @@ class VitestCache {
5611
5636
  }
5612
5637
  }
5613
5638
 
5639
+ const created = /* @__PURE__ */ new Set(), promises = /* @__PURE__ */ new Map();
5640
+ function createFetchModuleFunction(resolver, tmpDir = join(tmpdir(), nanoid()), dump) {
5641
+ return async (url, importer, environment, cacheFs, options) => {
5642
+ // We are copy pasting Vite's externalization logic from `fetchModule` because
5643
+ // we instead rely on our own `shouldExternalize` method because Vite
5644
+ // doesn't support `resolve.external` in non SSR environments (jsdom/happy-dom)
5645
+ if (url.startsWith("data:")) return {
5646
+ externalize: url,
5647
+ type: "builtin"
5648
+ };
5649
+ if (url === "/@vite/client" || url === "@vite/client")
5650
+ // this will be stubbed
5651
+ return {
5652
+ externalize: "/@vite/client",
5653
+ type: "module"
5654
+ };
5655
+ const isFileUrl = url.startsWith("file://");
5656
+ if (isExternalUrl(url) && !isFileUrl) return {
5657
+ externalize: url,
5658
+ type: "network"
5659
+ };
5660
+ // Vite does the same in `fetchModule`, but we want to externalize modules ourselves,
5661
+ // so we do this first to resolve the module and check its `id`. The next call of
5662
+ // `ensureEntryFromUrl` inside `fetchModule` is cached and should take no time
5663
+ // This also makes it so externalized modules are inside the module graph.
5664
+ const moduleGraphModule = await environment.moduleGraph.ensureEntryFromUrl(unwrapId(url)), cached = !!moduleGraphModule.transformResult;
5665
+ // if url is already cached, we can just confirm it's also cached on the server
5666
+ if (options?.cached && cached) return { cache: true };
5667
+ if (moduleGraphModule.id) {
5668
+ const externalize = await resolver.shouldExternalize(moduleGraphModule.id);
5669
+ if (externalize) return {
5670
+ externalize,
5671
+ type: "module"
5672
+ };
5673
+ }
5674
+ let moduleRunnerModule;
5675
+ if (dump?.dumpFolder && dump.readFromDump) {
5676
+ const path = resolve(dump?.dumpFolder, url.replace(/[^\w+]/g, "-"));
5677
+ if (existsSync(path)) {
5678
+ const code = await readFile(path, "utf-8"), matchIndex = code.lastIndexOf("\n//");
5679
+ if (matchIndex !== -1) {
5680
+ const { id, file } = JSON.parse(code.slice(matchIndex + 4));
5681
+ moduleRunnerModule = {
5682
+ code,
5683
+ id,
5684
+ url,
5685
+ file,
5686
+ invalidate: false
5687
+ };
5688
+ }
5689
+ }
5690
+ }
5691
+ if (!moduleRunnerModule) moduleRunnerModule = await fetchModule(environment, url, importer, {
5692
+ ...options,
5693
+ inlineSourceMap: false
5694
+ }).catch(handleRollupError);
5695
+ const result = processResultSource(environment, moduleRunnerModule);
5696
+ if (dump?.dumpFolder && "code" in result) {
5697
+ const path = resolve(dump?.dumpFolder, result.url.replace(/[^\w+]/g, "-"));
5698
+ await writeFile(path, `${result.code}\n// ${JSON.stringify({
5699
+ id: result.id,
5700
+ file: result.file
5701
+ })}`, "utf-8");
5702
+ }
5703
+ if (!cacheFs || !("code" in result)) return result;
5704
+ const code = result.code, transformResult = result.transformResult;
5705
+ if (!transformResult) throw new Error(`"transformResult" in not defined. This is a bug in Vitest.`);
5706
+ // to avoid serialising large chunks of code,
5707
+ // we store them in a tmp file and read in the test thread
5708
+ if ("_vitestTmp" in transformResult) return getCachedResult(result, Reflect.get(transformResult, "_vitestTmp"));
5709
+ const dir = join(tmpDir, environment.name), name = hash("sha1", result.id, "hex"), tmp = join(dir, name);
5710
+ if (!created.has(dir)) mkdirSync(dir, { recursive: true }), created.add(dir);
5711
+ return promises.has(tmp) ? (await promises.get(tmp), Reflect.set(transformResult, "_vitestTmp", tmp), getCachedResult(result, tmp)) : (promises.set(tmp, atomicWriteFile(tmp, code).catch(() => writeFile(tmp, code, "utf-8")).finally(() => promises.delete(tmp))), await promises.get(tmp), getCachedResult(result, tmp));
5712
+ };
5713
+ }
5714
+ let SOURCEMAPPING_URL = "sourceMa";
5715
+ SOURCEMAPPING_URL += "ppingURL";
5716
+ const MODULE_RUNNER_SOURCEMAPPING_SOURCE = "//# sourceMappingSource=vite-generated";
5717
+ function processResultSource(environment, result) {
5718
+ if (!("code" in result)) return result;
5719
+ const node = environment.moduleGraph.getModuleById(result.id);
5720
+ if (node?.transformResult)
5721
+ // this also overrides node.transformResult.code which is also what the module
5722
+ // runner does under the hood by default (we disable source maps inlining)
5723
+ inlineSourceMap(node.transformResult);
5724
+ return {
5725
+ ...result,
5726
+ code: node?.transformResult?.code || result.code,
5727
+ transformResult: node?.transformResult
5728
+ };
5729
+ }
5730
+ const OTHER_SOURCE_MAP_REGEXP = new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,([A-Za-z0-9+/=]+)$`, "gm");
5731
+ // we have to inline the source map ourselves, because
5732
+ // - we don't need //# sourceURL since we are running code in VM
5733
+ // - important in stack traces and the V8 coverage
5734
+ // - we need to inject an empty line for --inspect-brk
5735
+ function inlineSourceMap(result) {
5736
+ const map = result.map;
5737
+ let code = result.code;
5738
+ if (!map || !("version" in map) || code.includes(MODULE_RUNNER_SOURCEMAPPING_SOURCE)) return result;
5739
+ if (OTHER_SOURCE_MAP_REGEXP.lastIndex = 0, OTHER_SOURCE_MAP_REGEXP.test(code)) code = code.replace(OTHER_SOURCE_MAP_REGEXP, "");
5740
+ const sourceMap = { ...map };
5741
+ // If the first line is not present on source maps, add simple 1:1 mapping ([0,0,0,0], [1,0,0,0])
5742
+ // so that debuggers can be set to break on first line
5743
+ if (sourceMap.mappings[0] === ";") sourceMap.mappings = `AAAA,CAAA${sourceMap.mappings}`;
5744
+ return result.code = `${code.trimEnd()}\n${MODULE_RUNNER_SOURCEMAPPING_SOURCE}\n//# ${SOURCEMAPPING_URL}=${genSourceMapUrl(sourceMap)}\n`, result;
5745
+ }
5746
+ function genSourceMapUrl(map) {
5747
+ if (typeof map !== "string") map = JSON.stringify(map);
5748
+ return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
5749
+ }
5750
+ function getCachedResult(result, tmp) {
5751
+ return {
5752
+ cached: true,
5753
+ file: result.file,
5754
+ id: result.id,
5755
+ tmp,
5756
+ url: result.url,
5757
+ invalidate: result.invalidate
5758
+ };
5759
+ }
5760
+ // serialize rollup error on server to preserve details as a test error
5761
+ function handleRollupError(e) {
5762
+ throw e instanceof Error && ("plugin" in e || "frame" in e || "id" in e) ? {
5763
+ name: e.name,
5764
+ message: e.message,
5765
+ stack: e.stack,
5766
+ cause: e.cause,
5767
+ __vitest_rollup_error__: {
5768
+ plugin: e.plugin,
5769
+ id: e.id,
5770
+ loc: e.loc,
5771
+ frame: e.frame
5772
+ }
5773
+ } : e;
5774
+ }
5775
+ /**
5776
+ * Performs an atomic write operation using the write-then-rename pattern.
5777
+ *
5778
+ * Why we need this:
5779
+ * - Ensures file integrity by never leaving partially written files on disk
5780
+ * - Prevents other processes from reading incomplete data during writes
5781
+ * - Particularly important for test files where incomplete writes could cause test failures
5782
+ *
5783
+ * The implementation writes to a temporary file first, then renames it to the target path.
5784
+ * This rename operation is atomic on most filesystems (including POSIX-compliant ones),
5785
+ * guaranteeing that other processes will only ever see the complete file.
5786
+ *
5787
+ * Added in https://github.com/vitest-dev/vitest/pull/7531
5788
+ */
5789
+ async function atomicWriteFile(realFilePath, data) {
5790
+ const dir = dirname(realFilePath), tmpFilePath = join(dir, `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
5791
+ try {
5792
+ await writeFile(tmpFilePath, data, "utf-8"), await rename(tmpFilePath, realFilePath);
5793
+ } finally {
5794
+ try {
5795
+ if (await stat(tmpFilePath)) await unlink(tmpFilePath);
5796
+ } catch {}
5797
+ }
5798
+ }
5799
+
5800
+ // this is copy pasted from vite
5801
+ function normalizeResolvedIdToUrl(environment, resolvedId) {
5802
+ const root = environment.config.root, depsOptimizer = environment.depsOptimizer;
5803
+ let url;
5804
+ // normalize all imports into resolved URLs
5805
+ // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'`
5806
+ if (resolvedId.startsWith(withTrailingSlash(root)))
5807
+ // in root: infer short absolute path from root
5808
+ url = resolvedId.slice(root.length);
5809
+ else if (depsOptimizer?.isOptimizedDepFile(resolvedId) || resolvedId !== "/@react-refresh" && path.isAbsolute(resolvedId) && existsSync(cleanUrl(resolvedId)))
5810
+ // an optimized deps may not yet exists in the filesystem, or
5811
+ // a regular file exists but is out of root: rewrite to absolute /@fs/ paths
5812
+ url = path.posix.join("/@fs/", resolvedId);
5813
+ else url = resolvedId;
5814
+ // if the resolved id is not a valid browser import specifier,
5815
+ // prefix it to make it valid. We will strip this before feeding it
5816
+ // back into the transform pipeline
5817
+ if (url[0] !== "." && url[0] !== "/") url = wrapId(resolvedId);
5818
+ return url;
5819
+ }
5820
+
5614
5821
  class ServerModuleRunner extends ModuleRunner {
5615
- constructor(environment, resolver, config) {
5616
- const fetchModule = createFetchModuleFunction(resolver, false);
5822
+ constructor(environment, fetcher, config) {
5617
5823
  super({
5618
5824
  hmr: false,
5619
- sourcemapInterceptor: "node",
5620
5825
  transport: { async invoke(event) {
5621
5826
  if (event.type !== "custom") throw new Error(`Vitest Module Runner doesn't support Vite HMR events.`);
5622
5827
  const { data } = event.data;
5623
5828
  try {
5624
- const result = await fetchModule(data[0], data[1], environment, data[2]);
5625
- return { result };
5829
+ return { result: await fetcher(data[0], data[1], environment, false, data[2]) };
5626
5830
  } catch (error) {
5627
5831
  return { error };
5628
5832
  }
@@ -5761,8 +5965,8 @@ class Logger {
5761
5965
  const projectsFilter = toArray(config.project);
5762
5966
  if (projectsFilter.length) this.console.error(c.dim("projects: ") + c.yellow(projectsFilter.join(comma)));
5763
5967
  this.ctx.projects.forEach((project) => {
5764
- const config = project.config, printConfig = !project.isRootProject() && project.name;
5765
- if (printConfig) this.console.error(`\n${formatProjectName(project)}\n`);
5968
+ const config = project.config;
5969
+ if (!project.isRootProject() && project.name) this.console.error(`\n${formatProjectName(project)}\n`);
5766
5970
  if (config.include) this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
5767
5971
  if (config.exclude) this.console.error(c.dim("exclude: ") + c.yellow(config.exclude.join(comma)));
5768
5972
  if (config.typecheck.enabled) this.console.error(c.dim("typecheck include: ") + c.yellow(config.typecheck.include.join(comma))), this.console.error(c.dim("typecheck exclude: ") + c.yellow(config.typecheck.exclude.join(comma)));
@@ -5787,7 +5991,7 @@ class Logger {
5787
5991
  if (!project.browser) return;
5788
5992
  const resolvedUrls = project.browser.vite.resolvedUrls, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
5789
5993
  if (!origin) return;
5790
- const output = project.isRootProject() ? "" : formatProjectName(project), provider = project.browser.provider.name, providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5994
+ const output = project.isRootProject() ? "" : formatProjectName(project), provider = project.browser.provider?.name, providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5791
5995
  this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/__vitest_test__/", origin))}\n`));
5792
5996
  }
5793
5997
  printUnhandledErrors(errors) {
@@ -5819,7 +6023,8 @@ This might cause false positive tests. Resolve unhandled errors to make sure you
5819
6023
  // Interrupted signals don't set exit code automatically.
5820
6024
  // Use same exit code as node: https://nodejs.org/api/process.html#signal-events
5821
6025
  if (cleanup(), process.exitCode === void 0) process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
5822
- process.exit();
6026
+ // Timeout to flush stderr
6027
+ setTimeout(() => process.exit(), 1);
5823
6028
  };
5824
6029
  process.once("SIGINT", onExit), process.once("SIGTERM", onExit), process.once("exit", onExit), this.ctx.onClose(() => {
5825
6030
  process.off("SIGINT", onExit), process.off("SIGTERM", onExit), process.off("exit", onExit), cleanup();
@@ -5853,7 +6058,7 @@ class VitestPackageInstaller {
5853
6058
  }
5854
6059
  if (/* @__PURE__ */ isPackageExists(dependency, { paths: [root, __dirname] })) return true;
5855
6060
  if (process.stderr.write(c.red(`${c.inverse(c.red(" MISSING DEPENDENCY "))} Cannot find dependency '${dependency}'\n\n`)), !isTTY) return false;
5856
- const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; }), { install } = await prompts.default({
6061
+ const { install } = await (await import('./index.Dc3xnDvT.js').then(function (n) { return n.i; })).default({
5857
6062
  type: "confirm",
5858
6063
  name: "install",
5859
6064
  message: c.reset(`Do you want to install ${c.green(dependency)}?`)
@@ -5866,12 +6071,1057 @@ class VitestPackageInstaller {
5866
6071
  }
5867
6072
  }
5868
6073
 
5869
- function serializeConfig(config, coreConfig, viteConfig) {
5870
- const optimizer = config.deps?.optimizer || {}, poolOptions = config.poolOptions, isolate = viteConfig?.test?.isolate;
6074
+ function getDefaultThreadsCount(config) {
6075
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
6076
+ return config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
6077
+ }
6078
+ function getWorkerMemoryLimit(config) {
6079
+ return config.vmMemoryLimit ? config.vmMemoryLimit : 1 / (config.maxWorkers ?? getDefaultThreadsCount(config));
6080
+ }
6081
+ /**
6082
+ * Converts a string representing an amount of memory to bytes.
6083
+ *
6084
+ * @param input The value to convert to bytes.
6085
+ * @param percentageReference The reference value to use when a '%' value is supplied.
6086
+ */
6087
+ function stringToBytes(input, percentageReference) {
6088
+ if (input === null || input === void 0) return input;
6089
+ if (typeof input === "string") if (Number.isNaN(Number.parseFloat(input.slice(-1)))) {
6090
+ let [, numericString, trailingChars] = input.match(/(.*?)([^0-9.-]+)$/) || [];
6091
+ if (trailingChars && numericString) {
6092
+ const numericValue = Number.parseFloat(numericString);
6093
+ switch (trailingChars = trailingChars.toLowerCase(), trailingChars) {
6094
+ case "%":
6095
+ input = numericValue / 100;
6096
+ break;
6097
+ case "kb":
6098
+ case "k": return numericValue * 1e3;
6099
+ case "kib": return numericValue * 1024;
6100
+ case "mb":
6101
+ case "m": return numericValue * 1e3 * 1e3;
6102
+ case "mib": return numericValue * 1024 * 1024;
6103
+ case "gb":
6104
+ case "g": return numericValue * 1e3 * 1e3 * 1e3;
6105
+ case "gib": return numericValue * 1024 * 1024 * 1024;
6106
+ }
6107
+ }
6108
+ } else input = Number.parseFloat(input);
6109
+ if (typeof input === "number") if (input <= 1 && input > 0) {
6110
+ if (percentageReference) return Math.floor(input * percentageReference);
6111
+ throw new Error("For a percentage based memory limit a percentageReference must be supplied");
6112
+ } else if (input > 1) return Math.floor(input);
6113
+ else throw new Error("Unexpected numerical input for \"memoryLimit\"");
6114
+ return null;
6115
+ }
6116
+
6117
+ async function getSpecificationsEnvironments(specifications) {
6118
+ const environments = /* @__PURE__ */ new WeakMap(), cache = /* @__PURE__ */ new Map();
6119
+ return await Promise.all(specifications.map(async (spec) => {
6120
+ const { moduleId: filepath, project } = spec;
6121
+ // reuse if projects have the same test files
6122
+ let code = cache.get(filepath);
6123
+ if (!code) code = await promises$1.readFile(filepath, "utf-8"), cache.set(filepath, code);
6124
+ // 1. Check for control comments in the file
6125
+ let env = code.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
6126
+ // 2. Fallback to global env
6127
+ env ||= project.config.environment || "node";
6128
+ let envOptionsJson = code.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
6129
+ if (envOptionsJson?.endsWith("*/"))
6130
+ // Trim closing Docblock characters the above regex might have captured
6131
+ envOptionsJson = envOptionsJson.slice(0, -2);
6132
+ const envOptions = JSON.parse(envOptionsJson || "null"), environment = {
6133
+ name: env,
6134
+ options: envOptions ? { [env === "happy-dom" ? "happyDOM" : env]: envOptions } : null
6135
+ };
6136
+ environments.set(spec, environment);
6137
+ })), environments;
6138
+ }
6139
+
6140
+ const debug = createDebugger("vitest:browser:pool");
6141
+ function createBrowserPool(vitest) {
6142
+ const providers = /* @__PURE__ */ new Set(), numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, maxThreadsCount = Math.min(12, numCpus - 1), threadsCount = vitest.config.watch ? Math.max(Math.floor(maxThreadsCount / 2), 1) : Math.max(maxThreadsCount, 1), projectPools = /* @__PURE__ */ new WeakMap(), ensurePool = (project) => {
6143
+ if (projectPools.has(project)) return projectPools.get(project);
6144
+ debug?.("creating pool for project %s", project.name);
6145
+ const resolvedUrls = project.browser.vite.resolvedUrls, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
6146
+ if (!origin) throw new Error(`Can't find browser origin URL for project "${project.name}"`);
6147
+ const pool = new BrowserPool(project, {
6148
+ maxWorkers: getThreadsCount(project),
6149
+ origin
6150
+ });
6151
+ return projectPools.set(project, pool), vitest.onCancel(() => {
6152
+ pool.cancel();
6153
+ }), pool;
6154
+ }, runWorkspaceTests = async (method, specs) => {
6155
+ const groupedFiles = /* @__PURE__ */ new Map();
6156
+ for (const { project, moduleId, testLines } of specs) {
6157
+ const files = groupedFiles.get(project) || [];
6158
+ files.push({
6159
+ filepath: moduleId,
6160
+ testLocations: testLines
6161
+ }), groupedFiles.set(project, files);
6162
+ }
6163
+ let isCancelled = false;
6164
+ vitest.onCancel(() => {
6165
+ isCancelled = true;
6166
+ });
6167
+ const initialisedPools = await Promise.all([...groupedFiles.entries()].map(async ([project, files]) => {
6168
+ if (await project._initBrowserProvider(), !project.browser) throw new TypeError(`The browser server was not initialized${project.name ? ` for the "${project.name}" project` : ""}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
6169
+ if (isCancelled) return;
6170
+ debug?.("provider is ready for %s project", project.name);
6171
+ const pool = ensurePool(project);
6172
+ return vitest.state.clearFiles(project, files.map((f) => f.filepath)), providers.add(project.browser.provider), {
6173
+ pool,
6174
+ provider: project.browser.provider,
6175
+ runTests: () => pool.runTests(method, files)
6176
+ };
6177
+ }));
6178
+ if (isCancelled) return;
6179
+ const parallelPools = [], nonParallelPools = [];
6180
+ for (const pool of initialisedPools) {
6181
+ if (!pool)
6182
+ // this means it was cancelled
6183
+ return;
6184
+ if (pool.provider.mocker && pool.provider.supportsParallelism) parallelPools.push(pool.runTests);
6185
+ else nonParallelPools.push(pool.runTests);
6186
+ }
6187
+ await Promise.all(parallelPools.map((runTests) => runTests()));
6188
+ for (const runTests of nonParallelPools) {
6189
+ if (isCancelled) return;
6190
+ await runTests();
6191
+ }
6192
+ };
6193
+ function getThreadsCount(project) {
6194
+ const config = project.config.browser;
6195
+ return !config.headless || !config.fileParallelism || !project.browser.provider.supportsParallelism ? 1 : project.config.maxWorkers ? project.config.maxWorkers : threadsCount;
6196
+ }
6197
+ return {
6198
+ name: "browser",
6199
+ async close() {
6200
+ await Promise.all([...providers].map((provider) => provider.close())), vitest._browserSessions.sessionIds.clear(), providers.clear(), vitest.projects.forEach((project) => {
6201
+ project.browser?.state.orchestrators.forEach((orchestrator) => {
6202
+ orchestrator.$close();
6203
+ });
6204
+ }), debug?.("browser pool closed all providers");
6205
+ },
6206
+ runTests: (files) => runWorkspaceTests("run", files),
6207
+ collectTests: (files) => runWorkspaceTests("collect", files)
6208
+ };
6209
+ }
6210
+ function escapePathToRegexp(path) {
6211
+ return path.replace(/[/\\.?*()^${}|[\]+]/g, "\\$&");
6212
+ }
6213
+ class BrowserPool {
6214
+ _queue = [];
6215
+ _promise;
6216
+ _providedContext;
6217
+ readySessions = /* @__PURE__ */ new Set();
6218
+ constructor(project, options) {
6219
+ this.project = project, this.options = options;
6220
+ }
6221
+ cancel() {
6222
+ this._queue = [];
6223
+ }
6224
+ reject(error) {
6225
+ this._promise?.reject(error), this._promise = void 0, this.cancel();
6226
+ }
6227
+ get orchestrators() {
6228
+ return this.project.browser.state.orchestrators;
6229
+ }
6230
+ async runTests(method, files) {
6231
+ if (this._promise ??= createDefer(), !files.length) return debug?.("no tests found, finishing test run immediately"), this._promise.resolve(), this._promise;
6232
+ if (this._providedContext = stringify(this.project.getProvidedContext()), this._queue.push(...files), this.readySessions.forEach((sessionId) => {
6233
+ if (this._queue.length) this.readySessions.delete(sessionId), this.runNextTest(method, sessionId);
6234
+ }), this.orchestrators.size >= this.options.maxWorkers) return debug?.("all orchestrators are ready, not creating more"), this._promise;
6235
+ // open the minimum amount of tabs
6236
+ // if there is only 1 file running, we don't need 8 tabs running
6237
+ const workerCount = Math.min(this.options.maxWorkers - this.orchestrators.size, files.length), promises = [];
6238
+ for (let i = 0; i < workerCount; i++) {
6239
+ const sessionId = crypto.randomUUID();
6240
+ this.project.vitest._browserSessions.sessionIds.add(sessionId);
6241
+ const project = this.project.name;
6242
+ debug?.("[%s] creating session for %s", sessionId, project);
6243
+ const page = this.openPage(sessionId).then(() => {
6244
+ // start running tests on the page when it's ready
6245
+ this.runNextTest(method, sessionId);
6246
+ });
6247
+ promises.push(page);
6248
+ }
6249
+ return await Promise.all(promises), debug?.("all sessions are created"), this._promise;
6250
+ }
6251
+ async openPage(sessionId) {
6252
+ const sessionPromise = this.project.vitest._browserSessions.createSession(sessionId, this.project, this), browser = this.project.browser, url = new URL("/__vitest_test__/", this.options.origin);
6253
+ url.searchParams.set("sessionId", sessionId);
6254
+ const pagePromise = browser.provider.openPage(sessionId, url.toString());
6255
+ await Promise.all([sessionPromise, pagePromise]);
6256
+ }
6257
+ getOrchestrator(sessionId) {
6258
+ const orchestrator = this.orchestrators.get(sessionId);
6259
+ if (!orchestrator) throw new Error(`Orchestrator not found for session ${sessionId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
6260
+ return orchestrator;
6261
+ }
6262
+ finishSession(sessionId) {
6263
+ // the last worker finished running tests
6264
+ if (this.readySessions.add(sessionId), this.readySessions.size === this.orchestrators.size) this._promise?.resolve(), this._promise = void 0, debug?.("[%s] all tests finished running", sessionId);
6265
+ else debug?.(`did not finish sessions for ${sessionId}: |ready - %s| |overall - %s|`, [...this.readySessions].join(", "), [...this.orchestrators.keys()].join(", "));
6266
+ }
6267
+ runNextTest(method, sessionId) {
6268
+ const file = this._queue.shift();
6269
+ if (!file) {
6270
+ // we don't need to cleanup testers if isolation is enabled,
6271
+ // because cleanup is done at the end of every test
6272
+ if (debug?.("[%s] no more tests to run", sessionId), this.project.config.browser.isolate) {
6273
+ this.finishSession(sessionId);
6274
+ return;
6275
+ }
6276
+ this.getOrchestrator(sessionId).cleanupTesters().catch((error) => this.reject(error)).finally(() => this.finishSession(sessionId));
6277
+ return;
6278
+ }
6279
+ if (!this._promise) throw new Error(`Unexpected empty queue`);
6280
+ const orchestrator = this.getOrchestrator(sessionId);
6281
+ debug?.("[%s] run test %s", sessionId, file), this.setBreakpoint(sessionId, file.filepath).then(() => {
6282
+ // this starts running tests inside the orchestrator
6283
+ orchestrator.createTesters({
6284
+ method,
6285
+ files: [file],
6286
+ providedContext: this._providedContext || "[{}]"
6287
+ }).then(() => {
6288
+ debug?.("[%s] test %s finished running", sessionId, file), this.runNextTest(method, sessionId);
6289
+ }).catch((error) => {
6290
+ // if user cancels the test run manually, ignore the error and exit gracefully
6291
+ if (this.project.vitest.isCancelling && error instanceof Error && error.message.startsWith("Browser connection was closed while running tests")) {
6292
+ this.cancel(), this._promise?.resolve(), this._promise = void 0, debug?.("[%s] browser connection was closed", sessionId);
6293
+ return;
6294
+ }
6295
+ debug?.("[%s] error during %s test run: %s", sessionId, file, error), this.reject(error);
6296
+ });
6297
+ }).catch((err) => this.reject(err));
6298
+ }
6299
+ async setBreakpoint(sessionId, file) {
6300
+ if (!this.project.config.inspector.waitForDebugger) return;
6301
+ const provider = this.project.browser.provider, browser = this.project.config.browser.name;
6302
+ if (shouldIgnoreDebugger(provider.name, browser)) {
6303
+ debug?.("[$s] ignoring debugger in %s browser because it is not supported", sessionId, browser);
6304
+ return;
6305
+ }
6306
+ if (!provider.getCDPSession) throw new Error("Unable to set breakpoint, CDP not supported");
6307
+ debug?.("[%s] set breakpoint for %s", sessionId, file);
6308
+ const session = await provider.getCDPSession(sessionId);
6309
+ await session.send("Debugger.enable", {}), await session.send("Debugger.setBreakpointByUrl", {
6310
+ lineNumber: 0,
6311
+ urlRegex: escapePathToRegexp(file)
6312
+ });
6313
+ }
6314
+ }
6315
+ function shouldIgnoreDebugger(provider, browser) {
6316
+ return provider === "webdriverio" ? browser !== "chrome" && browser !== "edge" : browser !== "chromium";
6317
+ }
6318
+
6319
+ function createMethodsRPC(project, options = {}) {
6320
+ const vitest = project.vitest, cacheFs = options.cacheFs ?? false;
6321
+ if (project.vitest.state.metadata[project.name] ??= {
6322
+ externalized: {},
6323
+ duration: {},
6324
+ tmps: {}
6325
+ }, project.config.dumpDir && !existsSync(project.config.dumpDir)) mkdirSync(project.config.dumpDir, { recursive: true });
6326
+ return project.vitest.state.metadata[project.name].dumpDir = project.config.dumpDir, {
6327
+ async fetch(url, importer, environmentName, options) {
6328
+ const environment = project.vite.environments[environmentName];
6329
+ if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
6330
+ const start = performance.now();
6331
+ return await project._fetcher(url, importer, environment, cacheFs, options).then((result) => {
6332
+ const duration = performance.now() - start;
6333
+ project.vitest.state.transformTime += duration;
6334
+ const metadata = project.vitest.state.metadata[project.name];
6335
+ if ("externalize" in result) metadata.externalized[url] = result.externalize;
6336
+ if ("tmp" in result) metadata.tmps[url] = result.tmp;
6337
+ return metadata.duration[url] ??= [], metadata.duration[url].push(duration), result;
6338
+ });
6339
+ },
6340
+ async resolve(id, importer, environmentName) {
6341
+ const environment = project.vite.environments[environmentName];
6342
+ if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
6343
+ const resolved = await environment.pluginContainer.resolveId(id, importer);
6344
+ return resolved ? {
6345
+ file: cleanUrl(resolved.id),
6346
+ url: normalizeResolvedIdToUrl(environment, resolved.id),
6347
+ id: resolved.id
6348
+ } : null;
6349
+ },
6350
+ snapshotSaved(snapshot) {
6351
+ vitest.snapshot.add(snapshot);
6352
+ },
6353
+ resolveSnapshotPath(testPath) {
6354
+ return vitest.snapshot.resolvePath(testPath, { config: project.serializedConfig });
6355
+ },
6356
+ async transform(id) {
6357
+ const environment = project.vite.environments.__vitest_vm__;
6358
+ if (!environment) throw new Error(`The VM environment was not defined in the Vite config. This is a bug in Vitest. Please, open a new issue with reproduction.`);
6359
+ const url = normalizeResolvedIdToUrl(environment, fileURLToPath(id));
6360
+ return { code: (await environment.transformRequest(url).catch(handleRollupError))?.code };
6361
+ },
6362
+ async onQueued(file) {
6363
+ if (options.collect) vitest.state.collectFiles(project, [file]);
6364
+ else await vitest._testRun.enqueued(project, file);
6365
+ },
6366
+ async onCollected(files) {
6367
+ if (options.collect) vitest.state.collectFiles(project, files);
6368
+ else await vitest._testRun.collected(project, files);
6369
+ },
6370
+ onAfterSuiteRun(meta) {
6371
+ vitest.coverageProvider?.onAfterSuiteRun(meta);
6372
+ },
6373
+ async onTaskAnnotate(testId, annotation) {
6374
+ return vitest._testRun.annotate(testId, annotation);
6375
+ },
6376
+ async onTaskUpdate(packs, events) {
6377
+ if (options.collect) vitest.state.updateTasks(packs);
6378
+ else await vitest._testRun.updated(packs, events);
6379
+ },
6380
+ async onUserConsoleLog(log) {
6381
+ if (options.collect) vitest.state.updateUserLog(log);
6382
+ else await vitest._testRun.log(log);
6383
+ },
6384
+ onUnhandledError(err, type) {
6385
+ vitest.state.catchError(err, type);
6386
+ },
6387
+ onCancel(reason) {
6388
+ vitest.cancelCurrentRun(reason);
6389
+ },
6390
+ getCountOfFailedTests() {
6391
+ return vitest.state.getCountOfFailedTests();
6392
+ }
6393
+ };
6394
+ }
6395
+
6396
+ var RunnerState = /* @__PURE__ */ function(RunnerState) {
6397
+ return RunnerState["IDLE"] = "idle", RunnerState["STARTING"] = "starting", RunnerState["STARTED"] = "started", RunnerState["STOPPING"] = "stopping", RunnerState["STOPPED"] = "stopped", RunnerState;
6398
+ }(RunnerState || {});
6399
+ const START_TIMEOUT = 1e4, STOP_TIMEOUT = 1e4;
6400
+ /** @experimental */
6401
+ class PoolRunner {
6402
+ /** Exposed to test runner as `VITEST_POOL_ID`. Value is between 1-`maxWorkers`. */
6403
+ poolId = void 0;
6404
+ project;
6405
+ environment;
6406
+ _state = RunnerState.IDLE;
6407
+ _operationLock = null;
6408
+ _eventEmitter = new EventEmitter();
6409
+ _rpc;
6410
+ get isTerminated() {
6411
+ return this._state === RunnerState.STOPPED;
6412
+ }
6413
+ get isStarted() {
6414
+ return this._state === RunnerState.STARTED;
6415
+ }
6416
+ constructor(options, worker) {
6417
+ this.worker = worker, this.project = options.project, this.environment = options.environment, this._rpc = createBirpc(createMethodsRPC(this.project, {
6418
+ collect: options.method === "collect",
6419
+ cacheFs: worker.cacheFs
6420
+ }), {
6421
+ eventNames: ["onCancel"],
6422
+ post: (request) => this.postMessage(request),
6423
+ on: (callback) => this._eventEmitter.on("rpc", callback),
6424
+ timeout: -1
6425
+ }), this.project.vitest.onCancel((reason) => this._rpc.onCancel(reason));
6426
+ }
6427
+ postMessage(message) {
6428
+ // Only send messages when runner is active (not fully stopped)
6429
+ // Allow sending during STOPPING state for the 'stop' message itself
6430
+ if (this._state !== RunnerState.STOPPED) return this.worker.send(message);
6431
+ }
6432
+ async start() {
6433
+ // Wait for any ongoing operation to complete
6434
+ if (this._operationLock) await this._operationLock;
6435
+ if (!(this._state === RunnerState.STARTED || this._state === RunnerState.STARTING)) {
6436
+ if (this._state === RunnerState.STOPPED) throw new Error("[vitest-pool-runner]: Cannot start a stopped runner");
6437
+ // Create operation lock to prevent concurrent start/stop
6438
+ this._operationLock = createDefer();
6439
+ try {
6440
+ this._state = RunnerState.STARTING, await this.worker.start(), this.worker.on("error", this.emitWorkerError), this.worker.on("exit", this.emitUnexpectedExit), this.worker.on("message", this.emitWorkerMessage);
6441
+ const startPromise = this.withTimeout(this.waitForStart(), START_TIMEOUT);
6442
+ this.postMessage({
6443
+ type: "start",
6444
+ __vitest_worker_request__: true,
6445
+ options: { reportMemory: this.worker.reportMemory ?? false }
6446
+ }), await startPromise, this._state = RunnerState.STARTED;
6447
+ } catch (error) {
6448
+ throw this._state = RunnerState.IDLE, error;
6449
+ } finally {
6450
+ this._operationLock.resolve(), this._operationLock = null;
6451
+ }
6452
+ }
6453
+ }
6454
+ async stop() {
6455
+ // Wait for any ongoing operation to complete
6456
+ if (this._operationLock) await this._operationLock;
6457
+ if (!(this._state === RunnerState.STOPPED || this._state === RunnerState.STOPPING)) {
6458
+ if (this._state === RunnerState.IDLE) {
6459
+ this._state = RunnerState.STOPPED;
6460
+ return;
6461
+ }
6462
+ // Create operation lock to prevent concurrent start/stop
6463
+ this._operationLock = createDefer();
6464
+ try {
6465
+ this._state = RunnerState.STOPPING, this.worker.off("exit", this.emitUnexpectedExit), await this.withTimeout(new Promise((resolve) => {
6466
+ const onStop = (response) => {
6467
+ if (response.type === "stopped") {
6468
+ if (response.error) this.project.vitest.state.catchError(response.error, "Teardown Error");
6469
+ resolve(), this.off("message", onStop);
6470
+ }
6471
+ };
6472
+ this.on("message", onStop), this.postMessage({
6473
+ type: "stop",
6474
+ __vitest_worker_request__: true
6475
+ });
6476
+ }), STOP_TIMEOUT), this._eventEmitter.removeAllListeners(), this._rpc.$close(/* @__PURE__ */ new Error("[vitest-pool-runner]: Pending methods while closing rpc")), await this.worker.stop(), this._state = RunnerState.STOPPED;
6477
+ } catch (error) {
6478
+ throw this._state = RunnerState.STOPPED, error;
6479
+ } finally {
6480
+ this._operationLock.resolve(), this._operationLock = null;
6481
+ }
6482
+ }
6483
+ }
6484
+ on(event, callback) {
6485
+ this._eventEmitter.on(event, callback);
6486
+ }
6487
+ off(event, callback) {
6488
+ this._eventEmitter.off(event, callback);
6489
+ }
6490
+ emitWorkerError = (maybeError) => {
6491
+ const error = maybeError instanceof Error ? maybeError : new Error(String(maybeError));
6492
+ this._eventEmitter.emit("error", error);
6493
+ };
6494
+ emitWorkerMessage = (response) => {
6495
+ try {
6496
+ const message = this.worker.deserialize(response);
6497
+ if (typeof message === "object" && message != null && message.__vitest_worker_response__) this._eventEmitter.emit("message", message);
6498
+ else this._eventEmitter.emit("rpc", message);
6499
+ } catch (error) {
6500
+ this._eventEmitter.emit("error", error);
6501
+ }
6502
+ };
6503
+ emitUnexpectedExit = () => {
6504
+ const error = /* @__PURE__ */ new Error("Worker exited unexpectedly");
6505
+ this._eventEmitter.emit("error", error);
6506
+ };
6507
+ waitForStart() {
6508
+ return new Promise((resolve) => {
6509
+ const onStart = (message) => {
6510
+ if (message.type === "started") this.off("message", onStart), resolve();
6511
+ };
6512
+ this.on("message", onStart);
6513
+ });
6514
+ }
6515
+ withTimeout(promise, timeout) {
6516
+ return new Promise((resolve_, reject_) => {
6517
+ const timer = setTimeout(() => reject(/* @__PURE__ */ new Error("[vitest-pool-runner]: Timeout waiting for worker to respond")), timeout);
6518
+ function resolve(value) {
6519
+ clearTimeout(timer), resolve_(value);
6520
+ }
6521
+ function reject(error) {
6522
+ clearTimeout(timer), reject_(error);
6523
+ }
6524
+ promise.then(resolve, reject);
6525
+ });
6526
+ }
6527
+ }
6528
+
6529
+ const SIGKILL_TIMEOUT = 500;
6530
+ /** @experimental */
6531
+ class ForksPoolWorker {
6532
+ name = "forks";
6533
+ cacheFs = true;
6534
+ entrypoint;
6535
+ execArgv;
6536
+ env;
6537
+ _fork;
6538
+ constructor(options) {
6539
+ /** Loads {@link file://./../../../runtime/workers/forks.ts} */
6540
+ this.execArgv = options.execArgv, this.env = options.env, this.entrypoint = resolve$1(options.distPath, "workers/forks.js");
6541
+ }
6542
+ on(event, callback) {
6543
+ this.fork.on(event, callback);
6544
+ }
6545
+ off(event, callback) {
6546
+ this.fork.off(event, callback);
6547
+ }
6548
+ send(message) {
6549
+ if ("context" in message) message = {
6550
+ ...message,
6551
+ context: {
6552
+ ...message.context,
6553
+ config: wrapSerializableConfig(message.context.config)
6554
+ }
6555
+ };
6556
+ this.fork.send(v8.serialize(message));
6557
+ }
6558
+ async start() {
6559
+ this._fork ||= fork(this.entrypoint, [], {
6560
+ env: this.env,
6561
+ execArgv: this.execArgv
6562
+ });
6563
+ }
6564
+ async stop() {
6565
+ const fork = this.fork, waitForExit = new Promise((resolve) => {
6566
+ if (fork.exitCode != null) resolve();
6567
+ else fork.once("exit", resolve);
6568
+ }), sigkillTimeout = setTimeout(() => fork.kill("SIGKILL"), SIGKILL_TIMEOUT);
6569
+ fork.kill(), await waitForExit, clearTimeout(sigkillTimeout), this._fork = void 0;
6570
+ }
6571
+ deserialize(data) {
6572
+ try {
6573
+ return v8.deserialize(Buffer.from(data));
6574
+ } catch (error) {
6575
+ let stringified = "";
6576
+ try {
6577
+ stringified = `\nReceived value: ${JSON.stringify(data)}`;
6578
+ } catch {}
6579
+ throw new Error(`[vitest-pool]: Unexpected call to process.send(). Make sure your test cases are not interfering with process's channel.${stringified}`, { cause: error });
6580
+ }
6581
+ }
6582
+ get fork() {
6583
+ if (!this._fork) throw new Error(`The child process was torn down or never initialized. This is a bug in Vitest.`);
6584
+ return this._fork;
6585
+ }
6586
+ }
6587
+ /**
6588
+ * Prepares `SerializedConfig` for serialization, e.g. `node:v8.serialize`
6589
+ * - Unwrapping done in {@link file://./../../../runtime/workers/init-forks.ts}
6590
+ */
6591
+ function wrapSerializableConfig(config) {
6592
+ let testNamePattern = config.testNamePattern, defines = config.defines;
6593
+ // v8 serialize does not support regex
6594
+ if (testNamePattern && typeof testNamePattern !== "string") testNamePattern = `$$vitest:${testNamePattern.toString()}`;
6595
+ // v8 serialize drops properties with undefined value
6596
+ if (defines) defines = {
6597
+ keys: Object.keys(defines),
6598
+ original: defines
6599
+ };
6600
+ return {
6601
+ ...config,
6602
+ testNamePattern,
6603
+ defines
6604
+ };
6605
+ }
6606
+
6607
+ /** @experimental */
6608
+ class ThreadsPoolWorker {
6609
+ name = "threads";
6610
+ entrypoint;
6611
+ execArgv;
6612
+ env;
6613
+ _thread;
6614
+ constructor(options) {
6615
+ /** Loads {@link file://./../../../runtime/workers/threads.ts} */
6616
+ this.execArgv = options.execArgv, this.env = options.env, this.entrypoint = resolve$1(options.distPath, "workers/threads.js");
6617
+ }
6618
+ on(event, callback) {
6619
+ this.thread.on(event, callback);
6620
+ }
6621
+ off(event, callback) {
6622
+ this.thread.off(event, callback);
6623
+ }
6624
+ send(message) {
6625
+ this.thread.postMessage(message);
6626
+ }
6627
+ async start() {
6628
+ // This can be called multiple times if the runtime is shared.
6629
+ this._thread ||= new Worker(this.entrypoint, {
6630
+ env: this.env,
6631
+ execArgv: this.execArgv
6632
+ });
6633
+ }
6634
+ async stop() {
6635
+ await this.thread.terminate().then(() => {
6636
+ this._thread = void 0;
6637
+ });
6638
+ }
6639
+ deserialize(data) {
6640
+ return data;
6641
+ }
6642
+ get thread() {
6643
+ if (!this._thread) throw new Error(`The worker thread was torn down or never initialized. This is a bug in Vitest.`);
6644
+ return this._thread;
6645
+ }
6646
+ }
6647
+
6648
+ /** @experimental */
6649
+ class TypecheckPoolWorker {
6650
+ name = "typecheck";
6651
+ project;
6652
+ _eventEmitter = new EventEmitter$1();
6653
+ constructor(options) {
6654
+ this.project = options.project;
6655
+ }
6656
+ async start() {
6657
+ // noop, onMessage handles it
6658
+ }
6659
+ async stop() {
6660
+ // noop, onMessage handles it
6661
+ }
6662
+ canReuse() {
6663
+ return true;
6664
+ }
6665
+ send(message) {
6666
+ onMessage(message, this.project).then((response) => {
6667
+ if (response) this._eventEmitter.emit("message", response);
6668
+ });
6669
+ }
6670
+ on(event, callback) {
6671
+ this._eventEmitter.on(event, callback);
6672
+ }
6673
+ off(event, callback) {
6674
+ this._eventEmitter.on(event, callback);
6675
+ }
6676
+ deserialize(data) {
6677
+ return data;
6678
+ }
6679
+ }
6680
+ const __vitest_worker_response__ = true, runners = /* @__PURE__ */ new WeakMap();
6681
+ async function onMessage(message, project) {
6682
+ if (message?.__vitest_worker_request__ !== true) return;
6683
+ let runner = runners.get(project.vitest);
6684
+ if (!runner) runner = createRunner(project.vitest), runners.set(project.vitest, runner);
6685
+ let runPromise;
6686
+ switch (message.type) {
6687
+ case "start": return {
6688
+ type: "started",
6689
+ __vitest_worker_response__
6690
+ };
6691
+ case "run": return runPromise = runner.runTests(message.context.files, project).catch((error) => error), {
6692
+ type: "testfileFinished",
6693
+ error: await runPromise,
6694
+ __vitest_worker_response__
6695
+ };
6696
+ case "collect": return runPromise = runner.collectTests(message.context.files, project).catch((error) => error), {
6697
+ type: "testfileFinished",
6698
+ error: await runPromise,
6699
+ __vitest_worker_response__
6700
+ };
6701
+ case "stop": return await runPromise, await project.typechecker?.stop(), {
6702
+ type: "stopped",
6703
+ __vitest_worker_response__
6704
+ };
6705
+ }
6706
+ throw new Error(`Unexpected message ${JSON.stringify(message, null, 2)}`);
6707
+ }
6708
+ function createRunner(vitest) {
6709
+ const promisesMap = /* @__PURE__ */ new WeakMap(), rerunTriggered = /* @__PURE__ */ new WeakSet();
6710
+ async function onParseEnd(project, { files, sourceErrors }) {
6711
+ const checker = project.typechecker, { packs, events } = checker.getTestPacksAndEvents();
6712
+ if (await vitest._testRun.updated(packs, events), !project.config.typecheck.ignoreSourceErrors) sourceErrors.forEach((error) => vitest.state.catchError(error, "Unhandled Source Error"));
6713
+ if (!hasFailed(files) && !sourceErrors.length && checker.getExitCode()) {
6714
+ const error = new Error(checker.getOutput());
6715
+ error.stack = "", vitest.state.catchError(error, "Typecheck Error");
6716
+ }
6717
+ // triggered by TSC watcher, not Vitest watcher, so we need to emulate what Vitest does in this case
6718
+ if (promisesMap.get(project)?.resolve(), rerunTriggered.delete(project), vitest.config.watch && !vitest.runningPromise) {
6719
+ const modules = files.map((file) => vitest.state.getReportedEntity(file)).filter((e) => e?.type === "module"), state = vitest.isCancelling ? "interrupted" : modules.some((m) => !m.ok()) ? "failed" : "passed";
6720
+ await vitest.report("onTestRunEnd", modules, [], state), await vitest.report("onWatcherStart", files, [...project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors, ...vitest.state.getUnhandledErrors()]);
6721
+ }
6722
+ }
6723
+ async function createWorkspaceTypechecker(project, files) {
6724
+ const checker = project.typechecker ?? new Typechecker(project);
6725
+ return project.typechecker ? checker : (project.typechecker = checker, checker.setFiles(files), checker.onParseStart(async () => {
6726
+ const files = checker.getTestFiles();
6727
+ for (const file of files) await vitest._testRun.enqueued(project, file);
6728
+ await vitest._testRun.collected(project, files);
6729
+ }), checker.onParseEnd((result) => onParseEnd(project, result)), checker.onWatcherRerun(async () => {
6730
+ if (rerunTriggered.add(project), !vitest.runningPromise) vitest.state.clearErrors(), await vitest.report("onWatcherRerun", files, "File change detected. Triggering rerun.");
6731
+ await checker.collectTests();
6732
+ const testFiles = checker.getTestFiles();
6733
+ for (const file of testFiles) await vitest._testRun.enqueued(project, file);
6734
+ await vitest._testRun.collected(project, testFiles);
6735
+ const { packs, events } = checker.getTestPacksAndEvents();
6736
+ await vitest._testRun.updated(packs, events);
6737
+ }), checker);
6738
+ }
6739
+ async function startTypechecker(project, files) {
6740
+ if (project.typechecker) return;
6741
+ const checker = await createWorkspaceTypechecker(project, files);
6742
+ await checker.collectTests(), await checker.start();
6743
+ }
6744
+ async function collectTests(specs, project) {
6745
+ const files = specs.map((spec) => spec.filepath), checker = await createWorkspaceTypechecker(project, files);
6746
+ checker.setFiles(files), await checker.collectTests();
6747
+ const testFiles = checker.getTestFiles();
6748
+ vitest.state.collectFiles(project, testFiles);
6749
+ }
6750
+ async function runTests(specs, project) {
6751
+ const promises = [], files = specs.map((spec) => spec.filepath), promise = createDefer(), triggered = await new Promise((resolve) => {
6752
+ const _i = setInterval(() => {
6753
+ if (!project.typechecker || rerunTriggered.has(project)) resolve(true), clearInterval(_i);
6754
+ });
6755
+ setTimeout(() => {
6756
+ resolve(false), clearInterval(_i);
6757
+ }, 500).unref();
6758
+ });
6759
+ if (project.typechecker && !triggered) {
6760
+ const testFiles = project.typechecker.getTestFiles();
6761
+ for (const file of testFiles) await vitest._testRun.enqueued(project, file);
6762
+ await vitest._testRun.collected(project, testFiles), await onParseEnd(project, project.typechecker.getResult());
6763
+ }
6764
+ promises.push(promise), promisesMap.set(project, promise), promises.push(startTypechecker(project, files)), await Promise.all(promises);
6765
+ }
6766
+ return {
6767
+ runTests,
6768
+ collectTests
6769
+ };
6770
+ }
6771
+
6772
+ /** @experimental */
6773
+ class VmForksPoolWorker extends ForksPoolWorker {
6774
+ name = "vmForks";
6775
+ reportMemory = true;
6776
+ entrypoint;
6777
+ constructor(options) {
6778
+ /** Loads {@link file://./../../../runtime/workers/vmForks.ts} */
6779
+ super(options), this.execArgv.push("--experimental-vm-modules"), this.entrypoint = resolve$1(options.distPath, "workers/vmForks.js");
6780
+ }
6781
+ }
6782
+
6783
+ /** @experimental */
6784
+ class VmThreadsPoolWorker extends ThreadsPoolWorker {
6785
+ name = "vmThreads";
6786
+ reportMemory = true;
6787
+ entrypoint;
6788
+ constructor(options) {
6789
+ /** Loads {@link file://./../../../runtime/workers/vmThreads.ts} */
6790
+ super(options), this.execArgv.push("--experimental-vm-modules"), this.entrypoint = resolve$1(options.distPath, "workers/vmThreads.js");
6791
+ }
6792
+ }
6793
+
6794
+ const WORKER_START_TIMEOUT = 5e3;
6795
+ class Pool {
6796
+ maxWorkers = 0;
6797
+ workerIds = /* @__PURE__ */ new Map();
6798
+ queue = [];
6799
+ activeTasks = [];
6800
+ sharedRunners = [];
6801
+ exitPromises = [];
6802
+ _isCancelling = false;
6803
+ constructor(options, logger) {
6804
+ this.options = options, this.logger = logger;
6805
+ }
6806
+ setMaxWorkers(maxWorkers) {
6807
+ this.maxWorkers = maxWorkers, this.workerIds = new Map(Array.from({ length: maxWorkers }).fill(0).map((_, i) => [i + 1, true]));
6808
+ }
6809
+ async run(task, method) {
6810
+ // Prevent new tasks from being queued during cancellation
6811
+ if (this._isCancelling) throw new Error("[vitest-pool]: Cannot run tasks while pool is cancelling");
6812
+ // Every runner related failure should make this promise reject so that it's picked by pool.
6813
+ // This resolver is used to make the error handling in recursive queue easier.
6814
+ const testFinish = withResolvers();
6815
+ this.queue.push({
6816
+ task,
6817
+ resolver: testFinish,
6818
+ method
6819
+ }), this.schedule(), await testFinish.promise;
6820
+ }
6821
+ async schedule() {
6822
+ if (this.queue.length === 0 || this.activeTasks.length >= this.maxWorkers) return;
6823
+ const { task, resolver, method } = this.queue.shift();
6824
+ try {
6825
+ let isMemoryLimitReached = false;
6826
+ const runner = this.getPoolRunner(task, method), activeTask = {
6827
+ task,
6828
+ resolver,
6829
+ method,
6830
+ cancelTask
6831
+ };
6832
+ this.activeTasks.push(activeTask), runner.on("error", (error) => {
6833
+ resolver.reject(new Error(`[vitest-pool]: Worker ${task.worker} emitted error.`, { cause: error }));
6834
+ });
6835
+ async function cancelTask() {
6836
+ await runner.stop(), resolver.reject(/* @__PURE__ */ new Error("Cancelled"));
6837
+ }
6838
+ const onFinished = (message) => {
6839
+ if (message?.__vitest_worker_response__ && message.type === "testfileFinished") {
6840
+ if (task.memoryLimit && message.usedMemory) isMemoryLimitReached = message.usedMemory >= task.memoryLimit;
6841
+ if (message.error) this.options.state.catchError(message.error, "Test Run Error");
6842
+ runner.off("message", onFinished), resolver.resolve();
6843
+ }
6844
+ };
6845
+ if (runner.on("message", onFinished), !runner.isStarted) {
6846
+ const id = setTimeout(() => resolver.reject(/* @__PURE__ */ new Error(`[vitest-pool]: Timeout starting ${task.worker} runner.`)), WORKER_START_TIMEOUT);
6847
+ await runner.start().finally(() => clearTimeout(id));
6848
+ }
6849
+ const poolId = runner.poolId ?? this.getWorkerId();
6850
+ runner.poolId = poolId, runner.postMessage({
6851
+ __vitest_worker_request__: true,
6852
+ type: method,
6853
+ context: task.context,
6854
+ poolId
6855
+ }), await resolver.promise;
6856
+ const index = this.activeTasks.indexOf(activeTask);
6857
+ if (index !== -1) this.activeTasks.splice(index, 1);
6858
+ if (!task.isolate && !isMemoryLimitReached && this.queue[0]?.task.isolate === false && isEqualRunner(runner, this.queue[0].task)) return this.sharedRunners.push(runner), this.schedule();
6859
+ // Runner terminations are started but not awaited until the end of full run.
6860
+ // Runner termination can also already start from task cancellation.
6861
+ if (!runner.isTerminated) {
6862
+ const id = setTimeout(() => this.logger.error(`[vitest-pool]: Timeout terminating ${task.worker} worker for test files ${formatFiles(task)}.`), this.options.teardownTimeout);
6863
+ this.exitPromises.push(runner.stop().then(() => clearTimeout(id)).catch((error) => this.logger.error(`[vitest-pool]: Failed to terminate ${task.worker} worker for test files ${formatFiles(task)}.`, error)));
6864
+ }
6865
+ this.freeWorkerId(poolId);
6866
+ }
6867
+ // This is mostly to avoid zombie workers when/if Vitest internals run into errors
6868
+ catch (error) {
6869
+ return resolver.reject(error);
6870
+ }
6871
+ return this.schedule();
6872
+ }
6873
+ async cancel() {
6874
+ // Set flag to prevent new tasks from being queued
6875
+ this._isCancelling = true;
6876
+ const pendingTasks = this.queue.splice(0);
6877
+ if (pendingTasks.length) {
6878
+ const error = /* @__PURE__ */ new Error("Cancelled");
6879
+ pendingTasks.forEach((task) => task.resolver.reject(error));
6880
+ }
6881
+ const activeTasks = this.activeTasks.splice(0);
6882
+ await Promise.all(activeTasks.map((task) => task.cancelTask()));
6883
+ const sharedRunners = this.sharedRunners.splice(0);
6884
+ // Reset flag after cancellation completes
6885
+ await Promise.all(sharedRunners.map((runner) => runner.stop())), await Promise.all(this.exitPromises.splice(0)), this.workerIds.forEach((_, id) => this.freeWorkerId(id)), this._isCancelling = false;
6886
+ }
6887
+ async close() {
6888
+ await this.cancel();
6889
+ }
6890
+ getPoolRunner(task, method) {
6891
+ if (task.isolate === false) {
6892
+ const index = this.sharedRunners.findIndex((runner) => isEqualRunner(runner, task));
6893
+ if (index !== -1) return this.sharedRunners.splice(index, 1)[0];
6894
+ }
6895
+ const options = {
6896
+ distPath: this.options.distPath,
6897
+ project: task.project,
6898
+ method,
6899
+ environment: task.context.environment.name,
6900
+ env: task.env,
6901
+ execArgv: task.execArgv
6902
+ };
6903
+ switch (task.worker) {
6904
+ case "forks": return new PoolRunner(options, new ForksPoolWorker(options));
6905
+ case "vmForks": return new PoolRunner(options, new VmForksPoolWorker(options));
6906
+ case "threads": return new PoolRunner(options, new ThreadsPoolWorker(options));
6907
+ case "vmThreads": return new PoolRunner(options, new VmThreadsPoolWorker(options));
6908
+ case "typescript": return new PoolRunner(options, new TypecheckPoolWorker(options));
6909
+ }
6910
+ const customPool = task.project.config.poolRunner;
6911
+ if (customPool != null && customPool.name === task.worker) return new PoolRunner(options, customPool.createPoolWorker(options));
6912
+ throw new Error(`Runner ${task.worker} is not supported. Test files: ${formatFiles(task)}.`);
6913
+ }
6914
+ getWorkerId() {
6915
+ let workerId = 0;
6916
+ return this.workerIds.forEach((state, id) => {
6917
+ if (state && !workerId) workerId = id, this.workerIds.set(id, false);
6918
+ }), workerId;
6919
+ }
6920
+ freeWorkerId(id) {
6921
+ this.workerIds.set(id, true);
6922
+ }
6923
+ }
6924
+ function withResolvers() {
6925
+ let resolve = () => {}, reject = (_error) => {};
6926
+ const promise = new Promise((res, rej) => {
6927
+ resolve = res, reject = rej;
6928
+ });
6929
+ return {
6930
+ resolve,
6931
+ reject,
6932
+ promise
6933
+ };
6934
+ }
6935
+ function formatFiles(task) {
6936
+ return task.context.files.map((file) => file.filepath).join(", ");
6937
+ }
6938
+ function isEqualRunner(runner, task) {
6939
+ if (task.isolate) throw new Error("Isolated tasks should not share runners");
6940
+ return runner.worker.name === task.worker && runner.project === task.project && runner.environment === task.context.environment.name && (!runner.worker.canReuse || runner.worker.canReuse(task));
6941
+ }
6942
+
6943
+ const suppressWarningsPath = resolve(rootDir, "./suppress-warnings.cjs");
6944
+ function getFilePoolName(project) {
6945
+ return project.config.browser.enabled ? "browser" : project.config.pool;
6946
+ }
6947
+ function createPool(ctx) {
6948
+ const pool = new Pool({
6949
+ distPath: ctx.distPath,
6950
+ teardownTimeout: ctx.config.teardownTimeout,
6951
+ state: ctx.state
6952
+ }, ctx.logger), options = resolveOptions(ctx), Sequencer = ctx.config.sequence.sequencer, sequencer = new Sequencer(ctx);
6953
+ let browserPool;
6954
+ async function executeTests(method, specs, invalidates) {
6955
+ if (ctx.onCancel(() => pool.cancel()), ctx.config.shard) {
6956
+ if (!ctx.config.passWithNoTests && ctx.config.shard.count > specs.length) throw new Error(`--shard <count> must be a smaller than count of test files. Resolved ${specs.length} test files for --shard=${ctx.config.shard.index}/${ctx.config.shard.count}.`);
6957
+ specs = await sequencer.shard(Array.from(specs));
6958
+ }
6959
+ const taskGroups = [];
6960
+ let workerId = 0;
6961
+ const sorted = await sequencer.sort(specs), environments = await getSpecificationsEnvironments(specs), groups = groupSpecs(sorted);
6962
+ for (const group of groups) {
6963
+ if (!group) continue;
6964
+ const taskGroup = [], browserSpecs = [];
6965
+ taskGroups.push({
6966
+ tasks: taskGroup,
6967
+ maxWorkers: group.maxWorkers,
6968
+ browserSpecs
6969
+ });
6970
+ for (const specs of group.specs) {
6971
+ const { project, pool } = specs[0];
6972
+ if (pool === "browser") {
6973
+ browserSpecs.push(...specs);
6974
+ continue;
6975
+ }
6976
+ const environment = environments.get(specs[0]);
6977
+ if (!environment) throw new Error(`Cannot find the environment. This is a bug in Vitest.`);
6978
+ taskGroup.push({
6979
+ context: {
6980
+ pool,
6981
+ config: project.serializedConfig,
6982
+ files: specs.map((spec) => ({
6983
+ filepath: spec.moduleId,
6984
+ testLocations: spec.testLines
6985
+ })),
6986
+ invalidates,
6987
+ environment,
6988
+ projectName: project.name,
6989
+ providedContext: project.getProvidedContext(),
6990
+ workerId: workerId++
6991
+ },
6992
+ project,
6993
+ env: {
6994
+ ...options.env,
6995
+ ...project.config.env
6996
+ },
6997
+ execArgv: [...options.execArgv, ...project.config.execArgv],
6998
+ worker: pool,
6999
+ isolate: project.config.isolate,
7000
+ memoryLimit: getMemoryLimit(ctx.config, pool) ?? null
7001
+ });
7002
+ }
7003
+ }
7004
+ const results = [];
7005
+ for (const { tasks, browserSpecs, maxWorkers } of taskGroups) {
7006
+ pool.setMaxWorkers(maxWorkers);
7007
+ const promises = tasks.map(async (task) => {
7008
+ if (ctx.isCancelling) return ctx.state.cancelFiles(task.context.files, task.project);
7009
+ try {
7010
+ await pool.run(task, method);
7011
+ } catch (error) {
7012
+ // Intentionally cancelled
7013
+ if (ctx.isCancelling && error instanceof Error && error.message === "Cancelled") ctx.state.cancelFiles(task.context.files, task.project);
7014
+ else throw error;
7015
+ }
7016
+ });
7017
+ if (browserSpecs.length) if (browserPool ??= createBrowserPool(ctx), method === "collect") promises.push(browserPool.collectTests(browserSpecs));
7018
+ else promises.push(browserPool.runTests(browserSpecs));
7019
+ const groupResults = await Promise.allSettled(promises);
7020
+ results.push(...groupResults);
7021
+ }
7022
+ const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
7023
+ if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
7024
+ }
7025
+ return {
7026
+ name: "default",
7027
+ runTests: (files, invalidates) => executeTests("run", files, invalidates),
7028
+ collectTests: (files, invalidates) => executeTests("collect", files, invalidates),
7029
+ async close() {
7030
+ await Promise.all([pool.close(), browserPool?.close?.()]);
7031
+ }
7032
+ };
7033
+ }
7034
+ function resolveOptions(ctx) {
7035
+ // in addition to resolve.conditions Vite also adds production/development,
7036
+ // see: https://github.com/vitejs/vite/blob/af2aa09575229462635b7cbb6d248ca853057ba2/packages/vite/src/node/plugins/resolve.ts#L1056-L1080
7037
+ const viteMajor = Number(version.split(".")[0]), conditions = [...new Set(viteMajor >= 6 ? ctx.vite.config.ssr.resolve?.conditions ?? [] : [
7038
+ "production",
7039
+ "development",
7040
+ ...ctx.vite.config.resolve.conditions
7041
+ ])].filter((condition) => {
7042
+ return condition === "production" ? ctx.vite.config.isProduction : condition === "development" ? !ctx.vite.config.isProduction : true;
7043
+ }).map((condition) => {
7044
+ return viteMajor >= 6 && condition === "development|production" ? ctx.vite.config.isProduction ? "production" : "development" : condition;
7045
+ }).flatMap((c) => ["--conditions", c]), options = {
7046
+ execArgv: [
7047
+ ...process.execArgv.filter((execArg) => execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir")),
7048
+ ...conditions,
7049
+ "--experimental-import-meta-resolve",
7050
+ "--require",
7051
+ suppressWarningsPath
7052
+ ],
7053
+ env: {
7054
+ TEST: "true",
7055
+ VITEST: "true",
7056
+ NODE_ENV: process.env.NODE_ENV || "test",
7057
+ VITEST_MODE: ctx.config.watch ? "WATCH" : "RUN",
7058
+ FORCE_TTY: isatty(1) ? "true" : "",
7059
+ ...process.env,
7060
+ ...ctx.config.env
7061
+ }
7062
+ };
7063
+ // env are case-insensitive on Windows, but spawned processes don't support it
7064
+ if (isWindows) for (const name in options.env) options.env[name.toUpperCase()] = options.env[name];
7065
+ return options;
7066
+ }
7067
+ function resolveMaxWorkers(project) {
7068
+ if (project.config.maxWorkers) return project.config.maxWorkers;
7069
+ if (project.vitest.config.maxWorkers) return project.vitest.config.maxWorkers;
7070
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
7071
+ return project.vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
7072
+ }
7073
+ function getMemoryLimit(config, pool) {
7074
+ if (pool !== "vmForks" && pool !== "vmThreads") return null;
7075
+ const memory = nodeos.totalmem(), limit = getWorkerMemoryLimit(config);
7076
+ // just ignore "memoryLimit" value because we cannot detect memory limit
7077
+ return typeof memory === "number" ? stringToBytes(limit, config.watch ? memory / 2 : memory) : typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%" ? stringToBytes(limit) : null;
7078
+ }
7079
+ function groupSpecs(specs) {
7080
+ const groups = [], sequential = {
7081
+ specs: [],
7082
+ maxWorkers: 1
7083
+ }, typechecks = {};
7084
+ specs.forEach((spec) => {
7085
+ if (spec.pool === "typescript") {
7086
+ typechecks[spec.project.name] ||= [], typechecks[spec.project.name].push(spec);
7087
+ return;
7088
+ }
7089
+ const order = spec.project.config.sequence.groupOrder;
7090
+ // Files that have disabled parallelism and default groupId are set into their own group
7091
+ if (order === 0 && spec.project.config.fileParallelism === false) return sequential.specs.push([spec]);
7092
+ const maxWorkers = resolveMaxWorkers(spec.project);
7093
+ // Multiple projects with different maxWorkers but same groupId
7094
+ if (groups[order] ||= {
7095
+ specs: [],
7096
+ maxWorkers
7097
+ }, groups[order].maxWorkers !== maxWorkers) {
7098
+ const last = groups[order].specs.at(-1)?.at(-1)?.project.name;
7099
+ throw new Error(`Projects "${last}" and "${spec.project.name}" have different 'maxWorkers' but same 'sequence.groupId'.\nProvide unique 'sequence.groupId' for them.`);
7100
+ }
7101
+ groups[order].specs.push([spec]);
7102
+ });
7103
+ let order = Math.max(0, ...groups.keys()) + 1;
7104
+ for (const projectName in typechecks) {
7105
+ const maxWorkers = resolveMaxWorkers(typechecks[projectName][0].project), previous = groups[order - 1];
7106
+ if (previous && previous.typecheck && maxWorkers !== previous.maxWorkers) order += 1;
7107
+ groups[order] ||= {
7108
+ specs: [],
7109
+ maxWorkers,
7110
+ typecheck: true
7111
+ }, groups[order].specs.push(typechecks[projectName]);
7112
+ }
7113
+ if (sequential.specs.length) groups.push(sequential);
7114
+ return groups;
7115
+ }
7116
+
7117
+ function serializeConfig(project) {
7118
+ const { config, globalConfig } = project, viteConfig = project._vite?.config, optimizer = config.deps?.optimizer || {};
5871
7119
  return {
5872
7120
  environmentOptions: config.environmentOptions,
5873
7121
  mode: config.mode,
5874
7122
  isolate: config.isolate,
7123
+ fileParallelism: config.fileParallelism,
7124
+ maxWorkers: config.maxWorkers,
5875
7125
  base: config.base,
5876
7126
  logHeapUsage: config.logHeapUsage,
5877
7127
  runner: config.runner,
@@ -5911,18 +7161,6 @@ function serializeConfig(config, coreConfig, viteConfig) {
5911
7161
  };
5912
7162
  })(config.coverage),
5913
7163
  fakeTimers: config.fakeTimers,
5914
- poolOptions: {
5915
- forks: {
5916
- singleFork: poolOptions?.forks?.singleFork ?? coreConfig.poolOptions?.forks?.singleFork ?? false,
5917
- isolate: poolOptions?.forks?.isolate ?? isolate ?? coreConfig.poolOptions?.forks?.isolate ?? true
5918
- },
5919
- threads: {
5920
- singleThread: poolOptions?.threads?.singleThread ?? coreConfig.poolOptions?.threads?.singleThread ?? false,
5921
- isolate: poolOptions?.threads?.isolate ?? isolate ?? coreConfig.poolOptions?.threads?.isolate ?? true
5922
- },
5923
- vmThreads: { singleThread: poolOptions?.vmThreads?.singleThread ?? coreConfig.poolOptions?.vmThreads?.singleThread ?? false },
5924
- vmForks: { singleFork: poolOptions?.vmForks?.singleFork ?? coreConfig.poolOptions?.vmForks?.singleFork ?? false }
5925
- },
5926
7164
  deps: {
5927
7165
  web: config.deps.web || {},
5928
7166
  optimizer: Object.entries(optimizer).reduce((acc, [name, option]) => {
@@ -5933,27 +7171,28 @@ function serializeConfig(config, coreConfig, viteConfig) {
5933
7171
  },
5934
7172
  snapshotOptions: {
5935
7173
  snapshotEnvironment: void 0,
5936
- updateSnapshot: coreConfig.snapshotOptions.updateSnapshot,
5937
- snapshotFormat: { ...coreConfig.snapshotOptions.snapshotFormat },
5938
- expand: config.snapshotOptions.expand ?? coreConfig.snapshotOptions.expand
7174
+ updateSnapshot: globalConfig.snapshotOptions.updateSnapshot,
7175
+ snapshotFormat: { ...globalConfig.snapshotOptions.snapshotFormat },
7176
+ expand: config.snapshotOptions.expand ?? globalConfig.snapshotOptions.expand
5939
7177
  },
5940
7178
  sequence: {
5941
- shuffle: coreConfig.sequence.shuffle,
5942
- concurrent: coreConfig.sequence.concurrent,
5943
- seed: coreConfig.sequence.seed,
5944
- hooks: coreConfig.sequence.hooks,
5945
- setupFiles: coreConfig.sequence.setupFiles
7179
+ shuffle: globalConfig.sequence.shuffle,
7180
+ concurrent: globalConfig.sequence.concurrent,
7181
+ seed: globalConfig.sequence.seed,
7182
+ hooks: globalConfig.sequence.hooks,
7183
+ setupFiles: globalConfig.sequence.setupFiles
5946
7184
  },
5947
- inspect: coreConfig.inspect,
5948
- inspectBrk: coreConfig.inspectBrk,
5949
- inspector: coreConfig.inspector,
7185
+ inspect: globalConfig.inspect,
7186
+ inspectBrk: globalConfig.inspectBrk,
7187
+ inspector: globalConfig.inspector,
5950
7188
  watch: config.watch,
5951
- includeTaskLocation: config.includeTaskLocation ?? coreConfig.includeTaskLocation,
7189
+ includeTaskLocation: config.includeTaskLocation ?? globalConfig.includeTaskLocation,
5952
7190
  env: {
5953
7191
  ...viteConfig?.env,
5954
7192
  ...config.env
5955
7193
  },
5956
7194
  browser: ((browser) => {
7195
+ const provider = project.browser?.provider;
5957
7196
  return {
5958
7197
  name: browser.name,
5959
7198
  headless: browser.headless,
@@ -5963,13 +7202,15 @@ function serializeConfig(config, coreConfig, viteConfig) {
5963
7202
  viewport: browser.viewport,
5964
7203
  screenshotFailures: browser.screenshotFailures,
5965
7204
  locators: { testIdAttribute: browser.locators.testIdAttribute },
5966
- providerOptions: browser.provider === "playwright" ? { actionTimeout: browser.providerOptions?.context?.actionTimeout } : {},
5967
- trackUnhandledErrors: browser.trackUnhandledErrors ?? true
7205
+ providerOptions: provider?.name === "playwright" ? { actionTimeout: provider?.options?.actionTimeout } : {},
7206
+ trackUnhandledErrors: browser.trackUnhandledErrors ?? true,
7207
+ trace: browser.trace.mode
5968
7208
  };
5969
7209
  })(config.browser),
5970
7210
  standalone: config.standalone,
5971
- printConsoleTrace: config.printConsoleTrace ?? coreConfig.printConsoleTrace,
5972
- benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples }
7211
+ printConsoleTrace: config.printConsoleTrace ?? globalConfig.printConsoleTrace,
7212
+ benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples },
7213
+ serializedDefines: config.browser.enabled ? "" : project._serializedDefines || ""
5973
7214
  };
5974
7215
  }
5975
7216
 
@@ -5999,6 +7240,7 @@ async function loadGlobalSetupFile(file, runner) {
5999
7240
  function CoverageTransform(ctx) {
6000
7241
  return {
6001
7242
  name: "vitest:coverage-transform",
7243
+ enforce: "post",
6002
7244
  transform(srcCode, id) {
6003
7245
  return ctx.coverageProvider?.onFileTransform?.(srcCode, id, this);
6004
7246
  }
@@ -6498,7 +7740,7 @@ function MetaEnvReplacerPlugin() {
6498
7740
  transform(code, id) {
6499
7741
  if (!/\bimport\.meta\.env\b/.test(code)) return null;
6500
7742
  let s = null;
6501
- const cleanCode = stripLiteral(code), envs = cleanCode.matchAll(/\bimport\.meta\.env\b/g);
7743
+ const envs = stripLiteral(code).matchAll(/\bimport\.meta\.env\b/g);
6502
7744
  for (const env of envs) {
6503
7745
  s ||= new MagicString(code);
6504
7746
  const startIndex = env.index, endIndex = startIndex + env[0].length;
@@ -6630,9 +7872,7 @@ function isInline(id) {
6630
7872
  return cssInlineRE.test(id);
6631
7873
  }
6632
7874
  function getCSSModuleProxyReturn(strategy, filename) {
6633
- if (strategy === "non-scoped") return "style";
6634
- const hash = generateCssFilenameHash(filename);
6635
- return `\`_\${style}_${hash}\``;
7875
+ return strategy === "non-scoped" ? "style" : `\`_\${style}_${generateCssFilenameHash(filename)}\``;
6636
7876
  }
6637
7877
  function CSSEnablerPlugin(ctx) {
6638
7878
  const shouldProcessCSS = (id) => {
@@ -6654,12 +7894,12 @@ function CSSEnablerPlugin(ctx) {
6654
7894
  // return proxy for css modules, so that imported module has names:
6655
7895
  // styles.foo returns a "foo" instead of "undefined"
6656
7896
  // we don't use code content to generate hash for "scoped", because it's empty
6657
- const scopeStrategy = typeof ctx.config.css !== "boolean" && ctx.config.css.modules?.classNameStrategy || "stable", proxyReturn = getCSSModuleProxyReturn(scopeStrategy, relative(ctx.config.root, id)), code = `export default new Proxy(Object.create(null), {
7897
+ const scopeStrategy = typeof ctx.config.css !== "boolean" && ctx.config.css.modules?.classNameStrategy || "stable";
7898
+ return { code: `export default new Proxy(Object.create(null), {
6658
7899
  get(_, style) {
6659
- return ${proxyReturn};
7900
+ return ${getCSSModuleProxyReturn(scopeStrategy, relative(ctx.config.root, id))};
6660
7901
  },
6661
- })`;
6662
- return { code };
7902
+ })` };
6663
7903
  }
6664
7904
  return { code: "export default \"\"" };
6665
7905
  }
@@ -6774,12 +8014,7 @@ function getDefaultResolveOptions() {
6774
8014
  };
6775
8015
  }
6776
8016
  function getDefaultServerConditions() {
6777
- const viteMajor = Number(version.split(".")[0]);
6778
- if (viteMajor >= 6) {
6779
- const conditions = vite.defaultServerConditions;
6780
- return conditions.filter((c) => c !== "module");
6781
- }
6782
- return ["node"];
8017
+ return Number(version.split(".")[0]) >= 6 ? vite.defaultServerConditions.filter((c) => c !== "module") : ["node"];
6783
8018
  }
6784
8019
 
6785
8020
  function ModuleRunnerTransform() {
@@ -6836,16 +8071,17 @@ function VitestProjectResolver(ctx) {
6836
8071
  const plugin = {
6837
8072
  name: "vitest:resolve-root",
6838
8073
  enforce: "pre",
6839
- async resolveId(id, _, { ssr }) {
6840
- if (id === "vitest" || id.startsWith("@vitest/") || id.startsWith("vitest/")) {
6841
- // always redirect the request to the root vitest plugin since
6842
- // it will be the one used to run Vitest
6843
- const resolved = await ctx.vite.pluginContainer.resolveId(id, void 0, {
6844
- skip: new Set([plugin]),
6845
- ssr
6846
- });
6847
- return resolved;
8074
+ config: {
8075
+ order: "post",
8076
+ handler() {
8077
+ return { base: "/" };
6848
8078
  }
8079
+ },
8080
+ async resolveId(id, _, { ssr }) {
8081
+ if (id === "vitest" || id.startsWith("@vitest/") || id.startsWith("vitest/")) return await ctx.vite.pluginContainer.resolveId(id, void 0, {
8082
+ skip: new Set([plugin]),
8083
+ ssr
8084
+ });
6849
8085
  }
6850
8086
  };
6851
8087
  return plugin;
@@ -6854,6 +8090,12 @@ function VitestCoreResolver(ctx) {
6854
8090
  return {
6855
8091
  name: "vitest:resolve-core",
6856
8092
  enforce: "pre",
8093
+ config: {
8094
+ order: "post",
8095
+ handler() {
8096
+ return { base: "/" };
8097
+ }
8098
+ },
6857
8099
  async resolveId(id) {
6858
8100
  if (id === "vitest") return resolve(distDir, "index.js");
6859
8101
  if (id.startsWith("@vitest/") || id.startsWith("vitest/"))
@@ -6880,24 +8122,22 @@ function WorkspaceVitestPlugin(project, options) {
6880
8122
  if (existsSync(pkgJsonPath)) name = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).name;
6881
8123
  if (typeof name !== "string" || !name) name = basename(dir);
6882
8124
  } else name = options.workspacePath.toString();
6883
- const isUserBrowserEnabled = viteConfig.test?.browser?.enabled, isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled), workspaceNames = [name], browser = viteConfig.test.browser || {};
8125
+ const isBrowserEnabled = viteConfig.test?.browser?.enabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled), workspaceNames = [name], browser = viteConfig.test.browser || {};
6884
8126
  if (isBrowserEnabled && browser.name && !browser.instances?.length)
6885
8127
  // vitest injects `instances` in this case later on
6886
8128
  workspaceNames.push(name ? `${name} (${browser.name})` : browser.name);
6887
- viteConfig.test?.browser?.instances?.forEach((instance) => {
6888
- if (instance.name ??= name ? `${name} (${instance.browser})` : instance.browser, isBrowserEnabled) workspaceNames.push(instance.name);
6889
- });
6890
- const filters = project.vitest.config.project;
6891
8129
  // if there is `--project=...` filter, check if any of the potential projects match
6892
8130
  // if projects don't match, we ignore the test project altogether
6893
8131
  // if some of them match, they will later be filtered again by `resolveWorkspace`
6894
- if (filters.length) {
6895
- const hasProject = workspaceNames.some((name) => {
8132
+ if (viteConfig.test?.browser?.instances?.forEach((instance) => {
8133
+ if (instance.name ??= name ? `${name} (${instance.browser})` : instance.browser, isBrowserEnabled) workspaceNames.push(instance.name);
8134
+ }), project.vitest.config.project.length) {
8135
+ if (!workspaceNames.some((name) => {
6896
8136
  return project.vitest.matchesProjectFilter(name);
6897
- });
6898
- if (!hasProject) throw new VitestFilteredOutProjectError();
8137
+ })) throw new VitestFilteredOutProjectError();
6899
8138
  }
6900
8139
  return {
8140
+ base: "/",
6901
8141
  environments: { __vitest__: { dev: {} } },
6902
8142
  test: { name: {
6903
8143
  label: name,
@@ -7036,13 +8276,11 @@ async function isValidNodeImport(id) {
7036
8276
  const extension = extname(id);
7037
8277
  if (BUILTIN_EXTENSIONS.has(extension)) return true;
7038
8278
  if (extension !== ".js") return false;
7039
- id = id.replace("file:///", "");
7040
- const package_ = findNearestPackageData(dirname(id));
7041
- if (package_.type === "module") return true;
8279
+ if (id = id.replace("file:///", ""), findNearestPackageData(dirname(id)).type === "module") return true;
7042
8280
  if (/\.(?:\w+-)?esm?(?:-\w+)?\.js$|\/esm?\//.test(id)) return false;
7043
8281
  try {
7044
8282
  await esModuleLexer.init;
7045
- const code = await promises.readFile(id, "utf8"), [, , , hasModuleSyntax] = esModuleLexer.parse(code);
8283
+ const code = await promises$1.readFile(id, "utf8"), [, , , hasModuleSyntax] = esModuleLexer.parse(code);
7046
8284
  return !hasModuleSyntax;
7047
8285
  } catch {
7048
8286
  return false;
@@ -7062,8 +8300,8 @@ async function _shouldExternalize(id, options) {
7062
8300
  // Unless the user explicitly opted to inline them, externalize Vite deps.
7063
8301
  // They are too big to inline by default.
7064
8302
  if (matchExternalizePattern(id, moduleDirectories, options?.external) || options?.cacheDir && id.includes(options.cacheDir)) return id;
7065
- const isLibraryModule = moduleDirectories.some((dir) => id.includes(dir)), guessCJS = isLibraryModule && options?.fallbackCJS;
7066
- return id = guessCJS ? guessCJSversion(id) || id : id, matchExternalizePattern(id, moduleDirectories, defaultInline) ? false : matchExternalizePattern(id, moduleDirectories, depsExternal) || isLibraryModule && await isValidNodeImport(id) ? id : false;
8303
+ const isLibraryModule = moduleDirectories.some((dir) => id.includes(dir));
8304
+ return id = isLibraryModule && options?.fallbackCJS ? guessCJSversion(id) || id : id, matchExternalizePattern(id, moduleDirectories, defaultInline) ? false : matchExternalizePattern(id, moduleDirectories, depsExternal) || isLibraryModule && await isValidNodeImport(id) ? id : false;
7067
8305
  }
7068
8306
  function matchExternalizePattern(id, moduleDirectories, patterns) {
7069
8307
  if (patterns == null) return false;
@@ -7105,7 +8343,7 @@ class TestSpecification {
7105
8343
  */
7106
8344
  get testModule() {
7107
8345
  const task = this.project.vitest.state.idMap.get(this.taskId);
7108
- return task ? this.project.vitest.state.getReportedEntity(task) : void 0;
8346
+ if (task) return this.project.vitest.state.getReportedEntity(task);
7109
8347
  }
7110
8348
  toJSON() {
7111
8349
  return [
@@ -7136,7 +8374,6 @@ async function createViteServer(inlineConfig) {
7136
8374
  class TestProject {
7137
8375
  /**
7138
8376
  * The global Vitest instance.
7139
- * @experimental The public Vitest API is experimental and does not follow semver.
7140
8377
  */
7141
8378
  vitest;
7142
8379
  /**
@@ -7150,20 +8387,22 @@ class TestProject {
7150
8387
  /**
7151
8388
  * Temporary directory for the project. This is unique for each project. Vitest stores transformed content here.
7152
8389
  */
7153
- tmpDir = join(tmpdir(), nanoid());
8390
+ tmpDir;
7154
8391
  /** @internal */ typechecker;
7155
8392
  /** @internal */ _config;
7156
8393
  /** @internal */ _vite;
7157
8394
  /** @internal */ _hash;
7158
8395
  /** @internal */ _resolver;
8396
+ /** @internal */ _fetcher;
8397
+ /** @internal */ _serializedDefines;
7159
8398
  /** @inetrnal */ testFilesList = null;
7160
8399
  runner;
7161
8400
  closingPromise;
7162
8401
  typecheckFilesList = null;
7163
8402
  _globalSetups;
7164
8403
  _provided = {};
7165
- constructor(vitest, options) {
7166
- this.options = options, this.vitest = vitest, this.globalConfig = vitest.config;
8404
+ constructor(vitest, options, tmpDir) {
8405
+ this.options = options, this.vitest = vitest, this.globalConfig = vitest.config, this.tmpDir = tmpDir || join(tmpdir(), nanoid());
7167
8406
  }
7168
8407
  /**
7169
8408
  * The unique hash of this project. This value is consistent between the reruns.
@@ -7298,7 +8537,7 @@ class TestProject {
7298
8537
  const files = await this.globFiles(includeSource, exclude, cwd);
7299
8538
  await Promise.all(files.map(async (file) => {
7300
8539
  try {
7301
- const code = await promises.readFile(file, "utf-8");
8540
+ const code = await promises$1.readFile(file, "utf-8");
7302
8541
  if (this.isInSourceTestCode(code)) testFiles.push(file);
7303
8542
  } catch {
7304
8543
  return null;
@@ -7333,16 +8572,15 @@ class TestProject {
7333
8572
  }
7334
8573
  /** @internal */
7335
8574
  async globFiles(include, exclude, cwd) {
7336
- const globOptions = {
8575
+ // keep the slashes consistent with Vite
8576
+ // we are not using the pathe here because it normalizes the drive letter on Windows
8577
+ // and we want to keep it the same as working dir
8578
+ return (await glob(include, {
7337
8579
  dot: true,
7338
8580
  cwd,
7339
8581
  ignore: exclude,
7340
8582
  expandDirectories: false
7341
- }, files = await glob(include, globOptions);
7342
- // keep the slashes consistent with Vite
7343
- // we are not using the pathe here because it normalizes the drive letter on Windows
7344
- // and we want to keep it the same as working dir
7345
- return files.map((file) => slash(path.resolve(cwd, file)));
8583
+ })).map((file) => slash(path.resolve(cwd, file)));
7346
8584
  }
7347
8585
  /**
7348
8586
  * Test if a file matches the test globs. This does the actual glob matching if the test is not cached, unlike `isCachedTestFile`.
@@ -7377,28 +8615,22 @@ class TestProject {
7377
8615
  /** @internal */
7378
8616
  _parent;
7379
8617
  /** @internal */
7380
- _initParentBrowser = deduped(async () => {
8618
+ _initParentBrowser = deduped(async (childProject) => {
7381
8619
  if (!this.isBrowserEnabled() || this._parentBrowser) return;
7382
- await this.vitest.packageInstaller.ensureInstalled("@vitest/browser", this.config.root, this.vitest.version);
7383
- const { createBrowserServer, distRoot } = await import('@vitest/browser');
7384
- let cacheDir;
7385
- const browser = await createBrowserServer(this, this.vite.config.configFile, [
7386
- {
7387
- name: "vitest:browser-cacheDir",
7388
- configResolved(config) {
7389
- cacheDir = config.cacheDir;
7390
- }
7391
- },
7392
- ...MocksPlugins({ filter(id) {
7393
- return !(id.includes(distRoot) || id.includes(cacheDir));
7394
- } }),
7395
- MetaEnvReplacerPlugin()
7396
- ], [CoverageTransform(this.vitest)]);
8620
+ const provider = this.config.browser.provider || childProject.config.browser.provider;
8621
+ if (provider == null) throw new Error(`Proider was not specified in the "browser.provider" setting. Please, pass down playwright(), webdriverio() or preview() from "@vitest/browser-playwright", "@vitest/browser-webdriverio" or "@vitest/browser-preview" package respectively.`);
8622
+ if (typeof provider.serverFactory !== "function") throw new TypeError(`The browser provider options do not return a "serverFactory" function. Are you using the latest "@vitest/browser-${provider.name}" package?`);
8623
+ const browser = await provider.serverFactory({
8624
+ project: this,
8625
+ mocksPlugins: (options) => MocksPlugins(options),
8626
+ metaEnvReplacer: () => MetaEnvReplacerPlugin(),
8627
+ coveragePlugin: () => CoverageTransform(this.vitest)
8628
+ });
7397
8629
  if (this._parentBrowser = browser, this.config.browser.ui) setup(this.vitest, browser.vite);
7398
8630
  });
7399
8631
  /** @internal */
7400
8632
  _initBrowserServer = deduped(async () => {
7401
- if (await this._parent?._initParentBrowser(), !this.browser && this._parent?._parentBrowser) this.browser = this._parent._parentBrowser.spawn(this), await this.vitest.report("onBrowserInit", this);
8633
+ if (await this._parent?._initParentBrowser(this), !this.browser && this._parent?._parentBrowser) this.browser = this._parent._parentBrowser.spawn(this), await this.vitest.report("onBrowserInit", this);
7402
8634
  });
7403
8635
  /**
7404
8636
  * Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts.
@@ -7411,6 +8643,8 @@ class TestProject {
7411
8643
  this.browser?.close(),
7412
8644
  this.clearTmpDir()
7413
8645
  ].filter(Boolean)).then(() => {
8646
+ if (!this.runner.isClosed()) return this.runner.close();
8647
+ }).then(() => {
7414
8648
  this._provided = {}, this._vite = void 0;
7415
8649
  });
7416
8650
  return this.closingPromise;
@@ -7435,13 +8669,16 @@ class TestProject {
7435
8669
  // type is very strict here, so we cast it to any
7436
8670
  this.provide(providedKey, this.config.provide[providedKey]);
7437
8671
  }
7438
- this.closingPromise = void 0, this._resolver = new VitestResolver(server.config.cacheDir, this._config), this._vite = server;
8672
+ this.closingPromise = void 0, this._resolver = new VitestResolver(server.config.cacheDir, this._config), this._vite = server, this._serializedDefines = createDefinesScript(server.config.define), this._fetcher = createFetchModuleFunction(this._resolver, this.tmpDir, {
8673
+ dumpFolder: this.config.dumpDir,
8674
+ readFromDump: this.config.server.debug?.load ?? process.env.VITEST_DEBUG_LOAD_DUMP != null
8675
+ });
7439
8676
  const environment = server.environments.__vitest__;
7440
- this.runner = new ServerModuleRunner(environment, this._resolver, this._config);
8677
+ this.runner = new ServerModuleRunner(environment, this._fetcher, this._config);
7441
8678
  }
7442
8679
  _serializeOverriddenConfig() {
7443
8680
  // TODO: serialize the config _once_ or when needed
7444
- const config = serializeConfig(this.config, this.vitest.config, this.vite.config);
8681
+ const config = serializeConfig(this);
7445
8682
  return this.vitest.configOverride ? deepMerge(config, this.vitest.configOverride) : config;
7446
8683
  }
7447
8684
  async clearTmpDir() {
@@ -7466,13 +8703,13 @@ class TestProject {
7466
8703
  }
7467
8704
  /** @internal */
7468
8705
  static _createBasicProject(vitest) {
7469
- const project = new TestProject(vitest);
7470
- return project.runner = vitest.runner, project._vite = vitest.vite, project._config = vitest.config, project._resolver = vitest._resolver, project._setHash(), project._provideObject(vitest.config.provide), project;
8706
+ const project = new TestProject(vitest, void 0, vitest._tmpDir);
8707
+ return project.runner = vitest.runner, project._vite = vitest.vite, project._config = vitest.config, project._resolver = vitest._resolver, project._fetcher = vitest._fetcher, project._serializedDefines = createDefinesScript(vitest.vite.config.define), project._setHash(), project._provideObject(vitest.config.provide), project;
7471
8708
  }
7472
8709
  /** @internal */
7473
8710
  static _cloneBrowserProject(parent, config) {
7474
- const clone = new TestProject(parent.vitest);
7475
- return clone.runner = parent.runner, clone._vite = parent._vite, clone._resolver = parent._resolver, clone._config = config, clone._setHash(), clone._parent = parent, clone._provideObject(config.provide), clone;
8711
+ const clone = new TestProject(parent.vitest, void 0, parent.tmpDir);
8712
+ return clone.runner = parent.runner, clone._vite = parent._vite, clone._resolver = parent._resolver, clone._fetcher = parent._fetcher, clone._config = config, clone._setHash(), clone._parent = parent, clone._serializedDefines = parent._serializedDefines, clone._provideObject(config.provide), clone;
7476
8713
  }
7477
8714
  }
7478
8715
  function deduped(cb) {
@@ -7507,8 +8744,13 @@ function generateHash(str) {
7507
8744
  return `${hash}`;
7508
8745
  }
7509
8746
 
8747
+ // vitest.config.*
8748
+ // vite.config.*
8749
+ // vitest.unit.config.*
8750
+ // vite.unit.config.*
8751
+ const CONFIG_REGEXP = /^vite(?:st)?(?:\.\w+)?\.config\./;
7510
8752
  async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projectsDefinition, names) {
7511
- const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition), overridesOptions = [
8753
+ const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition), cliOverrides = [
7512
8754
  "logHeapUsage",
7513
8755
  "allowOnly",
7514
8756
  "sequence",
@@ -7527,7 +8769,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7527
8769
  "inspect",
7528
8770
  "inspectBrk",
7529
8771
  "fileParallelism"
7530
- ], cliOverrides = overridesOptions.reduce((acc, name) => {
8772
+ ].reduce((acc, name) => {
7531
8773
  if (name in cliOptions) acc[name] = cliOptions[name];
7532
8774
  return acc;
7533
8775
  }, {}), projectPromises = [], fileProjects = [...configFiles, ...nonConfigDirectories], concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
@@ -7595,19 +8837,13 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7595
8837
  }
7596
8838
  async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7597
8839
  const removeProjects = /* @__PURE__ */ new Set();
7598
- resolvedProjects.forEach((project) => {
8840
+ return resolvedProjects.forEach((project) => {
7599
8841
  if (!project.config.browser.enabled) return;
7600
- const instances = project.config.browser.instances || [], browser = project.config.browser.name;
7601
- if (instances.length === 0 && browser) instances.push({
7602
- browser,
7603
- name: project.name ? `${project.name} (${browser})` : browser
7604
- }), vitest.logger.warn(withLabel("yellow", "Vitest", [
7605
- `No browser "instances" were defined`,
7606
- project.name ? ` for the "${project.name}" project. ` : ". ",
7607
- `Running tests in "${project.config.browser.name}" browser. `,
7608
- "The \"browser.name\" field is deprecated since Vitest 3. ",
7609
- "Read more: https://vitest.dev/guide/browser/config#browser-instances"
7610
- ].filter(Boolean).join("")));
8842
+ const instances = project.config.browser.instances || [];
8843
+ if (instances.length === 0) {
8844
+ removeProjects.add(project);
8845
+ return;
8846
+ }
7611
8847
  const originalName = project.config.name, filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
7612
8848
  const newName = instance.name;
7613
8849
  return vitest.matchesProjectFilter(newName);
@@ -7617,7 +8853,6 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7617
8853
  removeProjects.add(project);
7618
8854
  return;
7619
8855
  }
7620
- if (project.config.browser.providerOptions) vitest.logger.warn(withLabel("yellow", "Vitest", `"providerOptions"${originalName ? ` in "${originalName}" project` : ""} is ignored because it's overridden by the configs. To hide this warning, remove the "providerOptions" property from the browser configuration.`));
7621
8856
  filteredInstances.forEach((config, index) => {
7622
8857
  const browser = config.browser;
7623
8858
  if (!browser) {
@@ -7626,6 +8861,7 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7626
8861
  }
7627
8862
  const name = config.name;
7628
8863
  if (name == null) throw new Error(`The browser configuration must have a "name" property. This is a bug in Vitest. Please, open a new issue with reproduction`);
8864
+ if (config.provider?.name != null && project.config.browser.provider?.name != null && config.provider?.name !== project.config.browser.provider?.name) throw new Error(`The instance cannot have a different provider from its parent. The "${name}" instance specifies "${config.provider?.name}" provider, but its parent has a "${project.config.browser.provider?.name}" provider.`);
7629
8865
  if (names.has(name)) throw new Error([
7630
8866
  `Cannot define a nested project for a ${browser} browser. The project name "${name}" was already defined. `,
7631
8867
  "If you have multiple instances for the same browser, make sure to define a custom \"name\". ",
@@ -7637,29 +8873,10 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7637
8873
  const clone = TestProject._cloneBrowserProject(project, clonedConfig);
7638
8874
  resolvedProjects.push(clone);
7639
8875
  }), removeProjects.add(project);
7640
- }), resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
7641
- const headedBrowserProjects = resolvedProjects.filter((project) => {
7642
- return project.config.browser.enabled && !project.config.browser.headless;
7643
- });
7644
- if (headedBrowserProjects.length > 1) {
7645
- const message = [`Found multiple projects that run browser tests in headed mode: "${headedBrowserProjects.map((p) => p.name).join("\", \"")}".`, ` Vitest cannot run multiple headed browsers at the same time.`].join("");
7646
- if (!isTTY) throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
7647
- const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; }), { projectName } = await prompts.default({
7648
- type: "select",
7649
- name: "projectName",
7650
- choices: headedBrowserProjects.map((project) => ({
7651
- title: project.name,
7652
- value: project.name
7653
- })),
7654
- message: `${message} Select a single project to run or cancel and run tests with "headless: true" option. Note that you can also start tests with --browser=name or --project=name flag.`
7655
- });
7656
- if (!projectName) throw new Error("The test run was aborted.");
7657
- return resolvedProjects.filter((project) => project.name === projectName);
7658
- }
7659
- return resolvedProjects;
8876
+ }), resolvedProjects.filter((project) => !removeProjects.has(project));
7660
8877
  }
7661
8878
  function cloneConfig(project, { browser,...config }) {
7662
- const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config, currentConfig = project.config.browser, clonedConfig = deepClone(project.config);
8879
+ const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name, provider,...overrideConfig } = config, currentConfig = project.config.browser, clonedConfig = deepClone(project.config);
7663
8880
  return mergeConfig({
7664
8881
  ...clonedConfig,
7665
8882
  browser: {
@@ -7670,9 +8887,9 @@ function cloneConfig(project, { browser,...config }) {
7670
8887
  screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
7671
8888
  screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
7672
8889
  headless: headless ?? currentConfig.headless,
8890
+ provider: provider ?? currentConfig.provider,
7673
8891
  name: browser,
7674
- providerOptions: config,
7675
- instances: void 0
8892
+ instances: []
7676
8893
  },
7677
8894
  include: overrideConfig.include && overrideConfig.include.length > 0 ? [] : clonedConfig.include,
7678
8895
  exclude: overrideConfig.exclude && overrideConfig.exclude.length > 0 ? [] : clonedConfig.exclude,
@@ -7692,14 +8909,17 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7692
8909
  const note = "Projects definition";
7693
8910
  throw new Error(`${note} references a non-existing file or a directory: ${file}`);
7694
8911
  }
7695
- const stats = await promises.stat(file);
8912
+ const stats = statSync(file);
7696
8913
  // user can specify a config file directly
7697
- if (stats.isFile()) projectsConfigFiles.push(file);
7698
- else if (stats.isDirectory()) {
7699
- const configFile = await resolveDirectoryConfig(file);
8914
+ if (stats.isFile()) {
8915
+ const name = basename(file);
8916
+ if (!CONFIG_REGEXP.test(name)) throw new Error(`The file "${relative(vitest.config.root, file)}" must start with "vitest.config"/"vite.config" or match the pattern "(vitest|vite).*.config.*" to be a valid project config.`);
8917
+ projectsConfigFiles.push(file);
8918
+ } else if (stats.isDirectory()) {
8919
+ const configFile = resolveDirectoryConfig(file);
7700
8920
  if (configFile) projectsConfigFiles.push(configFile);
7701
8921
  else {
7702
- const directory = file[file.length - 1] === "/" ? file : `${file}/`;
8922
+ const directory = file.at(-1) === "/" ? file : `${file}/`;
7703
8923
  nonConfigProjectDirectories.push(directory);
7704
8924
  }
7705
8925
  } else
@@ -7725,16 +8945,20 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7725
8945
  "**/*.timestamp-*",
7726
8946
  "**/.DS_Store"
7727
8947
  ]
7728
- }, projectsFs = await glob(projectsGlobMatches, globOptions);
7729
- await Promise.all(projectsFs.map(async (path) => {
8948
+ };
8949
+ (await glob(projectsGlobMatches, globOptions)).forEach((path) => {
7730
8950
  // directories are allowed with a glob like `packages/*`
7731
8951
  // in this case every directory is treated as a project
7732
8952
  if (path.endsWith("/")) {
7733
- const configFile = await resolveDirectoryConfig(path);
8953
+ const configFile = resolveDirectoryConfig(path);
7734
8954
  if (configFile) projectsConfigFiles.push(configFile);
7735
8955
  else nonConfigProjectDirectories.push(path);
7736
- } else projectsConfigFiles.push(path);
7737
- }));
8956
+ } else {
8957
+ const name = basename(path);
8958
+ if (!CONFIG_REGEXP.test(name)) throw new Error(`The projects glob matched a file "${relative(vitest.config.root, path)}", but it should also either start with "vitest.config"/"vite.config" or match the pattern "(vitest|vite).*.config.*".`);
8959
+ projectsConfigFiles.push(path);
8960
+ }
8961
+ });
7738
8962
  }
7739
8963
  const projectConfigFiles = Array.from(new Set(projectsConfigFiles));
7740
8964
  return {
@@ -7743,16 +8967,13 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7743
8967
  configFiles: projectConfigFiles
7744
8968
  };
7745
8969
  }
7746
- async function resolveDirectoryConfig(directory) {
7747
- const files = new Set(await promises.readdir(directory)), configFile = configFiles.find((file) => files.has(file));
8970
+ function resolveDirectoryConfig(directory) {
8971
+ const files = new Set(readdirSync(directory)), configFile = configFiles.find((file) => files.has(file));
7748
8972
  return configFile ? resolve(directory, configFile) : null;
7749
8973
  }
7750
8974
  function getDefaultTestProject(vitest) {
7751
8975
  const filter = vitest.config.project, project = vitest._ensureRootProject();
7752
- if (!filter.length) return project;
7753
- // check for the project name and browser names
7754
- const hasProjects = getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p));
7755
- return hasProjects ? project : null;
8976
+ return !filter.length || getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p)) ? project : null;
7756
8977
  }
7757
8978
  function getPotentialProjectNames(project) {
7758
8979
  const names = [project.name];
@@ -7775,17 +8996,11 @@ function createReporters(reporterReferences, ctx) {
7775
8996
  const runner = ctx.runner, promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
7776
8997
  if (Array.isArray(referenceOrInstance)) {
7777
8998
  const [reporterName, reporterOptions] = referenceOrInstance;
7778
- if (reporterName === "html") {
7779
- await ctx.packageInstaller.ensureInstalled("@vitest/ui", ctx.config.root, ctx.version);
7780
- const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
7781
- return new CustomReporter(reporterOptions);
7782
- } else if (reporterName in ReportersMap) {
8999
+ if (reporterName === "html") return await ctx.packageInstaller.ensureInstalled("@vitest/ui", ctx.config.root, ctx.version), new (await (loadCustomReporterModule("@vitest/ui/reporter", runner)))(reporterOptions);
9000
+ if (reporterName in ReportersMap) {
7783
9001
  const BuiltinReporter = ReportersMap[reporterName];
7784
9002
  return new BuiltinReporter(reporterOptions);
7785
- } else {
7786
- const CustomReporter = await loadCustomReporterModule(reporterName, runner);
7787
- return new CustomReporter(reporterOptions);
7788
- }
9003
+ } else return new (await (loadCustomReporterModule(reporterName, runner)))(reporterOptions);
7789
9004
  }
7790
9005
  return referenceOrInstance;
7791
9006
  });
@@ -7796,10 +9011,7 @@ function createBenchmarkReporters(reporterReferences, runner) {
7796
9011
  if (typeof referenceOrInstance === "string") if (referenceOrInstance in BenchmarkReportsMap) {
7797
9012
  const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
7798
9013
  return new BuiltinReporter();
7799
- } else {
7800
- const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
7801
- return new CustomReporter();
7802
- }
9014
+ } else return new (await (loadCustomReporterModule(referenceOrInstance, runner)))();
7803
9015
  return referenceOrInstance;
7804
9016
  });
7805
9017
  return Promise.all(promisedReporters);
@@ -7817,11 +9029,11 @@ function parseFilter(filter) {
7817
9029
  return { filename: filter };
7818
9030
  }
7819
9031
  function groupFilters(filters) {
7820
- const groupedFilters_ = groupBy(filters, (f) => f.filename), groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
9032
+ const groupedFilters_ = groupBy(filters, (f) => f.filename);
9033
+ return Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
7821
9034
  const [filename, filters] = entry, testLocations = filters.map((f) => f.lineNumber);
7822
9035
  return [filename, testLocations.filter((l) => l !== void 0)];
7823
9036
  }));
7824
- return groupedFilters;
7825
9037
  }
7826
9038
 
7827
9039
  class VitestSpecifications {
@@ -7882,7 +9094,7 @@ class VitestSpecifications {
7882
9094
  }
7883
9095
  async filterTestsBySource(specs) {
7884
9096
  if (this.vitest.config.changed && !this.vitest.config.related) {
7885
- const { VitestGit } = await import('./git.BFNcloKD.js'), vitestGit = new VitestGit(this.vitest.config.root), related = await vitestGit.findChangedFiles({ changedSince: this.vitest.config.changed });
9097
+ const { VitestGit } = await import('./git.BFNcloKD.js'), related = await new VitestGit(this.vitest.config.root).findChangedFiles({ changedSince: this.vitest.config.changed });
7886
9098
  if (!related) throw process.exitCode = 1, new GitNotFoundError();
7887
9099
  this.vitest.config.related = Array.from(new Set(related));
7888
9100
  }
@@ -7906,7 +9118,7 @@ class VitestSpecifications {
7906
9118
  const addImports = async (project, filepath) => {
7907
9119
  if (deps.has(filepath)) return;
7908
9120
  deps.add(filepath);
7909
- const mod = project.vite.environments.ssr.moduleGraph.getModuleById(filepath), transformed = mod?.transformResult || await project.vite.environments.ssr.transformRequest(filepath);
9121
+ const transformed = project.vite.environments.ssr.moduleGraph.getModuleById(filepath)?.transformResult || await project.vite.environments.ssr.transformRequest(filepath);
7910
9122
  if (!transformed) return;
7911
9123
  const dependencies = [...transformed.deps || [], ...transformed.dynamicDeps || []];
7912
9124
  await Promise.all(dependencies.map(async (dep) => {
@@ -8044,10 +9256,10 @@ class TestCase extends ReportedTaskImplementation {
8044
9256
  diagnostic() {
8045
9257
  const result = this.task.result;
8046
9258
  // startTime should always be available if the test has properly finished
8047
- if (!result || !result.startTime) return void 0;
8048
- const duration = result.duration || 0, slow = duration > this.project.globalConfig.slowTestThreshold;
9259
+ if (!result || !result.startTime) return;
9260
+ const duration = result.duration || 0;
8049
9261
  return {
8050
- slow,
9262
+ slow: duration > this.project.globalConfig.slowTestThreshold,
8051
9263
  heap: result.heap,
8052
9264
  duration,
8053
9265
  startTime: result.startTime,
@@ -8186,16 +9398,19 @@ class TestModule extends SuiteImplementation {
8186
9398
  * This value corresponds to the ID in the Vite's module graph.
8187
9399
  */
8188
9400
  moduleId;
9401
+ /**
9402
+ * Module id relative to the project. This is the same as `task.name`.
9403
+ */
9404
+ relativeModuleId;
8189
9405
  /** @internal */
8190
9406
  constructor(task, project) {
8191
- super(task, project), this.moduleId = task.filepath;
9407
+ super(task, project), this.moduleId = task.filepath, this.relativeModuleId = task.name;
8192
9408
  }
8193
9409
  /**
8194
9410
  * Checks the running state of the test file.
8195
9411
  */
8196
9412
  state() {
8197
- const state = this.task.result?.state;
8198
- return state === "queued" ? "queued" : getSuiteState(this.task);
9413
+ return this.task.result?.state === "queued" ? "queued" : getSuiteState(this.task);
8199
9414
  }
8200
9415
  /**
8201
9416
  * Useful information about the module like duration, memory usage, etc.
@@ -8254,10 +9469,10 @@ class StateManager {
8254
9469
  idMap = /* @__PURE__ */ new Map();
8255
9470
  taskFileMap = /* @__PURE__ */ new WeakMap();
8256
9471
  errorsSet = /* @__PURE__ */ new Set();
8257
- processTimeoutCauses = /* @__PURE__ */ new Set();
8258
9472
  reportedTasksMap = /* @__PURE__ */ new WeakMap();
8259
9473
  blobs;
8260
9474
  transformTime = 0;
9475
+ metadata = {};
8261
9476
  onUnhandledError;
8262
9477
  /** @internal */
8263
9478
  _data = {
@@ -8288,12 +9503,6 @@ class StateManager {
8288
9503
  getUnhandledErrors() {
8289
9504
  return Array.from(this.errorsSet.values());
8290
9505
  }
8291
- addProcessTimeoutCause(cause) {
8292
- this.processTimeoutCauses.add(cause);
8293
- }
8294
- getProcessTimeoutCauses() {
8295
- return Array.from(this.processTimeoutCauses.values());
8296
- }
8297
9506
  getPaths() {
8298
9507
  return Array.from(this.pathsSet);
8299
9508
  }
@@ -8354,6 +9563,10 @@ class StateManager {
8354
9563
  getReportedEntity(task) {
8355
9564
  return this.reportedTasksMap.get(task);
8356
9565
  }
9566
+ getReportedEntityById(taskId) {
9567
+ const task = this.idMap.get(taskId);
9568
+ return task ? this.reportedTasksMap.get(task) : void 0;
9569
+ }
8357
9570
  updateTasks(packs) {
8358
9571
  for (const [id, result, meta] of packs) {
8359
9572
  const task = this.idMap.get(id);
@@ -8374,7 +9587,12 @@ class StateManager {
8374
9587
  return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
8375
9588
  }
8376
9589
  cancelFiles(files, project) {
8377
- this.collectFiles(project, files.map((filepath) => createFileTask$1(filepath, project.config.root, project.config.name)));
9590
+ // if we don't filter existing modules, they will be overriden by `collectFiles`
9591
+ const nonRegisteredFiles = files.filter(({ filepath }) => {
9592
+ const relativePath = relative(project.config.root, filepath), id = generateFileHash(relativePath, project.name);
9593
+ return !this.idMap.has(id);
9594
+ });
9595
+ this.collectFiles(project, nonRegisteredFiles.map((file) => createFileTask$1(file.filepath, project.config.root, project.config.name)));
8378
9596
  }
8379
9597
  }
8380
9598
 
@@ -8859,7 +10077,7 @@ class TestRun {
8859
10077
  async updated(update, events) {
8860
10078
  this.syncUpdateStacks(update), this.vitest.state.updateTasks(update);
8861
10079
  for (const [id, event, data] of events) await this.reportEvent(id, event, data).catch((error) => {
8862
- this.vitest.state.catchError(serializeError$1(error), "Unhandled Reporter Error");
10080
+ this.vitest.state.catchError(serializeValue(error), "Unhandled Reporter Error");
8863
10081
  });
8864
10082
  // TODO: what is the order or reports here?
8865
10083
  // "onTaskUpdate" in parallel with others or before all or after all?
@@ -8871,11 +10089,20 @@ class TestRun {
8871
10089
  // specification won't have the File task if they were filtered by the --shard command
8872
10090
  const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null), state = this.vitest.isCancelling ? "interrupted" : this.hasFailed(modules) ? "failed" : "passed";
8873
10091
  if (state !== "passed") process.exitCode = 1;
8874
- this.vitest.report("onTestRunEnd", modules, [...errors], state);
10092
+ for (const project in await this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.state.metadata) {
10093
+ const meta = this.vitest.state.metadata[project];
10094
+ if (!meta?.dumpDir) continue;
10095
+ const path = resolve(meta.dumpDir, "vitest-metadata.json");
10096
+ meta.outline = {
10097
+ externalized: Object.keys(meta.externalized).length,
10098
+ inlined: Object.keys(meta.tmps).length
10099
+ }, await writeFile(path, JSON.stringify(meta, null, 2), "utf-8"), this.vitest.logger.log(`Metadata written to ${path}`);
10100
+ }
8875
10101
  }
8876
10102
  hasFailed(modules) {
8877
10103
  return modules.length ? modules.some((m) => !m.ok()) : !this.vitest.config.passWithNoTests;
8878
10104
  }
10105
+ // make sure the error always has a "stacks" property
8879
10106
  syncUpdateStacks(update) {
8880
10107
  update.forEach(([taskId, result]) => {
8881
10108
  const task = this.vitest.state.idMap.get(taskId), isBrowser = task && task.file.pool === "browser";
@@ -8926,9 +10153,8 @@ class TestRun {
8926
10153
  const path = attachment.path;
8927
10154
  if (path && !path.startsWith("http://") && !path.startsWith("https://")) {
8928
10155
  const currentPath = resolve(project.config.root, path), hash = createHash("sha1").update(currentPath).digest("hex"), newPath = resolve(project.config.attachmentsDir, `${sanitizeFilePath(annotation.message)}-${hash}${extname(currentPath)}`);
8929
- await mkdir(dirname(newPath), { recursive: true }), await copyFile(currentPath, newPath), attachment.path = newPath;
8930
- const contentType = attachment.contentType ?? mime.getType(basename(currentPath));
8931
- attachment.contentType = contentType || void 0;
10156
+ if (!existsSync(project.config.attachmentsDir)) await mkdir(project.config.attachmentsDir, { recursive: true });
10157
+ await copyFile(currentPath, newPath), attachment.path = newPath, attachment.contentType = (attachment.contentType ?? mime.getType(basename(currentPath))) || void 0;
8932
10158
  }
8933
10159
  return attachment;
8934
10160
  }
@@ -8988,21 +10214,14 @@ class VitestWatcher {
8988
10214
  }), triggered;
8989
10215
  }
8990
10216
  onFileChange = (id) => {
8991
- id = slash(id), this.vitest.logger.clearHighlightCache(id), this.vitest.invalidateFile(id);
8992
- const testFiles = this.getTestFilesFromWatcherTrigger(id);
8993
- if (testFiles) this.scheduleRerun(id);
8994
- else {
8995
- const needsRerun = this.handleFileChanged(id);
8996
- if (needsRerun) this.scheduleRerun(id);
8997
- }
10217
+ if (id = slash(id), this.vitest.logger.clearHighlightCache(id), this.vitest.invalidateFile(id), this.getTestFilesFromWatcherTrigger(id)) this.scheduleRerun(id);
10218
+ else if (this.handleFileChanged(id)) this.scheduleRerun(id);
8998
10219
  };
8999
10220
  onFileDelete = (id) => {
9000
10221
  if (id = slash(id), this.vitest.logger.clearHighlightCache(id), this.invalidates.add(id), this.vitest.state.filesMap.has(id)) this.vitest.projects.forEach((project) => project._removeCachedTestFile(id)), this.vitest.state.filesMap.delete(id), this.vitest.cache.results.removeFromCache(id), this.vitest.cache.stats.removeStats(id), this.changedTests.delete(id), this.vitest.report("onTestRemoved", id);
9001
10222
  };
9002
10223
  onFileCreate = (id) => {
9003
- id = slash(id), this.vitest.invalidateFile(id);
9004
- const testFiles = this.getTestFilesFromWatcherTrigger(id);
9005
- if (testFiles) {
10224
+ if (id = slash(id), this.vitest.invalidateFile(id), this.getTestFilesFromWatcherTrigger(id)) {
9006
10225
  this.scheduleRerun(id);
9007
10226
  return;
9008
10227
  }
@@ -9011,11 +10230,7 @@ class VitestWatcher {
9011
10230
  if (this.vitest.projects.forEach((project) => {
9012
10231
  if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) matchingProjects.push(project);
9013
10232
  }), matchingProjects.length > 0) this.changedTests.add(id), this.scheduleRerun(id);
9014
- else {
9015
- // it's possible that file was already there but watcher triggered "add" event instead
9016
- const needsRerun = this.handleFileChanged(id);
9017
- if (needsRerun) this.scheduleRerun(id);
9018
- }
10233
+ else if (this.handleFileChanged(id)) this.scheduleRerun(id);
9019
10234
  };
9020
10235
  handleSetupFile(filepath) {
9021
10236
  let isSetupFile = false;
@@ -9035,8 +10250,7 @@ class VitestWatcher {
9035
10250
  if (pm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) return this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file)), true;
9036
10251
  if (this.handleSetupFile(filepath)) return true;
9037
10252
  const projects = this.vitest.projects.filter((project) => {
9038
- const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
9039
- return moduleGraph.getModulesByFile(filepath)?.size;
10253
+ return (project.browser?.vite.moduleGraph || project.vite.moduleGraph).getModulesByFile(filepath)?.size;
9040
10254
  });
9041
10255
  if (!projects.length) return this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath)) ? (this.changedTests.add(filepath), true) : false;
9042
10256
  const files = [];
@@ -9050,9 +10264,7 @@ class VitestWatcher {
9050
10264
  }
9051
10265
  let rerun = false;
9052
10266
  for (const mod of mods) mod.importers.forEach((i) => {
9053
- if (!i.file) return;
9054
- const needsRerun = this.handleFileChanged(i.file);
9055
- if (needsRerun) rerun = true;
10267
+ if (i.file && this.handleFileChanged(i.file)) rerun = true;
9056
10268
  });
9057
10269
  if (rerun) files.push(filepath);
9058
10270
  }
@@ -9112,6 +10324,8 @@ class Vitest {
9112
10324
  /** @internal */ runner;
9113
10325
  /** @internal */ _testRun = void 0;
9114
10326
  /** @internal */ _resolver;
10327
+ /** @internal */ _fetcher;
10328
+ /** @internal */ _tmpDir = join(tmpdir(), nanoid());
9115
10329
  isFirstRun = true;
9116
10330
  restartsCount = 0;
9117
10331
  specifications;
@@ -9167,18 +10381,19 @@ class Vitest {
9167
10381
  this.watcher.unregisterWatcher(), clearTimeout(this._rerunTimer), this.restartsCount += 1, this.pool?.close?.(), this.pool = void 0, this.closingPromise = void 0, this.projects = [], this.runningPromise = void 0, this.coreWorkspaceProject = void 0, this.specifications.clearCache(), this._coverageProvider = void 0, this._onUserTestsRerun = [], this._vite = server;
9168
10382
  const resolved = resolveConfig(this, options, server.config);
9169
10383
  if (this._config = resolved, this._state = new StateManager({ onUnhandledError: resolved.onUnhandledError }), this._cache = new VitestCache(this.version), this._snapshot = new SnapshotManager({ ...resolved.snapshotOptions }), this._testRun = new TestRun(this), this.config.watch) this.watcher.registerWatcher();
9170
- this._resolver = new VitestResolver(server.config.cacheDir, resolved);
10384
+ this._resolver = new VitestResolver(server.config.cacheDir, resolved), this._fetcher = createFetchModuleFunction(this._resolver, this._tmpDir, {
10385
+ dumpFolder: this.config.dumpDir,
10386
+ readFromDump: this.config.server.debug?.load ?? process.env.VITEST_DEBUG_LOAD_DUMP != null
10387
+ });
9171
10388
  const environment = server.environments.__vitest__;
9172
- if (this.runner = new ServerModuleRunner(environment, this._resolver, resolved), this.config.watch) {
10389
+ if (this.runner = new ServerModuleRunner(environment, this._fetcher, resolved), this.config.watch) {
9173
10390
  // hijack server restart
9174
10391
  const serverRestart = server.restart;
9175
10392
  // since we set `server.hmr: false`, Vite does not auto restart itself
9176
10393
  server.restart = async (...args) => {
9177
10394
  await Promise.all(this._onRestartListeners.map((fn) => fn())), this.report("onServerRestart"), await this.close(), await serverRestart(...args);
9178
10395
  }, server.watcher.on("change", async (file) => {
9179
- file = normalize(file);
9180
- const isConfig = file === server.config.configFile || this.projects.some((p) => p.vite.config.configFile === file);
9181
- if (isConfig) await Promise.all(this._onRestartListeners.map((fn) => fn("config"))), this.report("onServerRestart", "config"), await this.close(), await serverRestart();
10396
+ if (file = normalize(file), file === server.config.configFile || this.projects.some((p) => p.vite.config.configFile === file)) await Promise.all(this._onRestartListeners.map((fn) => fn("config"))), this.report("onServerRestart", "config"), await this.close(), await serverRestart();
9182
10397
  });
9183
10398
  }
9184
10399
  this.cache.results.setConfig(resolved.root, resolved.cache);
@@ -9187,19 +10402,22 @@ class Vitest {
9187
10402
  } catch {}
9188
10403
  const projects = await this.resolveProjects(this._cliOptions);
9189
10404
  if (this.projects = projects, await Promise.all(projects.flatMap((project) => {
9190
- const hooks = project.vite.config.getSortedPluginHooks("configureVitest");
9191
- return hooks.map((hook) => hook({
10405
+ return project.vite.config.getSortedPluginHooks("configureVitest").map((hook) => hook({
9192
10406
  project,
9193
10407
  vitest: this,
9194
10408
  injectTestProjects: this.injectTestProject
9195
10409
  }));
9196
10410
  })), this._cliOptions.browser?.enabled) {
9197
- const browserProjects = this.projects.filter((p) => p.config.browser.enabled);
9198
- if (!browserProjects.length) throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`);
10411
+ if (!this.projects.filter((p) => p.config.browser.enabled).length) throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`);
9199
10412
  }
9200
10413
  if (!this.projects.length) {
9201
10414
  const filter = toArray(resolved.project).join("\", \"");
9202
- throw filter ? new Error(`No projects matched the filter "${filter}".`) : new Error(`Vitest wasn't able to resolve any project.`);
10415
+ if (filter) throw new Error(`No projects matched the filter "${filter}".`);
10416
+ {
10417
+ let error = `Vitest wasn't able to resolve any project.`;
10418
+ if (this.config.browser.enabled && !this.config.browser.instances?.length) error += ` Please, check that you specified the "browser.instances" option.`;
10419
+ throw new Error(error);
10420
+ }
9203
10421
  }
9204
10422
  if (!this.coreWorkspaceProject) this.coreWorkspaceProject = TestProject._createBasicProject(this);
9205
10423
  if (this.config.testNamePattern) this.configOverride.testNamePattern = this.config.testNamePattern;
@@ -9314,7 +10532,7 @@ class Vitest {
9314
10532
  }, await this.report("onInit", this);
9315
10533
  const specifications = [];
9316
10534
  for (const file of files) {
9317
- const project = this.getProjectByName(file.projectName || ""), specification = project.createSpecification(file.filepath, void 0, file.pool);
10535
+ const specification = this.getProjectByName(file.projectName || "").createSpecification(file.filepath, void 0, file.pool);
9318
10536
  specifications.push(specification);
9319
10537
  }
9320
10538
  await this._testRun.start(specifications).catch(noop);
@@ -9324,6 +10542,12 @@ class Vitest {
9324
10542
  unhandledErrors: this.state.getUnhandledErrors()
9325
10543
  };
9326
10544
  }
10545
+ /**
10546
+ * Returns the seed, if tests are running in a random order.
10547
+ */
10548
+ getSeed() {
10549
+ return this.config.sequence.seed ?? null;
10550
+ }
9327
10551
  /** @internal */
9328
10552
  async _reportFileTask(file) {
9329
10553
  const project = this.getProjectByName(file.projectName || "");
@@ -9607,6 +10831,12 @@ class Vitest {
9607
10831
  else this.configOverride.testNamePattern = pattern ? new RegExp(pattern) : void 0;
9608
10832
  }
9609
10833
  /**
10834
+ * Returns the regexp used for the global test name pattern.
10835
+ */
10836
+ getGlobalTestNamePattern() {
10837
+ return this.configOverride.testNamePattern == null ? this.config.testNamePattern : this.configOverride.testNamePattern;
10838
+ }
10839
+ /**
9610
10840
  * Resets the global test name pattern. This method doesn't run any tests.
9611
10841
  */
9612
10842
  resetGlobalTestNamePattern() {
@@ -9641,10 +10871,8 @@ class Vitest {
9641
10871
  */
9642
10872
  invalidateFile(filepath) {
9643
10873
  this.projects.forEach(({ vite, browser }) => {
9644
- const environments = [...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})];
9645
- environments.forEach(({ moduleGraph }) => {
9646
- const modules = moduleGraph.getModulesByFile(filepath);
9647
- modules?.forEach((module) => moduleGraph.invalidateModule(module));
10874
+ [...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})].forEach(({ moduleGraph }) => {
10875
+ moduleGraph.getModulesByFile(filepath)?.forEach((module) => moduleGraph.invalidateModule(module));
9648
10876
  });
9649
10877
  });
9650
10878
  }
@@ -9694,7 +10922,7 @@ class Vitest {
9694
10922
  async exit(force = false) {
9695
10923
  if (setTimeout(() => {
9696
10924
  this.report("onProcessTimeout").then(() => {
9697
- if (console.warn(`close timed out after ${this.config.teardownTimeout}ms`), this.state.getProcessTimeoutCauses().forEach((cause) => console.warn(cause)), !this.pool) {
10925
+ if (console.warn(`close timed out after ${this.config.teardownTimeout}ms`), !this.pool) {
9698
10926
  const runningServers = [this._vite, ...this.projects.map((p) => p._vite)].filter(Boolean).length;
9699
10927
  if (runningServers === 1) console.warn("Tests closed successfully but something prevents Vite server from exiting");
9700
10928
  else if (runningServers > 1) console.warn(`Tests closed successfully but something prevents ${runningServers} Vite servers from exiting`);
@@ -9767,8 +10995,7 @@ class Vitest {
9767
10995
  matchesProjectFilter(name) {
9768
10996
  const projects = this._config?.project || this._cliOptions?.project;
9769
10997
  return !projects || !projects.length ? true : toArray(projects).some((project) => {
9770
- const regexp = wildcardPatternToRegExp(project);
9771
- return regexp.test(name);
10998
+ return wildcardPatternToRegExp(project).test(name);
9772
10999
  });
9773
11000
  }
9774
11001
  }
@@ -9797,15 +11024,12 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
9797
11024
  // however to allow vitest plugins to modify vitest config values
9798
11025
  // this is repeated in configResolved where the config is final
9799
11026
  const testConfig = deepMerge({}, configDefaults, removeUndefinedValues(viteConfig.test ?? {}), options);
9800
- testConfig.api = resolveApiServerConfig(testConfig, defaultPort);
9801
- // store defines for globalThis to make them
9802
- // reassignable when running in worker in src/runtime/setup.ts
9803
- const defines = deleteDefineConfig(viteConfig);
9804
- options.defines = defines;
11027
+ testConfig.api = resolveApiServerConfig(testConfig, defaultPort), options.defines = deleteDefineConfig(viteConfig);
9805
11028
  let open = false;
9806
11029
  if (testConfig.ui && testConfig.open) open = testConfig.uiBase ?? "/__vitest__/";
9807
11030
  const resolveOptions = getDefaultResolveOptions();
9808
11031
  let config = {
11032
+ base: "/",
9809
11033
  root: viteConfig.test?.root || options.root,
9810
11034
  define: { "process.env.NODE_ENV": "process.env.NODE_ENV" },
9811
11035
  resolve: {
@@ -9829,10 +11053,6 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
9829
11053
  __vitest__: { dev: {} }
9830
11054
  },
9831
11055
  test: {
9832
- poolOptions: {
9833
- threads: { isolate: options.poolOptions?.threads?.isolate ?? options.isolate ?? testConfig.poolOptions?.threads?.isolate ?? viteConfig.test?.isolate },
9834
- forks: { isolate: options.poolOptions?.forks?.isolate ?? options.isolate ?? testConfig.poolOptions?.forks?.isolate ?? viteConfig.test?.isolate }
9835
- },
9836
11056
  root: testConfig.root ?? viteConfig.test?.root,
9837
11057
  deps: testConfig.deps ?? viteConfig.test?.deps
9838
11058
  }
@@ -9916,7 +11136,7 @@ function removeUndefinedValues(obj) {
9916
11136
  }
9917
11137
 
9918
11138
  async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {}) {
9919
- const ctx = new Vitest(mode, deepClone(options), vitestOptions), root = slash(resolve$1(options.root || process.cwd())), configPath = options.config === false ? false : options.config ? resolve$1(root, options.config) : await findUp(configFiles, { cwd: root });
11139
+ const ctx = new Vitest(mode, deepClone(options), vitestOptions), root = slash(resolve$1(options.root || process.cwd())), configPath = options.config === false ? false : options.config ? resolve$1(root, options.config) : any(configFiles, { cwd: root });
9920
11140
  options.config = configPath;
9921
11141
  const { browser: _removeBrowser,...restOptions } = options, config = {
9922
11142
  configFile: configPath,
@@ -9968,9 +11188,11 @@ class WatchFilter {
9968
11188
  this.write(`${ESC}1G${ESC}0J`), onSubmit(void 0);
9969
11189
  return;
9970
11190
  case key?.name === "enter":
9971
- case key?.name === "return":
9972
- onSubmit(this.results[this.selectionIndex] || this.currentKeyword || ""), this.currentKeyword = void 0;
11191
+ case key?.name === "return": {
11192
+ const selection = this.results[this.selectionIndex], result = typeof selection === "string" ? selection : selection?.key;
11193
+ onSubmit(result || this.currentKeyword || ""), this.currentKeyword = void 0;
9973
11194
  break;
11195
+ }
9974
11196
  case key?.name === "up":
9975
11197
  if (this.selectionIndex && this.selectionIndex > 0) this.selectionIndex--;
9976
11198
  else this.selectionIndex = -1;
@@ -10032,7 +11254,7 @@ ${c.dim(` ...and ${remainingResultCount} more ${remainingResultCount === 1 ? "
10032
11254
  this.stdout.write(data);
10033
11255
  }
10034
11256
  getLastResults() {
10035
- return this.results;
11257
+ return this.results.map((r) => typeof r === "string" ? r : r.toString());
10036
11258
  }
10037
11259
  }
10038
11260
 
@@ -10058,6 +11280,26 @@ ${c.bold(" Watch Usage")}
10058
11280
  ${keys.map((i) => c.dim(" press ") + c.reset([i[0]].flat().map(c.bold).join(", ")) + c.dim(` to ${i[1]}`)).join("\n")}
10059
11281
  `);
10060
11282
  }
11283
+ function* traverseFilteredTestNames(parentName, filter, t) {
11284
+ if (isTestCase(t)) {
11285
+ if (t.name.match(filter)) {
11286
+ const displayName = `${parentName} > ${t.name}`;
11287
+ yield {
11288
+ key: t.name,
11289
+ toString: () => displayName
11290
+ };
11291
+ }
11292
+ } else {
11293
+ parentName = parentName.length ? `${parentName} > ${t.name}` : t.name;
11294
+ for (const task of t.tasks) yield* traverseFilteredTestNames(parentName, filter, task);
11295
+ }
11296
+ }
11297
+ function* getFilteredTestNames(pattern, suite) {
11298
+ try {
11299
+ const reg = new RegExp(pattern), files = /* @__PURE__ */ new Set();
11300
+ for (const file of suite) if (!files.has(file.name)) files.add(file.name), yield* traverseFilteredTestNames("", reg, file);
11301
+ } catch {}
11302
+ }
10061
11303
  function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10062
11304
  let latestFilename = "";
10063
11305
  async function _keypressHandler(str, key) {
@@ -10107,15 +11349,8 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10107
11349
  }
10108
11350
  async function inputNamePattern() {
10109
11351
  off();
10110
- const watchFilter = new WatchFilter("Input test name pattern (RegExp)", stdin, stdout), filter = await watchFilter.filter((str) => {
10111
- const files = ctx.state.getFiles(), tests = getTests(files);
10112
- try {
10113
- const reg = new RegExp(str);
10114
- return tests.map((test) => test.name).filter((testName) => testName.match(reg));
10115
- } catch {
10116
- // `new RegExp` may throw error when input is invalid regexp
10117
- return [];
10118
- }
11352
+ const filter = await new WatchFilter("Input test name pattern (RegExp)", stdin, stdout).filter((str) => {
11353
+ return [...getFilteredTestNames(str, ctx.state.getFiles())];
10119
11354
  });
10120
11355
  if (on(), typeof filter === "undefined") return;
10121
11356
  const files = ctx.state.getFilepaths(), cliFiles = ctx.config.standalone && !files.length ? await ctx._globTestFilepaths() : void 0;
@@ -10134,8 +11369,7 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10134
11369
  async function inputFilePattern() {
10135
11370
  off();
10136
11371
  const watchFilter = new WatchFilter("Input filename pattern", stdin, stdout), filter = await watchFilter.filter(async (str) => {
10137
- const specifications = await ctx.globTestSpecifications([str]);
10138
- return specifications.map((specification) => relative(ctx.config.root, specification.moduleId)).filter((file, index, all) => all.indexOf(file) === index);
11372
+ return (await ctx.globTestSpecifications([str])).map((specification) => relative(ctx.config.root, specification.moduleId)).filter((file, index, all) => all.indexOf(file) === index);
10139
11373
  });
10140
11374
  if (on(), typeof filter === "undefined") return;
10141
11375
  latestFilename = filter?.trim() || "";
@@ -10287,4 +11521,4 @@ var cliApi = /*#__PURE__*/Object.freeze({
10287
11521
  startVitest: startVitest
10288
11522
  });
10289
11523
 
10290
- export { FilesNotFoundError as F, GitNotFoundError as G, Vitest as V, VitestPlugin as a, VitestPackageInstaller as b, createVitest as c, experimental_getRunnerTask as d, escapeTestName as e, registerConsoleShortcuts as f, createViteLogger as g, createDebugger as h, isValidApiRequest as i, cliApi as j, resolveFsAllow as r, startVitest as s };
11524
+ export { FilesNotFoundError as F, GitNotFoundError as G, ThreadsPoolWorker as T, Vitest as V, VitestPlugin as a, VitestPackageInstaller as b, createVitest as c, createMethodsRPC as d, escapeTestName as e, ForksPoolWorker as f, getFilePoolName as g, TypecheckPoolWorker as h, isValidApiRequest as i, VmForksPoolWorker as j, VmThreadsPoolWorker as k, experimental_getRunnerTask as l, registerConsoleShortcuts as m, createViteLogger as n, createDebugger as o, cliApi as p, resolveFsAllow as r, startVitest as s };