vitest 4.0.0-beta.1 → 4.0.0-beta.10

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 +2 -2
  2. package/dist/browser.d.ts +13 -14
  3. package/dist/browser.js +6 -5
  4. package/dist/chunks/base.Cjha6usc.js +129 -0
  5. package/dist/chunks/{benchmark.CYdenmiT.js → benchmark.CJUa-Hsa.js} +6 -8
  6. package/dist/chunks/{benchmark.d.BwvBVTda.d.ts → benchmark.d.DAaHLpsq.d.ts} +4 -4
  7. package/dist/chunks/{browser.d.q8Z0P0q1.d.ts → browser.d.yFAklsD1.d.ts} +5 -5
  8. package/dist/chunks/{cac.D3EzDDZd.js → cac.DCxo_nSu.js} +70 -152
  9. package/dist/chunks/{cli-api.Dn5gKePv.js → cli-api.BJJXh9BV.js} +1330 -1677
  10. package/dist/chunks/{config.d.HJdfX-8k.d.ts → config.d.B_LthbQq.d.ts} +58 -63
  11. package/dist/chunks/{console.CtFJOzRO.js → console.7h5kHUIf.js} +34 -70
  12. package/dist/chunks/{constants.DnKduX2e.js → constants.D_Q9UYh-.js} +1 -9
  13. package/dist/chunks/{coverage.Cwa-XhJt.js → coverage.BCU-r2QL.js} +515 -781
  14. package/dist/chunks/{coverage.DVF1vEu8.js → coverage.D_JHT54q.js} +2 -2
  15. package/dist/chunks/{coverage.d.S9RMNXIe.d.ts → coverage.d.BZtK59WP.d.ts} +10 -8
  16. package/dist/chunks/{creator.GK6I-cL4.js → creator.08Gi-vCA.js} +93 -77
  17. package/dist/chunks/{date.Bq6ZW5rf.js → date.-jtEtIeV.js} +6 -17
  18. package/dist/chunks/{environment.d.CUq4cUgQ.d.ts → environment.d.BsToaxti.d.ts} +27 -6
  19. package/dist/chunks/{git.BVQ8w_Sw.js → git.BFNcloKD.js} +1 -2
  20. package/dist/chunks/{global.d.CVbXEflG.d.ts → global.d.BK3X7FW1.d.ts} +2 -5
  21. package/dist/chunks/{globals.Cxal6MLI.js → globals.DG-S3xFe.js} +8 -8
  22. package/dist/chunks/{index.CZI_8rVt.js → index.BIP7prJq.js} +289 -608
  23. package/dist/chunks/{index.B521nVV-.js → index.Bgo3tNWt.js} +23 -4
  24. package/dist/chunks/{index.TfbsX-3I.js → index.BjKEiSn0.js} +14 -24
  25. package/dist/chunks/{index.BWf_gE5n.js → index.CMfqw92x.js} +7 -6
  26. package/dist/chunks/{index.CmSc2RE5.js → index.DIWhzsUh.js} +72 -118
  27. package/dist/chunks/{inspector.C914Efll.js → inspector.CvQD-Nie.js} +10 -25
  28. package/dist/chunks/moduleRunner.d.D9nBoC4p.d.ts +201 -0
  29. package/dist/chunks/moduleTransport.I-bgQy0S.js +19 -0
  30. package/dist/chunks/{node.fjCdwEIl.js → node.CyipiPvJ.js} +1 -1
  31. package/dist/chunks/{plugin.d.C2EcJUjo.d.ts → plugin.d.BMVSnsGV.d.ts} +1 -1
  32. package/dist/chunks/{reporters.d.DxZg19fy.d.ts → reporters.d.BUWjmRYq.d.ts} +1226 -1291
  33. package/dist/chunks/resolveSnapshotEnvironment.Bkht6Yor.js +81 -0
  34. package/dist/chunks/resolver.Bx6lE0iq.js +119 -0
  35. package/dist/chunks/rpc.BKr6mtxz.js +65 -0
  36. package/dist/chunks/{setup-common.D7ZqXFx-.js → setup-common.uiMcU3cv.js} +17 -29
  37. package/dist/chunks/startModuleRunner.p67gbNo9.js +665 -0
  38. package/dist/chunks/{suite.d.FvehnV49.d.ts → suite.d.BJWk38HB.d.ts} +1 -1
  39. package/dist/chunks/test.BiqSKISg.js +214 -0
  40. package/dist/chunks/{typechecker.CVytUJuF.js → typechecker.DB-fIMaH.js} +144 -213
  41. package/dist/chunks/{utils.CAioKnHs.js → utils.C2YI6McM.js} +5 -14
  42. package/dist/chunks/{utils.XdZDrNZV.js → utils.D2R2NiOH.js} +8 -27
  43. package/dist/chunks/{vi.bdSIJ99Y.js → vi.ZPgvtBao.js} +156 -305
  44. package/dist/chunks/{vm.BThCzidc.js → vm.Ca0Y0W5f.js} +116 -226
  45. package/dist/chunks/{worker.d.DoNjFAiv.d.ts → worker.d.BDsXGkwh.d.ts} +28 -22
  46. package/dist/chunks/{worker.d.CmvJfRGs.d.ts → worker.d.BNcX_2mH.d.ts} +1 -1
  47. package/dist/cli.js +4 -4
  48. package/dist/config.cjs +3 -9
  49. package/dist/config.d.ts +49 -54
  50. package/dist/config.js +1 -1
  51. package/dist/coverage.d.ts +27 -26
  52. package/dist/coverage.js +6 -7
  53. package/dist/environments.d.ts +9 -13
  54. package/dist/environments.js +1 -1
  55. package/dist/index.d.ts +38 -45
  56. package/dist/index.js +7 -9
  57. package/dist/module-evaluator.d.ts +13 -0
  58. package/dist/module-evaluator.js +276 -0
  59. package/dist/module-runner.js +15 -0
  60. package/dist/node.d.ts +40 -41
  61. package/dist/node.js +23 -33
  62. package/dist/reporters.d.ts +12 -13
  63. package/dist/reporters.js +3 -3
  64. package/dist/runners.d.ts +3 -3
  65. package/dist/runners.js +13 -232
  66. package/dist/snapshot.js +2 -2
  67. package/dist/suite.d.ts +2 -2
  68. package/dist/suite.js +2 -2
  69. package/dist/worker.js +90 -47
  70. package/dist/workers/forks.js +34 -10
  71. package/dist/workers/runVmTests.js +36 -56
  72. package/dist/workers/threads.js +34 -10
  73. package/dist/workers/vmForks.js +11 -10
  74. package/dist/workers/vmThreads.js +11 -10
  75. package/dist/workers.d.ts +5 -4
  76. package/dist/workers.js +35 -17
  77. package/globals.d.ts +17 -17
  78. package/package.json +32 -31
  79. package/dist/chunks/base.Bj3pWTr1.js +0 -38
  80. package/dist/chunks/execute.B7h3T_Hc.js +0 -708
  81. package/dist/chunks/index.D-VkfKhf.js +0 -105
  82. package/dist/chunks/rpc.CsFtxqeq.js +0 -83
  83. package/dist/chunks/runBaseTests.BC7ZIH5L.js +0 -129
  84. package/dist/execute.d.ts +0 -148
  85. package/dist/execute.js +0 -13
@@ -1,19 +1,18 @@
1
1
  import fs, { promises, existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { relative, resolve, dirname, extname, normalize, join, basename, isAbsolute } from 'pathe';
3
- import { C as CoverageProviderMap } from './coverage.DVF1vEu8.js';
3
+ import { C as CoverageProviderMap } from './coverage.D_JHT54q.js';
4
4
  import path, { resolve as resolve$1 } from 'node:path';
5
- import { noop, isPrimitive, createDefer, slash, highlight, toArray, deepMerge, nanoid, deepClone, notNullish } from '@vitest/utils';
5
+ import { noop, createDefer, slash, highlight, toArray, cleanUrl, deepMerge, KNOWN_ASSET_RE, nanoid, deepClone, isPrimitive, notNullish } from '@vitest/utils';
6
6
  import { f as findUp, p as prompt } from './index.X0nbfr6-.js';
7
7
  import * as vite from 'vite';
8
- import { searchForWorkspaceRoot, version, createServer, mergeConfig } from 'vite';
9
- import { A as API_PATH, c as configFiles, d as defaultBrowserPort, w as workspacesFiles, a as defaultPort } from './constants.DnKduX2e.js';
10
- import { generateFileHash, limitConcurrency, createFileTask, hasFailed, getTasks, getTests } from '@vitest/runner/utils';
8
+ import { parseAst, searchForWorkspaceRoot, version, mergeConfig, createServer } from 'vite';
9
+ import { A as API_PATH, c as configFiles, d as defaultBrowserPort, a as defaultPort } from './constants.D_Q9UYh-.js';
10
+ 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';
11
12
  import { SnapshotManager } from '@vitest/snapshot/manager';
12
- import { ViteNodeRunner } from 'vite-node/client';
13
- import { ViteNodeServer } from 'vite-node/server';
14
- import { v as version$1 } from './cac.D3EzDDZd.js';
15
- import { c as createBirpc } from './index.B521nVV-.js';
16
- 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.CZI_8rVt.js';
13
+ import { v as version$1 } from './cac.DCxo_nSu.js';
14
+ 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.BIP7prJq.js';
17
16
  import require$$0$3 from 'events';
18
17
  import require$$1$1 from 'https';
19
18
  import require$$2 from 'http';
@@ -25,27 +24,30 @@ import require$$7 from 'url';
25
24
  import require$$0 from 'zlib';
26
25
  import require$$0$1 from 'buffer';
27
26
  import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
28
- import { parseErrorStacktrace } from '@vitest/utils/source-map';
29
27
  import crypto, { createHash } from 'node:crypto';
30
28
  import { distDir, rootDir } from '../path.js';
31
- import { h as hash, R as RandomSequencer, i as isPackageExists, g as getFilePoolName, d as isBrowserEnabled, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.Cwa-XhJt.js';
32
- import { c as convertTasksToEvents } from './typechecker.CVytUJuF.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.BCU-r2QL.js';
30
+ import { b as ancestor, c as createDefinesScript, d as convertTasksToEvents } from './typechecker.DB-fIMaH.js';
31
+ import { TraceMap, originalPositionFor, parseErrorStacktrace } from '@vitest/utils/source-map';
32
+ import createDebug from 'debug';
33
+ import { VitestModuleEvaluator } from '#module-evaluator';
34
+ import { ModuleRunner } from 'vite/module-runner';
33
35
  import { Console } from 'node:console';
34
36
  import c from 'tinyrainbow';
35
- import { createRequire } from 'node:module';
36
- import url from 'node:url';
37
+ import { createRequire, builtinModules, isBuiltin } from 'node:module';
38
+ import url, { pathToFileURL } from 'node:url';
37
39
  import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
38
40
  import { rm, mkdir, copyFile } from 'node:fs/promises';
39
- import nodeos__default, { tmpdir } from 'node:os';
40
41
  import pm from 'picomatch';
41
42
  import { glob, isDynamicPattern } from 'tinyglobby';
42
- import { normalizeRequestId, cleanUrl } from 'vite-node/utils';
43
+ import MagicString from 'magic-string';
43
44
  import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
44
45
  import { c as configDefaults } from './defaults.CXFFjsi8.js';
45
- import MagicString from 'magic-string';
46
- import { a as BenchmarkReportsMap } from './index.TfbsX-3I.js';
46
+ import { f as findNearestPackageData } from './resolver.Bx6lE0iq.js';
47
+ import * as esModuleLexer from 'es-module-lexer';
48
+ import { a as BenchmarkReportsMap } from './index.BjKEiSn0.js';
47
49
  import assert$1 from 'node:assert';
48
- import { serializeError } from '@vitest/utils/error';
50
+ import { serializeError as serializeError$1 } from '@vitest/utils/error';
49
51
  import readline from 'node:readline';
50
52
  import { stripVTControlCharacters } from 'node:util';
51
53
 
@@ -4717,9 +4719,11 @@ function requireWebsocketServer () {
4717
4719
  return;
4718
4720
  }
4719
4721
 
4720
- if (version !== 8 && version !== 13) {
4722
+ if (version !== 13 && version !== 8) {
4721
4723
  const message = 'Missing or invalid Sec-WebSocket-Version header';
4722
- abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
4724
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
4725
+ 'Sec-WebSocket-Version': '13, 8'
4726
+ });
4723
4727
  return;
4724
4728
  }
4725
4729
 
@@ -4987,16 +4991,24 @@ function requireWebsocketServer () {
4987
4991
  * @param {Duplex} socket The socket of the upgrade request
4988
4992
  * @param {Number} code The HTTP response status code
4989
4993
  * @param {String} message The HTTP response body
4994
+ * @param {Object} [headers] The HTTP response headers
4990
4995
  * @private
4991
4996
  */
4992
- function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
4997
+ function abortHandshakeOrEmitwsClientError(
4998
+ server,
4999
+ req,
5000
+ socket,
5001
+ code,
5002
+ message,
5003
+ headers
5004
+ ) {
4993
5005
  if (server.listenerCount('wsClientError')) {
4994
5006
  const err = new Error(message);
4995
5007
  Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
4996
5008
 
4997
5009
  server.emit('wsClientError', err, socket, req);
4998
5010
  } else {
4999
- abortHandshake(socket, code, message);
5011
+ abortHandshake(socket, code, message, headers);
5000
5012
  }
5001
5013
  }
5002
5014
  return websocketServer;
@@ -5006,25 +5018,18 @@ var websocketServerExports = requireWebsocketServer();
5006
5018
  var WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServerExports);
5007
5019
 
5008
5020
  async function getModuleGraph(ctx, projectName, id, browser = false) {
5009
- const graph = {};
5010
- const externalized = /* @__PURE__ */ new Set();
5011
- const inlined = /* @__PURE__ */ new Set();
5012
- const project = ctx.getProjectByName(projectName);
5021
+ const graph = {}, externalized = /* @__PURE__ */ new Set(), inlined = /* @__PURE__ */ new Set(), project = ctx.getProjectByName(projectName);
5013
5022
  async function get(mod, seen = /* @__PURE__ */ new Map()) {
5014
- if (!mod || !mod.id) return;
5015
- if (mod.id === "\0@vitest/browser/context") return;
5023
+ if (!mod || !mod.id || mod.id === "\0@vitest/browser/context") return;
5016
5024
  if (seen.has(mod)) return seen.get(mod);
5017
5025
  let id = clearId(mod.id);
5018
5026
  seen.set(mod, id);
5019
- const rewrote = browser ? mod.file?.includes(project.browser.vite.config.cacheDir) ? mod.id : false : await project.vitenode.shouldExternalize(id);
5020
- if (rewrote) {
5021
- id = rewrote;
5022
- externalized.add(id);
5023
- seen.set(mod, id);
5024
- } else inlined.add(id);
5027
+ // TODO: how to know if it was rewritten(?) - what is rewritten?
5028
+ const rewrote = browser ? mod.file?.includes(project.browser.vite.config.cacheDir) ? mod.id : false : false;
5029
+ if (rewrote) id = rewrote, externalized.add(id), seen.set(mod, id);
5030
+ else inlined.add(id);
5025
5031
  const mods = Array.from(mod.importedModules).filter((i) => i.id && !i.id.includes("/vitest/dist/"));
5026
- graph[id] = (await Promise.all(mods.map((m) => get(m, seen)))).filter(Boolean);
5027
- return id;
5032
+ return graph[id] = (await Promise.all(mods.map((m) => get(m, seen)))).filter(Boolean), id;
5028
5033
  }
5029
5034
  if (browser && project.browser) await get(project.browser.vite.moduleGraph.getModuleById(id));
5030
5035
  else await get(project.vite.moduleGraph.getModuleById(id));
@@ -5076,21 +5081,19 @@ catch {}
5076
5081
  }
5077
5082
 
5078
5083
  function setup(ctx, _server) {
5079
- const wss = new WebSocketServer({ noServer: true });
5080
- const clients = /* @__PURE__ */ new Map();
5081
- const server = _server || ctx.server;
5084
+ const wss = new WebSocketServer({ noServer: true }), clients = /* @__PURE__ */ new Map(), server = _server || ctx.vite;
5082
5085
  server.httpServer?.on("upgrade", (request, socket, head) => {
5083
5086
  if (!request.url) return;
5084
5087
  const { pathname } = new URL(request.url, "http://localhost");
5085
- if (pathname !== API_PATH) return;
5086
- if (!isValidApiRequest(ctx.config, request)) {
5087
- socket.destroy();
5088
- return;
5088
+ if (pathname === API_PATH) {
5089
+ if (!isValidApiRequest(ctx.config, request)) {
5090
+ socket.destroy();
5091
+ return;
5092
+ }
5093
+ wss.handleUpgrade(request, socket, head, (ws) => {
5094
+ wss.emit("connection", ws, request), setupClient(ws);
5095
+ });
5089
5096
  }
5090
- wss.handleUpgrade(request, socket, head, (ws) => {
5091
- wss.emit("connection", ws, request);
5092
- setupClient(ws);
5093
- });
5094
5097
  });
5095
5098
  function setupClient(ws) {
5096
5099
  const rpc = createBirpc({
@@ -5104,8 +5107,7 @@ function setup(ctx, _server) {
5104
5107
  return ctx.state.getPaths();
5105
5108
  },
5106
5109
  async readTestFile(id) {
5107
- if (!ctx.state.filesMap.has(id) || !existsSync(id)) return null;
5108
- return promises.readFile(id, "utf-8");
5110
+ return !ctx.state.filesMap.has(id) || !existsSync(id) ? null : promises.readFile(id, "utf-8");
5109
5111
  },
5110
5112
  async saveTestFile(id, content) {
5111
5113
  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.`);
@@ -5120,9 +5122,6 @@ function setup(ctx, _server) {
5120
5122
  getConfig() {
5121
5123
  return ctx.getRootProject().serializedConfig;
5122
5124
  },
5123
- getResolvedProjectNames() {
5124
- return ctx.projects.map((p) => p.name);
5125
- },
5126
5125
  getResolvedProjectLabels() {
5127
5126
  return ctx.projects.map((p) => ({
5128
5127
  name: p.name,
@@ -5130,8 +5129,7 @@ function setup(ctx, _server) {
5130
5129
  }));
5131
5130
  },
5132
5131
  async getTransformResult(projectName, id, browser = false) {
5133
- const project = ctx.getProjectByName(projectName);
5134
- const result = browser ? await project.browser.vite.transformRequest(id) : await project.vitenode.transformRequest(id);
5132
+ const project = ctx.getProjectByName(projectName), result = browser ? await project.browser.vite.transformRequest(id) : await project.vite.transformRequest(id);
5135
5133
  if (result) {
5136
5134
  try {
5137
5135
  result.source = result.source || await promises.readFile(id, "utf-8");
@@ -5172,59 +5170,43 @@ function setup(ctx, _server) {
5172
5170
  ],
5173
5171
  serialize: (data) => stringify(data, stringifyReplace),
5174
5172
  deserialize: parse,
5175
- onTimeoutError(functionName) {
5176
- throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
5177
- }
5173
+ timeout: -1
5178
5174
  });
5179
- clients.set(ws, rpc);
5180
- ws.on("close", () => {
5181
- clients.delete(ws);
5175
+ clients.set(ws, rpc), ws.on("close", () => {
5176
+ clients.delete(ws), rpc.$close(/* @__PURE__ */ new Error("[vitest-api]: Pending methods while closing rpc"));
5182
5177
  });
5183
5178
  }
5184
5179
  ctx.reporters.push(new WebSocketReporter(ctx, wss, clients));
5185
5180
  }
5186
5181
  class WebSocketReporter {
5187
5182
  constructor(ctx, wss, clients) {
5188
- this.ctx = ctx;
5189
- this.wss = wss;
5190
- this.clients = clients;
5183
+ this.ctx = ctx, this.wss = wss, this.clients = clients;
5191
5184
  }
5192
- onCollected(files) {
5193
- if (this.clients.size === 0) return;
5194
- this.clients.forEach((client) => {
5195
- client.onCollected?.(files)?.catch?.(noop);
5185
+ onTestModuleCollected(testModule) {
5186
+ this.clients.size !== 0 && this.clients.forEach((client) => {
5187
+ client.onCollected?.([testModule.task])?.catch?.(noop);
5196
5188
  });
5197
5189
  }
5198
- onSpecsCollected(specs) {
5190
+ onTestRunStart(specifications) {
5199
5191
  if (this.clients.size === 0) return;
5192
+ const serializedSpecs = specifications.map((spec) => spec.toJSON());
5200
5193
  this.clients.forEach((client) => {
5201
- client.onSpecsCollected?.(specs)?.catch?.(noop);
5194
+ client.onSpecsCollected?.(serializedSpecs)?.catch?.(noop);
5202
5195
  });
5203
5196
  }
5204
5197
  async onTestCaseAnnotate(testCase, annotation) {
5205
- if (this.clients.size === 0) return;
5206
- this.clients.forEach((client) => {
5198
+ this.clients.size !== 0 && this.clients.forEach((client) => {
5207
5199
  client.onTestAnnotate?.(testCase.id, annotation)?.catch?.(noop);
5208
5200
  });
5209
5201
  }
5210
5202
  async onTaskUpdate(packs, events) {
5211
- if (this.clients.size === 0) return;
5212
- packs.forEach(([taskId, result]) => {
5213
- const task = this.ctx.state.idMap.get(taskId);
5214
- const isBrowser = task && task.file.pool === "browser";
5215
- result?.errors?.forEach((error) => {
5216
- if (isPrimitive(error)) return;
5217
- if (isBrowser) {
5218
- const project = this.ctx.getProjectByName(task.file.projectName || "");
5219
- error.stacks = project.browser?.parseErrorStacktrace(error);
5220
- } else error.stacks = parseErrorStacktrace(error);
5221
- });
5222
- });
5223
- this.clients.forEach((client) => {
5203
+ this.clients.size !== 0 && this.clients.forEach((client) => {
5224
5204
  client.onTaskUpdate?.(packs, events)?.catch?.(noop);
5225
5205
  });
5226
5206
  }
5227
- onFinished(files, errors) {
5207
+ onTestRunEnd(testModules, unhandledErrors) {
5208
+ if (!this.clients.size) return;
5209
+ const files = testModules.map((testModule) => testModule.task), errors = [...unhandledErrors];
5228
5210
  this.clients.forEach((client) => {
5229
5211
  client.onFinished?.(files, errors)?.catch?.(noop);
5230
5212
  });
@@ -5247,6 +5229,265 @@ var setup$1 = /*#__PURE__*/Object.freeze({
5247
5229
  setup: setup
5248
5230
  });
5249
5231
 
5232
+ function createDebugger(namespace) {
5233
+ const debug = createDebug(namespace);
5234
+ if (debug.enabled) return debug;
5235
+ }
5236
+
5237
+ const debug = createDebugger("vitest:ast-collect-info"), verbose = createDebugger("vitest:ast-collect-verbose");
5238
+ function astParseFile(filepath, code) {
5239
+ const ast = parseAst(code);
5240
+ if (verbose) verbose("Collecting", filepath, code);
5241
+ else debug?.("Collecting", filepath);
5242
+ const definitions = [], getName = (callee) => {
5243
+ if (!callee) return null;
5244
+ if (callee.type === "Identifier") return callee.name;
5245
+ if (callee.type === "CallExpression") return getName(callee.callee);
5246
+ if (callee.type === "TaggedTemplateExpression") return getName(callee.tag);
5247
+ if (callee.type === "MemberExpression")
5248
+ // call as `__vite_ssr__.test.skip()`
5249
+ return callee.object?.type === "Identifier" && [
5250
+ "it",
5251
+ "test",
5252
+ "describe",
5253
+ "suite"
5254
+ ].includes(callee.object.name) ? callee.object?.name : callee.object?.name?.startsWith("__vite_ssr_") || callee.object?.object?.name?.startsWith("__vite_ssr_") && callee.object?.property?.name === "Vitest" ? getName(callee.property) : getName(callee.object?.property);
5255
+ // unwrap (0, ...)
5256
+ if (callee.type === "SequenceExpression" && callee.expressions.length === 2) {
5257
+ const [e0, e1] = callee.expressions;
5258
+ if (e0.type === "Literal" && e0.value === 0) return getName(e1);
5259
+ }
5260
+ return null;
5261
+ };
5262
+ return ancestor(ast, { CallExpression(node) {
5263
+ const { callee } = node, name = getName(callee);
5264
+ if (!name) return;
5265
+ if (![
5266
+ "it",
5267
+ "test",
5268
+ "describe",
5269
+ "suite"
5270
+ ].includes(name)) {
5271
+ verbose?.(`Skipping ${name} (unknown call)`);
5272
+ return;
5273
+ }
5274
+ const property = callee?.property?.name;
5275
+ let mode = !property || property === name ? "run" : property;
5276
+ // they will be picked up in the next iteration
5277
+ if ([
5278
+ "each",
5279
+ "for",
5280
+ "skipIf",
5281
+ "runIf"
5282
+ ].includes(mode)) return;
5283
+ let start;
5284
+ const end = node.end;
5285
+ // .each or (0, __vite_ssr_exports_0__.test)()
5286
+ if (callee.type === "CallExpression" || callee.type === "SequenceExpression" || callee.type === "TaggedTemplateExpression") start = callee.end;
5287
+ else start = node.start;
5288
+ const messageNode = node.arguments?.[0];
5289
+ if (messageNode == null) {
5290
+ verbose?.(`Skipping node at ${node.start} because it doesn't have a name`);
5291
+ return;
5292
+ }
5293
+ let message;
5294
+ if (messageNode?.type === "Literal" || messageNode?.type === "TemplateLiteral") message = code.slice(messageNode.start + 1, messageNode.end - 1);
5295
+ else message = code.slice(messageNode.start, messageNode.end);
5296
+ if (message.startsWith("0,")) message = message.slice(2);
5297
+ // cannot statically analyze, so we always skip it
5298
+ if (message = message.replace(/__vite_ssr_import_\d+__\./g, "").replace(/__vi_import_\d+__\./g, ""), mode === "skipIf" || mode === "runIf") mode = "skip";
5299
+ const parentCalleeName = typeof callee?.callee === "object" && callee?.callee.type === "MemberExpression" && callee?.callee.property?.name;
5300
+ let isDynamicEach = parentCalleeName === "each" || parentCalleeName === "for";
5301
+ if (!isDynamicEach && callee.type === "TaggedTemplateExpression") {
5302
+ const property = callee.tag?.property?.name;
5303
+ isDynamicEach = property === "each" || property === "for";
5304
+ }
5305
+ debug?.("Found", name, message, `(${mode})`), definitions.push({
5306
+ start,
5307
+ end,
5308
+ name: message,
5309
+ type: name === "it" || name === "test" ? "test" : "suite",
5310
+ mode,
5311
+ task: null,
5312
+ dynamic: isDynamicEach
5313
+ });
5314
+ } }), {
5315
+ ast,
5316
+ definitions
5317
+ };
5318
+ }
5319
+ function createFailedFileTask(project, filepath, error) {
5320
+ const testFilepath = relative(project.config.root, filepath), file = {
5321
+ filepath,
5322
+ type: "suite",
5323
+ id: /* @__PURE__ */ generateHash$1(`${testFilepath}${project.config.name || ""}`),
5324
+ name: testFilepath,
5325
+ mode: "run",
5326
+ tasks: [],
5327
+ start: 0,
5328
+ end: 0,
5329
+ projectName: project.name,
5330
+ meta: {},
5331
+ pool: project.browser ? "browser" : project.config.pool,
5332
+ file: null,
5333
+ result: {
5334
+ state: "fail",
5335
+ errors: serializeError(project, error)
5336
+ }
5337
+ };
5338
+ return file.file = file, file;
5339
+ }
5340
+ 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 [{
5352
+ name: error.name,
5353
+ stack: error.stack,
5354
+ message: error.message
5355
+ }];
5356
+ }
5357
+ function createFileTask(testFilepath, code, requestMap, options) {
5358
+ const { definitions, ast } = astParseFile(testFilepath, code), file = {
5359
+ filepath: options.filepath,
5360
+ type: "suite",
5361
+ id: /* @__PURE__ */ generateHash$1(`${testFilepath}${options.name || ""}`),
5362
+ name: testFilepath,
5363
+ mode: "run",
5364
+ tasks: [],
5365
+ start: ast.start,
5366
+ end: ast.end,
5367
+ projectName: options.name,
5368
+ meta: {},
5369
+ pool: "browser",
5370
+ file: null
5371
+ };
5372
+ file.file = file;
5373
+ const indexMap = createIndexMap(code), map = requestMap && new TraceMap(requestMap);
5374
+ let lastSuite = file;
5375
+ const updateLatestSuite = (index) => {
5376
+ while (lastSuite.suite && lastSuite.end < index) lastSuite = lastSuite.suite;
5377
+ return lastSuite;
5378
+ };
5379
+ definitions.sort((a, b) => a.start - b.start).forEach((definition) => {
5380
+ const latestSuite = updateLatestSuite(definition.start);
5381
+ let mode = definition.mode;
5382
+ if (latestSuite.mode !== "run")
5383
+ // inherit suite mode, if it's set
5384
+ mode = latestSuite.mode;
5385
+ const processedLocation = indexMap.get(definition.start);
5386
+ let location;
5387
+ if (map && processedLocation) {
5388
+ const originalLocation = originalPositionFor(map, {
5389
+ line: processedLocation.line,
5390
+ column: processedLocation.column
5391
+ });
5392
+ 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}`);
5395
+ if (definition.type === "suite") {
5396
+ const task = {
5397
+ type: definition.type,
5398
+ id: "",
5399
+ suite: latestSuite,
5400
+ file,
5401
+ tasks: [],
5402
+ mode,
5403
+ name: definition.name,
5404
+ end: definition.end,
5405
+ start: definition.start,
5406
+ location,
5407
+ dynamic: definition.dynamic,
5408
+ meta: {}
5409
+ };
5410
+ definition.task = task, latestSuite.tasks.push(task), lastSuite = task;
5411
+ return;
5412
+ }
5413
+ const task = {
5414
+ type: definition.type,
5415
+ id: "",
5416
+ suite: latestSuite,
5417
+ file,
5418
+ mode,
5419
+ context: {},
5420
+ name: definition.name,
5421
+ end: definition.end,
5422
+ start: definition.start,
5423
+ location,
5424
+ dynamic: definition.dynamic,
5425
+ meta: {},
5426
+ timeout: 0,
5427
+ annotations: []
5428
+ };
5429
+ definition.task = task, latestSuite.tasks.push(task);
5430
+ }), calculateSuiteHash(file);
5431
+ const hasOnly = someTasksAreOnly(file);
5432
+ if (interpretTaskModes(file, options.testNamePattern, void 0, hasOnly, false, options.allowOnly), markDynamicTests(file.tasks), !file.tasks.length) file.result = {
5433
+ state: "fail",
5434
+ errors: [{
5435
+ name: "Error",
5436
+ message: `No test suite found in file ${options.filepath}`
5437
+ }]
5438
+ };
5439
+ return file;
5440
+ }
5441
+ async function astCollectTests(project, filepath) {
5442
+ const request = await transformSSR(project, filepath), testFilepath = relative(project.config.root, filepath);
5443
+ return request ? createFileTask(testFilepath, request.code, request.map, {
5444
+ name: project.config.name,
5445
+ filepath,
5446
+ allowOnly: project.config.allowOnly,
5447
+ testNamePattern: project.config.testNamePattern,
5448
+ 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.`)));
5450
+ }
5451
+ async function transformSSR(project, filepath) {
5452
+ const request = await project.vite.transformRequest(filepath, { ssr: false });
5453
+ return request ? await project.vite.ssrTransform(request.code, request.map, filepath) : null;
5454
+ }
5455
+ function createIndexMap(source) {
5456
+ const map = /* @__PURE__ */ new Map();
5457
+ let index = 0, line = 1, column = 1;
5458
+ for (const char of source) if (map.set(index++, {
5459
+ line,
5460
+ column
5461
+ }), char === "\n" || char === "\r\n") line++, column = 0;
5462
+ else column++;
5463
+ return map;
5464
+ }
5465
+ function markDynamicTests(tasks) {
5466
+ for (const task of tasks) {
5467
+ if (task.dynamic) task.id += "-dynamic";
5468
+ if ("tasks" in task) markDynamicTests(task.tasks);
5469
+ }
5470
+ }
5471
+ function escapeRegex(str) {
5472
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5473
+ }
5474
+ const kReplacers = new Map([
5475
+ ["%i", "\\d+?"],
5476
+ ["%#", "\\d+?"],
5477
+ ["%d", "[\\d.eE+-]+?"],
5478
+ ["%f", "[\\d.eE+-]+?"],
5479
+ ["%s", ".+?"],
5480
+ ["%j", ".+?"],
5481
+ ["%o", ".+?"],
5482
+ ["%%", "%"]
5483
+ ]);
5484
+ function escapeTestName(label, dynamic) {
5485
+ if (!dynamic) return escapeRegex(label);
5486
+ // Replace object access patterns ($value, $obj.a) with %s first
5487
+ let pattern = label.replace(/\$[a-z_.]+/gi, "%s");
5488
+ return pattern = escapeRegex(pattern), pattern = pattern.replace(/%[i#dfsjo%]/g, (m) => kReplacers.get(m) || m), pattern;
5489
+ }
5490
+
5250
5491
  class BrowserSessions {
5251
5492
  sessions = /* @__PURE__ */ new Map();
5252
5493
  sessionIds = /* @__PURE__ */ new Set();
@@ -5258,23 +5499,18 @@ class BrowserSessions {
5258
5499
  }
5259
5500
  createSession(sessionId, project, pool) {
5260
5501
  // this promise only waits for the WS connection with the orhcestrator to be established
5261
- const defer = createDefer();
5262
- const timeout = setTimeout(() => {
5263
- defer.reject(new Error(`Failed to connect to the browser session "${sessionId}" [${project.name}] within the timeout.`));
5502
+ const defer = createDefer(), timeout = setTimeout(() => {
5503
+ defer.reject(/* @__PURE__ */ new Error(`Failed to connect to the browser session "${sessionId}" [${project.name}] within the timeout.`));
5264
5504
  }, project.vitest.config.browser.connectTimeout ?? 6e4).unref();
5265
- this.sessions.set(sessionId, {
5505
+ return this.sessions.set(sessionId, {
5266
5506
  project,
5267
5507
  connected: () => {
5268
- defer.resolve();
5269
- clearTimeout(timeout);
5508
+ defer.resolve(), clearTimeout(timeout);
5270
5509
  },
5271
5510
  fail: (error) => {
5272
- defer.resolve();
5273
- clearTimeout(timeout);
5274
- pool.reject(error);
5511
+ defer.resolve(), clearTimeout(timeout), pool.reject(error);
5275
5512
  }
5276
- });
5277
- return defer;
5513
+ }), defer;
5278
5514
  }
5279
5515
  }
5280
5516
 
@@ -5285,7 +5521,7 @@ class FilesStatsCache {
5285
5521
  }
5286
5522
  async populateStats(root, specs) {
5287
5523
  const promises = specs.map((spec) => {
5288
- const key = `${spec[0].name}:${relative(root, spec.moduleId)}`;
5524
+ const key = `${spec.project.name}:${relative(root, spec.moduleId)}`;
5289
5525
  return this.updateStats(spec.moduleId, key);
5290
5526
  });
5291
5527
  await Promise.all(promises);
@@ -5315,37 +5551,25 @@ class ResultsCache {
5315
5551
  return this.cachePath;
5316
5552
  }
5317
5553
  setConfig(root, config) {
5318
- this.root = root;
5319
- if (config) this.cachePath = resolve(config.dir, "results.json");
5554
+ if (this.root = root, config) this.cachePath = resolve(config.dir, "results.json");
5320
5555
  }
5321
5556
  getResults(key) {
5322
5557
  return this.cache.get(key);
5323
5558
  }
5324
5559
  async readFromCache() {
5325
- if (!this.cachePath) return;
5326
- if (!fs.existsSync(this.cachePath)) return;
5327
- const resultsCache = await fs.promises.readFile(this.cachePath, "utf8");
5328
- const { results, version } = JSON.parse(resultsCache || "[]");
5329
- const [major, minor] = version.split(".");
5560
+ if (!this.cachePath || !fs.existsSync(this.cachePath)) return;
5561
+ const resultsCache = await fs.promises.readFile(this.cachePath, "utf8"), { results, version } = JSON.parse(resultsCache || "[]"), [major, minor] = version.split(".");
5330
5562
  // handling changed in 0.30.0
5331
- if (major > 0 || Number(minor) >= 30) {
5332
- this.cache = new Map(results);
5333
- this.version = version;
5334
- results.forEach(([spec]) => {
5335
- const [projectName, relativePath] = spec.split(":");
5336
- const keyMap = this.workspacesKeyMap.get(relativePath) || [];
5337
- keyMap.push(projectName);
5338
- this.workspacesKeyMap.set(relativePath, keyMap);
5339
- });
5340
- }
5563
+ if (major > 0 || Number(minor) >= 30) this.cache = new Map(results), this.version = version, results.forEach(([spec]) => {
5564
+ const [projectName, relativePath] = spec.split(":"), keyMap = this.workspacesKeyMap.get(relativePath) || [];
5565
+ keyMap.push(projectName), this.workspacesKeyMap.set(relativePath, keyMap);
5566
+ });
5341
5567
  }
5342
5568
  updateResults(files) {
5343
5569
  files.forEach((file) => {
5344
5570
  const result = file.result;
5345
5571
  if (!result) return;
5346
- const duration = result.duration || 0;
5347
- // store as relative, so cache would be the same in CI and locally
5348
- const relativePath = relative(this.root, file.filepath);
5572
+ const duration = result.duration || 0, relativePath = relative(this.root, file.filepath);
5349
5573
  this.cache.set(`${file.projectName || ""}:${relativePath}`, {
5350
5574
  duration: duration >= 0 ? duration : 0,
5351
5575
  failed: result.state === "fail"
@@ -5359,8 +5583,7 @@ class ResultsCache {
5359
5583
  }
5360
5584
  async writeToCache() {
5361
5585
  if (!this.cachePath) return;
5362
- const results = Array.from(this.cache.entries());
5363
- const cacheDirname = dirname(this.cachePath);
5586
+ const results = Array.from(this.cache.entries()), cacheDirname = dirname(this.cachePath);
5364
5587
  if (!fs.existsSync(cacheDirname)) await fs.promises.mkdir(cacheDirname, { recursive: true });
5365
5588
  const cache = JSON.stringify({
5366
5589
  version: this.version,
@@ -5388,6 +5611,34 @@ class VitestCache {
5388
5611
  }
5389
5612
  }
5390
5613
 
5614
+ class ServerModuleRunner extends ModuleRunner {
5615
+ constructor(environment, resolver, config) {
5616
+ const fetchModule = createFetchModuleFunction(resolver, false);
5617
+ super({
5618
+ hmr: false,
5619
+ sourcemapInterceptor: "node",
5620
+ transport: { async invoke(event) {
5621
+ if (event.type !== "custom") throw new Error(`Vitest Module Runner doesn't support Vite HMR events.`);
5622
+ const { data } = event.data;
5623
+ try {
5624
+ const result = await fetchModule(data[0], data[1], environment, data[2]);
5625
+ return { result };
5626
+ } catch (error) {
5627
+ return { error };
5628
+ }
5629
+ } }
5630
+ }, new VitestModuleEvaluator()), this.environment = environment, this.config = config;
5631
+ }
5632
+ async import(rawId) {
5633
+ const resolved = await this.environment.pluginContainer.resolveId(rawId, this.config.root);
5634
+ if (!resolved) return super.import(rawId);
5635
+ // Vite will make "@vitest/coverage-v8" into "@vitest/coverage-v8.js" url
5636
+ // instead of using an actual file path-like URL, so we resolve it here first
5637
+ const url = normalizeResolvedIdToUrl(this.environment, resolved.id);
5638
+ return super.import(url);
5639
+ }
5640
+ }
5641
+
5391
5642
  class FilesNotFoundError extends Error {
5392
5643
  code = "VITEST_FILES_NOT_FOUND";
5393
5644
  constructor(mode) {
@@ -5443,43 +5694,26 @@ function highlightCode(id, source, colors) {
5443
5694
  });
5444
5695
  }
5445
5696
 
5446
- const PAD = " ";
5447
- const ESC$1 = "\x1B[";
5448
- const ERASE_DOWN = `${ESC$1}J`;
5449
- const ERASE_SCROLLBACK = `${ESC$1}3J`;
5450
- const CURSOR_TO_START = `${ESC$1}1;1H`;
5451
- const HIDE_CURSOR = `${ESC$1}?25l`;
5452
- const SHOW_CURSOR = `${ESC$1}?25h`;
5453
- const CLEAR_SCREEN = "\x1Bc";
5697
+ const PAD = " ", ESC$1 = "\x1B[", ERASE_DOWN = `${ESC$1}J`, ERASE_SCROLLBACK = `${ESC$1}3J`, CURSOR_TO_START = `${ESC$1}1;1H`, HIDE_CURSOR = `${ESC$1}?25l`, SHOW_CURSOR = `${ESC$1}?25h`, CLEAR_SCREEN = "\x1Bc";
5454
5698
  class Logger {
5455
5699
  _clearScreenPending;
5456
5700
  _highlights = /* @__PURE__ */ new Map();
5457
5701
  cleanupListeners = [];
5458
5702
  console;
5459
5703
  constructor(ctx, outputStream = process.stdout, errorStream = process.stderr) {
5460
- this.ctx = ctx;
5461
- this.outputStream = outputStream;
5462
- this.errorStream = errorStream;
5463
- this.console = new Console({
5704
+ if (this.ctx = ctx, this.outputStream = outputStream, this.errorStream = errorStream, this.console = new Console({
5464
5705
  stdout: outputStream,
5465
5706
  stderr: errorStream
5466
- });
5467
- this._highlights.clear();
5468
- this.addCleanupListeners();
5469
- this.registerUnhandledRejection();
5470
- if (this.outputStream.isTTY) this.outputStream.write(HIDE_CURSOR);
5707
+ }), this._highlights.clear(), this.addCleanupListeners(), this.registerUnhandledRejection(), this.outputStream.isTTY) this.outputStream.write(HIDE_CURSOR);
5471
5708
  }
5472
5709
  log(...args) {
5473
- this._clearScreen();
5474
- this.console.log(...args);
5710
+ this._clearScreen(), this.console.log(...args);
5475
5711
  }
5476
5712
  error(...args) {
5477
- this._clearScreen();
5478
- this.console.error(...args);
5713
+ this._clearScreen(), this.console.error(...args);
5479
5714
  }
5480
5715
  warn(...args) {
5481
- this._clearScreen();
5482
- this.console.warn(...args);
5716
+ this._clearScreen(), this.console.warn(...args);
5483
5717
  }
5484
5718
  clearFullScreen(message = "") {
5485
5719
  if (!this.ctx.config.clearScreen) {
@@ -5494,14 +5728,12 @@ class Logger {
5494
5728
  this.console.log(message);
5495
5729
  return;
5496
5730
  }
5497
- this._clearScreenPending = message;
5498
- if (force) this._clearScreen();
5731
+ if (this._clearScreenPending = message, force) this._clearScreen();
5499
5732
  }
5500
5733
  _clearScreen() {
5501
5734
  if (this._clearScreenPending == null) return;
5502
5735
  const log = this._clearScreenPending;
5503
- this._clearScreenPending = void 0;
5504
- this.console.log(`${CURSOR_TO_START}${ERASE_DOWN}${log}`);
5736
+ this._clearScreenPending = void 0, this.console.log(`${CURSOR_TO_START}${ERASE_DOWN}${log}`);
5505
5737
  }
5506
5738
  printError(err, options = {}) {
5507
5739
  printError(err, this.ctx, this, options);
@@ -5516,8 +5748,7 @@ class Logger {
5516
5748
  highlight(filename, source) {
5517
5749
  if (this._highlights.has(filename)) return this._highlights.get(filename);
5518
5750
  const code = highlightCode(filename, source);
5519
- this._highlights.set(filename, code);
5520
- return code;
5751
+ return this._highlights.set(filename, code), code;
5521
5752
  }
5522
5753
  printNoTestFound(filters) {
5523
5754
  const config = this.ctx.config;
@@ -5530,34 +5761,22 @@ class Logger {
5530
5761
  const projectsFilter = toArray(config.project);
5531
5762
  if (projectsFilter.length) this.console.error(c.dim("projects: ") + c.yellow(projectsFilter.join(comma)));
5532
5763
  this.ctx.projects.forEach((project) => {
5533
- const config = project.config;
5534
- const printConfig = !project.isRootProject() && project.name;
5764
+ const config = project.config, printConfig = !project.isRootProject() && project.name;
5535
5765
  if (printConfig) this.console.error(`\n${formatProjectName(project)}\n`);
5536
5766
  if (config.include) this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
5537
5767
  if (config.exclude) this.console.error(c.dim("exclude: ") + c.yellow(config.exclude.join(comma)));
5538
- if (config.typecheck.enabled) {
5539
- this.console.error(c.dim("typecheck include: ") + c.yellow(config.typecheck.include.join(comma)));
5540
- this.console.error(c.dim("typecheck exclude: ") + c.yellow(config.typecheck.exclude.join(comma)));
5541
- }
5542
- });
5543
- this.console.error();
5768
+ 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)));
5769
+ }), this.console.error();
5544
5770
  }
5545
5771
  printBanner() {
5546
5772
  this.log();
5547
- const color = this.ctx.config.watch ? "blue" : "cyan";
5548
- const mode = this.ctx.config.watch ? "DEV" : "RUN";
5549
- this.log(withLabel(color, mode, `v${this.ctx.version} `) + c.gray(this.ctx.config.root));
5550
- if (this.ctx.config.sequence.sequencer === RandomSequencer) this.log(PAD + c.gray(`Running tests with seed "${this.ctx.config.sequence.seed}"`));
5773
+ const color = this.ctx.config.watch ? "blue" : "cyan", mode = this.ctx.config.watch ? "DEV" : "RUN";
5774
+ if (this.log(withLabel(color, mode, `v${this.ctx.version} `) + c.gray(this.ctx.config.root)), this.ctx.config.sequence.sequencer === RandomSequencer) this.log(PAD + c.gray(`Running tests with seed "${this.ctx.config.sequence.seed}"`));
5551
5775
  if (this.ctx.config.ui) {
5552
- const host = this.ctx.config.api?.host || "localhost";
5553
- const port = this.ctx.server.config.server.port;
5554
- const base = this.ctx.config.uiBase;
5776
+ const host = this.ctx.config.api?.host || "localhost", port = this.ctx.vite.config.server.port, base = this.ctx.config.uiBase;
5555
5777
  this.log(PAD + c.dim(c.green(`UI started at http://${host}:${c.bold(port)}${base}`)));
5556
5778
  } else if (this.ctx.config.api?.port) {
5557
- const resolvedUrls = this.ctx.server.resolvedUrls;
5558
- // workaround for https://github.com/vitejs/vite/issues/15438, it was fixed in vite 5.1
5559
- const fallbackUrl = `http://${this.ctx.config.api.host || "localhost"}:${this.ctx.config.api.port}`;
5560
- const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0] ?? fallbackUrl;
5779
+ const resolvedUrls = this.ctx.vite.resolvedUrls, fallbackUrl = `http://${this.ctx.config.api.host || "localhost"}:${this.ctx.config.api.port}`, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0] ?? fallbackUrl;
5561
5780
  this.log(PAD + c.dim(c.green(`API started at ${new URL("/", origin)}`)));
5562
5781
  }
5563
5782
  if (this.ctx.coverageProvider) this.log(PAD + c.dim("Coverage enabled with ") + c.yellow(this.ctx.coverageProvider.name));
@@ -5566,35 +5785,26 @@ class Logger {
5566
5785
  }
5567
5786
  printBrowserBanner(project) {
5568
5787
  if (!project.browser) return;
5569
- const resolvedUrls = project.browser.vite.resolvedUrls;
5570
- const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
5788
+ const resolvedUrls = project.browser.vite.resolvedUrls, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
5571
5789
  if (!origin) return;
5572
- const output = project.isRootProject() ? "" : formatProjectName(project);
5573
- const provider = project.browser.provider.name;
5574
- const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5790
+ const output = project.isRootProject() ? "" : formatProjectName(project), provider = project.browser.provider.name, providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5575
5791
  this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/__vitest_test__/", origin))}\n`));
5576
5792
  }
5577
5793
  printUnhandledErrors(errors) {
5578
5794
  const errorMessage = c.red(c.bold(`\nVitest caught ${errors.length} unhandled error${errors.length > 1 ? "s" : ""} during the test run.
5579
5795
  This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.`));
5580
- this.error(errorBanner("Unhandled Errors"));
5581
- this.error(errorMessage);
5582
- errors.forEach((err) => {
5796
+ this.error(errorBanner("Unhandled Errors")), this.error(errorMessage), errors.forEach((err) => {
5583
5797
  this.printError(err, {
5584
5798
  fullStack: true,
5585
5799
  type: err.type || "Unhandled Error"
5586
5800
  });
5587
- });
5588
- this.error(c.red(divider()));
5801
+ }), this.error(c.red(divider()));
5589
5802
  }
5590
5803
  printSourceTypeErrors(errors) {
5591
5804
  const errorMessage = c.red(c.bold(`\nVitest found ${errors.length} error${errors.length > 1 ? "s" : ""} not related to your test files.`));
5592
- this.log(errorBanner("Source Errors"));
5593
- this.log(errorMessage);
5594
- errors.forEach((err) => {
5805
+ this.log(errorBanner("Source Errors")), this.log(errorMessage), errors.forEach((err) => {
5595
5806
  this.printError(err, { fullStack: true });
5596
- });
5597
- this.log(c.red(divider()));
5807
+ }), this.log(c.red(divider()));
5598
5808
  }
5599
5809
  getColumns() {
5600
5810
  return "columns" in this.outputStream ? this.outputStream.columns : 80;
@@ -5604,38 +5814,25 @@ This might cause false positive tests. Resolve unhandled errors to make sure you
5604
5814
  }
5605
5815
  addCleanupListeners() {
5606
5816
  const cleanup = () => {
5607
- this.cleanupListeners.forEach((fn) => fn());
5608
- if (this.outputStream.isTTY) this.outputStream.write(SHOW_CURSOR);
5609
- };
5610
- const onExit = (signal, exitCode) => {
5611
- cleanup();
5817
+ if (this.cleanupListeners.forEach((fn) => fn()), this.outputStream.isTTY) this.outputStream.write(SHOW_CURSOR);
5818
+ }, onExit = (signal, exitCode) => {
5612
5819
  // Interrupted signals don't set exit code automatically.
5613
5820
  // Use same exit code as node: https://nodejs.org/api/process.html#signal-events
5614
- if (process.exitCode === void 0) process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
5821
+ if (cleanup(), process.exitCode === void 0) process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
5615
5822
  process.exit();
5616
5823
  };
5617
- process.once("SIGINT", onExit);
5618
- process.once("SIGTERM", onExit);
5619
- process.once("exit", onExit);
5620
- this.ctx.onClose(() => {
5621
- process.off("SIGINT", onExit);
5622
- process.off("SIGTERM", onExit);
5623
- process.off("exit", onExit);
5624
- cleanup();
5824
+ process.once("SIGINT", onExit), process.once("SIGTERM", onExit), process.once("exit", onExit), this.ctx.onClose(() => {
5825
+ process.off("SIGINT", onExit), process.off("SIGTERM", onExit), process.off("exit", onExit), cleanup();
5625
5826
  });
5626
5827
  }
5627
5828
  registerUnhandledRejection() {
5628
5829
  const onUnhandledRejection = (err) => {
5629
- process.exitCode = 1;
5630
- this.printError(err, {
5830
+ process.exitCode = 1, this.printError(err, {
5631
5831
  fullStack: true,
5632
5832
  type: "Unhandled Rejection"
5633
- });
5634
- this.error("\n\n");
5635
- process.exit();
5833
+ }), this.error("\n\n"), process.exit();
5636
5834
  };
5637
- process.on("unhandledRejection", onUnhandledRejection);
5638
- this.ctx.onClose(() => {
5835
+ process.on("unhandledRejection", onUnhandledRejection), this.ctx.onClose(() => {
5639
5836
  process.off("unhandledRejection", onUnhandledRejection);
5640
5837
  });
5641
5838
  }
@@ -5651,36 +5848,26 @@ class VitestPackageInstaller {
5651
5848
  if (process.versions.pnp) {
5652
5849
  const targetRequire = createRequire(__dirname);
5653
5850
  try {
5654
- targetRequire.resolve(dependency, { paths: [root, __dirname] });
5655
- return true;
5851
+ return targetRequire.resolve(dependency, { paths: [root, __dirname] }), true;
5656
5852
  } catch {}
5657
5853
  }
5658
5854
  if (/* @__PURE__ */ isPackageExists(dependency, { paths: [root, __dirname] })) return true;
5659
- process.stderr.write(c.red(`${c.inverse(c.red(" MISSING DEPENDENCY "))} Cannot find dependency '${dependency}'\n\n`));
5660
- if (!isTTY) return false;
5661
- const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; });
5662
- const { install } = await prompts.default({
5855
+ 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({
5663
5857
  type: "confirm",
5664
5858
  name: "install",
5665
5859
  message: c.reset(`Do you want to install ${c.green(dependency)}?`)
5666
5860
  });
5667
5861
  if (install) {
5668
5862
  const packageName = version ? `${dependency}@${version}` : dependency;
5669
- await (await import('./index.D3XRDfWc.js')).installPackage(packageName, { dev: true });
5670
- // TODO: somehow it fails to load the package after installation, remove this when it's fixed
5671
- process.stderr.write(c.yellow(`\nPackage ${packageName} installed, re-run the command to start.\n`));
5672
- process.exit();
5673
- return true;
5863
+ return await (await import('./index.D3XRDfWc.js')).installPackage(packageName, { dev: true }), process.stderr.write(c.yellow(`\nPackage ${packageName} installed, re-run the command to start.\n`)), process.exit(), true;
5674
5864
  }
5675
5865
  return false;
5676
5866
  }
5677
5867
  }
5678
5868
 
5679
- function serializeConfig(config, coreConfig, viteConfig) {
5680
- const optimizer = config.deps?.optimizer;
5681
- const poolOptions = config.poolOptions;
5682
- // Resolve from server.config to avoid comparing against default value
5683
- const isolate = viteConfig?.test?.isolate;
5869
+ function serializeConfig(project) {
5870
+ const { config, globalConfig } = project, viteConfig = project._vite?.config, optimizer = config.deps?.optimizer || {}, poolOptions = config.poolOptions, isolate = viteConfig?.test?.isolate;
5684
5871
  return {
5685
5872
  environmentOptions: config.environmentOptions,
5686
5873
  mode: config.mode,
@@ -5714,8 +5901,7 @@ function serializeConfig(config, coreConfig, viteConfig) {
5714
5901
  snapshotEnvironment: config.snapshotEnvironment,
5715
5902
  passWithNoTests: config.passWithNoTests,
5716
5903
  coverage: ((coverage) => {
5717
- const htmlReporter = coverage.reporter.find(([reporterName]) => reporterName === "html");
5718
- const subdir = htmlReporter && htmlReporter[1]?.subdir;
5904
+ const htmlReporter = coverage.reporter.find(([reporterName]) => reporterName === "html"), subdir = htmlReporter && htmlReporter[1]?.subdir;
5719
5905
  return {
5720
5906
  reportsDirectory: coverage.reportsDirectory,
5721
5907
  provider: coverage.provider,
@@ -5727,51 +5913,48 @@ function serializeConfig(config, coreConfig, viteConfig) {
5727
5913
  fakeTimers: config.fakeTimers,
5728
5914
  poolOptions: {
5729
5915
  forks: {
5730
- singleFork: poolOptions?.forks?.singleFork ?? coreConfig.poolOptions?.forks?.singleFork ?? false,
5731
- isolate: poolOptions?.forks?.isolate ?? isolate ?? coreConfig.poolOptions?.forks?.isolate ?? true
5916
+ singleFork: poolOptions?.forks?.singleFork ?? globalConfig.poolOptions?.forks?.singleFork ?? false,
5917
+ isolate: poolOptions?.forks?.isolate ?? isolate ?? globalConfig.poolOptions?.forks?.isolate ?? true
5732
5918
  },
5733
5919
  threads: {
5734
- singleThread: poolOptions?.threads?.singleThread ?? coreConfig.poolOptions?.threads?.singleThread ?? false,
5735
- isolate: poolOptions?.threads?.isolate ?? isolate ?? coreConfig.poolOptions?.threads?.isolate ?? true
5920
+ singleThread: poolOptions?.threads?.singleThread ?? globalConfig.poolOptions?.threads?.singleThread ?? false,
5921
+ isolate: poolOptions?.threads?.isolate ?? isolate ?? globalConfig.poolOptions?.threads?.isolate ?? true
5736
5922
  },
5737
- vmThreads: { singleThread: poolOptions?.vmThreads?.singleThread ?? coreConfig.poolOptions?.vmThreads?.singleThread ?? false },
5738
- vmForks: { singleFork: poolOptions?.vmForks?.singleFork ?? coreConfig.poolOptions?.vmForks?.singleFork ?? false }
5923
+ vmThreads: { singleThread: poolOptions?.vmThreads?.singleThread ?? globalConfig.poolOptions?.vmThreads?.singleThread ?? false },
5924
+ vmForks: { singleFork: poolOptions?.vmForks?.singleFork ?? globalConfig.poolOptions?.vmForks?.singleFork ?? false }
5739
5925
  },
5740
5926
  deps: {
5741
5927
  web: config.deps.web || {},
5742
- optimizer: {
5743
- web: { enabled: optimizer?.web?.enabled ?? true },
5744
- ssr: { enabled: optimizer?.ssr?.enabled ?? true }
5745
- },
5928
+ optimizer: Object.entries(optimizer).reduce((acc, [name, option]) => {
5929
+ return acc[name] = { enabled: option?.enabled ?? false }, acc;
5930
+ }, {}),
5746
5931
  interopDefault: config.deps.interopDefault,
5747
5932
  moduleDirectories: config.deps.moduleDirectories
5748
5933
  },
5749
5934
  snapshotOptions: {
5750
5935
  snapshotEnvironment: void 0,
5751
- updateSnapshot: coreConfig.snapshotOptions.updateSnapshot,
5752
- snapshotFormat: {
5753
- ...coreConfig.snapshotOptions.snapshotFormat,
5754
- compareKeys: void 0
5755
- },
5756
- expand: config.snapshotOptions.expand ?? coreConfig.snapshotOptions.expand
5936
+ updateSnapshot: globalConfig.snapshotOptions.updateSnapshot,
5937
+ snapshotFormat: { ...globalConfig.snapshotOptions.snapshotFormat },
5938
+ expand: config.snapshotOptions.expand ?? globalConfig.snapshotOptions.expand
5757
5939
  },
5758
5940
  sequence: {
5759
- shuffle: coreConfig.sequence.shuffle,
5760
- concurrent: coreConfig.sequence.concurrent,
5761
- seed: coreConfig.sequence.seed,
5762
- hooks: coreConfig.sequence.hooks,
5763
- setupFiles: coreConfig.sequence.setupFiles
5941
+ shuffle: globalConfig.sequence.shuffle,
5942
+ concurrent: globalConfig.sequence.concurrent,
5943
+ seed: globalConfig.sequence.seed,
5944
+ hooks: globalConfig.sequence.hooks,
5945
+ setupFiles: globalConfig.sequence.setupFiles
5764
5946
  },
5765
- inspect: coreConfig.inspect,
5766
- inspectBrk: coreConfig.inspectBrk,
5767
- inspector: coreConfig.inspector,
5947
+ inspect: globalConfig.inspect,
5948
+ inspectBrk: globalConfig.inspectBrk,
5949
+ inspector: globalConfig.inspector,
5768
5950
  watch: config.watch,
5769
- includeTaskLocation: config.includeTaskLocation ?? coreConfig.includeTaskLocation,
5951
+ includeTaskLocation: config.includeTaskLocation ?? globalConfig.includeTaskLocation,
5770
5952
  env: {
5771
5953
  ...viteConfig?.env,
5772
5954
  ...config.env
5773
5955
  },
5774
5956
  browser: ((browser) => {
5957
+ const provider = project.browser?.provider;
5775
5958
  return {
5776
5959
  name: browser.name,
5777
5960
  headless: browser.headless,
@@ -5781,12 +5964,14 @@ function serializeConfig(config, coreConfig, viteConfig) {
5781
5964
  viewport: browser.viewport,
5782
5965
  screenshotFailures: browser.screenshotFailures,
5783
5966
  locators: { testIdAttribute: browser.locators.testIdAttribute },
5784
- providerOptions: browser.provider === "playwright" ? { actionTimeout: browser.providerOptions?.context?.actionTimeout } : {}
5967
+ providerOptions: provider?.name === "playwright" ? { actionTimeout: provider?.options?.actionTimeout } : {},
5968
+ trackUnhandledErrors: browser.trackUnhandledErrors ?? true
5785
5969
  };
5786
5970
  })(config.browser),
5787
5971
  standalone: config.standalone,
5788
- printConsoleTrace: config.printConsoleTrace ?? coreConfig.printConsoleTrace,
5789
- benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples }
5972
+ printConsoleTrace: config.printConsoleTrace ?? globalConfig.printConsoleTrace,
5973
+ benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples },
5974
+ serializedDefines: config.browser.enabled ? "" : project._serializedDefines || ""
5790
5975
  };
5791
5976
  }
5792
5977
 
@@ -5795,7 +5980,7 @@ async function loadGlobalSetupFiles(runner, globalSetup) {
5795
5980
  return Promise.all(globalSetupFiles.map((file) => loadGlobalSetupFile(file, runner)));
5796
5981
  }
5797
5982
  async function loadGlobalSetupFile(file, runner) {
5798
- const m = await runner.executeFile(file);
5983
+ const m = await runner.import(file);
5799
5984
  for (const exp of [
5800
5985
  "default",
5801
5986
  "setup",
@@ -5805,211 +5990,24 @@ async function loadGlobalSetupFile(file, runner) {
5805
5990
  file,
5806
5991
  setup: m.default
5807
5992
  };
5808
- else if (m.setup || m.teardown) return {
5993
+ if (m.setup || m.teardown) return {
5809
5994
  file,
5810
5995
  setup: m.setup,
5811
5996
  teardown: m.teardown
5812
5997
  };
5813
- else throw new Error(`invalid globalSetup file ${file}. Must export setup, teardown or have a default export`);
5998
+ throw new Error(`invalid globalSetup file ${file}. Must export setup, teardown or have a default export`);
5814
5999
  }
5815
6000
 
5816
6001
  function CoverageTransform(ctx) {
5817
6002
  return {
5818
6003
  name: "vitest:coverage-transform",
6004
+ enforce: "post",
5819
6005
  transform(srcCode, id) {
5820
- return ctx.coverageProvider?.onFileTransform?.(srcCode, normalizeRequestId(id), this);
5821
- }
5822
- };
5823
- }
5824
-
5825
- function MocksPlugins(options = {}) {
5826
- const normalizedDistDir = normalize(distDir);
5827
- return [hoistMocksPlugin({
5828
- filter(id) {
5829
- if (id.includes(normalizedDistDir)) return false;
5830
- if (options.filter) return options.filter(id);
5831
- return true;
5832
- },
5833
- codeFrameGenerator(node, id, code) {
5834
- return generateCodeFrame(code, 4, node.start + 1);
5835
- }
5836
- }), automockPlugin()];
5837
- }
5838
-
5839
- function generateCssFilenameHash(filepath) {
5840
- return hash("sha1", filepath, "hex").slice(0, 6);
5841
- }
5842
- function generateScopedClassName(strategy, name, filename) {
5843
- // should be configured by Vite defaults
5844
- if (strategy === "scoped") return null;
5845
- if (strategy === "non-scoped") return name;
5846
- const hash = generateCssFilenameHash(filename);
5847
- return `_${name}_${hash}`;
5848
- }
5849
-
5850
- const LogLevels = {
5851
- silent: 0,
5852
- error: 1,
5853
- warn: 2,
5854
- info: 3
5855
- };
5856
- function clearScreen(logger) {
5857
- const repeatCount = process.stdout.rows - 2;
5858
- const blank = repeatCount > 0 ? "\n".repeat(repeatCount) : "";
5859
- logger.clearScreen(blank);
5860
- }
5861
- let lastType;
5862
- let lastMsg;
5863
- let sameCount = 0;
5864
- // Only initialize the timeFormatter when the timestamp option is used, and
5865
- // reuse it across all loggers
5866
- let timeFormatter;
5867
- function getTimeFormatter() {
5868
- timeFormatter ??= new Intl.DateTimeFormat(void 0, {
5869
- hour: "numeric",
5870
- minute: "numeric",
5871
- second: "numeric"
5872
- });
5873
- return timeFormatter;
5874
- }
5875
- // This is copy-pasted and needs to be synced from time to time. Ideally, Vite's `createLogger` should accept a custom `console`
5876
- // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/logger.ts?rgh-link-date=2024-10-16T23%3A29%3A19Z
5877
- // When Vitest supports only Vite 6 and above, we can use Vite's `createLogger({ console })`
5878
- // https://github.com/vitejs/vite/pull/18379
5879
- function createViteLogger(console, level = "info", options = {}) {
5880
- const loggedErrors = /* @__PURE__ */ new WeakSet();
5881
- const { prefix = "[vite]", allowClearScreen = true } = options;
5882
- const thresh = LogLevels[level];
5883
- const canClearScreen = allowClearScreen && process.stdout.isTTY && !process.env.CI;
5884
- const clear = canClearScreen ? clearScreen : () => {};
5885
- function format(type, msg, options = {}) {
5886
- if (options.timestamp) {
5887
- let tag = "";
5888
- if (type === "info") tag = c.cyan(c.bold(prefix));
5889
- else if (type === "warn") tag = c.yellow(c.bold(prefix));
5890
- else tag = c.red(c.bold(prefix));
5891
- const environment = options.environment ? `${options.environment} ` : "";
5892
- return `${c.dim(getTimeFormatter().format(/* @__PURE__ */ new Date()))} ${tag} ${environment}${msg}`;
5893
- } else return msg;
5894
- }
5895
- function output(type, msg, options = {}) {
5896
- if (thresh >= LogLevels[type]) {
5897
- const method = type === "info" ? "log" : type;
5898
- if (options.error) loggedErrors.add(options.error);
5899
- if (canClearScreen) if (type === lastType && msg === lastMsg) {
5900
- sameCount++;
5901
- clear(console);
5902
- console[method](format(type, msg, options), c.yellow(`(x${sameCount + 1})`));
5903
- } else {
5904
- sameCount = 0;
5905
- lastMsg = msg;
5906
- lastType = type;
5907
- if (options.clear) clear(console);
5908
- console[method](format(type, msg, options));
5909
- }
5910
- else console[method](format(type, msg, options));
5911
- }
5912
- }
5913
- const warnedMessages = /* @__PURE__ */ new Set();
5914
- const logger = {
5915
- hasWarned: false,
5916
- info(msg, opts) {
5917
- output("info", msg, opts);
5918
- },
5919
- warn(msg, opts) {
5920
- logger.hasWarned = true;
5921
- output("warn", msg, opts);
5922
- },
5923
- warnOnce(msg, opts) {
5924
- if (warnedMessages.has(msg)) return;
5925
- logger.hasWarned = true;
5926
- output("warn", msg, opts);
5927
- warnedMessages.add(msg);
5928
- },
5929
- error(msg, opts) {
5930
- logger.hasWarned = true;
5931
- output("error", msg, opts);
5932
- },
5933
- clearScreen(type) {
5934
- if (thresh >= LogLevels[type]) clear(console);
5935
- },
5936
- hasErrorLogged(error) {
5937
- return loggedErrors.has(error);
5938
- }
5939
- };
5940
- return logger;
5941
- }
5942
- // silence warning by Vite for statically not analyzable dynamic import
5943
- function silenceImportViteIgnoreWarning(logger) {
5944
- return {
5945
- ...logger,
5946
- warn(msg, options) {
5947
- if (msg.includes("The above dynamic import cannot be analyzed by Vite")) return;
5948
- logger.warn(msg, options);
6006
+ return ctx.coverageProvider?.onFileTransform?.(srcCode, id, this);
5949
6007
  }
5950
6008
  };
5951
6009
  }
5952
6010
 
5953
- const cssLangs = "\\.(?:css|less|sass|scss|styl|stylus|pcss|postcss)(?:$|\\?)";
5954
- const cssLangRE = new RegExp(cssLangs);
5955
- const cssModuleRE = new RegExp(`\\.module${cssLangs}`);
5956
- const cssInlineRE = /[?&]inline(?:&|$)/;
5957
- function isCSS(id) {
5958
- return cssLangRE.test(id);
5959
- }
5960
- function isCSSModule(id) {
5961
- return cssModuleRE.test(id);
5962
- }
5963
- // inline css requests are expected to just return the
5964
- // string content directly and not the proxy module
5965
- function isInline(id) {
5966
- return cssInlineRE.test(id);
5967
- }
5968
- function getCSSModuleProxyReturn(strategy, filename) {
5969
- if (strategy === "non-scoped") return "style";
5970
- const hash = generateCssFilenameHash(filename);
5971
- return `\`_\${style}_${hash}\``;
5972
- }
5973
- function CSSEnablerPlugin(ctx) {
5974
- const shouldProcessCSS = (id) => {
5975
- const { css } = ctx.config;
5976
- if (typeof css === "boolean") return css;
5977
- if (toArray(css.exclude).some((re) => re.test(id))) return false;
5978
- if (toArray(css.include).some((re) => re.test(id))) return true;
5979
- return false;
5980
- };
5981
- return [{
5982
- name: "vitest:css-disable",
5983
- enforce: "pre",
5984
- transform(code, id) {
5985
- if (!isCSS(id)) return;
5986
- // css plugin inside Vite won't do anything if the code is empty
5987
- // but it will put __vite__updateStyle anyway
5988
- if (!shouldProcessCSS(id)) return { code: "" };
5989
- }
5990
- }, {
5991
- name: "vitest:css-empty-post",
5992
- enforce: "post",
5993
- transform(_, id) {
5994
- if (!isCSS(id) || shouldProcessCSS(id)) return;
5995
- if (isCSSModule(id) && !isInline(id)) {
5996
- // return proxy for css modules, so that imported module has names:
5997
- // styles.foo returns a "foo" instead of "undefined"
5998
- // we don't use code content to generate hash for "scoped", because it's empty
5999
- const scopeStrategy = typeof ctx.config.css !== "boolean" && ctx.config.css.modules?.classNameStrategy || "stable";
6000
- const proxyReturn = getCSSModuleProxyReturn(scopeStrategy, relative(ctx.config.root, id));
6001
- const code = `export default new Proxy(Object.create(null), {
6002
- get(_, style) {
6003
- return ${proxyReturn};
6004
- },
6005
- })`;
6006
- return { code };
6007
- }
6008
- return { code: "export default \"\"" };
6009
- }
6010
- }];
6011
- }
6012
-
6013
6011
  var jsTokens_1;
6014
6012
  var hasRequiredJsTokens;
6015
6013
 
@@ -6494,66 +6492,244 @@ function stripLiteralDetailed(code, options) {
6494
6492
  return stripLiteralJsTokens(code);
6495
6493
  }
6496
6494
 
6497
- const metaUrlLength = 15;
6498
- const locationString = "self.location".padEnd(metaUrlLength, " ");
6499
- // Vite transforms new URL('./path', import.meta.url) to new URL('/path.js', import.meta.url)
6500
- // This makes "href" equal to "http://localhost:3000/path.js" in the browser, but if we keep it like this,
6501
- // then in tests the URL will become "file:///path.js".
6502
- // To battle this, we replace "import.meta.url" with "self.location" in the code to keep the browser behavior.
6503
- function NormalizeURLPlugin() {
6495
+ // so people can reassign envs at runtime
6496
+ // import.meta.env.VITE_NAME = 'app' -> process.env.VITE_NAME = 'app'
6497
+ function MetaEnvReplacerPlugin() {
6504
6498
  return {
6505
- name: "vitest:normalize-url",
6506
- enforce: "post",
6507
- transform(code, id, options) {
6508
- const ssr = options?.ssr === true;
6509
- if (ssr || !code.includes("new URL") || !code.includes("import.meta.url")) return;
6510
- const cleanString = stripLiteral(code);
6511
- const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*(?:'[^']+'|"[^"]+"|`[^`]+`)\s*,\s*(?:'' \+ )?import\.meta\.url\s*(?:,\s*)?\)/g;
6512
- let updatedCode = code;
6513
- let match;
6514
- // eslint-disable-next-line no-cond-assign
6515
- while (match = assetImportMetaUrlRE.exec(cleanString)) {
6516
- const { 0: exp, index } = match;
6517
- const metaUrlIndex = index + exp.indexOf("import.meta.url");
6518
- updatedCode = updatedCode.slice(0, metaUrlIndex) + locationString + updatedCode.slice(metaUrlIndex + metaUrlLength);
6499
+ name: "vitest:meta-env-replacer",
6500
+ enforce: "pre",
6501
+ transform(code, id) {
6502
+ if (!/\bimport\.meta\.env\b/.test(code)) return null;
6503
+ let s = null;
6504
+ const cleanCode = stripLiteral(code), envs = cleanCode.matchAll(/\bimport\.meta\.env\b/g);
6505
+ for (const env of envs) {
6506
+ s ||= new MagicString(code);
6507
+ const startIndex = env.index, endIndex = startIndex + env[0].length;
6508
+ s.overwrite(startIndex, endIndex, `Object.assign(/* istanbul ignore next */ globalThis.__vitest_worker__?.metaEnv ?? import.meta.env)`);
6519
6509
  }
6520
- return {
6521
- code: updatedCode,
6522
- map: null
6510
+ if (s) return {
6511
+ code: s.toString(),
6512
+ map: s.generateMap({
6513
+ hires: "boundary",
6514
+ source: cleanUrl(id)
6515
+ })
6523
6516
  };
6524
6517
  }
6525
6518
  };
6526
6519
  }
6527
6520
 
6528
- function resolveOptimizerConfig(_testOptions, viteOptions) {
6529
- const testOptions = _testOptions || {};
6530
- const newConfig = {};
6531
- const [major, minor, fix] = version.split(".").map(Number);
6532
- const allowed = major >= 5 || major === 4 && minor >= 4 || major === 4 && minor === 3 && fix >= 2;
6533
- if (!allowed && testOptions?.enabled === true) console.warn(`Vitest: "deps.optimizer" is only available in Vite >= 4.3.2, current Vite version: ${version}`);
6534
- else testOptions.enabled ??= false;
6535
- if (!allowed || testOptions?.enabled !== true) {
6536
- newConfig.cacheDir = void 0;
6537
- newConfig.optimizeDeps = {
6538
- disabled: true,
6539
- entries: []
6540
- };
6541
- } else {
6542
- const currentInclude = testOptions.include || viteOptions?.include || [];
6543
- const exclude = [
6544
- "vitest",
6545
- "react",
6546
- "vue",
6547
- ...testOptions.exclude || viteOptions?.exclude || []
6548
- ];
6549
- const runtime = currentInclude.filter((n) => n.endsWith("jsx-dev-runtime") || n.endsWith("jsx-runtime"));
6550
- exclude.push(...runtime);
6551
- const include = (testOptions.include || viteOptions?.include || []).filter((n) => !exclude.includes(n));
6552
- newConfig.optimizeDeps = {
6553
- ...viteOptions,
6554
- ...testOptions,
6555
- noDiscovery: true,
6556
- disabled: false,
6521
+ function MocksPlugins(options = {}) {
6522
+ const normalizedDistDir = normalize(distDir);
6523
+ return [hoistMocksPlugin({
6524
+ filter(id) {
6525
+ return id.includes(normalizedDistDir) ? false : options.filter ? options.filter(id) : true;
6526
+ },
6527
+ codeFrameGenerator(node, id, code) {
6528
+ return generateCodeFrame(code, 4, node.start + 1);
6529
+ }
6530
+ }), automockPlugin()];
6531
+ }
6532
+
6533
+ function generateCssFilenameHash(filepath) {
6534
+ return hash("sha1", filepath, "hex").slice(0, 6);
6535
+ }
6536
+ function generateScopedClassName(strategy, name, filename) {
6537
+ // should be configured by Vite defaults
6538
+ if (strategy === "scoped") return null;
6539
+ if (strategy === "non-scoped") return name;
6540
+ const hash = generateCssFilenameHash(filename);
6541
+ return `_${name}_${hash}`;
6542
+ }
6543
+
6544
+ const LogLevels = {
6545
+ silent: 0,
6546
+ error: 1,
6547
+ warn: 2,
6548
+ info: 3
6549
+ };
6550
+ function clearScreen(logger) {
6551
+ const repeatCount = process.stdout.rows - 2, blank = repeatCount > 0 ? "\n".repeat(repeatCount) : "";
6552
+ logger.clearScreen(blank);
6553
+ }
6554
+ let lastType, lastMsg, sameCount = 0, timeFormatter;
6555
+ function getTimeFormatter() {
6556
+ return timeFormatter ??= new Intl.DateTimeFormat(void 0, {
6557
+ hour: "numeric",
6558
+ minute: "numeric",
6559
+ second: "numeric"
6560
+ }), timeFormatter;
6561
+ }
6562
+ // This is copy-pasted and needs to be synced from time to time. Ideally, Vite's `createLogger` should accept a custom `console`
6563
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/logger.ts?rgh-link-date=2024-10-16T23%3A29%3A19Z
6564
+ // When Vitest supports only Vite 6 and above, we can use Vite's `createLogger({ console })`
6565
+ // https://github.com/vitejs/vite/pull/18379
6566
+ function createViteLogger(console, level = "info", options = {}) {
6567
+ const loggedErrors = /* @__PURE__ */ new WeakSet(), { prefix = "[vite]", allowClearScreen = true } = options, thresh = LogLevels[level], canClearScreen = allowClearScreen && process.stdout.isTTY && !process.env.CI, clear = canClearScreen ? clearScreen : () => {};
6568
+ function format(type, msg, options = {}) {
6569
+ if (options.timestamp) {
6570
+ let tag = "";
6571
+ if (type === "info") tag = c.cyan(c.bold(prefix));
6572
+ else if (type === "warn") tag = c.yellow(c.bold(prefix));
6573
+ else tag = c.red(c.bold(prefix));
6574
+ const environment = options.environment ? `${options.environment} ` : "";
6575
+ return `${c.dim(getTimeFormatter().format(/* @__PURE__ */ new Date()))} ${tag} ${environment}${msg}`;
6576
+ } else return msg;
6577
+ }
6578
+ function output(type, msg, options = {}) {
6579
+ if (thresh >= LogLevels[type]) {
6580
+ const method = type === "info" ? "log" : type;
6581
+ if (options.error) loggedErrors.add(options.error);
6582
+ if (canClearScreen) if (type === lastType && msg === lastMsg) sameCount++, clear(console), console[method](format(type, msg, options), c.yellow(`(x${sameCount + 1})`));
6583
+ else {
6584
+ if (sameCount = 0, lastMsg = msg, lastType = type, options.clear) clear(console);
6585
+ console[method](format(type, msg, options));
6586
+ }
6587
+ else console[method](format(type, msg, options));
6588
+ }
6589
+ }
6590
+ const warnedMessages = /* @__PURE__ */ new Set(), logger = {
6591
+ hasWarned: false,
6592
+ info(msg, opts) {
6593
+ output("info", msg, opts);
6594
+ },
6595
+ warn(msg, opts) {
6596
+ logger.hasWarned = true, output("warn", msg, opts);
6597
+ },
6598
+ warnOnce(msg, opts) {
6599
+ warnedMessages.has(msg) || (logger.hasWarned = true, output("warn", msg, opts), warnedMessages.add(msg));
6600
+ },
6601
+ error(msg, opts) {
6602
+ logger.hasWarned = true, output("error", msg, opts);
6603
+ },
6604
+ clearScreen(type) {
6605
+ if (thresh >= LogLevels[type]) clear(console);
6606
+ },
6607
+ hasErrorLogged(error) {
6608
+ return loggedErrors.has(error);
6609
+ }
6610
+ };
6611
+ return logger;
6612
+ }
6613
+ // silence warning by Vite for statically not analyzable dynamic import
6614
+ function silenceImportViteIgnoreWarning(logger) {
6615
+ return {
6616
+ ...logger,
6617
+ warn(msg, options) {
6618
+ msg.includes("The above dynamic import cannot be analyzed by Vite") || logger.warn(msg, options);
6619
+ }
6620
+ };
6621
+ }
6622
+
6623
+ const cssLangs = "\\.(?:css|less|sass|scss|styl|stylus|pcss|postcss)(?:$|\\?)", cssLangRE = new RegExp(cssLangs), cssModuleRE = /* @__PURE__ */ new RegExp(`\\.module${cssLangs}`), cssInlineRE = /[?&]inline(?:&|$)/;
6624
+ function isCSS(id) {
6625
+ return cssLangRE.test(id);
6626
+ }
6627
+ function isCSSModule(id) {
6628
+ return cssModuleRE.test(id);
6629
+ }
6630
+ // inline css requests are expected to just return the
6631
+ // string content directly and not the proxy module
6632
+ function isInline(id) {
6633
+ return cssInlineRE.test(id);
6634
+ }
6635
+ function getCSSModuleProxyReturn(strategy, filename) {
6636
+ if (strategy === "non-scoped") return "style";
6637
+ const hash = generateCssFilenameHash(filename);
6638
+ return `\`_\${style}_${hash}\``;
6639
+ }
6640
+ function CSSEnablerPlugin(ctx) {
6641
+ const shouldProcessCSS = (id) => {
6642
+ const { css } = ctx.config;
6643
+ return typeof css === "boolean" ? css : toArray(css.exclude).some((re) => re.test(id)) ? false : !!toArray(css.include).some((re) => re.test(id));
6644
+ };
6645
+ return [{
6646
+ name: "vitest:css-disable",
6647
+ enforce: "pre",
6648
+ transform(code, id) {
6649
+ if (isCSS(id) && !shouldProcessCSS(id)) return { code: "" };
6650
+ }
6651
+ }, {
6652
+ name: "vitest:css-empty-post",
6653
+ enforce: "post",
6654
+ transform(_, id) {
6655
+ if (!(!isCSS(id) || shouldProcessCSS(id))) {
6656
+ if (isCSSModule(id) && !isInline(id)) {
6657
+ // return proxy for css modules, so that imported module has names:
6658
+ // styles.foo returns a "foo" instead of "undefined"
6659
+ // we don't use code content to generate hash for "scoped", because it's empty
6660
+ 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), {
6661
+ get(_, style) {
6662
+ return ${proxyReturn};
6663
+ },
6664
+ })`;
6665
+ return { code };
6666
+ }
6667
+ return { code: "export default \"\"" };
6668
+ }
6669
+ }
6670
+ }];
6671
+ }
6672
+
6673
+ const metaUrlLength = 15, locationString = "self.location".padEnd(metaUrlLength, " ");
6674
+ // Vite transforms new URL('./path', import.meta.url) to new URL('/path.js', import.meta.url)
6675
+ // This makes "href" equal to "http://localhost:3000/path.js" in the browser, but if we keep it like this,
6676
+ // then in tests the URL will become "file:///path.js".
6677
+ // To battle this, we replace "import.meta.url" with "self.location" in the code to keep the browser behavior.
6678
+ function NormalizeURLPlugin() {
6679
+ return {
6680
+ name: "vitest:normalize-url",
6681
+ enforce: "post",
6682
+ transform(code) {
6683
+ if (this.environment.name !== "client" || !code.includes("new URL") || !code.includes("import.meta.url")) return;
6684
+ const cleanString = stripLiteral(code), assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*(?:'[^']+'|"[^"]+"|`[^`]+`)\s*,\s*(?:'' \+ )?import\.meta\.url\s*(?:,\s*)?\)/g;
6685
+ let updatedCode = code, match;
6686
+ // eslint-disable-next-line no-cond-assign
6687
+ while (match = assetImportMetaUrlRE.exec(cleanString)) {
6688
+ const { 0: exp, index } = match, metaUrlIndex = index + exp.indexOf("import.meta.url");
6689
+ updatedCode = updatedCode.slice(0, metaUrlIndex) + locationString + updatedCode.slice(metaUrlIndex + metaUrlLength);
6690
+ }
6691
+ return {
6692
+ code: updatedCode,
6693
+ map: null
6694
+ };
6695
+ }
6696
+ };
6697
+ }
6698
+
6699
+ function VitestOptimizer() {
6700
+ return {
6701
+ name: "vitest:normalize-optimizer",
6702
+ config: {
6703
+ order: "post",
6704
+ handler(viteConfig) {
6705
+ const testConfig = viteConfig.test || {}, root = resolve(viteConfig.root || process.cwd()), name = viteConfig.test?.name, label = typeof name === "string" ? name : name?.label || "";
6706
+ viteConfig.cacheDir = VitestCache.resolveCacheDir(resolve(root || process.cwd()), testConfig.cache != null && testConfig.cache !== false ? testConfig.cache.dir : viteConfig.cacheDir, label);
6707
+ }
6708
+ }
6709
+ };
6710
+ }
6711
+
6712
+ function resolveOptimizerConfig(_testOptions, viteOptions) {
6713
+ const testOptions = _testOptions || {};
6714
+ let optimizeDeps;
6715
+ if (testOptions.enabled !== true) testOptions.enabled ??= false, optimizeDeps = {
6716
+ disabled: true,
6717
+ entries: []
6718
+ };
6719
+ else {
6720
+ const currentInclude = testOptions.include || viteOptions?.include || [], exclude = [
6721
+ "vitest",
6722
+ "react",
6723
+ "vue",
6724
+ ...testOptions.exclude || viteOptions?.exclude || []
6725
+ ], runtime = currentInclude.filter((n) => n.endsWith("jsx-dev-runtime") || n.endsWith("jsx-runtime"));
6726
+ exclude.push(...runtime);
6727
+ const include = (testOptions.include || viteOptions?.include || []).filter((n) => !exclude.includes(n));
6728
+ optimizeDeps = {
6729
+ ...viteOptions,
6730
+ ...testOptions,
6731
+ noDiscovery: true,
6732
+ disabled: false,
6557
6733
  entries: [],
6558
6734
  exclude,
6559
6735
  include
@@ -6561,23 +6737,12 @@ function resolveOptimizerConfig(_testOptions, viteOptions) {
6561
6737
  }
6562
6738
  // `optimizeDeps.disabled` is deprecated since v5.1.0-beta.1
6563
6739
  // https://github.com/vitejs/vite/pull/15184
6564
- if (major >= 5 && minor >= 1 || major >= 6) {
6565
- if (newConfig.optimizeDeps.disabled) {
6566
- newConfig.optimizeDeps.noDiscovery = true;
6567
- newConfig.optimizeDeps.include = [];
6568
- }
6569
- delete newConfig.optimizeDeps.disabled;
6570
- }
6571
- return newConfig;
6740
+ if (optimizeDeps.disabled) optimizeDeps.noDiscovery = true, optimizeDeps.include = [];
6741
+ return delete optimizeDeps.disabled, optimizeDeps;
6572
6742
  }
6573
6743
  function deleteDefineConfig(viteConfig) {
6574
6744
  const defines = {};
6575
- if (viteConfig.define) {
6576
- delete viteConfig.define["import.meta.vitest"];
6577
- delete viteConfig.define["process.env"];
6578
- delete viteConfig.define.process;
6579
- delete viteConfig.define.global;
6580
- }
6745
+ if (viteConfig.define) delete viteConfig.define["import.meta.vitest"], delete viteConfig.define["process.env"], delete viteConfig.define.process, delete viteConfig.define.global;
6581
6746
  for (const key in viteConfig.define) {
6582
6747
  const val = viteConfig.define[key];
6583
6748
  let replacement;
@@ -6590,26 +6755,20 @@ function deleteDefineConfig(viteConfig) {
6590
6755
  }
6591
6756
  if (key.startsWith("import.meta.env.")) {
6592
6757
  const envKey = key.slice(16);
6593
- process.env[envKey] = replacement;
6594
- delete viteConfig.define[key];
6758
+ process.env[envKey] = replacement, delete viteConfig.define[key];
6595
6759
  } else if (key.startsWith("process.env.")) {
6596
6760
  const envKey = key.slice(12);
6597
- process.env[envKey] = replacement;
6598
- delete viteConfig.define[key];
6599
- } else if (!key.includes(".")) {
6600
- defines[key] = replacement;
6601
- delete viteConfig.define[key];
6602
- }
6761
+ process.env[envKey] = replacement, delete viteConfig.define[key];
6762
+ } else if (!key.includes(".")) defines[key] = replacement, delete viteConfig.define[key];
6603
6763
  }
6604
6764
  return defines;
6605
6765
  }
6606
6766
  function resolveFsAllow(projectRoot, rootConfigFile) {
6607
- if (!rootConfigFile) return [searchForWorkspaceRoot(projectRoot), rootDir];
6608
- return [
6767
+ return rootConfigFile ? [
6609
6768
  dirname(rootConfigFile),
6610
6769
  searchForWorkspaceRoot(projectRoot),
6611
6770
  rootDir
6612
- ];
6771
+ ] : [searchForWorkspaceRoot(projectRoot), rootDir];
6613
6772
  }
6614
6773
  function getDefaultResolveOptions() {
6615
6774
  return {
@@ -6626,53 +6785,54 @@ function getDefaultServerConditions() {
6626
6785
  return ["node"];
6627
6786
  }
6628
6787
 
6629
- function VitestOptimizer() {
6788
+ function ModuleRunnerTransform() {
6789
+ // make sure Vite always applies the module runner transform
6630
6790
  return {
6631
- name: "vitest:normalize-optimizer",
6791
+ name: "vitest:environments-module-runner",
6632
6792
  config: {
6633
6793
  order: "post",
6634
- handler(viteConfig) {
6635
- const testConfig = viteConfig.test || {};
6636
- const webOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.web, viteConfig.optimizeDeps);
6637
- const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.optimizer?.ssr, viteConfig.ssr?.optimizeDeps);
6638
- const root = resolve(viteConfig.root || process.cwd());
6639
- const name = viteConfig.test?.name;
6640
- const label = typeof name === "string" ? name : name?.label || "";
6641
- viteConfig.cacheDir = VitestCache.resolveCacheDir(resolve(root || process.cwd()), testConfig.cache != null && testConfig.cache !== false ? testConfig.cache.dir : viteConfig.cacheDir, label);
6642
- viteConfig.optimizeDeps = webOptimizer.optimizeDeps;
6643
- viteConfig.ssr ??= {};
6644
- viteConfig.ssr.optimizeDeps = ssrOptimizer.optimizeDeps;
6794
+ handler(config) {
6795
+ const testConfig = config.test || {};
6796
+ config.environments ??= {};
6797
+ const names = new Set(Object.keys(config.environments));
6798
+ names.add("client"), names.add("ssr");
6799
+ const pool = config.test?.pool;
6800
+ if (pool === "vmForks" || pool === "vmThreads") names.add("__vitest_vm__");
6801
+ const external = [], noExternal = [];
6802
+ let noExternalAll;
6803
+ for (const name of names) {
6804
+ config.environments[name] ??= {};
6805
+ const environment = config.environments[name];
6806
+ // vm tests run using the native import mechanism
6807
+ if (environment.dev ??= {}, name === "__vitest_vm__") environment.dev.moduleRunnerTransform = false, environment.consumer = "client";
6808
+ else environment.dev.moduleRunnerTransform = true;
6809
+ environment.dev.preTransformRequests = false, environment.keepProcessEnv = true;
6810
+ const resolveExternal = name === "client" ? config.resolve?.external : [], resolveNoExternal = name === "client" ? config.resolve?.noExternal : [], topLevelResolveOptions = {};
6811
+ if (resolveExternal != null) topLevelResolveOptions.external = resolveExternal;
6812
+ if (resolveNoExternal != null) topLevelResolveOptions.noExternal = resolveNoExternal;
6813
+ const currentResolveOptions = mergeConfig(topLevelResolveOptions, environment.resolve || {}), envNoExternal = resolveViteResolveOptions("noExternal", currentResolveOptions);
6814
+ if (envNoExternal === true) noExternalAll = true;
6815
+ else noExternal.push(...envNoExternal);
6816
+ const envExternal = resolveViteResolveOptions("external", currentResolveOptions);
6817
+ if (envExternal !== true) external.push(...envExternal);
6818
+ if (environment.resolve ??= {}, environment.resolve.external = [...builtinModules, ...builtinModules.map((m) => `node:${m}`)], environment.resolve.noExternal = true, name === "__vitest_vm__" || name === "__vitest__") continue;
6819
+ const currentOptimizeDeps = environment.optimizeDeps || (name === "client" ? config.optimizeDeps : name === "ssr" ? config.ssr?.optimizeDeps : void 0), optimizeDeps = resolveOptimizerConfig(testConfig.deps?.optimizer?.[name], currentOptimizeDeps);
6820
+ // Vite respects the root level optimize deps, so we override it instead
6821
+ if (name === "client") config.optimizeDeps = optimizeDeps, environment.optimizeDeps = void 0;
6822
+ else if (name === "ssr") config.ssr ??= {}, config.ssr.optimizeDeps = optimizeDeps, environment.optimizeDeps = void 0;
6823
+ else environment.optimizeDeps = optimizeDeps;
6824
+ }
6825
+ if (testConfig.server ??= {}, testConfig.server.deps ??= {}, testConfig.server.deps.inline !== true) {
6826
+ if (noExternalAll) testConfig.server.deps.inline = true;
6827
+ else if (noExternal.length) testConfig.server.deps.inline ??= [], testConfig.server.deps.inline.push(...noExternal);
6828
+ }
6829
+ if (external.length) testConfig.server.deps.external ??= [], testConfig.server.deps.external.push(...external);
6645
6830
  }
6646
6831
  }
6647
6832
  };
6648
6833
  }
6649
-
6650
- // so people can reassign envs at runtime
6651
- // import.meta.env.VITE_NAME = 'app' -> process.env.VITE_NAME = 'app'
6652
- function SsrReplacerPlugin() {
6653
- return {
6654
- name: "vitest:ssr-replacer",
6655
- enforce: "pre",
6656
- transform(code, id) {
6657
- if (!/\bimport\.meta\.env\b/.test(code)) return null;
6658
- let s = null;
6659
- const cleanCode = stripLiteral(code);
6660
- const envs = cleanCode.matchAll(/\bimport\.meta\.env\b/g);
6661
- for (const env of envs) {
6662
- s ||= new MagicString(code);
6663
- const startIndex = env.index;
6664
- const endIndex = startIndex + env[0].length;
6665
- s.overwrite(startIndex, endIndex, "__vite_ssr_import_meta__.env");
6666
- }
6667
- if (s) return {
6668
- code: s.toString(),
6669
- map: s.generateMap({
6670
- hires: "boundary",
6671
- source: cleanUrl(id)
6672
- })
6673
- };
6674
- }
6675
- };
6834
+ function resolveViteResolveOptions(key, options) {
6835
+ return Array.isArray(options[key]) ? options[key] : typeof options[key] === "string" || options[key] instanceof RegExp ? [options[key]] : typeof options[key] === "boolean" ? true : [];
6676
6836
  }
6677
6837
 
6678
6838
  function VitestProjectResolver(ctx) {
@@ -6683,7 +6843,7 @@ function VitestProjectResolver(ctx) {
6683
6843
  if (id === "vitest" || id.startsWith("@vitest/") || id.startsWith("vitest/")) {
6684
6844
  // always redirect the request to the root vitest plugin since
6685
6845
  // it will be the one used to run Vitest
6686
- const resolved = await ctx.server.pluginContainer.resolveId(id, void 0, {
6846
+ const resolved = await ctx.vite.pluginContainer.resolveId(id, void 0, {
6687
6847
  skip: new Set([plugin]),
6688
6848
  ssr
6689
6849
  });
@@ -6719,23 +6879,16 @@ function WorkspaceVitestPlugin(project, options) {
6719
6879
  };
6720
6880
  if (!name) if (typeof options.workspacePath === "string") {
6721
6881
  // if there is a package.json, read the name from it
6722
- const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath);
6723
- const pkgJsonPath = resolve(dir, "package.json");
6882
+ const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath), pkgJsonPath = resolve(dir, "package.json");
6724
6883
  if (existsSync(pkgJsonPath)) name = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).name;
6725
6884
  if (typeof name !== "string" || !name) name = basename(dir);
6726
6885
  } else name = options.workspacePath.toString();
6727
- const isUserBrowserEnabled = viteConfig.test?.browser?.enabled;
6728
- const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled);
6729
- // keep project names to potentially filter it out
6730
- const workspaceNames = [name];
6731
- const browser = viteConfig.test.browser || {};
6886
+ const isUserBrowserEnabled = viteConfig.test?.browser?.enabled, isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled), workspaceNames = [name], browser = viteConfig.test.browser || {};
6732
6887
  if (isBrowserEnabled && browser.name && !browser.instances?.length)
6733
6888
  // vitest injects `instances` in this case later on
6734
6889
  workspaceNames.push(name ? `${name} (${browser.name})` : browser.name);
6735
6890
  viteConfig.test?.browser?.instances?.forEach((instance) => {
6736
- // every instance is a potential project
6737
- instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
6738
- if (isBrowserEnabled) workspaceNames.push(instance.name);
6891
+ if (instance.name ??= name ? `${name} (${instance.browser})` : instance.browser, isBrowserEnabled) workspaceNames.push(instance.name);
6739
6892
  });
6740
6893
  const filters = project.vitest.config.project;
6741
6894
  // if there is `--project=...` filter, check if any of the potential projects match
@@ -6747,10 +6900,13 @@ function WorkspaceVitestPlugin(project, options) {
6747
6900
  });
6748
6901
  if (!hasProject) throw new VitestFilteredOutProjectError();
6749
6902
  }
6750
- return { test: { name: {
6751
- label: name,
6752
- color
6753
- } } };
6903
+ return {
6904
+ environments: { __vitest__: { dev: {} } },
6905
+ test: { name: {
6906
+ label: name,
6907
+ color
6908
+ } }
6909
+ };
6754
6910
  }
6755
6911
  },
6756
6912
  {
@@ -6760,22 +6916,14 @@ function WorkspaceVitestPlugin(project, options) {
6760
6916
  this.meta.watchMode = false;
6761
6917
  },
6762
6918
  config(viteConfig) {
6763
- const defines = deleteDefineConfig(viteConfig);
6764
- const testConfig = viteConfig.test || {};
6765
- const root = testConfig.root || viteConfig.root || options.root;
6766
- const resolveOptions = getDefaultResolveOptions();
6767
- const config = {
6919
+ const defines = deleteDefineConfig(viteConfig), testConfig = viteConfig.test || {}, root = testConfig.root || viteConfig.root || options.root, resolveOptions = getDefaultResolveOptions();
6920
+ let config = {
6768
6921
  root,
6769
6922
  define: { "process.env.NODE_ENV": "process.env.NODE_ENV" },
6770
6923
  resolve: {
6771
6924
  ...resolveOptions,
6772
6925
  alias: testConfig.alias
6773
6926
  },
6774
- esbuild: viteConfig.esbuild === false ? false : {
6775
- target: viteConfig.esbuild?.target || "node18",
6776
- sourcemap: "external",
6777
- legalComments: "inline"
6778
- },
6779
6927
  server: {
6780
6928
  watch: null,
6781
6929
  open: false,
@@ -6788,19 +6936,27 @@ function WorkspaceVitestPlugin(project, options) {
6788
6936
  environments: { ssr: { resolve: resolveOptions } },
6789
6937
  test: {}
6790
6938
  };
6939
+ if ("rolldownVersion" in vite) config = {
6940
+ ...config,
6941
+ oxc: viteConfig.oxc === false ? false : { target: viteConfig.oxc?.target || "node18" }
6942
+ };
6943
+ else config = {
6944
+ ...config,
6945
+ esbuild: viteConfig.esbuild === false ? false : {
6946
+ target: viteConfig.esbuild?.target || "node18",
6947
+ sourcemap: "external",
6948
+ legalComments: "inline"
6949
+ }
6950
+ };
6791
6951
  config.test.defines = defines;
6792
6952
  const classNameStrategy = typeof testConfig.css !== "boolean" && testConfig.css?.modules?.classNameStrategy || "stable";
6793
6953
  if (classNameStrategy !== "scoped") {
6794
- config.css ??= {};
6795
- config.css.modules ??= {};
6796
- if (config.css.modules) config.css.modules.generateScopedName = (name, filename) => {
6954
+ if (config.css ??= {}, config.css.modules ??= {}, config.css.modules) config.css.modules.generateScopedName = (name, filename) => {
6797
6955
  const root = project.config.root;
6798
6956
  return generateScopedClassName(classNameStrategy, name, relative(root, filename));
6799
6957
  };
6800
6958
  }
6801
- config.customLogger = createViteLogger(project.vitest.logger, viteConfig.logLevel || "warn", { allowClearScreen: false });
6802
- config.customLogger = silenceImportViteIgnoreWarning(config.customLogger);
6803
- return config;
6959
+ return config.customLogger = createViteLogger(project.vitest.logger, viteConfig.logLevel || "warn", { allowClearScreen: false }), config.customLogger = silenceImportViteIgnoreWarning(config.customLogger), config;
6804
6960
  }
6805
6961
  },
6806
6962
  {
@@ -6808,33 +6964,120 @@ function WorkspaceVitestPlugin(project, options) {
6808
6964
  enforce: "post",
6809
6965
  async configureServer(server) {
6810
6966
  const options = deepMerge({}, configDefaults, server.config.test || {});
6811
- await project._configureServer(options, server);
6812
- await server.watcher.close();
6967
+ await project._configureServer(options, server), await server.watcher.close();
6813
6968
  }
6814
6969
  },
6815
- SsrReplacerPlugin(),
6970
+ MetaEnvReplacerPlugin(),
6816
6971
  ...CSSEnablerPlugin(project),
6817
6972
  CoverageTransform(project.vitest),
6818
6973
  ...MocksPlugins(),
6819
6974
  VitestProjectResolver(project.vitest),
6820
6975
  VitestOptimizer(),
6821
- NormalizeURLPlugin()
6976
+ NormalizeURLPlugin(),
6977
+ ModuleRunnerTransform()
6822
6978
  ];
6823
6979
  }
6824
6980
 
6981
+ class VitestResolver {
6982
+ options;
6983
+ externalizeCache = /* @__PURE__ */ new Map();
6984
+ constructor(cacheDir, config) {
6985
+ this.options = {
6986
+ moduleDirectories: config.deps.moduleDirectories,
6987
+ inlineFiles: config.setupFiles.flatMap((file) => {
6988
+ if (file.startsWith("file://")) return file;
6989
+ const resolvedId = resolve(file);
6990
+ return [resolvedId, pathToFileURL(resolvedId).href];
6991
+ }),
6992
+ cacheDir,
6993
+ inline: config.server.deps?.inline,
6994
+ external: config.server.deps?.external
6995
+ };
6996
+ }
6997
+ shouldExternalize(file) {
6998
+ return shouldExternalize(normalizeId(file), this.options, this.externalizeCache);
6999
+ }
7000
+ }
7001
+ function normalizeId(id) {
7002
+ if (id.startsWith("/@fs/")) id = id.slice(isWindows ? 5 : 4);
7003
+ return id;
7004
+ }
7005
+ const BUILTIN_EXTENSIONS = new Set([
7006
+ ".mjs",
7007
+ ".cjs",
7008
+ ".node",
7009
+ ".wasm"
7010
+ ]), ESM_EXT_RE = /\.(es|esm|esm-browser|esm-bundler|es6|module)\.js$/, ESM_FOLDER_RE = /\/(es|esm)\/(.*\.js)$/, defaultInline = [
7011
+ /virtual:/,
7012
+ /\.[mc]?ts$/,
7013
+ /[?&](init|raw|url|inline)\b/,
7014
+ KNOWN_ASSET_RE,
7015
+ /^(?!.*node_modules).*\.mjs$/,
7016
+ /^(?!.*node_modules).*\.cjs\.js$/,
7017
+ /vite\w*\/dist\/client\/env.mjs/
7018
+ ], depsExternal = [/\/node_modules\/.*\.cjs\.js$/, /\/node_modules\/.*\.mjs$/];
7019
+ function guessCJSversion(id) {
7020
+ if (id.match(ESM_EXT_RE)) {
7021
+ for (const i of [
7022
+ id.replace(ESM_EXT_RE, ".mjs"),
7023
+ id.replace(ESM_EXT_RE, ".umd.js"),
7024
+ id.replace(ESM_EXT_RE, ".cjs.js"),
7025
+ id.replace(ESM_EXT_RE, ".js")
7026
+ ]) if (existsSync(i)) return i;
7027
+ }
7028
+ if (id.match(ESM_FOLDER_RE)) {
7029
+ for (const i of [
7030
+ id.replace(ESM_FOLDER_RE, "/umd/$1"),
7031
+ id.replace(ESM_FOLDER_RE, "/cjs/$1"),
7032
+ id.replace(ESM_FOLDER_RE, "/lib/$1"),
7033
+ id.replace(ESM_FOLDER_RE, "/$1")
7034
+ ]) if (existsSync(i)) return i;
7035
+ }
7036
+ }
7037
+ // The code from https://github.com/unjs/mlly/blob/c5bcca0cda175921344fd6de1bc0c499e73e5dac/src/syntax.ts#L51-L98
7038
+ async function isValidNodeImport(id) {
7039
+ const extension = extname(id);
7040
+ if (BUILTIN_EXTENSIONS.has(extension)) return true;
7041
+ if (extension !== ".js") return false;
7042
+ id = id.replace("file:///", "");
7043
+ const package_ = findNearestPackageData(dirname(id));
7044
+ if (package_.type === "module") return true;
7045
+ if (/\.(?:\w+-)?esm?(?:-\w+)?\.js$|\/esm?\//.test(id)) return false;
7046
+ try {
7047
+ await esModuleLexer.init;
7048
+ const code = await promises.readFile(id, "utf8"), [, , , hasModuleSyntax] = esModuleLexer.parse(code);
7049
+ return !hasModuleSyntax;
7050
+ } catch {
7051
+ return false;
7052
+ }
7053
+ }
7054
+ async function shouldExternalize(id, options, cache) {
7055
+ if (!cache.has(id)) cache.set(id, _shouldExternalize(id, options));
7056
+ return cache.get(id);
7057
+ }
7058
+ async function _shouldExternalize(id, options) {
7059
+ // data: should be processed by native import,
7060
+ // since it is a feature of ESM.
7061
+ // also externalize network imports since nodejs allows it when --experimental-network-imports
7062
+ if (isBuiltin(id) || id.startsWith("data:") || /^(?:https?:)?\/\//.test(id)) return id;
7063
+ const moduleDirectories = options?.moduleDirectories || ["/node_modules/"];
7064
+ if (matchExternalizePattern(id, moduleDirectories, options?.inline) || options?.inlineFiles && options?.inlineFiles.includes(id)) return false;
7065
+ // Unless the user explicitly opted to inline them, externalize Vite deps.
7066
+ // They are too big to inline by default.
7067
+ if (matchExternalizePattern(id, moduleDirectories, options?.external) || options?.cacheDir && id.includes(options.cacheDir)) return id;
7068
+ const isLibraryModule = moduleDirectories.some((dir) => id.includes(dir)), guessCJS = isLibraryModule && options?.fallbackCJS;
7069
+ return id = guessCJS ? guessCJSversion(id) || id : id, matchExternalizePattern(id, moduleDirectories, defaultInline) ? false : matchExternalizePattern(id, moduleDirectories, depsExternal) || isLibraryModule && await isValidNodeImport(id) ? id : false;
7070
+ }
7071
+ function matchExternalizePattern(id, moduleDirectories, patterns) {
7072
+ if (patterns == null) return false;
7073
+ if (patterns === true) return true;
7074
+ for (const ex of patterns) if (typeof ex === "string") {
7075
+ if (moduleDirectories.some((dir) => id.includes(join(dir, ex)))) return true;
7076
+ } else if (ex.test(id)) return true;
7077
+ return false;
7078
+ }
7079
+
6825
7080
  class TestSpecification {
6826
- /**
6827
- * @deprecated use `project` instead
6828
- */
6829
- 0;
6830
- /**
6831
- * @deprecated use `moduleId` instead
6832
- */
6833
- 1;
6834
- /**
6835
- * @deprecated use `pool` instead
6836
- */
6837
- 2;
6838
7081
  /**
6839
7082
  * The task ID associated with the test module.
6840
7083
  */
@@ -6857,24 +7100,15 @@ class TestSpecification {
6857
7100
  */
6858
7101
  testLines;
6859
7102
  constructor(project, moduleId, pool, testLines) {
6860
- this[0] = project;
6861
- this[1] = moduleId;
6862
- this[2] = { pool };
6863
- const name = project.config.name;
6864
- const hashName = pool !== "typescript" ? name : name ? `${name}:__typecheck__` : "__typecheck__";
6865
- this.taskId = generateFileHash(relative(project.config.root, moduleId), hashName);
6866
- this.project = project;
6867
- this.moduleId = moduleId;
6868
- this.pool = pool;
6869
- this.testLines = testLines;
7103
+ const name = project.config.name, hashName = pool !== "typescript" ? name : name ? `${name}:__typecheck__` : "__typecheck__";
7104
+ this.taskId = generateFileHash(relative(project.config.root, moduleId), hashName), this.project = project, this.moduleId = moduleId, this.pool = pool, this.testLines = testLines;
6870
7105
  }
6871
7106
  /**
6872
7107
  * Test module associated with the specification.
6873
7108
  */
6874
7109
  get testModule() {
6875
7110
  const task = this.project.vitest.state.idMap.get(this.taskId);
6876
- if (!task) return void 0;
6877
- return this.project.vitest.state.getReportedEntity(task);
7111
+ return task ? this.project.vitest.state.getReportedEntity(task) : void 0;
6878
7112
  }
6879
7113
  toJSON() {
6880
7114
  return [
@@ -6889,15 +7123,6 @@ class TestSpecification {
6889
7123
  }
6890
7124
  ];
6891
7125
  }
6892
- /**
6893
- * for backwards compatibility
6894
- * @deprecated
6895
- */
6896
- *[Symbol.iterator]() {
6897
- yield this.project;
6898
- yield this.moduleId;
6899
- yield this.pool;
6900
- }
6901
7126
  }
6902
7127
 
6903
7128
  async function createViteServer(inlineConfig) {
@@ -6905,12 +7130,10 @@ async function createViteServer(inlineConfig) {
6905
7130
  // But Vitest works correctly either way
6906
7131
  const error = console.error;
6907
7132
  console.error = (...args) => {
6908
- if (typeof args[0] === "string" && args[0].includes("WebSocket server error:")) return;
6909
- error(...args);
7133
+ typeof args[0] === "string" && args[0].includes("WebSocket server error:") || error(...args);
6910
7134
  };
6911
7135
  const server = await createServer(inlineConfig);
6912
- console.error = error;
6913
- return server;
7136
+ return console.error = error, server;
6914
7137
  }
6915
7138
 
6916
7139
  class TestProject {
@@ -6927,29 +7150,24 @@ class TestProject {
6927
7150
  * Browser instance if the browser is enabled. This is initialized when the tests run for the first time.
6928
7151
  */
6929
7152
  browser;
6930
- /** @deprecated use `vitest` instead */
6931
- ctx;
6932
7153
  /**
6933
7154
  * Temporary directory for the project. This is unique for each project. Vitest stores transformed content here.
6934
7155
  */
6935
7156
  tmpDir = join(tmpdir(), nanoid());
6936
- /** @internal */ vitenode;
6937
7157
  /** @internal */ typechecker;
6938
7158
  /** @internal */ _config;
6939
7159
  /** @internal */ _vite;
6940
7160
  /** @internal */ _hash;
7161
+ /** @internal */ _resolver;
7162
+ /** @internal */ _serializedDefines;
7163
+ /** @inetrnal */ testFilesList = null;
6941
7164
  runner;
6942
7165
  closingPromise;
6943
- testFilesList = null;
6944
7166
  typecheckFilesList = null;
6945
7167
  _globalSetups;
6946
7168
  _provided = {};
6947
- constructor(path, vitest, options) {
6948
- this.path = path;
6949
- this.options = options;
6950
- this.vitest = vitest;
6951
- this.ctx = vitest;
6952
- this.globalConfig = vitest.config;
7169
+ constructor(vitest, options) {
7170
+ this.options = options, this.vitest = vitest, this.globalConfig = vitest.config;
6953
7171
  }
6954
7172
  /**
6955
7173
  * The unique hash of this project. This value is consistent between the reruns.
@@ -6978,10 +7196,9 @@ class TestProject {
6978
7196
  * Get the provided context. The project context is merged with the global context.
6979
7197
  */
6980
7198
  getProvidedContext() {
6981
- if (this.isRootProject()) return this._provided;
6982
7199
  // globalSetup can run even if core workspace is not part of the test run
6983
7200
  // so we need to inherit its provided context
6984
- return {
7201
+ return this.isRootProject() ? this._provided : {
6985
7202
  ...this.vitest.getRootProject().getProvidedContext(),
6986
7203
  ...this._provided
6987
7204
  };
@@ -6991,7 +7208,7 @@ class TestProject {
6991
7208
  * @param moduleId The file path
6992
7209
  */
6993
7210
  createSpecification(moduleId, locations, pool) {
6994
- return new TestSpecification(this, moduleId, pool || getFilePoolName(this, moduleId), locations);
7211
+ return new TestSpecification(this, moduleId, pool || getFilePoolName(this), locations);
6995
7212
  }
6996
7213
  toJSON() {
6997
7214
  return {
@@ -7005,13 +7222,11 @@ class TestProject {
7005
7222
  */
7006
7223
  get vite() {
7007
7224
  if (!this._vite) throw new Error("The server was not set. It means that `project.vite` was called before the Vite server was established.");
7008
- // checking it once should be enough
7009
- Object.defineProperty(this, "vite", {
7225
+ return Object.defineProperty(this, "vite", {
7010
7226
  configurable: true,
7011
7227
  writable: true,
7012
7228
  value: this._vite
7013
- });
7014
- return this._vite;
7229
+ }), this._vite;
7015
7230
  }
7016
7231
  /**
7017
7232
  * Resolved project configuration.
@@ -7044,85 +7259,38 @@ class TestProject {
7044
7259
  get serializedConfig() {
7045
7260
  return this._serializeOverriddenConfig();
7046
7261
  }
7047
- /** @deprecated use `vite` instead */
7048
- get server() {
7049
- return this._vite;
7050
- }
7051
7262
  /**
7052
7263
  * Check if this is the root project. The root project is the one that has the root config.
7053
7264
  */
7054
7265
  isRootProject() {
7055
7266
  return this.vitest.getRootProject() === this;
7056
7267
  }
7057
- /** @deprecated use `isRootProject` instead */
7058
- isCore() {
7059
- return this.isRootProject();
7060
- }
7061
- /** @deprecated use `createSpecification` instead */
7062
- createSpec(moduleId, pool) {
7063
- return new TestSpecification(this, moduleId, pool);
7064
- }
7065
- /** @deprecated */
7066
- initializeGlobalSetup() {
7067
- return this._initializeGlobalSetup();
7068
- }
7069
7268
  /** @internal */
7070
7269
  async _initializeGlobalSetup() {
7071
- if (this._globalSetups) return;
7072
- this._globalSetups = await loadGlobalSetupFiles(this.runner, this.config.globalSetup);
7073
- for (const globalSetupFile of this._globalSetups) {
7074
- const teardown = await globalSetupFile.setup?.(this);
7075
- if (teardown == null || !!globalSetupFile.teardown) continue;
7076
- if (typeof teardown !== "function") throw new TypeError(`invalid return value in globalSetup file ${globalSetupFile.file}. Must return a function`);
7077
- globalSetupFile.teardown = teardown;
7270
+ if (!this._globalSetups) {
7271
+ this._globalSetups = await loadGlobalSetupFiles(this.runner, this.config.globalSetup);
7272
+ for (const globalSetupFile of this._globalSetups) {
7273
+ const teardown = await globalSetupFile.setup?.(this);
7274
+ if (teardown == null || !!globalSetupFile.teardown) continue;
7275
+ if (typeof teardown !== "function") throw new TypeError(`invalid return value in globalSetup file ${globalSetupFile.file}. Must return a function`);
7276
+ globalSetupFile.teardown = teardown;
7277
+ }
7078
7278
  }
7079
7279
  }
7080
7280
  onTestsRerun(cb) {
7081
7281
  this.vitest.onTestsRerun(cb);
7082
7282
  }
7083
- /** @deprecated */
7084
- teardownGlobalSetup() {
7085
- return this._teardownGlobalSetup();
7086
- }
7087
7283
  /** @internal */
7088
7284
  async _teardownGlobalSetup() {
7089
- if (!this._globalSetups) return;
7090
- for (const globalSetupFile of [...this._globalSetups].reverse()) await globalSetupFile.teardown?.();
7091
- }
7092
- /** @deprecated use `vitest.logger` instead */
7093
- get logger() {
7094
- return this.vitest.logger;
7095
- }
7096
- // it's possible that file path was imported with different queries (?raw, ?url, etc)
7097
- /** @deprecated use `.vite` or `.browser.vite` directly */
7098
- getModulesByFilepath(file) {
7099
- const set = this.server.moduleGraph.getModulesByFile(file) || this.browser?.vite.moduleGraph.getModulesByFile(file);
7100
- return set || /* @__PURE__ */ new Set();
7101
- }
7102
- /** @deprecated use `.vite` or `.browser.vite` directly */
7103
- getModuleById(id) {
7104
- return this.server.moduleGraph.getModuleById(id) || this.browser?.vite.moduleGraph.getModuleById(id);
7105
- }
7106
- /** @deprecated use `.vite` or `.browser.vite` directly */
7107
- getSourceMapModuleById(id) {
7108
- const mod = this.server.moduleGraph.getModuleById(id);
7109
- return mod?.ssrTransformResult?.map || mod?.transformResult?.map;
7110
- }
7111
- /** @deprecated use `vitest.reporters` instead */
7112
- get reporters() {
7113
- return this.ctx.reporters;
7285
+ if (this._globalSetups) for (const globalSetupFile of [...this._globalSetups].reverse()) await globalSetupFile.teardown?.();
7114
7286
  }
7115
7287
  /**
7116
7288
  * Get all files in the project that match the globs in the config and the filters.
7117
7289
  * @param filters String filters to match the test files.
7118
7290
  */
7119
7291
  async globTestFiles(filters = []) {
7120
- const dir = this.config.dir || this.config.root;
7121
- const { include, exclude, includeSource } = this.config;
7122
- const typecheck = this.config.typecheck;
7123
- const [testFiles, typecheckTestFiles] = await Promise.all([typecheck.enabled && typecheck.only ? [] : this.globAllTestFiles(include, exclude, includeSource, dir), typecheck.enabled ? this.typecheckFilesList || this.globFiles(typecheck.include, typecheck.exclude, dir) : []]);
7124
- this.typecheckFilesList = typecheckTestFiles;
7125
- return {
7292
+ const dir = this.config.dir || this.config.root, { include, exclude, includeSource } = this.config, typecheck = this.config.typecheck, [testFiles, typecheckTestFiles] = await Promise.all([typecheck.enabled && typecheck.only ? [] : this.globAllTestFiles(include, exclude, includeSource, dir), typecheck.enabled ? this.typecheckFilesList || this.globFiles(typecheck.include, typecheck.exclude, dir) : []]);
7293
+ return this.typecheckFilesList = typecheckTestFiles, {
7126
7294
  testFiles: this.filterFiles(testFiles, filters, dir),
7127
7295
  typecheckTestFiles: this.filterFiles(typecheckTestFiles, filters, dir)
7128
7296
  };
@@ -7141,8 +7309,7 @@ class TestProject {
7141
7309
  }
7142
7310
  }));
7143
7311
  }
7144
- this.testFilesList = testFiles;
7145
- return testFiles;
7312
+ return this.testFilesList = testFiles, testFiles;
7146
7313
  }
7147
7314
  isBrowserEnabled() {
7148
7315
  return isBrowserEnabled(this.config);
@@ -7168,10 +7335,6 @@ class TestProject {
7168
7335
  _isCachedTypecheckFile(testPath) {
7169
7336
  return !!this.typecheckFilesList && this.typecheckFilesList.includes(testPath);
7170
7337
  }
7171
- /** @deprecated use `serializedConfig` instead */
7172
- getSerializableConfig() {
7173
- return this._serializeOverriddenConfig();
7174
- }
7175
7338
  /** @internal */
7176
7339
  async globFiles(include, exclude, cwd) {
7177
7340
  const globOptions = {
@@ -7179,8 +7342,7 @@ class TestProject {
7179
7342
  cwd,
7180
7343
  ignore: exclude,
7181
7344
  expandDirectories: false
7182
- };
7183
- const files = await glob(include, globOptions);
7345
+ }, files = await glob(include, globOptions);
7184
7346
  // keep the slashes consistent with Vite
7185
7347
  // we are not using the pathe here because it normalizes the drive letter on Windows
7186
7348
  // and we want to keep it the same as working dir
@@ -7193,29 +7355,19 @@ class TestProject {
7193
7355
  if (this._isCachedTestFile(moduleId)) return true;
7194
7356
  const relativeId = relative(this.config.dir || this.config.root, moduleId);
7195
7357
  if (pm.isMatch(relativeId, this.config.exclude)) return false;
7196
- if (pm.isMatch(relativeId, this.config.include)) {
7197
- this.markTestFile(moduleId);
7198
- return true;
7199
- }
7358
+ if (pm.isMatch(relativeId, this.config.include)) return this.markTestFile(moduleId), true;
7200
7359
  if (this.config.includeSource?.length && pm.isMatch(relativeId, this.config.includeSource)) {
7201
7360
  const code = source?.() || readFileSync(moduleId, "utf-8");
7202
- if (this.isInSourceTestCode(code)) {
7203
- this.markTestFile(moduleId);
7204
- return true;
7205
- }
7361
+ if (this.isInSourceTestCode(code)) return this.markTestFile(moduleId), true;
7206
7362
  }
7207
7363
  return false;
7208
7364
  }
7209
- /** @deprecated use `matchesTestGlob` instead */
7210
- async isTargetFile(id, source) {
7211
- return this.matchesTestGlob(id, source ? () => source : void 0);
7212
- }
7213
7365
  isInSourceTestCode(code) {
7214
7366
  return code.includes("import.meta.vitest");
7215
7367
  }
7216
7368
  filterFiles(testFiles, filters, dir) {
7217
7369
  if (filters.length && process.platform === "win32") filters = filters.map((f) => slash(f));
7218
- if (filters.length) return testFiles.filter((t) => {
7370
+ return filters.length ? testFiles.filter((t) => {
7219
7371
  const testFile = relative(dir, t).toLocaleLowerCase();
7220
7372
  return filters.some((f) => {
7221
7373
  // if filter is a full file path, we should include it if it's in the same folder
@@ -7223,8 +7375,7 @@ class TestProject {
7223
7375
  const relativePath = f.endsWith("/") ? join(relative(dir, f), "/") : relative(dir, f);
7224
7376
  return testFile.includes(f.toLocaleLowerCase()) || testFile.includes(relativePath.toLocaleLowerCase());
7225
7377
  });
7226
- });
7227
- return testFiles;
7378
+ }) : testFiles;
7228
7379
  }
7229
7380
  _parentBrowser;
7230
7381
  /** @internal */
@@ -7235,25 +7386,23 @@ class TestProject {
7235
7386
  await this.vitest.packageInstaller.ensureInstalled("@vitest/browser", this.config.root, this.vitest.version);
7236
7387
  const { createBrowserServer, distRoot } = await import('@vitest/browser');
7237
7388
  let cacheDir;
7238
- const browser = await createBrowserServer(this, this.vite.config.configFile, [{
7239
- name: "vitest:browser-cacheDir",
7240
- configResolved(config) {
7241
- cacheDir = config.cacheDir;
7242
- }
7243
- }, ...MocksPlugins({ filter(id) {
7244
- if (id.includes(distRoot) || id.includes(cacheDir)) return false;
7245
- return true;
7246
- } })], [CoverageTransform(this.vitest)]);
7247
- this._parentBrowser = browser;
7248
- if (this.config.browser.ui) setup(this.vitest, browser.vite);
7389
+ const browser = await createBrowserServer(this, this.vite.config.configFile, [
7390
+ {
7391
+ name: "vitest:browser-cacheDir",
7392
+ configResolved(config) {
7393
+ cacheDir = config.cacheDir;
7394
+ }
7395
+ },
7396
+ ...MocksPlugins({ filter(id) {
7397
+ return !(id.includes(distRoot) || id.includes(cacheDir));
7398
+ } }),
7399
+ MetaEnvReplacerPlugin()
7400
+ ], [CoverageTransform(this.vitest)]);
7401
+ if (this._parentBrowser = browser, this.config.browser.ui) setup(this.vitest, browser.vite);
7249
7402
  });
7250
7403
  /** @internal */
7251
7404
  _initBrowserServer = deduped(async () => {
7252
- await this._parent?._initParentBrowser();
7253
- if (!this.browser && this._parent?._parentBrowser) {
7254
- this.browser = this._parent._parentBrowser.spawn(this);
7255
- await this.vitest.report("onBrowserInit", this);
7256
- }
7405
+ if (await this._parent?._initParentBrowser(), !this.browser && this._parent?._parentBrowser) this.browser = this._parent._parentBrowser.spawn(this), await this.vitest.report("onBrowserInit", this);
7257
7406
  });
7258
7407
  /**
7259
7408
  * Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts.
@@ -7266,8 +7415,7 @@ class TestProject {
7266
7415
  this.browser?.close(),
7267
7416
  this.clearTmpDir()
7268
7417
  ].filter(Boolean)).then(() => {
7269
- this._provided = {};
7270
- this._vite = void 0;
7418
+ this._provided = {}, this._vite = void 0;
7271
7419
  });
7272
7420
  return this.closingPromise;
7273
7421
  }
@@ -7276,66 +7424,41 @@ class TestProject {
7276
7424
  * @param moduleId The ID of the module in Vite module graph
7277
7425
  */
7278
7426
  import(moduleId) {
7279
- return this.runner.executeId(moduleId);
7280
- }
7281
- /** @deprecated use `name` instead */
7282
- getName() {
7283
- return this.config.name || "";
7284
- }
7285
- /** @deprecated internal */
7286
- setServer(options, server) {
7287
- return this._configureServer(options, server);
7427
+ return this.runner.import(moduleId);
7288
7428
  }
7289
7429
  _setHash() {
7290
7430
  this._hash = generateHash(this._config.root + this._config.name);
7291
7431
  }
7292
7432
  /** @internal */
7293
7433
  async _configureServer(options, server) {
7294
- this._config = resolveConfig(this.vitest, {
7434
+ for (const _providedKey in this._config = resolveConfig(this.vitest, {
7295
7435
  ...options,
7296
7436
  coverage: this.vitest.config.coverage
7297
- }, server.config);
7298
- this._setHash();
7299
- for (const _providedKey in this.config.provide) {
7437
+ }, server.config), this._setHash(), this.config.provide) {
7300
7438
  const providedKey = _providedKey;
7301
7439
  // type is very strict here, so we cast it to any
7302
7440
  this.provide(providedKey, this.config.provide[providedKey]);
7303
7441
  }
7304
- this.closingPromise = void 0;
7305
- this._vite = server;
7306
- this.vitenode = new ViteNodeServer(server, this.config.server);
7307
- const node = this.vitenode;
7308
- this.runner = new ViteNodeRunner({
7309
- root: server.config.root,
7310
- base: server.config.base,
7311
- fetchModule(id) {
7312
- return node.fetchModule(id);
7313
- },
7314
- resolveId(id, importer) {
7315
- return node.resolveId(id, importer);
7316
- }
7317
- });
7442
+ this.closingPromise = void 0, this._resolver = new VitestResolver(server.config.cacheDir, this._config), this._vite = server, this._serializedDefines = createDefinesScript(server.config.define);
7443
+ const environment = server.environments.__vitest__;
7444
+ this.runner = new ServerModuleRunner(environment, this._resolver, this._config);
7318
7445
  }
7319
7446
  _serializeOverriddenConfig() {
7320
7447
  // TODO: serialize the config _once_ or when needed
7321
- const config = serializeConfig(this.config, this.vitest.config, this.vite.config);
7322
- if (!this.vitest.configOverride) return config;
7323
- return deepMerge(config, this.vitest.configOverride);
7448
+ const config = serializeConfig(this);
7449
+ return this.vitest.configOverride ? deepMerge(config, this.vitest.configOverride) : config;
7324
7450
  }
7325
7451
  async clearTmpDir() {
7326
7452
  try {
7327
7453
  await rm(this.tmpDir, { recursive: true });
7328
7454
  } catch {}
7329
7455
  }
7330
- /** @deprecated */
7331
- initBrowserProvider() {
7332
- return this._initBrowserProvider();
7333
- }
7334
7456
  /** @internal */
7335
7457
  _initBrowserProvider = deduped(async () => {
7336
- if (!this.isBrowserEnabled() || this.browser?.provider) return;
7337
- if (!this.browser) await this._initBrowserServer();
7338
- await this.browser?.initBrowserProvider(this);
7458
+ if (!(!this.isBrowserEnabled() || this.browser?.provider)) {
7459
+ if (!this.browser) await this._initBrowserServer();
7460
+ await this.browser?.initBrowserProvider(this);
7461
+ }
7339
7462
  });
7340
7463
  /** @internal */
7341
7464
  _provideObject(context) {
@@ -7347,41 +7470,26 @@ class TestProject {
7347
7470
  }
7348
7471
  /** @internal */
7349
7472
  static _createBasicProject(vitest) {
7350
- const project = new TestProject(vitest.config.name || vitest.config.root, vitest);
7351
- project.vitenode = vitest.vitenode;
7352
- project.runner = vitest.runner;
7353
- project._vite = vitest.server;
7354
- project._config = vitest.config;
7355
- project._setHash();
7356
- project._provideObject(vitest.config.provide);
7357
- return project;
7473
+ const project = new TestProject(vitest);
7474
+ return project.runner = vitest.runner, project._vite = vitest.vite, project._config = vitest.config, project._resolver = vitest._resolver, project._serializedDefines = createDefinesScript(vitest.vite.config.define), project._setHash(), project._provideObject(vitest.config.provide), project;
7358
7475
  }
7359
7476
  /** @internal */
7360
7477
  static _cloneBrowserProject(parent, config) {
7361
- const clone = new TestProject(parent.path, parent.vitest);
7362
- clone.vitenode = parent.vitenode;
7363
- clone.runner = parent.runner;
7364
- clone._vite = parent._vite;
7365
- clone._config = config;
7366
- clone._setHash();
7367
- clone._parent = parent;
7368
- clone._provideObject(config.provide);
7369
- return clone;
7478
+ const clone = new TestProject(parent.vitest);
7479
+ return clone.runner = parent.runner, clone._vite = parent._vite, clone._resolver = parent._resolver, clone._config = config, clone._setHash(), clone._parent = parent, clone._serializedDefines = parent._serializedDefines, clone._provideObject(config.provide), clone;
7370
7480
  }
7371
7481
  }
7372
7482
  function deduped(cb) {
7373
7483
  let _promise;
7374
- return (...args) => {
7484
+ return ((...args) => {
7375
7485
  if (!_promise) _promise = cb(...args).finally(() => {
7376
7486
  _promise = void 0;
7377
7487
  });
7378
7488
  return _promise;
7379
- };
7489
+ });
7380
7490
  }
7381
7491
  async function initializeProject(workspacePath, ctx, options) {
7382
- const project = new TestProject(workspacePath, ctx, options);
7383
- const { configFile,...restOptions } = options;
7384
- const config = {
7492
+ const project = new TestProject(ctx, options), { configFile,...restOptions } = options, config = {
7385
7493
  ...restOptions,
7386
7494
  configFile,
7387
7495
  configLoader: ctx.vite.config.inlineConfig.configLoader,
@@ -7391,25 +7499,20 @@ async function initializeProject(workspacePath, ctx, options) {
7391
7499
  workspacePath
7392
7500
  })]
7393
7501
  };
7394
- await createViteServer(config);
7395
- return project;
7502
+ return await createViteServer(config), project;
7396
7503
  }
7397
7504
  function generateHash(str) {
7398
7505
  let hash = 0;
7399
7506
  if (str.length === 0) return `${hash}`;
7400
7507
  for (let i = 0; i < str.length; i++) {
7401
7508
  const char = str.charCodeAt(i);
7402
- hash = (hash << 5) - hash + char;
7403
- hash = hash & hash;
7509
+ hash = (hash << 5) - hash + char, hash = hash & hash;
7404
7510
  }
7405
7511
  return `${hash}`;
7406
7512
  }
7407
7513
 
7408
7514
  async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projectsDefinition, names) {
7409
- const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition);
7410
- // cli options that affect the project config,
7411
- // not all options are allowed to be overridden
7412
- const overridesOptions = [
7515
+ const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition), overridesOptions = [
7413
7516
  "logHeapUsage",
7414
7517
  "allowOnly",
7415
7518
  "sequence",
@@ -7428,21 +7531,12 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7428
7531
  "inspect",
7429
7532
  "inspectBrk",
7430
7533
  "fileParallelism"
7431
- ];
7432
- const cliOverrides = overridesOptions.reduce((acc, name) => {
7534
+ ], cliOverrides = overridesOptions.reduce((acc, name) => {
7433
7535
  if (name in cliOptions) acc[name] = cliOptions[name];
7434
7536
  return acc;
7435
- }, {});
7436
- const projectPromises = [];
7437
- const fileProjects = [...configFiles, ...nonConfigDirectories];
7438
- const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
7537
+ }, {}), projectPromises = [], fileProjects = [...configFiles, ...nonConfigDirectories], concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
7439
7538
  projectConfigs.forEach((options, index) => {
7440
- const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
7441
- // if extends a config file, resolve the file path
7442
- const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
7443
- // if `root` is configured, resolve it relative to the workspace file or vite root (like other options)
7444
- // if `root` is not specified, inline configs use the same root as the root project
7445
- const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
7539
+ const configRoot = vitest.config.root, configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false, root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
7446
7540
  projectPromises.push(concurrent(() => initializeProject(index, vitest, {
7447
7541
  ...options,
7448
7542
  root,
@@ -7460,8 +7554,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7460
7554
  if (project) projectPromises.push(Promise.resolve(project));
7461
7555
  continue;
7462
7556
  }
7463
- const configFile = path.endsWith("/") ? false : path;
7464
- const root = path.endsWith("/") ? path : dirname(path);
7557
+ const configFile = path.endsWith("/") ? false : path, root = path.endsWith("/") ? path : dirname(path);
7465
7558
  projectPromises.push(concurrent(() => initializeProject(path, vitest, {
7466
7559
  root,
7467
7560
  configFile,
@@ -7474,9 +7567,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7474
7567
  vitest.config.project.length ? `The filter matched no projects: ${vitest.config.project.join(", ")}. ` : "",
7475
7568
  `The projects definition: ${JSON.stringify(projectsDefinition, null, 4)}.`
7476
7569
  ].join(""));
7477
- const resolvedProjectsPromises = await Promise.allSettled(projectPromises);
7478
- const errors = [];
7479
- const resolvedProjects = [];
7570
+ const resolvedProjectsPromises = await Promise.allSettled(projectPromises), errors = [], resolvedProjects = [];
7480
7571
  for (const result of resolvedProjectsPromises) if (result.status === "rejected") {
7481
7572
  if (result.reason instanceof VitestFilteredOutProjectError)
7482
7573
  // filter out filtered out projects
@@ -7488,8 +7579,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7488
7579
  for (const project of resolvedProjects) {
7489
7580
  const name = project.name;
7490
7581
  if (names.has(name)) {
7491
- const duplicate = resolvedProjects.find((p) => p.name === name && p !== project);
7492
- const filesError = fileProjects.length ? [
7582
+ const duplicate = resolvedProjects.find((p) => p.name === name && p !== project), filesError = fileProjects.length ? [
7493
7583
  "\n\nYour config matched these files:\n",
7494
7584
  fileProjects.map((p) => ` - ${relative(vitest.config.root, p)}`).join("\n"),
7495
7585
  "\n\n"
@@ -7511,24 +7601,18 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7511
7601
  const removeProjects = /* @__PURE__ */ new Set();
7512
7602
  resolvedProjects.forEach((project) => {
7513
7603
  if (!project.config.browser.enabled) return;
7514
- const instances = project.config.browser.instances || [];
7515
- const browser = project.config.browser.name;
7516
- if (instances.length === 0 && browser) {
7517
- instances.push({
7518
- browser,
7519
- name: project.name ? `${project.name} (${browser})` : browser
7520
- });
7521
- vitest.logger.warn(withLabel("yellow", "Vitest", [
7522
- `No browser "instances" were defined`,
7523
- project.name ? ` for the "${project.name}" project. ` : ". ",
7524
- `Running tests in "${project.config.browser.name}" browser. `,
7525
- "The \"browser.name\" field is deprecated since Vitest 3. ",
7526
- "Read more: https://vitest.dev/guide/browser/config#browser-instances"
7527
- ].filter(Boolean).join("")));
7528
- }
7529
- const originalName = project.config.name;
7530
- // if original name is in the --project=name filter, keep all instances
7531
- const filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
7604
+ const instances = project.config.browser.instances || [], browser = project.config.browser.name;
7605
+ if (instances.length === 0 && browser) instances.push({
7606
+ browser,
7607
+ name: project.name ? `${project.name} (${browser})` : browser
7608
+ }), vitest.logger.warn(withLabel("yellow", "Vitest", [
7609
+ `No browser "instances" were defined`,
7610
+ project.name ? ` for the "${project.name}" project. ` : ". ",
7611
+ `Running tests in "${project.config.browser.name}" browser. `,
7612
+ "The \"browser.name\" field is deprecated since Vitest 3. ",
7613
+ "Read more: https://vitest.dev/guide/browser/config#browser-instances"
7614
+ ].filter(Boolean).join("")));
7615
+ const originalName = project.config.name, filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
7532
7616
  const newName = instance.name;
7533
7617
  return vitest.matchesProjectFilter(newName);
7534
7618
  });
@@ -7537,12 +7621,10 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7537
7621
  removeProjects.add(project);
7538
7622
  return;
7539
7623
  }
7540
- 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.`));
7541
7624
  filteredInstances.forEach((config, index) => {
7542
7625
  const browser = config.browser;
7543
7626
  if (!browser) {
7544
- const nth = index + 1;
7545
- const ending = nth === 2 ? "nd" : nth === 3 ? "rd" : "th";
7627
+ const nth = index + 1, ending = nth === 2 ? "nd" : nth === 3 ? "rd" : "th";
7546
7628
  throw new Error(`The browser configuration must have a "browser" property. The ${nth}${ending} item in "browser.instances" doesn't have it. Make sure your${originalName ? ` "${originalName}"` : ""} configuration is correct.`);
7547
7629
  }
7548
7630
  const name = config.name;
@@ -7557,18 +7639,15 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7557
7639
  clonedConfig.name = name;
7558
7640
  const clone = TestProject._cloneBrowserProject(project, clonedConfig);
7559
7641
  resolvedProjects.push(clone);
7560
- });
7561
- removeProjects.add(project);
7562
- });
7563
- resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
7642
+ }), removeProjects.add(project);
7643
+ }), resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
7564
7644
  const headedBrowserProjects = resolvedProjects.filter((project) => {
7565
7645
  return project.config.browser.enabled && !project.config.browser.headless;
7566
7646
  });
7567
7647
  if (headedBrowserProjects.length > 1) {
7568
7648
  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("");
7569
7649
  if (!isTTY) throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
7570
- const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; });
7571
- const { projectName } = await prompts.default({
7650
+ const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; }), { projectName } = await prompts.default({
7572
7651
  type: "select",
7573
7652
  name: "projectName",
7574
7653
  choices: headedBrowserProjects.map((project) => ({
@@ -7583,10 +7662,9 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7583
7662
  return resolvedProjects;
7584
7663
  }
7585
7664
  function cloneConfig(project, { browser,...config }) {
7586
- const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config;
7587
- const currentConfig = project.config.browser;
7665
+ const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name, provider,...overrideConfig } = config, currentConfig = project.config.browser, clonedConfig = deepClone(project.config);
7588
7666
  return mergeConfig({
7589
- ...deepClone(project.config),
7667
+ ...clonedConfig,
7590
7668
  browser: {
7591
7669
  ...project.config.browser,
7592
7670
  locators: locators ? { testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute } : project.config.browser.locators,
@@ -7595,21 +7673,18 @@ function cloneConfig(project, { browser,...config }) {
7595
7673
  screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
7596
7674
  screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
7597
7675
  headless: headless ?? currentConfig.headless,
7676
+ provider: provider ?? currentConfig.provider,
7598
7677
  name: browser,
7599
- providerOptions: config,
7600
- instances: void 0
7601
- }
7678
+ instances: []
7679
+ },
7680
+ include: overrideConfig.include && overrideConfig.include.length > 0 ? [] : clonedConfig.include,
7681
+ exclude: overrideConfig.exclude && overrideConfig.exclude.length > 0 ? [] : clonedConfig.exclude,
7682
+ includeSource: overrideConfig.includeSource && overrideConfig.includeSource.length > 0 ? [] : clonedConfig.includeSource
7602
7683
  }, overrideConfig);
7603
7684
  }
7604
7685
  async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition) {
7605
7686
  // project configurations that were specified directly
7606
- const projectsOptions = [];
7607
- // custom config files that were specified directly or resolved from a directory
7608
- const projectsConfigFiles = [];
7609
- // custom glob matches that should be resolved as directories or config files
7610
- const projectsGlobMatches = [];
7611
- // directories that don't have a config file inside, but should be treated as projects
7612
- const nonConfigProjectDirectories = [];
7687
+ const projectsOptions = [], projectsConfigFiles = [], projectsGlobMatches = [], nonConfigProjectDirectories = [];
7613
7688
  for (const definition of projectsDefinition) if (typeof definition === "string") {
7614
7689
  const stringOption = definition.replace("<rootDir>", vitest.config.root);
7615
7690
  // if the string doesn't contain a glob, we can resolve it directly
@@ -7617,8 +7692,7 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7617
7692
  if (!isDynamicPattern(stringOption)) {
7618
7693
  const file = resolve(vitest.config.root, stringOption);
7619
7694
  if (!existsSync(file)) {
7620
- const relativeWorkspaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : void 0;
7621
- const note = workspaceConfigPath ? `Workspace config file "${relativeWorkspaceConfigPath}"` : "Projects definition";
7695
+ const note = "Projects definition";
7622
7696
  throw new Error(`${note} references a non-existing file or a directory: ${file}`);
7623
7697
  }
7624
7698
  const stats = await promises.stat(file);
@@ -7628,7 +7702,7 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7628
7702
  const configFile = await resolveDirectoryConfig(file);
7629
7703
  if (configFile) projectsConfigFiles.push(configFile);
7630
7704
  else {
7631
- const directory = file[file.length - 1] === "/" ? file : `${file}/`;
7705
+ const directory = file.at(-1) === "/" ? file : `${file}/`;
7632
7706
  nonConfigProjectDirectories.push(directory);
7633
7707
  }
7634
7708
  } else
@@ -7654,8 +7728,7 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7654
7728
  "**/*.timestamp-*",
7655
7729
  "**/.DS_Store"
7656
7730
  ]
7657
- };
7658
- const projectsFs = await glob(projectsGlobMatches, globOptions);
7731
+ }, projectsFs = await glob(projectsGlobMatches, globOptions);
7659
7732
  await Promise.all(projectsFs.map(async (path) => {
7660
7733
  // directories are allowed with a glob like `packages/*`
7661
7734
  // in this case every directory is treated as a project
@@ -7674,21 +7747,15 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7674
7747
  };
7675
7748
  }
7676
7749
  async function resolveDirectoryConfig(directory) {
7677
- const files = new Set(await promises.readdir(directory));
7678
- // default resolution looks for vitest.config.* or vite.config.* files
7679
- // this simulates how `findUp` works in packages/vitest/src/node/create.ts:29
7680
- const configFile = configFiles.find((file) => files.has(file));
7681
- if (configFile) return resolve(directory, configFile);
7682
- return null;
7750
+ const files = new Set(await promises.readdir(directory)), configFile = configFiles.find((file) => files.has(file));
7751
+ return configFile ? resolve(directory, configFile) : null;
7683
7752
  }
7684
7753
  function getDefaultTestProject(vitest) {
7685
- const filter = vitest.config.project;
7686
- const project = vitest._ensureRootProject();
7754
+ const filter = vitest.config.project, project = vitest._ensureRootProject();
7687
7755
  if (!filter.length) return project;
7688
7756
  // check for the project name and browser names
7689
7757
  const hasProjects = getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p));
7690
- if (hasProjects) return project;
7691
- return null;
7758
+ return hasProjects ? project : null;
7692
7759
  }
7693
7760
  function getPotentialProjectNames(project) {
7694
7761
  const names = [project.name];
@@ -7700,7 +7767,7 @@ function getPotentialProjectNames(project) {
7700
7767
  async function loadCustomReporterModule(path, runner) {
7701
7768
  let customReporterModule;
7702
7769
  try {
7703
- customReporterModule = await runner.executeId(path);
7770
+ customReporterModule = await runner.import(path);
7704
7771
  } catch (customReporterModuleError) {
7705
7772
  throw new Error(`Failed to load custom Reporter from ${path}`, { cause: customReporterModuleError });
7706
7773
  }
@@ -7708,12 +7775,11 @@ async function loadCustomReporterModule(path, runner) {
7708
7775
  return customReporterModule.default;
7709
7776
  }
7710
7777
  function createReporters(reporterReferences, ctx) {
7711
- const runner = ctx.runner;
7712
- const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
7778
+ const runner = ctx.runner, promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
7713
7779
  if (Array.isArray(referenceOrInstance)) {
7714
7780
  const [reporterName, reporterOptions] = referenceOrInstance;
7715
7781
  if (reporterName === "html") {
7716
- await ctx.packageInstaller.ensureInstalled("@vitest/ui", runner.root, ctx.version);
7782
+ await ctx.packageInstaller.ensureInstalled("@vitest/ui", ctx.config.root, ctx.version);
7717
7783
  const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
7718
7784
  return new CustomReporter(reporterOptions);
7719
7785
  } else if (reporterName in ReportersMap) {
@@ -7750,14 +7816,12 @@ function parseFilter(filter) {
7750
7816
  filename: parsedFilename,
7751
7817
  lineNumber: Number.parseInt(lineNumber)
7752
7818
  };
7753
- else if (lineNumber.match(/^\d+-\d+$/)) throw new RangeLocationFilterProvidedError(filter);
7754
- else return { filename: filter };
7819
+ if (lineNumber.match(/^\d+-\d+$/)) throw new RangeLocationFilterProvidedError(filter);
7820
+ return { filename: filter };
7755
7821
  }
7756
7822
  function groupFilters(filters) {
7757
- const groupedFilters_ = groupBy(filters, (f) => f.filename);
7758
- const groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
7759
- const [filename, filters] = entry;
7760
- const testLocations = filters.map((f) => f.lineNumber);
7823
+ const groupedFilters_ = groupBy(filters, (f) => f.filename), groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
7824
+ const [filename, filters] = entry, testLocations = filters.map((f) => f.lineNumber);
7761
7825
  return [filename, testLocations.filter((l) => l !== void 0)];
7762
7826
  }));
7763
7827
  return groupedFilters;
@@ -7776,45 +7840,35 @@ class VitestSpecifications {
7776
7840
  if (project._isCachedTestFile(moduleId)) specs.push(project.createSpecification(moduleId));
7777
7841
  if (project._isCachedTypecheckFile(moduleId)) specs.push(project.createSpecification(moduleId, [], "typescript"));
7778
7842
  }
7779
- specs.forEach((spec) => this.ensureSpecificationCached(spec));
7780
- return specs;
7843
+ return specs.forEach((spec) => this.ensureSpecificationCached(spec)), specs;
7781
7844
  }
7782
7845
  async getRelevantTestSpecifications(filters = []) {
7783
7846
  return this.filterTestsBySource(await this.globTestSpecifications(filters));
7784
7847
  }
7785
7848
  async globTestSpecifications(filters = []) {
7786
- const files = [];
7787
- const dir = process.cwd();
7788
- const parsedFilters = filters.map((f) => parseFilter(f));
7849
+ const files = [], dir = process.cwd(), parsedFilters = filters.map((f) => parseFilter(f));
7789
7850
  // Require includeTaskLocation when a location filter is passed
7790
7851
  if (!this.vitest.config.includeTaskLocation && parsedFilters.some((f) => f.lineNumber !== void 0)) throw new IncludeTaskLocationDisabledError();
7791
7852
  const testLines = groupFilters(parsedFilters.map((f) => ({
7792
7853
  ...f,
7793
7854
  filename: resolve(dir, f.filename)
7794
- })));
7795
- // Key is file and val specifies whether we have matched this file with testLocation
7796
- const testLocHasMatch = {};
7797
- await Promise.all(this.vitest.projects.map(async (project) => {
7855
+ }))), testLocHasMatch = {};
7856
+ return await Promise.all(this.vitest.projects.map(async (project) => {
7798
7857
  const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
7799
7858
  testFiles.forEach((file) => {
7800
7859
  const lines = testLines[file];
7801
7860
  testLocHasMatch[file] = true;
7802
7861
  const spec = project.createSpecification(file, lines);
7803
- this.ensureSpecificationCached(spec);
7804
- files.push(spec);
7805
- });
7806
- typecheckTestFiles.forEach((file) => {
7862
+ this.ensureSpecificationCached(spec), files.push(spec);
7863
+ }), typecheckTestFiles.forEach((file) => {
7807
7864
  const lines = testLines[file];
7808
7865
  testLocHasMatch[file] = true;
7809
7866
  const spec = project.createSpecification(file, lines, "typescript");
7810
- this.ensureSpecificationCached(spec);
7811
- files.push(spec);
7867
+ this.ensureSpecificationCached(spec), files.push(spec);
7812
7868
  });
7813
- }));
7814
- Object.entries(testLines).forEach(([filepath, loc]) => {
7869
+ })), Object.entries(testLines).forEach(([filepath, loc]) => {
7815
7870
  if (loc.length !== 0 && !testLocHasMatch[filepath]) throw new LocationFilterFileNotFoundError(relative(dir, filepath));
7816
- });
7817
- return files;
7871
+ }), files;
7818
7872
  }
7819
7873
  clearCache(moduleId) {
7820
7874
  if (moduleId) this._cachedSpecs.delete(moduleId);
@@ -7824,30 +7878,20 @@ class VitestSpecifications {
7824
7878
  return this._cachedSpecs.get(moduleId);
7825
7879
  }
7826
7880
  ensureSpecificationCached(spec) {
7827
- const file = spec.moduleId;
7828
- const specs = this._cachedSpecs.get(file) || [];
7829
- const index = specs.findIndex((_s) => _s.project === spec.project && _s.pool === spec.pool);
7830
- if (index === -1) {
7831
- specs.push(spec);
7832
- this._cachedSpecs.set(file, specs);
7833
- } else specs.splice(index, 1, spec);
7881
+ const file = spec.moduleId, specs = this._cachedSpecs.get(file) || [], index = specs.findIndex((_s) => _s.project === spec.project && _s.pool === spec.pool);
7882
+ if (index === -1) specs.push(spec), this._cachedSpecs.set(file, specs);
7883
+ else specs.splice(index, 1, spec);
7834
7884
  return specs;
7835
7885
  }
7836
7886
  async filterTestsBySource(specs) {
7837
7887
  if (this.vitest.config.changed && !this.vitest.config.related) {
7838
- const { VitestGit } = await import('./git.BVQ8w_Sw.js');
7839
- const vitestGit = new VitestGit(this.vitest.config.root);
7840
- const related = await vitestGit.findChangedFiles({ changedSince: this.vitest.config.changed });
7841
- if (!related) {
7842
- process.exitCode = 1;
7843
- throw new GitNotFoundError();
7844
- }
7888
+ const { VitestGit } = await import('./git.BFNcloKD.js'), vitestGit = new VitestGit(this.vitest.config.root), related = await vitestGit.findChangedFiles({ changedSince: this.vitest.config.changed });
7889
+ if (!related) throw process.exitCode = 1, new GitNotFoundError();
7845
7890
  this.vitest.config.related = Array.from(new Set(related));
7846
7891
  }
7847
7892
  const related = this.vitest.config.related;
7848
7893
  if (!related) return specs;
7849
- const forceRerunTriggers = this.vitest.config.forceRerunTriggers;
7850
- const matcher = forceRerunTriggers.length ? pm(forceRerunTriggers) : void 0;
7894
+ const forceRerunTriggers = this.vitest.config.forceRerunTriggers, matcher = forceRerunTriggers.length ? pm(forceRerunTriggers) : void 0;
7851
7895
  if (matcher && related.some((file) => matcher(file))) return specs;
7852
7896
  // don't run anything if no related sources are found
7853
7897
  // if we are in watch mode, we want to process all tests
@@ -7855,8 +7899,7 @@ class VitestSpecifications {
7855
7899
  const testGraphs = await Promise.all(specs.map(async (spec) => {
7856
7900
  const deps = await this.getTestDependencies(spec);
7857
7901
  return [spec, deps];
7858
- }));
7859
- const runningTests = [];
7902
+ })), runningTests = [];
7860
7903
  for (const [specification, deps] of testGraphs)
7861
7904
  // if deps or the test itself were changed
7862
7905
  if (related.some((path) => path === specification.moduleId || deps.has(path))) runningTests.push(specification);
@@ -7866,8 +7909,7 @@ class VitestSpecifications {
7866
7909
  const addImports = async (project, filepath) => {
7867
7910
  if (deps.has(filepath)) return;
7868
7911
  deps.add(filepath);
7869
- const mod = project.vite.moduleGraph.getModuleById(filepath);
7870
- const transformed = mod?.ssrTransformResult || await project.vitenode.transformRequest(filepath);
7912
+ const mod = project.vite.environments.ssr.moduleGraph.getModuleById(filepath), transformed = mod?.transformResult || await project.vite.environments.ssr.transformRequest(filepath);
7871
7913
  if (!transformed) return;
7872
7914
  const dependencies = [...transformed.deps || [], ...transformed.dynamicDeps || []];
7873
7915
  await Promise.all(dependencies.map(async (dep) => {
@@ -7875,9 +7917,7 @@ class VitestSpecifications {
7875
7917
  if (!fsPath.includes("node_modules") && !deps.has(fsPath) && existsSync(fsPath)) await addImports(project, fsPath);
7876
7918
  }));
7877
7919
  };
7878
- await addImports(spec.project, spec.moduleId);
7879
- deps.delete(spec.moduleId);
7880
- return deps;
7920
+ return await addImports(spec.project, spec.moduleId), deps.delete(spec.moduleId), deps;
7881
7921
  }
7882
7922
  }
7883
7923
 
@@ -7903,10 +7943,7 @@ class ReportedTaskImplementation {
7903
7943
  location;
7904
7944
  /** @internal */
7905
7945
  constructor(task, project) {
7906
- this.task = task;
7907
- this.project = project;
7908
- this.id = task.id;
7909
- this.location = task.location;
7946
+ this.task = task, this.project = project, this.id = task.id, this.location = task.location;
7910
7947
  }
7911
7948
  /**
7912
7949
  * Checks if the test did not fail the suite.
@@ -7928,8 +7965,7 @@ class ReportedTaskImplementation {
7928
7965
  */
7929
7966
  static register(task, project) {
7930
7967
  const state = new this(task, project);
7931
- storeTask(project, task, state);
7932
- return state;
7968
+ return storeTask(project, task, state), state;
7933
7969
  }
7934
7970
  }
7935
7971
  class TestCase extends ReportedTaskImplementation {
@@ -7953,9 +7989,7 @@ class TestCase extends ReportedTaskImplementation {
7953
7989
  parent;
7954
7990
  /** @internal */
7955
7991
  constructor(task, project) {
7956
- super(task, project);
7957
- this.name = task.name;
7958
- this.module = getReportedTask(project, task.file);
7992
+ super(task, project), this.name = task.name, this.module = getReportedTask(project, task.file);
7959
7993
  const suite = this.task.suite;
7960
7994
  if (suite) this.parent = getReportedTask(project, suite);
7961
7995
  else this.parent = this.module;
@@ -7977,8 +8011,7 @@ class TestCase extends ReportedTaskImplementation {
7977
8011
  * - **skipped**: Test was skipped during collection or dynamically with `ctx.skip()`.
7978
8012
  */
7979
8013
  result() {
7980
- const result = this.task.result;
7981
- const mode = result?.state || this.task.mode;
8014
+ const result = this.task.result, mode = result?.state || this.task.mode;
7982
8015
  if (!result && (mode === "skip" || mode === "todo")) return {
7983
8016
  state: "skipped",
7984
8017
  note: void 0,
@@ -7989,16 +8022,14 @@ class TestCase extends ReportedTaskImplementation {
7989
8022
  errors: void 0
7990
8023
  };
7991
8024
  const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
7992
- if (state === "skipped") return {
8025
+ return state === "skipped" ? {
7993
8026
  state,
7994
8027
  note: result.note,
7995
8028
  errors: void 0
7996
- };
7997
- if (state === "passed") return {
8029
+ } : state === "passed" ? {
7998
8030
  state,
7999
8031
  errors: result.errors
8000
- };
8001
- return {
8032
+ } : {
8002
8033
  state,
8003
8034
  errors: result.errors || []
8004
8035
  };
@@ -8017,8 +8048,7 @@ class TestCase extends ReportedTaskImplementation {
8017
8048
  const result = this.task.result;
8018
8049
  // startTime should always be available if the test has properly finished
8019
8050
  if (!result || !result.startTime) return void 0;
8020
- const duration = result.duration || 0;
8021
- const slow = duration > this.project.globalConfig.slowTestThreshold;
8051
+ const duration = result.duration || 0, slow = duration > this.project.globalConfig.slowTestThreshold;
8022
8052
  return {
8023
8053
  slow,
8024
8054
  heap: result.heap,
@@ -8034,8 +8064,7 @@ class TestCollection {
8034
8064
  #task;
8035
8065
  #project;
8036
8066
  constructor(task, project) {
8037
- this.#task = task;
8038
- this.#project = project;
8067
+ this.#task = task, this.#project = project;
8039
8068
  }
8040
8069
  /**
8041
8070
  * Returns the test or suite at a specific index.
@@ -8088,10 +8117,7 @@ class TestCollection {
8088
8117
  * Filters all suites that are part of this collection and its children.
8089
8118
  */
8090
8119
  *allSuites() {
8091
- for (const child of this) if (child.type === "suite") {
8092
- yield child;
8093
- yield* child.children.allSuites();
8094
- }
8120
+ for (const child of this) if (child.type === "suite") yield child, yield* child.children.allSuites();
8095
8121
  }
8096
8122
  *[Symbol.iterator]() {
8097
8123
  for (const task of this.#task.tasks) yield getReportedTask(this.#project, task);
@@ -8104,8 +8130,7 @@ class SuiteImplementation extends ReportedTaskImplementation {
8104
8130
  children;
8105
8131
  /** @internal */
8106
8132
  constructor(task, project) {
8107
- super(task, project);
8108
- this.children = new TestCollection(task, project);
8133
+ super(task, project), this.children = new TestCollection(task, project);
8109
8134
  }
8110
8135
  /**
8111
8136
  * Errors that happened outside of the test run during collection, like syntax errors.
@@ -8135,9 +8160,7 @@ class TestSuite extends SuiteImplementation {
8135
8160
  options;
8136
8161
  /** @internal */
8137
8162
  constructor(task, project) {
8138
- super(task, project);
8139
- this.name = task.name;
8140
- this.module = getReportedTask(project, task.file);
8163
+ super(task, project), this.name = task.name, this.module = getReportedTask(project, task.file);
8141
8164
  const suite = this.task.suite;
8142
8165
  if (suite) this.parent = getReportedTask(project, suite);
8143
8166
  else this.parent = this.module;
@@ -8166,31 +8189,27 @@ class TestModule extends SuiteImplementation {
8166
8189
  * This value corresponds to the ID in the Vite's module graph.
8167
8190
  */
8168
8191
  moduleId;
8192
+ /**
8193
+ * Module id relative to the project. This is the same as `task.name`.
8194
+ */
8195
+ relativeModuleId;
8169
8196
  /** @internal */
8170
8197
  constructor(task, project) {
8171
- super(task, project);
8172
- this.moduleId = task.filepath;
8198
+ super(task, project), this.moduleId = task.filepath, this.relativeModuleId = task.name;
8173
8199
  }
8174
8200
  /**
8175
8201
  * Checks the running state of the test file.
8176
8202
  */
8177
8203
  state() {
8178
8204
  const state = this.task.result?.state;
8179
- if (state === "queued") return "queued";
8180
- return getSuiteState(this.task);
8205
+ return state === "queued" ? "queued" : getSuiteState(this.task);
8181
8206
  }
8182
8207
  /**
8183
8208
  * Useful information about the module like duration, memory usage, etc.
8184
8209
  * If the module was not executed yet, all diagnostic values will return `0`.
8185
8210
  */
8186
8211
  diagnostic() {
8187
- const setupDuration = this.task.setupDuration || 0;
8188
- const collectDuration = this.task.collectDuration || 0;
8189
- const prepareDuration = this.task.prepareDuration || 0;
8190
- const environmentSetupDuration = this.task.environmentLoad || 0;
8191
- const duration = this.task.result?.duration || 0;
8192
- const heap = this.task.result?.heap;
8193
- const importDurations = this.task.importDurations ?? {};
8212
+ const setupDuration = this.task.setupDuration || 0, collectDuration = this.task.collectDuration || 0, prepareDuration = this.task.prepareDuration || 0, environmentSetupDuration = this.task.environmentLoad || 0, duration = this.task.result?.duration || 0, heap = this.task.result?.heap, importDurations = this.task.importDurations ?? {};
8194
8213
  return {
8195
8214
  environmentSetupDuration,
8196
8215
  prepareDuration,
@@ -8222,18 +8241,19 @@ function getReportedTask(project, runnerTask) {
8222
8241
  return reportedTask;
8223
8242
  }
8224
8243
  function getSuiteState(task) {
8225
- const mode = task.mode;
8226
- const state = task.result?.state;
8244
+ const mode = task.mode, state = task.result?.state;
8227
8245
  if (mode === "skip" || mode === "todo" || state === "skip" || state === "todo") return "skipped";
8228
8246
  if (state == null || state === "run" || state === "only") return "pending";
8229
8247
  if (state === "fail") return "failed";
8230
8248
  if (state === "pass") return "passed";
8231
8249
  throw new Error(`Unknown suite state: ${state}`);
8232
8250
  }
8251
+ function experimental_getRunnerTask(entity) {
8252
+ return entity.task;
8253
+ }
8233
8254
 
8234
8255
  function isAggregateError(err) {
8235
- if (typeof AggregateError !== "undefined" && err instanceof AggregateError) return true;
8236
- return err instanceof Error && "errors" in err;
8256
+ return typeof AggregateError !== "undefined" && err instanceof AggregateError ? true : err instanceof Error && "errors" in err;
8237
8257
  }
8238
8258
  class StateManager {
8239
8259
  filesMap = /* @__PURE__ */ new Map();
@@ -8244,25 +8264,30 @@ class StateManager {
8244
8264
  processTimeoutCauses = /* @__PURE__ */ new Set();
8245
8265
  reportedTasksMap = /* @__PURE__ */ new WeakMap();
8246
8266
  blobs;
8247
- catchError(err, type) {
8248
- if (isAggregateError(err)) return err.errors.forEach((error) => this.catchError(error, type));
8249
- if (err === Object(err)) err.type = type;
8250
- else err = {
8267
+ transformTime = 0;
8268
+ onUnhandledError;
8269
+ /** @internal */
8270
+ _data = {
8271
+ browserLastPort: defaultBrowserPort,
8272
+ timeoutIncreased: false
8273
+ };
8274
+ constructor(options) {
8275
+ this.onUnhandledError = options.onUnhandledError;
8276
+ }
8277
+ catchError(error, type) {
8278
+ if (isAggregateError(error)) return error.errors.forEach((error) => this.catchError(error, type));
8279
+ if (typeof error === "object" && error !== null) error.type = type;
8280
+ else error = {
8251
8281
  type,
8252
- message: err
8282
+ message: error
8253
8283
  };
8254
- const _err = err;
8255
- if (_err && typeof _err === "object" && _err.code === "VITEST_PENDING") {
8256
- const task = this.idMap.get(_err.taskId);
8257
- if (task) {
8258
- task.mode = "skip";
8259
- task.result ??= { state: "skip" };
8260
- task.result.state = "skip";
8261
- task.result.note = _err.note;
8262
- }
8284
+ const _error = error;
8285
+ if (_error && typeof _error === "object" && _error.code === "VITEST_PENDING") {
8286
+ const task = this.idMap.get(_error.taskId);
8287
+ if (task) task.mode = "skip", task.result ??= { state: "skip" }, task.result.state = "skip", task.result.note = _error.note;
8263
8288
  return;
8264
8289
  }
8265
- this.errorsSet.add(err);
8290
+ if (!this.onUnhandledError || this.onUnhandledError(error) !== false) this.errorsSet.add(error);
8266
8291
  }
8267
8292
  clearErrors() {
8268
8293
  this.errorsSet.clear();
@@ -8283,12 +8308,8 @@ class StateManager {
8283
8308
  * Return files that were running or collected.
8284
8309
  */
8285
8310
  getFiles(keys) {
8286
- if (keys) return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
8287
- return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
8288
- // print typecheck files first
8289
- if (f1.meta?.typecheck && f2.meta?.typecheck) return 0;
8290
- if (f1.meta?.typecheck) return -1;
8291
- return 1;
8311
+ return keys ? keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local) : Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
8312
+ return f1.meta?.typecheck && f2.meta?.typecheck ? 0 : f1.meta?.typecheck ? -1 : 1;
8292
8313
  });
8293
8314
  }
8294
8315
  getTestModules(keys) {
@@ -8307,25 +8328,17 @@ class StateManager {
8307
8328
  }
8308
8329
  collectFiles(project, files = []) {
8309
8330
  files.forEach((file) => {
8310
- const existing = this.filesMap.get(file.filepath) || [];
8311
- const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
8312
- const currentFile = existing.find((i) => i.projectName === file.projectName);
8331
+ const existing = this.filesMap.get(file.filepath) || [], otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck), currentFile = existing.find((i) => i.projectName === file.projectName);
8313
8332
  // keep logs for the previous file because it should always be initiated before the collections phase
8314
8333
  // which means that all logs are collected during the collection and not inside tests
8315
8334
  if (currentFile) file.logs = currentFile.logs;
8316
- otherFiles.push(file);
8317
- this.filesMap.set(file.filepath, otherFiles);
8318
- this.updateId(file, project);
8335
+ otherFiles.push(file), this.filesMap.set(file.filepath, otherFiles), this.updateId(file, project);
8319
8336
  });
8320
8337
  }
8321
8338
  clearFiles(project, paths = []) {
8322
8339
  paths.forEach((path) => {
8323
- const files = this.filesMap.get(path);
8324
- const fileTask = createFileTask(path, project.config.root, project.config.name);
8325
- fileTask.local = true;
8326
- TestModule.register(fileTask, project);
8327
- this.idMap.set(fileTask.id, fileTask);
8328
- if (!files) {
8340
+ const files = this.filesMap.get(path), fileTask = createFileTask$1(path, project.config.root, project.config.name);
8341
+ if (fileTask.local = true, TestModule.register(fileTask, project), this.idMap.set(fileTask.id, fileTask), !files) {
8329
8342
  this.filesMap.set(path, [fileTask]);
8330
8343
  return;
8331
8344
  }
@@ -8336,14 +8349,14 @@ class StateManager {
8336
8349
  });
8337
8350
  }
8338
8351
  updateId(task, project) {
8339
- if (this.idMap.get(task.id) === task) return;
8340
- if (task.type === "suite" && "filepath" in task) TestModule.register(task, project);
8341
- else if (task.type === "suite") TestSuite.register(task, project);
8342
- else TestCase.register(task, project);
8343
- this.idMap.set(task.id, task);
8344
- if (task.type === "suite") task.tasks.forEach((task) => {
8345
- this.updateId(task, project);
8346
- });
8352
+ if (this.idMap.get(task.id) !== task) {
8353
+ if (task.type === "suite" && "filepath" in task) TestModule.register(task, project);
8354
+ else if (task.type === "suite") TestSuite.register(task, project);
8355
+ else TestCase.register(task, project);
8356
+ if (this.idMap.set(task.id, task), task.type === "suite") task.tasks.forEach((task) => {
8357
+ this.updateId(task, project);
8358
+ });
8359
+ }
8347
8360
  }
8348
8361
  getReportedEntity(task) {
8349
8362
  return this.reportedTasksMap.get(task);
@@ -8352,10 +8365,8 @@ class StateManager {
8352
8365
  for (const [id, result, meta] of packs) {
8353
8366
  const task = this.idMap.get(id);
8354
8367
  if (task) {
8355
- task.result = result;
8356
- task.meta = meta;
8357
8368
  // skipped with new PendingError
8358
- if (result?.state === "skip") task.mode = "skip";
8369
+ if (task.result = result, task.meta = meta, result?.state === "skip") task.mode = "skip";
8359
8370
  }
8360
8371
  }
8361
8372
  }
@@ -8370,7 +8381,7 @@ class StateManager {
8370
8381
  return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
8371
8382
  }
8372
8383
  cancelFiles(files, project) {
8373
- this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
8384
+ this.collectFiles(project, files.map((filepath) => createFileTask$1(filepath, project.config.root, project.config.name)));
8374
8385
  }
8375
8386
  }
8376
8387
 
@@ -8832,10 +8843,7 @@ class TestRun {
8832
8843
  }
8833
8844
  async start(specifications) {
8834
8845
  const filepaths = specifications.map((spec) => spec.moduleId);
8835
- this.vitest.state.collectPaths(filepaths);
8836
- await this.vitest.report("onPathsCollected", Array.from(new Set(filepaths)));
8837
- await this.vitest.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
8838
- await this.vitest.report("onTestRunStart", [...specifications]);
8846
+ this.vitest.state.collectPaths(filepaths), await this.vitest.report("onTestRunStart", [...specifications]);
8839
8847
  }
8840
8848
  async enqueued(project, file) {
8841
8849
  this.vitest.state.collectFiles(project, [file]);
@@ -8843,30 +8851,22 @@ class TestRun {
8843
8851
  await this.vitest.report("onTestModuleQueued", testModule);
8844
8852
  }
8845
8853
  async collected(project, files) {
8846
- this.vitest.state.collectFiles(project, files);
8847
- await Promise.all([this.vitest.report("onCollected", files), ...files.map((file) => {
8854
+ this.vitest.state.collectFiles(project, files), await Promise.all(files.map((file) => {
8848
8855
  const testModule = this.vitest.state.getReportedEntity(file);
8849
8856
  return this.vitest.report("onTestModuleCollected", testModule);
8850
- })]);
8857
+ }));
8851
8858
  }
8852
8859
  async log(log) {
8853
- this.vitest.state.updateUserLog(log);
8854
- await this.vitest.report("onUserConsoleLog", log);
8860
+ this.vitest.state.updateUserLog(log), await this.vitest.report("onUserConsoleLog", log);
8855
8861
  }
8856
8862
  async annotate(testId, annotation) {
8857
- const task = this.vitest.state.idMap.get(testId);
8858
- const entity = task && this.vitest.state.getReportedEntity(task);
8859
- assert$1(task && entity, `Entity must be found for task ${task?.name || testId}`);
8860
- assert$1(entity.type === "test", `Annotation can only be added to a test, instead got ${entity.type}`);
8861
- await this.resolveTestAttachment(entity, annotation);
8862
- entity.task.annotations.push(annotation);
8863
- await this.vitest.report("onTestCaseAnnotate", entity, annotation);
8864
- return annotation;
8863
+ const task = this.vitest.state.idMap.get(testId), entity = task && this.vitest.state.getReportedEntity(task);
8864
+ return assert$1(task && entity, `Entity must be found for task ${task?.name || testId}`), assert$1(entity.type === "test", `Annotation can only be added to a test, instead got ${entity.type}`), await this.resolveTestAttachment(entity, annotation), entity.task.annotations.push(annotation), await this.vitest.report("onTestCaseAnnotate", entity, annotation), annotation;
8865
8865
  }
8866
8866
  async updated(update, events) {
8867
- this.vitest.state.updateTasks(update);
8867
+ this.syncUpdateStacks(update), this.vitest.state.updateTasks(update);
8868
8868
  for (const [id, event, data] of events) await this.reportEvent(id, event, data).catch((error) => {
8869
- this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
8869
+ this.vitest.state.catchError(serializeError$1(error), "Unhandled Reporter Error");
8870
8870
  });
8871
8871
  // TODO: what is the order or reports here?
8872
8872
  // "onTaskUpdate" in parallel with others or before all or after all?
@@ -8874,30 +8874,32 @@ class TestRun {
8874
8874
  await this.vitest.report("onTaskUpdate", update, events);
8875
8875
  }
8876
8876
  async end(specifications, errors, coverage) {
8877
+ if (coverage) await this.vitest.report("onCoverage", coverage);
8877
8878
  // specification won't have the File task if they were filtered by the --shard command
8878
- const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
8879
- const files = modules.map((m) => m.task);
8880
- const state = this.vitest.isCancelling ? "interrupted" : this.hasFailed(modules) ? "failed" : "passed";
8879
+ const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null), state = this.vitest.isCancelling ? "interrupted" : this.hasFailed(modules) ? "failed" : "passed";
8881
8880
  if (state !== "passed") process.exitCode = 1;
8882
- try {
8883
- await Promise.all([this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.report("onFinished", files, errors, coverage)]);
8884
- } finally {
8885
- if (coverage) await this.vitest.report("onCoverage", coverage);
8886
- }
8881
+ await this.vitest.report("onTestRunEnd", modules, [...errors], state);
8887
8882
  }
8888
8883
  hasFailed(modules) {
8889
- if (!modules.length) return !this.vitest.config.passWithNoTests;
8890
- return modules.some((m) => !m.ok());
8884
+ return modules.length ? modules.some((m) => !m.ok()) : !this.vitest.config.passWithNoTests;
8885
+ }
8886
+ syncUpdateStacks(update) {
8887
+ update.forEach(([taskId, result]) => {
8888
+ const task = this.vitest.state.idMap.get(taskId), isBrowser = task && task.file.pool === "browser";
8889
+ result?.errors?.forEach((error) => {
8890
+ if (isPrimitive(error)) return;
8891
+ const project = this.vitest.getProjectByName(task.file.projectName || "");
8892
+ if (isBrowser) error.stacks = project.browser?.parseErrorStacktrace(error, { frameFilter: project.config.onStackTrace }) || [];
8893
+ else error.stacks = parseErrorStacktrace(error, { frameFilter: project.config.onStackTrace });
8894
+ });
8895
+ });
8891
8896
  }
8892
8897
  async reportEvent(id, event, data) {
8893
- const task = this.vitest.state.idMap.get(id);
8894
- const entity = task && this.vitest.state.getReportedEntity(task);
8895
- assert$1(task && entity, `Entity must be found for task ${task?.name || id}`);
8896
- if (event === "suite-prepare" && entity.type === "suite") return await this.vitest.report("onTestSuiteReady", entity);
8898
+ const task = this.vitest.state.idMap.get(id), entity = task && this.vitest.state.getReportedEntity(task);
8899
+ if (assert$1(task && entity, `Entity must be found for task ${task?.name || id}`), event === "suite-prepare" && entity.type === "suite") return await this.vitest.report("onTestSuiteReady", entity);
8897
8900
  if (event === "suite-prepare" && entity.type === "module") return await this.vitest.report("onTestModuleStart", entity);
8898
8901
  if (event === "suite-finished") {
8899
- assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
8900
- if (entity.state() === "skipped")
8902
+ if (assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module"), entity.state() === "skipped")
8901
8903
  // everything inside suite or a module is skipped,
8902
8904
  // so we won't get any children events
8903
8905
  // we need to report everything manually
@@ -8909,8 +8911,7 @@ class TestRun {
8909
8911
  if (event === "test-prepare" && entity.type === "test") return await this.vitest.report("onTestCaseReady", entity);
8910
8912
  if (event === "test-finished" && entity.type === "test") return await this.vitest.report("onTestCaseResult", entity);
8911
8913
  if (event.startsWith("before-hook") || event.startsWith("after-hook")) {
8912
- const isBefore = event.startsWith("before-hook");
8913
- const hook = entity.type === "test" ? {
8914
+ const isBefore = event.startsWith("before-hook"), hook = entity.type === "test" ? {
8914
8915
  name: isBefore ? "beforeEach" : "afterEach",
8915
8916
  entity
8916
8917
  } : {
@@ -8922,37 +8923,25 @@ class TestRun {
8922
8923
  // this can only happen in --merge-reports, and annotation is already resolved
8923
8924
  if (event === "test-annotation") {
8924
8925
  const annotation = data?.annotation;
8925
- assert$1(annotation && entity.type === "test");
8926
- await this.vitest.report("onTestCaseAnnotate", entity, annotation);
8926
+ assert$1(annotation && entity.type === "test"), await this.vitest.report("onTestCaseAnnotate", entity, annotation);
8927
8927
  }
8928
8928
  }
8929
8929
  }
8930
8930
  async resolveTestAttachment(test, annotation) {
8931
- const project = test.project;
8932
- const attachment = annotation.attachment;
8931
+ const project = test.project, attachment = annotation.attachment;
8933
8932
  if (!attachment) return attachment;
8934
8933
  const path = attachment.path;
8935
8934
  if (path && !path.startsWith("http://") && !path.startsWith("https://")) {
8936
- const currentPath = resolve(project.config.root, path);
8937
- const hash = createHash("sha1").update(currentPath).digest("hex");
8938
- const newPath = resolve(project.config.attachmentsDir, `${sanitizeFilePath(annotation.message)}-${hash}${extname(currentPath)}`);
8939
- await mkdir(dirname(newPath), { recursive: true });
8940
- await copyFile(currentPath, newPath);
8941
- attachment.path = newPath;
8935
+ 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)}`);
8936
+ await mkdir(dirname(newPath), { recursive: true }), await copyFile(currentPath, newPath), attachment.path = newPath;
8942
8937
  const contentType = attachment.contentType ?? mime.getType(basename(currentPath));
8943
8938
  attachment.contentType = contentType || void 0;
8944
8939
  }
8945
8940
  return attachment;
8946
8941
  }
8947
8942
  async reportChildren(children) {
8948
- for (const child of children) if (child.type === "test") {
8949
- await this.vitest.report("onTestCaseReady", child);
8950
- await this.vitest.report("onTestCaseResult", child);
8951
- } else {
8952
- await this.vitest.report("onTestSuiteReady", child);
8953
- await this.reportChildren(child.children);
8954
- await this.vitest.report("onTestSuiteResult", child);
8955
- }
8943
+ for (const child of children) if (child.type === "test") await this.vitest.report("onTestCaseReady", child), await this.vitest.report("onTestCaseResult", child);
8944
+ else await this.vitest.report("onTestSuiteReady", child), await this.reportChildren(child.children), await this.vitest.report("onTestSuiteResult", child);
8956
8945
  }
8957
8946
  }
8958
8947
  function sanitizeFilePath(s) {
@@ -8980,23 +8969,15 @@ class VitestWatcher {
8980
8969
  * @internal
8981
8970
  */
8982
8971
  onWatcherRerun(cb) {
8983
- this._onRerun.push(cb);
8984
- return this;
8972
+ return this._onRerun.push(cb), this;
8985
8973
  }
8986
8974
  unregisterWatcher = noop;
8987
8975
  registerWatcher() {
8988
8976
  const watcher = this.vitest.vite.watcher;
8989
8977
  if (this.vitest.config.forceRerunTriggers.length) watcher.add(this.vitest.config.forceRerunTriggers);
8990
- watcher.on("change", this.onChange);
8991
- watcher.on("unlink", this.onUnlink);
8992
- watcher.on("add", this.onAdd);
8993
- this.unregisterWatcher = () => {
8994
- watcher.off("change", this.onChange);
8995
- watcher.off("unlink", this.onUnlink);
8996
- watcher.off("add", this.onAdd);
8997
- this.unregisterWatcher = noop;
8998
- };
8999
- return this;
8978
+ return watcher.on("change", this.onFileChange), watcher.on("unlink", this.onFileDelete), watcher.on("add", this.onFileCreate), this.unregisterWatcher = () => {
8979
+ watcher.off("change", this.onFileChange), watcher.off("unlink", this.onFileDelete), watcher.off("add", this.onFileCreate), this.unregisterWatcher = noop;
8980
+ }, this;
9000
8981
  }
9001
8982
  scheduleRerun(file) {
9002
8983
  this._onRerun.forEach((cb) => cb(file));
@@ -9004,25 +8985,17 @@ class VitestWatcher {
9004
8985
  getTestFilesFromWatcherTrigger(id) {
9005
8986
  if (!this.vitest.config.watchTriggerPatterns) return false;
9006
8987
  let triggered = false;
9007
- this.vitest.config.watchTriggerPatterns.forEach((definition) => {
8988
+ return this.vitest.config.watchTriggerPatterns.forEach((definition) => {
9008
8989
  const exec = definition.pattern.exec(id);
9009
8990
  if (exec) {
9010
8991
  const files = definition.testsToRun(id, exec);
9011
- if (Array.isArray(files)) {
9012
- triggered = true;
9013
- files.forEach((file) => this.changedTests.add(resolve(this.vitest.config.root, file)));
9014
- } else if (typeof files === "string") {
9015
- triggered = true;
9016
- this.changedTests.add(resolve(this.vitest.config.root, files));
9017
- }
8992
+ if (Array.isArray(files)) triggered = true, files.forEach((file) => this.changedTests.add(resolve(this.vitest.config.root, file)));
8993
+ else if (typeof files === "string") triggered = true, this.changedTests.add(resolve(this.vitest.config.root, files));
9018
8994
  }
9019
- });
9020
- return triggered;
8995
+ }), triggered;
9021
8996
  }
9022
- onChange = (id) => {
9023
- id = slash(id);
9024
- this.vitest.logger.clearHighlightCache(id);
9025
- this.vitest.invalidateFile(id);
8997
+ onFileChange = (id) => {
8998
+ id = slash(id), this.vitest.logger.clearHighlightCache(id), this.vitest.invalidateFile(id);
9026
8999
  const testFiles = this.getTestFilesFromWatcherTrigger(id);
9027
9000
  if (testFiles) this.scheduleRerun(id);
9028
9001
  else {
@@ -9030,22 +9003,11 @@ class VitestWatcher {
9030
9003
  if (needsRerun) this.scheduleRerun(id);
9031
9004
  }
9032
9005
  };
9033
- onUnlink = (id) => {
9034
- id = slash(id);
9035
- this.vitest.logger.clearHighlightCache(id);
9036
- this.invalidates.add(id);
9037
- if (this.vitest.state.filesMap.has(id)) {
9038
- this.vitest.projects.forEach((project) => project._removeCachedTestFile(id));
9039
- this.vitest.state.filesMap.delete(id);
9040
- this.vitest.cache.results.removeFromCache(id);
9041
- this.vitest.cache.stats.removeStats(id);
9042
- this.changedTests.delete(id);
9043
- this.vitest.report("onTestRemoved", id);
9044
- }
9006
+ onFileDelete = (id) => {
9007
+ 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);
9045
9008
  };
9046
- onAdd = (id) => {
9047
- id = slash(id);
9048
- this.vitest.invalidateFile(id);
9009
+ onFileCreate = (id) => {
9010
+ id = slash(id), this.vitest.invalidateFile(id);
9049
9011
  const testFiles = this.getTestFilesFromWatcherTrigger(id);
9050
9012
  if (testFiles) {
9051
9013
  this.scheduleRerun(id);
@@ -9053,13 +9015,10 @@ class VitestWatcher {
9053
9015
  }
9054
9016
  let fileContent;
9055
9017
  const matchingProjects = [];
9056
- this.vitest.projects.forEach((project) => {
9018
+ if (this.vitest.projects.forEach((project) => {
9057
9019
  if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) matchingProjects.push(project);
9058
- });
9059
- if (matchingProjects.length > 0) {
9060
- this.changedTests.add(id);
9061
- this.scheduleRerun(id);
9062
- } else {
9020
+ }), matchingProjects.length > 0) this.changedTests.add(id), this.scheduleRerun(id);
9021
+ else {
9063
9022
  // it's possible that file was already there but watcher triggered "add" event instead
9064
9023
  const needsRerun = this.handleFileChanged(id);
9065
9024
  if (needsRerun) this.scheduleRerun(id);
@@ -9067,51 +9026,33 @@ class VitestWatcher {
9067
9026
  };
9068
9027
  handleSetupFile(filepath) {
9069
9028
  let isSetupFile = false;
9070
- this.vitest.projects.forEach((project) => {
9071
- if (!project.config.setupFiles.includes(filepath)) return;
9072
- this.vitest.state.filesMap.forEach((files) => {
9029
+ return this.vitest.projects.forEach((project) => {
9030
+ project.config.setupFiles.includes(filepath) && this.vitest.state.filesMap.forEach((files) => {
9073
9031
  files.forEach((file) => {
9074
- if (file.projectName === project.name) {
9075
- isSetupFile = true;
9076
- this.changedTests.add(file.filepath);
9077
- }
9032
+ if (file.projectName === project.name) isSetupFile = true, this.changedTests.add(file.filepath);
9078
9033
  });
9079
9034
  });
9080
- });
9081
- return isSetupFile;
9035
+ }), isSetupFile;
9082
9036
  }
9083
9037
  /**
9084
9038
  * @returns A value indicating whether rerun is needed (changedTests was mutated)
9085
9039
  */
9086
9040
  handleFileChanged(filepath) {
9087
9041
  if (this.changedTests.has(filepath) || this.invalidates.has(filepath)) return false;
9088
- if (pm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) {
9089
- this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file));
9090
- return true;
9091
- }
9042
+ if (pm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) return this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file)), true;
9092
9043
  if (this.handleSetupFile(filepath)) return true;
9093
9044
  const projects = this.vitest.projects.filter((project) => {
9094
9045
  const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
9095
9046
  return moduleGraph.getModulesByFile(filepath)?.size;
9096
9047
  });
9097
- if (!projects.length) {
9098
- // if there are no modules it's possible that server was restarted
9099
- // we don't have information about importers anymore, so let's check if the file is a test file at least
9100
- if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
9101
- this.changedTests.add(filepath);
9102
- return true;
9103
- }
9104
- return false;
9105
- }
9048
+ if (!projects.length) return this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath)) ? (this.changedTests.add(filepath), true) : false;
9106
9049
  const files = [];
9107
9050
  for (const project of projects) {
9108
9051
  const mods = project.browser?.vite.moduleGraph.getModulesByFile(filepath) || project.vite.moduleGraph.getModulesByFile(filepath);
9109
9052
  if (!mods || !mods.size) continue;
9110
- this.invalidates.add(filepath);
9111
9053
  // one of test files that we already run, or one of test files that we can run
9112
- if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
9113
- this.changedTests.add(filepath);
9114
- files.push(filepath);
9054
+ if (this.invalidates.add(filepath), this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
9055
+ this.changedTests.add(filepath), files.push(filepath);
9115
9056
  continue;
9116
9057
  }
9117
9058
  let rerun = false;
@@ -9158,43 +9099,38 @@ class Vitest {
9158
9099
  * If projects were filtered with `--project` flag, they won't appear here.
9159
9100
  */
9160
9101
  projects = [];
9102
+ /**
9103
+ * A watcher handler. This is not the file system watcher. The handler only
9104
+ * exposes methods to handle changed files.
9105
+ *
9106
+ * If you have your own watcher, you can use these methods to replicate
9107
+ * Vitest behaviour.
9108
+ */
9109
+ watcher;
9161
9110
  /** @internal */ configOverride = {};
9162
- /** @internal */ coverageProvider;
9163
9111
  /** @internal */ filenamePattern;
9164
9112
  /** @internal */ runningPromise;
9165
9113
  /** @internal */ closingPromise;
9166
9114
  /** @internal */ isCancelling = false;
9167
9115
  /** @internal */ coreWorkspaceProject;
9168
- /**
9169
- * @internal
9170
- * @deprecated
9171
- */
9172
- resolvedProjects = [];
9173
- /** @internal */ _browserLastPort = defaultBrowserPort;
9174
9116
  /** @internal */ _browserSessions = new BrowserSessions();
9175
9117
  /** @internal */ _cliOptions = {};
9176
9118
  /** @internal */ reporters = [];
9177
- /** @internal */ vitenode = void 0;
9178
- /** @internal */ runner = void 0;
9119
+ /** @internal */ runner;
9179
9120
  /** @internal */ _testRun = void 0;
9121
+ /** @internal */ _resolver;
9180
9122
  isFirstRun = true;
9181
9123
  restartsCount = 0;
9182
9124
  specifications;
9183
- watcher;
9184
9125
  pool;
9185
9126
  _config;
9186
9127
  _vite;
9187
9128
  _state;
9188
9129
  _cache;
9189
9130
  _snapshot;
9190
- _workspaceConfigPath;
9131
+ _coverageProvider;
9191
9132
  constructor(mode, cliOptions, options = {}) {
9192
- this.mode = mode;
9193
- this._cliOptions = cliOptions;
9194
- this.logger = new Logger(this, options.stdout, options.stderr);
9195
- this.packageInstaller = options.packageInstaller || new VitestPackageInstaller();
9196
- this.specifications = new VitestSpecifications(this);
9197
- this.watcher = new VitestWatcher(this).onWatcherRerun((file) => this.scheduleRerun([file]));
9133
+ this.mode = mode, this._cliOptions = cliOptions, this.logger = new Logger(this, options.stdout, options.stderr), this.packageInstaller = options.packageInstaller || new VitestPackageInstaller(), this.specifications = new VitestSpecifications(this), this.watcher = new VitestWatcher(this).onWatcherRerun((file) => this.scheduleRerun(file));
9198
9134
  }
9199
9135
  _onRestartListeners = [];
9200
9136
  _onClose = [];
@@ -9202,114 +9138,54 @@ class Vitest {
9202
9138
  _onCancelListeners = [];
9203
9139
  _onUserTestsRerun = [];
9204
9140
  _onFilterWatchedSpecification = [];
9205
- /** @deprecated will be removed in 4.0, use `onFilterWatchedSpecification` instead */
9206
- get invalidates() {
9207
- return this.watcher.invalidates;
9208
- }
9209
- /** @deprecated will be removed in 4.0, use `onFilterWatchedSpecification` instead */
9210
- get changedTests() {
9211
- return this.watcher.changedTests;
9212
- }
9213
9141
  /**
9214
9142
  * The global config.
9215
9143
  */
9216
9144
  get config() {
9217
- assert(this._config, "config");
9218
- return this._config;
9219
- }
9220
- /** @deprecated use `vitest.vite` instead */
9221
- get server() {
9222
- return this._vite;
9145
+ return assert(this._config, "config"), this._config;
9223
9146
  }
9224
9147
  /**
9225
9148
  * Global Vite's dev server instance.
9226
9149
  */
9227
9150
  get vite() {
9228
- assert(this._vite, "vite", "server");
9229
- return this._vite;
9151
+ return assert(this._vite, "vite", "server"), this._vite;
9230
9152
  }
9231
9153
  /**
9232
9154
  * The global test state manager.
9233
9155
  * @experimental The State API is experimental and not subject to semver.
9234
9156
  */
9235
9157
  get state() {
9236
- assert(this._state, "state");
9237
- return this._state;
9158
+ return assert(this._state, "state"), this._state;
9238
9159
  }
9239
9160
  /**
9240
9161
  * The global snapshot manager. You can access the current state on `snapshot.summary`.
9241
9162
  */
9242
9163
  get snapshot() {
9243
- assert(this._snapshot, "snapshot", "snapshot manager");
9244
- return this._snapshot;
9164
+ return assert(this._snapshot, "snapshot", "snapshot manager"), this._snapshot;
9245
9165
  }
9246
9166
  /**
9247
9167
  * Test results and test file stats cache. Primarily used by the sequencer to sort tests.
9248
9168
  */
9249
9169
  get cache() {
9250
- assert(this._cache, "cache");
9251
- return this._cache;
9252
- }
9253
- /** @deprecated internal */
9254
- setServer(options, server) {
9255
- return this._setServer(options, server);
9170
+ return assert(this._cache, "cache"), this._cache;
9256
9171
  }
9257
9172
  /** @internal */
9258
9173
  async _setServer(options, server) {
9259
- this.watcher.unregisterWatcher();
9260
- clearTimeout(this._rerunTimer);
9261
- this.restartsCount += 1;
9262
- this._browserLastPort = defaultBrowserPort;
9263
- this.pool?.close?.();
9264
- this.pool = void 0;
9265
- this.closingPromise = void 0;
9266
- this.projects = [];
9267
- this.resolvedProjects = [];
9268
- this._workspaceConfigPath = void 0;
9269
- this.coverageProvider = void 0;
9270
- this.runningPromise = void 0;
9271
- this.coreWorkspaceProject = void 0;
9272
- this.specifications.clearCache();
9273
- this._onUserTestsRerun = [];
9274
- this._vite = server;
9174
+ 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;
9275
9175
  const resolved = resolveConfig(this, options, server.config);
9276
- this._config = resolved;
9277
- this._state = new StateManager();
9278
- this._cache = new VitestCache(this.version);
9279
- this._snapshot = new SnapshotManager({ ...resolved.snapshotOptions });
9280
- this._testRun = new TestRun(this);
9281
- if (this.config.watch) this.watcher.registerWatcher();
9282
- this.vitenode = new ViteNodeServer(server, this.config.server);
9283
- const node = this.vitenode;
9284
- this.runner = new ViteNodeRunner({
9285
- root: server.config.root,
9286
- base: server.config.base,
9287
- fetchModule(id) {
9288
- return node.fetchModule(id);
9289
- },
9290
- resolveId(id, importer) {
9291
- return node.resolveId(id, importer);
9292
- }
9293
- });
9294
- if (this.config.watch) {
9176
+ 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();
9177
+ this._resolver = new VitestResolver(server.config.cacheDir, resolved);
9178
+ const environment = server.environments.__vitest__;
9179
+ if (this.runner = new ServerModuleRunner(environment, this._resolver, resolved), this.config.watch) {
9295
9180
  // hijack server restart
9296
9181
  const serverRestart = server.restart;
9297
- server.restart = async (...args) => {
9298
- await Promise.all(this._onRestartListeners.map((fn) => fn()));
9299
- this.report("onServerRestart");
9300
- await this.close();
9301
- await serverRestart(...args);
9302
- };
9303
9182
  // since we set `server.hmr: false`, Vite does not auto restart itself
9304
- server.watcher.on("change", async (file) => {
9183
+ server.restart = async (...args) => {
9184
+ await Promise.all(this._onRestartListeners.map((fn) => fn())), this.report("onServerRestart"), await this.close(), await serverRestart(...args);
9185
+ }, server.watcher.on("change", async (file) => {
9305
9186
  file = normalize(file);
9306
- const isConfig = file === server.config.configFile || this.projects.some((p) => p.vite.config.configFile === file) || file === this._workspaceConfigPath;
9307
- if (isConfig) {
9308
- await Promise.all(this._onRestartListeners.map((fn) => fn("config")));
9309
- this.report("onServerRestart", "config");
9310
- await this.close();
9311
- await serverRestart();
9312
- }
9187
+ const isConfig = file === server.config.configFile || this.projects.some((p) => p.vite.config.configFile === file);
9188
+ if (isConfig) await Promise.all(this._onRestartListeners.map((fn) => fn("config"))), this.report("onServerRestart", "config"), await this.close(), await serverRestart();
9313
9189
  });
9314
9190
  }
9315
9191
  this.cache.results.setConfig(resolved.root, resolved.cache);
@@ -9317,29 +9193,44 @@ class Vitest {
9317
9193
  await this.cache.results.readFromCache();
9318
9194
  } catch {}
9319
9195
  const projects = await this.resolveProjects(this._cliOptions);
9320
- this.resolvedProjects = projects;
9321
- this.projects = projects;
9322
- await Promise.all(projects.flatMap((project) => {
9196
+ if (this.projects = projects, await Promise.all(projects.flatMap((project) => {
9323
9197
  const hooks = project.vite.config.getSortedPluginHooks("configureVitest");
9324
9198
  return hooks.map((hook) => hook({
9325
9199
  project,
9326
9200
  vitest: this,
9327
9201
  injectTestProjects: this.injectTestProject
9328
9202
  }));
9329
- }));
9330
- if (this._cliOptions.browser?.enabled) {
9203
+ })), this._cliOptions.browser?.enabled) {
9331
9204
  const browserProjects = this.projects.filter((p) => p.config.browser.enabled);
9332
9205
  if (!browserProjects.length) throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`);
9333
9206
  }
9334
9207
  if (!this.projects.length) {
9335
9208
  const filter = toArray(resolved.project).join("\", \"");
9336
- if (filter) throw new Error(`No projects matched the filter "${filter}".`);
9337
- else throw new Error(`Vitest wasn't able to resolve any project.`);
9209
+ throw filter ? new Error(`No projects matched the filter "${filter}".`) : new Error(`Vitest wasn't able to resolve any project.`);
9338
9210
  }
9339
9211
  if (!this.coreWorkspaceProject) this.coreWorkspaceProject = TestProject._createBasicProject(this);
9340
9212
  if (this.config.testNamePattern) this.configOverride.testNamePattern = this.config.testNamePattern;
9341
- this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray(resolved.benchmark?.reporters), this.runner) : await createReporters(resolved.reporters, this);
9342
- await Promise.all(this._onSetServer.map((fn) => fn()));
9213
+ this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray(resolved.benchmark?.reporters), this.runner) : await createReporters(resolved.reporters, this), await Promise.all(this._onSetServer.map((fn) => fn()));
9214
+ }
9215
+ /** @internal */
9216
+ get coverageProvider() {
9217
+ return this.configOverride.coverage?.enabled === false ? null : this._coverageProvider;
9218
+ }
9219
+ async enableCoverage() {
9220
+ this.configOverride.coverage = {}, this.configOverride.coverage.enabled = true, await this.createCoverageProvider(), await this.coverageProvider?.onEnabled?.();
9221
+ }
9222
+ disableCoverage() {
9223
+ this.configOverride.coverage ??= {}, this.configOverride.coverage.enabled = false;
9224
+ }
9225
+ _coverageOverrideCache = /* @__PURE__ */ new WeakMap();
9226
+ /** @internal */
9227
+ get _coverageOptions() {
9228
+ if (!this.configOverride.coverage) return this.config.coverage;
9229
+ if (!this._coverageOverrideCache.has(this.configOverride.coverage)) {
9230
+ const coverage = deepClone(this.config.coverage), options = deepMerge(coverage, this.configOverride.coverage);
9231
+ this._coverageOverrideCache.set(this.configOverride.coverage, options);
9232
+ }
9233
+ return this._coverageOverrideCache.get(this.configOverride.coverage);
9343
9234
  }
9344
9235
  /**
9345
9236
  * Inject new test projects into the workspace.
@@ -9347,10 +9238,8 @@ class Vitest {
9347
9238
  * @returns An array of new test projects. Can be empty if the name was filtered out.
9348
9239
  */
9349
9240
  injectTestProject = async (config) => {
9350
- const currentNames = new Set(this.projects.map((p) => p.name));
9351
- const projects = await resolveProjects(this, this._cliOptions, void 0, Array.isArray(config) ? config : [config], currentNames);
9352
- this.projects.push(...projects);
9353
- return projects;
9241
+ const currentNames = new Set(this.projects.map((p) => p.name)), projects = await resolveProjects(this, this._cliOptions, void 0, Array.isArray(config) ? config : [config], currentNames);
9242
+ return this.projects.push(...projects), projects;
9354
9243
  };
9355
9244
  /**
9356
9245
  * Provide a value to the test context. This value will be available to all tests with `inject`.
@@ -9366,13 +9255,7 @@ class Vitest {
9366
9255
  }
9367
9256
  /** @internal */
9368
9257
  _ensureRootProject() {
9369
- if (this.coreWorkspaceProject) return this.coreWorkspaceProject;
9370
- this.coreWorkspaceProject = TestProject._createBasicProject(this);
9371
- return this.coreWorkspaceProject;
9372
- }
9373
- /** @deprecated use `getRootProject` instead */
9374
- getCoreWorkspaceProject() {
9375
- return this.getRootProject();
9258
+ return this.coreWorkspaceProject ||= TestProject._createBasicProject(this), this.coreWorkspaceProject;
9376
9259
  }
9377
9260
  /**
9378
9261
  * Return project that has the root (or "global") config.
@@ -9381,14 +9264,6 @@ class Vitest {
9381
9264
  if (!this.coreWorkspaceProject) throw new Error(`Root project is not initialized. This means that the Vite server was not established yet and the the workspace config is not resolved.`);
9382
9265
  return this.coreWorkspaceProject;
9383
9266
  }
9384
- /**
9385
- * @deprecated use Reported Task API instead
9386
- */
9387
- getProjectByTaskId(taskId) {
9388
- const task = this.state.idMap.get(taskId);
9389
- const projectName = task.projectName || task?.file?.projectName || "";
9390
- return this.getProjectByName(projectName);
9391
- }
9392
9267
  getProjectByName(name) {
9393
9268
  const project = this.projects.find((p) => p.name === name) || this.coreWorkspaceProject || this.projects[0];
9394
9269
  if (!project) throw new Error(`Project "${name}" was not found.`);
@@ -9399,43 +9274,25 @@ class Vitest {
9399
9274
  * @param moduleId The ID of the module in Vite module graph
9400
9275
  */
9401
9276
  import(moduleId) {
9402
- return this.runner.executeId(moduleId);
9403
- }
9404
- async resolveWorkspaceConfigPath() {
9405
- if (typeof this.config.workspace === "string") return this.config.workspace;
9406
- const configDir = this.vite.config.configFile ? dirname(this.vite.config.configFile) : this.config.root;
9407
- const rootFiles = await promises.readdir(configDir);
9408
- const workspaceConfigName = workspacesFiles.find((configFile) => {
9409
- return rootFiles.includes(configFile);
9410
- });
9411
- if (!workspaceConfigName) return void 0;
9412
- return join(configDir, workspaceConfigName);
9277
+ return this.runner.import(moduleId);
9278
+ }
9279
+ /**
9280
+ * Creates a coverage provider if `coverage` is enabled in the config.
9281
+ */
9282
+ async createCoverageProvider() {
9283
+ if (this._coverageProvider) return this._coverageProvider;
9284
+ const coverageProvider = await this.initCoverageProvider();
9285
+ if (coverageProvider) await coverageProvider.clean(this._coverageOptions.clean);
9286
+ return coverageProvider || null;
9413
9287
  }
9414
9288
  async resolveProjects(cliOptions) {
9415
9289
  const names = /* @__PURE__ */ new Set();
9416
- if (this.config.projects) {
9417
- if (typeof this.config.workspace !== "undefined") this.logger.warn("Both `test.projects` and `test.workspace` are defined. Ignoring the `test.workspace` option.");
9418
- return resolveProjects(this, cliOptions, void 0, this.config.projects, names);
9419
- }
9420
- if (Array.isArray(this.config.workspace)) {
9421
- this.logger.deprecate("The `test.workspace` option is deprecated and will be removed in the next major. To hide this warning, rename `test.workspace` option to `test.projects`.");
9422
- return resolveProjects(this, cliOptions, void 0, this.config.workspace, names);
9423
- }
9424
- const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
9425
- this._workspaceConfigPath = workspaceConfigPath;
9426
- // user doesn't have a workspace config, return default project
9427
- if (!workspaceConfigPath) {
9428
- // user can filter projects with --project flag, `getDefaultTestProject`
9429
- // returns the project only if it matches the filter
9430
- const project = getDefaultTestProject(this);
9431
- if (!project) return [];
9432
- return resolveBrowserProjects(this, new Set([project.name]), [project]);
9433
- }
9434
- const configFile = this.vite.config.configFile ? resolve(this.vite.config.root, this.vite.config.configFile) : "the root config file";
9435
- this.logger.deprecate(`The workspace file is deprecated and will be removed in the next major. Please, use the \`test.projects\` field in ${configFile} instead.`);
9436
- const workspaceModule = await this.import(workspaceConfigPath);
9437
- if (!workspaceModule.default || !Array.isArray(workspaceModule.default)) throw new TypeError(`Workspace config file "${workspaceConfigPath}" must export a default array of project paths.`);
9438
- return resolveProjects(this, cliOptions, workspaceConfigPath, workspaceModule.default, names);
9290
+ if (this.config.projects) return resolveProjects(this, cliOptions, void 0, this.config.projects, names);
9291
+ if ("workspace" in this.config) throw new Error("The `test.workspace` option was removed in Vitest 4. Please, migrate to `test.projects` instead. See https://vitest.dev/guide/projects for examples.");
9292
+ // user can filter projects with --project flag, `getDefaultTestProject`
9293
+ // returns the project only if it matches the filter
9294
+ const project = getDefaultTestProject(this);
9295
+ return project ? resolveBrowserProjects(this, new Set([project.name]), [project]) : [];
9439
9296
  }
9440
9297
  /**
9441
9298
  * Glob test files in every project and create a TestSpecification for each file and pool.
@@ -9445,13 +9302,10 @@ class Vitest {
9445
9302
  return this.specifications.globTestSpecifications(filters);
9446
9303
  }
9447
9304
  async initCoverageProvider() {
9448
- if (this.coverageProvider !== void 0) return;
9449
- this.coverageProvider = await getCoverageProvider(this.config.coverage, this.runner);
9450
- if (this.coverageProvider) {
9451
- await this.coverageProvider.initialize(this);
9452
- this.config.coverage = this.coverageProvider.resolveOptions();
9453
- }
9454
- return this.coverageProvider;
9305
+ if (this._coverageProvider != null) return;
9306
+ const coverageConfig = this.configOverride.coverage ? this.getRootProject().serializedConfig.coverage : this.config.coverage;
9307
+ if (this._coverageProvider = await getCoverageProvider(coverageConfig, this.runner), this._coverageProvider) await this._coverageProvider.initialize(this), this.config.coverage = this._coverageProvider.resolveOptions();
9308
+ return this._coverageProvider;
9455
9309
  }
9456
9310
  /**
9457
9311
  * Merge reports from multiple runs located in the specified directory (value from `--merge-reports` if not specified).
@@ -9464,23 +9318,15 @@ class Vitest {
9464
9318
  errors,
9465
9319
  coverages,
9466
9320
  executionTimes
9467
- };
9468
- await this.report("onInit", this);
9469
- await this.report("onPathsCollected", files.flatMap((f) => f.filepath));
9321
+ }, await this.report("onInit", this);
9470
9322
  const specifications = [];
9471
9323
  for (const file of files) {
9472
- const project = this.getProjectByName(file.projectName || "");
9473
- const specification = project.createSpecification(file.filepath, void 0, file.pool);
9324
+ const project = this.getProjectByName(file.projectName || ""), specification = project.createSpecification(file.filepath, void 0, file.pool);
9474
9325
  specifications.push(specification);
9475
9326
  }
9476
- await this.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
9477
9327
  await this._testRun.start(specifications).catch(noop);
9478
9328
  for (const file of files) await this._reportFileTask(file);
9479
- this._checkUnhandledErrors(errors);
9480
- await this._testRun.end(specifications, errors).catch(noop);
9481
- await this.initCoverageProvider();
9482
- await this.coverageProvider?.mergeReports?.(coverages);
9483
- return {
9329
+ return this._checkUnhandledErrors(errors), await this._testRun.end(specifications, errors).catch(noop), await this.initCoverageProvider(), await this.coverageProvider?.mergeReports?.(coverages), {
9484
9330
  testModules: this.state.getTestModules(),
9485
9331
  unhandledErrors: this.state.getUnhandledErrors()
9486
9332
  };
@@ -9488,10 +9334,8 @@ class Vitest {
9488
9334
  /** @internal */
9489
9335
  async _reportFileTask(file) {
9490
9336
  const project = this.getProjectByName(file.projectName || "");
9491
- await this._testRun.enqueued(project, file).catch(noop);
9492
- await this._testRun.collected(project, [file]).catch(noop);
9493
- const logs = [];
9494
- const { packs, events } = convertTasksToEvents(file, (task) => {
9337
+ await this._testRun.enqueued(project, file).catch(noop), await this._testRun.collected(project, [file]).catch(noop);
9338
+ const logs = [], { packs, events } = convertTasksToEvents(file, (task) => {
9495
9339
  if (task.logs) logs.push(...task.logs);
9496
9340
  });
9497
9341
  logs.sort((log1, log2) => log1.time - log2.time);
@@ -9500,16 +9344,10 @@ class Vitest {
9500
9344
  }
9501
9345
  async collect(filters) {
9502
9346
  const files = await this.specifications.getRelevantTestSpecifications(filters);
9503
- // if run with --changed, don't exit if no tests are found
9504
- if (!files.length) return {
9347
+ return files.length ? this.collectTests(files) : {
9505
9348
  testModules: [],
9506
9349
  unhandledErrors: []
9507
9350
  };
9508
- return this.collectTests(files);
9509
- }
9510
- /** @deprecated use `getRelevantTestSpecifications` instead */
9511
- listFiles(filters) {
9512
- return this.getRelevantTestSpecifications(filters);
9513
9351
  }
9514
9352
  /**
9515
9353
  * Returns the list of test files that match the config and filters.
@@ -9528,8 +9366,7 @@ class Vitest {
9528
9366
  */
9529
9367
  async start(filters) {
9530
9368
  try {
9531
- await this.initCoverageProvider();
9532
- await this.coverageProvider?.clean(this.config.coverage.clean);
9369
+ await this.initCoverageProvider(), await this.coverageProvider?.clean(this._coverageOptions.clean);
9533
9370
  } finally {
9534
9371
  await this.report("onInit", this);
9535
9372
  }
@@ -9539,20 +9376,13 @@ class Vitest {
9539
9376
  if (!files.length) {
9540
9377
  await this._testRun.start([]);
9541
9378
  const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true });
9542
- await this._testRun.end([], [], coverage);
9543
- // Report coverage for uncovered files
9544
- await this.reportCoverage(coverage, true);
9545
- if (!this.config.watch || !(this.config.changed || this.config.related?.length)) throw new FilesNotFoundError(this.mode);
9379
+ if (await this._testRun.end([], [], coverage), await this.reportCoverage(coverage, true), !this.config.watch || !(this.config.changed || this.config.related?.length)) throw new FilesNotFoundError(this.mode);
9546
9380
  }
9547
9381
  let testModules = {
9548
9382
  testModules: [],
9549
9383
  unhandledErrors: []
9550
9384
  };
9551
- if (files.length) {
9552
- // populate once, update cache on watch
9553
- await this.cache.stats.populateStats(this.config.root, files);
9554
- testModules = await this.runFiles(files, true);
9555
- }
9385
+ if (files.length) await this.cache.stats.populateStats(this.config.root, files), testModules = await this.runFiles(files, true);
9556
9386
  if (this.config.watch) await this.report("onWatcherStart");
9557
9387
  return testModules;
9558
9388
  }
@@ -9562,24 +9392,18 @@ class Vitest {
9562
9392
  */
9563
9393
  async init() {
9564
9394
  try {
9565
- await this.initCoverageProvider();
9566
- await this.coverageProvider?.clean(this.config.coverage.clean);
9395
+ await this.initCoverageProvider(), await this.coverageProvider?.clean(this._coverageOptions.clean);
9567
9396
  } finally {
9568
9397
  await this.report("onInit", this);
9569
9398
  }
9570
- // populate test files cache so watch mode can trigger a file rerun
9571
- await this.globTestSpecifications();
9572
- if (this.config.watch) await this.report("onWatcherStart");
9399
+ if (await this.globTestSpecifications(), this.config.watch) await this.report("onWatcherStart");
9573
9400
  }
9574
9401
  /**
9575
- * @deprecated remove when vscode extension supports "getModuleSpecifications"
9402
+ * If there is a test run happening, returns a promise that will
9403
+ * resolve when the test run is finished.
9576
9404
  */
9577
- getProjectsByTestFile(file) {
9578
- return this.getModuleSpecifications(file);
9579
- }
9580
- /** @deprecated */
9581
- getFileWorkspaceSpecs(file) {
9582
- return this.getModuleSpecifications(file);
9405
+ async waitForTestRunEnd() {
9406
+ this.runningPromise && await this.runningPromise;
9583
9407
  }
9584
9408
  /**
9585
9409
  * Get test specifications associated with the given module. If module is not a test file, an empty array is returned.
@@ -9594,7 +9418,9 @@ class Vitest {
9594
9418
  * Vitest automatically caches test specifications for each file. This method clears the cache for the given file or the whole cache altogether.
9595
9419
  */
9596
9420
  clearSpecificationsCache(moduleId) {
9597
- this.specifications.clearCache(moduleId);
9421
+ if (this.specifications.clearCache(moduleId), !moduleId) this.projects.forEach((project) => {
9422
+ project.testFilesList = null;
9423
+ });
9598
9424
  }
9599
9425
  /**
9600
9426
  * Run tests for the given test specifications. This does not trigger `onWatcher*` events.
@@ -9602,8 +9428,7 @@ class Vitest {
9602
9428
  * @param allTestsRun Indicates whether all tests were run. This only matters for coverage.
9603
9429
  */
9604
9430
  runTestSpecifications(specifications, allTestsRun = false) {
9605
- specifications.forEach((spec) => this.specifications.ensureSpecificationCached(spec));
9606
- return this.runFiles(specifications, allTestsRun);
9431
+ return specifications.forEach((spec) => this.specifications.ensureSpecificationCached(spec)), this.runFiles(specifications, allTestsRun);
9607
9432
  }
9608
9433
  /**
9609
9434
  * Rerun files and trigger `onWatcherRerun`, `onWatcherStart` and `onTestsRerun` events.
@@ -9611,28 +9436,17 @@ class Vitest {
9611
9436
  * @param allTestsRun Indicates whether all tests were run. This only matters for coverage.
9612
9437
  */
9613
9438
  async rerunTestSpecifications(specifications, allTestsRun = false) {
9614
- this.configOverride.testNamePattern = void 0;
9615
9439
  const files = specifications.map((spec) => spec.moduleId);
9616
9440
  await Promise.all([this.report("onWatcherRerun", files, "rerun test"), ...this._onUserTestsRerun.map((fn) => fn(specifications))]);
9617
9441
  const result = await this.runTestSpecifications(specifications, allTestsRun);
9618
- await this.report("onWatcherStart", this.state.getFiles(files));
9619
- return result;
9442
+ return await this.report("onWatcherStart", this.state.getFiles(files)), result;
9620
9443
  }
9621
9444
  async runFiles(specs, allTestsRun) {
9622
- await this._testRun.start(specs);
9623
- // previous run
9624
- await this.runningPromise;
9625
- this._onCancelListeners = [];
9626
- this.isCancelling = false;
9627
- // schedule the new run
9628
- this.runningPromise = (async () => {
9445
+ return await this._testRun.start(specs), await this.runningPromise, this._onCancelListeners = [], this.isCancelling = false, this.runningPromise = (async () => {
9629
9446
  try {
9630
9447
  if (!this.pool) this.pool = createPool(this);
9631
9448
  const invalidates = Array.from(this.watcher.invalidates);
9632
- this.watcher.invalidates.clear();
9633
- this.snapshot.clear();
9634
- this.state.clearErrors();
9635
- if (!this.isFirstRun && this.config.coverage.cleanOnRerun) await this.coverageProvider?.clean();
9449
+ if (this.watcher.invalidates.clear(), this.snapshot.clear(), this.state.clearErrors(), !this.isFirstRun && this._coverageOptions.cleanOnRerun) await this.coverageProvider?.clean();
9636
9450
  await this.initializeGlobalSetup(specs);
9637
9451
  try {
9638
9452
  await this.pool.runTests(specs, invalidates);
@@ -9649,21 +9463,24 @@ class Vitest {
9649
9463
  unhandledErrors: this.state.getUnhandledErrors()
9650
9464
  };
9651
9465
  } finally {
9652
- // TODO: wait for coverage only if `onFinished` is defined
9653
- const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun });
9654
- const errors = this.state.getUnhandledErrors();
9655
- this._checkUnhandledErrors(errors);
9656
- await this._testRun.end(specs, errors, coverage);
9657
- await this.reportCoverage(coverage, allTestsRun);
9466
+ const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun }), errors = this.state.getUnhandledErrors();
9467
+ this._checkUnhandledErrors(errors), await this._testRun.end(specs, errors, coverage), await this.reportCoverage(coverage, allTestsRun);
9658
9468
  }
9659
9469
  })().finally(() => {
9660
- this.runningPromise = void 0;
9661
- this.isFirstRun = false;
9662
- // all subsequent runs will treat this as a fresh run
9663
- this.config.changed = false;
9664
- this.config.related = void 0;
9470
+ this.runningPromise = void 0, this.isFirstRun = false, this.config.changed = false, this.config.related = void 0;
9471
+ }), await this.runningPromise;
9472
+ }
9473
+ async experimental_parseSpecifications(specifications, options) {
9474
+ if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecifications\` does not support "${this.mode}" mode.`);
9475
+ const concurrency = options?.concurrency ?? (typeof nodeos__default.availableParallelism === "function" ? nodeos__default.availableParallelism() : nodeos__default.cpus().length), limit = limitConcurrency(concurrency), promises = specifications.map((specification) => limit(() => this.experimental_parseSpecification(specification)));
9476
+ return Promise.all(promises);
9477
+ }
9478
+ async experimental_parseSpecification(specification) {
9479
+ if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecification\` does not support "${this.mode}" mode.`);
9480
+ const file = await astCollectTests(specification.project, specification.moduleId).catch((error) => {
9481
+ return createFailedFileTask(specification.project, specification.moduleId, error);
9665
9482
  });
9666
- return await this.runningPromise;
9483
+ return this.state.collectFiles(specification.project, [file]), this.state.getReportedEntity(file);
9667
9484
  }
9668
9485
  /**
9669
9486
  * Collect tests in specified modules. Vitest will run the files to collect tests.
@@ -9671,19 +9488,10 @@ class Vitest {
9671
9488
  */
9672
9489
  async collectTests(specifications) {
9673
9490
  const filepaths = specifications.map((spec) => spec.moduleId);
9674
- this.state.collectPaths(filepaths);
9675
- // previous run
9676
- await this.runningPromise;
9677
- this._onCancelListeners = [];
9678
- this.isCancelling = false;
9679
- // schedule the new run
9680
- this.runningPromise = (async () => {
9491
+ return this.state.collectPaths(filepaths), await this.runningPromise, this._onCancelListeners = [], this.isCancelling = false, this.runningPromise = (async () => {
9681
9492
  if (!this.pool) this.pool = createPool(this);
9682
9493
  const invalidates = Array.from(this.watcher.invalidates);
9683
- this.watcher.invalidates.clear();
9684
- this.snapshot.clear();
9685
- this.state.clearErrors();
9686
- await this.initializeGlobalSetup(specifications);
9494
+ this.watcher.invalidates.clear(), this.snapshot.clear(), this.state.clearErrors(), await this.initializeGlobalSetup(specifications);
9687
9495
  try {
9688
9496
  await this.pool.collectTests(specifications, invalidates);
9689
9497
  } catch (err) {
@@ -9698,28 +9506,21 @@ class Vitest {
9698
9506
  unhandledErrors: this.state.getUnhandledErrors()
9699
9507
  };
9700
9508
  })().finally(() => {
9701
- this.runningPromise = void 0;
9702
- // all subsequent runs will treat this as a fresh run
9703
- this.config.changed = false;
9704
- this.config.related = void 0;
9705
- });
9706
- return await this.runningPromise;
9509
+ this.runningPromise = void 0, this.config.changed = false, this.config.related = void 0;
9510
+ }), await this.runningPromise;
9707
9511
  }
9708
9512
  /**
9709
9513
  * Gracefully cancel the current test run. Vitest will wait until all running tests are finished before cancelling.
9710
9514
  */
9711
9515
  async cancelCurrentRun(reason) {
9712
- this.isCancelling = true;
9713
- await Promise.all(this._onCancelListeners.splice(0).map((listener) => listener(reason)));
9714
- await this.runningPromise;
9516
+ this.isCancelling = true, await Promise.all(this._onCancelListeners.splice(0).map((listener) => listener(reason))), await this.runningPromise;
9715
9517
  }
9716
9518
  /** @internal */
9717
9519
  async _initBrowserServers() {
9718
9520
  await Promise.all(this.projects.map((p) => p._initBrowserServer()));
9719
9521
  }
9720
9522
  async initializeGlobalSetup(paths) {
9721
- const projects = new Set(paths.map((spec) => spec.project));
9722
- const coreProject = this.getRootProject();
9523
+ const projects = new Set(paths.map((spec) => spec.project)), coreProject = this.getRootProject();
9723
9524
  if (!projects.has(coreProject)) projects.add(coreProject);
9724
9525
  for (const project of projects) await project._initializeGlobalSetup();
9725
9526
  }
@@ -9733,8 +9534,7 @@ class Vitest {
9733
9534
  const specifications = files.flatMap((file) => this.getModuleSpecifications(file));
9734
9535
  await Promise.all([this.report("onWatcherRerun", files, trigger), ...this._onUserTestsRerun.map((fn) => fn(specifications))]);
9735
9536
  const testResult = await this.runFiles(specifications, allTestsRun);
9736
- await this.report("onWatcherStart", this.state.getFiles(files));
9737
- return testResult;
9537
+ return await this.report("onWatcherStart", this.state.getFiles(files)), testResult;
9738
9538
  }
9739
9539
  /** @internal */
9740
9540
  async rerunTask(id) {
@@ -9754,9 +9554,8 @@ class Vitest {
9754
9554
  // Empty test name pattern should reset filename pattern as well
9755
9555
  if (pattern === "") this.filenamePattern = void 0;
9756
9556
  const testNamePattern = pattern ? new RegExp(pattern) : void 0;
9757
- this.configOverride.testNamePattern = testNamePattern;
9758
9557
  // filter only test files that have tests matching the pattern
9759
- if (testNamePattern) files = files.filter((filepath) => {
9558
+ if (this.configOverride.testNamePattern = testNamePattern, testNamePattern) files = files.filter((filepath) => {
9760
9559
  const files = this.state.getFiles([filepath]);
9761
9560
  return !files.length || files.some((file) => {
9762
9561
  const tasks = getTasks(file);
@@ -9780,9 +9579,7 @@ class Vitest {
9780
9579
  * @param files The list of files on the file system
9781
9580
  */
9782
9581
  async updateSnapshot(files) {
9783
- // default to failed files
9784
- files = files || [...this.state.getFailedFilepaths(), ...this.snapshot.summary.uncheckedKeysByFile.map((s) => s.filePath)];
9785
- this.enableSnapshotUpdate();
9582
+ files = files || [...this.state.getFailedFilepaths(), ...this.snapshot.summary.uncheckedKeysByFile.map((s) => s.filePath)], this.enableSnapshotUpdate();
9786
9583
  try {
9787
9584
  return await this.rerunFiles(files, "update snapshot", false);
9788
9585
  } finally {
@@ -9800,15 +9597,13 @@ class Vitest {
9800
9597
  this.configOverride.snapshotOptions = {
9801
9598
  updateSnapshot: "all",
9802
9599
  snapshotEnvironment: null
9803
- };
9804
- this.snapshot.options.updateSnapshot = "all";
9600
+ }, this.snapshot.options.updateSnapshot = "all";
9805
9601
  }
9806
9602
  /**
9807
9603
  * Disable the mode that allows updating snapshots when running tests.
9808
9604
  */
9809
9605
  resetSnapshotUpdate() {
9810
- delete this.configOverride.snapshotOptions;
9811
- this.snapshot.options.updateSnapshot = this.config.snapshotOptions.updateSnapshot;
9606
+ delete this.configOverride.snapshotOptions, this.snapshot.options.updateSnapshot = this.config.snapshotOptions.updateSnapshot;
9812
9607
  }
9813
9608
  /**
9814
9609
  * Set the global test name pattern to a regexp.
@@ -9819,74 +9614,60 @@ class Vitest {
9819
9614
  else this.configOverride.testNamePattern = pattern ? new RegExp(pattern) : void 0;
9820
9615
  }
9821
9616
  /**
9617
+ * Returns the regexp used for the global test name pattern.
9618
+ */
9619
+ getGlobalTestNamePattern() {
9620
+ return this.configOverride.testNamePattern == null ? this.config.testNamePattern : this.configOverride.testNamePattern;
9621
+ }
9622
+ /**
9822
9623
  * Resets the global test name pattern. This method doesn't run any tests.
9823
9624
  */
9824
9625
  resetGlobalTestNamePattern() {
9825
9626
  this.configOverride.testNamePattern = void 0;
9826
9627
  }
9827
9628
  _rerunTimer;
9828
- // we can't use a single `triggerId` yet because vscode extension relies on this
9829
9629
  async scheduleRerun(triggerId) {
9830
9630
  const currentCount = this.restartsCount;
9831
- clearTimeout(this._rerunTimer);
9832
- await this.runningPromise;
9833
- clearTimeout(this._rerunTimer);
9834
- // server restarted
9835
- if (this.restartsCount !== currentCount) return;
9836
- this._rerunTimer = setTimeout(async () => {
9631
+ clearTimeout(this._rerunTimer), await this.runningPromise, clearTimeout(this._rerunTimer), this.restartsCount === currentCount && (this._rerunTimer = setTimeout(async () => {
9837
9632
  if (this.watcher.changedTests.size === 0) {
9838
9633
  this.watcher.invalidates.clear();
9839
9634
  return;
9840
9635
  }
9841
9636
  // server restarted
9842
9637
  if (this.restartsCount !== currentCount) return;
9843
- this.isFirstRun = false;
9844
- this.snapshot.clear();
9638
+ this.isFirstRun = false, this.snapshot.clear();
9845
9639
  let files = Array.from(this.watcher.changedTests);
9846
9640
  if (this.filenamePattern) {
9847
9641
  const filteredFiles = await this.globTestSpecifications(this.filenamePattern);
9848
- files = files.filter((file) => filteredFiles.some((f) => f.moduleId === file));
9849
9642
  // A file that does not match the current filename pattern was changed
9850
- if (files.length === 0) return;
9643
+ if (files = files.filter((file) => filteredFiles.some((f) => f.moduleId === file)), files.length === 0) return;
9851
9644
  }
9852
9645
  this.watcher.changedTests.clear();
9853
- const triggerIds = new Set(triggerId.map((id) => relative(this.config.root, id)));
9854
- const triggerLabel = Array.from(triggerIds).join(", ");
9855
- // get file specifications and filter them if needed
9856
- const specifications = files.flatMap((file) => this.getModuleSpecifications(file)).filter((specification) => {
9857
- if (this._onFilterWatchedSpecification.length === 0) return true;
9858
- return this._onFilterWatchedSpecification.every((fn) => fn(specification));
9646
+ const triggerLabel = relative(this.config.root, triggerId), specifications = files.flatMap((file) => this.getModuleSpecifications(file)).filter((specification) => {
9647
+ return this._onFilterWatchedSpecification.length === 0 ? true : this._onFilterWatchedSpecification.every((fn) => fn(specification));
9859
9648
  });
9860
- await Promise.all([this.report("onWatcherRerun", files, triggerLabel), ...this._onUserTestsRerun.map((fn) => fn(specifications))]);
9861
- await this.runFiles(specifications, false);
9862
- await this.report("onWatcherStart", this.state.getFiles(files));
9863
- }, WATCHER_DEBOUNCE);
9649
+ await Promise.all([this.report("onWatcherRerun", files, triggerLabel), ...this._onUserTestsRerun.map((fn) => fn(specifications))]), await this.runFiles(specifications, false), await this.report("onWatcherStart", this.state.getFiles(files));
9650
+ }, WATCHER_DEBOUNCE));
9864
9651
  }
9865
9652
  /**
9866
9653
  * Invalidate a file in all projects.
9867
9654
  */
9868
9655
  invalidateFile(filepath) {
9869
9656
  this.projects.forEach(({ vite, browser }) => {
9870
- const serverMods = vite.moduleGraph.getModulesByFile(filepath);
9871
- serverMods?.forEach((mod) => vite.moduleGraph.invalidateModule(mod));
9872
- if (browser) {
9873
- const browserMods = browser.vite.moduleGraph.getModulesByFile(filepath);
9874
- browserMods?.forEach((mod) => browser.vite.moduleGraph.invalidateModule(mod));
9875
- }
9657
+ const environments = [...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})];
9658
+ environments.forEach(({ moduleGraph }) => {
9659
+ const modules = moduleGraph.getModulesByFile(filepath);
9660
+ modules?.forEach((module) => moduleGraph.invalidateModule(module));
9661
+ });
9876
9662
  });
9877
9663
  }
9878
- /** @deprecated use `invalidateFile` */
9879
- updateLastChanged(filepath) {
9880
- this.invalidateFile(filepath);
9881
- }
9882
9664
  /** @internal */
9883
9665
  _checkUnhandledErrors(errors) {
9884
9666
  if (errors.length && !this.config.dangerouslyIgnoreUnhandledErrors) process.exitCode = 1;
9885
9667
  }
9886
9668
  async reportCoverage(coverage, allTestsRun) {
9887
9669
  if (this.state.getCountOfFailedTests() > 0) {
9888
- await this.coverageProvider?.onTestFailure?.();
9889
- if (!this.config.coverage.reportOnFailure) return;
9670
+ if (await this.coverageProvider?.onTestFailure?.(), !this._coverageOptions.reportOnFailure) return;
9890
9671
  }
9891
9672
  if (this.coverageProvider) {
9892
9673
  await this.coverageProvider.reportCoverage(coverage, { allTestsRun });
@@ -9909,11 +9690,9 @@ class Vitest {
9909
9690
  // it's possible that it's not initialized at all because it's not running any tests
9910
9691
  if (this.coreWorkspaceProject && !this.projects.includes(this.coreWorkspaceProject)) closePromises.push(this.coreWorkspaceProject.close().then(() => this._vite = void 0));
9911
9692
  if (this.pool) closePromises.push((async () => {
9912
- await this.pool?.close?.();
9913
- this.pool = void 0;
9693
+ await this.pool?.close?.(), this.pool = void 0;
9914
9694
  })());
9915
- closePromises.push(...this._onClose.map((fn) => fn()));
9916
- return Promise.allSettled(closePromises).then((results) => {
9695
+ return closePromises.push(...this._onClose.map((fn) => fn())), Promise.allSettled(closePromises).then((results) => {
9917
9696
  results.forEach((r) => {
9918
9697
  if (r.status === "rejected") this.logger.error("error during close", r.reason);
9919
9698
  });
@@ -9926,11 +9705,9 @@ class Vitest {
9926
9705
  * @param force If true, the process will exit immediately after closing the projects.
9927
9706
  */
9928
9707
  async exit(force = false) {
9929
- setTimeout(() => {
9708
+ if (setTimeout(() => {
9930
9709
  this.report("onProcessTimeout").then(() => {
9931
- console.warn(`close timed out after ${this.config.teardownTimeout}ms`);
9932
- this.state.getProcessTimeoutCauses().forEach((cause) => console.warn(cause));
9933
- if (!this.pool) {
9710
+ if (console.warn(`close timed out after ${this.config.teardownTimeout}ms`), this.state.getProcessTimeoutCauses().forEach((cause) => console.warn(cause)), !this.pool) {
9934
9711
  const runningServers = [this._vite, ...this.projects.map((p) => p._vite)].filter(Boolean).length;
9935
9712
  if (runningServers === 1) console.warn("Tests closed successfully but something prevents Vite server from exiting");
9936
9713
  else if (runningServers > 1) console.warn(`Tests closed successfully but something prevents ${runningServers} Vite servers from exiting`);
@@ -9939,9 +9716,7 @@ class Vitest {
9939
9716
  }
9940
9717
  process.exit();
9941
9718
  });
9942
- }, this.config.teardownTimeout).unref();
9943
- await this.close();
9944
- if (force) process.exit();
9719
+ }, this.config.teardownTimeout).unref(), await this.close(), force) process.exit();
9945
9720
  }
9946
9721
  /** @internal */
9947
9722
  async report(name, ...args) {
@@ -9956,25 +9731,6 @@ class Vitest {
9956
9731
  return Array.from(new Set(specifications.map((spec) => spec.moduleId)));
9957
9732
  }
9958
9733
  /**
9959
- * @deprecated use `globTestSpecifications` instead
9960
- */
9961
- async globTestSpecs(filters = []) {
9962
- return this.globTestSpecifications(filters);
9963
- }
9964
- /**
9965
- * @deprecated use `globTestSpecifications` instead
9966
- */
9967
- async globTestFiles(filters = []) {
9968
- return this.globTestSpecifications(filters);
9969
- }
9970
- /** @deprecated filter by `this.projects` yourself */
9971
- getModuleProjects(filepath) {
9972
- return this.projects.filter((project) => {
9973
- return project.getModulesByFilepath(filepath).size;
9974
- // TODO: reevaluate || project.browser?.moduleGraph.getModulesByFile(id)?.size
9975
- });
9976
- }
9977
- /**
9978
9734
  * Should the server be kept running after the tests are done.
9979
9735
  */
9980
9736
  shouldKeepServer() {
@@ -10023,9 +9779,7 @@ class Vitest {
10023
9779
  */
10024
9780
  matchesProjectFilter(name) {
10025
9781
  const projects = this._config?.project || this._cliOptions?.project;
10026
- // no filters applied, any project can be included
10027
- if (!projects || !projects.length) return true;
10028
- return toArray(projects).some((project) => {
9782
+ return !projects || !projects.length ? true : toArray(projects).some((project) => {
10029
9783
  const regexp = wildcardPatternToRegExp(project);
10030
9784
  return regexp.test(name);
10031
9785
  });
@@ -10038,8 +9792,7 @@ function assert(condition, property, name = property) {
10038
9792
  async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(options))) {
10039
9793
  const userConfig = deepMerge({}, options);
10040
9794
  async function UIPlugin() {
10041
- await vitest.packageInstaller.ensureInstalled("@vitest/ui", options.root || process.cwd(), vitest.version);
10042
- return (await import('@vitest/ui')).default(vitest);
9795
+ return await vitest.packageInstaller.ensureInstalled("@vitest/ui", options.root || process.cwd(), vitest.version), (await import('@vitest/ui')).default(vitest);
10043
9796
  }
10044
9797
  return [
10045
9798
  {
@@ -10065,14 +9818,9 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
10065
9818
  let open = false;
10066
9819
  if (testConfig.ui && testConfig.open) open = testConfig.uiBase ?? "/__vitest__/";
10067
9820
  const resolveOptions = getDefaultResolveOptions();
10068
- const config = {
9821
+ let config = {
10069
9822
  root: viteConfig.test?.root || options.root,
10070
9823
  define: { "process.env.NODE_ENV": "process.env.NODE_ENV" },
10071
- esbuild: viteConfig.esbuild === false ? false : {
10072
- target: viteConfig.esbuild?.target || "node18",
10073
- sourcemap: "external",
10074
- legalComments: "inline"
10075
- },
10076
9824
  resolve: {
10077
9825
  ...resolveOptions,
10078
9826
  alias: testConfig.alias
@@ -10089,7 +9837,10 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
10089
9837
  outDir: "dummy-non-existing-folder",
10090
9838
  emptyOutDir: false
10091
9839
  },
10092
- environments: { ssr: { resolve: resolveOptions } },
9840
+ environments: {
9841
+ ssr: { resolve: resolveOptions },
9842
+ __vitest__: { dev: {} }
9843
+ },
10093
9844
  test: {
10094
9845
  poolOptions: {
10095
9846
  threads: { isolate: options.poolOptions?.threads?.isolate ?? options.isolate ?? testConfig.poolOptions?.threads?.isolate ?? viteConfig.test?.isolate },
@@ -10099,33 +9850,29 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
10099
9850
  deps: testConfig.deps ?? viteConfig.test?.deps
10100
9851
  }
10101
9852
  };
9853
+ if ("rolldownVersion" in vite) config = {
9854
+ ...config,
9855
+ oxc: viteConfig.oxc === false ? false : { target: viteConfig.oxc?.target || "node18" }
9856
+ };
9857
+ else config = {
9858
+ ...config,
9859
+ esbuild: viteConfig.esbuild === false ? false : {
9860
+ target: viteConfig.esbuild?.target || "node18",
9861
+ sourcemap: "external",
9862
+ legalComments: "inline"
9863
+ }
9864
+ };
10102
9865
  // inherit so it's available in VitestOptimizer
10103
9866
  // I cannot wait to rewrite all of this in Vitest 4
10104
9867
  if (options.cache != null) config.test.cache = options.cache;
10105
9868
  if (vitest.configOverride.project)
10106
9869
  // project filter was set by the user, so we need to filter the project
10107
9870
  options.project = vitest.configOverride.project;
10108
- config.customLogger = createViteLogger(vitest.logger, viteConfig.logLevel || "warn", { allowClearScreen: false });
10109
- config.customLogger = silenceImportViteIgnoreWarning(config.customLogger);
10110
- // we want inline dependencies to be resolved by analyser plugin so module graph is populated correctly
10111
- if (viteConfig.ssr?.noExternal !== true) {
10112
- const inline = testConfig.server?.deps?.inline;
10113
- if (inline === true) config.ssr = { noExternal: true };
10114
- else {
10115
- const noExternal = viteConfig.ssr?.noExternal;
10116
- const noExternalArray = typeof noExternal !== "undefined" ? toArray(noExternal) : void 0;
10117
- // filter the same packages
10118
- const uniqueInline = inline && noExternalArray ? inline.filter((dep) => !noExternalArray.includes(dep)) : inline;
10119
- config.ssr = { noExternal: uniqueInline };
10120
- }
10121
- }
10122
9871
  // chokidar fsevents is unstable on macos when emitting "ready" event
10123
- if (process.platform === "darwin" && false);
9872
+ if (config.customLogger = createViteLogger(vitest.logger, viteConfig.logLevel || "warn", { allowClearScreen: false }), config.customLogger = silenceImportViteIgnoreWarning(config.customLogger), process.platform === "darwin" && false);
10124
9873
  const classNameStrategy = typeof testConfig.css !== "boolean" && testConfig.css?.modules?.classNameStrategy || "stable";
10125
9874
  if (classNameStrategy !== "scoped") {
10126
- config.css ??= {};
10127
- config.css.modules ??= {};
10128
- if (config.css.modules) config.css.modules.generateScopedName = (name, filename) => {
9875
+ if (config.css ??= {}, config.css.modules ??= {}, config.css.modules) config.css.modules.generateScopedName = (name, filename) => {
10129
9876
  const root = vitest.config.root || options.root || process.cwd();
10130
9877
  return generateScopedClassName(classNameStrategy, name, relative(root, filename));
10131
9878
  };
@@ -10136,17 +9883,11 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
10136
9883
  const viteConfigTest = viteConfig.test || {};
10137
9884
  if (viteConfigTest.watch === false) viteConfigTest.run = true;
10138
9885
  if ("alias" in viteConfigTest) delete viteConfigTest.alias;
10139
- // viteConfig.test is final now, merge it for real
10140
- options = deepMerge({}, configDefaults, viteConfigTest, options);
10141
- options.api = resolveApiServerConfig(options, defaultPort);
9886
+ options = deepMerge({}, configDefaults, viteConfigTest, options), options.api = resolveApiServerConfig(options, defaultPort);
10142
9887
  // we replace every "import.meta.env" with "process.env"
10143
9888
  // to allow reassigning, so we need to put all envs on process.env
10144
9889
  const { PROD, DEV,...envs } = viteConfig.env;
10145
- // process.env can have only string values and will cast string on it if we pass other type,
10146
- // so we are making them truthy
10147
- process.env.PROD ??= PROD ? "1" : "";
10148
- process.env.DEV ??= DEV ? "1" : "";
10149
- for (const name in envs) process.env[name] ??= envs[name];
9890
+ for (const name in process.env.PROD ??= PROD ? "1" : "", process.env.DEV ??= DEV ? "1" : "", envs) process.env[name] ??= envs[name];
10150
9891
  // don't watch files in run mode
10151
9892
  if (!options.watch) viteConfig.server.watch = null;
10152
9893
  if (options.ui)
@@ -10166,20 +9907,20 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
10166
9907
  order: "post",
10167
9908
  async handler(server) {
10168
9909
  if (options.watch && false);
10169
- await vitest._setServer(options, server);
10170
- if (options.api && options.watch) (await Promise.resolve().then(function () { return setup$1; })).setup(vitest);
9910
+ if (await vitest._setServer(options, server), options.api && options.watch) (await Promise.resolve().then(function () { return setup$1; })).setup(vitest);
10171
9911
  // #415, in run mode we don't need the watcher, close it would improve the performance
10172
9912
  if (!options.watch) await server.watcher.close();
10173
9913
  }
10174
9914
  }
10175
9915
  },
10176
- SsrReplacerPlugin(),
9916
+ MetaEnvReplacerPlugin(),
10177
9917
  ...CSSEnablerPlugin(vitest),
10178
9918
  CoverageTransform(vitest),
10179
9919
  VitestCoreResolver(vitest),
10180
9920
  ...MocksPlugins(),
10181
9921
  VitestOptimizer(),
10182
- NormalizeURLPlugin()
9922
+ NormalizeURLPlugin(),
9923
+ ModuleRunnerTransform()
10183
9924
  ].filter(notNullish);
10184
9925
  }
10185
9926
  function removeUndefinedValues(obj) {
@@ -10188,25 +9929,19 @@ function removeUndefinedValues(obj) {
10188
9929
  }
10189
9930
 
10190
9931
  async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {}) {
10191
- const ctx = new Vitest(mode, deepClone(options), vitestOptions);
10192
- const root = slash(resolve$1(options.root || process.cwd()));
10193
- const configPath = options.config === false ? false : options.config ? resolve$1(root, options.config) : await findUp(configFiles, { cwd: root });
9932
+ 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 });
10194
9933
  options.config = configPath;
10195
- const { browser: _removeBrowser,...restOptions } = options;
10196
- const config = {
9934
+ const { browser: _removeBrowser,...restOptions } = options, config = {
10197
9935
  configFile: configPath,
10198
9936
  configLoader: options.configLoader,
10199
9937
  mode: options.mode || mode,
10200
9938
  plugins: await VitestPlugin(restOptions, ctx)
10201
- };
10202
- const server = await createViteServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })));
9939
+ }, server = await createViteServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })));
10203
9940
  if (ctx.config.api?.port) await server.listen();
10204
9941
  return ctx;
10205
9942
  }
10206
9943
 
10207
- const MAX_RESULT_COUNT = 10;
10208
- const SELECTION_MAX_INDEX = 7;
10209
- const ESC = "\x1B[";
9944
+ const MAX_RESULT_COUNT = 10, SELECTION_MAX_INDEX = 7, ESC = "\x1B[";
10210
9945
  class WatchFilter {
10211
9946
  filterRL;
10212
9947
  currentKeyword = void 0;
@@ -10217,23 +9952,17 @@ class WatchFilter {
10217
9952
  stdin;
10218
9953
  stdout;
10219
9954
  constructor(message, stdin = process.stdin, stdout$1 = stdout()) {
10220
- this.message = message;
10221
- this.stdin = stdin;
10222
- this.stdout = stdout$1;
10223
- this.filterRL = readline.createInterface({
9955
+ if (this.message = message, this.stdin = stdin, this.stdout = stdout$1, this.filterRL = readline.createInterface({
10224
9956
  input: this.stdin,
10225
9957
  escapeCodeTimeout: 50
10226
- });
10227
- readline.emitKeypressEvents(this.stdin, this.filterRL);
10228
- if (this.stdin.isTTY) this.stdin.setRawMode(true);
9958
+ }), readline.emitKeypressEvents(this.stdin, this.filterRL), this.stdin.isTTY) this.stdin.setRawMode(true);
10229
9959
  }
10230
9960
  async filter(filterFunc) {
10231
9961
  this.write(this.promptLine());
10232
9962
  const resultPromise = createDefer();
10233
9963
  this.onKeyPress = this.filterHandler(filterFunc, (result) => {
10234
9964
  resultPromise.resolve(result);
10235
- });
10236
- this.stdin.on("keypress", this.onKeyPress);
9965
+ }), this.stdin.on("keypress", this.onKeyPress);
10237
9966
  try {
10238
9967
  return await resultPromise;
10239
9968
  } finally {
@@ -10249,13 +9978,11 @@ class WatchFilter {
10249
9978
  break;
10250
9979
  case key?.ctrl && key?.name === "c":
10251
9980
  case key?.name === "escape":
10252
- this.write(`${ESC}1G${ESC}0J`);
10253
- onSubmit(void 0);
9981
+ this.write(`${ESC}1G${ESC}0J`), onSubmit(void 0);
10254
9982
  return;
10255
9983
  case key?.name === "enter":
10256
9984
  case key?.name === "return":
10257
- onSubmit(this.results[this.selectionIndex] || this.currentKeyword || "");
10258
- this.currentKeyword = void 0;
9985
+ onSubmit(this.results[this.selectionIndex] || this.currentKeyword || ""), this.currentKeyword = void 0;
10259
9986
  break;
10260
9987
  case key?.name === "up":
10261
9988
  if (this.selectionIndex && this.selectionIndex > 0) this.selectionIndex--;
@@ -10282,17 +10009,13 @@ class WatchFilter {
10282
10009
  const resultCountLine = this.results.length === 1 ? `Pattern matches ${this.results.length} result` : `Pattern matches ${this.results.length} results`;
10283
10010
  let resultBody = "";
10284
10011
  if (this.results.length > MAX_RESULT_COUNT) {
10285
- const offset = this.selectionIndex > SELECTION_MAX_INDEX ? this.selectionIndex - SELECTION_MAX_INDEX : 0;
10286
- const displayResults = this.results.slice(offset, MAX_RESULT_COUNT + offset);
10287
- const remainingResultCount = this.results.length - offset - displayResults.length;
10288
- resultBody = `${displayResults.map((result, index) => index + offset === this.selectionIndex ? c.green(` › ${result}`) : c.dim(` › ${result}`)).join("\n")}`;
10289
- if (remainingResultCount > 0) resultBody += `
10012
+ const offset = this.selectionIndex > SELECTION_MAX_INDEX ? this.selectionIndex - SELECTION_MAX_INDEX : 0, displayResults = this.results.slice(offset, MAX_RESULT_COUNT + offset), remainingResultCount = this.results.length - offset - displayResults.length;
10013
+ if (resultBody = `${displayResults.map((result, index) => index + offset === this.selectionIndex ? c.green(` › ${result}`) : c.dim(` › ${result}`)).join("\n")}`, remainingResultCount > 0) resultBody += `
10290
10014
  ${c.dim(` ...and ${remainingResultCount} more ${remainingResultCount === 1 ? "result" : "results"}`)}`;
10291
10015
  } else resultBody = this.results.map((result, index) => index === this.selectionIndex ? c.green(` › ${result}`) : c.dim(` › ${result}`)).join("\n");
10292
10016
  printStr += `\n${resultCountLine}\n${resultBody}`;
10293
10017
  }
10294
- this.eraseAndPrint(printStr);
10295
- this.restoreCursor();
10018
+ this.eraseAndPrint(printStr), this.restoreCursor();
10296
10019
  }
10297
10020
  keywordOffset() {
10298
10021
  return `? ${this.message} › `.length + 1;
@@ -10308,14 +10031,10 @@ ${c.dim(` ...and ${remainingResultCount} more ${remainingResultCount === 1 ? "
10308
10031
  // We have to take care of screen width in case of long lines
10309
10032
  rows += 1 + Math.floor(Math.max(stripVTControlCharacters(line).length - 1, 0) / columns);
10310
10033
  }
10311
- this.write(`${ESC}1G`);
10312
- this.write(`${ESC}J`);
10313
- this.write(str);
10314
- this.write(`${ESC}${rows - 1}A`);
10034
+ this.write(`${ESC}1G`), this.write(`${ESC}J`), this.write(str), this.write(`${ESC}${rows - 1}A`);
10315
10035
  }
10316
10036
  close() {
10317
- this.filterRL.close();
10318
- if (this.onKeyPress) this.stdin.removeListener("keypress", this.onKeyPress);
10037
+ if (this.filterRL.close(), this.onKeyPress) this.stdin.removeListener("keypress", this.onKeyPress);
10319
10038
  if (this.stdin.isTTY) this.stdin.setRawMode(false);
10320
10039
  }
10321
10040
  restoreCursor() {
@@ -10340,8 +10059,7 @@ const keys = [
10340
10059
  ["w", "filter by a project name"],
10341
10060
  ["b", "start the browser server if not started yet"],
10342
10061
  ["q", "quit"]
10343
- ];
10344
- const cancelKeys = [
10062
+ ], cancelKeys = [
10345
10063
  "space",
10346
10064
  "c",
10347
10065
  "h",
@@ -10359,17 +10077,12 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10359
10077
  // Cancel run and exit when ctrl-c or esc is pressed.
10360
10078
  // If cancelling takes long and key is pressed multiple times, exit forcefully.
10361
10079
  if (str === "" || str === "\x1B" || key && key.ctrl && key.name === "c") {
10362
- if (!ctx.isCancelling) {
10363
- ctx.logger.log(c.red("Cancelling test run. Press CTRL+c again to exit forcefully.\n"));
10364
- process.exitCode = 130;
10365
- await ctx.cancelCurrentRun("keyboard-input");
10366
- }
10080
+ if (!ctx.isCancelling) ctx.logger.log(c.red("Cancelling test run. Press CTRL+c again to exit forcefully.\n")), process.exitCode = 130, await ctx.cancelCurrentRun("keyboard-input");
10367
10081
  return ctx.exit(true);
10368
10082
  }
10369
10083
  // window not support suspend
10370
10084
  if (!isWindows && key && key.ctrl && key.name === "z") {
10371
- process.kill(process.ppid, "SIGTSTP");
10372
- process.kill(process.pid, "SIGTSTP");
10085
+ process.kill(process.ppid, "SIGTSTP"), process.kill(process.pid, "SIGTSTP");
10373
10086
  return;
10374
10087
  }
10375
10088
  const name = key?.name;
@@ -10398,24 +10111,17 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10398
10111
  if (name === "t") return inputNamePattern();
10399
10112
  // change fileNamePattern
10400
10113
  if (name === "p") return inputFilePattern();
10401
- if (name === "b") {
10402
- await ctx._initBrowserServers();
10403
- ctx.projects.forEach((project) => {
10404
- ctx.logger.log();
10405
- ctx.logger.printBrowserBanner(project);
10406
- });
10407
- return null;
10408
- }
10114
+ if (name === "b") return await ctx._initBrowserServers(), ctx.projects.forEach((project) => {
10115
+ ctx.logger.log(), ctx.logger.printBrowserBanner(project);
10116
+ }), null;
10409
10117
  }
10410
10118
  async function keypressHandler(str, key) {
10411
10119
  await _keypressHandler(str, key);
10412
10120
  }
10413
10121
  async function inputNamePattern() {
10414
10122
  off();
10415
- const watchFilter = new WatchFilter("Input test name pattern (RegExp)", stdin, stdout);
10416
- const filter = await watchFilter.filter((str) => {
10417
- const files = ctx.state.getFiles();
10418
- const tests = getTests(files);
10123
+ const watchFilter = new WatchFilter("Input test name pattern (RegExp)", stdin, stdout), filter = await watchFilter.filter((str) => {
10124
+ const files = ctx.state.getFiles(), tests = getTests(files);
10419
10125
  try {
10420
10126
  const reg = new RegExp(str);
10421
10127
  return tests.map((test) => test.name).filter((testName) => testName.match(reg));
@@ -10424,11 +10130,8 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10424
10130
  return [];
10425
10131
  }
10426
10132
  });
10427
- on();
10428
- if (typeof filter === "undefined") return;
10429
- const files = ctx.state.getFilepaths();
10430
- // if running in standalone mode, Vitest instance doesn't know about any test file
10431
- const cliFiles = ctx.config.standalone && !files.length ? await ctx._globTestFilepaths() : void 0;
10133
+ if (on(), typeof filter === "undefined") return;
10134
+ const files = ctx.state.getFilepaths(), cliFiles = ctx.config.standalone && !files.length ? await ctx._globTestFilepaths() : void 0;
10432
10135
  await ctx.changeNamePattern(filter?.trim() || "", cliFiles, "change pattern");
10433
10136
  }
10434
10137
  async function inputProjectName() {
@@ -10439,41 +10142,31 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10439
10142
  message: "Input a single project name",
10440
10143
  initial: ctx.config.project[0] || ""
10441
10144
  }]);
10442
- on();
10443
- await ctx.changeProjectName(filter.trim());
10145
+ on(), await ctx.changeProjectName(filter.trim());
10444
10146
  }
10445
10147
  async function inputFilePattern() {
10446
10148
  off();
10447
- const watchFilter = new WatchFilter("Input filename pattern", stdin, stdout);
10448
- const filter = await watchFilter.filter(async (str) => {
10449
- const files = await ctx.globTestFiles([str]);
10450
- return files.map((file) => relative(ctx.config.root, file[1]));
10149
+ const watchFilter = new WatchFilter("Input filename pattern", stdin, stdout), filter = await watchFilter.filter(async (str) => {
10150
+ const specifications = await ctx.globTestSpecifications([str]);
10151
+ return specifications.map((specification) => relative(ctx.config.root, specification.moduleId)).filter((file, index, all) => all.indexOf(file) === index);
10451
10152
  });
10452
- on();
10453
- if (typeof filter === "undefined") return;
10153
+ if (on(), typeof filter === "undefined") return;
10454
10154
  latestFilename = filter?.trim() || "";
10455
10155
  const lastResults = watchFilter.getLastResults();
10456
10156
  await ctx.changeFilenamePattern(latestFilename, filter && lastResults.length ? lastResults.map((i) => resolve(ctx.config.root, i)) : void 0);
10457
10157
  }
10458
10158
  let rl;
10459
10159
  function on() {
10460
- off();
10461
- rl = readline.createInterface({
10160
+ if (off(), rl = readline.createInterface({
10462
10161
  input: stdin,
10463
10162
  escapeCodeTimeout: 50
10464
- });
10465
- readline.emitKeypressEvents(stdin, rl);
10466
- if (stdin.isTTY) stdin.setRawMode(true);
10163
+ }), readline.emitKeypressEvents(stdin, rl), stdin.isTTY) stdin.setRawMode(true);
10467
10164
  stdin.on("keypress", keypressHandler);
10468
10165
  }
10469
10166
  function off() {
10470
- rl?.close();
10471
- rl = void 0;
10472
- stdin.removeListener("keypress", keypressHandler);
10473
- if (stdin.isTTY) stdin.setRawMode(false);
10167
+ if (rl?.close(), rl = void 0, stdin.removeListener("keypress", keypressHandler), stdin.isTTY) stdin.setRawMode(false);
10474
10168
  }
10475
- on();
10476
- return function cleanup() {
10169
+ return on(), function cleanup() {
10477
10170
  off();
10478
10171
  };
10479
10172
  }
@@ -10484,20 +10177,14 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10484
10177
  * Returns a Vitest instance if initialized successfully.
10485
10178
  */
10486
10179
  async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, vitestOptions) {
10487
- const root = resolve(options.root || process.cwd());
10488
- const ctx = await prepareVitest(mode, options, viteOverrides, vitestOptions);
10489
- if (mode === "test" && ctx.config.coverage.enabled) {
10490
- const provider = ctx.config.coverage.provider || "v8";
10491
- const requiredPackages = CoverageProviderMap[provider];
10180
+ const root = resolve(options.root || process.cwd()), ctx = await prepareVitest(mode, options, viteOverrides, vitestOptions, cliFilters);
10181
+ if (mode === "test" && ctx._coverageOptions.enabled) {
10182
+ const provider = ctx._coverageOptions.provider || "v8", requiredPackages = CoverageProviderMap[provider];
10492
10183
  if (requiredPackages) {
10493
- if (!await ctx.packageInstaller.ensureInstalled(requiredPackages, root, ctx.version)) {
10494
- process.exitCode = 1;
10495
- return ctx;
10496
- }
10184
+ if (!await ctx.packageInstaller.ensureInstalled(requiredPackages, root, ctx.version)) return process.exitCode = 1, ctx;
10497
10185
  }
10498
10186
  }
10499
- const stdin = vitestOptions?.stdin || process.stdin;
10500
- const stdout = vitestOptions?.stdout || process.stdout;
10187
+ const stdin = vitestOptions?.stdin || process.stdin, stdout = vitestOptions?.stdout || process.stdout;
10501
10188
  let stdinCleanup;
10502
10189
  if (stdin.isTTY && ctx.config.watch) stdinCleanup = registerConsoleShortcuts(ctx, stdin, stdout);
10503
10190
  ctx.onAfterSetServer(() => {
@@ -10509,54 +10196,27 @@ async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, v
10509
10196
  else if (ctx.config.standalone) await ctx.init();
10510
10197
  else await ctx.start(cliFilters);
10511
10198
  } catch (e) {
10512
- if (e instanceof FilesNotFoundError) return ctx;
10513
- if (e instanceof GitNotFoundError) {
10514
- ctx.logger.error(e.message);
10515
- return ctx;
10516
- }
10517
- if (e instanceof IncludeTaskLocationDisabledError || e instanceof RangeLocationFilterProvidedError || e instanceof LocationFilterFileNotFoundError) {
10518
- ctx.logger.printError(e, { verbose: false });
10519
- return ctx;
10520
- }
10521
- process.exitCode = 1;
10522
- ctx.logger.printError(e, {
10199
+ return e instanceof FilesNotFoundError ? ctx : e instanceof GitNotFoundError ? (ctx.logger.error(e.message), ctx) : e instanceof IncludeTaskLocationDisabledError || e instanceof RangeLocationFilterProvidedError || e instanceof LocationFilterFileNotFoundError ? (ctx.logger.printError(e, { verbose: false }), ctx) : (process.exitCode = 1, ctx.logger.printError(e, {
10523
10200
  fullStack: true,
10524
10201
  type: "Unhandled Error"
10525
- });
10526
- ctx.logger.error("\n\n");
10527
- return ctx;
10202
+ }), ctx.logger.error("\n\n"), ctx);
10528
10203
  }
10529
- if (ctx.shouldKeepServer()) return ctx;
10530
- stdinCleanup?.();
10531
- await ctx.close();
10532
- return ctx;
10204
+ return ctx.shouldKeepServer() ? ctx : (stdinCleanup?.(), await ctx.close(), ctx);
10533
10205
  }
10534
- async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions) {
10535
- process.env.TEST = "true";
10536
- process.env.VITEST = "true";
10537
- process.env.NODE_ENV ??= "test";
10538
- if (options.run) options.watch = false;
10206
+ async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions, cliFilters) {
10207
+ if (process.env.TEST = "true", process.env.VITEST = "true", process.env.NODE_ENV ??= "test", options.run) options.watch = false;
10208
+ if (options.standalone && (cliFilters?.length || 0) > 0) options.standalone = false;
10539
10209
  // this shouldn't affect _application root_ that can be changed inside config
10540
- const root = resolve(options.root || process.cwd());
10541
- const ctx = await createVitest(mode, options, viteOverrides, vitestOptions);
10542
- const environmentPackage = getEnvPackageName(ctx.config.environment);
10543
- if (environmentPackage && !await ctx.packageInstaller.ensureInstalled(environmentPackage, root)) {
10544
- process.exitCode = 1;
10545
- return ctx;
10546
- }
10547
- return ctx;
10210
+ const root = resolve(options.root || process.cwd()), ctx = await createVitest(mode, options, viteOverrides, vitestOptions), environmentPackage = getEnvPackageName(ctx.config.environment);
10211
+ return environmentPackage && !await ctx.packageInstaller.ensureInstalled(environmentPackage, root) && (process.exitCode = 1), ctx;
10548
10212
  }
10549
10213
  function processCollected(ctx, files, options) {
10550
10214
  let errorsPrinted = false;
10551
- forEachSuite(files, (suite) => {
10215
+ if (forEachSuite(files, (suite) => {
10552
10216
  suite.errors().forEach((error) => {
10553
- errorsPrinted = true;
10554
- ctx.logger.printError(error, { project: suite.project });
10217
+ errorsPrinted = true, ctx.logger.printError(error, { project: suite.project });
10555
10218
  });
10556
- });
10557
- if (errorsPrinted) return;
10558
- if (typeof options.json !== "undefined") return processJsonOutput(files, options);
10559
- return formatCollectedAsString(files).forEach((test) => console.log(test));
10219
+ }), !errorsPrinted) return typeof options.json === "undefined" ? formatCollectedAsString(files).forEach((test) => console.log(test)) : processJsonOutput(files, options);
10560
10220
  }
10561
10221
  function outputFileList(files, options) {
10562
10222
  if (typeof options.json !== "undefined") return outputJsonFileList(files, options);
@@ -10566,8 +10226,7 @@ function outputJsonFileList(files, options) {
10566
10226
  if (typeof options.json === "boolean") return console.log(JSON.stringify(formatFilesAsJSON(files), null, 2));
10567
10227
  if (typeof options.json === "string") {
10568
10228
  const jsonPath = resolve(options.root || process.cwd(), options.json);
10569
- mkdirSync(dirname(jsonPath), { recursive: true });
10570
- writeFileSync(jsonPath, JSON.stringify(formatFilesAsJSON(files), null, 2));
10229
+ mkdirSync(dirname(jsonPath), { recursive: true }), writeFileSync(jsonPath, JSON.stringify(formatFilesAsJSON(files), null, 2));
10571
10230
  }
10572
10231
  }
10573
10232
  function formatFilesAsJSON(files) {
@@ -10588,8 +10247,7 @@ function processJsonOutput(files, options) {
10588
10247
  if (typeof options.json === "boolean") return console.log(JSON.stringify(formatCollectedAsJSON(files), null, 2));
10589
10248
  if (typeof options.json === "string") {
10590
10249
  const jsonPath = resolve(options.root || process.cwd(), options.json);
10591
- mkdirSync(dirname(jsonPath), { recursive: true });
10592
- writeFileSync(jsonPath, JSON.stringify(formatCollectedAsJSON(files), null, 2));
10250
+ mkdirSync(dirname(jsonPath), { recursive: true }), writeFileSync(jsonPath, JSON.stringify(formatCollectedAsJSON(files), null, 2));
10593
10251
  }
10594
10252
  }
10595
10253
  function forEachSuite(modules, callback) {
@@ -10600,7 +10258,7 @@ function forEachSuite(modules, callback) {
10600
10258
  }
10601
10259
  function formatCollectedAsJSON(files) {
10602
10260
  const results = [];
10603
- files.forEach((file) => {
10261
+ return files.forEach((file) => {
10604
10262
  for (const test of file.children.allTests()) {
10605
10263
  if (test.result().state === "skipped") continue;
10606
10264
  const result = {
@@ -10611,19 +10269,17 @@ function formatCollectedAsJSON(files) {
10611
10269
  if (test.location) result.location = test.location;
10612
10270
  results.push(result);
10613
10271
  }
10614
- });
10615
- return results;
10272
+ }), results;
10616
10273
  }
10617
10274
  function formatCollectedAsString(testModules) {
10618
10275
  const results = [];
10619
- testModules.forEach((testModule) => {
10276
+ return testModules.forEach((testModule) => {
10620
10277
  for (const test of testModule.children.allTests()) {
10621
10278
  if (test.result().state === "skipped") continue;
10622
10279
  const fullName = `${test.module.task.name} > ${test.fullName}`;
10623
10280
  results.push((test.project.name ? `[${test.project.name}] ` : "") + fullName);
10624
10281
  }
10625
- });
10626
- return results;
10282
+ }), results;
10627
10283
  }
10628
10284
  const envPackageNames = {
10629
10285
  "jsdom": "jsdom",
@@ -10631,10 +10287,7 @@ const envPackageNames = {
10631
10287
  "edge-runtime": "@edge-runtime/vm"
10632
10288
  };
10633
10289
  function getEnvPackageName(env) {
10634
- if (env === "node") return null;
10635
- if (env in envPackageNames) return envPackageNames[env];
10636
- if (env[0] === "." || isAbsolute(env)) return null;
10637
- return `vitest-environment-${env}`;
10290
+ return env === "node" ? null : env in envPackageNames ? envPackageNames[env] : env[0] === "." || isAbsolute(env) ? null : `vitest-environment-${env}`;
10638
10291
  }
10639
10292
 
10640
10293
  var cliApi = /*#__PURE__*/Object.freeze({
@@ -10647,4 +10300,4 @@ var cliApi = /*#__PURE__*/Object.freeze({
10647
10300
  startVitest: startVitest
10648
10301
  });
10649
10302
 
10650
- export { FilesNotFoundError as F, GitNotFoundError as G, Vitest as V, VitestPlugin as a, VitestPackageInstaller as b, createVitest as c, registerConsoleShortcuts as d, createViteLogger as e, cliApi as f, isValidApiRequest as i, resolveFsAllow as r, startVitest as s };
10303
+ 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 };