vitest 4.0.0-beta.6 → 4.0.0-beta.7

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 (31) hide show
  1. package/dist/browser.d.ts +3 -3
  2. package/dist/chunks/{browser.d.Cawq_X_N.d.ts → browser.d.DOMmqJQx.d.ts} +1 -1
  3. package/dist/chunks/{cac.WE-urWw5.js → cac.Dsn7ixFt.js} +7 -6
  4. package/dist/chunks/{cli-api.CZz3evYC.js → cli-api.DfGJyldU.js} +341 -38
  5. package/dist/chunks/{config.d.CKNVOKm0.d.ts → config.d._GBBbReY.d.ts} +1 -0
  6. package/dist/chunks/{coverage.BPRS6xgn.js → coverage.Dvxug1RM.js} +1 -1
  7. package/dist/chunks/{index.VNI-1z5c.js → index.C3EbxYwt.js} +8 -3
  8. package/dist/chunks/{index.7w0eqmYM.js → index.D2B6d2vv.js} +1 -1
  9. package/dist/chunks/{index.BG0gqZH-.js → index.DfviD7lX.js} +16 -8
  10. package/dist/chunks/{moduleRunner.d.8kKUsuDg.d.ts → moduleRunner.d.CX4DuqOx.d.ts} +1 -1
  11. package/dist/chunks/{plugin.d.DuiQJfUL.d.ts → plugin.d.vcD4xbMS.d.ts} +1 -1
  12. package/dist/chunks/{reporters.d.CqR9-CDJ.d.ts → reporters.d.BC86JJdB.d.ts} +785 -727
  13. package/dist/chunks/{typechecker.Cd1wvxUM.js → typechecker.DSo_maXz.js} +1 -1
  14. package/dist/chunks/{worker.d.Db-UVmXc.d.ts → worker.d.BKu8cnnX.d.ts} +1 -1
  15. package/dist/chunks/{worker.d.D9QWnzAe.d.ts → worker.d.DYlqbejz.d.ts} +1 -1
  16. package/dist/cli.js +3 -3
  17. package/dist/config.d.ts +6 -6
  18. package/dist/coverage.d.ts +4 -4
  19. package/dist/coverage.js +2 -2
  20. package/dist/environments.js +1 -1
  21. package/dist/index.d.ts +6 -6
  22. package/dist/module-evaluator.d.ts +3 -3
  23. package/dist/node.d.ts +11 -9
  24. package/dist/node.js +11 -16
  25. package/dist/reporters.d.ts +4 -4
  26. package/dist/reporters.js +3 -3
  27. package/dist/runners.d.ts +1 -1
  28. package/dist/worker.js +1 -1
  29. package/dist/workers.d.ts +3 -3
  30. package/dist/workers.js +1 -1
  31. package/package.json +10 -10
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { S as SerializedCoverageConfig, a as SerializedConfig } from './chunks/config.d.CKNVOKm0.js';
1
+ import { S as SerializedCoverageConfig, a as SerializedConfig } from './chunks/config.d._GBBbReY.js';
2
2
  import { R as RuntimeCoverageModuleLoader } from './chunks/coverage.d.BZtK59WP.js';
3
3
  import { SerializedDiffOptions } from '@vitest/utils/diff';
4
- import { V as VitestModuleRunner } from './chunks/moduleRunner.d.8kKUsuDg.js';
4
+ import { V as VitestModuleRunner } from './chunks/moduleRunner.d.CX4DuqOx.js';
5
5
  export { collectTests, processError, startTests } from '@vitest/runner';
6
6
  import * as spy from '@vitest/spy';
7
7
  export { spy as SpyModule };
@@ -11,7 +11,7 @@ import '@vitest/pretty-format';
11
11
  import '@vitest/snapshot';
12
12
  import 'node:vm';
13
13
  import 'vite/module-runner';
14
- import './chunks/worker.d.D9QWnzAe.js';
14
+ import './chunks/worker.d.DYlqbejz.js';
15
15
  import './chunks/environment.d.2fYMoz3o.js';
16
16
  import '@vitest/mocker';
17
17
  import './chunks/mocker.d.BE_2ls6u.js';
@@ -1,4 +1,4 @@
1
- import { T as TestExecutionMethod } from './worker.d.D9QWnzAe.js';
1
+ import { T as TestExecutionMethod } from './worker.d.DYlqbejz.js';
2
2
 
3
3
  type SerializedTestSpecification = [project: {
4
4
  name: string | undefined;
@@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
3
3
  import { normalize } from 'pathe';
4
4
  import c from 'tinyrainbow';
5
5
  import { a as defaultPort, d as defaultBrowserPort } from './constants.D_Q9UYh-.js';
6
- import { R as ReportersMap } from './index.VNI-1z5c.js';
6
+ import { R as ReportersMap } from './index.C3EbxYwt.js';
7
7
 
8
8
  function toArr(any) {
9
9
  return any == null ? [] : Array.isArray(any) ? any : [any];
@@ -619,7 +619,7 @@ class CAC extends EventEmitter {
619
619
 
620
620
  const cac = (name = "") => new CAC(name);
621
621
 
622
- var version = "4.0.0-beta.6";
622
+ var version = "4.0.0-beta.7";
623
623
 
624
624
  const apiConfig = (port) => ({
625
625
  port: {
@@ -870,6 +870,7 @@ const cliOptionsConfig = {
870
870
  description: "If connection to the browser takes longer, the test suite will fail (default: `60_000`)",
871
871
  argument: "<timeout>"
872
872
  },
873
+ trackUnhandledErrors: { description: "Control if Vitest catches uncaught exceptions so they can be reported (default: `true`)" },
873
874
  orchestratorScripts: null,
874
875
  testerScripts: null,
875
876
  commands: null,
@@ -1335,10 +1336,10 @@ async function start(mode, cliFilters, options) {
1335
1336
  process.title = "node (vitest)";
1336
1337
  } catch {}
1337
1338
  try {
1338
- const { startVitest } = await import('./cli-api.CZz3evYC.js').then(function (n) { return n.f; }), ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options));
1339
+ const { startVitest } = await import('./cli-api.DfGJyldU.js').then(function (n) { return n.j; }), ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options));
1339
1340
  if (!ctx.shouldKeepServer()) await ctx.exit();
1340
1341
  } catch (e) {
1341
- const { errorBanner } = await import('./index.VNI-1z5c.js').then(function (n) { return n.u; });
1342
+ const { errorBanner } = await import('./index.C3EbxYwt.js').then(function (n) { return n.u; });
1342
1343
  if (console.error(`\n${errorBanner("Startup Error")}`), console.error(e), console.error("\n\n"), process.exitCode == null) process.exitCode = 1;
1343
1344
  process.exit();
1344
1345
  }
@@ -1353,7 +1354,7 @@ async function collect(mode, cliFilters, options) {
1353
1354
  process.title = "node (vitest)";
1354
1355
  } catch {}
1355
1356
  try {
1356
- const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.CZz3evYC.js').then(function (n) { return n.f; }), ctx = await prepareVitest(mode, {
1357
+ const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.DfGJyldU.js').then(function (n) { return n.j; }), ctx = await prepareVitest(mode, {
1357
1358
  ...normalizeCliOptions(cliFilters, options),
1358
1359
  watch: false,
1359
1360
  run: true
@@ -1371,7 +1372,7 @@ async function collect(mode, cliFilters, options) {
1371
1372
  }
1372
1373
  await ctx.close();
1373
1374
  } catch (e) {
1374
- const { errorBanner } = await import('./index.VNI-1z5c.js').then(function (n) { return n.u; });
1375
+ const { errorBanner } = await import('./index.C3EbxYwt.js').then(function (n) { return n.u; });
1375
1376
  if (console.error(`\n${errorBanner("Collect Error")}`), console.error(e), console.error("\n\n"), process.exitCode == null) process.exitCode = 1;
1376
1377
  process.exit();
1377
1378
  }
@@ -2,16 +2,17 @@ import fs, { promises, existsSync, readFileSync, mkdirSync, writeFileSync } from
2
2
  import { relative, resolve, dirname, extname, normalize, join, basename, isAbsolute } from 'pathe';
3
3
  import { C as CoverageProviderMap } from './coverage.D_JHT54q.js';
4
4
  import path, { resolve as resolve$1 } from 'node:path';
5
- import { noop, isPrimitive, createDefer, slash, highlight, toArray, cleanUrl, deepMerge, KNOWN_ASSET_RE, 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, mergeConfig, createServer } from 'vite';
8
+ import { parseAst, searchForWorkspaceRoot, version, mergeConfig, createServer } from 'vite';
9
9
  import { A as API_PATH, c as configFiles, d as defaultBrowserPort, a as defaultPort } from './constants.D_Q9UYh-.js';
10
- import { generateFileHash, limitConcurrency, createFileTask, hasFailed, getTasks, getTests } from '@vitest/runner/utils';
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 { v as version$1 } from './cac.WE-urWw5.js';
13
+ import { v as version$1 } from './cac.Dsn7ixFt.js';
13
14
  import { c as createBirpc } from './index.Bgo3tNWt.js';
14
- 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.VNI-1z5c.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.C3EbxYwt.js';
15
16
  import require$$0$3 from 'events';
16
17
  import require$$1$1 from 'https';
17
18
  import require$$2 from 'http';
@@ -23,11 +24,12 @@ import require$$7 from 'url';
23
24
  import require$$0 from 'zlib';
24
25
  import require$$0$1 from 'buffer';
25
26
  import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
26
- import { parseErrorStacktrace } from '@vitest/utils/source-map';
27
27
  import crypto, { createHash } from 'node:crypto';
28
28
  import { distDir, rootDir } from '../path.js';
29
- import { h as hash, d as createFetchModuleFunction, n as normalizeResolvedIdToUrl, R as RandomSequencer, i as isPackageExists, g as getFilePoolName, e as isBrowserEnabled, r as resolveConfig, f as groupBy, j as getCoverageProvider, k as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.BPRS6xgn.js';
30
- import { c as convertTasksToEvents } from './typechecker.Cd1wvxUM.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.Dvxug1RM.js';
30
+ import { b as ancestor, c as convertTasksToEvents } from './typechecker.DSo_maXz.js';
31
+ import { TraceMap, originalPositionFor, parseErrorStacktrace } from '@vitest/utils/source-map';
32
+ import createDebug from 'debug';
31
33
  import { VitestModuleEvaluator } from '#module-evaluator';
32
34
  import { ModuleRunner } from 'vite/module-runner';
33
35
  import { Console } from 'node:console';
@@ -36,7 +38,6 @@ import { createRequire, builtinModules, isBuiltin } from 'node:module';
36
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
43
  import MagicString from 'magic-string';
@@ -44,9 +45,9 @@ import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
44
45
  import { c as configDefaults } from './defaults.CXFFjsi8.js';
45
46
  import { f as findNearestPackageData } from './resolver.Bx6lE0iq.js';
46
47
  import * as esModuleLexer from 'es-module-lexer';
47
- import { a as BenchmarkReportsMap } from './index.7w0eqmYM.js';
48
+ import { a as BenchmarkReportsMap } from './index.D2B6d2vv.js';
48
49
  import assert$1 from 'node:assert';
49
- import { serializeError } from '@vitest/utils/error';
50
+ import { serializeError as serializeError$1 } from '@vitest/utils/error';
50
51
  import readline from 'node:readline';
51
52
  import { stripVTControlCharacters } from 'node:util';
52
53
 
@@ -5199,17 +5200,9 @@ class WebSocketReporter {
5199
5200
  });
5200
5201
  }
5201
5202
  async onTaskUpdate(packs, events) {
5202
- this.clients.size !== 0 && (packs.forEach(([taskId, result]) => {
5203
- const task = this.ctx.state.idMap.get(taskId), isBrowser = task && task.file.pool === "browser";
5204
- result?.errors?.forEach((error) => {
5205
- if (!isPrimitive(error)) if (isBrowser) {
5206
- const project = this.ctx.getProjectByName(task.file.projectName || "");
5207
- error.stacks = project.browser?.parseErrorStacktrace(error);
5208
- } else error.stacks = parseErrorStacktrace(error);
5209
- });
5210
- }), this.clients.forEach((client) => {
5203
+ this.clients.size !== 0 && this.clients.forEach((client) => {
5211
5204
  client.onTaskUpdate?.(packs, events)?.catch?.(noop);
5212
- }));
5205
+ });
5213
5206
  }
5214
5207
  onTestRunEnd(testModules, unhandledErrors) {
5215
5208
  if (!this.clients.size) return;
@@ -5236,6 +5229,265 @@ var setup$1 = /*#__PURE__*/Object.freeze({
5236
5229
  setup: setup
5237
5230
  });
5238
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.getName(),
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
+
5239
5491
  class BrowserSessions {
5240
5492
  sessions = /* @__PURE__ */ new Map();
5241
5493
  sessionIds = /* @__PURE__ */ new Set();
@@ -5711,7 +5963,8 @@ function serializeConfig(config, coreConfig, viteConfig) {
5711
5963
  viewport: browser.viewport,
5712
5964
  screenshotFailures: browser.screenshotFailures,
5713
5965
  locators: { testIdAttribute: browser.locators.testIdAttribute },
5714
- providerOptions: browser.provider === "playwright" ? { actionTimeout: browser.providerOptions?.context?.actionTimeout } : {}
5966
+ providerOptions: browser.provider === "playwright" ? { actionTimeout: browser.providerOptions?.context?.actionTimeout } : {},
5967
+ trackUnhandledErrors: browser.trackUnhandledErrors ?? true
5715
5968
  };
5716
5969
  })(config.browser),
5717
5970
  standalone: config.standalone,
@@ -6925,9 +7178,9 @@ class TestProject {
6925
7178
  /** @internal */ _vite;
6926
7179
  /** @internal */ _hash;
6927
7180
  /** @internal */ _resolver;
7181
+ /** @inetrnal */ testFilesList = null;
6928
7182
  runner;
6929
7183
  closingPromise;
6930
- testFilesList = null;
6931
7184
  typecheckFilesList = null;
6932
7185
  _globalSetups;
6933
7186
  _provided = {};
@@ -8073,6 +8326,9 @@ function getSuiteState(task) {
8073
8326
  if (state === "pass") return "passed";
8074
8327
  throw new Error(`Unknown suite state: ${state}`);
8075
8328
  }
8329
+ function experimental_getRunnerTask(entity) {
8330
+ return entity.task;
8331
+ }
8076
8332
 
8077
8333
  function isAggregateError(err) {
8078
8334
  return typeof AggregateError !== "undefined" && err instanceof AggregateError ? true : err instanceof Error && "errors" in err;
@@ -8159,7 +8415,7 @@ class StateManager {
8159
8415
  }
8160
8416
  clearFiles(project, paths = []) {
8161
8417
  paths.forEach((path) => {
8162
- const files = this.filesMap.get(path), fileTask = createFileTask(path, project.config.root, project.config.name);
8418
+ const files = this.filesMap.get(path), fileTask = createFileTask$1(path, project.config.root, project.config.name);
8163
8419
  if (fileTask.local = true, TestModule.register(fileTask, project), this.idMap.set(fileTask.id, fileTask), !files) {
8164
8420
  this.filesMap.set(path, [fileTask]);
8165
8421
  return;
@@ -8203,7 +8459,7 @@ class StateManager {
8203
8459
  return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
8204
8460
  }
8205
8461
  cancelFiles(files, project) {
8206
- this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
8462
+ this.collectFiles(project, files.map((filepath) => createFileTask$1(filepath, project.config.root, project.config.name)));
8207
8463
  }
8208
8464
  }
8209
8465
 
@@ -8686,9 +8942,9 @@ class TestRun {
8686
8942
  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;
8687
8943
  }
8688
8944
  async updated(update, events) {
8689
- this.vitest.state.updateTasks(update);
8945
+ this.syncUpdateStacks(update), this.vitest.state.updateTasks(update);
8690
8946
  for (const [id, event, data] of events) await this.reportEvent(id, event, data).catch((error) => {
8691
- this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
8947
+ this.vitest.state.catchError(serializeError$1(error), "Unhandled Reporter Error");
8692
8948
  });
8693
8949
  // TODO: what is the order or reports here?
8694
8950
  // "onTaskUpdate" in parallel with others or before all or after all?
@@ -8705,6 +8961,17 @@ class TestRun {
8705
8961
  hasFailed(modules) {
8706
8962
  return modules.length ? modules.some((m) => !m.ok()) : !this.vitest.config.passWithNoTests;
8707
8963
  }
8964
+ syncUpdateStacks(update) {
8965
+ update.forEach(([taskId, result]) => {
8966
+ const task = this.vitest.state.idMap.get(taskId), isBrowser = task && task.file.pool === "browser";
8967
+ result?.errors?.forEach((error) => {
8968
+ if (isPrimitive(error)) return;
8969
+ const project = this.vitest.getProjectByName(task.file.projectName || "");
8970
+ if (isBrowser) error.stacks = project.browser?.parseErrorStacktrace(error, { frameFilter: project.config.onStackTrace }) || [];
8971
+ else error.stacks = parseErrorStacktrace(error, { frameFilter: project.config.onStackTrace });
8972
+ });
8973
+ });
8974
+ }
8708
8975
  async reportEvent(id, event, data) {
8709
8976
  const task = this.vitest.state.idMap.get(id), entity = task && this.vitest.state.getReportedEntity(task);
8710
8977
  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);
@@ -8786,8 +9053,8 @@ class VitestWatcher {
8786
9053
  registerWatcher() {
8787
9054
  const watcher = this.vitest.vite.watcher;
8788
9055
  if (this.vitest.config.forceRerunTriggers.length) watcher.add(this.vitest.config.forceRerunTriggers);
8789
- return watcher.on("change", this.onChange), watcher.on("unlink", this.onUnlink), watcher.on("add", this.onAdd), this.unregisterWatcher = () => {
8790
- watcher.off("change", this.onChange), watcher.off("unlink", this.onUnlink), watcher.off("add", this.onAdd), this.unregisterWatcher = noop;
9056
+ return watcher.on("change", this.onFileChange), watcher.on("unlink", this.onFileDelete), watcher.on("add", this.onFileCreate), this.unregisterWatcher = () => {
9057
+ watcher.off("change", this.onFileChange), watcher.off("unlink", this.onFileDelete), watcher.off("add", this.onFileCreate), this.unregisterWatcher = noop;
8791
9058
  }, this;
8792
9059
  }
8793
9060
  scheduleRerun(file) {
@@ -8805,7 +9072,7 @@ class VitestWatcher {
8805
9072
  }
8806
9073
  }), triggered;
8807
9074
  }
8808
- onChange = (id) => {
9075
+ onFileChange = (id) => {
8809
9076
  id = slash(id), this.vitest.logger.clearHighlightCache(id), this.vitest.invalidateFile(id);
8810
9077
  const testFiles = this.getTestFilesFromWatcherTrigger(id);
8811
9078
  if (testFiles) this.scheduleRerun(id);
@@ -8814,10 +9081,10 @@ class VitestWatcher {
8814
9081
  if (needsRerun) this.scheduleRerun(id);
8815
9082
  }
8816
9083
  };
8817
- onUnlink = (id) => {
9084
+ onFileDelete = (id) => {
8818
9085
  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);
8819
9086
  };
8820
- onAdd = (id) => {
9087
+ onFileCreate = (id) => {
8821
9088
  id = slash(id), this.vitest.invalidateFile(id);
8822
9089
  const testFiles = this.getTestFilesFromWatcherTrigger(id);
8823
9090
  if (testFiles) {
@@ -8910,6 +9177,14 @@ class Vitest {
8910
9177
  * If projects were filtered with `--project` flag, they won't appear here.
8911
9178
  */
8912
9179
  projects = [];
9180
+ /**
9181
+ * A watcher handler. This is not the file system watcher. The handler only
9182
+ * exposes methods to handle changed files.
9183
+ *
9184
+ * If you have your own watcher, you can use these methods to replicate
9185
+ * Vitest behaviour.
9186
+ */
9187
+ watcher;
8913
9188
  /** @internal */ configOverride = {};
8914
9189
  /** @internal */ coverageProvider;
8915
9190
  /** @internal */ filenamePattern;
@@ -8926,7 +9201,6 @@ class Vitest {
8926
9201
  isFirstRun = true;
8927
9202
  restartsCount = 0;
8928
9203
  specifications;
8929
- watcher;
8930
9204
  pool;
8931
9205
  _config;
8932
9206
  _vite;
@@ -8934,7 +9208,7 @@ class Vitest {
8934
9208
  _cache;
8935
9209
  _snapshot;
8936
9210
  constructor(mode, cliOptions, options = {}) {
8937
- 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]));
9211
+ 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));
8938
9212
  }
8939
9213
  _onRestartListeners = [];
8940
9214
  _onClose = [];
@@ -9087,6 +9361,15 @@ class Vitest {
9087
9361
  import(moduleId) {
9088
9362
  return this.runner.import(moduleId);
9089
9363
  }
9364
+ /**
9365
+ * Creates a coverage provider if `coverage` is enabled in the config.
9366
+ */
9367
+ async createCoverageProvider() {
9368
+ if (this.coverageProvider) return this.coverageProvider;
9369
+ const coverageProvider = await this.initCoverageProvider();
9370
+ if (coverageProvider) await coverageProvider.clean(this.config.coverage.clean);
9371
+ return coverageProvider || null;
9372
+ }
9090
9373
  async resolveProjects(cliOptions) {
9091
9374
  const names = /* @__PURE__ */ new Set();
9092
9375
  if (this.config.projects) return resolveProjects(this, cliOptions, void 0, this.config.projects, names);
@@ -9215,6 +9498,13 @@ class Vitest {
9215
9498
  return this.getModuleSpecifications(file);
9216
9499
  }
9217
9500
  /**
9501
+ * If there is a test run happening, returns a promise that will
9502
+ * resolve when the test run is finished.
9503
+ */
9504
+ async waitForTestRunEnd() {
9505
+ this.runningPromise && await this.runningPromise;
9506
+ }
9507
+ /**
9218
9508
  * Get test specifications associated with the given module. If module is not a test file, an empty array is returned.
9219
9509
  *
9220
9510
  * **Note:** this method relies on a cache generated by `globTestSpecifications`. If the file was not processed yet, use `project.matchesGlobPattern` instead.
@@ -9227,7 +9517,9 @@ class Vitest {
9227
9517
  * Vitest automatically caches test specifications for each file. This method clears the cache for the given file or the whole cache altogether.
9228
9518
  */
9229
9519
  clearSpecificationsCache(moduleId) {
9230
- this.specifications.clearCache(moduleId);
9520
+ if (this.specifications.clearCache(moduleId), !moduleId) this.projects.forEach((project) => {
9521
+ project.testFilesList = null;
9522
+ });
9231
9523
  }
9232
9524
  /**
9233
9525
  * Run tests for the given test specifications. This does not trigger `onWatcher*` events.
@@ -9278,6 +9570,18 @@ class Vitest {
9278
9570
  this.runningPromise = void 0, this.isFirstRun = false, this.config.changed = false, this.config.related = void 0;
9279
9571
  }), await this.runningPromise;
9280
9572
  }
9573
+ async experimental_parseSpecifications(specifications, options) {
9574
+ if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecifications\` does not support "${this.mode}" mode.`);
9575
+ 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)));
9576
+ return Promise.all(promises);
9577
+ }
9578
+ async experimental_parseSpecification(specification) {
9579
+ if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecification\` does not support "${this.mode}" mode.`);
9580
+ const file = await astCollectTests(specification.project, specification.moduleId).catch((error) => {
9581
+ return createFailedFileTask(specification.project, specification.moduleId, error);
9582
+ });
9583
+ return this.state.collectFiles(specification.project, [file]), this.state.getReportedEntity(file);
9584
+ }
9281
9585
  /**
9282
9586
  * Collect tests in specified modules. Vitest will run the files to collect tests.
9283
9587
  * @param specifications A list of specifications to run.
@@ -9416,7 +9720,6 @@ class Vitest {
9416
9720
  this.configOverride.testNamePattern = void 0;
9417
9721
  }
9418
9722
  _rerunTimer;
9419
- // we can't use a single `triggerId` yet because vscode extension relies on this
9420
9723
  async scheduleRerun(triggerId) {
9421
9724
  const currentCount = this.restartsCount;
9422
9725
  clearTimeout(this._rerunTimer), await this.runningPromise, clearTimeout(this._rerunTimer), this.restartsCount === currentCount && (this._rerunTimer = setTimeout(async () => {
@@ -9434,7 +9737,7 @@ class Vitest {
9434
9737
  if (files = files.filter((file) => filteredFiles.some((f) => f.moduleId === file)), files.length === 0) return;
9435
9738
  }
9436
9739
  this.watcher.changedTests.clear();
9437
- const triggerIds = new Set(triggerId.map((id) => relative(this.config.root, id))), triggerLabel = Array.from(triggerIds).join(", "), specifications = files.flatMap((file) => this.getModuleSpecifications(file)).filter((specification) => {
9740
+ const triggerLabel = relative(this.config.root, triggerId), specifications = files.flatMap((file) => this.getModuleSpecifications(file)).filter((specification) => {
9438
9741
  return this._onFilterWatchedSpecification.length === 0 ? true : this._onFilterWatchedSpecification.every((fn) => fn(specification));
9439
9742
  });
9440
9743
  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));
@@ -10114,4 +10417,4 @@ var cliApi = /*#__PURE__*/Object.freeze({
10114
10417
  startVitest: startVitest
10115
10418
  });
10116
10419
 
10117
- 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 };
10420
+ 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 };
@@ -187,6 +187,7 @@ interface SerializedConfig {
187
187
  // for playwright
188
188
  actionTimeout?: number;
189
189
  };
190
+ trackUnhandledErrors: boolean;
190
191
  };
191
192
  standalone: boolean;
192
193
  logHeapUsage: boolean | undefined;
@@ -23,7 +23,7 @@ import { isatty } from 'node:tty';
23
23
  import EventEmitter from 'node:events';
24
24
  import { c as createBirpc } from './index.Bgo3tNWt.js';
25
25
  import Tinypool$1, { Tinypool } from 'tinypool';
26
- import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.Cd1wvxUM.js';
26
+ import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.DSo_maXz.js';
27
27
  import { MessageChannel } from 'node:worker_threads';
28
28
  import { hasFailed } from '@vitest/runner/utils';
29
29
  import { rootDir } from '../path.js';