@depup/vitest 5.0.2-depup.0 → 5.0.3-depup.0

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 (72) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +6 -4
  3. package/browser/context.d.ts +7 -1
  4. package/browser/context.js +5 -5
  5. package/changes.json +11 -3
  6. package/dist/browser.d.ts +4 -4
  7. package/dist/browser.js +10 -10
  8. package/dist/chunks/{base.5tQXSzMP.js → base.DhkQeZwd.js} +14 -14
  9. package/dist/chunks/{browser.d.g5Thl309.d.ts → browser.d.3cffPS1V.d.ts} +3 -3
  10. package/dist/chunks/{cac.BLbEtnDd.js → cac.DfDGTQ9W.js} +9 -9
  11. package/dist/chunks/{cli-api.DL4Bd7Vh.js → cli-api.C4x4GGip.js} +5 -5
  12. package/dist/chunks/{config.d.CU_b-wJj.d.ts → config.d.BxjInJat.d.ts} +13 -15
  13. package/dist/chunks/{console.B09ye7y0.js → console.RBA5x-0t.js} +3 -2
  14. package/dist/chunks/{coverage.AipniaqB.js → coverage.S6Dao4g-.js} +1 -1
  15. package/dist/chunks/{coverage.CX7NN5s7.js → coverage._yXj5zeb.js} +1 -1
  16. package/dist/chunks/{creator.2VTnpYzN.js → creator.203NYCo3.js} +12 -12
  17. package/dist/chunks/{display.pkpxlVcY.js → display.CXBnkt9a.js} +1 -1
  18. package/dist/chunks/{doctor.BCXa6-1R.js → doctor.CpHPRoza.js} +2 -2
  19. package/dist/chunks/{environment.d.C6xYahWA.d.ts → environment.d.DAj1Vt__.d.ts} +1 -1
  20. package/dist/chunks/{globals.Brdv9iOs.js → globals.9QtHn8Sr.js} +9 -9
  21. package/dist/chunks/{index.DGdajAO2.js → index.C4tKZ0yW.js} +14 -11
  22. package/dist/chunks/{index.C-uw7tH9.js → index.DpLw24bj.js} +168 -78
  23. package/dist/chunks/{index.M2dsQ_UQ.js → index.cx-_w29y.js} +551 -179
  24. package/dist/chunks/{index.BgwtGYxD.js → index.ggMXe2IF.js} +5 -5
  25. package/dist/chunks/{index.hTNFpC24.js → index.k8L_ZRzq.js} +20 -5
  26. package/dist/chunks/{init-forks.CdGu3HUy.js → init-forks.hzLs7kMZ.js} +1 -1
  27. package/dist/chunks/{init-threads.2f_lsHNK.js → init-threads.COb0VD5D.js} +1 -1
  28. package/dist/chunks/{init.IsjtLXKe.js → init.D0qF-GGk.js} +4 -5
  29. package/dist/chunks/{native.CF4uT5hY.js → native.CBlba4VV.js} +2 -2
  30. package/dist/chunks/{nativeModuleMocker.DwRB4F9R.js → nativeModuleMocker.CXSh3m91.js} +2 -2
  31. package/dist/chunks/{nativeModuleRunner.J0QLzNtK.js → nativeModuleRunner.jwZPY3dg.js} +1 -1
  32. package/dist/chunks/{node.CfQ_OGWr.js → node.BzimPqT5.js} +1 -1
  33. package/dist/chunks/{pathe.M-eThtNZ.DwEga6ro.js → pathe.M-eThtNZ.CknpM8Or.js} +1 -1
  34. package/dist/chunks/{plugin.d.My_z-jmU.d.ts → plugin.d.BsjqSb4-.d.ts} +26 -20
  35. package/dist/chunks/{plugins.Cigb0uSy.js → plugins.D2ut1V9b.js} +1 -1
  36. package/dist/chunks/{resolver.NpfwMKt9.js → resolver.fbW9AUvG.js} +1 -1
  37. package/dist/chunks/{rpc.CJVl3hZH.js → rpc.BTmCtPly.js} +3 -2
  38. package/dist/chunks/{rpc.d.DA9Utv4e.d.ts → rpc.d.BPFHl-HE.d.ts} +1 -1
  39. package/dist/chunks/{run.C5UmxDPh.js → run.BTlFXlnv.js} +29 -19
  40. package/dist/chunks/{setup-common.Ct8rQvjH.js → setup-common.BglbuAHD.js} +3 -3
  41. package/dist/chunks/{source-map.BH0bbrs9.js → source-map.DUxvo12T.js} +4 -8
  42. package/dist/chunks/{source-map.d.YqWNcp4e.d.ts → source-map.d.BLHtC2OB.d.ts} +1 -1
  43. package/dist/chunks/{spy.KLHgXex_.js → spy.DgMTfhrK.js} +18 -16
  44. package/dist/chunks/{task-utils.d.BZm4GSQD.d.ts → task-utils.d.C-IeGxLU.d.ts} +1 -1
  45. package/dist/chunks/{utils.CJ0JImL8.js → utils.RcGwuOc_.js} +2 -2
  46. package/dist/chunks/{vm.KI2AYADz.js → vm.vTyZj_02.js} +8 -8
  47. package/dist/chunks/{worker.d.MLmnzOJE.d.ts → worker.d.Bt2A2mmi.d.ts} +3 -3
  48. package/dist/cli.js +2 -2
  49. package/dist/config.d.ts +9 -9
  50. package/dist/index.d.ts +10 -10
  51. package/dist/index.js +9 -9
  52. package/dist/module-evaluator.d.ts +3 -2
  53. package/dist/module-evaluator.js +8 -5
  54. package/dist/node.d.ts +9 -9
  55. package/dist/node.js +14 -14
  56. package/dist/runtime.d.ts +6 -6
  57. package/dist/runtime.js +4 -4
  58. package/dist/spy.js +1 -1
  59. package/dist/task-utils.js +2 -1
  60. package/dist/worker.d.ts +4 -4
  61. package/dist/worker.js +17 -17
  62. package/dist/workers/forks.js +18 -18
  63. package/dist/workers/runVmTests.js +14 -14
  64. package/dist/workers/threads.js +18 -18
  65. package/dist/workers/vmForks.js +12 -12
  66. package/dist/workers/vmThreads.js +12 -12
  67. package/globals.d.ts +19 -19
  68. package/index.cjs +3 -3
  69. package/optional-runtime-types.d.ts +1 -1
  70. package/optional-types.d.ts +1 -1
  71. package/package.json +33 -20
  72. package/suppress-warnings.cjs +1 -1
@@ -1,8 +1,9 @@
1
+ import { readFile, rm, writeFile, rename, stat, unlink, mkdir, readdir, copyFile } from 'node:fs/promises';
1
2
  import * as os from 'node:os';
2
3
  import os__default, { homedir, tmpdir, availableParallelism, hostname } from 'node:os';
3
- import { j as join, b as basename, d as dirname, i as isAbsolute, r as resolve, c as cleanUrl, D as noop$1, f as extname, h as KNOWN_ASSET_RE, F as withTrailingSlash, G as wrapId, H as isExternalUrl, I as unwrapId, a as relative, z as unique, e as createDefer, s as slash, t as toArray, m as isPrimitive, n as normalize, J as deepMerge, p as deepClone, L as sanitizeFilePath } from './pathe.M-eThtNZ.DwEga6ro.js';
4
- import { e as getDefaultExportFromCjs, T as TraceMap, o as originalPositionFor, k as generatedPositionFor, l as eachMapping, m as stackIgnorePatterns, f as parseErrorStacktrace, p as parseStacktrace, b as serializeValue } from './source-map.BH0bbrs9.js';
5
- import { v as version$1 } from './cac.BLbEtnDd.js';
4
+ import { j as join, b as basename, d as dirname, i as isAbsolute, r as resolve, c as cleanUrl, D as noop$1, f as extname, h as KNOWN_ASSET_RE, F as withTrailingSlash, G as wrapId, H as isExternalUrl, I as unwrapId, a as relative, z as unique, e as createDefer, s as slash, t as toArray, m as isPrimitive, n as normalize, J as deepMerge, p as deepClone, L as sanitizeFilePath } from './pathe.M-eThtNZ.CknpM8Or.js';
5
+ import { T as TraceMap, o as originalPositionFor, i as generatedPositionFor, j as eachMapping, k as stackIgnorePatterns, e as parseErrorStacktrace, p as parseStacktrace, b as serializeValue } from './source-map.DUxvo12T.js';
6
+ import { v as version$1 } from './cac.DfDGTQ9W.js';
6
7
  import * as fs from 'node:fs';
7
8
  import fs__default, { promises, existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync, statfsSync, realpathSync, statSync, readdirSync } from 'node:fs';
8
9
  import { performance as performance$1 } from 'node:perf_hooks';
@@ -18,25 +19,25 @@ import require$$7 from 'url';
18
19
  import require$$0 from 'zlib';
19
20
  import require$$0$1 from 'buffer';
20
21
  import require$$2 from 'util';
22
+ import { g as getDefaultExportFromCjs } from './index.cx-_w29y.js';
21
23
  import { A as API_PATH, a as defaultPort, c as configFiles, d as defaultBrowserPort, b as defaultInspectPort } from './constants.-juJ8b_4.js';
22
24
  import * as vite from 'vite';
23
25
  import { createServer, isFileLoadingAllowed, normalizePath, fetchModule, parseAst, searchForWorkspaceRoot, version, isRunnableDevEnvironment, mergeConfig, resolveConfig as resolveConfig$2 } from 'vite';
24
26
  import crypto, { createHash } from 'node:crypto';
25
27
  import { rootDir, distDir } from '../path.js';
26
- import { z as validateTags, A as limitConcurrency, B as shuffle, C as createTagsFilter } from './run.C5UmxDPh.js';
27
- import { N as NativeModuleRunner, i as isPackageExists, r as resolveModule } from './nativeModuleRunner.J0QLzNtK.js';
28
+ import { z as validateTags, A as limitConcurrency, B as shuffle, C as createTagsFilter } from './run.BTlFXlnv.js';
29
+ import { N as NativeModuleRunner, i as isPackageExists, r as resolveModule } from './nativeModuleRunner.jwZPY3dg.js';
28
30
  import { createFileTask as createFileTask$1, createTaskName, calculateSuiteHash, convertTasksToEvents, hasFailed, generateFileHash, getTestName, hasFailedSnapshot, getTests, getSuites, getTasks, getFullName, interpretTaskModes } from '../task-utils.js';
29
31
  import { Traces } from '../traces.js';
30
32
  import { createDebug } from 'obug';
31
- import { readFile, rm, writeFile, rename, stat, unlink, mkdir, readdir, copyFile } from 'node:fs/promises';
33
+ import { y } from './tinyrainbow.Ht9iggcq.js';
32
34
  import module$1, { isBuiltin, builtinModules, createRequire } from 'node:module';
33
35
  import url, { pathToFileURL, fileURLToPath } from 'node:url';
34
- import { l as lookupPackageScopeType } from './resolver.NpfwMKt9.js';
36
+ import { l as lookupPackageScopeType } from './resolver.fbW9AUvG.js';
35
37
  import * as esModuleLexer from 'es-module-lexer';
36
38
  import { ssrImportKey, ssrModuleExportsKey, ssrExportAllKey, ssrImportMetaKey, ModuleRunner } from 'vite/module-runner';
37
39
  import { i as isWindows, a as isForceColor, b as isTTY } from './env.DzFJjrmK.js';
38
40
  import path, { resolve as resolve$1 } from 'node:path';
39
- import { y } from './tinyrainbow.Ht9iggcq.js';
40
41
  import { glob, isDynamicPattern } from 'tinyglobby';
41
42
  import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.D2ip7f-X.js';
42
43
  import MagicString from 'magic-string';
@@ -44,13 +45,13 @@ import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
44
45
  import { Console } from 'node:console';
45
46
  import { Writable } from 'node:stream';
46
47
  import { stripVTControlCharacters } from 'node:util';
47
- import { i as inspect } from './display.pkpxlVcY.js';
48
+ import { i as inspect } from './display.CXBnkt9a.js';
48
49
  import { x } from 'tinyexec';
49
50
  import { p as positionToOffset, l as lineSplitRE } from './offset.Dy-5Fdfn.js';
50
- import { e as errorBanner, F as F_POINTER, d as divider, t as truncateString, w as withLabel, f as formatProjectName, a as formatTimeString, b as taskFail, s as separator, c as F_CHECK, g as F_DOWN_RIGHT, h as getStateSymbol, r as renderSnapshotSummary, p as padSummaryTitle, i as getStateString$1, j as formatTime, k as countTestErrors, l as F_TREE_NODE_END, m as F_TREE_NODE_MIDDLE, n as noun, o as F_RIGHT } from './utils.CJ0JImL8.js';
51
+ import { e as errorBanner, F as F_POINTER, d as divider, t as truncateString, w as withLabel, f as formatProjectName, a as formatTimeString, b as taskFail, s as separator, c as F_CHECK, g as F_DOWN_RIGHT, h as getStateSymbol, r as renderSnapshotSummary, p as padSummaryTitle, i as getStateString$1, j as formatTime, k as countTestErrors, l as F_TREE_NODE_END, m as F_TREE_NODE_MIDDLE, n as noun, o as F_RIGHT } from './utils.RcGwuOc_.js';
51
52
  import { VitestModuleEvaluator } from '#module-evaluator';
52
53
  import pm from 'picomatch';
53
- import { g as getCoverageFilesDirectory, r as resolveCoverageProviderModule } from './coverage.CX7NN5s7.js';
54
+ import { g as getCoverageFilesDirectory, r as resolveCoverageProviderModule } from './coverage._yXj5zeb.js';
54
55
  import { isAgent, isCI, provider } from 'std-env';
55
56
  import process$1 from 'node:process';
56
57
  import { isatty } from 'node:tty';
@@ -5955,7 +5956,7 @@ function groupBy(collection, iteratee) {
5955
5956
  }
5956
5957
  function stdout() {
5957
5958
  // @ts-expect-error Node.js maps process.stdout to console._stdout
5958
- // eslint-disable-next-line no-console
5959
+ // oxlint-disable-next-line no-console
5959
5960
  return console._stdout || process.stdout;
5960
5961
  }
5961
5962
  function escapeRegExp(s) {
@@ -6038,7 +6039,7 @@ function detectCodeBlock(content) {
6038
6039
  const envOptions = JSON.parse(envOptionsJson || "null");
6039
6040
  const tags = [];
6040
6041
  let tagMatch;
6041
- // eslint-disable-next-line no-cond-assign
6042
+ // oxlint-disable-next-line no-cond-assign
6042
6043
  while (tagMatch = content.match(/(\/\/|\*)\s*@module-tag\s+([\w\-/]+)\b/)) {
6043
6044
  tags.push(tagMatch[2]);
6044
6045
  content = content.slice(tagMatch.index + tagMatch[0].length);
@@ -6332,21 +6333,25 @@ class ModuleFetcher {
6332
6333
  return cachedModule;
6333
6334
  }
6334
6335
  const result = await this.fetchAndProcess(environment, url, importer, moduleGraphModule, options);
6335
- const importedUrls = this.getSerializedImports(moduleGraphModule);
6336
+ const imports = this.getCachedImports(environment, moduleGraphModule);
6336
6337
  const map = moduleGraphModule.transformResult?.map;
6337
6338
  const mappings = map && !("version" in map) && map.mappings === "";
6338
- const cachedResult = await this.cacheResult(result, cachePath, moduleGraphModule.transformResult, importedUrls, !!mappings);
6339
+ const cachedResult = await this.cacheResult(result, cachePath, moduleGraphModule.transformResult, imports, !!mappings);
6339
6340
  // remember where the code is stored on disk so that repeat fetches and the
6340
6341
  // `fetchWarmModules` snapshot can point at it in this session already, not
6341
6342
  // only after the cache is read back in the next one
6342
6343
  if ("code" in result && moduleGraphModule.transformResult) moduleGraphModule.transformResult.__vitestTmp = cachePath;
6343
6344
  return cachedResult;
6344
6345
  }
6345
- // we need this for UI to be able to show a module graph
6346
- getSerializedImports(node) {
6347
- const imports = [];
6348
- node.importedModules.forEach((importer) => {
6349
- imports.push(importer.url);
6346
+ getCachedImports(environment, node) {
6347
+ const imports = {
6348
+ urls: [],
6349
+ ids: {}
6350
+ };
6351
+ node.importedModules.forEach(({ url, id }) => {
6352
+ if (id == null) return;
6353
+ imports.urls.push(url);
6354
+ if (id !== urlToId(environment, url)) imports.ids[url] = id;
6350
6355
  });
6351
6356
  return imports;
6352
6357
  }
@@ -6377,7 +6382,7 @@ class ModuleFetcher {
6377
6382
  return this.fsCache.generateCachePath(this.config, environment, moduleGraphModule.id, fileContent);
6378
6383
  }
6379
6384
  async readFileContentToCache(environment, moduleGraphModule) {
6380
- if (moduleGraphModule.file && !moduleGraphModule.file.startsWith("\0") && !moduleGraphModule.file.startsWith("virtual:")) {
6385
+ if (moduleGraphModule.file && !isVirtualFile(moduleGraphModule.file)) {
6381
6386
  const result = await this.readFileConcurrently(moduleGraphModule.file);
6382
6387
  if (result != null) return result;
6383
6388
  }
@@ -6405,6 +6410,8 @@ class ModuleFetcher {
6405
6410
  }
6406
6411
  const cachedModule = await this.fsCache.getCachedModule(cachePath);
6407
6412
  if (!cachedModule) return;
6413
+ const importedModules = await this.resolveCachedImports(environment, cachedModule);
6414
+ if (!importedModules) return;
6408
6415
  // keep the module graph in sync
6409
6416
  let map = extractSourceMap(cachedModule.code);
6410
6417
  if (map && cachedModule.file) map.file = cachedModule.file;
@@ -6426,13 +6433,10 @@ class ModuleFetcher {
6426
6433
  const environmentNode = environment.moduleGraph.getModuleById(importer);
6427
6434
  if (environmentNode) moduleGraphModule.importers.add(environmentNode);
6428
6435
  }
6429
- await Promise.all(cachedModule.importedUrls.map(async (url) => {
6430
- const moduleNode = await environment.moduleGraph.ensureEntryFromUrl(url).catch(() => null);
6431
- if (moduleNode) {
6432
- moduleNode.importers.add(moduleGraphModule);
6433
- moduleGraphModule.importedModules.add(moduleNode);
6434
- }
6435
- }));
6436
+ for (const moduleNode of importedModules) {
6437
+ moduleNode.importers.add(moduleGraphModule);
6438
+ moduleGraphModule.importedModules.add(moduleNode);
6439
+ }
6436
6440
  return {
6437
6441
  cached: true,
6438
6442
  file: cachedModule.file,
@@ -6443,6 +6447,26 @@ class ModuleFetcher {
6443
6447
  moduleType
6444
6448
  };
6445
6449
  }
6450
+ // the cached code imports the urls that Vite resolved when the module was
6451
+ // transformed, so the entry is stale once any of them points elsewhere
6452
+ async resolveCachedImports(environment, cachedModule) {
6453
+ const safeModulePaths = getSafeModulePaths(environment);
6454
+ const { urls, ids } = cachedModule.imports;
6455
+ const importedModules = await Promise.all(urls.map(async (url) => {
6456
+ const id = Object.hasOwn(ids, url) ? ids[url] : urlToId(environment, url);
6457
+ const moduleNode = await environment.moduleGraph.ensureEntryFromUrl(url).catch(() => null);
6458
+ if (!moduleNode || moduleNode.id !== id || url.startsWith("/@fs/") && moduleNode.file != null && !existsSync(moduleNode.file)) {
6459
+ debugFs$1?.(`${y.red("[stale]")} ${cachedModule.id} imports ${url}, which no longer resolves to ${id}`);
6460
+ return null;
6461
+ }
6462
+ // import analysis marks out-of-root imports as safe to load from
6463
+ // outside `server.fs.allow`; a cached importer never goes through it
6464
+ if (url.startsWith("/@fs/") && moduleNode.file) safeModulePaths?.add(moduleNode.file);
6465
+ return moduleNode;
6466
+ }));
6467
+ const resolved = importedModules.filter((moduleNode) => moduleNode != null);
6468
+ if (resolved.length === importedModules.length) return resolved;
6469
+ }
6446
6470
  async fetchAndProcess(environment, url, importer, moduleGraphModule, options) {
6447
6471
  const result = processResultSource(environment, await fetchModule(environment, url, importer, {
6448
6472
  ...options,
@@ -6454,7 +6478,7 @@ class ModuleFetcher {
6454
6478
  return result;
6455
6479
  }
6456
6480
  sourceLoader(file) {
6457
- if (!file || file.startsWith("\0") || file.startsWith("virtual:")) return;
6481
+ if (!file || isVirtualFile(file)) return;
6458
6482
  return () => this.readFileConcurrently(file);
6459
6483
  }
6460
6484
  // the module type is a pure function of the module, so detect it at most once
@@ -6468,10 +6492,10 @@ class ModuleFetcher {
6468
6492
  if (transformResult) transformResult.__vitestModuleType = moduleType;
6469
6493
  return moduleType;
6470
6494
  }
6471
- async cacheResult(result, cachePath, transformResult, importedUrls = [], mappings = false) {
6495
+ async cacheResult(result, cachePath, transformResult, imports, mappings = false) {
6472
6496
  const returnResult = "code" in result ? getCachedResult(result, cachePath) : result;
6473
6497
  if (saveCachePromises.has(cachePath)) return saveCachePromises.get(cachePath);
6474
- const savePromise = this.fsCache.saveCachedModule(cachePath, result, transformResult, importedUrls, mappings).then(() => returnResult).catch((error) => {
6498
+ const savePromise = this.fsCache.saveCachedModule(cachePath, result, transformResult, imports, mappings).then(() => returnResult).catch((error) => {
6475
6499
  debugFs$1?.(`failed to cache ${cachePath}, serving it inline: ${error}`);
6476
6500
  return result;
6477
6501
  }).finally(() => {
@@ -6491,6 +6515,20 @@ class ModuleFetcher {
6491
6515
  return readFilePromises.get(file);
6492
6516
  }
6493
6517
  }
6518
+ // \x00 is a virtual file convention
6519
+ function isVirtualFile(file) {
6520
+ return file.startsWith("\0") || file.startsWith("virtual:");
6521
+ }
6522
+ // inverts the url that import analysis writes for a resolved id
6523
+ function urlToId(environment, url) {
6524
+ if (url.startsWith("/@fs/")) return fsPathFromId(url);
6525
+ if (url[0] === "/") return environment.config.root + url;
6526
+ return url;
6527
+ }
6528
+ // Vite keeps the set out of its public types
6529
+ function getSafeModulePaths(environment) {
6530
+ return environment.getTopLevelConfig().safeModulePaths;
6531
+ }
6494
6532
  function createFetchModuleFunction(resolver, config, fsCache, traces, tmpProjectDir) {
6495
6533
  const fetcher = new ModuleFetcher(resolver, config, fsCache, tmpProjectDir);
6496
6534
  return async (url, importer, environment, cacheFs, options, otelCarrier) => {
@@ -7120,10 +7158,10 @@ class FileSystemModuleCache {
7120
7158
  */
7121
7159
  rootCache;
7122
7160
  metadataFilePath;
7123
- version = "1.0.0-beta.7";
7161
+ version = "1.0.0-beta.8";
7124
7162
  fsCacheRoots = /* @__PURE__ */ new WeakMap();
7125
7163
  fsEnvironmentHashMap = /* @__PURE__ */ new WeakMap();
7126
- fsCacheKeyGenerators = /* @__PURE__ */ new Set();
7164
+ fsCacheKeyGenerators = /* @__PURE__ */ new WeakMap();
7127
7165
  warnedDeprecatedIgnore = /* @__PURE__ */ new Set();
7128
7166
  // this exists only to avoid the perf. cost of reading a file and generating a hash again
7129
7167
  // surprisingly, on some machines this has negligible effect
@@ -7133,8 +7171,13 @@ class FileSystemModuleCache {
7133
7171
  this.rootCache = vitest.config.fsModuleCachePath || join(searchForWorkspaceRoot(vitest.viteConfig.root), "node_modules", DEFAULT_CACHE_DIRNAME);
7134
7172
  this.metadataFilePath = join(this.rootCache, METADATA_FILE);
7135
7173
  }
7136
- defineCacheKeyGenerator(callback) {
7137
- this.fsCacheKeyGenerators.add(callback);
7174
+ defineCacheKeyGenerator(config, callback) {
7175
+ let generators = this.fsCacheKeyGenerators.get(config);
7176
+ if (!generators) {
7177
+ generators = /* @__PURE__ */ new Set();
7178
+ this.fsCacheKeyGenerators.set(config, generators);
7179
+ }
7180
+ generators.add(callback);
7138
7181
  }
7139
7182
  // A plugin can exclude itself from the cache key via `api.vitest.ignoreFsModuleCache`.
7140
7183
  ignoresFsModuleCache(plugin) {
@@ -7188,7 +7231,7 @@ class FileSystemModuleCache {
7188
7231
  url: meta.url,
7189
7232
  file: meta.file,
7190
7233
  code,
7191
- importedUrls: meta.importedUrls,
7234
+ imports: meta.imports,
7192
7235
  mappings: meta.mappings,
7193
7236
  moduleType: meta.moduleType,
7194
7237
  deps: meta.deps,
@@ -7196,13 +7239,16 @@ class FileSystemModuleCache {
7196
7239
  staticMocks: meta.staticMocks
7197
7240
  };
7198
7241
  }
7199
- async saveCachedModule(cachedFilePath, fetchResult, transformResult, importedUrls = [], mappings = false) {
7242
+ async saveCachedModule(cachedFilePath, fetchResult, transformResult, imports = {
7243
+ urls: [],
7244
+ ids: {}
7245
+ }, mappings = false) {
7200
7246
  if ("code" in fetchResult) {
7201
7247
  const result = {
7202
7248
  file: fetchResult.file,
7203
7249
  id: fetchResult.id,
7204
7250
  url: fetchResult.url,
7205
- importedUrls,
7251
+ imports,
7206
7252
  mappings,
7207
7253
  moduleType: fetchResult.moduleType,
7208
7254
  deps: transformResult?.deps,
@@ -7244,7 +7290,7 @@ class FileSystemModuleCache {
7244
7290
  return null;
7245
7291
  }
7246
7292
  let hashString = "";
7247
- for (const generator of this.fsCacheKeyGenerators) {
7293
+ for (const generator of this.fsCacheKeyGenerators.get(vitestConfig) || []) {
7248
7294
  const result = generator({
7249
7295
  environment,
7250
7296
  id,
@@ -7667,6 +7713,18 @@ function BrowserLoaderPlugin(captures, harness) {
7667
7713
  }
7668
7714
  }];
7669
7715
  }
7716
+ const deferredListen = /* @__PURE__ */ new WeakMap();
7717
+ function listenClusterServer(server) {
7718
+ const deferred = deferredListen.get(server);
7719
+ if (!deferred) return Promise.resolve();
7720
+ deferred.listening ??= server.listen(deferred.port).then(() => {
7721
+ deferredListen.delete(server);
7722
+ }, (error) => {
7723
+ deferred.listening = void 0;
7724
+ throw error;
7725
+ });
7726
+ return deferred.listening;
7727
+ }
7670
7728
  async function createClusterServer(vitest, viteConfig, config, children) {
7671
7729
  const contribution = config._browserContribution;
7672
7730
  if (!contribution) {
@@ -7693,7 +7751,10 @@ async function createClusterServer(vitest, viteConfig, config, children) {
7693
7751
  });
7694
7752
  }
7695
7753
  const server = await createViteServer(viteConfig);
7696
- await server.listen(config.api.port);
7754
+ // listen on startup only for `api`/`ui`, otherwise on the first browser launch
7755
+ // (collect-only processes never listen, so Vite never starts a dependency scan)
7756
+ if (config._apiRequested) await server.listen(config.api.port);
7757
+ else deferredListen.set(server, { port: config.api.port });
7697
7758
  contribution.setupRpc(parent);
7698
7759
  return {
7699
7760
  server,
@@ -7847,6 +7908,8 @@ function ModuleRunnerTransform() {
7847
7908
  for (const name of names) {
7848
7909
  config.environments[name] ??= {};
7849
7910
  const environment = config.environments[name];
7911
+ // other environments consumed by the browser are not run by Vitest
7912
+ if (browserEnabled && name !== "client" && environment.consumer === "client") continue;
7850
7913
  environment.dev ??= {};
7851
7914
  // vm tests run using the native import mechanism
7852
7915
  if (name === "__vitest_vm__") {
@@ -7862,11 +7925,15 @@ function ModuleRunnerTransform() {
7862
7925
  configEnvironment: {
7863
7926
  order: "post",
7864
7927
  handler(name, config) {
7865
- if (name === "__vitest_vm__" || name === "__vitest__") return;
7866
- // In browser mode the `client` environment is browser-managed: don't
7867
- // apply node-runner externalization / `optimizeDeps` to it (that would
7868
- // discard the browser `optimizeDeps.include`, e.g. `vitest > expect-type`).
7869
- if (name === "client" && testConfig.browser?.enabled) return;
7928
+ if (name === "__vitest__") return;
7929
+ // In browser mode the `client` environment (and any other environment
7930
+ // consumed by the browser) is browser-managed: don't apply node-runner
7931
+ // externalization / `optimizeDeps` to it (that would discard the browser
7932
+ // `optimizeDeps.include`, e.g. `vitest > expect-type`).
7933
+ if (testConfig.browser?.enabled && (name === "client" || config.consumer === "client")) return;
7934
+ const optimizerOptions = name === "__vitest_vm__" ? void 0 : testConfig?.deps?.optimizer?.[name];
7935
+ config.optimizeDeps = resolveOptimizerConfig(optimizerOptions, config.optimizeDeps);
7936
+ if (name === "__vitest_vm__") return;
7870
7937
  config.resolve ??= {};
7871
7938
  // remove Vite's externalization logic because we have our own (unfortunately)
7872
7939
  config.resolve.external = [...builtinModules, ...builtinModules.filter((m) => !m.startsWith("node:")).map((m) => `node:${m}`)];
@@ -7875,7 +7942,6 @@ function ModuleRunnerTransform() {
7875
7942
  // to externalize modules and always resolve static imports
7876
7943
  // in both SSR and Client environments
7877
7944
  config.resolve.noExternal = true;
7878
- config.optimizeDeps = resolveOptimizerConfig(testConfig?.deps?.optimizer?.[name], config.optimizeDeps);
7879
7945
  }
7880
7946
  }
7881
7947
  };
@@ -9155,7 +9221,7 @@ function printStack(logger, project, stack, highlight, errorProperties, onStack)
9155
9221
  }
9156
9222
  }
9157
9223
  function hasProperties(obj) {
9158
- // eslint-disable-next-line no-unreachable-loop
9224
+ // oxlint-disable-next-line no-unreachable-loop
9159
9225
  for (const _key in obj) return true;
9160
9226
  return false;
9161
9227
  }
@@ -9232,7 +9298,7 @@ function NormalizeURLPlugin() {
9232
9298
  const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*(?:'[^']+'|"[^"]+"|`[^`]+`)\s*,\s*(?:'' \+ )?import\.meta\.url\s*(?:,\s*)?\)/g;
9233
9299
  let updatedCode = code;
9234
9300
  let match;
9235
- // eslint-disable-next-line no-cond-assign
9301
+ // oxlint-disable-next-line no-cond-assign
9236
9302
  while (match = assetImportMetaUrlRE.exec(cleanString)) {
9237
9303
  const { 0: exp, index } = match;
9238
9304
  const metaUrlIndex = index + exp.indexOf("import.meta.url");
@@ -9509,8 +9575,10 @@ function TestConfigPlugin(harness, captures, cliOptions, globalConfig, project,
9509
9575
  resolvedTestConfig.defines = defines;
9510
9576
  resolvedTestConfig._scriptDefines = scriptDefines;
9511
9577
  }
9578
+ const apiRequested = !!(testConfig.ui || testConfig.api);
9512
9579
  const api = resolveApiServerConfig(testConfig, isBrowserEnabled ? harness._browserLastPort++ : defaultPort, harness.logger);
9513
9580
  testConfig.api = api;
9581
+ resolvedTestConfig._apiRequested = apiRequested;
9514
9582
  if (globalConfig) {
9515
9583
  api.token = globalConfig.api.token;
9516
9584
  api.tokenCreated = globalConfig.api.tokenCreated;
@@ -10019,7 +10087,7 @@ class BrowserConnectionError extends Error {
10019
10087
  class LocationFilterFileNotFoundError extends Error {
10020
10088
  code = "VITEST_LOCATION_FILTER_FILE_NOT_FOUND";
10021
10089
  constructor(filename) {
10022
- super(`Couldn\'t find file ${filename}. Note when specifying the test location you have to specify the full test filename.`);
10090
+ super(`Couldn't find file ${filename}. Note when specifying the test location you have to specify the full test filename.`);
10023
10091
  }
10024
10092
  }
10025
10093
  class IncludeTaskLocationDisabledError extends Error {
@@ -10179,7 +10247,9 @@ class BrowserPool {
10179
10247
  this._otel.span.end();
10180
10248
  }
10181
10249
  reject(error) {
10182
- this._promise?.reject(error);
10250
+ // if user cancels the test run manually, ignore the error and exit gracefully
10251
+ if (this.project.vitest.isCancelling && error instanceof BrowserConnectionError) this._promise?.resolve();
10252
+ else this._promise?.reject(error);
10183
10253
  this._promise = void 0;
10184
10254
  this.cancel();
10185
10255
  }
@@ -11430,6 +11500,8 @@ class Pool {
11430
11500
  try {
11431
11501
  let isMemoryLimitReached = false;
11432
11502
  const runner = this.getPoolRunner(task, method);
11503
+ // `setMaxWorkers` can replace the map for the next group before this id is freed
11504
+ const workerIds = this.workerIds;
11433
11505
  const poolId = runner.poolId ?? this.getConcurrencyId();
11434
11506
  runner.poolId = poolId;
11435
11507
  const activeTask = {
@@ -11484,7 +11556,7 @@ class Pool {
11484
11556
  const id = setTimeout(() => this.logger.error(`[vitest-pool]: Timeout terminating ${task.worker} worker for test files ${formatFiles(task)}.`), this.options.teardownTimeout);
11485
11557
  this.exitPromises.push(runner.stop({ force: resolver.isRejected }).then(() => clearTimeout(id)).catch((error) => this.logger.error(`[vitest-pool]: Failed to terminate ${task.worker} worker for test files ${formatFiles(task)}.`, error)));
11486
11558
  }
11487
- this.freeWorkerId(poolId);
11559
+ workerIds.set(poolId, true);
11488
11560
  }
11489
11561
  // This is mostly to avoid zombie workers when/if Vitest internals run into errors
11490
11562
  catch (error) {
@@ -12044,7 +12116,6 @@ class TestProject {
12044
12116
  } catch (err) {
12045
12117
  throw new Error(`Cannot provide "${key}" because it's not serializable.`, { cause: err });
12046
12118
  }
12047
- // casting `any` because the default type is `never` since `ProvidedContext` is empty
12048
12119
  this._provided[key] = value;
12049
12120
  };
12050
12121
  /**
@@ -12292,14 +12363,16 @@ class TestProject {
12292
12363
  _initBrowserProvider = deduped(async () => {
12293
12364
  if (!this.isBrowserEnabled() || this.browser?.provider) return;
12294
12365
  // The browser server is created eagerly with the project, so `this.browser`
12295
- // is already set here; we only need to initialize the provider.
12296
- if (this.browser) await this.vitest.report("onBrowserInit", this);
12366
+ // is already set here; bind the port if startup did not, then init the provider.
12367
+ if (this.browser) {
12368
+ await listenClusterServer(this.vite);
12369
+ await this.vitest.report("onBrowserInit", this);
12370
+ }
12297
12371
  await this.browser?.initBrowserProvider(this);
12298
12372
  });
12299
12373
  _provideObject(context) {
12300
12374
  for (const _providedKey in context) {
12301
12375
  const providedKey = _providedKey;
12302
- // type is very strict here, so we cast it to any
12303
12376
  this.provide(providedKey, context[providedKey]);
12304
12377
  }
12305
12378
  }
@@ -14043,7 +14116,7 @@ class Logger {
14043
14116
  const url = new URL(this.ctx.config.uiBase, `http://${host}:${port}`);
14044
14117
  url.searchParams.set("token", this.ctx.config.api.token);
14045
14118
  this.log(PAD + y.dim(y.green(`UI started at ${url}`)));
14046
- } else if (this.ctx.config.api?.port) {
14119
+ } else if (this.ctx.config.api?.port && this.ctx.config._apiRequested) {
14047
14120
  const resolvedUrls = this.ctx.vite.resolvedUrls;
14048
14121
  // workaround for https://github.com/vitejs/vite/issues/15438, it was fixed in vite 5.1
14049
14122
  const fallbackUrl = `http://${this.ctx.config.api.host || "localhost"}:${this.ctx.config.api.port}`;
@@ -14584,7 +14657,7 @@ function resolveTestConfig(logger, options, viteConfig, globalConfig) {
14584
14657
  resolved.css.modules.classNameStrategy ??= "stable";
14585
14658
  }
14586
14659
  if (resolved.cache !== false) {
14587
- if (resolved.cache && typeof resolved.cache.dir === "string") 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"`);
14660
+ if (resolved.cache && typeof resolved.cache.dir === "string") 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"`);
14588
14661
  resolved.cache = { dir: viteConfig.cacheDir };
14589
14662
  }
14590
14663
  resolved.sequence ??= {};
@@ -15654,7 +15727,7 @@ async function parseTransformResult(moduleGraph, transformResult) {
15654
15727
  const lineColumnMap = createIndexLocationsMap(code);
15655
15728
  const importPositions = [];
15656
15729
  let match;
15657
- // eslint-disable-next-line no-cond-assign
15730
+ // oxlint-disable-next-line no-cond-assign
15658
15731
  while (match = regexp.exec(code)) {
15659
15732
  const startIndex = match.index;
15660
15733
  const endIndex = match.index + match[0].length - 1;
@@ -17932,12 +18005,7 @@ function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
17932
18005
  value = String(value || "").replace(regex, "");
17933
18006
  {
17934
18007
  // remove everything discouraged by XML 1.0 specifications
17935
- regex = /* @__PURE__ */ new RegExp(
17936
- /* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
17937
- "([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDFFE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uDFFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))",
17938
- "g"
17939
- /* eslint-enable */
17940
- );
18008
+ regex = /* @__PURE__ */ new RegExp("([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDFFE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uDFFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))", "g");
17941
18009
  value = value.replace(regex, "");
17942
18010
  }
17943
18011
  return value;
@@ -18380,17 +18448,17 @@ class VerboseReporter extends DefaultReporter {
18380
18448
  }
18381
18449
 
18382
18450
  const ReportersMap = {
18383
- "default": DefaultReporter,
18384
- "agent": MinimalReporter,
18385
- "minimal": MinimalReporter,
18386
- "blob": BlobReporter,
18387
- "verbose": VerboseReporter,
18388
- "dot": DotReporter,
18389
- "json": JsonReporter,
18390
- "tap": TapReporter,
18451
+ default: DefaultReporter,
18452
+ agent: MinimalReporter,
18453
+ minimal: MinimalReporter,
18454
+ blob: BlobReporter,
18455
+ verbose: VerboseReporter,
18456
+ dot: DotReporter,
18457
+ json: JsonReporter,
18458
+ tap: TapReporter,
18391
18459
  "tap-flat": TapFlatReporter,
18392
- "junit": JUnitReporter,
18393
- "tree": TreeReporter,
18460
+ junit: JUnitReporter,
18461
+ tree: TreeReporter,
18394
18462
  "hanging-process": HangingProcessReporter,
18395
18463
  "github-actions": GithubActionsReporter
18396
18464
  };
@@ -19982,7 +20050,9 @@ class TestRun {
19982
20050
  if (coverage) await this.vitest.report("onCoverage", coverage);
19983
20051
  // specification won't have the File task if they were filtered by the --shard command
19984
20052
  const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
19985
- const state = this.vitest.isCancelling ? "interrupted" : this.hasFailed(modules) ? "failed" : "passed";
20053
+ const state = this.vitest.isCancelling ? "interrupted" : // by this point, the run will be marked as failed if there are any errors,
20054
+ // should it be done by testRun.end?
20055
+ this.hasFailed(modules) ? "failed" : "passed";
19986
20056
  if (state !== "passed") process.exitCode = 1;
19987
20057
  await this.vitest.report("onTestRunEnd", modules, [...errors], state);
19988
20058
  for (const project in this.vitest.state.metadata) {
@@ -20500,7 +20570,9 @@ class Vitest {
20500
20570
  this._fsCache = new FileSystemModuleCache(this);
20501
20571
  this.snapshot = new SnapshotManager({ ...resolved.snapshotOptions });
20502
20572
  this._resolver = new VitestResolver(this.viteConfig.cacheDir, resolved);
20503
- this._fetcher = createFetchModuleFunction(this._resolver, resolved, this._fsCache, this._traces, this._tmpDir);
20573
+ // a closed run removes the temp dir, so a restart must allocate a new one
20574
+ const tmpDir = this._tmpDir ??= join(tmpdir(), nanoid());
20575
+ this._fetcher = createFetchModuleFunction(this._resolver, resolved, this._fsCache, this._traces, tmpDir);
20504
20576
  }
20505
20577
  _restartPromise;
20506
20578
  _restartQueued = false;
@@ -20594,7 +20666,7 @@ class Vitest {
20594
20666
  project,
20595
20667
  vitest: this,
20596
20668
  injectTestProjects: this.injectTestProject,
20597
- defineCacheKeyGenerator: (callback) => this._fsCache.defineCacheKeyGenerator(callback),
20669
+ defineCacheKeyGenerator: (callback) => this._fsCache.defineCacheKeyGenerator(project.config, callback),
20598
20670
  /**
20599
20671
  * @deprecated Use `defineCacheKeyGenerator` instead.
20600
20672
  */
@@ -20603,7 +20675,7 @@ class Vitest {
20603
20675
  this._warnedExperimentalCacheKeyGenerator = true;
20604
20676
  this.logger.deprecate("`experimental_defineCacheKeyGenerator` is deprecated. Use `defineCacheKeyGenerator` instead.");
20605
20677
  }
20606
- this._fsCache.defineCacheKeyGenerator(callback);
20678
+ this._fsCache.defineCacheKeyGenerator(project.config, callback);
20607
20679
  }
20608
20680
  }));
20609
20681
  }));
@@ -21435,11 +21507,29 @@ class Vitest {
21435
21507
  for (const error of errors) this.logger.error("error during close", error);
21436
21508
  this._checkUnhandledErrors(errors);
21437
21509
  });
21510
+ // the pool is down: forked workers read tmp module copies from disk
21511
+ await this._clearTmpDir();
21438
21512
  await this._traces?.finish();
21439
21513
  })();
21440
21514
  return this.closingPromise;
21441
21515
  }
21442
21516
  /**
21517
+ * Removes the root temp directory with the tmp module copies,
21518
+ * mirroring `TestProject.clearTmpDir()`. The reference is cleared before
21519
+ * the removal so a repeated close is a no-op.
21520
+ */
21521
+ async _clearTmpDir() {
21522
+ if (!this._tmpDir) return;
21523
+ const tmpDir = this._tmpDir;
21524
+ this._tmpDir = void 0;
21525
+ try {
21526
+ await rm(tmpDir, {
21527
+ recursive: true,
21528
+ force: true
21529
+ });
21530
+ } catch {}
21531
+ }
21532
+ /**
21443
21533
  * Closes all projects and exit the process
21444
21534
  * @param force If true, the process will exit immediately after closing the projects.
21445
21535
  */