vitest 4.0.13 → 4.0.15
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.
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/{base.Dqf2QAxh.js → base.CTp-EStD.js} +6 -6
- package/dist/chunks/browser.d.DBzUq_Na.d.ts +57 -0
- package/dist/chunks/{cac.L-UbQ_Ix.js → cac.BNNpZQl7.js} +10 -25
- package/dist/chunks/{cli-api.CdZ6wo9-.js → cli-api.C7sYjHmQ.js} +442 -110
- package/dist/chunks/{config.d.g6OOauRt.d.ts → config.d.CzIjkicf.d.ts} +1 -0
- package/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts +7 -0
- package/dist/chunks/{globals.C0izxiX3.js → globals.DOayXfHP.js} +3 -3
- package/dist/chunks/{index.DWDW6mLz.js → index.456_DGfR.js} +137 -22
- package/dist/chunks/{index.QWbK7rHY.js → index.BspFP3mn.js} +12 -7
- package/dist/chunks/{index.CMvpbrsJ.js → index.Drsj_6e7.js} +1 -1
- package/dist/chunks/{index.DBx1AtPJ.js → index.Z5E_ObnR.js} +1 -1
- package/dist/chunks/{index.CQwQ_SLL.js → index.bFLgAE-Z.js} +2 -2
- package/dist/chunks/{init-forks.CglOH45c.js → init-forks.CKEYp90N.js} +11 -2
- package/dist/chunks/{init-threads.BuMdIy1r.js → init-threads.D8Ok07M7.js} +1 -1
- package/dist/chunks/{init.MkYs5nmh.js → init.B04saIIg.js} +4 -4
- package/dist/chunks/modules.DJPjQW6m.js +35 -0
- package/dist/chunks/{plugin.d.B4l3vYM_.d.ts → plugin.d.CY7CUjf-.d.ts} +1 -1
- package/dist/chunks/{reporters.d.J2RlBlp9.d.ts → reporters.d.OXEK7y4s.d.ts} +29 -6
- package/dist/chunks/{setup-common.DGHc_BUK.js → setup-common.Cm-kSBVi.js} +1 -1
- package/dist/chunks/{startModuleRunner.W28wBIgJ.js → startModuleRunner.Iz2V0ESw.js} +8 -9
- package/dist/chunks/{test.DqQZzsWf.js → test.BT8LKgU9.js} +8 -3
- package/dist/chunks/{vi.BiaV1qII.js → vi.2VT5v0um.js} +40 -30
- package/dist/chunks/{vm.Y19jrZy2.js → vm.BwmD1Rql.js} +2 -2
- package/dist/chunks/{worker.d.DCy61tzi.d.ts → worker.d.B4A26qg6.d.ts} +2 -2
- package/dist/cli.js +2 -2
- package/dist/config.d.ts +7 -7
- package/dist/coverage.d.ts +8 -8
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +22 -14
- package/dist/index.js +3 -3
- package/dist/module-evaluator.d.ts +5 -0
- package/dist/module-evaluator.js +23 -5
- package/dist/module-runner.js +2 -1
- package/dist/node.d.ts +8 -8
- package/dist/node.js +7 -6
- package/dist/reporters.d.ts +7 -7
- package/dist/reporters.js +2 -2
- package/dist/runners.d.ts +2 -1
- package/dist/runners.js +2 -2
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +10 -9
- package/dist/workers/forks.js +11 -10
- package/dist/workers/runVmTests.js +5 -5
- package/dist/workers/threads.js +11 -10
- package/dist/workers/vmForks.js +6 -5
- package/dist/workers/vmThreads.js +6 -5
- package/package.json +15 -20
- package/dist/chunks/browser.d.CDvMh6F9.d.ts +0 -18
|
@@ -6,16 +6,16 @@ import { noop, createDefer, slash, isExternalUrl, unwrapId, withTrailingSlash, c
|
|
|
6
6
|
import { a as any, p as prompt } from './index.D4KonVSU.js';
|
|
7
7
|
import { h as hash, R as RandomSequencer, i as isPackageExists, c as isBrowserEnabled, r as resolveConfig, g as getCoverageProvider, a as resolveApiServerConfig, d as resolveModule } from './coverage.CtyeYmKM.js';
|
|
8
8
|
import * as vite from 'vite';
|
|
9
|
-
import { parseAst, searchForWorkspaceRoot, fetchModule, version, mergeConfig, createServer } from 'vite';
|
|
9
|
+
import { isFileServingAllowed, parseAst, searchForWorkspaceRoot, fetchModule, version, mergeConfig, createServer } from 'vite';
|
|
10
10
|
import { A as API_PATH, c as configFiles, d as defaultBrowserPort, a as defaultPort } from './constants.D_Q9UYh-.js';
|
|
11
11
|
import * as nodeos from 'node:os';
|
|
12
12
|
import nodeos__default, { tmpdir } from 'node:os';
|
|
13
|
-
import { generateHash as generateHash$1, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, getTasks, isTestCase } from '@vitest/runner/utils';
|
|
13
|
+
import { generateHash as generateHash$1, createTaskName, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, getTasks, isTestCase } from '@vitest/runner/utils';
|
|
14
14
|
import { SnapshotManager } from '@vitest/snapshot/manager';
|
|
15
|
-
import { v as version$1 } from './cac.
|
|
15
|
+
import { v as version$1 } from './cac.BNNpZQl7.js';
|
|
16
16
|
import { performance as performance$1 } from 'node:perf_hooks';
|
|
17
17
|
import { c as createBirpc } from './index.0kCJoeWi.js';
|
|
18
|
-
import { p as parse, d as stringify, e as TraceMap, o as originalPositionFor,
|
|
18
|
+
import { p as parse, d as stringify, e as createIndexLocationsMap, h as TraceMap, o as originalPositionFor, i as ancestor, j as printError, f as formatProjectName, w as withLabel, k as errorBanner, l as divider, m as Typechecker, n as generateCodeFrame, q as escapeRegExp, r as createDefinesScript, R as ReportersMap, u as groupBy, B as BlobReporter, v as readBlobs, x as convertTasksToEvents, H as HangingProcessReporter, y as wildcardPatternToRegExp, z as stdout } from './index.456_DGfR.js';
|
|
19
19
|
import require$$0$3 from 'events';
|
|
20
20
|
import require$$1$1 from 'https';
|
|
21
21
|
import require$$2 from 'http';
|
|
@@ -30,18 +30,19 @@ import { g as getDefaultExportFromCjs } from './_commonjsHelpers.D26ty3Ew.js';
|
|
|
30
30
|
import crypto, { createHash } from 'node:crypto';
|
|
31
31
|
import { rootDir, distDir } from '../path.js';
|
|
32
32
|
import { T as Traces } from './traces.U4xDYhzZ.js';
|
|
33
|
-
import createDebug from '
|
|
33
|
+
import { createDebug } from 'obug';
|
|
34
34
|
import { rm, readFile, writeFile, rename, stat, unlink, mkdir, copyFile } from 'node:fs/promises';
|
|
35
35
|
import c from 'tinyrainbow';
|
|
36
36
|
import { VitestModuleEvaluator } from '#module-evaluator';
|
|
37
37
|
import { ModuleRunner } from 'vite/module-runner';
|
|
38
38
|
import { Console } from 'node:console';
|
|
39
39
|
import { highlight } from '@vitest/utils/highlight';
|
|
40
|
-
import { createRequire,
|
|
40
|
+
import { createRequire, builtinModules, isBuiltin as isBuiltin$1 } from 'node:module';
|
|
41
41
|
import url, { fileURLToPath, pathToFileURL } from 'node:url';
|
|
42
42
|
import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
|
|
43
43
|
import { isatty } from 'node:tty';
|
|
44
44
|
import EventEmitter$1, { EventEmitter } from 'node:events';
|
|
45
|
+
import { t as toBuiltin, i as isBuiltin } from './modules.DJPjQW6m.js';
|
|
45
46
|
import { fork } from 'node:child_process';
|
|
46
47
|
import { Worker } from 'node:worker_threads';
|
|
47
48
|
import pm from 'picomatch';
|
|
@@ -52,7 +53,7 @@ import { c as configDefaults } from './defaults.BOqNVLsY.js';
|
|
|
52
53
|
import { KNOWN_ASSET_RE } from '@vitest/utils/constants';
|
|
53
54
|
import { findNearestPackageData } from '@vitest/utils/resolver';
|
|
54
55
|
import * as esModuleLexer from 'es-module-lexer';
|
|
55
|
-
import { a as BenchmarkReportsMap } from './index.
|
|
56
|
+
import { a as BenchmarkReportsMap } from './index.Drsj_6e7.js';
|
|
56
57
|
import assert$1 from 'node:assert';
|
|
57
58
|
import { serializeValue } from '@vitest/utils/serialize';
|
|
58
59
|
import { parseErrorStacktrace } from '@vitest/utils/source-map';
|
|
@@ -5025,30 +5026,55 @@ function requireWebsocketServer () {
|
|
|
5025
5026
|
var websocketServerExports = requireWebsocketServer();
|
|
5026
5027
|
var WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServerExports);
|
|
5027
5028
|
|
|
5028
|
-
|
|
5029
|
+
function getTestFileEnvironment(project, testFile, browser = false) {
|
|
5030
|
+
let environment;
|
|
5031
|
+
if (browser) environment = project.browser?.vite.environments.client;
|
|
5032
|
+
else for (const name in project.vite.environments) {
|
|
5033
|
+
const env = project.vite.environments[name];
|
|
5034
|
+
if (env.moduleGraph.getModuleById(testFile)) {
|
|
5035
|
+
environment = env;
|
|
5036
|
+
break;
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
return environment;
|
|
5040
|
+
}
|
|
5041
|
+
|
|
5042
|
+
async function getModuleGraph(ctx, projectName, testFilePath, browser = false) {
|
|
5029
5043
|
const graph = {};
|
|
5030
5044
|
const externalized = /* @__PURE__ */ new Set();
|
|
5031
5045
|
const inlined = /* @__PURE__ */ new Set();
|
|
5032
5046
|
const project = ctx.getProjectByName(projectName);
|
|
5033
|
-
|
|
5047
|
+
const environment = getTestFileEnvironment(project, testFilePath, browser);
|
|
5048
|
+
if (!environment) throw new Error(`Cannot find environment for ${testFilePath}`);
|
|
5049
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5050
|
+
function get(mod) {
|
|
5034
5051
|
if (!mod || !mod.id) return;
|
|
5035
|
-
if (mod.id === "\0vitest/browser") return;
|
|
5052
|
+
if (mod.id === "\0vitest/browser" || mod.id.includes("plugin-vue:export-helper")) return;
|
|
5036
5053
|
if (seen.has(mod)) return seen.get(mod);
|
|
5037
|
-
|
|
5054
|
+
const id = clearId(mod.id);
|
|
5038
5055
|
seen.set(mod, id);
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5056
|
+
if (id.startsWith("__vite-browser-external:")) {
|
|
5057
|
+
const external = id.slice(24);
|
|
5058
|
+
externalized.add(external);
|
|
5059
|
+
return external;
|
|
5060
|
+
}
|
|
5061
|
+
const external = project._resolver.wasExternalized(id);
|
|
5062
|
+
if (typeof external === "string") {
|
|
5063
|
+
externalized.add(external);
|
|
5064
|
+
return external;
|
|
5065
|
+
}
|
|
5066
|
+
if (browser && mod.file?.includes(project.browser.vite.config.cacheDir)) {
|
|
5067
|
+
externalized.add(mod.id);
|
|
5068
|
+
return id;
|
|
5069
|
+
}
|
|
5070
|
+
inlined.add(id);
|
|
5071
|
+
graph[id] = Array.from(mod.importedModules).filter((i) => i.id && !i.id.includes("/vitest/dist/")).map((m) => get(m)).filter(Boolean);
|
|
5048
5072
|
return id;
|
|
5049
5073
|
}
|
|
5050
|
-
|
|
5051
|
-
|
|
5074
|
+
get(environment.moduleGraph.getModuleById(testFilePath));
|
|
5075
|
+
project.config.setupFiles.forEach((setupFile) => {
|
|
5076
|
+
get(environment.moduleGraph.getModuleById(setupFile));
|
|
5077
|
+
});
|
|
5052
5078
|
return {
|
|
5053
5079
|
graph,
|
|
5054
5080
|
externalized: Array.from(externalized),
|
|
@@ -5146,15 +5172,36 @@ function setup(ctx, _server) {
|
|
|
5146
5172
|
color: p.color
|
|
5147
5173
|
}));
|
|
5148
5174
|
},
|
|
5149
|
-
async
|
|
5175
|
+
async getExternalResult(moduleId, testFileTaskId) {
|
|
5176
|
+
const testModule = ctx.state.getReportedEntityById(testFileTaskId);
|
|
5177
|
+
if (!testModule) return;
|
|
5178
|
+
if (!isFileServingAllowed(testModule.project.vite.config, moduleId)) return;
|
|
5179
|
+
const result = {};
|
|
5180
|
+
try {
|
|
5181
|
+
result.source = await promises.readFile(moduleId, "utf-8");
|
|
5182
|
+
} catch {}
|
|
5183
|
+
return result;
|
|
5184
|
+
},
|
|
5185
|
+
async getTransformResult(projectName, moduleId, testFileTaskId, browser = false) {
|
|
5150
5186
|
const project = ctx.getProjectByName(projectName);
|
|
5151
|
-
const
|
|
5152
|
-
if (
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5187
|
+
const testModule = ctx.state.getReportedEntityById(testFileTaskId);
|
|
5188
|
+
if (!testModule || !isFileServingAllowed(project.vite.config, moduleId)) return;
|
|
5189
|
+
const environment = getTestFileEnvironment(project, testModule.moduleId, browser);
|
|
5190
|
+
const moduleNode = environment?.moduleGraph.getModuleById(moduleId);
|
|
5191
|
+
if (!environment || !moduleNode?.transformResult) return;
|
|
5192
|
+
const result = moduleNode.transformResult;
|
|
5193
|
+
try {
|
|
5194
|
+
result.source = result.source || (moduleNode.file ? await promises.readFile(moduleNode.file, "utf-8") : void 0);
|
|
5195
|
+
} catch {}
|
|
5196
|
+
// TODO: store this in HTML reporter separetly
|
|
5197
|
+
const transformDuration = ctx.state.metadata[projectName]?.duration[moduleNode.url]?.[0];
|
|
5198
|
+
if (transformDuration != null) result.transformTime = transformDuration;
|
|
5199
|
+
try {
|
|
5200
|
+
const diagnostic = await ctx.experimental_getSourceModuleDiagnostic(moduleId, testModule);
|
|
5201
|
+
result.modules = diagnostic.modules;
|
|
5202
|
+
result.untrackedModules = diagnostic.untrackedModules;
|
|
5203
|
+
} catch {}
|
|
5204
|
+
return result;
|
|
5158
5205
|
},
|
|
5159
5206
|
async getModuleGraph(project, id, browser) {
|
|
5160
5207
|
return getModuleGraph(ctx, project, id, browser);
|
|
@@ -5273,31 +5320,6 @@ var setup$1 = /*#__PURE__*/Object.freeze({
|
|
|
5273
5320
|
setup: setup
|
|
5274
5321
|
});
|
|
5275
5322
|
|
|
5276
|
-
function groupBy(collection, iteratee) {
|
|
5277
|
-
return collection.reduce((acc, item) => {
|
|
5278
|
-
const key = iteratee(item);
|
|
5279
|
-
acc[key] ||= [];
|
|
5280
|
-
acc[key].push(item);
|
|
5281
|
-
return acc;
|
|
5282
|
-
}, {});
|
|
5283
|
-
}
|
|
5284
|
-
function stdout() {
|
|
5285
|
-
// @ts-expect-error Node.js maps process.stdout to console._stdout
|
|
5286
|
-
// eslint-disable-next-line no-console
|
|
5287
|
-
return console._stdout || process.stdout;
|
|
5288
|
-
}
|
|
5289
|
-
function escapeRegExp(s) {
|
|
5290
|
-
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
5291
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5292
|
-
}
|
|
5293
|
-
function wildcardPatternToRegExp(pattern) {
|
|
5294
|
-
const negated = pattern[0] === "!";
|
|
5295
|
-
if (negated) pattern = pattern.slice(1);
|
|
5296
|
-
let regexp = `${pattern.split("*").map(escapeRegExp).join(".*")}$`;
|
|
5297
|
-
if (negated) regexp = `(?!${regexp})`;
|
|
5298
|
-
return new RegExp(`^${regexp}`, "i");
|
|
5299
|
-
}
|
|
5300
|
-
|
|
5301
5323
|
function createDebugger(namespace) {
|
|
5302
5324
|
const debug = createDebug(namespace);
|
|
5303
5325
|
if (debug.enabled) return debug;
|
|
@@ -5401,6 +5423,7 @@ function createFailedFileTask(project, filepath, error) {
|
|
|
5401
5423
|
type: "suite",
|
|
5402
5424
|
id: /* @__PURE__ */ generateHash$1(`${testFilepath}${project.config.name || ""}`),
|
|
5403
5425
|
name: testFilepath,
|
|
5426
|
+
fullName: testFilepath,
|
|
5404
5427
|
mode: "run",
|
|
5405
5428
|
tasks: [],
|
|
5406
5429
|
start: 0,
|
|
@@ -5438,6 +5461,7 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5438
5461
|
type: "suite",
|
|
5439
5462
|
id: /* @__PURE__ */ generateHash$1(`${testFilepath}${options.name || ""}`),
|
|
5440
5463
|
name: testFilepath,
|
|
5464
|
+
fullName: testFilepath,
|
|
5441
5465
|
mode: "run",
|
|
5442
5466
|
tasks: [],
|
|
5443
5467
|
start: ast.start,
|
|
@@ -5448,7 +5472,7 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5448
5472
|
file: null
|
|
5449
5473
|
};
|
|
5450
5474
|
file.file = file;
|
|
5451
|
-
const indexMap =
|
|
5475
|
+
const indexMap = createIndexLocationsMap(code);
|
|
5452
5476
|
const map = requestMap && new TraceMap(requestMap);
|
|
5453
5477
|
let lastSuite = file;
|
|
5454
5478
|
const updateLatestSuite = (index) => {
|
|
@@ -5482,6 +5506,8 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5482
5506
|
tasks: [],
|
|
5483
5507
|
mode,
|
|
5484
5508
|
name: definition.name,
|
|
5509
|
+
fullName: createTaskName([latestSuite.fullName, definition.name]),
|
|
5510
|
+
fullTestName: createTaskName([latestSuite.fullTestName, definition.name]),
|
|
5485
5511
|
end: definition.end,
|
|
5486
5512
|
start: definition.start,
|
|
5487
5513
|
location,
|
|
@@ -5501,6 +5527,8 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5501
5527
|
mode,
|
|
5502
5528
|
context: {},
|
|
5503
5529
|
name: definition.name,
|
|
5530
|
+
fullName: createTaskName([latestSuite.fullName, definition.name]),
|
|
5531
|
+
fullTestName: createTaskName([latestSuite.fullTestName, definition.name]),
|
|
5504
5532
|
end: definition.end,
|
|
5505
5533
|
start: definition.start,
|
|
5506
5534
|
location,
|
|
@@ -5546,23 +5574,6 @@ async function transformSSR(project, filepath) {
|
|
|
5546
5574
|
if (!request) return null;
|
|
5547
5575
|
return await project.vite.ssrTransform(request.code, request.map, filepath);
|
|
5548
5576
|
}
|
|
5549
|
-
function createIndexMap(source) {
|
|
5550
|
-
const map = /* @__PURE__ */ new Map();
|
|
5551
|
-
let index = 0;
|
|
5552
|
-
let line = 1;
|
|
5553
|
-
let column = 1;
|
|
5554
|
-
for (const char of source) {
|
|
5555
|
-
map.set(index++, {
|
|
5556
|
-
line,
|
|
5557
|
-
column
|
|
5558
|
-
});
|
|
5559
|
-
if (char === "\n" || char === "\r\n") {
|
|
5560
|
-
line++;
|
|
5561
|
-
column = 0;
|
|
5562
|
-
} else column++;
|
|
5563
|
-
}
|
|
5564
|
-
return map;
|
|
5565
|
-
}
|
|
5566
5577
|
function markDynamicTests(tasks) {
|
|
5567
5578
|
for (const task of tasks) {
|
|
5568
5579
|
if (task.dynamic) task.id += "-dynamic";
|
|
@@ -5760,7 +5771,7 @@ class FileSystemModuleCache {
|
|
|
5760
5771
|
*/
|
|
5761
5772
|
rootCache;
|
|
5762
5773
|
metadataFilePath;
|
|
5763
|
-
version = "1.0.0-beta.
|
|
5774
|
+
version = "1.0.0-beta.3";
|
|
5764
5775
|
fsCacheRoots = /* @__PURE__ */ new WeakMap();
|
|
5765
5776
|
fsEnvironmentHashMap = /* @__PURE__ */ new WeakMap();
|
|
5766
5777
|
fsCacheKeyGenerators = /* @__PURE__ */ new Set();
|
|
@@ -5805,7 +5816,7 @@ class FileSystemModuleCache {
|
|
|
5805
5816
|
}
|
|
5806
5817
|
async getCachedModule(cachedFilePath) {
|
|
5807
5818
|
if (!existsSync(cachedFilePath)) {
|
|
5808
|
-
debugFs?.(`${c.red("[empty]")} ${cachedFilePath} doesn't exist, transforming by vite
|
|
5819
|
+
debugFs?.(`${c.red("[empty]")} ${cachedFilePath} doesn't exist, transforming by vite first`);
|
|
5809
5820
|
return;
|
|
5810
5821
|
}
|
|
5811
5822
|
const fileResult = await this.readCachedFileConcurrently(cachedFilePath);
|
|
@@ -5818,16 +5829,18 @@ class FileSystemModuleCache {
|
|
|
5818
5829
|
file: meta.file,
|
|
5819
5830
|
code,
|
|
5820
5831
|
importers: meta.importers,
|
|
5832
|
+
importedUrls: meta.importedUrls,
|
|
5821
5833
|
mappings: meta.mappings
|
|
5822
5834
|
};
|
|
5823
5835
|
}
|
|
5824
|
-
async saveCachedModule(cachedFilePath, fetchResult, importers = [], mappings = false) {
|
|
5836
|
+
async saveCachedModule(cachedFilePath, fetchResult, importers = [], importedUrls = [], mappings = false) {
|
|
5825
5837
|
if ("code" in fetchResult) {
|
|
5826
5838
|
const result = {
|
|
5827
5839
|
file: fetchResult.file,
|
|
5828
5840
|
id: fetchResult.id,
|
|
5829
5841
|
url: fetchResult.url,
|
|
5830
5842
|
importers,
|
|
5843
|
+
importedUrls,
|
|
5831
5844
|
mappings
|
|
5832
5845
|
};
|
|
5833
5846
|
debugFs?.(`${c.yellow("[write]")} ${fetchResult.id} is cached in ${cachedFilePath}`);
|
|
@@ -5855,12 +5868,12 @@ class FileSystemModuleCache {
|
|
|
5855
5868
|
else if (result === null) debugMemory?.(`${c.green("[read]")} ${id} was bailed out`);
|
|
5856
5869
|
return result;
|
|
5857
5870
|
}
|
|
5858
|
-
generateCachePath(vitestConfig, environment,
|
|
5871
|
+
generateCachePath(vitestConfig, environment, id, fileContent) {
|
|
5859
5872
|
// bail out if file has import.meta.glob because it depends on other files
|
|
5860
5873
|
// TODO: figure out a way to still support it
|
|
5861
5874
|
if (fileContent.includes("import.meta.glob(")) {
|
|
5862
5875
|
this.saveMemoryCache(environment, id, null);
|
|
5863
|
-
debugMemory?.(`${c.yellow("[write]")} ${id} was bailed out`);
|
|
5876
|
+
debugMemory?.(`${c.yellow("[write]")} ${id} was bailed out because it has "import.meta.glob"`);
|
|
5864
5877
|
return null;
|
|
5865
5878
|
}
|
|
5866
5879
|
let hashString = "";
|
|
@@ -5889,7 +5902,7 @@ class FileSystemModuleCache {
|
|
|
5889
5902
|
mode: config.mode,
|
|
5890
5903
|
consumer: config.consumer,
|
|
5891
5904
|
resolve: config.resolve,
|
|
5892
|
-
plugins: config.plugins.map((p) => p.name),
|
|
5905
|
+
plugins: config.plugins.filter((p) => p.api?.vitest?.experimental?.ignoreFsModuleCache !== true).map((p) => p.name),
|
|
5893
5906
|
configFileDependencies: config.configFileDependencies.map((file) => tryReadFileSync(file)),
|
|
5894
5907
|
environment: environment.name,
|
|
5895
5908
|
css: vitestConfig.css
|
|
@@ -5904,6 +5917,7 @@ class FileSystemModuleCache {
|
|
|
5904
5917
|
let cacheRoot = this.fsCacheRoots.get(vitestConfig);
|
|
5905
5918
|
if (cacheRoot == null) {
|
|
5906
5919
|
cacheRoot = vitestConfig.experimental.fsModuleCachePath || this.rootCache;
|
|
5920
|
+
this.fsCacheRoots.set(vitestConfig, cacheRoot);
|
|
5907
5921
|
if (!existsSync(cacheRoot)) mkdirSync(cacheRoot, { recursive: true });
|
|
5908
5922
|
}
|
|
5909
5923
|
const fsResultPath = join(cacheRoot, cacheKey);
|
|
@@ -6073,11 +6087,10 @@ const readFilePromises = /* @__PURE__ */ new Map();
|
|
|
6073
6087
|
class ModuleFetcher {
|
|
6074
6088
|
tmpDirectories = /* @__PURE__ */ new Set();
|
|
6075
6089
|
fsCacheEnabled;
|
|
6076
|
-
constructor(resolver, config, fsCache,
|
|
6090
|
+
constructor(resolver, config, fsCache, tmpProjectDir) {
|
|
6077
6091
|
this.resolver = resolver;
|
|
6078
6092
|
this.config = config;
|
|
6079
6093
|
this.fsCache = fsCache;
|
|
6080
|
-
this.traces = traces;
|
|
6081
6094
|
this.tmpProjectDir = tmpProjectDir;
|
|
6082
6095
|
this.fsCacheEnabled = config.experimental?.fsModuleCache === true;
|
|
6083
6096
|
}
|
|
@@ -6108,6 +6121,11 @@ class ModuleFetcher {
|
|
|
6108
6121
|
const cached = !!moduleGraphModule.transformResult;
|
|
6109
6122
|
if (moduleGraphModule.file) trace.setAttribute("code.file.path", moduleGraphModule.file);
|
|
6110
6123
|
if (options?.cached && cached) return { cache: true };
|
|
6124
|
+
const externalize = await this.resolver.shouldExternalize(moduleGraphModule.id);
|
|
6125
|
+
if (externalize) return {
|
|
6126
|
+
externalize,
|
|
6127
|
+
type: "module"
|
|
6128
|
+
};
|
|
6111
6129
|
const cachePath = await this.getCachePath(environment, moduleGraphModule);
|
|
6112
6130
|
// full fs caching is disabled, but we still want to keep tmp files if makeTmpCopies is enabled
|
|
6113
6131
|
// this is primarily used by the forks pool to avoid using process.send(bigBuffer)
|
|
@@ -6140,10 +6158,20 @@ class ModuleFetcher {
|
|
|
6140
6158
|
}
|
|
6141
6159
|
const result = await this.fetchAndProcess(environment, url, importer, moduleGraphModule, options);
|
|
6142
6160
|
const importers = this.getSerializedDependencies(moduleGraphModule);
|
|
6161
|
+
const importedUrls = this.getSerializedImports(moduleGraphModule);
|
|
6143
6162
|
const map = moduleGraphModule.transformResult?.map;
|
|
6144
6163
|
const mappings = map && !("version" in map) && map.mappings === "";
|
|
6145
|
-
return this.cacheResult(result, cachePath, importers, !!mappings);
|
|
6164
|
+
return this.cacheResult(result, cachePath, importers, importedUrls, !!mappings);
|
|
6146
6165
|
}
|
|
6166
|
+
// we need this for UI to be able to show a module graph
|
|
6167
|
+
getSerializedImports(node) {
|
|
6168
|
+
const imports = [];
|
|
6169
|
+
node.importedModules.forEach((importer) => {
|
|
6170
|
+
imports.push(importer.url);
|
|
6171
|
+
});
|
|
6172
|
+
return imports;
|
|
6173
|
+
}
|
|
6174
|
+
// we need this for the watcher to be able to find the related test file
|
|
6147
6175
|
getSerializedDependencies(node) {
|
|
6148
6176
|
const dependencies = [];
|
|
6149
6177
|
node.importers.forEach((importer) => {
|
|
@@ -6175,7 +6203,7 @@ class ModuleFetcher {
|
|
|
6175
6203
|
// null means the file should not be cached
|
|
6176
6204
|
if (memoryCacheKey !== void 0) return memoryCacheKey;
|
|
6177
6205
|
const fileContent = await this.readFileContentToCache(environment, moduleGraphModule);
|
|
6178
|
-
return this.fsCache.generateCachePath(this.config, environment,
|
|
6206
|
+
return this.fsCache.generateCachePath(this.config, environment, moduleGraphModule.id, fileContent);
|
|
6179
6207
|
}
|
|
6180
6208
|
async readFileContentToCache(environment, moduleGraphModule) {
|
|
6181
6209
|
if (moduleGraphModule.file && !moduleGraphModule.file.startsWith("\0") && !moduleGraphModule.file.startsWith("virtual:")) {
|
|
@@ -6214,6 +6242,10 @@ class ModuleFetcher {
|
|
|
6214
6242
|
const environmentNode = environment.moduleGraph.getModuleById(importer);
|
|
6215
6243
|
if (environmentNode) moduleGraphModule.importers.add(environmentNode);
|
|
6216
6244
|
});
|
|
6245
|
+
await Promise.all(cachedModule.importedUrls.map(async (url) => {
|
|
6246
|
+
const moduleNode = await environment.moduleGraph.ensureEntryFromUrl(url).catch(() => null);
|
|
6247
|
+
if (moduleNode) moduleGraphModule.importedModules.add(moduleNode);
|
|
6248
|
+
}));
|
|
6217
6249
|
return {
|
|
6218
6250
|
cached: true,
|
|
6219
6251
|
file: cachedModule.file,
|
|
@@ -6224,23 +6256,18 @@ class ModuleFetcher {
|
|
|
6224
6256
|
};
|
|
6225
6257
|
}
|
|
6226
6258
|
async fetchAndProcess(environment, url, importer, moduleGraphModule, options) {
|
|
6227
|
-
const externalize = await this.resolver.shouldExternalize(moduleGraphModule.id);
|
|
6228
|
-
if (externalize) return {
|
|
6229
|
-
externalize,
|
|
6230
|
-
type: "module"
|
|
6231
|
-
};
|
|
6232
6259
|
return processResultSource(environment, await fetchModule(environment, url, importer, {
|
|
6233
6260
|
...options,
|
|
6234
6261
|
inlineSourceMap: false
|
|
6235
6262
|
}).catch(handleRollupError));
|
|
6236
6263
|
}
|
|
6237
|
-
async cacheResult(result, cachePath, importers = [], mappings = false) {
|
|
6264
|
+
async cacheResult(result, cachePath, importers = [], importedUrls = [], mappings = false) {
|
|
6238
6265
|
const returnResult = "code" in result ? getCachedResult(result, cachePath) : result;
|
|
6239
6266
|
if (saveCachePromises.has(cachePath)) {
|
|
6240
6267
|
await saveCachePromises.get(cachePath);
|
|
6241
6268
|
return returnResult;
|
|
6242
6269
|
}
|
|
6243
|
-
const savePromise = this.fsCache.saveCachedModule(cachePath, result, importers, mappings).then(() => result).finally(() => {
|
|
6270
|
+
const savePromise = this.fsCache.saveCachedModule(cachePath, result, importers, importedUrls, mappings).then(() => result).finally(() => {
|
|
6244
6271
|
saveCachePromises.delete(cachePath);
|
|
6245
6272
|
});
|
|
6246
6273
|
saveCachePromises.set(cachePath, savePromise);
|
|
@@ -6259,7 +6286,7 @@ class ModuleFetcher {
|
|
|
6259
6286
|
}
|
|
6260
6287
|
}
|
|
6261
6288
|
function createFetchModuleFunction(resolver, config, fsCache, traces, tmpProjectDir) {
|
|
6262
|
-
const fetcher = new ModuleFetcher(resolver, config, fsCache,
|
|
6289
|
+
const fetcher = new ModuleFetcher(resolver, config, fsCache, tmpProjectDir);
|
|
6263
6290
|
return async (url, importer, environment, cacheFs, options, otelCarrier) => {
|
|
6264
6291
|
await traces.waitInit();
|
|
6265
6292
|
const context = otelCarrier ? traces.getContextFromCarrier(otelCarrier) : void 0;
|
|
@@ -6658,6 +6685,238 @@ This might cause false positive tests. Resolve unhandled errors to make sure you
|
|
|
6658
6685
|
}
|
|
6659
6686
|
}
|
|
6660
6687
|
|
|
6688
|
+
// this function recieves the module diagnostic with the location of imports
|
|
6689
|
+
// and populates it with collected import durations; the duration is injected
|
|
6690
|
+
// only if the current module is the one that imported the module
|
|
6691
|
+
// if testModule is not defined, then Vitest aggregates durations of ALL collected test modules
|
|
6692
|
+
function collectModuleDurationsDiagnostic(moduleId, state, moduleDiagnostic, testModule) {
|
|
6693
|
+
if (!moduleDiagnostic) return {
|
|
6694
|
+
modules: [],
|
|
6695
|
+
untrackedModules: []
|
|
6696
|
+
};
|
|
6697
|
+
const modules = [];
|
|
6698
|
+
const modulesById = {};
|
|
6699
|
+
const allModules = [...moduleDiagnostic.modules, ...moduleDiagnostic.untracked];
|
|
6700
|
+
const visitedByFiles = {};
|
|
6701
|
+
// this aggregates the times for _ALL_ tests if testModule is not passed
|
|
6702
|
+
// so if the module was imported in separate tests, the time will be accumulated
|
|
6703
|
+
for (const files of testModule ? [[testModule.task]] : state.filesMap.values()) for (const file of files) {
|
|
6704
|
+
const importDurations = file.importDurations;
|
|
6705
|
+
if (!importDurations) continue;
|
|
6706
|
+
const currentModule = state.getReportedEntity(file);
|
|
6707
|
+
if (!currentModule) continue;
|
|
6708
|
+
const visitedKey = currentModule.project.config.isolate === false ? "non-isolate" : file.id;
|
|
6709
|
+
if (!visitedByFiles[visitedKey]) visitedByFiles[visitedKey] = /* @__PURE__ */ new Set();
|
|
6710
|
+
const visited = visitedByFiles[visitedKey];
|
|
6711
|
+
allModules.forEach(({ resolvedId, resolvedUrl }) => {
|
|
6712
|
+
const durations = importDurations[resolvedId];
|
|
6713
|
+
// do not accumulate if module was already visited by suite (or suites in non-isolate mode)
|
|
6714
|
+
if (!durations || visited.has(resolvedId)) return;
|
|
6715
|
+
const importer = getModuleImporter(moduleId, durations, currentModule);
|
|
6716
|
+
modulesById[resolvedId] ??= {
|
|
6717
|
+
selfTime: 0,
|
|
6718
|
+
totalTime: 0,
|
|
6719
|
+
transformTime: 0,
|
|
6720
|
+
external: durations.external,
|
|
6721
|
+
importer
|
|
6722
|
+
};
|
|
6723
|
+
// only track if the current module imported this module,
|
|
6724
|
+
// otherwise it was imported instantly because it's cached
|
|
6725
|
+
if (importer === moduleId) {
|
|
6726
|
+
visited.add(resolvedId);
|
|
6727
|
+
modulesById[resolvedId].selfTime += durations.selfTime;
|
|
6728
|
+
modulesById[resolvedId].totalTime += durations.totalTime;
|
|
6729
|
+
// don't aggregate
|
|
6730
|
+
modulesById[resolvedId].transformTime = state.metadata[currentModule.project.name]?.duration[resolvedUrl]?.[0];
|
|
6731
|
+
}
|
|
6732
|
+
});
|
|
6733
|
+
}
|
|
6734
|
+
// if module was imported twice in the same file,
|
|
6735
|
+
// show only one time - the second should be shown as 0
|
|
6736
|
+
const visitedInFile = /* @__PURE__ */ new Set();
|
|
6737
|
+
moduleDiagnostic.modules.forEach((diagnostic) => {
|
|
6738
|
+
const durations = modulesById[diagnostic.resolvedId];
|
|
6739
|
+
if (!durations) return;
|
|
6740
|
+
if (visitedInFile.has(diagnostic.resolvedId)) modules.push({
|
|
6741
|
+
...diagnostic,
|
|
6742
|
+
selfTime: 0,
|
|
6743
|
+
totalTime: 0,
|
|
6744
|
+
transformTime: 0,
|
|
6745
|
+
external: durations.external,
|
|
6746
|
+
importer: durations.importer
|
|
6747
|
+
});
|
|
6748
|
+
else {
|
|
6749
|
+
visitedInFile.add(diagnostic.resolvedId);
|
|
6750
|
+
modules.push({
|
|
6751
|
+
...diagnostic,
|
|
6752
|
+
...durations
|
|
6753
|
+
});
|
|
6754
|
+
}
|
|
6755
|
+
});
|
|
6756
|
+
const untracked = [];
|
|
6757
|
+
moduleDiagnostic.untracked.forEach((diagnostic) => {
|
|
6758
|
+
const durations = modulesById[diagnostic.resolvedId];
|
|
6759
|
+
if (!durations) return;
|
|
6760
|
+
if (visitedInFile.has(diagnostic.resolvedId)) untracked.push({
|
|
6761
|
+
selfTime: 0,
|
|
6762
|
+
totalTime: 0,
|
|
6763
|
+
transformTime: 0,
|
|
6764
|
+
external: durations.external,
|
|
6765
|
+
importer: durations.importer,
|
|
6766
|
+
resolvedId: diagnostic.resolvedId,
|
|
6767
|
+
resolvedUrl: diagnostic.resolvedUrl,
|
|
6768
|
+
url: diagnostic.rawUrl
|
|
6769
|
+
});
|
|
6770
|
+
else {
|
|
6771
|
+
visitedInFile.add(diagnostic.resolvedId);
|
|
6772
|
+
untracked.push({
|
|
6773
|
+
...durations,
|
|
6774
|
+
resolvedId: diagnostic.resolvedId,
|
|
6775
|
+
resolvedUrl: diagnostic.resolvedUrl,
|
|
6776
|
+
url: diagnostic.rawUrl
|
|
6777
|
+
});
|
|
6778
|
+
}
|
|
6779
|
+
});
|
|
6780
|
+
return {
|
|
6781
|
+
modules,
|
|
6782
|
+
untrackedModules: untracked
|
|
6783
|
+
};
|
|
6784
|
+
}
|
|
6785
|
+
function getModuleImporter(moduleId, durations, testModule) {
|
|
6786
|
+
if (durations.importer === moduleId) return moduleId;
|
|
6787
|
+
if (!durations.importer) {
|
|
6788
|
+
if (moduleId === testModule.moduleId) return testModule.moduleId;
|
|
6789
|
+
return testModule.project.config.setupFiles.includes(moduleId) ? moduleId : durations.importer;
|
|
6790
|
+
}
|
|
6791
|
+
return durations.importer;
|
|
6792
|
+
}
|
|
6793
|
+
// the idea of this is very simple
|
|
6794
|
+
// it parses the source code to extract import/export statements
|
|
6795
|
+
// it parses SSR transformed file to extract __vite_ssr_import__ and __vite_ssr_dynamic_import__
|
|
6796
|
+
// it combines the two by looking at the original positions of SSR primitives
|
|
6797
|
+
// in the end, we are able to return a list of modules that were imported by this module
|
|
6798
|
+
// mapped to their IDs in Vite's module graph
|
|
6799
|
+
async function collectSourceModulesLocations(moduleId, moduleGraph) {
|
|
6800
|
+
const transformResult = moduleGraph.getModuleById(moduleId)?.transformResult;
|
|
6801
|
+
if (!transformResult || !transformResult.ssr) return;
|
|
6802
|
+
const map = transformResult.map;
|
|
6803
|
+
if (!map || !("version" in map) || !map.sources.length) return;
|
|
6804
|
+
const sourceImports = map.sources.reduce((acc, sourceId, index) => {
|
|
6805
|
+
const source = map.sourcesContent?.[index];
|
|
6806
|
+
if (source != null) acc[sourceId] = parseSourceImportsAndExports(source);
|
|
6807
|
+
return acc;
|
|
6808
|
+
}, {});
|
|
6809
|
+
const transformImports = await parseTransformResult(moduleGraph, transformResult);
|
|
6810
|
+
const traceMap = map && "version" in map && new TraceMap(map);
|
|
6811
|
+
const modules = {};
|
|
6812
|
+
const untracked = [];
|
|
6813
|
+
transformImports.forEach((row) => {
|
|
6814
|
+
const original = traceMap && originalPositionFor(traceMap, row.start);
|
|
6815
|
+
if (original && original.source != null) {
|
|
6816
|
+
// if there are several at the same position, this is a bug
|
|
6817
|
+
// probably caused by import.meta.glob imports returning incorrect positions
|
|
6818
|
+
// all the new import.meta.glob imports come first, so only the last module on this line is correct
|
|
6819
|
+
const sourceImport = sourceImports[original.source].get(`${original.line}:${original.column}`);
|
|
6820
|
+
if (sourceImport) {
|
|
6821
|
+
if (modules[sourceImport.rawUrl]) {
|
|
6822
|
+
// remove imports with a different resolvedId
|
|
6823
|
+
const differentImports = modules[sourceImport.rawUrl].filter((d) => d.resolvedId !== row.resolvedId);
|
|
6824
|
+
untracked.push(...differentImports);
|
|
6825
|
+
modules[sourceImport.rawUrl] = modules[sourceImport.rawUrl].filter((d) => d.resolvedId === row.resolvedId);
|
|
6826
|
+
}
|
|
6827
|
+
modules[sourceImport.rawUrl] ??= [];
|
|
6828
|
+
modules[sourceImport.rawUrl].push({
|
|
6829
|
+
start: sourceImport.start,
|
|
6830
|
+
end: sourceImport.end,
|
|
6831
|
+
startIndex: sourceImport.startIndex,
|
|
6832
|
+
endIndex: sourceImport.endIndex,
|
|
6833
|
+
rawUrl: sourceImport.rawUrl,
|
|
6834
|
+
resolvedId: row.resolvedId,
|
|
6835
|
+
resolvedUrl: row.resolvedUrl
|
|
6836
|
+
});
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
});
|
|
6840
|
+
return {
|
|
6841
|
+
modules: Object.values(modules).flat(),
|
|
6842
|
+
untracked
|
|
6843
|
+
};
|
|
6844
|
+
}
|
|
6845
|
+
function fillSourcesMap(syntax, sourcesMap, source, indexMap) {
|
|
6846
|
+
const splitSeparator = `${syntax} `;
|
|
6847
|
+
const splitSources = source.split(splitSeparator);
|
|
6848
|
+
const chunks = [];
|
|
6849
|
+
let index = 0;
|
|
6850
|
+
for (const chunk of splitSources) {
|
|
6851
|
+
chunks.push({
|
|
6852
|
+
chunk,
|
|
6853
|
+
startIndex: index
|
|
6854
|
+
});
|
|
6855
|
+
index += chunk.length + splitSeparator.length;
|
|
6856
|
+
}
|
|
6857
|
+
chunks.forEach(({ chunk, startIndex }) => {
|
|
6858
|
+
const normalized = chunk.replace(/'/g, "\"");
|
|
6859
|
+
const startQuoteIdx = normalized.indexOf("\"");
|
|
6860
|
+
if (startQuoteIdx === -1) return;
|
|
6861
|
+
const endQuoteIdx = normalized.indexOf("\"", startQuoteIdx + 1);
|
|
6862
|
+
if (endQuoteIdx === -1) return;
|
|
6863
|
+
const staticSyntax = {
|
|
6864
|
+
startIndex: startIndex + startQuoteIdx,
|
|
6865
|
+
endIndex: startIndex + endQuoteIdx + 1,
|
|
6866
|
+
start: indexMap.get(startIndex + startQuoteIdx),
|
|
6867
|
+
end: indexMap.get(startIndex + endQuoteIdx + 1),
|
|
6868
|
+
rawUrl: normalized.slice(startQuoteIdx + 1, endQuoteIdx)
|
|
6869
|
+
};
|
|
6870
|
+
// -7 to include "import "
|
|
6871
|
+
for (let i = startIndex - 7; i < staticSyntax.endIndex; i++) {
|
|
6872
|
+
const location = indexMap.get(i);
|
|
6873
|
+
if (location) sourcesMap.set(`${location.line}:${location.column}`, staticSyntax);
|
|
6874
|
+
}
|
|
6875
|
+
});
|
|
6876
|
+
}
|
|
6877
|
+
// this function tries to parse ESM static import and export statements from
|
|
6878
|
+
// the source. if the source is not JS/TS, but supports static ESM syntax,
|
|
6879
|
+
// then this will also find them because it' only checks the strings, it doesn't parse the AST
|
|
6880
|
+
function parseSourceImportsAndExports(source) {
|
|
6881
|
+
if (!source.includes("import ") && !source.includes("export ")) return /* @__PURE__ */ new Map();
|
|
6882
|
+
const sourcesMap = /* @__PURE__ */ new Map();
|
|
6883
|
+
const indexMap = createIndexLocationsMap(source);
|
|
6884
|
+
fillSourcesMap("import", sourcesMap, source, indexMap);
|
|
6885
|
+
fillSourcesMap("export", sourcesMap, source, indexMap);
|
|
6886
|
+
return sourcesMap;
|
|
6887
|
+
}
|
|
6888
|
+
async function parseTransformResult(moduleGraph, transformResult) {
|
|
6889
|
+
const code = transformResult.code;
|
|
6890
|
+
const regexp = /(?:__vite_ssr_import__|__vite_ssr_dynamic_import__)\("([^"]+)"/g;
|
|
6891
|
+
const lineColumnMap = createIndexLocationsMap(code);
|
|
6892
|
+
const importPositions = [];
|
|
6893
|
+
let match;
|
|
6894
|
+
// eslint-disable-next-line no-cond-assign
|
|
6895
|
+
while (match = regexp.exec(code)) {
|
|
6896
|
+
const startIndex = match.index;
|
|
6897
|
+
const endIndex = match.index + match[0].length - 1;
|
|
6898
|
+
importPositions.push({
|
|
6899
|
+
raw: match[1],
|
|
6900
|
+
startIndex,
|
|
6901
|
+
endIndex
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6904
|
+
return (await Promise.all(importPositions.map(async ({ startIndex, endIndex, raw }) => {
|
|
6905
|
+
const position = lineColumnMap.get(startIndex);
|
|
6906
|
+
const endPosition = lineColumnMap.get(endIndex);
|
|
6907
|
+
const moduleNode = await moduleGraph.getModuleByUrl(raw);
|
|
6908
|
+
if (!position || !endPosition || !moduleNode || !moduleNode.id) return;
|
|
6909
|
+
return {
|
|
6910
|
+
resolvedId: moduleNode.id,
|
|
6911
|
+
resolvedUrl: moduleNode.url,
|
|
6912
|
+
start: position,
|
|
6913
|
+
end: endPosition,
|
|
6914
|
+
startIndex,
|
|
6915
|
+
endIndex
|
|
6916
|
+
};
|
|
6917
|
+
}))).filter((n) => n != null);
|
|
6918
|
+
}
|
|
6919
|
+
|
|
6661
6920
|
const __dirname$1 = url.fileURLToPath(new URL(".", import.meta.url));
|
|
6662
6921
|
class VitestPackageInstaller {
|
|
6663
6922
|
isPackageExists(name, options) {
|
|
@@ -7036,7 +7295,7 @@ function createMethodsRPC(project, methodsOptions = {}) {
|
|
|
7036
7295
|
const file = cleanUrl(resolved.id);
|
|
7037
7296
|
if (resolved.external) return {
|
|
7038
7297
|
file,
|
|
7039
|
-
url:
|
|
7298
|
+
url: isBuiltin(resolved.id) ? toBuiltin(resolved.id) : resolved.id,
|
|
7040
7299
|
id: resolved.id
|
|
7041
7300
|
};
|
|
7042
7301
|
return {
|
|
@@ -7179,7 +7438,6 @@ class PoolRunner {
|
|
|
7179
7438
|
__vitest_worker_request__: true,
|
|
7180
7439
|
type: method,
|
|
7181
7440
|
context,
|
|
7182
|
-
poolId: this.poolId,
|
|
7183
7441
|
otelCarrier: this.getOTELCarrier()
|
|
7184
7442
|
});
|
|
7185
7443
|
}
|
|
@@ -7187,7 +7445,7 @@ class PoolRunner {
|
|
|
7187
7445
|
const activeContext = this._otel?.currentContext || this._otel?.workerContext;
|
|
7188
7446
|
return activeContext ? this._traces.getContextCarrier(activeContext) : void 0;
|
|
7189
7447
|
}
|
|
7190
|
-
async start() {
|
|
7448
|
+
async start(options) {
|
|
7191
7449
|
// Wait for any ongoing operation to complete
|
|
7192
7450
|
if (this._operationLock) await this._operationLock;
|
|
7193
7451
|
if (this._state === RunnerState.STARTED || this._state === RunnerState.STARTING) return;
|
|
@@ -7211,6 +7469,8 @@ class PoolRunner {
|
|
|
7211
7469
|
const tracesSdk = projectConfig?.sdkPath ?? globalConfig?.sdkPath;
|
|
7212
7470
|
this.postMessage({
|
|
7213
7471
|
type: "start",
|
|
7472
|
+
poolId: this.poolId,
|
|
7473
|
+
workerId: options.workerId,
|
|
7214
7474
|
__vitest_worker_request__: true,
|
|
7215
7475
|
options: { reportMemory: this.worker.reportMemory ?? false },
|
|
7216
7476
|
context: {
|
|
@@ -7239,7 +7499,7 @@ class PoolRunner {
|
|
|
7239
7499
|
this._operationLock = null;
|
|
7240
7500
|
}
|
|
7241
7501
|
}
|
|
7242
|
-
async stop() {
|
|
7502
|
+
async stop(options) {
|
|
7243
7503
|
// Wait for any ongoing operation to complete
|
|
7244
7504
|
if (this._operationLock) await this._operationLock;
|
|
7245
7505
|
if (this._state === RunnerState.STOPPED || this._state === RunnerState.STOPPING) return;
|
|
@@ -7267,6 +7527,11 @@ class PoolRunner {
|
|
|
7267
7527
|
this.off("message", onStop);
|
|
7268
7528
|
}
|
|
7269
7529
|
};
|
|
7530
|
+
// Don't wait for graceful exit's response when force exiting
|
|
7531
|
+
if (options?.force) return onStop({
|
|
7532
|
+
type: "stopped",
|
|
7533
|
+
__vitest_worker_response__: true
|
|
7534
|
+
});
|
|
7270
7535
|
this.on("message", onStop);
|
|
7271
7536
|
this.postMessage({
|
|
7272
7537
|
type: "stop",
|
|
@@ -7716,6 +7981,8 @@ class Pool {
|
|
|
7716
7981
|
try {
|
|
7717
7982
|
let isMemoryLimitReached = false;
|
|
7718
7983
|
const runner = this.getPoolRunner(task, method);
|
|
7984
|
+
const poolId = runner.poolId ?? this.getWorkerId();
|
|
7985
|
+
runner.poolId = poolId;
|
|
7719
7986
|
const activeTask = {
|
|
7720
7987
|
task,
|
|
7721
7988
|
resolver,
|
|
@@ -7724,7 +7991,8 @@ class Pool {
|
|
|
7724
7991
|
};
|
|
7725
7992
|
this.activeTasks.push(activeTask);
|
|
7726
7993
|
// active tasks receive cancel signal and shut down gracefully
|
|
7727
|
-
async function cancelTask() {
|
|
7994
|
+
async function cancelTask(options) {
|
|
7995
|
+
if (options?.force) await runner.stop({ force: true });
|
|
7728
7996
|
await runner.waitForTerminated();
|
|
7729
7997
|
resolver.reject(/* @__PURE__ */ new Error("Cancelled"));
|
|
7730
7998
|
}
|
|
@@ -7742,10 +8010,8 @@ class Pool {
|
|
|
7742
8010
|
resolver.reject(new Error(`[vitest-pool]: Worker ${task.worker} emitted error.`, { cause: error }));
|
|
7743
8011
|
});
|
|
7744
8012
|
const id = setTimeout(() => resolver.reject(/* @__PURE__ */ new Error(`[vitest-pool]: Timeout starting ${task.worker} runner.`)), WORKER_START_TIMEOUT);
|
|
7745
|
-
await runner.start().finally(() => clearTimeout(id));
|
|
8013
|
+
await runner.start({ workerId: task.context.workerId }).finally(() => clearTimeout(id));
|
|
7746
8014
|
}
|
|
7747
|
-
const poolId = runner.poolId ?? this.getWorkerId();
|
|
7748
|
-
runner.poolId = poolId;
|
|
7749
8015
|
const span = runner.startTracesSpan(`vitest.worker.${method}`);
|
|
7750
8016
|
// Start running the test in the worker
|
|
7751
8017
|
runner.request(method, task.context);
|
|
@@ -7776,6 +8042,9 @@ catch (error) {
|
|
|
7776
8042
|
return this.schedule();
|
|
7777
8043
|
}
|
|
7778
8044
|
async cancel() {
|
|
8045
|
+
// Force exit if previous cancel is still on-going
|
|
8046
|
+
// for example when user does 'CTRL+c' twice in row
|
|
8047
|
+
const force = this._isCancelling;
|
|
7779
8048
|
// Set flag to prevent new tasks from being queued
|
|
7780
8049
|
this._isCancelling = true;
|
|
7781
8050
|
const pendingTasks = this.queue.splice(0);
|
|
@@ -7783,11 +8052,12 @@ catch (error) {
|
|
|
7783
8052
|
const error = /* @__PURE__ */ new Error("Cancelled");
|
|
7784
8053
|
pendingTasks.forEach((task) => task.resolver.reject(error));
|
|
7785
8054
|
}
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
await Promise.all(this.exitPromises
|
|
8055
|
+
await Promise.all(this.activeTasks.map((task) => task.cancelTask({ force })));
|
|
8056
|
+
this.activeTasks = [];
|
|
8057
|
+
await Promise.all(this.sharedRunners.map((runner) => runner.stop()));
|
|
8058
|
+
this.sharedRunners = [];
|
|
8059
|
+
await Promise.all(this.exitPromises);
|
|
8060
|
+
this.exitPromises = [];
|
|
7791
8061
|
this.workerIds.forEach((_, id) => this.freeWorkerId(id));
|
|
7792
8062
|
// Reset flag after cancellation completes
|
|
7793
8063
|
this._isCancelling = false;
|
|
@@ -8191,7 +8461,10 @@ function serializeConfig(project) {
|
|
|
8191
8461
|
printConsoleTrace: config.printConsoleTrace ?? globalConfig.printConsoleTrace,
|
|
8192
8462
|
benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples },
|
|
8193
8463
|
serializedDefines: config.browser.enabled ? "" : project._serializedDefines || "",
|
|
8194
|
-
experimental: {
|
|
8464
|
+
experimental: {
|
|
8465
|
+
fsModuleCache: config.experimental.fsModuleCache ?? false,
|
|
8466
|
+
printImportBreakdown: config.experimental.printImportBreakdown
|
|
8467
|
+
}
|
|
8195
8468
|
};
|
|
8196
8469
|
}
|
|
8197
8470
|
|
|
@@ -9111,7 +9384,10 @@ function ModuleRunnerTransform() {
|
|
|
9111
9384
|
environment.resolve.noExternal = true;
|
|
9112
9385
|
// Workaround `noExternal` merging bug on Vite 6
|
|
9113
9386
|
// https://github.com/vitejs/vite/pull/20502
|
|
9114
|
-
if (name === "ssr")
|
|
9387
|
+
if (name === "ssr") {
|
|
9388
|
+
delete config.ssr?.noExternal;
|
|
9389
|
+
delete config.ssr?.external;
|
|
9390
|
+
}
|
|
9115
9391
|
if (name === "__vitest_vm__" || name === "__vitest__") continue;
|
|
9116
9392
|
const currentOptimizeDeps = environment.optimizeDeps || (name === "client" ? config.optimizeDeps : name === "ssr" ? config.ssr?.optimizeDeps : void 0);
|
|
9117
9393
|
const optimizeDeps = resolveOptimizerConfig(testConfig.deps?.optimizer?.[name], currentOptimizeDeps);
|
|
@@ -9341,6 +9617,7 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
9341
9617
|
|
|
9342
9618
|
class VitestResolver {
|
|
9343
9619
|
options;
|
|
9620
|
+
externalizeConcurrentCache = /* @__PURE__ */ new Map();
|
|
9344
9621
|
externalizeCache = /* @__PURE__ */ new Map();
|
|
9345
9622
|
constructor(cacheDir, config) {
|
|
9346
9623
|
// sorting to make cache consistent
|
|
@@ -9360,8 +9637,20 @@ class VitestResolver {
|
|
|
9360
9637
|
external
|
|
9361
9638
|
};
|
|
9362
9639
|
}
|
|
9363
|
-
|
|
9364
|
-
|
|
9640
|
+
wasExternalized(file) {
|
|
9641
|
+
const normalizedFile = normalizeId(file);
|
|
9642
|
+
if (!this.externalizeCache.has(normalizedFile)) return false;
|
|
9643
|
+
return this.externalizeCache.get(normalizedFile) ?? false;
|
|
9644
|
+
}
|
|
9645
|
+
async shouldExternalize(file) {
|
|
9646
|
+
const normalizedFile = normalizeId(file);
|
|
9647
|
+
if (this.externalizeCache.has(normalizedFile)) return this.externalizeCache.get(normalizedFile);
|
|
9648
|
+
return shouldExternalize(normalizeId(file), this.options, this.externalizeConcurrentCache).then((result) => {
|
|
9649
|
+
this.externalizeCache.set(normalizedFile, result);
|
|
9650
|
+
return result;
|
|
9651
|
+
}).finally(() => {
|
|
9652
|
+
this.externalizeConcurrentCache.delete(normalizedFile);
|
|
9653
|
+
});
|
|
9365
9654
|
}
|
|
9366
9655
|
}
|
|
9367
9656
|
function normalizeId(id) {
|
|
@@ -9406,6 +9695,9 @@ function guessCJSversion(id) {
|
|
|
9406
9695
|
}
|
|
9407
9696
|
// The code from https://github.com/unjs/mlly/blob/c5bcca0cda175921344fd6de1bc0c499e73e5dac/src/syntax.ts#L51-L98
|
|
9408
9697
|
async function isValidNodeImport(id) {
|
|
9698
|
+
// clean url to strip off `?v=...` query etc.
|
|
9699
|
+
// node can natively import files with query params, so externalizing them is safe.
|
|
9700
|
+
id = cleanUrl(id);
|
|
9409
9701
|
const extension = extname(id);
|
|
9410
9702
|
if (BUILTIN_EXTENSIONS.has(extension)) return true;
|
|
9411
9703
|
if (extension !== ".js") return false;
|
|
@@ -9426,7 +9718,7 @@ async function shouldExternalize(id, options, cache) {
|
|
|
9426
9718
|
return cache.get(id);
|
|
9427
9719
|
}
|
|
9428
9720
|
async function _shouldExternalize(id, options) {
|
|
9429
|
-
if (isBuiltin(id)) return id;
|
|
9721
|
+
if (isBuiltin$1(id)) return id;
|
|
9430
9722
|
// data: should be processed by native import,
|
|
9431
9723
|
// since it is a feature of ESM.
|
|
9432
9724
|
// also externalize network imports since nodejs allows it when --experimental-network-imports
|
|
@@ -9443,7 +9735,6 @@ async function _shouldExternalize(id, options) {
|
|
|
9443
9735
|
if (matchPattern(id, moduleDirectories, defaultInline)) return false;
|
|
9444
9736
|
if (matchPattern(id, moduleDirectories, depsExternal)) return id;
|
|
9445
9737
|
if (isLibraryModule && await isValidNodeImport(id)) return id;
|
|
9446
|
-
return false;
|
|
9447
9738
|
}
|
|
9448
9739
|
function matchPattern(id, moduleDirectories, patterns) {
|
|
9449
9740
|
if (patterns == null) return false;
|
|
@@ -10674,6 +10965,12 @@ class TestSuite extends SuiteImplementation {
|
|
|
10674
10965
|
class TestModule extends SuiteImplementation {
|
|
10675
10966
|
type = "module";
|
|
10676
10967
|
/**
|
|
10968
|
+
* The Vite environment that processes files on the server.
|
|
10969
|
+
*
|
|
10970
|
+
* Can be empty if test module did not run yet.
|
|
10971
|
+
*/
|
|
10972
|
+
viteEnvironment;
|
|
10973
|
+
/**
|
|
10677
10974
|
* This is usually an absolute UNIX file path.
|
|
10678
10975
|
* It can be a virtual ID if the file is not on the disk.
|
|
10679
10976
|
* This value corresponds to the ID in the Vite's module graph.
|
|
@@ -10688,6 +10985,8 @@ class TestModule extends SuiteImplementation {
|
|
|
10688
10985
|
super(task, project);
|
|
10689
10986
|
this.moduleId = task.filepath;
|
|
10690
10987
|
this.relativeModuleId = task.name;
|
|
10988
|
+
if (task.viteEnvironment === "__browser__") this.viteEnvironment = project.browser?.vite.environments.client;
|
|
10989
|
+
else if (typeof task.viteEnvironment === "string") this.viteEnvironment = project.vite.environments[task.viteEnvironment];
|
|
10691
10990
|
}
|
|
10692
10991
|
/**
|
|
10693
10992
|
* Checks the running state of the test file.
|
|
@@ -12240,6 +12539,39 @@ class Vitest {
|
|
|
12240
12539
|
return await this.runningPromise;
|
|
12241
12540
|
});
|
|
12242
12541
|
}
|
|
12542
|
+
/**
|
|
12543
|
+
* Returns module's diagnostic. If `testModule` is not provided, `selfTime` and `totalTime` will be aggregated across all tests.
|
|
12544
|
+
*
|
|
12545
|
+
* If the module was not transformed or executed, the diagnostic will be empty.
|
|
12546
|
+
* @experimental
|
|
12547
|
+
* @see {@link https://vitest.dev/api/advanced/vitest#getsourcemodulediagnostic}
|
|
12548
|
+
*/
|
|
12549
|
+
async experimental_getSourceModuleDiagnostic(moduleId, testModule) {
|
|
12550
|
+
if (testModule) {
|
|
12551
|
+
const viteEnvironment = testModule.viteEnvironment;
|
|
12552
|
+
// if there is no viteEnvironment, it means the file did not run yet
|
|
12553
|
+
if (!viteEnvironment) return {
|
|
12554
|
+
modules: [],
|
|
12555
|
+
untrackedModules: []
|
|
12556
|
+
};
|
|
12557
|
+
const moduleLocations = await collectSourceModulesLocations(moduleId, viteEnvironment.moduleGraph);
|
|
12558
|
+
return collectModuleDurationsDiagnostic(moduleId, this.state, moduleLocations, testModule);
|
|
12559
|
+
}
|
|
12560
|
+
const environments = this.projects.flatMap((p) => {
|
|
12561
|
+
return Object.values(p.vite.environments);
|
|
12562
|
+
});
|
|
12563
|
+
const aggregatedLocationsResult = await Promise.all(environments.map((environment) => collectSourceModulesLocations(moduleId, environment.moduleGraph)));
|
|
12564
|
+
return collectModuleDurationsDiagnostic(moduleId, this.state, aggregatedLocationsResult.reduce((acc, locations) => {
|
|
12565
|
+
if (locations) {
|
|
12566
|
+
acc.modules.push(...locations.modules);
|
|
12567
|
+
acc.untracked.push(...locations.untracked);
|
|
12568
|
+
}
|
|
12569
|
+
return acc;
|
|
12570
|
+
}, {
|
|
12571
|
+
modules: [],
|
|
12572
|
+
untracked: []
|
|
12573
|
+
}));
|
|
12574
|
+
}
|
|
12243
12575
|
async experimental_parseSpecifications(specifications, options) {
|
|
12244
12576
|
if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecifications\` does not support "${this.mode}" mode.`);
|
|
12245
12577
|
const limit = limitConcurrency(options?.concurrency ?? (typeof nodeos__default.availableParallelism === "function" ? nodeos__default.availableParallelism() : nodeos__default.cpus().length));
|