vitest 4.0.6 → 4.0.8

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.BgTO2qAg.js +156 -0
  5. package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
  6. package/dist/chunks/{browser.d.ScGeWTou.d.ts → browser.d.DTTM2PTh.d.ts} +1 -1
  7. package/dist/chunks/{cac.BBqWH4nd.js → cac.CfkWq8Qy.js} +117 -43
  8. package/dist/chunks/{cli-api.UL3SwFUb.js → cli-api.BQ-bjcRi.js} +1870 -847
  9. package/dist/chunks/console.Cf-YriPC.js +146 -0
  10. package/dist/chunks/{coverage.DuCn_Tmx.js → coverage.NVjCOln1.js} +281 -103
  11. package/dist/chunks/{creator.cqqifzG7.js → creator.fzVyoMf3.js} +74 -30
  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.DdOkMiVb.d.ts → global.d.DVdCfKp5.d.ts} +1 -1
  15. package/dist/chunks/{globals.BGT_RUsD.js → globals.DOh96BiR.js} +5 -5
  16. package/dist/chunks/{resolveSnapshotEnvironment.BZzLjzkh.js → index.BY4-tcno.js} +42 -25
  17. package/dist/chunks/{index.Bgo3tNWt.js → index.DAL392Ss.js} +40 -15
  18. package/dist/chunks/{index.RwjEGCQ0.js → index.DIFZf73e.js} +2 -2
  19. package/dist/chunks/{index.DV0mQLEO.js → index.DfKyPFVi.js} +195 -64
  20. package/dist/chunks/{index.BL8Hg4Uk.js → index.kotH7DY7.js} +837 -380
  21. package/dist/chunks/{index.CpdwpN7L.js → index.op2Re5rn.js} +22 -12
  22. package/dist/chunks/{init-forks.CSGFj9zN.js → init-forks.2hx7cf78.js} +16 -5
  23. package/dist/chunks/{init-threads.CIJLeFO8.js → init-threads.Cm4OCIWA.js} +3 -2
  24. package/dist/chunks/{init.DUeOfNO9.js → init.DMDG-idf.js} +124 -54
  25. package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
  26. package/dist/chunks/{moduleRunner.d.TP-w6tIQ.d.ts → moduleRunner.d.CzOZ_4wC.d.ts} +1 -1
  27. package/dist/chunks/{node.BwAWWjHZ.js → node.Ce0vMQM7.js} +1 -1
  28. package/dist/chunks/{plugin.d.lctzD3Wk.d.ts → plugin.d.D4RrtywJ.d.ts} +1 -1
  29. package/dist/chunks/{reporters.d.PEs0tXod.d.ts → reporters.d.Da1D1VbQ.d.ts} +19 -9
  30. package/dist/chunks/rpc.BUV7uWKJ.js +76 -0
  31. package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.LGjNSzXp.js} +20 -8
  32. package/dist/chunks/{startModuleRunner.Di-EZqh0.js → startModuleRunner.BOmUtLIO.js} +228 -105
  33. package/dist/chunks/{test.CnspO-X4.js → test.ClrAtjMv.js} +48 -22
  34. package/dist/chunks/{utils.CG9h5ccR.js → utils.DvEY5TfP.js} +14 -5
  35. package/dist/chunks/{vi.BZvkKVkM.js → vi.Bgcdy3bQ.js} +261 -111
  36. package/dist/chunks/{vm.Co_lR2NL.js → vm.BIkCDs68.js} +177 -70
  37. package/dist/chunks/{worker.d.B4Hthdvt.d.ts → worker.d.DadbA89M.d.ts} +52 -6
  38. package/dist/cli.js +2 -2
  39. package/dist/config.d.ts +5 -5
  40. package/dist/coverage.d.ts +3 -3
  41. package/dist/coverage.js +1 -1
  42. package/dist/environments.js +2 -1
  43. package/dist/index.d.ts +5 -5
  44. package/dist/index.js +5 -5
  45. package/dist/module-evaluator.d.ts +2 -2
  46. package/dist/module-evaluator.js +85 -35
  47. package/dist/module-runner.js +2 -2
  48. package/dist/node.d.ts +7 -7
  49. package/dist/node.js +16 -12
  50. package/dist/reporters.d.ts +3 -3
  51. package/dist/reporters.js +2 -2
  52. package/dist/runners.js +7 -7
  53. package/dist/snapshot.js +2 -2
  54. package/dist/suite.js +2 -2
  55. package/dist/worker.d.ts +2 -1
  56. package/dist/worker.js +27 -27
  57. package/dist/workers/forks.js +34 -31
  58. package/dist/workers/runVmTests.js +41 -22
  59. package/dist/workers/threads.js +34 -31
  60. package/dist/workers/vmForks.js +14 -14
  61. package/dist/workers/vmThreads.js +14 -14
  62. package/package.json +20 -20
  63. package/dist/chunks/base.BAf_bYeI.js +0 -128
  64. package/dist/chunks/console.CTJL2nuH.js +0 -115
  65. package/dist/chunks/rpc.Dv1Jt3i2.js +0 -66
@@ -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 {
@@ -2374,25 +2377,43 @@ class BaseSequencer {
2374
2377
  async sort(files) {
2375
2378
  const cache = this.ctx.cache;
2376
2379
  return [...files].sort((a, b) => {
2377
- 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);
2380
+ // "sequence.groupOrder" is higher priority
2381
+ const groupOrderDiff = a.project.config.sequence.groupOrder - b.project.config.sequence.groupOrder;
2382
+ if (groupOrderDiff !== 0) return groupOrderDiff;
2383
+ // Projects run sequential
2384
+ if (a.project.name !== b.project.name) return a.project.name < b.project.name ? -1 : 1;
2385
+ // Isolated run first
2386
+ if (a.project.config.isolate && !b.project.config.isolate) return -1;
2387
+ if (!a.project.config.isolate && b.project.config.isolate) return 1;
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);
2378
2392
  if (!aState || !bState) {
2379
- 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;
2380
2397
  // run larger files first
2381
- return !statsA || !statsB ? !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0 : statsB.size - statsA.size;
2398
+ return statsB.size - statsA.size;
2382
2399
  }
2400
+ // run failed first
2401
+ if (aState.failed && !bState.failed) return -1;
2402
+ if (!aState.failed && bState.failed) return 1;
2383
2403
  // run longer first
2384
- return aState.failed && !bState.failed ? -1 : !aState.failed && bState.failed ? 1 : bState.duration - aState.duration;
2404
+ return bState.duration - aState.duration;
2385
2405
  });
2386
2406
  }
2387
2407
  // Calculate distributed shard range [start, end] distributed equally
2388
2408
  calculateShardRange(filesCount, index, count) {
2389
- const baseShardSize = Math.floor(filesCount / count), remainderTestFilesCount = filesCount % count;
2409
+ const baseShardSize = Math.floor(filesCount / count);
2410
+ const remainderTestFilesCount = filesCount % count;
2390
2411
  if (remainderTestFilesCount >= index) {
2391
- const shardSize = baseShardSize + 1, shardStart = shardSize * (index - 1), shardEnd = shardSize * index;
2392
- return [shardStart, shardEnd];
2412
+ const shardSize = baseShardSize + 1;
2413
+ return [shardSize * (index - 1), shardSize * index];
2393
2414
  }
2394
- const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize, shardEnd = shardStart + baseShardSize;
2395
- return [shardStart, shardEnd];
2415
+ const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
2416
+ return [shardStart, shardStart + baseShardSize];
2396
2417
  }
2397
2418
  }
2398
2419
 
@@ -2411,10 +2432,11 @@ function parseInspector(inspect) {
2411
2432
  if (typeof inspect === "number") return { port: inspect };
2412
2433
  if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
2413
2434
  const [host, port] = inspect.split(":");
2414
- return port ? {
2435
+ if (!port) return { host };
2436
+ return {
2415
2437
  host,
2416
2438
  port: Number(port) || defaultInspectPort
2417
- } : { host };
2439
+ };
2418
2440
  }
2419
2441
  function resolveApiServerConfig(options, defaultPort) {
2420
2442
  let api;
@@ -2432,10 +2454,12 @@ function resolveApiServerConfig(options, defaultPort) {
2432
2454
  return api;
2433
2455
  }
2434
2456
  function resolveInlineWorkerOption(value) {
2435
- 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);
2436
2459
  }
2437
2460
  function resolveConfig$1(vitest, options, viteConfig) {
2438
- const mode = vitest.mode, logger = vitest.logger;
2461
+ const mode = vitest.mode;
2462
+ const logger = vitest.logger;
2439
2463
  if (options.dom) {
2440
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}"`));
2441
2465
  options.environment = "happy-dom";
@@ -2446,18 +2470,30 @@ function resolveConfig$1(vitest, options, viteConfig) {
2446
2470
  root: viteConfig.root,
2447
2471
  mode
2448
2472
  };
2449
- if (options.pool && typeof options.pool !== "string") resolved.pool = options.pool.name, resolved.poolRunner = options.pool;
2450
- 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.`);
2451
2483
  const inspector = resolved.inspect || resolved.inspectBrk;
2452
- if (resolved.inspector = {
2484
+ resolved.inspector = {
2453
2485
  ...resolved.inspector,
2454
2486
  ...parseInspector(inspector),
2455
2487
  enabled: !!inspector,
2456
2488
  waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
2457
- }, viteConfig.base !== "/") resolved.base = viteConfig.base;
2458
- 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) {
2459
2493
  if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
2460
- 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));
2461
2497
  if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
2462
2498
  if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
2463
2499
  resolved.shard = {
@@ -2471,7 +2507,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
2471
2507
  if (!(options.fileParallelism ?? mode !== "benchmark"))
2472
2508
  // ignore user config, parallelism cannot be implemented without limiting workers
2473
2509
  resolved.maxWorkers = 1;
2474
- 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
+ }
2475
2514
  if (resolved.inspect || resolved.inspectBrk) {
2476
2515
  if (resolved.maxWorkers !== 1) {
2477
2516
  const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
@@ -2490,12 +2529,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
2490
2529
  // open the default browser anyway
2491
2530
  if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
2492
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);
2493
2534
  // if `instances` were defined, but now they are empty,
2494
2535
  // let's throw an error because the filter is invalid
2495
- 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(""));
2496
2537
  }
2497
2538
  }
2498
- const containsChromium = hasBrowserChromium(vitest, resolved), hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
2539
+ const containsChromium = hasBrowserChromium(vitest, resolved);
2540
+ const hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
2499
2541
  // Browser-mode "Chromium" only features:
2500
2542
  if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
2501
2543
  const browserConfig = `
@@ -2507,7 +2549,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
2507
2549
  ],
2508
2550
  },
2509
2551
  }
2510
- `.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 = `
2511
2555
  {
2512
2556
  browser: {
2513
2557
  provider: ${preferredProvider}(),
@@ -2534,12 +2578,29 @@ function resolveConfig$1(vitest, options, viteConfig) {
2534
2578
  throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
2535
2579
  }
2536
2580
  }
2537
- 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) {
2538
2583
  const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
2539
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`);
2540
2585
  }
2541
2586
  if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
2542
- 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 = [
2543
2604
  ...resolved.coverage.exclude,
2544
2605
  ...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
2545
2606
  ...resolved.include,
@@ -2548,28 +2609,45 @@ function resolveConfig$1(vitest, options, viteConfig) {
2548
2609
  "**/virtual:*",
2549
2610
  "**/__x00__*",
2550
2611
  "**/node_modules/**"
2551
- ].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);
2552
2615
  if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
2553
- if (resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments"), resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
2554
- 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 = [];
2555
2621
  // TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
2556
- 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.`);
2557
2623
  }
2558
2624
  const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
2559
- if (resolved.snapshotOptions = {
2625
+ resolved.snapshotOptions = {
2560
2626
  expand: resolved.expandSnapshotDiff ?? false,
2561
2627
  snapshotFormat: resolved.snapshotFormat || {},
2562
2628
  updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
2563
2629
  resolveSnapshotPath: options.resolveSnapshotPath,
2564
2630
  snapshotEnvironment: null
2565
- }, resolved.snapshotSerializers ??= [], resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root)), resolved.forceRerunTriggers.push(...resolved.snapshotSerializers), options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
2566
- 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;
2567
2639
  if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
2568
2640
  if (mode === "benchmark") {
2569
2641
  resolved.benchmark = {
2570
2642
  ...benchmarkConfigDefaults,
2571
2643
  ...resolved.benchmark
2572
- }, 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;
2573
2651
  const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
2574
2652
  if (reporters.length) resolved.benchmark.reporters = reporters;
2575
2653
  else resolved.benchmark.reporters = ["default"];
@@ -2578,11 +2656,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
2578
2656
  if (options.compare) resolved.benchmark.compare = options.compare;
2579
2657
  if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
2580
2658
  }
2581
- if (typeof resolved.diff === "string") resolved.diff = resolvePath(resolved.diff, resolved.root), resolved.forceRerunTriggers.push(resolved.diff);
2582
- 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 = {
2583
2664
  ...resolveApiServerConfig(options, defaultPort),
2584
2665
  token: crypto.randomUUID()
2585
- }, 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));
2586
2668
  /*
2587
2669
  * Reporters can be defined in many different ways:
2588
2670
  * { reporter: 'json' }
@@ -2611,8 +2693,11 @@ function resolveConfig$1(vitest, options, viteConfig) {
2611
2693
  if (mode !== "benchmark") {
2612
2694
  // @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
2613
2695
  // it is passed down as "vitest --reporter ../reporter.js"
2614
- const reportersFromCLI = resolved.reporter, cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
2615
- 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;
2616
2701
  });
2617
2702
  if (cliReporters.length) {
2618
2703
  // When CLI reporters are specified, preserve options from config file
@@ -2626,56 +2711,88 @@ function resolveConfig$1(vitest, options, viteConfig) {
2626
2711
  }
2627
2712
  }
2628
2713
  if (!resolved.reporters.length) {
2714
+ resolved.reporters.push(["default", {}]);
2629
2715
  // also enable github-actions reporter as a default
2630
- 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", {}]);
2631
2717
  }
2632
2718
  if (resolved.changed) resolved.passWithNoTests ??= true;
2633
- 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
+ }
2634
2724
  if (resolved.cache !== false) {
2635
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"`);
2636
2726
  resolved.cache = { dir: viteConfig.cacheDir };
2637
2727
  }
2638
- if (resolved.sequence ??= {}, resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
2728
+ resolved.sequence ??= {};
2729
+ if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
2639
2730
  const { files, tests } = resolved.sequence.shuffle;
2640
- resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer, resolved.sequence.shuffle = tests;
2731
+ resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
2732
+ resolved.sequence.shuffle = tests;
2641
2733
  }
2642
2734
  if (!resolved.sequence?.sequencer)
2643
2735
  // CLI flag has higher priority
2644
2736
  resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
2645
- if (resolved.sequence.groupOrder ??= 0, resolved.sequence.hooks ??= "stack", resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
2646
- 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 = {
2647
2741
  ...configDefaults.typecheck,
2648
2742
  ...resolved.typecheck
2649
- }, 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."));
2650
- if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.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);
2651
2755
  if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
2652
- 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") {
2653
2762
  const source = `@vitest/browser-${resolved.browser.provider}`;
2654
- 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`);
2655
2764
  }
2656
2765
  const isPreview = resolved.browser.provider?.name === "preview";
2657
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.`);
2658
- if (isPreview && resolved.browser.screenshotFailures === true) console.warn(c.yellow([
2659
- `Browser provider "preview" doesn't support screenshots, `,
2660
- `so "browser.screenshotFailures" option is forcefully disabled. `,
2661
- `Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
2662
- ].join(""))), resolved.browser.screenshotFailures = false;
2663
- 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;
2664
2775
  if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
2776
+ resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
2665
2777
  // enable includeTaskLocation by default in UI mode
2666
- if (resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort }, resolved.browser.enabled) {
2778
+ if (resolved.browser.enabled) {
2667
2779
  if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
2668
2780
  } else if (resolved.ui) resolved.includeTaskLocation ??= true;
2669
2781
  if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
2670
2782
  if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
2671
2783
  if (toArray(resolved.reporters).some((reporter) => {
2672
- return Array.isArray(reporter) ? reporter[0] === "html" : false;
2784
+ if (Array.isArray(reporter)) return reporter[0] === "html";
2785
+ return false;
2673
2786
  })) resolved.includeTaskLocation ??= true;
2674
- 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) {
2675
2790
  const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
2676
2791
  resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
2677
2792
  }
2678
- 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;
2679
2796
  }
2680
2797
  function isBrowserEnabled(config) {
2681
2798
  return Boolean(config.browser?.enabled);
@@ -2693,21 +2810,32 @@ function resolveCoverageReporters(configReporters) {
2693
2810
  return resolvedReporters;
2694
2811
  }
2695
2812
  function isChromiumName(provider, name) {
2696
- return provider === "playwright" ? name === "chromium" : name === "chrome" || name === "edge";
2813
+ if (provider === "playwright") return name === "chromium";
2814
+ return name === "chrome" || name === "edge";
2697
2815
  }
2698
2816
  function hasBrowserChromium(vitest, config) {
2699
2817
  const browser = config.browser;
2700
- 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) => {
2701
2822
  const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
2702
- return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : false;
2703
- }) : false;
2823
+ // browser config is filtered out
2824
+ if (!vitest.matchesProjectFilter(name)) return false;
2825
+ return isChromiumName(browser.provider.name, instance.browser);
2826
+ });
2704
2827
  }
2705
2828
  function hasOnlyBrowserChromium(vitest, config) {
2706
2829
  const browser = config.browser;
2707
- 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) => {
2708
2834
  const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
2709
- return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : true;
2710
- }) : false;
2835
+ // browser config is filtered out
2836
+ if (!vitest.matchesProjectFilter(name)) return true;
2837
+ return isChromiumName(browser.provider.name, instance.browser);
2838
+ });
2711
2839
  }
2712
2840
 
2713
2841
  const THRESHOLD_KEYS = [
@@ -2715,11 +2843,14 @@ const THRESHOLD_KEYS = [
2715
2843
  "functions",
2716
2844
  "statements",
2717
2845
  "branches"
2718
- ], 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");
2719
2849
  let uniqueId = 0;
2720
2850
  async function getCoverageProvider(options, loader) {
2721
2851
  const coverageModule = await resolveCoverageProviderModule(options, loader);
2722
- return coverageModule ? coverageModule.getProvider() : null;
2852
+ if (coverageModule) return coverageModule.getProvider();
2853
+ return null;
2723
2854
  }
2724
2855
  class BaseCoverageProvider {
2725
2856
  ctx;
@@ -2732,7 +2863,8 @@ class BaseCoverageProvider {
2732
2863
  coverageFilesDirectory;
2733
2864
  roots = [];
2734
2865
  _initialize(ctx) {
2735
- 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} `))}.
2736
2868
  Running mixed versions is not supported and may lead into bugs
2737
2869
  Update your dependencies and make sure the versions match.`));
2738
2870
  const config = ctx._coverageOptions;
@@ -2750,25 +2882,34 @@ Update your dependencies and make sure the versions match.`));
2750
2882
  statements: config.thresholds["100"] ? 100 : config.thresholds.statements
2751
2883
  }
2752
2884
  };
2753
- 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);
2754
2888
  // If --project filter is set pick only roots of resolved projects
2755
- 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];
2756
2890
  }
2757
2891
  /**
2758
2892
  * Check if file matches `coverage.include` but not `coverage.exclude`
2759
2893
  */
2760
2894
  isIncluded(_filename, root) {
2761
- 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);
2762
2898
  if (cacheHit !== void 0) return cacheHit;
2763
2899
  // File outside project root with default allowExternal
2764
- 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
+ }
2765
2904
  // By default `coverage.include` matches all files, except "coverage.exclude"
2766
- const glob = this.options.include || "**", included = pm.isMatch(filename, glob, {
2905
+ const glob = this.options.include || "**";
2906
+ const included = pm.isMatch(filename, glob, {
2767
2907
  contains: true,
2768
2908
  dot: true,
2769
2909
  ignore: this.options.exclude
2770
2910
  });
2771
- return this.globCache.set(filename, included), included;
2911
+ this.globCache.set(filename, included);
2912
+ return included;
2772
2913
  }
2773
2914
  async getUntestedFilesByRoot(testedFiles, include, root) {
2774
2915
  let includedFiles = await glob(include, {
@@ -2778,7 +2919,9 @@ Update your dependencies and make sure the versions match.`));
2778
2919
  dot: true,
2779
2920
  onlyFiles: true
2780
2921
  });
2781
- 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));
2782
2925
  return includedFiles.map((file) => slash(path.resolve(root, file)));
2783
2926
  }
2784
2927
  async getUntestedFiles(testedFiles) {
@@ -2809,49 +2952,66 @@ Update your dependencies and make sure the versions match.`));
2809
2952
  force: true,
2810
2953
  maxRetries: 10
2811
2954
  });
2812
- 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 = [];
2813
2958
  }
2814
2959
  onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
2815
2960
  if (!coverage) return;
2816
2961
  let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
2817
- if (!entry) entry = {}, this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
2818
- 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] ??= {};
2819
2969
  // If there's a result from previous run, overwrite it
2820
- entry[environment] ??= {}, entry[environment][testFilenames] = filename;
2970
+ entry[environment][testFilenames] = filename;
2821
2971
  const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
2822
2972
  this.pendingPromises.push(promise);
2823
2973
  }
2824
2974
  async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
2825
2975
  let index = 0;
2826
2976
  const total = this.pendingPromises.length;
2827
- await Promise.all(this.pendingPromises), this.pendingPromises = [];
2977
+ await Promise.all(this.pendingPromises);
2978
+ this.pendingPromises = [];
2828
2979
  for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
2829
- const filenames = Object.values(coverageByTestfiles), project = this.ctx.getProjectByName(projectName);
2980
+ const filenames = Object.values(coverageByTestfiles);
2981
+ const project = this.ctx.getProjectByName(projectName);
2830
2982
  for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
2831
- 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
+ }
2832
2987
  await Promise.all(chunk.map(async (filename) => {
2833
- const contents = await promises.readFile(filename, "utf-8"), coverage = JSON.parse(contents);
2834
- onFileRead(coverage);
2988
+ const contents = await promises.readFile(filename, "utf-8");
2989
+ onFileRead(JSON.parse(contents));
2835
2990
  }));
2836
2991
  }
2837
2992
  await onFinished(project, environment);
2838
2993
  }
2839
2994
  }
2840
2995
  async cleanAfterRun() {
2996
+ this.coverageFiles = /* @__PURE__ */ new Map();
2997
+ await promises.rm(this.coverageFilesDirectory, { recursive: true });
2841
2998
  // Remove empty reports directory, e.g. when only text-reporter is used
2842
- 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 });
2843
3000
  }
2844
3001
  async onTestFailure() {
2845
3002
  if (!this.options.reportOnFailure) await this.cleanAfterRun();
2846
3003
  }
2847
3004
  async reportCoverage(coverageMap, { allTestsRun }) {
2848
- 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();
2849
3007
  }
2850
3008
  async reportThresholds(coverageMap, allTestsRun) {
2851
3009
  const resolvedThresholds = this.resolveThresholds(coverageMap);
2852
- if (this.checkThresholds(resolvedThresholds), this.options.thresholds?.autoUpdate && allTestsRun) {
3010
+ this.checkThresholds(resolvedThresholds);
3011
+ if (this.options.thresholds?.autoUpdate && allTestsRun) {
2853
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.");
2854
- 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);
2855
3015
  await this.updateThresholds({
2856
3016
  thresholds: resolvedThresholds,
2857
3017
  configurationFile: configModule,
@@ -2865,10 +3025,16 @@ Update your dependencies and make sure the versions match.`));
2865
3025
  * for specific files defined by glob pattern or global for all other files.
2866
3026
  */
2867
3027
  resolveThresholds(coverageMap) {
2868
- const resolvedThresholds = [], files = coverageMap.files(), globalCoverageMap = this.createCoverageMap();
3028
+ const resolvedThresholds = [];
3029
+ const files = coverageMap.files();
3030
+ const globalCoverageMap = this.createCoverageMap();
2869
3031
  for (const key of Object.keys(this.options.thresholds)) {
2870
3032
  if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
2871
- 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)));
2872
3038
  for (const file of matchingFiles) {
2873
3039
  const fileCoverage = coverageMap.fileCoverageFor(file);
2874
3040
  globCoverageMap.addFileCoverage(fileCoverage);
@@ -2884,7 +3050,7 @@ Update your dependencies and make sure the versions match.`));
2884
3050
  const fileCoverage = coverageMap.fileCoverageFor(file);
2885
3051
  globalCoverageMap.addFileCoverage(fileCoverage);
2886
3052
  }
2887
- return resolvedThresholds.unshift({
3053
+ resolvedThresholds.unshift({
2888
3054
  name: GLOBAL_THRESHOLDS_KEY,
2889
3055
  coverageMap: globalCoverageMap,
2890
3056
  thresholds: {
@@ -2893,7 +3059,8 @@ Update your dependencies and make sure the versions match.`));
2893
3059
  lines: this.options.thresholds?.lines,
2894
3060
  statements: this.options.thresholds?.statements
2895
3061
  }
2896
- }), resolvedThresholds;
3062
+ });
3063
+ return resolvedThresholds;
2897
3064
  }
2898
3065
  /**
2899
3066
  * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
@@ -2931,7 +3098,8 @@ Update your dependencies and make sure the versions match.`));
2931
3098
  this.ctx.logger.error(errorMessage);
2932
3099
  }
2933
3100
  } else {
2934
- 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;
2935
3103
  if (uncovered > absoluteThreshold) {
2936
3104
  process.exitCode = 1;
2937
3105
  /**
@@ -2955,7 +3123,8 @@ Update your dependencies and make sure the versions match.`));
2955
3123
  const config = resolveConfig(configurationFile);
2956
3124
  assertConfigurationModule(config);
2957
3125
  for (const { coverageMap, thresholds, name } of allThresholds) {
2958
- 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 = [];
2959
3128
  for (const key of THRESHOLD_KEYS) {
2960
3129
  const threshold = thresholds[key] ?? 100;
2961
3130
  /**
@@ -2966,7 +3135,8 @@ Update your dependencies and make sure the versions match.`));
2966
3135
  const actual = Math.min(...summaries.map((summary) => summary[key].pct));
2967
3136
  if (actual > threshold) thresholdsToUpdate.push([key, actual]);
2968
3137
  } else {
2969
- 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));
2970
3140
  if (actual < absoluteThreshold) {
2971
3141
  // If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
2972
3142
  const updatedThreshold = actual === 0 ? 100 : actual * -1;
@@ -2986,7 +3156,10 @@ Update your dependencies and make sure the versions match.`));
2986
3156
  }
2987
3157
  }
2988
3158
  }
2989
- 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
+ }
2990
3163
  }
2991
3164
  async mergeReports(coverageMaps) {
2992
3165
  const coverageMap = this.createCoverageMap();
@@ -2998,8 +3171,10 @@ Update your dependencies and make sure the versions match.`));
2998
3171
  }
2999
3172
  toSlices(array, size) {
3000
3173
  return array.reduce((chunks, item) => {
3001
- const index = Math.max(0, chunks.length - 1), lastChunk = chunks[index] || [];
3002
- 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]);
3003
3178
  else lastChunk.push(item);
3004
3179
  return chunks;
3005
3180
  }, []);
@@ -3040,12 +3215,14 @@ Update your dependencies and make sure the versions match.`));
3040
3215
  * Narrow down `unknown` glob thresholds to resolved ones
3041
3216
  */
3042
3217
  function resolveGlobThresholds(thresholds) {
3043
- 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 {
3044
3220
  lines: 100,
3045
3221
  branches: 100,
3046
3222
  functions: 100,
3047
3223
  statements: 100
3048
- } : {
3224
+ };
3225
+ return {
3049
3226
  lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
3050
3227
  branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
3051
3228
  functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
@@ -3071,7 +3248,8 @@ function resolveConfig(configModule) {
3071
3248
  if (config) return config;
3072
3249
  // "export default mergeConfig(..., defineConfig(...))"
3073
3250
  if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
3074
- if (config = resolveMergeConfig(mod), config) return config;
3251
+ config = resolveMergeConfig(mod);
3252
+ if (config) return config;
3075
3253
  }
3076
3254
  } catch (error) {
3077
3255
  // Reduce magicast's verbose errors to readable ones