vitest 4.0.7 → 4.0.9

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 (65) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/browser.d.ts +2 -2
  3. package/dist/browser.js +2 -2
  4. package/dist/chunks/{base.D3GxgUMI.js → base.CiIV2DDC.js} +71 -36
  5. package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
  6. package/dist/chunks/{browser.d.-LKfRopd.d.ts → browser.d.DnU_kh8a.d.ts} +1 -1
  7. package/dist/chunks/{cac.G9DAn-c7.js → cac.B_NTJoIH.js} +115 -42
  8. package/dist/chunks/{cli-api.Csks4as1.js → cli-api.D48wY175.js} +1863 -845
  9. package/dist/chunks/console.Cf-YriPC.js +146 -0
  10. package/dist/chunks/{coverage.C2LA1DSL.js → coverage.BUlIqJrL.js} +284 -114
  11. package/dist/chunks/{creator.cqqifzG7.js → creator.BzqvXeRE.js} +75 -31
  12. package/dist/chunks/{date.-jtEtIeV.js → date.Bq6ZW5rf.js} +17 -6
  13. package/dist/chunks/{git.BFNcloKD.js → git.Bm2pzPAa.js} +3 -3
  14. package/dist/chunks/{global.d.DxtanrNO.d.ts → global.d.BQDgW9Pr.d.ts} +1 -1
  15. package/dist/chunks/{globals.BGT_RUsD.js → globals.DBrtKPdh.js} +5 -5
  16. package/dist/chunks/index.0kCJoeWi.js +220 -0
  17. package/dist/chunks/{index.CWIFvlX5.js → index.BfmpdV5p.js} +165 -54
  18. package/dist/chunks/{index.RwjEGCQ0.js → index.CGezRSGU.js} +2 -2
  19. package/dist/chunks/{index.DEPqWSIZ.js → index.CPA8jGhR.js} +33 -16
  20. package/dist/chunks/{index.CVpyv-Zg.js → index.kotH7DY7.js} +832 -373
  21. package/dist/chunks/{index.jMQYiEWE.js → index.op2Re5rn.js} +22 -12
  22. package/dist/chunks/{index.Dc3xnDvT.js → index.z7NPOg2E.js} +4 -4
  23. package/dist/chunks/{init-forks.IU-xQ2_X.js → init-forks.aqTzCSR2.js} +14 -4
  24. package/dist/chunks/{init-threads.C_NWvZkU.js → init-threads.C7T0-YMD.js} +1 -1
  25. package/dist/chunks/{init.fmH9J833.js → init.BQhNfT0h.js} +53 -30
  26. package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
  27. package/dist/chunks/{moduleRunner.d.DEkTotCv.d.ts → moduleRunner.d.BxT-OjLR.d.ts} +1 -1
  28. package/dist/chunks/{node.BwAWWjHZ.js → node.Ce0vMQM7.js} +1 -1
  29. package/dist/chunks/{plugin.d.Cpes8Bt6.d.ts → plugin.d.DevON6kQ.d.ts} +1 -1
  30. package/dist/chunks/{reporters.d.CSNcMDxF.d.ts → reporters.d.BQ0wpUaj.d.ts} +6 -5
  31. package/dist/chunks/{rpc.D38ahn14.js → rpc.BytlcPfC.js} +20 -7
  32. package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.Dw1XgX0v.js} +20 -8
  33. package/dist/chunks/{startModuleRunner.Cn7hCL7D.js → startModuleRunner.DLjmA_wU.js} +209 -86
  34. package/dist/chunks/{test.B6aJd6T3.js → test.w5HLbjmU.js} +48 -22
  35. package/dist/chunks/{utils.CG9h5ccR.js → utils.DvEY5TfP.js} +14 -5
  36. package/dist/chunks/{vi.BZvkKVkM.js → vi.CyIUVSoU.js} +267 -117
  37. package/dist/chunks/{vm.BL7_zzOr.js → vm.DXN8eCh2.js} +181 -75
  38. package/dist/chunks/{worker.d.D25zYZ7N.d.ts → worker.d.ZGohxCEd.d.ts} +74 -7
  39. package/dist/cli.js +2 -2
  40. package/dist/config.d.ts +5 -5
  41. package/dist/coverage.d.ts +3 -3
  42. package/dist/coverage.js +1 -1
  43. package/dist/environments.js +1 -1
  44. package/dist/index.d.ts +5 -5
  45. package/dist/index.js +5 -5
  46. package/dist/module-evaluator.d.ts +2 -2
  47. package/dist/module-evaluator.js +88 -38
  48. package/dist/module-runner.js +2 -2
  49. package/dist/node.d.ts +7 -7
  50. package/dist/node.js +17 -13
  51. package/dist/reporters.d.ts +3 -3
  52. package/dist/reporters.js +2 -2
  53. package/dist/runners.js +7 -7
  54. package/dist/snapshot.js +2 -2
  55. package/dist/suite.js +2 -2
  56. package/dist/worker.d.ts +1 -1
  57. package/dist/worker.js +15 -15
  58. package/dist/workers/forks.js +16 -16
  59. package/dist/workers/runVmTests.js +41 -22
  60. package/dist/workers/threads.js +16 -16
  61. package/dist/workers/vmForks.js +11 -11
  62. package/dist/workers/vmThreads.js +11 -11
  63. package/package.json +20 -20
  64. package/dist/chunks/console.CTJL2nuH.js +0 -115
  65. package/dist/chunks/index.Bgo3tNWt.js +0 -176
@@ -1487,11 +1487,11 @@ function resolvePackageTarget(
1487
1487
  * @param {URL} base
1488
1488
  * @returns {boolean}
1489
1489
  */
1490
- function isConditionalExportsMainSugar(exports, packageJsonUrl, base) {
1491
- if (typeof exports === 'string' || Array.isArray(exports)) return true
1492
- if (typeof exports !== 'object' || exports === null) return false
1490
+ function isConditionalExportsMainSugar(exports$1, packageJsonUrl, base) {
1491
+ if (typeof exports$1 === 'string' || Array.isArray(exports$1)) return true
1492
+ if (typeof exports$1 !== 'object' || exports$1 === null) return false
1493
1493
 
1494
- const keys = Object.getOwnPropertyNames(exports);
1494
+ const keys = Object.getOwnPropertyNames(exports$1);
1495
1495
  let isConditionalSugar = false;
1496
1496
  let i = 0;
1497
1497
  let keyIndex = -1;
@@ -1553,19 +1553,19 @@ function packageExportsResolve(
1553
1553
  base,
1554
1554
  conditions
1555
1555
  ) {
1556
- let exports = packageConfig.exports;
1556
+ let exports$1 = packageConfig.exports;
1557
1557
 
1558
- if (isConditionalExportsMainSugar(exports, packageJsonUrl, base)) {
1559
- exports = {'.': exports};
1558
+ if (isConditionalExportsMainSugar(exports$1, packageJsonUrl, base)) {
1559
+ exports$1 = {'.': exports$1};
1560
1560
  }
1561
1561
 
1562
1562
  if (
1563
- own.call(exports, packageSubpath) &&
1563
+ own.call(exports$1, packageSubpath) &&
1564
1564
  !packageSubpath.includes('*') &&
1565
1565
  !packageSubpath.endsWith('/')
1566
1566
  ) {
1567
1567
  // @ts-expect-error: indexable.
1568
- const target = exports[packageSubpath];
1568
+ const target = exports$1[packageSubpath];
1569
1569
  const resolveResult = resolvePackageTarget(
1570
1570
  packageJsonUrl,
1571
1571
  target,
@@ -1586,7 +1586,7 @@ function packageExportsResolve(
1586
1586
 
1587
1587
  let bestMatch = '';
1588
1588
  let bestMatchSubpath = '';
1589
- const keys = Object.getOwnPropertyNames(exports);
1589
+ const keys = Object.getOwnPropertyNames(exports$1);
1590
1590
  let i = -1;
1591
1591
 
1592
1592
  while (++i < keys.length) {
@@ -1630,7 +1630,7 @@ function packageExportsResolve(
1630
1630
 
1631
1631
  if (bestMatch) {
1632
1632
  // @ts-expect-error: indexable.
1633
- const target = /** @type {unknown} */ (exports[bestMatch]);
1633
+ const target = /** @type {unknown} */ (exports$1[bestMatch]);
1634
1634
  const resolveResult = resolvePackageTarget(
1635
1635
  packageJsonUrl,
1636
1636
  target,
@@ -2350,7 +2350,8 @@ const isPackageListed = quansync(function* (name, cwd) {
2350
2350
  isPackageListed.sync;
2351
2351
 
2352
2352
  function getWorkersCountByPercentage(percent) {
2353
- const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length, workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
2353
+ const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length;
2354
+ const workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
2354
2355
  return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
2355
2356
  }
2356
2357
 
@@ -2361,7 +2362,9 @@ class BaseSequencer {
2361
2362
  }
2362
2363
  // async so it can be extended by other sequelizers
2363
2364
  async shard(files) {
2364
- const { config } = this.ctx, { index, count } = config.shard, [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
2365
+ const { config } = this.ctx;
2366
+ const { index, count } = config.shard;
2367
+ const [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
2365
2368
  return [...files].map((spec) => {
2366
2369
  const specPath = resolve(slash(config.root), slash(spec.moduleId))?.slice(config.root.length);
2367
2370
  return {
@@ -2382,25 +2385,35 @@ class BaseSequencer {
2382
2385
  // Isolated run first
2383
2386
  if (a.project.config.isolate && !b.project.config.isolate) return -1;
2384
2387
  if (!a.project.config.isolate && b.project.config.isolate) return 1;
2385
- const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}`, keyB = `${b.project.name}:${relative(this.ctx.config.root, b.moduleId)}`, aState = cache.getFileTestResults(keyA), bState = cache.getFileTestResults(keyB);
2388
+ const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}`;
2389
+ const keyB = `${b.project.name}:${relative(this.ctx.config.root, b.moduleId)}`;
2390
+ const aState = cache.getFileTestResults(keyA);
2391
+ const bState = cache.getFileTestResults(keyB);
2386
2392
  if (!aState || !bState) {
2387
- const statsA = cache.getFileStats(keyA), statsB = cache.getFileStats(keyB);
2393
+ const statsA = cache.getFileStats(keyA);
2394
+ const statsB = cache.getFileStats(keyB);
2395
+ // run unknown first
2396
+ if (!statsA || !statsB) return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0;
2388
2397
  // run larger files first
2389
- return !statsA || !statsB ? !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0 : statsB.size - statsA.size;
2398
+ return statsB.size - statsA.size;
2390
2399
  }
2400
+ // run failed first
2401
+ if (aState.failed && !bState.failed) return -1;
2402
+ if (!aState.failed && bState.failed) return 1;
2391
2403
  // run longer first
2392
- return aState.failed && !bState.failed ? -1 : !aState.failed && bState.failed ? 1 : bState.duration - aState.duration;
2404
+ return bState.duration - aState.duration;
2393
2405
  });
2394
2406
  }
2395
2407
  // Calculate distributed shard range [start, end] distributed equally
2396
2408
  calculateShardRange(filesCount, index, count) {
2397
- const baseShardSize = Math.floor(filesCount / count), remainderTestFilesCount = filesCount % count;
2409
+ const baseShardSize = Math.floor(filesCount / count);
2410
+ const remainderTestFilesCount = filesCount % count;
2398
2411
  if (remainderTestFilesCount >= index) {
2399
- const shardSize = baseShardSize + 1, shardStart = shardSize * (index - 1), shardEnd = shardSize * index;
2400
- return [shardStart, shardEnd];
2412
+ const shardSize = baseShardSize + 1;
2413
+ return [shardSize * (index - 1), shardSize * index];
2401
2414
  }
2402
- const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize, shardEnd = shardStart + baseShardSize;
2403
- return [shardStart, shardEnd];
2415
+ const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
2416
+ return [shardStart, shardStart + baseShardSize];
2404
2417
  }
2405
2418
  }
2406
2419
 
@@ -2419,10 +2432,11 @@ function parseInspector(inspect) {
2419
2432
  if (typeof inspect === "number") return { port: inspect };
2420
2433
  if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
2421
2434
  const [host, port] = inspect.split(":");
2422
- return port ? {
2435
+ if (!port) return { host };
2436
+ return {
2423
2437
  host,
2424
2438
  port: Number(port) || defaultInspectPort
2425
- } : { host };
2439
+ };
2426
2440
  }
2427
2441
  function resolveApiServerConfig(options, defaultPort) {
2428
2442
  let api;
@@ -2440,10 +2454,12 @@ function resolveApiServerConfig(options, defaultPort) {
2440
2454
  return api;
2441
2455
  }
2442
2456
  function resolveInlineWorkerOption(value) {
2443
- return typeof value === "string" && value.trim().endsWith("%") ? getWorkersCountByPercentage(value) : Number(value);
2457
+ if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
2458
+ else return Number(value);
2444
2459
  }
2445
2460
  function resolveConfig$1(vitest, options, viteConfig) {
2446
- const mode = vitest.mode, logger = vitest.logger;
2461
+ const mode = vitest.mode;
2462
+ const logger = vitest.logger;
2447
2463
  if (options.dom) {
2448
2464
  if (viteConfig.test?.environment != null && viteConfig.test.environment !== "happy-dom") logger.console.warn(c.yellow(`${c.inverse(c.yellow(" Vitest "))} Your config.test.environment ("${viteConfig.test.environment}") conflicts with --dom flag ("happy-dom"), ignoring "${viteConfig.test.environment}"`));
2449
2465
  options.environment = "happy-dom";
@@ -2454,18 +2470,30 @@ function resolveConfig$1(vitest, options, viteConfig) {
2454
2470
  root: viteConfig.root,
2455
2471
  mode
2456
2472
  };
2457
- if (options.pool && typeof options.pool !== "string") resolved.pool = options.pool.name, resolved.poolRunner = options.pool;
2458
- if (resolved.pool ??= "forks", resolved.project = toArray(resolved.project), resolved.provide ??= {}, resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "", resolved.color = typeof options.name !== "string" ? options.name?.color : void 0, resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`);
2473
+ if (options.pool && typeof options.pool !== "string") {
2474
+ resolved.pool = options.pool.name;
2475
+ resolved.poolRunner = options.pool;
2476
+ }
2477
+ resolved.pool ??= "forks";
2478
+ resolved.project = toArray(resolved.project);
2479
+ resolved.provide ??= {};
2480
+ resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "";
2481
+ resolved.color = typeof options.name !== "string" ? options.name?.color : void 0;
2482
+ if (resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`);
2459
2483
  const inspector = resolved.inspect || resolved.inspectBrk;
2460
- if (resolved.inspector = {
2484
+ resolved.inspector = {
2461
2485
  ...resolved.inspector,
2462
2486
  ...parseInspector(inspector),
2463
2487
  enabled: !!inspector,
2464
2488
  waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
2465
- }, viteConfig.base !== "/") resolved.base = viteConfig.base;
2466
- if (resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true, options.shard) {
2489
+ };
2490
+ if (viteConfig.base !== "/") resolved.base = viteConfig.base;
2491
+ resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true;
2492
+ if (options.shard) {
2467
2493
  if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
2468
- const [indexString, countString] = options.shard.split("/"), index = Math.abs(Number.parseInt(indexString, 10)), count = Math.abs(Number.parseInt(countString, 10));
2494
+ const [indexString, countString] = options.shard.split("/");
2495
+ const index = Math.abs(Number.parseInt(indexString, 10));
2496
+ const count = Math.abs(Number.parseInt(countString, 10));
2469
2497
  if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
2470
2498
  if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
2471
2499
  resolved.shard = {
@@ -2479,7 +2507,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
2479
2507
  if (!(options.fileParallelism ?? mode !== "benchmark"))
2480
2508
  // ignore user config, parallelism cannot be implemented without limiting workers
2481
2509
  resolved.maxWorkers = 1;
2482
- if (resolved.maxConcurrency === 0) logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`)), resolved.maxConcurrency = configDefaults.maxConcurrency;
2510
+ if (resolved.maxConcurrency === 0) {
2511
+ logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`));
2512
+ resolved.maxConcurrency = configDefaults.maxConcurrency;
2513
+ }
2483
2514
  if (resolved.inspect || resolved.inspectBrk) {
2484
2515
  if (resolved.maxWorkers !== 1) {
2485
2516
  const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
@@ -2498,12 +2529,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
2498
2529
  // open the default browser anyway
2499
2530
  if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
2500
2531
  if (browser.name && instances?.length) {
2532
+ // --browser=chromium filters configs to a single one
2533
+ browser.instances = browser.instances.filter((instance) => instance.browser === browser.name);
2501
2534
  // if `instances` were defined, but now they are empty,
2502
2535
  // let's throw an error because the filter is invalid
2503
- if (browser.instances = browser.instances.filter((instance) => instance.browser === browser.name), !browser.instances.length) throw new Error([`"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`, ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map((c) => c.browser).join(", ")}). Did you mean to use another name?`].join(""));
2536
+ if (!browser.instances.length) throw new Error([`"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`, ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map((c) => c.browser).join(", ")}). Did you mean to use another name?`].join(""));
2504
2537
  }
2505
2538
  }
2506
- const containsChromium = hasBrowserChromium(vitest, resolved), hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
2539
+ const containsChromium = hasBrowserChromium(vitest, resolved);
2540
+ const hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
2507
2541
  // Browser-mode "Chromium" only features:
2508
2542
  if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
2509
2543
  const browserConfig = `
@@ -2515,7 +2549,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
2515
2549
  ],
2516
2550
  },
2517
2551
  }
2518
- `.trim(), preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name, correctExample = `
2552
+ `.trim();
2553
+ const preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name;
2554
+ const correctExample = `
2519
2555
  {
2520
2556
  browser: {
2521
2557
  provider: ${preferredProvider}(),
@@ -2542,12 +2578,29 @@ function resolveConfig$1(vitest, options, viteConfig) {
2542
2578
  throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
2543
2579
  }
2544
2580
  }
2545
- if (resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter), resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
2581
+ resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter);
2582
+ if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
2546
2583
  const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
2547
2584
  if (reportsDirectory === resolved.root || reportsDirectory === process.cwd()) throw new Error(`You cannot set "coverage.reportsDirectory" as ${reportsDirectory}. Vitest needs to be able to remove this directory before test run`);
2548
2585
  }
2549
2586
  if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
2550
- if (resolved.expect ??= {}, resolved.deps ??= {}, resolved.deps.moduleDirectories ??= [], resolved.deps.optimizer ??= {}, resolved.deps.optimizer.ssr ??= {}, resolved.deps.optimizer.ssr.enabled ??= false, resolved.deps.optimizer.client ??= {}, resolved.deps.optimizer.client.enabled ??= false, resolved.deps.web ??= {}, resolved.deps.web.transformAssets ??= true, resolved.deps.web.transformCss ??= true, resolved.deps.web.transformGlobPattern ??= [], resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root)), resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root)), resolved.coverage.exclude = [
2587
+ resolved.expect ??= {};
2588
+ resolved.deps ??= {};
2589
+ resolved.deps.moduleDirectories ??= [];
2590
+ resolved.deps.optimizer ??= {};
2591
+ resolved.deps.optimizer.ssr ??= {};
2592
+ resolved.deps.optimizer.ssr.enabled ??= false;
2593
+ resolved.deps.optimizer.client ??= {};
2594
+ resolved.deps.optimizer.client.enabled ??= false;
2595
+ resolved.deps.web ??= {};
2596
+ resolved.deps.web.transformAssets ??= true;
2597
+ resolved.deps.web.transformCss ??= true;
2598
+ resolved.deps.web.transformGlobPattern ??= [];
2599
+ resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root));
2600
+ resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root));
2601
+ // Add hard-coded default coverage exclusions. These cannot be overidden by user config.
2602
+ // Override original exclude array for cases where user re-uses same object in test.exclude.
2603
+ resolved.coverage.exclude = [
2551
2604
  ...resolved.coverage.exclude,
2552
2605
  ...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
2553
2606
  ...resolved.include,
@@ -2556,28 +2609,45 @@ function resolveConfig$1(vitest, options, viteConfig) {
2556
2609
  "**/virtual:*",
2557
2610
  "**/__x00__*",
2558
2611
  "**/node_modules/**"
2559
- ].filter((pattern) => typeof pattern === "string"), resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles], resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
2612
+ ].filter((pattern) => typeof pattern === "string");
2613
+ resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles];
2614
+ if (resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
2560
2615
  if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
2561
- if (resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments"), resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
2562
- if (resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0, resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
2616
+ resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments");
2617
+ if (resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
2618
+ resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0;
2619
+ if (resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
2620
+ resolved.snapshotFormat.plugins = [];
2563
2621
  // TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
2564
- if (resolved.snapshotFormat.plugins = [], typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
2622
+ if (typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
2565
2623
  }
2566
2624
  const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
2567
- if (resolved.snapshotOptions = {
2625
+ resolved.snapshotOptions = {
2568
2626
  expand: resolved.expandSnapshotDiff ?? false,
2569
2627
  snapshotFormat: resolved.snapshotFormat || {},
2570
2628
  updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
2571
2629
  resolveSnapshotPath: options.resolveSnapshotPath,
2572
2630
  snapshotEnvironment: null
2573
- }, resolved.snapshotSerializers ??= [], resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root)), resolved.forceRerunTriggers.push(...resolved.snapshotSerializers), options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
2574
- if (resolved.execArgv ??= [], resolved.pool ??= "threads", resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
2631
+ };
2632
+ resolved.snapshotSerializers ??= [];
2633
+ resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root));
2634
+ resolved.forceRerunTriggers.push(...resolved.snapshotSerializers);
2635
+ if (options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
2636
+ resolved.execArgv ??= [];
2637
+ resolved.pool ??= "threads";
2638
+ if (resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
2575
2639
  if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
2576
2640
  if (mode === "benchmark") {
2577
2641
  resolved.benchmark = {
2578
2642
  ...benchmarkConfigDefaults,
2579
2643
  ...resolved.benchmark
2580
- }, resolved.coverage.enabled = false, resolved.typecheck.enabled = false, resolved.include = resolved.benchmark.include, resolved.exclude = resolved.benchmark.exclude, resolved.includeSource = resolved.benchmark.includeSource;
2644
+ };
2645
+ // override test config
2646
+ resolved.coverage.enabled = false;
2647
+ resolved.typecheck.enabled = false;
2648
+ resolved.include = resolved.benchmark.include;
2649
+ resolved.exclude = resolved.benchmark.exclude;
2650
+ resolved.includeSource = resolved.benchmark.includeSource;
2581
2651
  const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
2582
2652
  if (reporters.length) resolved.benchmark.reporters = reporters;
2583
2653
  else resolved.benchmark.reporters = ["default"];
@@ -2586,11 +2656,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
2586
2656
  if (options.compare) resolved.benchmark.compare = options.compare;
2587
2657
  if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
2588
2658
  }
2589
- if (typeof resolved.diff === "string") resolved.diff = resolvePath(resolved.diff, resolved.root), resolved.forceRerunTriggers.push(resolved.diff);
2590
- if (resolved.api = {
2659
+ if (typeof resolved.diff === "string") {
2660
+ resolved.diff = resolvePath(resolved.diff, resolved.root);
2661
+ resolved.forceRerunTriggers.push(resolved.diff);
2662
+ }
2663
+ resolved.api = {
2591
2664
  ...resolveApiServerConfig(options, defaultPort),
2592
2665
  token: crypto.randomUUID()
2593
- }, options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
2666
+ };
2667
+ if (options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
2594
2668
  /*
2595
2669
  * Reporters can be defined in many different ways:
2596
2670
  * { reporter: 'json' }
@@ -2619,8 +2693,11 @@ function resolveConfig$1(vitest, options, viteConfig) {
2619
2693
  if (mode !== "benchmark") {
2620
2694
  // @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
2621
2695
  // it is passed down as "vitest --reporter ../reporter.js"
2622
- const reportersFromCLI = resolved.reporter, cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
2623
- return /^\.\.?\//.test(reporter) ? resolve(process.cwd(), reporter) : reporter;
2696
+ const reportersFromCLI = resolved.reporter;
2697
+ const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
2698
+ // ./reporter.js || ../reporter.js, but not .reporters/reporter.js
2699
+ if (/^\.\.?\//.test(reporter)) return resolve(process.cwd(), reporter);
2700
+ return reporter;
2624
2701
  });
2625
2702
  if (cliReporters.length) {
2626
2703
  // When CLI reporters are specified, preserve options from config file
@@ -2634,56 +2711,88 @@ function resolveConfig$1(vitest, options, viteConfig) {
2634
2711
  }
2635
2712
  }
2636
2713
  if (!resolved.reporters.length) {
2714
+ resolved.reporters.push(["default", {}]);
2637
2715
  // also enable github-actions reporter as a default
2638
- if (resolved.reporters.push(["default", {}]), process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
2716
+ if (process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
2639
2717
  }
2640
2718
  if (resolved.changed) resolved.passWithNoTests ??= true;
2641
- if (resolved.css ??= {}, typeof resolved.css === "object") resolved.css.modules ??= {}, resolved.css.modules.classNameStrategy ??= "stable";
2719
+ resolved.css ??= {};
2720
+ if (typeof resolved.css === "object") {
2721
+ resolved.css.modules ??= {};
2722
+ resolved.css.modules.classNameStrategy ??= "stable";
2723
+ }
2642
2724
  if (resolved.cache !== false) {
2643
2725
  if (resolved.cache && typeof resolved.cache.dir === "string") vitest.logger.deprecate(`"cache.dir" is deprecated, use Vite's "cacheDir" instead if you want to change the cache director. Note caches will be written to "cacheDir\/vitest"`);
2644
2726
  resolved.cache = { dir: viteConfig.cacheDir };
2645
2727
  }
2646
- if (resolved.sequence ??= {}, resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
2728
+ resolved.sequence ??= {};
2729
+ if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
2647
2730
  const { files, tests } = resolved.sequence.shuffle;
2648
- resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer, resolved.sequence.shuffle = tests;
2731
+ resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
2732
+ resolved.sequence.shuffle = tests;
2649
2733
  }
2650
2734
  if (!resolved.sequence?.sequencer)
2651
2735
  // CLI flag has higher priority
2652
2736
  resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
2653
- if (resolved.sequence.groupOrder ??= 0, resolved.sequence.hooks ??= "stack", resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
2654
- if (resolved.typecheck = {
2737
+ resolved.sequence.groupOrder ??= 0;
2738
+ resolved.sequence.hooks ??= "stack";
2739
+ if (resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
2740
+ resolved.typecheck = {
2655
2741
  ...configDefaults.typecheck,
2656
2742
  ...resolved.typecheck
2657
- }, resolved.typecheck ??= {}, resolved.typecheck.enabled ??= false, resolved.typecheck.enabled) logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
2658
- if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.isolate ??= resolved.isolate ?? true, resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark", resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI, resolved.browser.commands ??= {}, resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
2743
+ };
2744
+ resolved.typecheck ??= {};
2745
+ resolved.typecheck.enabled ??= false;
2746
+ if (resolved.typecheck.enabled) logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
2747
+ resolved.browser.enabled ??= false;
2748
+ resolved.browser.headless ??= isCI;
2749
+ resolved.browser.isolate ??= resolved.isolate ?? true;
2750
+ resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
2751
+ // disable in headless mode by default, and if CI is detected
2752
+ resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
2753
+ resolved.browser.commands ??= {};
2754
+ if (resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
2659
2755
  if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
2660
- if (resolved.browser.viewport ??= {}, resolved.browser.viewport.width ??= 414, resolved.browser.viewport.height ??= 896, resolved.browser.locators ??= {}, resolved.browser.locators.testIdAttribute ??= "data-testid", typeof resolved.browser.provider === "string") {
2756
+ resolved.browser.viewport ??= {};
2757
+ resolved.browser.viewport.width ??= 414;
2758
+ resolved.browser.viewport.height ??= 896;
2759
+ resolved.browser.locators ??= {};
2760
+ resolved.browser.locators.testIdAttribute ??= "data-testid";
2761
+ if (typeof resolved.browser.provider === "string") {
2661
2762
  const source = `@vitest/browser-${resolved.browser.provider}`;
2662
- throw new TypeError(`The \`browser.provider\` configuration was changed to accept a factory instead of a string. Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/guide/browser/config#provider`);
2763
+ throw new TypeError(`The \`browser.provider\` configuration was changed to accept a factory instead of a string. Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/config/browser/provider`);
2663
2764
  }
2664
2765
  const isPreview = resolved.browser.provider?.name === "preview";
2665
2766
  if (!isPreview && resolved.browser.enabled && provider === "stackblitz") throw new Error(`stackblitz environment does not support the ${resolved.browser.provider?.name} provider. Please, use "@vitest/browser-preview" instead.`);
2666
- if (isPreview && resolved.browser.screenshotFailures === true) console.warn(c.yellow([
2667
- `Browser provider "preview" doesn't support screenshots, `,
2668
- `so "browser.screenshotFailures" option is forcefully disabled. `,
2669
- `Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
2670
- ].join(""))), resolved.browser.screenshotFailures = false;
2671
- else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
2767
+ if (isPreview && resolved.browser.screenshotFailures === true) {
2768
+ console.warn(c.yellow([
2769
+ `Browser provider "preview" doesn't support screenshots, `,
2770
+ `so "browser.screenshotFailures" option is forcefully disabled. `,
2771
+ `Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
2772
+ ].join("")));
2773
+ resolved.browser.screenshotFailures = false;
2774
+ } else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
2672
2775
  if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
2776
+ resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
2673
2777
  // enable includeTaskLocation by default in UI mode
2674
- if (resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort }, resolved.browser.enabled) {
2778
+ if (resolved.browser.enabled) {
2675
2779
  if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
2676
2780
  } else if (resolved.ui) resolved.includeTaskLocation ??= true;
2677
2781
  if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
2678
2782
  if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
2679
2783
  if (toArray(resolved.reporters).some((reporter) => {
2680
- return Array.isArray(reporter) ? reporter[0] === "html" : false;
2784
+ if (Array.isArray(reporter)) return reporter[0] === "html";
2785
+ return false;
2681
2786
  })) resolved.includeTaskLocation ??= true;
2682
- if (resolved.server ??= {}, resolved.server.deps ??= {}, resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
2787
+ resolved.server ??= {};
2788
+ resolved.server.deps ??= {};
2789
+ if (resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
2683
2790
  const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
2684
2791
  resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
2685
2792
  }
2686
- return resolved.testTimeout ??= resolved.browser.enabled ? 3e4 : 5e3, resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4, resolved;
2793
+ resolved.testTimeout ??= resolved.browser.enabled ? 3e4 : 5e3;
2794
+ resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4;
2795
+ return resolved;
2687
2796
  }
2688
2797
  function isBrowserEnabled(config) {
2689
2798
  return Boolean(config.browser?.enabled);
@@ -2701,21 +2810,32 @@ function resolveCoverageReporters(configReporters) {
2701
2810
  return resolvedReporters;
2702
2811
  }
2703
2812
  function isChromiumName(provider, name) {
2704
- return provider === "playwright" ? name === "chromium" : name === "chrome" || name === "edge";
2813
+ if (provider === "playwright") return name === "chromium";
2814
+ return name === "chrome" || name === "edge";
2705
2815
  }
2706
2816
  function hasBrowserChromium(vitest, config) {
2707
2817
  const browser = config.browser;
2708
- return !browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled ? false : browser.name ? isChromiumName(browser.provider.name, browser.name) : browser.instances ? browser.instances.some((instance) => {
2818
+ if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
2819
+ if (browser.name) return isChromiumName(browser.provider.name, browser.name);
2820
+ if (!browser.instances) return false;
2821
+ return browser.instances.some((instance) => {
2709
2822
  const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
2710
- return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : false;
2711
- }) : false;
2823
+ // browser config is filtered out
2824
+ if (!vitest.matchesProjectFilter(name)) return false;
2825
+ return isChromiumName(browser.provider.name, instance.browser);
2826
+ });
2712
2827
  }
2713
2828
  function hasOnlyBrowserChromium(vitest, config) {
2714
2829
  const browser = config.browser;
2715
- return !browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled ? false : browser.name ? isChromiumName(browser.provider.name, browser.name) : browser.instances ? browser.instances.every((instance) => {
2830
+ if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
2831
+ if (browser.name) return isChromiumName(browser.provider.name, browser.name);
2832
+ if (!browser.instances) return false;
2833
+ return browser.instances.every((instance) => {
2716
2834
  const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
2717
- return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : true;
2718
- }) : false;
2835
+ // browser config is filtered out
2836
+ if (!vitest.matchesProjectFilter(name)) return true;
2837
+ return isChromiumName(browser.provider.name, instance.browser);
2838
+ });
2719
2839
  }
2720
2840
 
2721
2841
  const THRESHOLD_KEYS = [
@@ -2723,11 +2843,14 @@ const THRESHOLD_KEYS = [
2723
2843
  "functions",
2724
2844
  "statements",
2725
2845
  "branches"
2726
- ], GLOBAL_THRESHOLDS_KEY = "global", DEFAULT_PROJECT = Symbol.for("default-project");
2846
+ ];
2847
+ const GLOBAL_THRESHOLDS_KEY = "global";
2848
+ const DEFAULT_PROJECT = Symbol.for("default-project");
2727
2849
  let uniqueId = 0;
2728
2850
  async function getCoverageProvider(options, loader) {
2729
2851
  const coverageModule = await resolveCoverageProviderModule(options, loader);
2730
- return coverageModule ? coverageModule.getProvider() : null;
2852
+ if (coverageModule) return coverageModule.getProvider();
2853
+ return null;
2731
2854
  }
2732
2855
  class BaseCoverageProvider {
2733
2856
  ctx;
@@ -2740,7 +2863,8 @@ class BaseCoverageProvider {
2740
2863
  coverageFilesDirectory;
2741
2864
  roots = [];
2742
2865
  _initialize(ctx) {
2743
- if (this.ctx = ctx, ctx.version !== this.version) ctx.logger.warn(c.yellow(`Loaded ${c.inverse(c.yellow(` vitest@${ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/coverage-${this.name}@${this.version} `))}.
2866
+ this.ctx = ctx;
2867
+ if (ctx.version !== this.version) ctx.logger.warn(c.yellow(`Loaded ${c.inverse(c.yellow(` vitest@${ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/coverage-${this.name}@${this.version} `))}.
2744
2868
  Running mixed versions is not supported and may lead into bugs
2745
2869
  Update your dependencies and make sure the versions match.`));
2746
2870
  const config = ctx._coverageOptions;
@@ -2758,25 +2882,34 @@ Update your dependencies and make sure the versions match.`));
2758
2882
  statements: config.thresholds["100"] ? 100 : config.thresholds.statements
2759
2883
  }
2760
2884
  };
2761
- const shard = this.ctx.config.shard, tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
2885
+ const shard = this.ctx.config.shard;
2886
+ const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
2887
+ this.coverageFilesDirectory = resolve(this.options.reportsDirectory, tempDirectory);
2762
2888
  // If --project filter is set pick only roots of resolved projects
2763
- this.coverageFilesDirectory = resolve(this.options.reportsDirectory, tempDirectory), this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
2889
+ this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
2764
2890
  }
2765
2891
  /**
2766
2892
  * Check if file matches `coverage.include` but not `coverage.exclude`
2767
2893
  */
2768
2894
  isIncluded(_filename, root) {
2769
- const roots = root ? [root] : this.roots, filename = slash(_filename), cacheHit = this.globCache.get(filename);
2895
+ const roots = root ? [root] : this.roots;
2896
+ const filename = slash(_filename);
2897
+ const cacheHit = this.globCache.get(filename);
2770
2898
  if (cacheHit !== void 0) return cacheHit;
2771
2899
  // File outside project root with default allowExternal
2772
- if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) return this.globCache.set(filename, false), false;
2900
+ if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) {
2901
+ this.globCache.set(filename, false);
2902
+ return false;
2903
+ }
2773
2904
  // By default `coverage.include` matches all files, except "coverage.exclude"
2774
- const glob = this.options.include || "**", included = pm.isMatch(filename, glob, {
2905
+ const glob = this.options.include || "**";
2906
+ const included = pm.isMatch(filename, glob, {
2775
2907
  contains: true,
2776
2908
  dot: true,
2777
2909
  ignore: this.options.exclude
2778
2910
  });
2779
- return this.globCache.set(filename, included), included;
2911
+ this.globCache.set(filename, included);
2912
+ return included;
2780
2913
  }
2781
2914
  async getUntestedFilesByRoot(testedFiles, include, root) {
2782
2915
  let includedFiles = await glob(include, {
@@ -2786,7 +2919,9 @@ Update your dependencies and make sure the versions match.`));
2786
2919
  dot: true,
2787
2920
  onlyFiles: true
2788
2921
  });
2789
- if (includedFiles = includedFiles.filter((file) => this.isIncluded(file, root)), this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
2922
+ // Run again through picomatch as tinyglobby's exclude pattern is different ({ "exclude": ["math"] } should ignore "src/math.ts")
2923
+ includedFiles = includedFiles.filter((file) => this.isIncluded(file, root));
2924
+ if (this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
2790
2925
  return includedFiles.map((file) => slash(path.resolve(root, file)));
2791
2926
  }
2792
2927
  async getUntestedFiles(testedFiles) {
@@ -2817,49 +2952,66 @@ Update your dependencies and make sure the versions match.`));
2817
2952
  force: true,
2818
2953
  maxRetries: 10
2819
2954
  });
2820
- await promises.mkdir(this.coverageFilesDirectory, { recursive: true }), this.coverageFiles = /* @__PURE__ */ new Map(), this.pendingPromises = [];
2955
+ await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
2956
+ this.coverageFiles = /* @__PURE__ */ new Map();
2957
+ this.pendingPromises = [];
2821
2958
  }
2822
2959
  onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
2823
2960
  if (!coverage) return;
2824
2961
  let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
2825
- if (!entry) entry = {}, this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
2826
- const testFilenames = testFiles.join(), filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
2962
+ if (!entry) {
2963
+ entry = {};
2964
+ this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
2965
+ }
2966
+ const testFilenames = testFiles.join();
2967
+ const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
2968
+ entry[environment] ??= {};
2827
2969
  // If there's a result from previous run, overwrite it
2828
- entry[environment] ??= {}, entry[environment][testFilenames] = filename;
2970
+ entry[environment][testFilenames] = filename;
2829
2971
  const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
2830
2972
  this.pendingPromises.push(promise);
2831
2973
  }
2832
2974
  async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
2833
2975
  let index = 0;
2834
2976
  const total = this.pendingPromises.length;
2835
- await Promise.all(this.pendingPromises), this.pendingPromises = [];
2977
+ await Promise.all(this.pendingPromises);
2978
+ this.pendingPromises = [];
2836
2979
  for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
2837
- const filenames = Object.values(coverageByTestfiles), project = this.ctx.getProjectByName(projectName);
2980
+ const filenames = Object.values(coverageByTestfiles);
2981
+ const project = this.ctx.getProjectByName(projectName);
2838
2982
  for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
2839
- if (onDebug.enabled) index += chunk.length, onDebug(`Reading coverage results ${index}/${total}`);
2983
+ if (onDebug.enabled) {
2984
+ index += chunk.length;
2985
+ onDebug(`Reading coverage results ${index}/${total}`);
2986
+ }
2840
2987
  await Promise.all(chunk.map(async (filename) => {
2841
- const contents = await promises.readFile(filename, "utf-8"), coverage = JSON.parse(contents);
2842
- onFileRead(coverage);
2988
+ const contents = await promises.readFile(filename, "utf-8");
2989
+ onFileRead(JSON.parse(contents));
2843
2990
  }));
2844
2991
  }
2845
2992
  await onFinished(project, environment);
2846
2993
  }
2847
2994
  }
2848
2995
  async cleanAfterRun() {
2996
+ this.coverageFiles = /* @__PURE__ */ new Map();
2997
+ await promises.rm(this.coverageFilesDirectory, { recursive: true });
2849
2998
  // Remove empty reports directory, e.g. when only text-reporter is used
2850
- if (this.coverageFiles = /* @__PURE__ */ new Map(), await promises.rm(this.coverageFilesDirectory, { recursive: true }), readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
2999
+ if (readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
2851
3000
  }
2852
3001
  async onTestFailure() {
2853
3002
  if (!this.options.reportOnFailure) await this.cleanAfterRun();
2854
3003
  }
2855
3004
  async reportCoverage(coverageMap, { allTestsRun }) {
2856
- if (await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun), !(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
3005
+ await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
3006
+ if (!(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
2857
3007
  }
2858
3008
  async reportThresholds(coverageMap, allTestsRun) {
2859
3009
  const resolvedThresholds = this.resolveThresholds(coverageMap);
2860
- if (this.checkThresholds(resolvedThresholds), this.options.thresholds?.autoUpdate && allTestsRun) {
3010
+ this.checkThresholds(resolvedThresholds);
3011
+ if (this.options.thresholds?.autoUpdate && allTestsRun) {
2861
3012
  if (!this.ctx.vite.config.configFile) throw new Error("Missing configurationFile. The \"coverage.thresholds.autoUpdate\" can only be enabled when configuration file is used.");
2862
- const configFilePath = this.ctx.vite.config.configFile, configModule = await this.parseConfigModule(configFilePath);
3013
+ const configFilePath = this.ctx.vite.config.configFile;
3014
+ const configModule = await this.parseConfigModule(configFilePath);
2863
3015
  await this.updateThresholds({
2864
3016
  thresholds: resolvedThresholds,
2865
3017
  configurationFile: configModule,
@@ -2873,10 +3025,16 @@ Update your dependencies and make sure the versions match.`));
2873
3025
  * for specific files defined by glob pattern or global for all other files.
2874
3026
  */
2875
3027
  resolveThresholds(coverageMap) {
2876
- const resolvedThresholds = [], files = coverageMap.files(), globalCoverageMap = this.createCoverageMap();
3028
+ const resolvedThresholds = [];
3029
+ const files = coverageMap.files();
3030
+ const globalCoverageMap = this.createCoverageMap();
2877
3031
  for (const key of Object.keys(this.options.thresholds)) {
2878
3032
  if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
2879
- const glob = key, globThresholds = resolveGlobThresholds(this.options.thresholds[glob]), globCoverageMap = this.createCoverageMap(), matcher = pm(glob), matchingFiles = files.filter((file) => matcher(relative(this.ctx.config.root, file)));
3033
+ const glob = key;
3034
+ const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
3035
+ const globCoverageMap = this.createCoverageMap();
3036
+ const matcher = pm(glob);
3037
+ const matchingFiles = files.filter((file) => matcher(relative(this.ctx.config.root, file)));
2880
3038
  for (const file of matchingFiles) {
2881
3039
  const fileCoverage = coverageMap.fileCoverageFor(file);
2882
3040
  globCoverageMap.addFileCoverage(fileCoverage);
@@ -2892,7 +3050,7 @@ Update your dependencies and make sure the versions match.`));
2892
3050
  const fileCoverage = coverageMap.fileCoverageFor(file);
2893
3051
  globalCoverageMap.addFileCoverage(fileCoverage);
2894
3052
  }
2895
- return resolvedThresholds.unshift({
3053
+ resolvedThresholds.unshift({
2896
3054
  name: GLOBAL_THRESHOLDS_KEY,
2897
3055
  coverageMap: globalCoverageMap,
2898
3056
  thresholds: {
@@ -2901,7 +3059,8 @@ Update your dependencies and make sure the versions match.`));
2901
3059
  lines: this.options.thresholds?.lines,
2902
3060
  statements: this.options.thresholds?.statements
2903
3061
  }
2904
- }), resolvedThresholds;
3062
+ });
3063
+ return resolvedThresholds;
2905
3064
  }
2906
3065
  /**
2907
3066
  * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
@@ -2939,7 +3098,8 @@ Update your dependencies and make sure the versions match.`));
2939
3098
  this.ctx.logger.error(errorMessage);
2940
3099
  }
2941
3100
  } else {
2942
- const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered, absoluteThreshold = threshold * -1;
3101
+ const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered;
3102
+ const absoluteThreshold = threshold * -1;
2943
3103
  if (uncovered > absoluteThreshold) {
2944
3104
  process.exitCode = 1;
2945
3105
  /**
@@ -2963,7 +3123,8 @@ Update your dependencies and make sure the versions match.`));
2963
3123
  const config = resolveConfig(configurationFile);
2964
3124
  assertConfigurationModule(config);
2965
3125
  for (const { coverageMap, thresholds, name } of allThresholds) {
2966
- const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()], thresholdsToUpdate = [];
3126
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()];
3127
+ const thresholdsToUpdate = [];
2967
3128
  for (const key of THRESHOLD_KEYS) {
2968
3129
  const threshold = thresholds[key] ?? 100;
2969
3130
  /**
@@ -2974,7 +3135,8 @@ Update your dependencies and make sure the versions match.`));
2974
3135
  const actual = Math.min(...summaries.map((summary) => summary[key].pct));
2975
3136
  if (actual > threshold) thresholdsToUpdate.push([key, actual]);
2976
3137
  } else {
2977
- const absoluteThreshold = threshold * -1, actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
3138
+ const absoluteThreshold = threshold * -1;
3139
+ const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
2978
3140
  if (actual < absoluteThreshold) {
2979
3141
  // If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
2980
3142
  const updatedThreshold = actual === 0 ? 100 : actual * -1;
@@ -2994,7 +3156,10 @@ Update your dependencies and make sure the versions match.`));
2994
3156
  }
2995
3157
  }
2996
3158
  }
2997
- if (updatedThresholds) this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds."), onUpdate();
3159
+ if (updatedThresholds) {
3160
+ this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
3161
+ onUpdate();
3162
+ }
2998
3163
  }
2999
3164
  async mergeReports(coverageMaps) {
3000
3165
  const coverageMap = this.createCoverageMap();
@@ -3006,8 +3171,10 @@ Update your dependencies and make sure the versions match.`));
3006
3171
  }
3007
3172
  toSlices(array, size) {
3008
3173
  return array.reduce((chunks, item) => {
3009
- const index = Math.max(0, chunks.length - 1), lastChunk = chunks[index] || [];
3010
- if (chunks[index] = lastChunk, lastChunk.length >= size) chunks.push([item]);
3174
+ const index = Math.max(0, chunks.length - 1);
3175
+ const lastChunk = chunks[index] || [];
3176
+ chunks[index] = lastChunk;
3177
+ if (lastChunk.length >= size) chunks.push([item]);
3011
3178
  else lastChunk.push(item);
3012
3179
  return chunks;
3013
3180
  }, []);
@@ -3048,12 +3215,14 @@ Update your dependencies and make sure the versions match.`));
3048
3215
  * Narrow down `unknown` glob thresholds to resolved ones
3049
3216
  */
3050
3217
  function resolveGlobThresholds(thresholds) {
3051
- return !thresholds || typeof thresholds !== "object" ? {} : 100 in thresholds && thresholds[100] === true ? {
3218
+ if (!thresholds || typeof thresholds !== "object") return {};
3219
+ if (100 in thresholds && thresholds[100] === true) return {
3052
3220
  lines: 100,
3053
3221
  branches: 100,
3054
3222
  functions: 100,
3055
3223
  statements: 100
3056
- } : {
3224
+ };
3225
+ return {
3057
3226
  lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
3058
3227
  branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
3059
3228
  functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
@@ -3079,7 +3248,8 @@ function resolveConfig(configModule) {
3079
3248
  if (config) return config;
3080
3249
  // "export default mergeConfig(..., defineConfig(...))"
3081
3250
  if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
3082
- if (config = resolveMergeConfig(mod), config) return config;
3251
+ config = resolveMergeConfig(mod);
3252
+ if (config) return config;
3083
3253
  }
3084
3254
  } catch (error) {
3085
3255
  // Reduce magicast's verbose errors to readable ones