vitest 4.0.0-beta.4 → 4.0.0-beta.6

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 (78) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/browser.d.ts +8 -9
  3. package/dist/browser.js +3 -2
  4. package/dist/chunks/base.BXI97p6t.js +39 -0
  5. package/dist/chunks/{benchmark.CYdenmiT.js → benchmark.UW6Ezvxy.js} +6 -8
  6. package/dist/chunks/{browser.d.BRP8scJf.d.ts → browser.d.Cawq_X_N.d.ts} +1 -1
  7. package/dist/chunks/{cac.CY0IAxC4.js → cac.WE-urWw5.js} +38 -115
  8. package/dist/chunks/{cli-api.B8xRY9Zt.js → cli-api.CZz3evYC.js} +931 -1439
  9. package/dist/chunks/{config.d.DZo8c7fw.d.ts → config.d.CKNVOKm0.d.ts} +3 -8
  10. package/dist/chunks/{console.DoJHFxmj.js → console.B0quX7yH.js} +32 -68
  11. package/dist/chunks/{constants.CXzqaLmq.js → constants.D_Q9UYh-.js} +1 -6
  12. package/dist/chunks/{coverage.C84l9G-M.js → coverage.BPRS6xgn.js} +395 -665
  13. package/dist/chunks/{coverage.DVF1vEu8.js → coverage.D_JHT54q.js} +2 -2
  14. package/dist/chunks/{coverage.d.CNYjU4GF.d.ts → coverage.d.BZtK59WP.d.ts} +7 -5
  15. package/dist/chunks/{creator.yfA2ExGt.js → creator.KEg6n5IC.js} +29 -75
  16. package/dist/chunks/{date.Bq6ZW5rf.js → date.-jtEtIeV.js} +6 -17
  17. package/dist/chunks/{environment.d.Bhm9oc0v.d.ts → environment.d.2fYMoz3o.d.ts} +26 -4
  18. package/dist/chunks/{git.BVQ8w_Sw.js → git.BFNcloKD.js} +1 -2
  19. package/dist/chunks/{global.d.DAhT2emn.d.ts → global.d.K6uBQHzY.d.ts} +1 -1
  20. package/dist/chunks/{globals.Dgo-vS5G.js → globals.lgsmH00r.js} +7 -6
  21. package/dist/chunks/{index.D3SKT3tv.js → index.7w0eqmYM.js} +14 -24
  22. package/dist/chunks/{index.D1_MsKEt.js → index.AR8aAkCC.js} +4 -2
  23. package/dist/chunks/{index.CmSc2RE5.js → index.BG0gqZH-.js} +43 -106
  24. package/dist/chunks/{index.CtUvr1c8.js → index.CsFXYRkW.js} +27 -46
  25. package/dist/chunks/{index.Bz6b0Ib7.js → index.VNI-1z5c.js} +276 -604
  26. package/dist/chunks/{inspector.C914Efll.js → inspector.CvQD-Nie.js} +10 -25
  27. package/dist/chunks/moduleRunner.d.8kKUsuDg.d.ts +202 -0
  28. package/dist/chunks/moduleTransport.I-bgQy0S.js +19 -0
  29. package/dist/chunks/{node.fjCdwEIl.js → node.BOqcT2jW.js} +1 -1
  30. package/dist/chunks/{plugin.d.CLhMcYdD.d.ts → plugin.d.DuiQJfUL.d.ts} +1 -1
  31. package/dist/chunks/{reporters.d.DWg40D2B.d.ts → reporters.d.CqR9-CDJ.d.ts} +52 -101
  32. package/dist/chunks/resolver.Bx6lE0iq.js +119 -0
  33. package/dist/chunks/{rpc.jnQO9F8a.js → rpc.RpPylpp0.js} +7 -21
  34. package/dist/chunks/runBaseTests.D6sfuWBM.js +99 -0
  35. package/dist/chunks/{setup-common.Ebx5x0eP.js → setup-common.hLGRxhC8.js} +15 -27
  36. package/dist/chunks/startModuleRunner.C8TW8zTN.js +655 -0
  37. package/dist/chunks/{typechecker.CMNPqJOo.js → typechecker.Cd1wvxUM.js} +97 -209
  38. package/dist/chunks/{utils.CcGm2cd1.js → utils.C2YI6McM.js} +4 -13
  39. package/dist/chunks/{utils.XdZDrNZV.js → utils.C7__0Iv5.js} +7 -17
  40. package/dist/chunks/{vi.CA0EPI9Y.js → vi.BfdOiD4j.js} +116 -269
  41. package/dist/chunks/{vm.BUnLJt_P.js → vm.BHBje7cC.js} +101 -225
  42. package/dist/chunks/{worker.d.zjyR34Pb.d.ts → worker.d.D9QWnzAe.d.ts} +16 -13
  43. package/dist/chunks/{worker.d.C-1AbnVe.d.ts → worker.d.Db-UVmXc.d.ts} +1 -1
  44. package/dist/cli.js +4 -4
  45. package/dist/config.cjs +3 -9
  46. package/dist/config.d.ts +10 -12
  47. package/dist/config.js +1 -1
  48. package/dist/coverage.d.ts +10 -11
  49. package/dist/coverage.js +5 -6
  50. package/dist/environments.d.ts +2 -2
  51. package/dist/environments.js +1 -1
  52. package/dist/index.d.ts +10 -9
  53. package/dist/index.js +6 -5
  54. package/dist/module-evaluator.d.ts +12 -0
  55. package/dist/module-evaluator.js +276 -0
  56. package/dist/module-runner.js +15 -0
  57. package/dist/node.d.ts +12 -13
  58. package/dist/node.js +19 -24
  59. package/dist/reporters.d.ts +7 -8
  60. package/dist/reporters.js +3 -3
  61. package/dist/runners.d.ts +3 -3
  62. package/dist/runners.js +35 -57
  63. package/dist/snapshot.js +2 -2
  64. package/dist/suite.js +2 -2
  65. package/dist/worker.js +82 -45
  66. package/dist/workers/forks.js +11 -10
  67. package/dist/workers/runVmTests.js +27 -46
  68. package/dist/workers/threads.js +11 -10
  69. package/dist/workers/vmForks.js +11 -10
  70. package/dist/workers/vmThreads.js +11 -10
  71. package/dist/workers.d.ts +5 -4
  72. package/dist/workers.js +17 -16
  73. package/package.json +22 -17
  74. package/dist/chunks/base.BaCDDRPG.js +0 -38
  75. package/dist/chunks/execute.Dt-pCVcL.js +0 -708
  76. package/dist/chunks/runBaseTests.DBVVLMSb.js +0 -129
  77. package/dist/execute.d.ts +0 -148
  78. package/dist/execute.js +0 -13
@@ -1,13 +1,12 @@
1
1
  import fs, { statSync, realpathSync, promises as promises$1, mkdirSync, existsSync, readdirSync, writeFileSync } from 'node:fs';
2
2
  import path, { win32, dirname, join, resolve } from 'node:path';
3
+ import { isExternalUrl, unwrapId, nanoid, withTrailingSlash as withTrailingSlash$1, cleanUrl, wrapId, createDefer, slash, shuffle, toArray } from '@vitest/utils';
3
4
  import { isAbsolute, join as join$1, dirname as dirname$1, resolve as resolve$1, relative, normalize } from 'pathe';
4
5
  import pm from 'picomatch';
5
6
  import { glob } from 'tinyglobby';
6
7
  import c from 'tinyrainbow';
7
- import { slash, cleanUrl } from 'vite-node/utils';
8
8
  import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.CXFFjsi8.js';
9
9
  import crypto from 'node:crypto';
10
- import { createDefer, shuffle, toArray, slash as slash$1 } from '@vitest/utils';
11
10
  import { builtinModules, createRequire } from 'node:module';
12
11
  import process$1 from 'node:process';
13
12
  import fs$1, { writeFile, rename, stat, unlink } from 'node:fs/promises';
@@ -15,28 +14,26 @@ import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1, URL
15
14
  import assert from 'node:assert';
16
15
  import v8 from 'node:v8';
17
16
  import { format, inspect } from 'node:util';
18
- import { version, mergeConfig } from 'vite';
19
- import { c as configFiles, e as extraInlineDeps, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort } from './constants.CXzqaLmq.js';
17
+ import { fetchModule, version, mergeConfig } from 'vite';
18
+ import { c as configFiles, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort } from './constants.D_Q9UYh-.js';
20
19
  import { a as isWindows } from './env.D4Lgay0q.js';
21
20
  import * as nodeos from 'node:os';
22
- import nodeos__default from 'node:os';
21
+ import nodeos__default, { tmpdir } from 'node:os';
23
22
  import { isatty } from 'node:tty';
24
23
  import EventEmitter from 'node:events';
25
24
  import { c as createBirpc } from './index.Bgo3tNWt.js';
26
25
  import Tinypool$1, { Tinypool } from 'tinypool';
27
- import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.CMNPqJOo.js';
26
+ import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.Cd1wvxUM.js';
28
27
  import { MessageChannel } from 'node:worker_threads';
29
28
  import { hasFailed } from '@vitest/runner/utils';
30
29
  import { rootDir } from '../path.js';
31
30
  import { isCI, provider } from 'std-env';
32
- import { r as resolveCoverageProviderModule } from './coverage.DVF1vEu8.js';
31
+ import { r as resolveCoverageProviderModule } from './coverage.D_JHT54q.js';
33
32
 
34
33
  function groupBy(collection, iteratee) {
35
34
  return collection.reduce((acc, item) => {
36
35
  const key = iteratee(item);
37
- acc[key] ||= [];
38
- acc[key].push(item);
39
- return acc;
36
+ return acc[key] ||= [], acc[key].push(item), acc;
40
37
  }, {});
41
38
  }
42
39
  function stdout() {
@@ -2385,8 +2382,7 @@ const isPackageListed = quansync(function* (name, cwd) {
2385
2382
  isPackageListed.sync;
2386
2383
 
2387
2384
  function getWorkersCountByPercentage(percent) {
2388
- const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length;
2389
- const workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
2385
+ const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length, workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
2390
2386
  return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
2391
2387
  }
2392
2388
 
@@ -2396,11 +2392,6 @@ const envsOrder = [
2396
2392
  "happy-dom",
2397
2393
  "edge-runtime"
2398
2394
  ];
2399
- function getTransformMode(patterns, filename) {
2400
- if (patterns.web && pm.isMatch(filename, patterns.web)) return "web";
2401
- if (patterns.ssr && pm.isMatch(filename, patterns.ssr)) return "ssr";
2402
- return void 0;
2403
- }
2404
2395
  async function groupFilesByEnv(files) {
2405
2396
  const filesWithEnv = await Promise.all(files.map(async ({ moduleId: filepath, project, testLines }) => {
2406
2397
  const code = await promises$1.readFile(filepath, "utf-8");
@@ -2408,16 +2399,12 @@ async function groupFilesByEnv(files) {
2408
2399
  let env = code.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
2409
2400
  // 2. Fallback to global env
2410
2401
  env ||= project.config.environment || "node";
2411
- const transformMode = getTransformMode(project.config.testTransformMode, filepath);
2412
2402
  let envOptionsJson = code.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
2413
2403
  if (envOptionsJson?.endsWith("*/"))
2414
2404
  // Trim closing Docblock characters the above regex might have captured
2415
2405
  envOptionsJson = envOptionsJson.slice(0, -2);
2416
- const envOptions = JSON.parse(envOptionsJson || "null");
2417
- const envKey = env === "happy-dom" ? "happyDOM" : env;
2418
- const environment = {
2406
+ const envOptions = JSON.parse(envOptionsJson || "null"), envKey = env === "happy-dom" ? "happyDOM" : env, environment = {
2419
2407
  name: env,
2420
- transformMode,
2421
2408
  options: envOptions ? { [envKey]: envOptions } : null
2422
2409
  };
2423
2410
  return {
@@ -2432,45 +2419,198 @@ async function groupFilesByEnv(files) {
2432
2419
  return groupBy(filesWithEnv, ({ environment }) => environment.name);
2433
2420
  }
2434
2421
 
2435
- const created = /* @__PURE__ */ new Set();
2436
- const promises = /* @__PURE__ */ new Map();
2422
+ const created = /* @__PURE__ */ new Set(), promises = /* @__PURE__ */ new Map();
2423
+ function createFetchModuleFunction(resolver, cacheFs = false, tmpDir = join$1(tmpdir(), nanoid())) {
2424
+ const cachedFsResults = /* @__PURE__ */ new Map();
2425
+ return async (url, importer, environment, options) => {
2426
+ // We are copy pasting Vite's externalization logic from `fetchModule` because
2427
+ // we instead rely on our own `shouldExternalize` method because Vite
2428
+ // doesn't support `resolve.external` in non SSR environments (jsdom/happy-dom)
2429
+ if (url.startsWith("data:")) return {
2430
+ externalize: url,
2431
+ type: "builtin"
2432
+ };
2433
+ if (url === "/@vite/client" || url === "@vite/client")
2434
+ // this will be stubbed
2435
+ return {
2436
+ externalize: "/@vite/client",
2437
+ type: "module"
2438
+ };
2439
+ const isFileUrl = url.startsWith("file://");
2440
+ if (isExternalUrl(url) && !isFileUrl) return {
2441
+ externalize: url,
2442
+ type: "network"
2443
+ };
2444
+ // Vite does the same in `fetchModule`, but we want to externalize modules ourselves,
2445
+ // so we do this first to resolve the module and check its `id`. The next call of
2446
+ // `ensureEntryFromUrl` inside `fetchModule` is cached and should take no time
2447
+ // This also makes it so externalized modules are inside the module graph.
2448
+ const moduleGraphModule = await environment.moduleGraph.ensureEntryFromUrl(unwrapId(url)), cached = !!moduleGraphModule.transformResult;
2449
+ // if url is already cached, we can just confirm it's also cached on the server
2450
+ if (options?.cached && cached) return { cache: true };
2451
+ if (moduleGraphModule.id) {
2452
+ const externalize = await resolver.shouldExternalize(moduleGraphModule.id);
2453
+ if (externalize) return {
2454
+ externalize,
2455
+ type: "module"
2456
+ };
2457
+ }
2458
+ const moduleRunnerModule = await fetchModule(environment, url, importer, {
2459
+ ...options,
2460
+ inlineSourceMap: false
2461
+ }).catch(handleRollupError), result = processResultSource(environment, moduleRunnerModule);
2462
+ if (!cacheFs || !("code" in result)) return result;
2463
+ const code = result.code;
2464
+ // to avoid serialising large chunks of code,
2465
+ // we store them in a tmp file and read in the test thread
2466
+ if (cachedFsResults.has(result.id)) return getCachedResult(result, cachedFsResults);
2467
+ const dir = join$1(tmpDir, environment.name), name = hash("sha1", result.id, "hex"), tmp = join$1(dir, name);
2468
+ if (!created.has(dir)) mkdirSync(dir, { recursive: true }), created.add(dir);
2469
+ return promises.has(tmp) ? (await promises.get(tmp), cachedFsResults.set(result.id, tmp), getCachedResult(result, cachedFsResults)) : (promises.set(tmp, atomicWriteFile(tmp, code).catch(() => writeFile(tmp, code, "utf-8")).finally(() => promises.delete(tmp))), await promises.get(tmp), cachedFsResults.set(result.id, tmp), getCachedResult(result, cachedFsResults));
2470
+ };
2471
+ }
2472
+ let SOURCEMAPPING_URL = "sourceMa";
2473
+ SOURCEMAPPING_URL += "ppingURL";
2474
+ const MODULE_RUNNER_SOURCEMAPPING_SOURCE = "//# sourceMappingSource=vite-generated";
2475
+ function processResultSource(environment, result) {
2476
+ if (!("code" in result)) return result;
2477
+ const node = environment.moduleGraph.getModuleById(result.id);
2478
+ if (node?.transformResult)
2479
+ // this also overrides node.transformResult.code which is also what the module
2480
+ // runner does under the hood by default (we disable source maps inlining)
2481
+ inlineSourceMap(node.transformResult);
2482
+ return {
2483
+ ...result,
2484
+ code: node?.transformResult?.code || result.code
2485
+ };
2486
+ }
2487
+ const OTHER_SOURCE_MAP_REGEXP = new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,([A-Za-z0-9+/=]+)$`, "gm");
2488
+ // we have to inline the source map ourselves, because
2489
+ // - we don't need //# sourceURL since we are running code in VM
2490
+ // - important in stack traces and the V8 coverage
2491
+ // - we need to inject an empty line for --inspect-brk
2492
+ function inlineSourceMap(result) {
2493
+ const map = result.map;
2494
+ let code = result.code;
2495
+ if (!map || !("version" in map) || code.includes(MODULE_RUNNER_SOURCEMAPPING_SOURCE)) return result;
2496
+ if (OTHER_SOURCE_MAP_REGEXP.lastIndex = 0, OTHER_SOURCE_MAP_REGEXP.test(code)) code = code.replace(OTHER_SOURCE_MAP_REGEXP, "");
2497
+ const sourceMap = { ...map };
2498
+ // If the first line is not present on source maps, add simple 1:1 mapping ([0,0,0,0], [1,0,0,0])
2499
+ // so that debuggers can be set to break on first line
2500
+ if (sourceMap.mappings.startsWith(";")) sourceMap.mappings = `AAAA,CAAA${sourceMap.mappings}`;
2501
+ return result.code = `${code.trimEnd()}\n${MODULE_RUNNER_SOURCEMAPPING_SOURCE}\n//# ${SOURCEMAPPING_URL}=${genSourceMapUrl(sourceMap)}\n`, result;
2502
+ }
2503
+ function genSourceMapUrl(map) {
2504
+ if (typeof map !== "string") map = JSON.stringify(map);
2505
+ return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
2506
+ }
2507
+ function getCachedResult(result, cachedFsResults) {
2508
+ const tmp = cachedFsResults.get(result.id);
2509
+ if (!tmp) throw new Error(`The cached result was returned too early for ${result.id}.`);
2510
+ return {
2511
+ cached: true,
2512
+ file: result.file,
2513
+ id: result.id,
2514
+ tmp,
2515
+ url: result.url,
2516
+ invalidate: result.invalidate
2517
+ };
2518
+ }
2519
+ // serialize rollup error on server to preserve details as a test error
2520
+ function handleRollupError(e) {
2521
+ throw e instanceof Error && ("plugin" in e || "frame" in e || "id" in e) ? {
2522
+ name: e.name,
2523
+ message: e.message,
2524
+ stack: e.stack,
2525
+ cause: e.cause,
2526
+ __vitest_rollup_error__: {
2527
+ plugin: e.plugin,
2528
+ id: e.id,
2529
+ loc: e.loc,
2530
+ frame: e.frame
2531
+ }
2532
+ } : e;
2533
+ }
2534
+ /**
2535
+ * Performs an atomic write operation using the write-then-rename pattern.
2536
+ *
2537
+ * Why we need this:
2538
+ * - Ensures file integrity by never leaving partially written files on disk
2539
+ * - Prevents other processes from reading incomplete data during writes
2540
+ * - Particularly important for test files where incomplete writes could cause test failures
2541
+ *
2542
+ * The implementation writes to a temporary file first, then renames it to the target path.
2543
+ * This rename operation is atomic on most filesystems (including POSIX-compliant ones),
2544
+ * guaranteeing that other processes will only ever see the complete file.
2545
+ *
2546
+ * Added in https://github.com/vitest-dev/vitest/pull/7531
2547
+ */
2548
+ async function atomicWriteFile(realFilePath, data) {
2549
+ const dir = dirname$1(realFilePath), tmpFilePath = join$1(dir, `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
2550
+ try {
2551
+ await writeFile(tmpFilePath, data, "utf-8"), await rename(tmpFilePath, realFilePath);
2552
+ } finally {
2553
+ try {
2554
+ if (await stat(tmpFilePath)) await unlink(tmpFilePath);
2555
+ } catch {}
2556
+ }
2557
+ }
2558
+
2559
+ // this is copy pasted from vite
2560
+ function normalizeResolvedIdToUrl(environment, resolvedId) {
2561
+ const root = environment.config.root, depsOptimizer = environment.depsOptimizer;
2562
+ let url;
2563
+ // normalize all imports into resolved URLs
2564
+ // e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'`
2565
+ if (resolvedId.startsWith(withTrailingSlash$1(root)))
2566
+ // in root: infer short absolute path from root
2567
+ url = resolvedId.slice(root.length);
2568
+ else if (depsOptimizer?.isOptimizedDepFile(resolvedId) || resolvedId !== "/@react-refresh" && path.isAbsolute(resolvedId) && existsSync(cleanUrl(resolvedId)))
2569
+ // an optimized deps may not yet exists in the filesystem, or
2570
+ // a regular file exists but is out of root: rewrite to absolute /@fs/ paths
2571
+ url = path.posix.join("/@fs/", resolvedId);
2572
+ else url = resolvedId;
2573
+ // if the resolved id is not a valid browser import specifier,
2574
+ // prefix it to make it valid. We will strip this before feeding it
2575
+ // back into the transform pipeline
2576
+ if (url[0] !== "." && url[0] !== "/") url = wrapId(resolvedId);
2577
+ return url;
2578
+ }
2579
+
2437
2580
  function createMethodsRPC(project, options = {}) {
2438
- const ctx = project.vitest;
2439
- const cacheFs = options.cacheFs ?? false;
2581
+ const ctx = project.vitest, cacheFs = options.cacheFs ?? false, fetch = createFetchModuleFunction(project._resolver, cacheFs, project.tmpDir);
2440
2582
  return {
2583
+ async fetch(url, importer, environmentName, options) {
2584
+ const environment = project.vite.environments[environmentName];
2585
+ if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
2586
+ const start = performance.now();
2587
+ try {
2588
+ return await fetch(url, importer, environment, options);
2589
+ } finally {
2590
+ project.vitest.state.transformTime += performance.now() - start;
2591
+ }
2592
+ },
2593
+ async resolve(id, importer, environmentName) {
2594
+ const environment = project.vite.environments[environmentName];
2595
+ if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
2596
+ const resolved = await environment.pluginContainer.resolveId(id, importer);
2597
+ return resolved ? {
2598
+ file: cleanUrl(resolved.id),
2599
+ url: normalizeResolvedIdToUrl(environment, resolved.id),
2600
+ id: resolved.id
2601
+ } : null;
2602
+ },
2441
2603
  snapshotSaved(snapshot) {
2442
2604
  ctx.snapshot.add(snapshot);
2443
2605
  },
2444
2606
  resolveSnapshotPath(testPath) {
2445
2607
  return ctx.snapshot.resolvePath(testPath, { config: project.serializedConfig });
2446
2608
  },
2447
- async fetch(id, transformMode) {
2448
- const result = await project.vitenode.fetchResult(id, transformMode).catch(handleRollupError);
2449
- const code = result.code;
2450
- if (!cacheFs || result.externalize) return result;
2451
- if ("id" in result && typeof result.id === "string") return { id: result.id };
2452
- if (code == null) throw new Error(`Failed to fetch module ${id}`);
2453
- const dir = join$1(project.tmpDir, transformMode);
2454
- const name = hash("sha1", id, "hex");
2455
- const tmp = join$1(dir, name);
2456
- if (!created.has(dir)) {
2457
- mkdirSync(dir, { recursive: true });
2458
- created.add(dir);
2459
- }
2460
- if (promises.has(tmp)) {
2461
- await promises.get(tmp);
2462
- return { id: tmp };
2463
- }
2464
- promises.set(tmp, atomicWriteFile(tmp, code).catch(() => writeFile(tmp, code, "utf-8")).finally(() => promises.delete(tmp)));
2465
- await promises.get(tmp);
2466
- Object.assign(result, { id: tmp });
2467
- return { id: tmp };
2468
- },
2469
- resolveId(id, importer, transformMode) {
2470
- return project.vitenode.resolveId(id, importer, transformMode).catch(handleRollupError);
2471
- },
2472
- transform(id, environment) {
2473
- return project.vitenode.transformModule(id, environment).catch(handleRollupError);
2609
+ async transform(id) {
2610
+ const environment = project.vite.environments.__vitest_vm__;
2611
+ if (!environment) throw new Error(`The VM environment was not defined in the Vite config. This is a bug in Vitest. Please, open a new issue with reproduction.`);
2612
+ const url = normalizeResolvedIdToUrl(environment, fileURLToPath$1(id)), result = await environment.transformRequest(url).catch(handleRollupError);
2613
+ return { code: result?.code };
2474
2614
  },
2475
2615
  async onQueued(file) {
2476
2616
  if (options.collect) ctx.state.collectFiles(project, [file]);
@@ -2505,58 +2645,12 @@ function createMethodsRPC(project, options = {}) {
2505
2645
  }
2506
2646
  };
2507
2647
  }
2508
- // serialize rollup error on server to preserve details as a test error
2509
- function handleRollupError(e) {
2510
- if (e instanceof Error && ("plugin" in e || "frame" in e || "id" in e))
2511
- // eslint-disable-next-line no-throw-literal
2512
- throw {
2513
- name: e.name,
2514
- message: e.message,
2515
- stack: e.stack,
2516
- cause: e.cause,
2517
- __vitest_rollup_error__: {
2518
- plugin: e.plugin,
2519
- id: e.id,
2520
- loc: e.loc,
2521
- frame: e.frame
2522
- }
2523
- };
2524
- throw e;
2525
- }
2526
- /**
2527
- * Performs an atomic write operation using the write-then-rename pattern.
2528
- *
2529
- * Why we need this:
2530
- * - Ensures file integrity by never leaving partially written files on disk
2531
- * - Prevents other processes from reading incomplete data during writes
2532
- * - Particularly important for test files where incomplete writes could cause test failures
2533
- *
2534
- * The implementation writes to a temporary file first, then renames it to the target path.
2535
- * This rename operation is atomic on most filesystems (including POSIX-compliant ones),
2536
- * guaranteeing that other processes will only ever see the complete file.
2537
- *
2538
- * Added in https://github.com/vitest-dev/vitest/pull/7531
2539
- */
2540
- async function atomicWriteFile(realFilePath, data) {
2541
- const dir = dirname$1(realFilePath);
2542
- const tmpFilePath = join$1(dir, `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
2543
- try {
2544
- await writeFile(tmpFilePath, data, "utf-8");
2545
- await rename(tmpFilePath, realFilePath);
2546
- } finally {
2547
- try {
2548
- if (await stat(tmpFilePath)) await unlink(tmpFilePath);
2549
- } catch {}
2550
- }
2551
- }
2552
2648
 
2553
2649
  function createChildProcessChannel$1(project, collect = false) {
2554
- const emitter = new EventEmitter();
2555
- const events = {
2650
+ const emitter = new EventEmitter(), events = {
2556
2651
  message: "message",
2557
2652
  response: "response"
2558
- };
2559
- const rpc = createBirpc(createMethodsRPC(project, {
2653
+ }, rpc = createBirpc(createMethodsRPC(project, {
2560
2654
  cacheFs: true,
2561
2655
  collect
2562
2656
  }), {
@@ -2586,20 +2680,13 @@ function createChildProcessChannel$1(project, collect = false) {
2586
2680
  onMessage: (callback) => emitter.on(events.message, callback),
2587
2681
  postMessage: (message) => emitter.emit(events.response, message),
2588
2682
  onClose: () => {
2589
- emitter.removeAllListeners();
2590
- rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
2683
+ emitter.removeAllListeners(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
2591
2684
  }
2592
2685
  };
2593
2686
  return channel;
2594
2687
  }
2595
2688
  function createForksPool(vitest, { execArgv, env }) {
2596
- const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
2597
- const threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
2598
- const poolOptions = vitest.config.poolOptions?.forks ?? {};
2599
- const maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount;
2600
- const minThreads = poolOptions.minForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads);
2601
- const worker = resolve(vitest.distPath, "workers/forks.js");
2602
- const options = {
2689
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.forks ?? {}, maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/forks.js"), options = {
2603
2690
  runtime: "child_process",
2604
2691
  filename: resolve(vitest.distPath, "worker.js"),
2605
2692
  teardown: "teardown",
@@ -2609,22 +2696,15 @@ function createForksPool(vitest, { execArgv, env }) {
2609
2696
  execArgv: [...poolOptions.execArgv ?? [], ...execArgv],
2610
2697
  terminateTimeout: vitest.config.teardownTimeout,
2611
2698
  concurrentTasksPerWorker: 1
2612
- };
2613
- const isolated = poolOptions.isolate ?? true;
2699
+ }, isolated = poolOptions.isolate ?? true;
2614
2700
  if (isolated) options.isolateWorkers = true;
2615
- if (poolOptions.singleFork || !vitest.config.fileParallelism) {
2616
- options.maxThreads = 1;
2617
- options.minThreads = 1;
2618
- }
2619
- const pool = new Tinypool(options);
2620
- const runWithFiles = (name) => {
2701
+ if (poolOptions.singleFork || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
2702
+ const pool = new Tinypool(options), runWithFiles = (name) => {
2621
2703
  let id = 0;
2622
2704
  async function runFiles(project, config, files, environment, invalidates = []) {
2623
2705
  const paths = files.map((f) => f.filepath);
2624
2706
  vitest.state.clearFiles(project, paths);
2625
- const channel = createChildProcessChannel$1(project, name === "collect");
2626
- const workerId = ++id;
2627
- const data = {
2707
+ const channel = createChildProcessChannel$1(project, name === "collect"), workerId = ++id, data = {
2628
2708
  pool: "forks",
2629
2709
  worker,
2630
2710
  config,
@@ -2650,40 +2730,25 @@ function createForksPool(vitest, { execArgv, env }) {
2650
2730
  return async (specs, invalidates) => {
2651
2731
  // Cancel pending tasks from pool when possible
2652
2732
  vitest.onCancel(() => pool.cancelPendingTasks());
2653
- const configs = /* @__PURE__ */ new WeakMap();
2654
- const getConfig = (project) => {
2733
+ const configs = /* @__PURE__ */ new WeakMap(), getConfig = (project) => {
2655
2734
  if (configs.has(project)) return configs.get(project);
2656
- const _config = project.getSerializableConfig();
2657
- const config = wrapSerializableConfig(_config);
2658
- configs.set(project, config);
2659
- return config;
2660
- };
2661
- const singleFork = specs.filter((spec) => spec.project.config.poolOptions?.forks?.singleFork);
2662
- const multipleForks = specs.filter((spec) => !spec.project.config.poolOptions?.forks?.singleFork);
2735
+ const _config = project.getSerializableConfig(), config = wrapSerializableConfig(_config);
2736
+ return configs.set(project, config), config;
2737
+ }, singleFork = specs.filter((spec) => spec.project.config.poolOptions?.forks?.singleFork), multipleForks = specs.filter((spec) => !spec.project.config.poolOptions?.forks?.singleFork);
2663
2738
  if (multipleForks.length) {
2664
- const filesByEnv = await groupFilesByEnv(multipleForks);
2665
- const files = Object.values(filesByEnv).flat();
2666
- const results = [];
2739
+ const filesByEnv = await groupFilesByEnv(multipleForks), files = Object.values(filesByEnv).flat(), results = [];
2667
2740
  if (isolated) results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
2668
2741
  else {
2669
2742
  // When isolation is disabled, we still need to isolate environments and workspace projects from each other.
2670
2743
  // Tasks are still running parallel but environments are isolated between tasks.
2671
2744
  const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
2672
- for (const group of Object.values(grouped)) {
2673
- // Push all files to pool's queue
2674
- results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
2675
- // Once all tasks are running or finished, recycle worker for isolation.
2676
- // On-going workers will run in the previous environment.
2677
- await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve));
2678
- await pool.recycleWorkers();
2679
- }
2745
+ for (const group of Object.values(grouped)) results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))), await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve)), await pool.recycleWorkers();
2680
2746
  }
2681
2747
  const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
2682
2748
  if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
2683
2749
  }
2684
2750
  if (singleFork.length) {
2685
- const filesByEnv = await groupFilesByEnv(singleFork);
2686
- const envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
2751
+ const filesByEnv = await groupFilesByEnv(singleFork), envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
2687
2752
  for (const env of envs) {
2688
2753
  const files = filesByEnv[env];
2689
2754
  if (!files?.length) continue;
@@ -2707,10 +2772,7 @@ function createForksPool(vitest, { execArgv, env }) {
2707
2772
  }
2708
2773
 
2709
2774
  function createWorkerChannel$1(project, collect) {
2710
- const channel = new MessageChannel();
2711
- const port = channel.port2;
2712
- const workerPort = channel.port1;
2713
- const rpc = createBirpc(createMethodsRPC(project, { collect }), {
2775
+ const channel = new MessageChannel(), port = channel.port2, workerPort = channel.port1, rpc = createBirpc(createMethodsRPC(project, { collect }), {
2714
2776
  eventNames: ["onCancel"],
2715
2777
  post(v) {
2716
2778
  port.postMessage(v);
@@ -2722,9 +2784,7 @@ function createWorkerChannel$1(project, collect) {
2722
2784
  });
2723
2785
  project.vitest.onCancel((reason) => rpc.onCancel(reason));
2724
2786
  const onClose = () => {
2725
- port.close();
2726
- workerPort.close();
2727
- rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
2787
+ port.close(), workerPort.close(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
2728
2788
  };
2729
2789
  return {
2730
2790
  workerPort,
@@ -2733,13 +2793,7 @@ function createWorkerChannel$1(project, collect) {
2733
2793
  };
2734
2794
  }
2735
2795
  function createThreadsPool(vitest, { execArgv, env }) {
2736
- const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
2737
- const threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
2738
- const poolOptions = vitest.config.poolOptions?.threads ?? {};
2739
- const maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount;
2740
- const minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads);
2741
- const worker = resolve(vitest.distPath, "workers/threads.js");
2742
- const options = {
2796
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.threads ?? {}, maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/threads.js"), options = {
2743
2797
  filename: resolve(vitest.distPath, "worker.js"),
2744
2798
  teardown: "teardown",
2745
2799
  useAtomics: poolOptions.useAtomics ?? false,
@@ -2749,22 +2803,15 @@ function createThreadsPool(vitest, { execArgv, env }) {
2749
2803
  execArgv: [...poolOptions.execArgv ?? [], ...execArgv],
2750
2804
  terminateTimeout: vitest.config.teardownTimeout,
2751
2805
  concurrentTasksPerWorker: 1
2752
- };
2753
- const isolated = poolOptions.isolate ?? true;
2806
+ }, isolated = poolOptions.isolate ?? true;
2754
2807
  if (isolated) options.isolateWorkers = true;
2755
- if (poolOptions.singleThread || !vitest.config.fileParallelism) {
2756
- options.maxThreads = 1;
2757
- options.minThreads = 1;
2758
- }
2759
- const pool = new Tinypool$1(options);
2760
- const runWithFiles = (name) => {
2808
+ if (poolOptions.singleThread || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
2809
+ const pool = new Tinypool$1(options), runWithFiles = (name) => {
2761
2810
  let id = 0;
2762
2811
  async function runFiles(project, config, files, environment, invalidates = []) {
2763
2812
  const paths = files.map((f) => f.filepath);
2764
2813
  vitest.state.clearFiles(project, paths);
2765
- const { workerPort, onClose } = createWorkerChannel$1(project, name === "collect");
2766
- const workerId = ++id;
2767
- const data = {
2814
+ const { workerPort, onClose } = createWorkerChannel$1(project, name === "collect"), workerId = ++id, data = {
2768
2815
  pool: "threads",
2769
2816
  worker,
2770
2817
  port: workerPort,
@@ -2792,39 +2839,25 @@ function createThreadsPool(vitest, { execArgv, env }) {
2792
2839
  return async (specs, invalidates) => {
2793
2840
  // Cancel pending tasks from pool when possible
2794
2841
  vitest.onCancel(() => pool.cancelPendingTasks());
2795
- const configs = /* @__PURE__ */ new WeakMap();
2796
- const getConfig = (project) => {
2842
+ const configs = /* @__PURE__ */ new WeakMap(), getConfig = (project) => {
2797
2843
  if (configs.has(project)) return configs.get(project);
2798
2844
  const config = project.serializedConfig;
2799
- configs.set(project, config);
2800
- return config;
2801
- };
2802
- const singleThreads = specs.filter((spec) => spec.project.config.poolOptions?.threads?.singleThread);
2803
- const multipleThreads = specs.filter((spec) => !spec.project.config.poolOptions?.threads?.singleThread);
2845
+ return configs.set(project, config), config;
2846
+ }, singleThreads = specs.filter((spec) => spec.project.config.poolOptions?.threads?.singleThread), multipleThreads = specs.filter((spec) => !spec.project.config.poolOptions?.threads?.singleThread);
2804
2847
  if (multipleThreads.length) {
2805
- const filesByEnv = await groupFilesByEnv(multipleThreads);
2806
- const files = Object.values(filesByEnv).flat();
2807
- const results = [];
2848
+ const filesByEnv = await groupFilesByEnv(multipleThreads), files = Object.values(filesByEnv).flat(), results = [];
2808
2849
  if (isolated) results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
2809
2850
  else {
2810
2851
  // When isolation is disabled, we still need to isolate environments and workspace projects from each other.
2811
2852
  // Tasks are still running parallel but environments are isolated between tasks.
2812
2853
  const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
2813
- for (const group of Object.values(grouped)) {
2814
- // Push all files to pool's queue
2815
- results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
2816
- // Once all tasks are running or finished, recycle worker for isolation.
2817
- // On-going workers will run in the previous environment.
2818
- await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve));
2819
- await pool.recycleWorkers();
2820
- }
2854
+ for (const group of Object.values(grouped)) results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))), await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve)), await pool.recycleWorkers();
2821
2855
  }
2822
2856
  const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
2823
2857
  if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
2824
2858
  }
2825
2859
  if (singleThreads.length) {
2826
- const filesByEnv = await groupFilesByEnv(singleThreads);
2827
- const envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
2860
+ const filesByEnv = await groupFilesByEnv(singleThreads), envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
2828
2861
  for (const env of envs) {
2829
2862
  const files = filesByEnv[env];
2830
2863
  if (!files?.length) continue;
@@ -2848,102 +2881,69 @@ function createThreadsPool(vitest, { execArgv, env }) {
2848
2881
  }
2849
2882
 
2850
2883
  function createTypecheckPool(vitest) {
2851
- const promisesMap = /* @__PURE__ */ new WeakMap();
2852
- const rerunTriggered = /* @__PURE__ */ new WeakSet();
2884
+ const promisesMap = /* @__PURE__ */ new WeakMap(), rerunTriggered = /* @__PURE__ */ new WeakSet();
2853
2885
  async function onParseEnd(project, { files, sourceErrors }) {
2854
- const checker = project.typechecker;
2855
- const { packs, events } = checker.getTestPacksAndEvents();
2856
- await vitest._testRun.updated(packs, events);
2857
- if (!project.config.typecheck.ignoreSourceErrors) sourceErrors.forEach((error) => vitest.state.catchError(error, "Unhandled Source Error"));
2886
+ const checker = project.typechecker, { packs, events } = checker.getTestPacksAndEvents();
2887
+ if (await vitest._testRun.updated(packs, events), !project.config.typecheck.ignoreSourceErrors) sourceErrors.forEach((error) => vitest.state.catchError(error, "Unhandled Source Error"));
2858
2888
  const processError = !hasFailed(files) && !sourceErrors.length && checker.getExitCode();
2859
2889
  if (processError) {
2860
2890
  const error = new Error(checker.getOutput());
2861
- error.stack = "";
2862
- vitest.state.catchError(error, "Typecheck Error");
2891
+ error.stack = "", vitest.state.catchError(error, "Typecheck Error");
2863
2892
  }
2864
- promisesMap.get(project)?.resolve();
2865
- rerunTriggered.delete(project);
2866
2893
  // triggered by TSC watcher, not Vitest watcher, so we need to emulate what Vitest does in this case
2867
- if (vitest.config.watch && !vitest.runningPromise) {
2868
- await vitest.report("onFinished", files, []);
2869
- await vitest.report("onWatcherStart", files, [...project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors, ...vitest.state.getUnhandledErrors()]);
2894
+ if (promisesMap.get(project)?.resolve(), rerunTriggered.delete(project), vitest.config.watch && !vitest.runningPromise) {
2895
+ const modules = files.map((file) => vitest.state.getReportedEntity(file)).filter((e) => e?.type === "module"), state = vitest.isCancelling ? "interrupted" : modules.some((m) => !m.ok()) ? "failed" : "passed";
2896
+ await vitest.report("onTestRunEnd", modules, [], state), await vitest.report("onWatcherStart", files, [...project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors, ...vitest.state.getUnhandledErrors()]);
2870
2897
  }
2871
2898
  }
2872
2899
  async function createWorkspaceTypechecker(project, files) {
2873
2900
  const checker = project.typechecker ?? new Typechecker(project);
2874
- if (project.typechecker) return checker;
2875
- project.typechecker = checker;
2876
- checker.setFiles(files);
2877
- checker.onParseStart(async () => {
2901
+ return project.typechecker ? checker : (project.typechecker = checker, checker.setFiles(files), checker.onParseStart(async () => {
2878
2902
  const files = checker.getTestFiles();
2879
2903
  for (const file of files) await vitest._testRun.enqueued(project, file);
2880
2904
  await vitest._testRun.collected(project, files);
2881
- });
2882
- checker.onParseEnd((result) => onParseEnd(project, result));
2883
- checker.onWatcherRerun(async () => {
2884
- rerunTriggered.add(project);
2885
- if (!vitest.runningPromise) {
2886
- vitest.state.clearErrors();
2887
- await vitest.report("onWatcherRerun", files, "File change detected. Triggering rerun.");
2888
- }
2905
+ }), checker.onParseEnd((result) => onParseEnd(project, result)), checker.onWatcherRerun(async () => {
2906
+ if (rerunTriggered.add(project), !vitest.runningPromise) vitest.state.clearErrors(), await vitest.report("onWatcherRerun", files, "File change detected. Triggering rerun.");
2889
2907
  await checker.collectTests();
2890
2908
  const testFiles = checker.getTestFiles();
2891
2909
  for (const file of testFiles) await vitest._testRun.enqueued(project, file);
2892
2910
  await vitest._testRun.collected(project, testFiles);
2893
2911
  const { packs, events } = checker.getTestPacksAndEvents();
2894
2912
  await vitest._testRun.updated(packs, events);
2895
- });
2896
- return checker;
2913
+ }), checker);
2897
2914
  }
2898
2915
  async function startTypechecker(project, files) {
2899
2916
  if (project.typechecker) return;
2900
2917
  const checker = await createWorkspaceTypechecker(project, files);
2901
- await checker.collectTests();
2902
- await checker.start();
2918
+ await checker.collectTests(), await checker.start();
2903
2919
  }
2904
2920
  async function collectTests(specs) {
2905
2921
  const specsByProject = groupBy(specs, (spec) => spec.project.name);
2906
2922
  for (const name in specsByProject) {
2907
- const project = specsByProject[name][0].project;
2908
- const files = specsByProject[name].map((spec) => spec.moduleId);
2909
- const checker = await createWorkspaceTypechecker(project, files);
2910
- checker.setFiles(files);
2911
- await checker.collectTests();
2923
+ const project = specsByProject[name][0].project, files = specsByProject[name].map((spec) => spec.moduleId), checker = await createWorkspaceTypechecker(project, files);
2924
+ checker.setFiles(files), await checker.collectTests();
2912
2925
  const testFiles = checker.getTestFiles();
2913
2926
  vitest.state.collectFiles(project, testFiles);
2914
2927
  }
2915
2928
  }
2916
2929
  async function runTests(specs) {
2917
- const specsByProject = groupBy(specs, (spec) => spec.project.name);
2918
- const promises = [];
2930
+ const specsByProject = groupBy(specs, (spec) => spec.project.name), promises = [];
2919
2931
  for (const name in specsByProject) {
2920
- const project = specsByProject[name][0].project;
2921
- const files = specsByProject[name].map((spec) => spec.moduleId);
2922
- const promise = createDefer();
2923
- // check that watcher actually triggered rerun
2924
- const _p = new Promise((resolve) => {
2932
+ const project = specsByProject[name][0].project, files = specsByProject[name].map((spec) => spec.moduleId), promise = createDefer(), _p = new Promise((resolve) => {
2925
2933
  const _i = setInterval(() => {
2926
- if (!project.typechecker || rerunTriggered.has(project)) {
2927
- resolve(true);
2928
- clearInterval(_i);
2929
- }
2934
+ if (!project.typechecker || rerunTriggered.has(project)) resolve(true), clearInterval(_i);
2930
2935
  });
2931
2936
  setTimeout(() => {
2932
- resolve(false);
2933
- clearInterval(_i);
2937
+ resolve(false), clearInterval(_i);
2934
2938
  }, 500).unref();
2935
- });
2936
- const triggered = await _p;
2939
+ }), triggered = await _p;
2937
2940
  if (project.typechecker && !triggered) {
2938
2941
  const testFiles = project.typechecker.getTestFiles();
2939
2942
  for (const file of testFiles) await vitest._testRun.enqueued(project, file);
2940
- await vitest._testRun.collected(project, testFiles);
2941
- await onParseEnd(project, project.typechecker.getResult());
2943
+ await vitest._testRun.collected(project, testFiles), await onParseEnd(project, project.typechecker.getResult());
2942
2944
  continue;
2943
2945
  }
2944
- promises.push(promise);
2945
- promisesMap.set(project, promise);
2946
- promises.push(startTypechecker(project, files));
2946
+ promises.push(promise), promisesMap.set(project, promise), promises.push(startTypechecker(project, files));
2947
2947
  }
2948
2948
  await Promise.all(promises);
2949
2949
  }
@@ -2987,8 +2987,7 @@ function stringToBytes(input, percentageReference) {
2987
2987
  let [, numericString, trailingChars] = input.match(/(.*?)([^0-9.-]+)$/) || [];
2988
2988
  if (trailingChars && numericString) {
2989
2989
  const numericValue = Number.parseFloat(numericString);
2990
- trailingChars = trailingChars.toLowerCase();
2991
- switch (trailingChars) {
2990
+ switch (trailingChars = trailingChars.toLowerCase(), trailingChars) {
2992
2991
  case "%":
2993
2992
  input = numericValue / 100;
2994
2993
  break;
@@ -3004,21 +3003,20 @@ function stringToBytes(input, percentageReference) {
3004
3003
  }
3005
3004
  }
3006
3005
  } else input = Number.parseFloat(input);
3007
- if (typeof input === "number") if (input <= 1 && input > 0) if (percentageReference) return Math.floor(input * percentageReference);
3008
- else throw new Error("For a percentage based memory limit a percentageReference must be supplied");
3009
- else if (input > 1) return Math.floor(input);
3006
+ if (typeof input === "number") if (input <= 1 && input > 0) {
3007
+ if (percentageReference) return Math.floor(input * percentageReference);
3008
+ throw new Error("For a percentage based memory limit a percentageReference must be supplied");
3009
+ } else if (input > 1) return Math.floor(input);
3010
3010
  else throw new Error("Unexpected numerical input for \"memoryLimit\"");
3011
3011
  return null;
3012
3012
  }
3013
3013
 
3014
3014
  const suppressWarningsPath$1 = resolve(rootDir, "./suppress-warnings.cjs");
3015
3015
  function createChildProcessChannel(project, collect) {
3016
- const emitter = new EventEmitter();
3017
- const events = {
3016
+ const emitter = new EventEmitter(), events = {
3018
3017
  message: "message",
3019
3018
  response: "response"
3020
- };
3021
- const rpc = createBirpc(createMethodsRPC(project, {
3019
+ }, rpc = createBirpc(createMethodsRPC(project, {
3022
3020
  cacheFs: true,
3023
3021
  collect
3024
3022
  }), {
@@ -3048,20 +3046,13 @@ function createChildProcessChannel(project, collect) {
3048
3046
  onMessage: (callback) => emitter.on(events.message, callback),
3049
3047
  postMessage: (message) => emitter.emit(events.response, message),
3050
3048
  onClose: () => {
3051
- emitter.removeAllListeners();
3052
- rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
3049
+ emitter.removeAllListeners(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
3053
3050
  }
3054
3051
  };
3055
3052
  return { channel };
3056
3053
  }
3057
3054
  function createVmForksPool(vitest, { execArgv, env }) {
3058
- const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
3059
- const threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
3060
- const poolOptions = vitest.config.poolOptions?.vmForks ?? {};
3061
- const maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount;
3062
- const minThreads = poolOptions.maxForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads);
3063
- const worker = resolve(vitest.distPath, "workers/vmForks.js");
3064
- const options = {
3055
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.vmForks ?? {}, maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.maxForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/vmForks.js"), options = {
3065
3056
  runtime: "child_process",
3066
3057
  filename: resolve(vitest.distPath, "worker.js"),
3067
3058
  maxThreads,
@@ -3079,19 +3070,13 @@ function createVmForksPool(vitest, { execArgv, env }) {
3079
3070
  concurrentTasksPerWorker: 1,
3080
3071
  maxMemoryLimitBeforeRecycle: getMemoryLimit$1(vitest.config) || void 0
3081
3072
  };
3082
- if (poolOptions.singleFork || !vitest.config.fileParallelism) {
3083
- options.maxThreads = 1;
3084
- options.minThreads = 1;
3085
- }
3086
- const pool = new Tinypool$1(options);
3087
- const runWithFiles = (name) => {
3073
+ if (poolOptions.singleFork || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
3074
+ const pool = new Tinypool$1(options), runWithFiles = (name) => {
3088
3075
  let id = 0;
3089
3076
  async function runFiles(project, config, files, environment, invalidates = []) {
3090
3077
  const paths = files.map((f) => f.filepath);
3091
3078
  vitest.state.clearFiles(project, paths);
3092
- const { channel } = createChildProcessChannel(project, name === "collect");
3093
- const workerId = ++id;
3094
- const data = {
3079
+ const { channel } = createChildProcessChannel(project, name === "collect"), workerId = ++id, data = {
3095
3080
  pool: "forks",
3096
3081
  worker,
3097
3082
  config,
@@ -3119,18 +3104,11 @@ function createVmForksPool(vitest, { execArgv, env }) {
3119
3104
  return async (specs, invalidates) => {
3120
3105
  // Cancel pending tasks from pool when possible
3121
3106
  vitest.onCancel(() => pool.cancelPendingTasks());
3122
- const configs = /* @__PURE__ */ new Map();
3123
- const getConfig = (project) => {
3107
+ const configs = /* @__PURE__ */ new Map(), getConfig = (project) => {
3124
3108
  if (configs.has(project)) return configs.get(project);
3125
- const _config = project.serializedConfig;
3126
- const config = wrapSerializableConfig(_config);
3127
- configs.set(project, config);
3128
- return config;
3129
- };
3130
- const filesByEnv = await groupFilesByEnv(specs);
3131
- const promises = Object.values(filesByEnv).flat();
3132
- const results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)));
3133
- const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
3109
+ const _config = project.serializedConfig, config = wrapSerializableConfig(_config);
3110
+ return configs.set(project, config), config;
3111
+ }, filesByEnv = await groupFilesByEnv(specs), promises = Object.values(filesByEnv).flat(), results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))), errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
3134
3112
  if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
3135
3113
  };
3136
3114
  };
@@ -3142,21 +3120,14 @@ function createVmForksPool(vitest, { execArgv, env }) {
3142
3120
  };
3143
3121
  }
3144
3122
  function getMemoryLimit$1(config) {
3145
- const memory = nodeos.totalmem();
3146
- const limit = getWorkerMemoryLimit(config, "vmForks");
3147
- if (typeof memory === "number") return stringToBytes(limit, config.watch ? memory / 2 : memory);
3148
- // If totalmem is not supported we cannot resolve percentage based values like 0.5, "50%"
3149
- if (typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%") return stringToBytes(limit);
3123
+ const memory = nodeos.totalmem(), limit = getWorkerMemoryLimit(config, "vmForks");
3150
3124
  // just ignore "memoryLimit" value because we cannot detect memory limit
3151
- return null;
3125
+ return typeof memory === "number" ? stringToBytes(limit, config.watch ? memory / 2 : memory) : typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%" ? stringToBytes(limit) : null;
3152
3126
  }
3153
3127
 
3154
3128
  const suppressWarningsPath = resolve(rootDir, "./suppress-warnings.cjs");
3155
3129
  function createWorkerChannel(project, collect) {
3156
- const channel = new MessageChannel();
3157
- const port = channel.port2;
3158
- const workerPort = channel.port1;
3159
- const rpc = createBirpc(createMethodsRPC(project, { collect }), {
3130
+ const channel = new MessageChannel(), port = channel.port2, workerPort = channel.port1, rpc = createBirpc(createMethodsRPC(project, { collect }), {
3160
3131
  eventNames: ["onCancel"],
3161
3132
  post(v) {
3162
3133
  port.postMessage(v);
@@ -3168,9 +3139,7 @@ function createWorkerChannel(project, collect) {
3168
3139
  });
3169
3140
  project.vitest.onCancel((reason) => rpc.onCancel(reason));
3170
3141
  function onClose() {
3171
- workerPort.close();
3172
- port.close();
3173
- rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
3142
+ workerPort.close(), port.close(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
3174
3143
  }
3175
3144
  return {
3176
3145
  workerPort,
@@ -3178,13 +3147,7 @@ function createWorkerChannel(project, collect) {
3178
3147
  };
3179
3148
  }
3180
3149
  function createVmThreadsPool(vitest, { execArgv, env }) {
3181
- const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
3182
- const threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
3183
- const poolOptions = vitest.config.poolOptions?.vmThreads ?? {};
3184
- const maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount;
3185
- const minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads);
3186
- const worker = resolve(vitest.distPath, "workers/vmThreads.js");
3187
- const options = {
3150
+ const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.vmThreads ?? {}, maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/vmThreads.js"), options = {
3188
3151
  filename: resolve(vitest.distPath, "worker.js"),
3189
3152
  useAtomics: poolOptions.useAtomics ?? false,
3190
3153
  maxThreads,
@@ -3202,19 +3165,13 @@ function createVmThreadsPool(vitest, { execArgv, env }) {
3202
3165
  concurrentTasksPerWorker: 1,
3203
3166
  maxMemoryLimitBeforeRecycle: getMemoryLimit(vitest.config) || void 0
3204
3167
  };
3205
- if (poolOptions.singleThread || !vitest.config.fileParallelism) {
3206
- options.maxThreads = 1;
3207
- options.minThreads = 1;
3208
- }
3209
- const pool = new Tinypool$1(options);
3210
- const runWithFiles = (name) => {
3168
+ if (poolOptions.singleThread || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
3169
+ const pool = new Tinypool$1(options), runWithFiles = (name) => {
3211
3170
  let id = 0;
3212
3171
  async function runFiles(project, config, files, environment, invalidates = []) {
3213
3172
  const paths = files.map((f) => f.filepath);
3214
3173
  vitest.state.clearFiles(project, paths);
3215
- const { workerPort, onClose } = createWorkerChannel(project, name === "collect");
3216
- const workerId = ++id;
3217
- const data = {
3174
+ const { workerPort, onClose } = createWorkerChannel(project, name === "collect"), workerId = ++id, data = {
3218
3175
  pool: "vmThreads",
3219
3176
  worker,
3220
3177
  port: workerPort,
@@ -3243,17 +3200,11 @@ function createVmThreadsPool(vitest, { execArgv, env }) {
3243
3200
  return async (specs, invalidates) => {
3244
3201
  // Cancel pending tasks from pool when possible
3245
3202
  vitest.onCancel(() => pool.cancelPendingTasks());
3246
- const configs = /* @__PURE__ */ new Map();
3247
- const getConfig = (project) => {
3203
+ const configs = /* @__PURE__ */ new Map(), getConfig = (project) => {
3248
3204
  if (configs.has(project)) return configs.get(project);
3249
3205
  const config = project.serializedConfig;
3250
- configs.set(project, config);
3251
- return config;
3252
- };
3253
- const filesByEnv = await groupFilesByEnv(specs);
3254
- const promises = Object.values(filesByEnv).flat();
3255
- const results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)));
3256
- const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
3206
+ return configs.set(project, config), config;
3207
+ }, filesByEnv = await groupFilesByEnv(specs), promises = Object.values(filesByEnv).flat(), results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))), errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
3257
3208
  if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
3258
3209
  };
3259
3210
  };
@@ -3265,13 +3216,9 @@ function createVmThreadsPool(vitest, { execArgv, env }) {
3265
3216
  };
3266
3217
  }
3267
3218
  function getMemoryLimit(config) {
3268
- const memory = nodeos.totalmem();
3269
- const limit = getWorkerMemoryLimit(config, "vmThreads");
3270
- if (typeof memory === "number") return stringToBytes(limit, config.watch ? memory / 2 : memory);
3271
- // If totalmem is not supported we cannot resolve percentage based values like 0.5, "50%"
3272
- if (typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%") return stringToBytes(limit);
3219
+ const memory = nodeos.totalmem(), limit = getWorkerMemoryLimit(config, "vmThreads");
3273
3220
  // just ignore "memoryLimit" value because we cannot detect memory limit
3274
- return null;
3221
+ return typeof memory === "number" ? stringToBytes(limit, config.watch ? memory / 2 : memory) : typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%" ? stringToBytes(limit) : null;
3275
3222
  }
3276
3223
 
3277
3224
  const builtinPools = [
@@ -3283,8 +3230,7 @@ const builtinPools = [
3283
3230
  "typescript"
3284
3231
  ];
3285
3232
  function getDefaultPoolName(project) {
3286
- if (project.config.browser.enabled) return "browser";
3287
- return project.config.pool;
3233
+ return project.config.browser.enabled ? "browser" : project.config.pool;
3288
3234
  }
3289
3235
  function getFilePoolName(project) {
3290
3236
  return getDefaultPoolName(project);
@@ -3297,26 +3243,15 @@ function createPool(ctx) {
3297
3243
  vmThreads: null,
3298
3244
  vmForks: null,
3299
3245
  typescript: null
3300
- };
3301
- // in addition to resolve.conditions Vite also adds production/development,
3302
- // see: https://github.com/vitejs/vite/blob/af2aa09575229462635b7cbb6d248ca853057ba2/packages/vite/src/node/plugins/resolve.ts#L1056-L1080
3303
- const viteMajor = Number(version.split(".")[0]);
3304
- const potentialConditions = new Set(viteMajor >= 6 ? ctx.vite.config.ssr.resolve?.conditions ?? [] : [
3246
+ }, viteMajor = Number(version.split(".")[0]), potentialConditions = new Set(viteMajor >= 6 ? ctx.vite.config.ssr.resolve?.conditions ?? [] : [
3305
3247
  "production",
3306
3248
  "development",
3307
3249
  ...ctx.vite.config.resolve.conditions
3308
- ]);
3309
- const conditions = [...potentialConditions].filter((condition) => {
3310
- if (condition === "production") return ctx.vite.config.isProduction;
3311
- if (condition === "development") return !ctx.vite.config.isProduction;
3312
- return true;
3250
+ ]), conditions = [...potentialConditions].filter((condition) => {
3251
+ return condition === "production" ? ctx.vite.config.isProduction : condition === "development" ? !ctx.vite.config.isProduction : true;
3313
3252
  }).map((condition) => {
3314
- if (viteMajor >= 6 && condition === "development|production") return ctx.vite.config.isProduction ? "production" : "development";
3315
- return condition;
3316
- }).flatMap((c) => ["--conditions", c]);
3317
- // Instead of passing whole process.execArgv to the workers, pick allowed options.
3318
- // Some options may crash worker, e.g. --prof, --title. nodejs/node#41103
3319
- const execArgv = process.execArgv.filter((execArg) => execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"));
3253
+ return viteMajor >= 6 && condition === "development|production" ? ctx.vite.config.isProduction ? "production" : "development" : condition;
3254
+ }).flatMap((c) => ["--conditions", c]), execArgv = process.execArgv.filter((execArg) => execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"));
3320
3255
  async function executeTests(method, files, invalidate) {
3321
3256
  const options = {
3322
3257
  execArgv: [...execArgv, ...conditions],
@@ -3332,25 +3267,22 @@ function createPool(ctx) {
3332
3267
  };
3333
3268
  // env are case-insensitive on Windows, but spawned processes don't support it
3334
3269
  if (isWindows) for (const name in options.env) options.env[name.toUpperCase()] = options.env[name];
3335
- const poolConcurrentPromises = /* @__PURE__ */ new Map();
3336
- const customPools = /* @__PURE__ */ new Map();
3270
+ const poolConcurrentPromises = /* @__PURE__ */ new Map(), customPools = /* @__PURE__ */ new Map();
3337
3271
  async function resolveCustomPool(filepath) {
3338
3272
  if (customPools.has(filepath)) return customPools.get(filepath);
3339
- const pool = await ctx.runner.executeId(filepath);
3273
+ const pool = await ctx.runner.import(filepath);
3340
3274
  if (typeof pool.default !== "function") throw new TypeError(`Custom pool "${filepath}" must export a function as default export`);
3341
3275
  const poolInstance = await pool.default(ctx, options);
3342
3276
  if (typeof poolInstance?.name !== "string") throw new TypeError(`Custom pool "${filepath}" should return an object with "name" property`);
3343
3277
  if (typeof poolInstance?.[method] !== "function") throw new TypeError(`Custom pool "${filepath}" should return an object with "${method}" method`);
3344
- customPools.set(filepath, poolInstance);
3345
- return poolInstance;
3278
+ return customPools.set(filepath, poolInstance), poolInstance;
3346
3279
  }
3347
3280
  function getConcurrentPool(pool, fn) {
3348
3281
  if (poolConcurrentPromises.has(pool)) return poolConcurrentPromises.get(pool);
3349
3282
  const promise = fn().finally(() => {
3350
3283
  poolConcurrentPromises.delete(pool);
3351
3284
  });
3352
- poolConcurrentPromises.set(pool, promise);
3353
- return promise;
3285
+ return poolConcurrentPromises.set(pool, promise), promise;
3354
3286
  }
3355
3287
  function getCustomPool(pool) {
3356
3288
  return getConcurrentPool(pool, () => resolveCustomPool(pool));
@@ -3361,9 +3293,7 @@ function createPool(ctx) {
3361
3293
  return createBrowserPool(ctx);
3362
3294
  });
3363
3295
  }
3364
- const groupedSpecifications = {};
3365
- const groups = /* @__PURE__ */ new Set();
3366
- const factories = {
3296
+ const groupedSpecifications = {}, groups = /* @__PURE__ */ new Set(), factories = {
3367
3297
  vmThreads: () => createVmThreadsPool(ctx, options),
3368
3298
  vmForks: () => createVmForksPool(ctx, options),
3369
3299
  threads: () => createThreadsPool(ctx, options),
@@ -3372,12 +3302,9 @@ function createPool(ctx) {
3372
3302
  };
3373
3303
  for (const spec of files) {
3374
3304
  const group = spec[0].config.sequence.groupOrder ?? 0;
3375
- groups.add(group);
3376
- groupedSpecifications[group] ??= [];
3377
- groupedSpecifications[group].push(spec);
3305
+ groups.add(group), groupedSpecifications[group] ??= [], groupedSpecifications[group].push(spec);
3378
3306
  }
3379
- const Sequencer = ctx.config.sequence.sequencer;
3380
- const sequencer = new Sequencer(ctx);
3307
+ const Sequencer = ctx.config.sequence.sequencer, sequencer = new Sequencer(ctx);
3381
3308
  async function sortSpecs(specs) {
3382
3309
  if (ctx.config.shard) {
3383
3310
  if (!ctx.config.passWithNoTests && ctx.config.shard.count > specs.length) throw new Error(`--shard <count> must be a smaller than count of test files. Resolved ${specs.length} test files for --shard=${ctx.config.shard.index}/${ctx.config.shard.count}.`);
@@ -3398,25 +3325,18 @@ function createPool(ctx) {
3398
3325
  };
3399
3326
  specifications.forEach((specification) => {
3400
3327
  const pool = specification[2].pool;
3401
- filesByPool[pool] ??= [];
3402
- filesByPool[pool].push(specification);
3403
- });
3404
- await Promise.all(Object.entries(filesByPool).map(async (entry) => {
3328
+ filesByPool[pool] ??= [], filesByPool[pool].push(specification);
3329
+ }), await Promise.all(Object.entries(filesByPool).map(async (entry) => {
3405
3330
  const [pool, files] = entry;
3406
3331
  if (!files.length) return null;
3407
3332
  const specs = await sortSpecs(files);
3408
3333
  if (pool in factories) {
3409
3334
  const factory = factories[pool];
3410
- pools[pool] ??= factory();
3411
- return pools[pool][method](specs, invalidate);
3412
- }
3413
- if (pool === "browser") {
3414
- pools.browser ??= await getBrowserPool();
3415
- return pools.browser[method](specs, invalidate);
3335
+ return pools[pool] ??= factory(), pools[pool][method](specs, invalidate);
3416
3336
  }
3337
+ if (pool === "browser") return pools.browser ??= await getBrowserPool(), pools.browser[method](specs, invalidate);
3417
3338
  const poolHandler = await getCustomPool(pool);
3418
- pools[poolHandler.name] ??= poolHandler;
3419
- return poolHandler[method](specs, invalidate);
3339
+ return pools[poolHandler.name] ??= poolHandler, poolHandler[method](specs, invalidate);
3420
3340
  }));
3421
3341
  }
3422
3342
  }
@@ -3437,12 +3357,9 @@ class BaseSequencer {
3437
3357
  }
3438
3358
  // async so it can be extended by other sequelizers
3439
3359
  async shard(files) {
3440
- const { config } = this.ctx;
3441
- const { index, count } = config.shard;
3442
- const [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
3360
+ const { config } = this.ctx, { index, count } = config.shard, [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
3443
3361
  return [...files].map((spec) => {
3444
- const fullPath = resolve$1(slash(config.root), slash(spec.moduleId));
3445
- const specPath = fullPath?.slice(config.root.length);
3362
+ const fullPath = resolve$1(slash(config.root), slash(spec.moduleId)), specPath = fullPath?.slice(config.root.length);
3446
3363
  return {
3447
3364
  spec,
3448
3365
  hash: hash("sha1", specPath, "hex")
@@ -3453,37 +3370,24 @@ class BaseSequencer {
3453
3370
  async sort(files) {
3454
3371
  const cache = this.ctx.cache;
3455
3372
  return [...files].sort((a, b) => {
3456
- const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}`;
3457
- const keyB = `${b.project.name}:${relative(this.ctx.config.root, b.moduleId)}`;
3458
- const aState = cache.getFileTestResults(keyA);
3459
- const bState = cache.getFileTestResults(keyB);
3373
+ 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);
3460
3374
  if (!aState || !bState) {
3461
- const statsA = cache.getFileStats(keyA);
3462
- const statsB = cache.getFileStats(keyB);
3463
- // run unknown first
3464
- if (!statsA || !statsB) return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0;
3375
+ const statsA = cache.getFileStats(keyA), statsB = cache.getFileStats(keyB);
3465
3376
  // run larger files first
3466
- return statsB.size - statsA.size;
3377
+ return !statsA || !statsB ? !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0 : statsB.size - statsA.size;
3467
3378
  }
3468
- // run failed first
3469
- if (aState.failed && !bState.failed) return -1;
3470
- if (!aState.failed && bState.failed) return 1;
3471
3379
  // run longer first
3472
- return bState.duration - aState.duration;
3380
+ return aState.failed && !bState.failed ? -1 : !aState.failed && bState.failed ? 1 : bState.duration - aState.duration;
3473
3381
  });
3474
3382
  }
3475
3383
  // Calculate distributed shard range [start, end] distributed equally
3476
3384
  calculateShardRange(filesCount, index, count) {
3477
- const baseShardSize = Math.floor(filesCount / count);
3478
- const remainderTestFilesCount = filesCount % count;
3385
+ const baseShardSize = Math.floor(filesCount / count), remainderTestFilesCount = filesCount % count;
3479
3386
  if (remainderTestFilesCount >= index) {
3480
- const shardSize = baseShardSize + 1;
3481
- const shardStart = shardSize * (index - 1);
3482
- const shardEnd = shardSize * index;
3387
+ const shardSize = baseShardSize + 1, shardStart = shardSize * (index - 1), shardEnd = shardSize * index;
3483
3388
  return [shardStart, shardEnd];
3484
3389
  }
3485
- const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
3486
- const shardEnd = shardStart + baseShardSize;
3390
+ const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize, shardEnd = shardStart + baseShardSize;
3487
3391
  return [shardStart, shardEnd];
3488
3392
  }
3489
3393
  }
@@ -3503,11 +3407,10 @@ function parseInspector(inspect) {
3503
3407
  if (typeof inspect === "number") return { port: inspect };
3504
3408
  if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
3505
3409
  const [host, port] = inspect.split(":");
3506
- if (!port) return { host };
3507
- return {
3410
+ return port ? {
3508
3411
  host,
3509
3412
  port: Number(port) || defaultInspectPort
3510
- };
3413
+ } : { host };
3511
3414
  }
3512
3415
  function resolveApiServerConfig(options, defaultPort) {
3513
3416
  let api;
@@ -3525,12 +3428,10 @@ function resolveApiServerConfig(options, defaultPort) {
3525
3428
  return api;
3526
3429
  }
3527
3430
  function resolveInlineWorkerOption(value) {
3528
- if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
3529
- else return Number(value);
3431
+ return typeof value === "string" && value.trim().endsWith("%") ? getWorkersCountByPercentage(value) : Number(value);
3530
3432
  }
3531
3433
  function resolveConfig$1(vitest, options, viteConfig) {
3532
- const mode = vitest.mode;
3533
- const logger = vitest.logger;
3434
+ const mode = vitest.mode, logger = vitest.logger;
3534
3435
  if (options.dom) {
3535
3436
  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}"`));
3536
3437
  options.environment = "happy-dom";
@@ -3541,25 +3442,17 @@ function resolveConfig$1(vitest, options, viteConfig) {
3541
3442
  root: viteConfig.root,
3542
3443
  mode
3543
3444
  };
3544
- resolved.project = toArray(resolved.project);
3545
- resolved.provide ??= {};
3546
- resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "";
3547
- resolved.color = typeof options.name !== "string" ? options.name?.color : void 0;
3548
- if (resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enabled Browser Mode, use "test.browser.enabled" instead.`);
3445
+ if (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 enabled Browser Mode, use "test.browser.enabled" instead.`);
3549
3446
  const inspector = resolved.inspect || resolved.inspectBrk;
3550
- resolved.inspector = {
3447
+ if (resolved.inspector = {
3551
3448
  ...resolved.inspector,
3552
3449
  ...parseInspector(inspector),
3553
3450
  enabled: !!inspector,
3554
3451
  waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
3555
- };
3556
- if (viteConfig.base !== "/") resolved.base = viteConfig.base;
3557
- resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true;
3558
- if (options.shard) {
3452
+ }, viteConfig.base !== "/") resolved.base = viteConfig.base;
3453
+ if (resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true, options.shard) {
3559
3454
  if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
3560
- const [indexString, countString] = options.shard.split("/");
3561
- const index = Math.abs(Number.parseInt(indexString, 10));
3562
- const count = Math.abs(Number.parseInt(countString, 10));
3455
+ const [indexString, countString] = options.shard.split("/"), index = Math.abs(Number.parseInt(indexString, 10)), count = Math.abs(Number.parseInt(countString, 10));
3563
3456
  if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
3564
3457
  if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
3565
3458
  resolved.shard = {
@@ -3571,20 +3464,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
3571
3464
  if (resolved.mergeReports && resolved.watch) throw new Error(`Cannot merge reports with --watch enabled`);
3572
3465
  if (resolved.maxWorkers) resolved.maxWorkers = resolveInlineWorkerOption(resolved.maxWorkers);
3573
3466
  if (resolved.minWorkers) resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers);
3574
- // run benchmark sequentially by default
3575
- resolved.fileParallelism ??= mode !== "benchmark";
3576
- if (!resolved.fileParallelism) {
3577
- // ignore user config, parallelism cannot be implemented without limiting workers
3578
- resolved.maxWorkers = 1;
3579
- resolved.minWorkers = 1;
3580
- }
3581
- if (resolved.maxConcurrency === 0) {
3582
- logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`));
3583
- resolved.maxConcurrency = configDefaults.maxConcurrency;
3584
- }
3467
+ if (resolved.fileParallelism ??= mode !== "benchmark", !resolved.fileParallelism) resolved.maxWorkers = 1, resolved.minWorkers = 1;
3468
+ 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;
3585
3469
  if (resolved.inspect || resolved.inspectBrk) {
3586
- const isSingleThread = resolved.pool === "threads" && resolved.poolOptions?.threads?.singleThread;
3587
- const isSingleFork = resolved.pool === "forks" && resolved.poolOptions?.forks?.singleFork;
3470
+ const isSingleThread = resolved.pool === "threads" && resolved.poolOptions?.threads?.singleThread, isSingleFork = resolved.pool === "forks" && resolved.poolOptions?.forks?.singleFork;
3588
3471
  if (resolved.fileParallelism && !isSingleThread && !isSingleFork) {
3589
3472
  const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
3590
3473
  throw new Error(`You cannot use ${inspectOption} without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"`);
@@ -3622,99 +3505,44 @@ function resolveConfig$1(vitest, options, viteConfig) {
3622
3505
  } }, null, 2)}\n\n...or disable ${inspectOption}\n`);
3623
3506
  }
3624
3507
  }
3625
- resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter);
3626
- if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
3508
+ if (resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter), resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
3627
3509
  const reportsDirectory = resolve$1(resolved.root, resolved.coverage.reportsDirectory);
3628
3510
  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`);
3629
3511
  }
3630
3512
  if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
3631
- resolved.expect ??= {};
3632
- resolved.deps ??= {};
3633
- resolved.deps.moduleDirectories ??= [];
3634
- resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => {
3513
+ resolved.expect ??= {}, resolved.deps ??= {}, resolved.deps.moduleDirectories ??= [];
3514
+ const envModuleDirectories = process.env.VITEST_MODULE_DIRECTORIES || process.env.npm_config_VITEST_MODULE_DIRECTORIES;
3515
+ if (envModuleDirectories) resolved.deps.moduleDirectories.push(...envModuleDirectories.split(","));
3516
+ if (resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => {
3635
3517
  if (!dir.startsWith("/")) dir = `/${dir}`;
3636
3518
  if (!dir.endsWith("/")) dir += "/";
3637
3519
  return normalize(dir);
3638
- });
3639
- if (!resolved.deps.moduleDirectories.includes("/node_modules/")) resolved.deps.moduleDirectories.push("/node_modules/");
3640
- resolved.deps.optimizer ??= {};
3641
- resolved.deps.optimizer.ssr ??= {};
3642
- resolved.deps.optimizer.ssr.enabled ??= true;
3643
- resolved.deps.optimizer.web ??= {};
3644
- resolved.deps.optimizer.web.enabled ??= true;
3645
- resolved.deps.web ??= {};
3646
- resolved.deps.web.transformAssets ??= true;
3647
- resolved.deps.web.transformCss ??= true;
3648
- resolved.deps.web.transformGlobPattern ??= [];
3649
- resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root));
3650
- resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root));
3651
- // Add hard-coded default coverage exclusions. These cannot be overidden by user config.
3652
- // Override original exclude array for cases where user re-uses same object in test.exclude.
3653
- resolved.coverage.exclude = [
3520
+ }), !resolved.deps.moduleDirectories.includes("/node_modules/")) resolved.deps.moduleDirectories.push("/node_modules/");
3521
+ if (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 = [
3654
3522
  ...resolved.coverage.exclude,
3655
3523
  ...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
3656
3524
  ...resolved.include,
3657
- resolved.config && slash$1(resolved.config),
3525
+ resolved.config && slash(resolved.config),
3658
3526
  ...configFiles,
3659
3527
  "**/virtual:*",
3660
3528
  "**/__x00__*",
3661
3529
  "**/node_modules/**"
3662
- ].filter((pattern) => pattern != null);
3663
- resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles];
3664
- resolved.server ??= {};
3665
- resolved.server.deps ??= {};
3666
- const deprecatedDepsOptions = [
3667
- "inline",
3668
- "external",
3669
- "fallbackCJS"
3670
- ];
3671
- deprecatedDepsOptions.forEach((option) => {
3672
- if (resolved.deps[option] === void 0) return;
3673
- if (option === "fallbackCJS") logger.console.warn(c.yellow(`${c.inverse(c.yellow(" Vitest "))} "deps.${option}" is deprecated. Use "server.deps.${option}" instead`));
3674
- else {
3675
- const transformMode = resolved.environment === "happy-dom" || resolved.environment === "jsdom" ? "web" : "ssr";
3676
- logger.console.warn(c.yellow(`${c.inverse(c.yellow(" Vitest "))} "deps.${option}" is deprecated. If you rely on vite-node directly, use "server.deps.${option}" instead. Otherwise, consider using "deps.optimizer.${transformMode}.${option === "external" ? "exclude" : "include"}"`));
3677
- }
3678
- if (resolved.server.deps[option] === void 0) resolved.server.deps[option] = resolved.deps[option];
3679
- });
3680
- if (resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
3681
- // vitenode will try to import such file with native node,
3682
- // but then our mocker will not work properly
3683
- if (resolved.server.deps.inline !== true) {
3684
- const ssrOptions = viteConfig.ssr;
3685
- if (ssrOptions?.noExternal === true && resolved.server.deps.inline == null) resolved.server.deps.inline = true;
3686
- else {
3687
- resolved.server.deps.inline ??= [];
3688
- resolved.server.deps.inline.push(...extraInlineDeps);
3689
- }
3690
- }
3691
- resolved.server.deps.inlineFiles ??= [];
3692
- resolved.server.deps.inlineFiles.push(...resolved.setupFiles);
3693
- resolved.server.deps.moduleDirectories ??= [];
3694
- resolved.server.deps.moduleDirectories.push(...resolved.deps.moduleDirectories);
3530
+ ].filter((pattern) => pattern != null), resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles], resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
3695
3531
  if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
3696
- resolved.attachmentsDir = resolve$1(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments");
3697
- if (resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
3698
- resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0;
3699
- if (resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
3700
- resolved.snapshotFormat.plugins = [];
3532
+ if (resolved.attachmentsDir = resolve$1(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments"), resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
3533
+ if (resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0, resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
3701
3534
  // TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
3702
- if (typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
3535
+ if (resolved.snapshotFormat.plugins = [], typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
3703
3536
  }
3704
3537
  const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
3705
- resolved.snapshotOptions = {
3538
+ if (resolved.snapshotOptions = {
3706
3539
  expand: resolved.expandSnapshotDiff ?? false,
3707
3540
  snapshotFormat: resolved.snapshotFormat || {},
3708
3541
  updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
3709
3542
  resolveSnapshotPath: options.resolveSnapshotPath,
3710
3543
  snapshotEnvironment: null
3711
- };
3712
- resolved.snapshotSerializers ??= [];
3713
- resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root));
3714
- resolved.forceRerunTriggers.push(...resolved.snapshotSerializers);
3715
- if (options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
3716
- resolved.pool ??= "threads";
3717
- if (process.env.VITEST_MAX_THREADS) resolved.poolOptions = {
3544
+ }, resolved.snapshotSerializers ??= [], resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root)), resolved.forceRerunTriggers.push(...resolved.snapshotSerializers), options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
3545
+ if (resolved.pool ??= "threads", process.env.VITEST_MAX_THREADS) resolved.poolOptions = {
3718
3546
  ...resolved.poolOptions,
3719
3547
  threads: {
3720
3548
  ...resolved.poolOptions?.threads,
@@ -3777,13 +3605,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
3777
3605
  resolved.benchmark = {
3778
3606
  ...benchmarkConfigDefaults,
3779
3607
  ...resolved.benchmark
3780
- };
3781
- // override test config
3782
- resolved.coverage.enabled = false;
3783
- resolved.typecheck.enabled = false;
3784
- resolved.include = resolved.benchmark.include;
3785
- resolved.exclude = resolved.benchmark.exclude;
3786
- resolved.includeSource = resolved.benchmark.includeSource;
3608
+ }, resolved.coverage.enabled = false, resolved.typecheck.enabled = false, resolved.include = resolved.benchmark.include, resolved.exclude = resolved.benchmark.exclude, resolved.includeSource = resolved.benchmark.includeSource;
3787
3609
  const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
3788
3610
  if (reporters.length) resolved.benchmark.reporters = reporters;
3789
3611
  else resolved.benchmark.reporters = ["default"];
@@ -3792,17 +3614,13 @@ function resolveConfig$1(vitest, options, viteConfig) {
3792
3614
  if (options.compare) resolved.benchmark.compare = options.compare;
3793
3615
  if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
3794
3616
  }
3795
- if (typeof resolved.diff === "string") {
3796
- resolved.diff = resolvePath(resolved.diff, resolved.root);
3797
- resolved.forceRerunTriggers.push(resolved.diff);
3798
- }
3617
+ if (typeof resolved.diff === "string") resolved.diff = resolvePath(resolved.diff, resolved.root), resolved.forceRerunTriggers.push(resolved.diff);
3799
3618
  // the server has been created, we don't need to override vite.server options
3800
3619
  const api = resolveApiServerConfig(options, defaultPort);
3801
- resolved.api = {
3620
+ if (resolved.api = {
3802
3621
  ...api,
3803
3622
  token: crypto.randomUUID()
3804
- };
3805
- if (options.related) resolved.related = toArray(options.related).map((file) => resolve$1(resolved.root, file));
3623
+ }, options.related) resolved.related = toArray(options.related).map((file) => resolve$1(resolved.root, file));
3806
3624
  /*
3807
3625
  * Reporters can be defined in many different ways:
3808
3626
  * { reporter: 'json' }
@@ -3831,84 +3649,51 @@ function resolveConfig$1(vitest, options, viteConfig) {
3831
3649
  if (mode !== "benchmark") {
3832
3650
  // @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
3833
3651
  // it is passed down as "vitest --reporter ../reporter.js"
3834
- const reportersFromCLI = resolved.reporter;
3835
- const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
3836
- // ./reporter.js || ../reporter.js, but not .reporters/reporter.js
3837
- if (/^\.\.?\//.test(reporter)) return resolve$1(process.cwd(), reporter);
3838
- return reporter;
3652
+ const reportersFromCLI = resolved.reporter, cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
3653
+ return /^\.\.?\//.test(reporter) ? resolve$1(process.cwd(), reporter) : reporter;
3839
3654
  });
3840
3655
  if (cliReporters.length) resolved.reporters = Array.from(new Set(toArray(cliReporters))).filter(Boolean).map((reporter) => [reporter, {}]);
3841
3656
  }
3842
3657
  if (!resolved.reporters.length) {
3843
- resolved.reporters.push(["default", {}]);
3844
3658
  // also enable github-actions reporter as a default
3845
- if (process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
3659
+ if (resolved.reporters.push(["default", {}]), process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
3846
3660
  }
3847
3661
  if (resolved.changed) resolved.passWithNoTests ??= true;
3848
- resolved.css ??= {};
3849
- if (typeof resolved.css === "object") {
3850
- resolved.css.modules ??= {};
3851
- resolved.css.modules.classNameStrategy ??= "stable";
3852
- }
3662
+ if (resolved.css ??= {}, typeof resolved.css === "object") resolved.css.modules ??= {}, resolved.css.modules.classNameStrategy ??= "stable";
3853
3663
  if (resolved.cache !== false) {
3854
3664
  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"`);
3855
3665
  resolved.cache = { dir: viteConfig.cacheDir };
3856
3666
  }
3857
- resolved.sequence ??= {};
3858
- if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
3667
+ if (resolved.sequence ??= {}, resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
3859
3668
  const { files, tests } = resolved.sequence.shuffle;
3860
- resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
3861
- resolved.sequence.shuffle = tests;
3669
+ resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer, resolved.sequence.shuffle = tests;
3862
3670
  }
3863
3671
  if (!resolved.sequence?.sequencer)
3864
3672
  // CLI flag has higher priority
3865
3673
  resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
3866
- resolved.sequence.groupOrder ??= 0;
3867
- resolved.sequence.hooks ??= "stack";
3868
- if (resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
3869
- resolved.typecheck = {
3674
+ if (resolved.sequence.groupOrder ??= 0, resolved.sequence.hooks ??= "stack", resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
3675
+ if (resolved.typecheck = {
3870
3676
  ...configDefaults.typecheck,
3871
3677
  ...resolved.typecheck
3872
- };
3873
- resolved.typecheck ??= {};
3874
- resolved.typecheck.enabled ??= false;
3875
- 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."));
3876
- resolved.browser.enabled ??= false;
3877
- resolved.browser.headless ??= isCI;
3878
- resolved.browser.isolate ??= true;
3879
- resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
3880
- // disable in headless mode by default, and if CI is detected
3881
- resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
3882
- if (resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve$1(resolved.root, resolved.browser.screenshotDirectory);
3678
+ }, 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."));
3679
+ 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.screenshotDirectory) resolved.browser.screenshotDirectory = resolve$1(resolved.root, resolved.browser.screenshotDirectory);
3883
3680
  const isPreview = resolved.browser.provider === "preview";
3884
- if (isPreview && resolved.browser.screenshotFailures === true) {
3885
- console.warn(c.yellow([
3886
- `Browser provider "preview" doesn't support screenshots, `,
3887
- `so "browser.screenshotFailures" option is forcefully disabled. `,
3888
- `Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
3889
- ].join("")));
3890
- resolved.browser.screenshotFailures = false;
3891
- } else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
3892
- resolved.browser.viewport ??= {};
3893
- resolved.browser.viewport.width ??= 414;
3894
- resolved.browser.viewport.height ??= 896;
3895
- resolved.browser.locators ??= {};
3896
- resolved.browser.locators.testIdAttribute ??= "data-testid";
3897
- if (resolved.browser.enabled && provider === "stackblitz") resolved.browser.provider = "preview";
3898
- resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
3681
+ if (isPreview && resolved.browser.screenshotFailures === true) console.warn(c.yellow([
3682
+ `Browser provider "preview" doesn't support screenshots, `,
3683
+ `so "browser.screenshotFailures" option is forcefully disabled. `,
3684
+ `Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
3685
+ ].join(""))), resolved.browser.screenshotFailures = false;
3686
+ else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
3687
+ if (resolved.browser.viewport ??= {}, resolved.browser.viewport.width ??= 414, resolved.browser.viewport.height ??= 896, resolved.browser.locators ??= {}, resolved.browser.locators.testIdAttribute ??= "data-testid", resolved.browser.enabled && provider === "stackblitz") resolved.browser.provider = "preview";
3899
3688
  // enable includeTaskLocation by default in UI mode
3900
- if (resolved.browser.enabled) {
3689
+ if (resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort }, resolved.browser.enabled) {
3901
3690
  if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
3902
3691
  } else if (resolved.ui) resolved.includeTaskLocation ??= true;
3903
3692
  const htmlReporter = toArray(resolved.reporters).some((reporter) => {
3904
- if (Array.isArray(reporter)) return reporter[0] === "html";
3905
- return false;
3693
+ return Array.isArray(reporter) ? reporter[0] === "html" : false;
3906
3694
  });
3907
3695
  if (htmlReporter) resolved.includeTaskLocation ??= true;
3908
- resolved.testTransformMode ??= {};
3909
- resolved.testTimeout ??= resolved.browser.enabled ? 15e3 : 5e3;
3910
- resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4;
3911
- return resolved;
3696
+ return resolved.server ??= {}, resolved.server.deps ??= {}, resolved.testTimeout ??= resolved.browser.enabled ? 15e3 : 5e3, resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4, resolved;
3912
3697
  }
3913
3698
  function isBrowserEnabled(config) {
3914
3699
  return Boolean(config.browser?.enabled);
@@ -3944,14 +3729,11 @@ const THRESHOLD_KEYS = [
3944
3729
  "functions",
3945
3730
  "statements",
3946
3731
  "branches"
3947
- ];
3948
- const GLOBAL_THRESHOLDS_KEY = "global";
3949
- const DEFAULT_PROJECT = Symbol.for("default-project");
3732
+ ], GLOBAL_THRESHOLDS_KEY = "global", DEFAULT_PROJECT = Symbol.for("default-project");
3950
3733
  let uniqueId = 0;
3951
3734
  async function getCoverageProvider(options, loader) {
3952
3735
  const coverageModule = await resolveCoverageProviderModule(options, loader);
3953
- if (coverageModule) return coverageModule.getProvider();
3954
- return null;
3736
+ return coverageModule ? coverageModule.getProvider() : null;
3955
3737
  }
3956
3738
  class BaseCoverageProvider {
3957
3739
  ctx;
@@ -3964,8 +3746,7 @@ class BaseCoverageProvider {
3964
3746
  coverageFilesDirectory;
3965
3747
  roots = [];
3966
3748
  _initialize(ctx) {
3967
- this.ctx = ctx;
3968
- 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} `))}.
3749
+ 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} `))}.
3969
3750
  Running mixed versions is not supported and may lead into bugs
3970
3751
  Update your dependencies and make sure the versions match.`));
3971
3752
  const config = ctx.config.coverage;
@@ -3983,25 +3764,18 @@ Update your dependencies and make sure the versions match.`));
3983
3764
  statements: config.thresholds["100"] ? 100 : config.thresholds.statements
3984
3765
  }
3985
3766
  };
3986
- const shard = this.ctx.config.shard;
3987
- const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
3988
- this.coverageFilesDirectory = resolve$1(this.options.reportsDirectory, tempDirectory);
3767
+ const shard = this.ctx.config.shard, tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
3989
3768
  // If --project filter is set pick only roots of resolved projects
3990
- this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
3769
+ this.coverageFilesDirectory = resolve$1(this.options.reportsDirectory, tempDirectory), this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
3991
3770
  }
3992
3771
  /**
3993
3772
  * Check if file matches `coverage.include` but not `coverage.exclude`
3994
3773
  */
3995
3774
  isIncluded(_filename, root) {
3996
- const roots = root ? [root] : this.roots;
3997
- const filename = slash(_filename);
3998
- const cacheHit = this.globCache.get(filename);
3775
+ const roots = root ? [root] : this.roots, filename = slash(_filename), cacheHit = this.globCache.get(filename);
3999
3776
  if (cacheHit !== void 0) return cacheHit;
4000
3777
  // File outside project root with default allowExternal
4001
- if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) {
4002
- this.globCache.set(filename, false);
4003
- return false;
4004
- }
3778
+ if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) return this.globCache.set(filename, false), false;
4005
3779
  // By default `coverage.include` matches all files, except "coverage.exclude"
4006
3780
  const glob = this.options.include || "**";
4007
3781
  let included = roots.some((root) => {
@@ -4013,9 +3787,7 @@ Update your dependencies and make sure the versions match.`));
4013
3787
  };
4014
3788
  return pm.isMatch(filename, glob, options);
4015
3789
  });
4016
- included &&= existsSync(cleanUrl(filename));
4017
- this.globCache.set(filename, included);
4018
- return included;
3790
+ return included &&= existsSync(cleanUrl(filename)), this.globCache.set(filename, included), included;
4019
3791
  }
4020
3792
  async getUntestedFilesByRoot(testedFiles, include, root) {
4021
3793
  let includedFiles = await glob(include, {
@@ -4025,15 +3797,12 @@ Update your dependencies and make sure the versions match.`));
4025
3797
  dot: true,
4026
3798
  onlyFiles: true
4027
3799
  });
4028
- // Run again through picomatch as tinyglobby's exclude pattern is different ({ "exclude": ["math"] } should ignore "src/math.ts")
4029
- includedFiles = includedFiles.filter((file) => this.isIncluded(file, root));
4030
- if (this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
3800
+ if (includedFiles = includedFiles.filter((file) => this.isIncluded(file, root)), this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
4031
3801
  return includedFiles.map((file) => slash(path.resolve(root, file)));
4032
3802
  }
4033
3803
  async getUntestedFiles(testedFiles) {
4034
3804
  if (this.options.include == null) return [];
4035
- const rootMapper = this.getUntestedFilesByRoot.bind(this, testedFiles, this.options.include);
4036
- const matrix = await Promise.all(this.roots.map(rootMapper));
3805
+ const rootMapper = this.getUntestedFilesByRoot.bind(this, testedFiles, this.options.include), matrix = await Promise.all(this.roots.map(rootMapper));
4037
3806
  return matrix.flatMap((files) => files);
4038
3807
  }
4039
3808
  createCoverageMap() {
@@ -4059,56 +3828,37 @@ Update your dependencies and make sure the versions match.`));
4059
3828
  force: true,
4060
3829
  maxRetries: 10
4061
3830
  });
4062
- await promises$1.mkdir(this.coverageFilesDirectory, { recursive: true });
4063
- this.coverageFiles = /* @__PURE__ */ new Map();
4064
- this.pendingPromises = [];
3831
+ await promises$1.mkdir(this.coverageFilesDirectory, { recursive: true }), this.coverageFiles = /* @__PURE__ */ new Map(), this.pendingPromises = [];
4065
3832
  }
4066
- onAfterSuiteRun({ coverage, transformMode, projectName, testFiles }) {
3833
+ onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
4067
3834
  if (!coverage) return;
4068
- if (transformMode !== "web" && transformMode !== "ssr" && transformMode !== "browser") throw new Error(`Invalid transform mode: ${transformMode}`);
4069
3835
  let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
4070
- if (!entry) {
4071
- entry = {
4072
- web: {},
4073
- ssr: {},
4074
- browser: {}
4075
- };
4076
- this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
4077
- }
4078
- const testFilenames = testFiles.join();
4079
- const filename = resolve$1(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
3836
+ if (!entry) entry = {}, this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
3837
+ const testFilenames = testFiles.join(), filename = resolve$1(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
4080
3838
  // If there's a result from previous run, overwrite it
4081
- entry[transformMode][testFilenames] = filename;
3839
+ entry[environment] ??= {}, entry[environment][testFilenames] = filename;
4082
3840
  const promise = promises$1.writeFile(filename, JSON.stringify(coverage), "utf-8");
4083
3841
  this.pendingPromises.push(promise);
4084
3842
  }
4085
3843
  async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
4086
3844
  let index = 0;
4087
3845
  const total = this.pendingPromises.length;
4088
- await Promise.all(this.pendingPromises);
4089
- this.pendingPromises = [];
4090
- for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [transformMode, coverageByTestfiles] of Object.entries(coveragePerProject)) {
4091
- const filenames = Object.values(coverageByTestfiles);
4092
- const project = this.ctx.getProjectByName(projectName);
3846
+ await Promise.all(this.pendingPromises), this.pendingPromises = [];
3847
+ for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
3848
+ const filenames = Object.values(coverageByTestfiles), project = this.ctx.getProjectByName(projectName);
4093
3849
  for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
4094
- if (onDebug.enabled) {
4095
- index += chunk.length;
4096
- onDebug(`Reading coverage results ${index}/${total}`);
4097
- }
3850
+ if (onDebug.enabled) index += chunk.length, onDebug(`Reading coverage results ${index}/${total}`);
4098
3851
  await Promise.all(chunk.map(async (filename) => {
4099
- const contents = await promises$1.readFile(filename, "utf-8");
4100
- const coverage = JSON.parse(contents);
3852
+ const contents = await promises$1.readFile(filename, "utf-8"), coverage = JSON.parse(contents);
4101
3853
  onFileRead(coverage);
4102
3854
  }));
4103
3855
  }
4104
- await onFinished(project, transformMode);
3856
+ await onFinished(project, environment);
4105
3857
  }
4106
3858
  }
4107
3859
  async cleanAfterRun() {
4108
- this.coverageFiles = /* @__PURE__ */ new Map();
4109
- await promises$1.rm(this.coverageFilesDirectory, { recursive: true });
4110
3860
  // Remove empty reports directory, e.g. when only text-reporter is used
4111
- if (readdirSync(this.options.reportsDirectory).length === 0) await promises$1.rm(this.options.reportsDirectory, { recursive: true });
3861
+ if (this.coverageFiles = /* @__PURE__ */ new Map(), await promises$1.rm(this.coverageFilesDirectory, { recursive: true }), readdirSync(this.options.reportsDirectory).length === 0) await promises$1.rm(this.options.reportsDirectory, { recursive: true });
4112
3862
  }
4113
3863
  async onTestFailure() {
4114
3864
  if (!this.options.reportOnFailure) await this.cleanAfterRun();
@@ -4121,11 +3871,9 @@ Update your dependencies and make sure the versions match.`));
4121
3871
  }
4122
3872
  async reportThresholds(coverageMap, allTestsRun) {
4123
3873
  const resolvedThresholds = this.resolveThresholds(coverageMap);
4124
- this.checkThresholds(resolvedThresholds);
4125
- if (this.options.thresholds?.autoUpdate && allTestsRun) {
3874
+ if (this.checkThresholds(resolvedThresholds), this.options.thresholds?.autoUpdate && allTestsRun) {
4126
3875
  if (!this.ctx.server.config.configFile) throw new Error("Missing configurationFile. The \"coverage.thresholds.autoUpdate\" can only be enabled when configuration file is used.");
4127
- const configFilePath = this.ctx.server.config.configFile;
4128
- const configModule = await this.parseConfigModule(configFilePath);
3876
+ const configFilePath = this.ctx.server.config.configFile, configModule = await this.parseConfigModule(configFilePath);
4129
3877
  await this.updateThresholds({
4130
3878
  thresholds: resolvedThresholds,
4131
3879
  configurationFile: configModule,
@@ -4139,16 +3887,10 @@ Update your dependencies and make sure the versions match.`));
4139
3887
  * for specific files defined by glob pattern or global for all other files.
4140
3888
  */
4141
3889
  resolveThresholds(coverageMap) {
4142
- const resolvedThresholds = [];
4143
- const files = coverageMap.files();
4144
- const globalCoverageMap = this.createCoverageMap();
3890
+ const resolvedThresholds = [], files = coverageMap.files(), globalCoverageMap = this.createCoverageMap();
4145
3891
  for (const key of Object.keys(this.options.thresholds)) {
4146
3892
  if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
4147
- const glob = key;
4148
- const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
4149
- const globCoverageMap = this.createCoverageMap();
4150
- const matcher = pm(glob);
4151
- const matchingFiles = files.filter((file) => matcher(relative(this.ctx.config.root, file)));
3893
+ 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)));
4152
3894
  for (const file of matchingFiles) {
4153
3895
  const fileCoverage = coverageMap.fileCoverageFor(file);
4154
3896
  globCoverageMap.addFileCoverage(fileCoverage);
@@ -4164,7 +3906,7 @@ Update your dependencies and make sure the versions match.`));
4164
3906
  const fileCoverage = coverageMap.fileCoverageFor(file);
4165
3907
  globalCoverageMap.addFileCoverage(fileCoverage);
4166
3908
  }
4167
- resolvedThresholds.unshift({
3909
+ return resolvedThresholds.unshift({
4168
3910
  name: GLOBAL_THRESHOLDS_KEY,
4169
3911
  coverageMap: globalCoverageMap,
4170
3912
  thresholds: {
@@ -4173,8 +3915,7 @@ Update your dependencies and make sure the versions match.`));
4173
3915
  lines: this.options.thresholds?.lines,
4174
3916
  statements: this.options.thresholds?.statements
4175
3917
  }
4176
- });
4177
- return resolvedThresholds;
3918
+ }), resolvedThresholds;
4178
3919
  }
4179
3920
  /**
4180
3921
  * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
@@ -4212,8 +3953,7 @@ Update your dependencies and make sure the versions match.`));
4212
3953
  this.ctx.logger.error(errorMessage);
4213
3954
  }
4214
3955
  } else {
4215
- const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered;
4216
- const absoluteThreshold = threshold * -1;
3956
+ const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered, absoluteThreshold = threshold * -1;
4217
3957
  if (uncovered > absoluteThreshold) {
4218
3958
  process.exitCode = 1;
4219
3959
  /**
@@ -4237,8 +3977,7 @@ Update your dependencies and make sure the versions match.`));
4237
3977
  const config = resolveConfig(configurationFile);
4238
3978
  assertConfigurationModule(config);
4239
3979
  for (const { coverageMap, thresholds, name } of allThresholds) {
4240
- const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()];
4241
- const thresholdsToUpdate = [];
3980
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()], thresholdsToUpdate = [];
4242
3981
  for (const key of THRESHOLD_KEYS) {
4243
3982
  const threshold = thresholds[key] ?? 100;
4244
3983
  /**
@@ -4249,8 +3988,7 @@ Update your dependencies and make sure the versions match.`));
4249
3988
  const actual = Math.min(...summaries.map((summary) => summary[key].pct));
4250
3989
  if (actual > threshold) thresholdsToUpdate.push([key, actual]);
4251
3990
  } else {
4252
- const absoluteThreshold = threshold * -1;
4253
- const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
3991
+ const absoluteThreshold = threshold * -1, actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
4254
3992
  if (actual < absoluteThreshold) {
4255
3993
  // If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
4256
3994
  const updatedThreshold = actual === 0 ? 100 : actual * -1;
@@ -4266,10 +4004,7 @@ Update your dependencies and make sure the versions match.`));
4266
4004
  glob[threshold] = newValue;
4267
4005
  }
4268
4006
  }
4269
- if (updatedThresholds) {
4270
- this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
4271
- onUpdate();
4272
- }
4007
+ if (updatedThresholds) this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds."), onUpdate();
4273
4008
  }
4274
4009
  async mergeReports(coverageMaps) {
4275
4010
  const coverageMap = this.createCoverageMap();
@@ -4281,10 +4016,8 @@ Update your dependencies and make sure the versions match.`));
4281
4016
  }
4282
4017
  toSlices(array, size) {
4283
4018
  return array.reduce((chunks, item) => {
4284
- const index = Math.max(0, chunks.length - 1);
4285
- const lastChunk = chunks[index] || [];
4286
- chunks[index] = lastChunk;
4287
- if (lastChunk.length >= size) chunks.push([item]);
4019
+ const index = Math.max(0, chunks.length - 1), lastChunk = chunks[index] || [];
4020
+ if (chunks[index] = lastChunk, lastChunk.length >= size) chunks.push([item]);
4288
4021
  else lastChunk.push(item);
4289
4022
  return chunks;
4290
4023
  }, []);
@@ -4293,25 +4026,25 @@ Update your dependencies and make sure the versions match.`));
4293
4026
  const servers = [...ctx.projects.map((project) => ({
4294
4027
  root: project.config.root,
4295
4028
  isBrowserEnabled: project.isBrowserEnabled(),
4296
- vitenode: project.vitenode
4297
- })),
4029
+ vite: project.vite
4030
+ })), (
4298
4031
  // Check core last as it will match all files anyway
4299
- {
4032
+ {
4300
4033
  root: ctx.config.root,
4301
- vitenode: ctx.vitenode,
4034
+ vite: ctx.vite,
4302
4035
  isBrowserEnabled: ctx.getRootProject().isBrowserEnabled()
4303
- }];
4036
+ })];
4304
4037
  return async function transformFile(filename) {
4305
4038
  let lastError;
4306
- for (const { root, vitenode, isBrowserEnabled } of servers) {
4039
+ for (const { root, vite, isBrowserEnabled } of servers) {
4307
4040
  // On Windows root doesn't start with "/" while filenames do
4308
4041
  if (!filename.startsWith(root) && !filename.startsWith(`/${root}`)) continue;
4309
4042
  if (isBrowserEnabled) {
4310
- const result = await vitenode.transformRequest(filename, void 0, "web").catch(() => null);
4043
+ const result = await vite.environments.client.transformRequest(filename).catch(() => null);
4311
4044
  if (result) return result;
4312
4045
  }
4313
4046
  try {
4314
- return await vitenode.transformRequest(filename);
4047
+ return await vite.environments.ssr.transformRequest(filename);
4315
4048
  } catch (error) {
4316
4049
  lastError = error;
4317
4050
  }
@@ -4325,14 +4058,12 @@ Update your dependencies and make sure the versions match.`));
4325
4058
  * Narrow down `unknown` glob thresholds to resolved ones
4326
4059
  */
4327
4060
  function resolveGlobThresholds(thresholds) {
4328
- if (!thresholds || typeof thresholds !== "object") return {};
4329
- if (100 in thresholds && thresholds[100] === true) return {
4061
+ return !thresholds || typeof thresholds !== "object" ? {} : 100 in thresholds && thresholds[100] === true ? {
4330
4062
  lines: 100,
4331
4063
  branches: 100,
4332
4064
  functions: 100,
4333
4065
  statements: 100
4334
- };
4335
- return {
4066
+ } : {
4336
4067
  lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
4337
4068
  branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
4338
4069
  functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
@@ -4358,8 +4089,7 @@ function resolveConfig(configModule) {
4358
4089
  if (config) return config;
4359
4090
  // "export default mergeConfig(..., defineConfig(...))"
4360
4091
  if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
4361
- config = resolveMergeConfig(mod);
4362
- if (config) return config;
4092
+ if (config = resolveMergeConfig(mod), config) return config;
4363
4093
  }
4364
4094
  } catch (error) {
4365
4095
  // Reduce magicast's verbose errors to readable ones
@@ -4388,4 +4118,4 @@ function resolveMergeConfig(mod) {
4388
4118
  }
4389
4119
  }
4390
4120
 
4391
- export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, createMethodsRPC as c, isBrowserEnabled as d, groupBy as e, getCoverageProvider as f, getFilePoolName as g, hash as h, isPackageExists as i, createPool as j, resolveConfig$1 as r, stdout as s, wildcardPatternToRegExp as w };
4121
+ export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, createMethodsRPC as c, createFetchModuleFunction as d, isBrowserEnabled as e, groupBy as f, getFilePoolName as g, hash as h, isPackageExists as i, getCoverageProvider as j, createPool as k, normalizeResolvedIdToUrl as n, resolveConfig$1 as r, stdout as s, wildcardPatternToRegExp as w };