vitest 4.0.9 → 4.0.11
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 +4 -3
- package/dist/browser.js +1 -1
- package/dist/chunks/_commonjsHelpers.D26ty3Ew.js +6 -0
- package/dist/chunks/{base.CiIV2DDC.js → base.DiCUKpyF.js} +47 -40
- package/dist/chunks/{browser.d.DnU_kh8a.d.ts → browser.d.D-d8eZY4.d.ts} +1 -1
- package/dist/chunks/{cac.B_NTJoIH.js → cac.aVhqBj0-.js} +21 -7
- package/dist/chunks/{cli-api.D48wY175.js → cli-api.-bIZD4XU.js} +960 -345
- package/dist/chunks/{coverage.BUlIqJrL.js → coverage.CtyeYmKM.js} +7 -1
- package/dist/chunks/{creator.BzqvXeRE.js → creator.DAmOKTvJ.js} +5 -5
- package/dist/chunks/{global.d.BQDgW9Pr.d.ts → global.d.uY4Q0M5z.d.ts} +1 -1
- package/dist/chunks/{globals.DBrtKPdh.js → globals.C0izxiX3.js} +3 -3
- package/dist/chunks/{index.op2Re5rn.js → index.CMvpbrsJ.js} +1 -1
- package/dist/chunks/{index.CPA8jGhR.js → index.CQwQ_SLL.js} +16 -4
- package/dist/chunks/{index.z7NPOg2E.js → index.D4KonVSU.js} +1 -1
- package/dist/chunks/{index.CGezRSGU.js → index.DBx1AtPJ.js} +3 -2
- package/dist/chunks/{index.kotH7DY7.js → index.DWDW6mLz.js} +15 -4
- package/dist/chunks/{index.BfmpdV5p.js → index.QWbK7rHY.js} +3 -3
- package/dist/chunks/init-forks.DIuGPyId.js +32 -0
- package/dist/chunks/{init-threads.C7T0-YMD.js → init-threads.jC_8JdoN.js} +3 -3
- package/dist/chunks/{init.BQhNfT0h.js → init.B3IeC_yW.js} +60 -22
- package/dist/chunks/{moduleRunner.d.BxT-OjLR.d.ts → moduleRunner.d.B5SW5pMI.d.ts} +9 -1
- package/dist/chunks/plugin.d.N8khPRFb.d.ts +38 -0
- package/dist/chunks/{reporters.d.BQ0wpUaj.d.ts → reporters.d.DgZLBdyd.d.ts} +109 -48
- package/dist/chunks/{setup-common.Dw1XgX0v.js → setup-common.DGHc_BUK.js} +2 -2
- package/dist/chunks/{startModuleRunner.DLjmA_wU.js → startModuleRunner.DaBMy1JT.js} +97 -49
- package/dist/chunks/{test.w5HLbjmU.js → test.DqQZzsWf.js} +15 -5
- package/dist/chunks/traces.BVPrsYso.js +151 -0
- package/dist/chunks/{config.d.BTfZNUu9.d.ts → traces.d.B8ukBJqA.d.ts} +36 -1
- package/dist/chunks/{vi.CyIUVSoU.js → vi.BiaV1qII.js} +2 -2
- package/dist/chunks/{vm.DXN8eCh2.js → vm.BKyGp1KW.js} +12 -8
- package/dist/chunks/{worker.d.ZGohxCEd.d.ts → worker.d.B_PZTrCQ.d.ts} +5 -4
- package/dist/cli.js +2 -2
- package/dist/config.d.ts +8 -7
- package/dist/coverage.d.ts +6 -5
- package/dist/coverage.js +2 -2
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +14 -12
- package/dist/index.js +4 -4
- package/dist/module-evaluator.d.ts +7 -6
- package/dist/module-evaluator.js +14 -1
- package/dist/module-runner.js +2 -1
- package/dist/node.d.ts +11 -10
- package/dist/node.js +12 -11
- package/dist/reporters.d.ts +6 -5
- package/dist/reporters.js +2 -2
- package/dist/runners.d.ts +5 -1
- package/dist/runners.js +3 -3
- package/dist/worker.d.ts +8 -7
- package/dist/worker.js +11 -10
- package/dist/workers/forks.js +12 -12
- package/dist/workers/runVmTests.js +24 -22
- package/dist/workers/threads.js +12 -11
- package/dist/workers/vmForks.js +6 -6
- package/dist/workers/vmThreads.js +6 -5
- package/package.json +19 -14
- package/dist/chunks/_commonjsHelpers.BFTU3MAI.js +0 -7
- package/dist/chunks/init-forks.aqTzCSR2.js +0 -65
- package/dist/chunks/plugin.d.DevON6kQ.d.ts +0 -9
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import fs, { promises
|
|
1
|
+
import fs, { promises, existsSync, mkdirSync, readFileSync, statSync, readdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { relative, resolve, dirname, join, extname, normalize, basename, isAbsolute } from 'pathe';
|
|
3
3
|
import { C as CoverageProviderMap } from './coverage.D_JHT54q.js';
|
|
4
4
|
import path, { resolve as resolve$1 } from 'node:path';
|
|
5
|
-
import { noop, createDefer, slash, isExternalUrl, unwrapId,
|
|
6
|
-
import { a as any, p as prompt } from './index.
|
|
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.
|
|
5
|
+
import { noop, createDefer, slash, isExternalUrl, unwrapId, withTrailingSlash, cleanUrl, wrapId, toArray, deepMerge, nanoid, deepClone, isPrimitive, notNullish } from '@vitest/utils/helpers';
|
|
6
|
+
import { a as any, p as prompt } from './index.D4KonVSU.js';
|
|
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, fetchModule, version,
|
|
9
|
+
import { 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
13
|
import { generateHash as generateHash$1, 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.aVhqBj0-.js';
|
|
16
|
+
import { performance as performance$1 } from 'node:perf_hooks';
|
|
16
17
|
import { c as createBirpc } from './index.0kCJoeWi.js';
|
|
17
|
-
import { p as parse, d as stringify, e as TraceMap, o as originalPositionFor, h as ancestor, i as printError, f as formatProjectName, w as withLabel, j as errorBanner, k as divider, l as Typechecker, m as generateCodeFrame, n as createDefinesScript, R as ReportersMap, B as BlobReporter, r as readBlobs, q as convertTasksToEvents, H as HangingProcessReporter } from './index.
|
|
18
|
+
import { p as parse, d as stringify, e as TraceMap, o as originalPositionFor, h as ancestor, i as printError, f as formatProjectName, w as withLabel, j as errorBanner, k as divider, l as Typechecker, m as generateCodeFrame, n as createDefinesScript, R as ReportersMap, B as BlobReporter, r as readBlobs, q as convertTasksToEvents, H as HangingProcessReporter } from './index.DWDW6mLz.js';
|
|
18
19
|
import require$$0$3 from 'events';
|
|
19
20
|
import require$$1$1 from 'https';
|
|
20
21
|
import require$$2 from 'http';
|
|
@@ -25,15 +26,16 @@ import require$$0$2 from 'stream';
|
|
|
25
26
|
import require$$7 from 'url';
|
|
26
27
|
import require$$0 from 'zlib';
|
|
27
28
|
import require$$0$1 from 'buffer';
|
|
28
|
-
import { g as getDefaultExportFromCjs } from './_commonjsHelpers.
|
|
29
|
+
import { g as getDefaultExportFromCjs } from './_commonjsHelpers.D26ty3Ew.js';
|
|
29
30
|
import crypto, { createHash } from 'node:crypto';
|
|
30
31
|
import { rootDir, distDir } from '../path.js';
|
|
32
|
+
import { T as Traces } from './traces.BVPrsYso.js';
|
|
31
33
|
import createDebug from 'debug';
|
|
32
|
-
import { readFile, writeFile, rename, stat, unlink,
|
|
34
|
+
import { rm, readFile, writeFile, rename, stat, unlink, mkdir, copyFile } from 'node:fs/promises';
|
|
35
|
+
import c from 'tinyrainbow';
|
|
33
36
|
import { VitestModuleEvaluator } from '#module-evaluator';
|
|
34
37
|
import { ModuleRunner } from 'vite/module-runner';
|
|
35
38
|
import { Console } from 'node:console';
|
|
36
|
-
import c from 'tinyrainbow';
|
|
37
39
|
import { highlight } from '@vitest/utils/highlight';
|
|
38
40
|
import { createRequire, isBuiltin, builtinModules } from 'node:module';
|
|
39
41
|
import url, { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -41,7 +43,6 @@ import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
|
|
|
41
43
|
import { isatty } from 'node:tty';
|
|
42
44
|
import EventEmitter$1, { EventEmitter } from 'node:events';
|
|
43
45
|
import { fork } from 'node:child_process';
|
|
44
|
-
import v8 from 'node:v8';
|
|
45
46
|
import { Worker } from 'node:worker_threads';
|
|
46
47
|
import pm from 'picomatch';
|
|
47
48
|
import { glob, isDynamicPattern } from 'tinyglobby';
|
|
@@ -51,7 +52,7 @@ import { c as configDefaults } from './defaults.BOqNVLsY.js';
|
|
|
51
52
|
import { KNOWN_ASSET_RE } from '@vitest/utils/constants';
|
|
52
53
|
import { findNearestPackageData } from '@vitest/utils/resolver';
|
|
53
54
|
import * as esModuleLexer from 'es-module-lexer';
|
|
54
|
-
import { a as BenchmarkReportsMap } from './index.
|
|
55
|
+
import { a as BenchmarkReportsMap } from './index.CMvpbrsJ.js';
|
|
55
56
|
import assert$1 from 'node:assert';
|
|
56
57
|
import { serializeValue } from '@vitest/utils/serialize';
|
|
57
58
|
import { parseErrorStacktrace } from '@vitest/utils/source-map';
|
|
@@ -5124,11 +5125,11 @@ function setup(ctx, _server) {
|
|
|
5124
5125
|
},
|
|
5125
5126
|
async readTestFile(id) {
|
|
5126
5127
|
if (!ctx.state.filesMap.has(id) || !existsSync(id)) return null;
|
|
5127
|
-
return promises
|
|
5128
|
+
return promises.readFile(id, "utf-8");
|
|
5128
5129
|
},
|
|
5129
5130
|
async saveTestFile(id, content) {
|
|
5130
5131
|
if (!ctx.state.filesMap.has(id) || !existsSync(id)) throw new Error(`Test file "${id}" was not registered, so it cannot be updated using the API.`);
|
|
5131
|
-
return promises
|
|
5132
|
+
return promises.writeFile(id, content, "utf-8");
|
|
5132
5133
|
},
|
|
5133
5134
|
async rerun(files, resetTestNamePattern) {
|
|
5134
5135
|
await ctx.rerunFiles(files, void 0, true, resetTestNamePattern);
|
|
@@ -5150,7 +5151,7 @@ function setup(ctx, _server) {
|
|
|
5150
5151
|
const result = browser ? await project.browser.vite.transformRequest(id) : await project.vite.transformRequest(id);
|
|
5151
5152
|
if (result) {
|
|
5152
5153
|
try {
|
|
5153
|
-
result.source = result.source || await promises
|
|
5154
|
+
result.source = result.source || await promises.readFile(id, "utf-8");
|
|
5154
5155
|
} catch {}
|
|
5155
5156
|
return result;
|
|
5156
5157
|
}
|
|
@@ -5198,6 +5199,8 @@ function setup(ctx, _server) {
|
|
|
5198
5199
|
ctx.reporters.push(new WebSocketReporter(ctx, wss, clients));
|
|
5199
5200
|
}
|
|
5200
5201
|
class WebSocketReporter {
|
|
5202
|
+
start = 0;
|
|
5203
|
+
end = 0;
|
|
5201
5204
|
constructor(ctx, wss, clients) {
|
|
5202
5205
|
this.ctx = ctx;
|
|
5203
5206
|
this.wss = wss;
|
|
@@ -5211,6 +5214,7 @@ class WebSocketReporter {
|
|
|
5211
5214
|
}
|
|
5212
5215
|
onTestRunStart(specifications) {
|
|
5213
5216
|
if (this.clients.size === 0) return;
|
|
5217
|
+
this.start = performance$1.now();
|
|
5214
5218
|
const serializedSpecs = specifications.map((spec) => spec.toJSON());
|
|
5215
5219
|
this.clients.forEach((client) => {
|
|
5216
5220
|
client.onSpecsCollected?.(serializedSpecs)?.catch?.(noop);
|
|
@@ -5222,18 +5226,33 @@ class WebSocketReporter {
|
|
|
5222
5226
|
client.onTestAnnotate?.(testCase.id, annotation)?.catch?.(noop);
|
|
5223
5227
|
});
|
|
5224
5228
|
}
|
|
5229
|
+
async onTestCaseArtifactRecord(testCase, artifact) {
|
|
5230
|
+
if (this.clients.size === 0) return;
|
|
5231
|
+
this.clients.forEach((client) => {
|
|
5232
|
+
client.onTestArtifactRecord?.(testCase.id, artifact)?.catch?.(noop);
|
|
5233
|
+
});
|
|
5234
|
+
}
|
|
5225
5235
|
async onTaskUpdate(packs, events) {
|
|
5226
5236
|
if (this.clients.size === 0) return;
|
|
5227
5237
|
this.clients.forEach((client) => {
|
|
5228
5238
|
client.onTaskUpdate?.(packs, events)?.catch?.(noop);
|
|
5229
5239
|
});
|
|
5230
5240
|
}
|
|
5241
|
+
sum(items, cb) {
|
|
5242
|
+
return items.reduce((total, next) => {
|
|
5243
|
+
return total + Math.max(cb(next) || 0, 0);
|
|
5244
|
+
}, 0);
|
|
5245
|
+
}
|
|
5231
5246
|
onTestRunEnd(testModules, unhandledErrors) {
|
|
5232
5247
|
if (!this.clients.size) return;
|
|
5233
5248
|
const files = testModules.map((testModule) => testModule.task);
|
|
5234
5249
|
const errors = [...unhandledErrors];
|
|
5250
|
+
this.end = performance$1.now();
|
|
5251
|
+
const blobs = this.ctx.state.blobs;
|
|
5252
|
+
// Execution time is either sum of all runs of `--merge-reports` or the current run's time
|
|
5253
|
+
const executionTime = blobs?.executionTimes ? this.sum(blobs.executionTimes, (time) => time) : this.end - this.start;
|
|
5235
5254
|
this.clients.forEach((client) => {
|
|
5236
|
-
client.onFinished?.(files, errors)?.catch?.(noop);
|
|
5255
|
+
client.onFinished?.(files, errors, void 0, executionTime)?.catch?.(noop);
|
|
5237
5256
|
});
|
|
5238
5257
|
}
|
|
5239
5258
|
onFinishedReportCoverage() {
|
|
@@ -5488,7 +5507,8 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5488
5507
|
dynamic: definition.dynamic,
|
|
5489
5508
|
meta: {},
|
|
5490
5509
|
timeout: 0,
|
|
5491
|
-
annotations: []
|
|
5510
|
+
annotations: [],
|
|
5511
|
+
artifacts: []
|
|
5492
5512
|
};
|
|
5493
5513
|
definition.task = task;
|
|
5494
5514
|
latestSuite.tasks.push(task);
|
|
@@ -5633,8 +5653,9 @@ class ResultsCache {
|
|
|
5633
5653
|
cachePath = null;
|
|
5634
5654
|
version;
|
|
5635
5655
|
root = "/";
|
|
5636
|
-
constructor(
|
|
5637
|
-
this.
|
|
5656
|
+
constructor(logger) {
|
|
5657
|
+
this.logger = logger;
|
|
5658
|
+
this.version = Vitest.version;
|
|
5638
5659
|
}
|
|
5639
5660
|
getCachePath() {
|
|
5640
5661
|
return this.cachePath;
|
|
@@ -5646,6 +5667,15 @@ class ResultsCache {
|
|
|
5646
5667
|
getResults(key) {
|
|
5647
5668
|
return this.cache.get(key);
|
|
5648
5669
|
}
|
|
5670
|
+
async clearCache() {
|
|
5671
|
+
if (this.cachePath && existsSync(this.cachePath)) {
|
|
5672
|
+
await rm(this.cachePath, {
|
|
5673
|
+
force: true,
|
|
5674
|
+
recursive: true
|
|
5675
|
+
});
|
|
5676
|
+
this.logger.log("[cache] cleared results cache at", this.cachePath);
|
|
5677
|
+
}
|
|
5678
|
+
}
|
|
5649
5679
|
async readFromCache() {
|
|
5650
5680
|
if (!this.cachePath) return;
|
|
5651
5681
|
if (!fs.existsSync(this.cachePath)) return;
|
|
@@ -5698,8 +5728,8 @@ class ResultsCache {
|
|
|
5698
5728
|
class VitestCache {
|
|
5699
5729
|
results;
|
|
5700
5730
|
stats = new FilesStatsCache();
|
|
5701
|
-
constructor(
|
|
5702
|
-
this.results = new ResultsCache(
|
|
5731
|
+
constructor(logger) {
|
|
5732
|
+
this.results = new ResultsCache(logger);
|
|
5703
5733
|
}
|
|
5704
5734
|
getFileTestResults(key) {
|
|
5705
5735
|
return this.results.getResults(key);
|
|
@@ -5712,93 +5742,526 @@ class VitestCache {
|
|
|
5712
5742
|
}
|
|
5713
5743
|
}
|
|
5714
5744
|
|
|
5715
|
-
const
|
|
5716
|
-
const
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5745
|
+
const debugFs = createDebugger("vitest:cache:fs");
|
|
5746
|
+
const debugMemory = createDebugger("vitest:cache:memory");
|
|
5747
|
+
const cacheComment = "\n//# vitestCache=";
|
|
5748
|
+
const cacheCommentLength = 17;
|
|
5749
|
+
const METADATA_FILE = "_metadata.json";
|
|
5750
|
+
/**
|
|
5751
|
+
* @experimental
|
|
5752
|
+
*/
|
|
5753
|
+
class FileSystemModuleCache {
|
|
5754
|
+
/**
|
|
5755
|
+
* Even though it's possible to override the folder of project's caches
|
|
5756
|
+
* We still keep a single metadata file for all projects because
|
|
5757
|
+
* - they can reference files between each other
|
|
5758
|
+
* - lockfile changes are reflected for the whole workspace, not just for a single project
|
|
5759
|
+
*/
|
|
5760
|
+
rootCache;
|
|
5761
|
+
metadataFilePath;
|
|
5762
|
+
version = "1.0.0-beta.1";
|
|
5763
|
+
fsCacheRoots = /* @__PURE__ */ new WeakMap();
|
|
5764
|
+
fsEnvironmentHashMap = /* @__PURE__ */ new WeakMap();
|
|
5765
|
+
fsCacheKeyGenerators = /* @__PURE__ */ new Set();
|
|
5766
|
+
// this exists only to avoid the perf. cost of reading a file and generating a hash again
|
|
5767
|
+
// surprisingly, on some machines this has negligible effect
|
|
5768
|
+
fsCacheKeys = /* @__PURE__ */ new WeakMap();
|
|
5769
|
+
constructor(vitest) {
|
|
5770
|
+
this.vitest = vitest;
|
|
5771
|
+
const workspaceRoot = searchForWorkspaceRoot(vitest.vite.config.root);
|
|
5772
|
+
this.rootCache = vitest.config.experimental.fsModuleCachePath || join(workspaceRoot, "node_modules", ".experimental-vitest-cache");
|
|
5773
|
+
this.metadataFilePath = join(this.rootCache, METADATA_FILE);
|
|
5774
|
+
}
|
|
5775
|
+
defineCacheKeyGenerator(callback) {
|
|
5776
|
+
this.fsCacheKeyGenerators.add(callback);
|
|
5777
|
+
}
|
|
5778
|
+
async clearCache(log = true) {
|
|
5779
|
+
const fsCachePaths = this.vitest.projects.map((r) => {
|
|
5780
|
+
return r.config.experimental.fsModuleCachePath || this.rootCache;
|
|
5781
|
+
});
|
|
5782
|
+
const uniquePaths = Array.from(new Set(fsCachePaths));
|
|
5783
|
+
await Promise.all(uniquePaths.map((directory) => rm(directory, {
|
|
5784
|
+
force: true,
|
|
5785
|
+
recursive: true
|
|
5786
|
+
})));
|
|
5787
|
+
if (log) this.vitest.logger.log(`[cache] cleared fs module cache at ${uniquePaths.join(", ")}`);
|
|
5788
|
+
}
|
|
5789
|
+
async getCachedModule(cachedFilePath) {
|
|
5790
|
+
if (!existsSync(cachedFilePath)) {
|
|
5791
|
+
debugFs?.(`${c.red("[empty]")} ${cachedFilePath} doesn't exist, transforming by vite instead`);
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
const code = await readFile(cachedFilePath, "utf-8");
|
|
5795
|
+
const matchIndex = code.lastIndexOf(cacheComment);
|
|
5796
|
+
if (matchIndex === -1) {
|
|
5797
|
+
debugFs?.(`${c.red("[empty]")} ${cachedFilePath} exists, but doesn't have a ${cacheComment} comment, transforming by vite instead`);
|
|
5798
|
+
return;
|
|
5799
|
+
}
|
|
5800
|
+
const meta = this.fromBase64(code.slice(matchIndex + cacheCommentLength));
|
|
5801
|
+
if (meta.externalize) {
|
|
5802
|
+
debugFs?.(`${c.green("[read]")} ${meta.externalize} is externalized inside ${cachedFilePath}`);
|
|
5803
|
+
return {
|
|
5804
|
+
externalize: meta.externalize,
|
|
5805
|
+
type: meta.type
|
|
5806
|
+
};
|
|
5807
|
+
}
|
|
5808
|
+
debugFs?.(`${c.green("[read]")} ${meta.id} is cached in ${cachedFilePath}`);
|
|
5728
5809
|
return {
|
|
5729
|
-
|
|
5730
|
-
|
|
5810
|
+
id: meta.id,
|
|
5811
|
+
url: meta.url,
|
|
5812
|
+
file: meta.file,
|
|
5813
|
+
code,
|
|
5814
|
+
importers: meta.importers,
|
|
5815
|
+
mappings: meta.mappings
|
|
5731
5816
|
};
|
|
5817
|
+
}
|
|
5818
|
+
async saveCachedModule(cachedFilePath, fetchResult, importers = [], mappings = false) {
|
|
5819
|
+
if ("externalize" in fetchResult) {
|
|
5820
|
+
debugFs?.(`${c.yellow("[write]")} ${fetchResult.externalize} is externalized inside ${cachedFilePath}`);
|
|
5821
|
+
await atomicWriteFile(cachedFilePath, `${cacheComment}${this.toBase64(fetchResult)}`);
|
|
5822
|
+
} else if ("code" in fetchResult) {
|
|
5823
|
+
const result = {
|
|
5824
|
+
file: fetchResult.file,
|
|
5825
|
+
id: fetchResult.id,
|
|
5826
|
+
url: fetchResult.url,
|
|
5827
|
+
importers,
|
|
5828
|
+
mappings
|
|
5829
|
+
};
|
|
5830
|
+
debugFs?.(`${c.yellow("[write]")} ${fetchResult.id} is cached in ${cachedFilePath}`);
|
|
5831
|
+
await atomicWriteFile(cachedFilePath, `${fetchResult.code}${cacheComment}${this.toBase64(result)}`);
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
toBase64(obj) {
|
|
5835
|
+
const json = stringify(obj);
|
|
5836
|
+
return Buffer.from(json).toString("base64");
|
|
5837
|
+
}
|
|
5838
|
+
fromBase64(obj) {
|
|
5839
|
+
return parse(Buffer.from(obj, "base64").toString("utf-8"));
|
|
5840
|
+
}
|
|
5841
|
+
invalidateCachePath(environment, id) {
|
|
5842
|
+
debugFs?.(`cache for ${id} in ${environment.name} environment is invalidated`);
|
|
5843
|
+
this.fsCacheKeys.get(environment)?.delete(id);
|
|
5844
|
+
}
|
|
5845
|
+
invalidateAllCachePaths(environment) {
|
|
5846
|
+
debugFs?.(`the ${environment.name} environment cache is invalidated`);
|
|
5847
|
+
this.fsCacheKeys.get(environment)?.clear();
|
|
5848
|
+
}
|
|
5849
|
+
getMemoryCachePath(environment, id) {
|
|
5850
|
+
const result = this.fsCacheKeys.get(environment)?.get(id);
|
|
5851
|
+
if (result != null) debugMemory?.(`${c.green("[read]")} ${id} was cached in ${result}`);
|
|
5852
|
+
else if (result === null) debugMemory?.(`${c.green("[read]")} ${id} was bailed out`);
|
|
5853
|
+
return result;
|
|
5854
|
+
}
|
|
5855
|
+
generateCachePath(vitestConfig, environment, resolver, id, fileContent) {
|
|
5856
|
+
let hashString = "";
|
|
5857
|
+
// bail out if file has import.meta.glob because it depends on other files
|
|
5858
|
+
// TODO: figure out a way to still support it
|
|
5859
|
+
if (fileContent.includes("import.meta.glob(")) {
|
|
5860
|
+
this.saveMemoryCache(environment, id, null);
|
|
5861
|
+
debugMemory?.(`${c.yellow("[write]")} ${id} was bailed out`);
|
|
5862
|
+
return null;
|
|
5863
|
+
}
|
|
5864
|
+
for (const generator of this.fsCacheKeyGenerators) {
|
|
5865
|
+
const result = generator({
|
|
5866
|
+
environment,
|
|
5867
|
+
id,
|
|
5868
|
+
sourceCode: fileContent
|
|
5869
|
+
});
|
|
5870
|
+
if (typeof result === "string") hashString += result;
|
|
5871
|
+
if (result === false) {
|
|
5872
|
+
this.saveMemoryCache(environment, id, null);
|
|
5873
|
+
debugMemory?.(`${c.yellow("[write]")} ${id} was bailed out by a custom generator`);
|
|
5874
|
+
return null;
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
const config = environment.config;
|
|
5878
|
+
// coverage provider is dynamic, so we also clear the whole cache if
|
|
5879
|
+
// vitest.enableCoverage/vitest.disableCoverage is called
|
|
5880
|
+
const coverageAffectsCache = String(this.vitest.config.coverage.enabled && this.vitest.coverageProvider?.requiresTransform?.(id));
|
|
5881
|
+
let cacheConfig = this.fsEnvironmentHashMap.get(environment);
|
|
5882
|
+
if (!cacheConfig) {
|
|
5883
|
+
cacheConfig = JSON.stringify({
|
|
5884
|
+
root: config.root,
|
|
5885
|
+
base: config.base,
|
|
5886
|
+
mode: config.mode,
|
|
5887
|
+
consumer: config.consumer,
|
|
5888
|
+
resolve: config.resolve,
|
|
5889
|
+
plugins: config.plugins.map((p) => p.name),
|
|
5890
|
+
configFileDependencies: config.configFileDependencies.map((file) => tryReadFileSync(file)),
|
|
5891
|
+
environment: environment.name,
|
|
5892
|
+
css: vitestConfig.css,
|
|
5893
|
+
resolver: {
|
|
5894
|
+
inline: resolver.options.inline,
|
|
5895
|
+
external: resolver.options.external,
|
|
5896
|
+
inlineFiles: resolver.options.inlineFiles,
|
|
5897
|
+
moduleDirectories: resolver.options.moduleDirectories
|
|
5898
|
+
}
|
|
5899
|
+
}, (_, value) => {
|
|
5900
|
+
if (typeof value === "function" || value instanceof RegExp) return value.toString();
|
|
5901
|
+
return value;
|
|
5902
|
+
});
|
|
5903
|
+
this.fsEnvironmentHashMap.set(environment, cacheConfig);
|
|
5904
|
+
}
|
|
5905
|
+
hashString += id + fileContent + (process.env.NODE_ENV ?? "") + this.version + cacheConfig + coverageAffectsCache;
|
|
5906
|
+
const cacheKey = hash("sha1", hashString, "hex");
|
|
5907
|
+
let cacheRoot = this.fsCacheRoots.get(vitestConfig);
|
|
5908
|
+
if (cacheRoot == null) {
|
|
5909
|
+
cacheRoot = vitestConfig.experimental.fsModuleCachePath || this.rootCache;
|
|
5910
|
+
if (!existsSync(cacheRoot)) mkdirSync(cacheRoot, { recursive: true });
|
|
5911
|
+
}
|
|
5912
|
+
const fsResultPath = join(cacheRoot, cacheKey);
|
|
5913
|
+
debugMemory?.(`${c.yellow("[write]")} ${id} generated a cache in ${fsResultPath}`);
|
|
5914
|
+
this.saveMemoryCache(environment, id, fsResultPath);
|
|
5915
|
+
return fsResultPath;
|
|
5916
|
+
}
|
|
5917
|
+
saveMemoryCache(environment, id, cache) {
|
|
5918
|
+
let environmentKeys = this.fsCacheKeys.get(environment);
|
|
5919
|
+
if (!environmentKeys) {
|
|
5920
|
+
environmentKeys = /* @__PURE__ */ new Map();
|
|
5921
|
+
this.fsCacheKeys.set(environment, environmentKeys);
|
|
5922
|
+
}
|
|
5923
|
+
environmentKeys.set(id, cache);
|
|
5924
|
+
}
|
|
5925
|
+
async readMetadata() {
|
|
5926
|
+
// metadata is shared between every projects in the workspace, so we ignore project's fsModuleCachePath
|
|
5927
|
+
if (!existsSync(this.metadataFilePath)) return;
|
|
5928
|
+
try {
|
|
5929
|
+
const content = await readFile(this.metadataFilePath, "utf-8");
|
|
5930
|
+
return JSON.parse(content);
|
|
5931
|
+
} catch {}
|
|
5932
|
+
}
|
|
5933
|
+
// before vitest starts running tests, we check that the lockfile wasn't updated
|
|
5934
|
+
// if it was, we nuke the previous cache in case a custom plugin was updated
|
|
5935
|
+
// or a new version of vite/vitest is installed
|
|
5936
|
+
// for the same reason we also cache config file content, but that won't catch changes made in external plugins
|
|
5937
|
+
async ensureCacheIntegrity() {
|
|
5938
|
+
if (![this.vitest.getRootProject(), ...this.vitest.projects].some((p) => p.config.experimental.fsModuleCache)) return;
|
|
5939
|
+
const metadata = await this.readMetadata();
|
|
5940
|
+
const currentLockfileHash = getLockfileHash(this.vitest.vite.config.root);
|
|
5941
|
+
// no metadata found, just store a new one, don't reset the cache
|
|
5942
|
+
if (!metadata) {
|
|
5943
|
+
if (!existsSync(this.rootCache)) mkdirSync(this.rootCache, { recursive: true });
|
|
5944
|
+
debugFs?.(`fs metadata file was created with hash ${currentLockfileHash}`);
|
|
5945
|
+
await writeFile(this.metadataFilePath, JSON.stringify({ lockfileHash: currentLockfileHash }, null, 2), "utf-8");
|
|
5946
|
+
return;
|
|
5947
|
+
}
|
|
5948
|
+
// if lockfile didn't change, don't do anything
|
|
5949
|
+
if (metadata.lockfileHash === currentLockfileHash) return;
|
|
5950
|
+
// lockfile changed, let's clear all caches
|
|
5951
|
+
await this.clearCache(false);
|
|
5952
|
+
this.vitest.vite.config.logger.info(`fs cache was cleared because lockfile has changed`, {
|
|
5953
|
+
timestamp: true,
|
|
5954
|
+
environment: c.yellow("[vitest]")
|
|
5955
|
+
});
|
|
5956
|
+
debugFs?.(`fs cache was cleared because lockfile has changed`);
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
/**
|
|
5960
|
+
* Performs an atomic write operation using the write-then-rename pattern.
|
|
5961
|
+
*
|
|
5962
|
+
* Why we need this:
|
|
5963
|
+
* - Ensures file integrity by never leaving partially written files on disk
|
|
5964
|
+
* - Prevents other processes from reading incomplete data during writes
|
|
5965
|
+
* - Particularly important for test files where incomplete writes could cause test failures
|
|
5966
|
+
*
|
|
5967
|
+
* The implementation writes to a temporary file first, then renames it to the target path.
|
|
5968
|
+
* This rename operation is atomic on most filesystems (including POSIX-compliant ones),
|
|
5969
|
+
* guaranteeing that other processes will only ever see the complete file.
|
|
5970
|
+
*
|
|
5971
|
+
* Added in https://github.com/vitest-dev/vitest/pull/7531
|
|
5972
|
+
*/
|
|
5973
|
+
async function atomicWriteFile(realFilePath, data) {
|
|
5974
|
+
const tmpFilePath = join(dirname(realFilePath), `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
5975
|
+
try {
|
|
5976
|
+
await writeFile(tmpFilePath, data, "utf-8");
|
|
5977
|
+
await rename(tmpFilePath, realFilePath);
|
|
5978
|
+
} finally {
|
|
5979
|
+
try {
|
|
5980
|
+
if (await stat(tmpFilePath)) await unlink(tmpFilePath);
|
|
5981
|
+
} catch {}
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
// lockfile hash resolution taken from vite
|
|
5985
|
+
// since this is experimental, we don't ask to expose it
|
|
5986
|
+
const lockfileFormats = [
|
|
5987
|
+
{
|
|
5988
|
+
path: "node_modules/.package-lock.json",
|
|
5989
|
+
checkPatchesDir: "patches",
|
|
5990
|
+
manager: "npm"
|
|
5991
|
+
},
|
|
5992
|
+
{
|
|
5993
|
+
path: "node_modules/.yarn-state.yml",
|
|
5994
|
+
checkPatchesDir: false,
|
|
5995
|
+
manager: "yarn"
|
|
5996
|
+
},
|
|
5997
|
+
{
|
|
5998
|
+
path: ".pnp.cjs",
|
|
5999
|
+
checkPatchesDir: ".yarn/patches",
|
|
6000
|
+
manager: "yarn"
|
|
6001
|
+
},
|
|
6002
|
+
{
|
|
6003
|
+
path: ".pnp.js",
|
|
6004
|
+
checkPatchesDir: ".yarn/patches",
|
|
6005
|
+
manager: "yarn"
|
|
6006
|
+
},
|
|
6007
|
+
{
|
|
6008
|
+
path: "node_modules/.yarn-integrity",
|
|
6009
|
+
checkPatchesDir: "patches",
|
|
6010
|
+
manager: "yarn"
|
|
6011
|
+
},
|
|
6012
|
+
{
|
|
6013
|
+
path: "node_modules/.pnpm/lock.yaml",
|
|
6014
|
+
checkPatchesDir: false,
|
|
6015
|
+
manager: "pnpm"
|
|
6016
|
+
},
|
|
6017
|
+
{
|
|
6018
|
+
path: ".rush/temp/shrinkwrap-deps.json",
|
|
6019
|
+
checkPatchesDir: false,
|
|
6020
|
+
manager: "pnpm"
|
|
6021
|
+
},
|
|
6022
|
+
{
|
|
6023
|
+
path: "bun.lock",
|
|
6024
|
+
checkPatchesDir: "patches",
|
|
6025
|
+
manager: "bun"
|
|
6026
|
+
},
|
|
6027
|
+
{
|
|
6028
|
+
path: "bun.lockb",
|
|
6029
|
+
checkPatchesDir: "patches",
|
|
6030
|
+
manager: "bun"
|
|
6031
|
+
}
|
|
6032
|
+
].sort((_, { manager }) => {
|
|
6033
|
+
return process.env.npm_config_user_agent?.startsWith(manager) ? 1 : -1;
|
|
6034
|
+
});
|
|
6035
|
+
const lockfilePaths = lockfileFormats.map((l) => l.path);
|
|
6036
|
+
function getLockfileHash(root) {
|
|
6037
|
+
const lockfilePath = lookupFile(root, lockfilePaths);
|
|
6038
|
+
let content = lockfilePath ? fs.readFileSync(lockfilePath, "utf-8") : "";
|
|
6039
|
+
if (lockfilePath) {
|
|
6040
|
+
const normalizedLockfilePath = lockfilePath.replaceAll("\\", "/");
|
|
6041
|
+
const lockfileFormat = lockfileFormats.find((f) => normalizedLockfilePath.endsWith(f.path));
|
|
6042
|
+
if (lockfileFormat.checkPatchesDir) {
|
|
6043
|
+
const stat = tryStatSync(join(lockfilePath.slice(0, -lockfileFormat.path.length), lockfileFormat.checkPatchesDir));
|
|
6044
|
+
if (stat?.isDirectory()) content += stat.mtimeMs.toString();
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
return hash("sha256", content, "hex").substring(0, 8).padEnd(8, "_");
|
|
6048
|
+
}
|
|
6049
|
+
function lookupFile(dir, fileNames) {
|
|
6050
|
+
while (dir) {
|
|
6051
|
+
for (const fileName of fileNames) {
|
|
6052
|
+
const fullPath = join(dir, fileName);
|
|
6053
|
+
if (tryStatSync(fullPath)?.isFile()) return fullPath;
|
|
6054
|
+
}
|
|
6055
|
+
const parentDir = dirname(dir);
|
|
6056
|
+
if (parentDir === dir) return;
|
|
6057
|
+
dir = parentDir;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
function tryReadFileSync(file) {
|
|
6061
|
+
try {
|
|
6062
|
+
return readFileSync(file, "utf-8");
|
|
6063
|
+
} catch {
|
|
6064
|
+
return "";
|
|
6065
|
+
}
|
|
6066
|
+
}
|
|
6067
|
+
function tryStatSync(file) {
|
|
6068
|
+
try {
|
|
6069
|
+
// The "throwIfNoEntry" is a performance optimization for cases where the file does not exist
|
|
6070
|
+
return fs.statSync(file, { throwIfNoEntry: false });
|
|
6071
|
+
} catch {}
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
const saveCachePromises = /* @__PURE__ */ new Map();
|
|
6075
|
+
const readFilePromises = /* @__PURE__ */ new Map();
|
|
6076
|
+
class ModuleFetcher {
|
|
6077
|
+
tmpDirectories = /* @__PURE__ */ new Set();
|
|
6078
|
+
fsCacheEnabled;
|
|
6079
|
+
constructor(resolver, config, fsCache, traces, tmpProjectDir) {
|
|
6080
|
+
this.resolver = resolver;
|
|
6081
|
+
this.config = config;
|
|
6082
|
+
this.fsCache = fsCache;
|
|
6083
|
+
this.traces = traces;
|
|
6084
|
+
this.tmpProjectDir = tmpProjectDir;
|
|
6085
|
+
this.fsCacheEnabled = config.experimental?.fsModuleCache === true;
|
|
6086
|
+
}
|
|
6087
|
+
async fetch(trace, url, importer, environment, makeTmpCopies, options) {
|
|
6088
|
+
if (url.startsWith("data:")) {
|
|
6089
|
+
trace.setAttribute("vitest.module.external", url);
|
|
6090
|
+
return {
|
|
6091
|
+
externalize: url,
|
|
6092
|
+
type: "builtin"
|
|
6093
|
+
};
|
|
6094
|
+
}
|
|
6095
|
+
if (url === "/@vite/client" || url === "@vite/client") {
|
|
6096
|
+
trace.setAttribute("vitest.module.external", url);
|
|
6097
|
+
return {
|
|
6098
|
+
externalize: "/@vite/client",
|
|
6099
|
+
type: "module"
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
5732
6102
|
const isFileUrl = url.startsWith("file://");
|
|
5733
|
-
if (isExternalUrl(url) && !isFileUrl)
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
// This also makes it so externalized modules are inside the module graph.
|
|
6103
|
+
if (isExternalUrl(url) && !isFileUrl) {
|
|
6104
|
+
trace.setAttribute("vitest.module.external", url);
|
|
6105
|
+
return {
|
|
6106
|
+
externalize: url,
|
|
6107
|
+
type: "network"
|
|
6108
|
+
};
|
|
6109
|
+
}
|
|
5741
6110
|
const moduleGraphModule = await environment.moduleGraph.ensureEntryFromUrl(unwrapId(url));
|
|
5742
6111
|
const cached = !!moduleGraphModule.transformResult;
|
|
5743
|
-
|
|
6112
|
+
if (moduleGraphModule.file) trace.setAttribute("code.file.path", moduleGraphModule.file);
|
|
5744
6113
|
if (options?.cached && cached) return { cache: true };
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
6114
|
+
const cachePath = await this.getCachePath(environment, moduleGraphModule);
|
|
6115
|
+
// full fs caching is disabled, but we still want to keep tmp files if makeTmpCopies is enabled
|
|
6116
|
+
// this is primarily used by the forks pool to avoid using process.send(bigBuffer)
|
|
6117
|
+
if (cachePath == null) {
|
|
6118
|
+
const result = await this.fetchAndProcess(environment, url, importer, moduleGraphModule, options);
|
|
6119
|
+
this.recordResult(trace, result);
|
|
6120
|
+
if (!makeTmpCopies || !("code" in result)) return result;
|
|
6121
|
+
const transformResult = moduleGraphModule.transformResult;
|
|
6122
|
+
const tmpPath = transformResult && Reflect.get(transformResult, "_vitest_tmp");
|
|
6123
|
+
if (typeof tmpPath === "string") return getCachedResult(result, tmpPath);
|
|
6124
|
+
const tmpDir = join(this.tmpProjectDir, environment.name);
|
|
6125
|
+
if (!this.tmpDirectories.has(tmpDir)) {
|
|
6126
|
+
if (!existsSync(tmpDir)) mkdirSync(tmpDir, { recursive: true });
|
|
6127
|
+
this.tmpDirectories.add(tmpDir);
|
|
6128
|
+
}
|
|
6129
|
+
const tmpFile = join(tmpDir, hash("sha1", result.id, "hex"));
|
|
6130
|
+
return this.cacheResult(result, tmpFile).then((result) => {
|
|
6131
|
+
if (transformResult) Reflect.set(transformResult, "_vitest_tmp", tmpFile);
|
|
6132
|
+
return result;
|
|
6133
|
+
});
|
|
5751
6134
|
}
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
6135
|
+
if (saveCachePromises.has(cachePath)) return saveCachePromises.get(cachePath).then((result) => {
|
|
6136
|
+
this.recordResult(trace, result);
|
|
6137
|
+
return result;
|
|
6138
|
+
});
|
|
6139
|
+
const cachedModule = await this.getCachedModule(cachePath, environment, moduleGraphModule);
|
|
6140
|
+
if (cachedModule) {
|
|
6141
|
+
this.recordResult(trace, cachedModule);
|
|
6142
|
+
return cachedModule;
|
|
6143
|
+
}
|
|
6144
|
+
const result = await this.fetchAndProcess(environment, url, importer, moduleGraphModule, options);
|
|
6145
|
+
const importers = this.getSerializedDependencies(moduleGraphModule);
|
|
6146
|
+
const map = moduleGraphModule.transformResult?.map;
|
|
6147
|
+
const mappings = map && !("version" in map) && map.mappings === "";
|
|
6148
|
+
return this.cacheResult(result, cachePath, importers, !!mappings);
|
|
6149
|
+
}
|
|
6150
|
+
getSerializedDependencies(node) {
|
|
6151
|
+
const dependencies = [];
|
|
6152
|
+
node.importers.forEach((importer) => {
|
|
6153
|
+
if (importer.id) dependencies.push(importer.id);
|
|
6154
|
+
});
|
|
6155
|
+
return dependencies;
|
|
6156
|
+
}
|
|
6157
|
+
recordResult(trace, result) {
|
|
6158
|
+
if ("externalize" in result) trace.setAttributes({
|
|
6159
|
+
"vitest.module.external": result.externalize,
|
|
6160
|
+
"vitest.fetched_module.type": result.type
|
|
6161
|
+
});
|
|
6162
|
+
if ("id" in result) {
|
|
6163
|
+
trace.setAttributes({
|
|
6164
|
+
"vitest.fetched_module.invalidate": result.invalidate,
|
|
6165
|
+
"vitest.fetched_module.id": result.id,
|
|
6166
|
+
"vitest.fetched_module.url": result.url,
|
|
6167
|
+
"vitest.fetched_module.cache": false
|
|
6168
|
+
});
|
|
6169
|
+
if (result.file) trace.setAttribute("code.file.path", result.file);
|
|
6170
|
+
}
|
|
6171
|
+
if ("code" in result) trace.setAttribute("vitest.fetched_module.code_length", result.code.length);
|
|
6172
|
+
}
|
|
6173
|
+
async getCachePath(environment, moduleGraphModule) {
|
|
6174
|
+
if (!this.fsCacheEnabled) return null;
|
|
6175
|
+
const moduleId = moduleGraphModule.id;
|
|
6176
|
+
const memoryCacheKey = this.fsCache.getMemoryCachePath(environment, moduleId);
|
|
6177
|
+
// undefined means there is no key in memory
|
|
6178
|
+
// null means the file should not be cached
|
|
6179
|
+
if (memoryCacheKey !== void 0) return memoryCacheKey;
|
|
6180
|
+
const fileContent = await this.readFileContentToCache(environment, moduleGraphModule);
|
|
6181
|
+
return this.fsCache.generateCachePath(this.config, environment, this.resolver, moduleGraphModule.id, fileContent);
|
|
6182
|
+
}
|
|
6183
|
+
async readFileContentToCache(environment, moduleGraphModule) {
|
|
6184
|
+
if (moduleGraphModule.file && !moduleGraphModule.file.startsWith("\0") && !moduleGraphModule.file.startsWith("virtual:")) {
|
|
6185
|
+
const result = await this.readFileConcurrently(moduleGraphModule.file);
|
|
6186
|
+
if (result != null) return result;
|
|
6187
|
+
}
|
|
6188
|
+
const loadResult = await environment.pluginContainer.load(moduleGraphModule.id);
|
|
6189
|
+
if (typeof loadResult === "string") return loadResult;
|
|
6190
|
+
if (loadResult != null) return loadResult.code;
|
|
6191
|
+
return "";
|
|
6192
|
+
}
|
|
6193
|
+
async getCachedModule(cachePath, environment, moduleGraphModule) {
|
|
6194
|
+
const cachedModule = await this.fsCache.getCachedModule(cachePath);
|
|
6195
|
+
if (cachedModule && "code" in cachedModule) {
|
|
6196
|
+
// keep the module graph in sync
|
|
6197
|
+
if (!moduleGraphModule.transformResult) {
|
|
6198
|
+
let map = extractSourceMap(cachedModule.code);
|
|
6199
|
+
if (map && cachedModule.file) map.file = cachedModule.file;
|
|
6200
|
+
// mappings is a special source map identifier in rollup
|
|
6201
|
+
if (!map && cachedModule.mappings) map = { mappings: "" };
|
|
6202
|
+
moduleGraphModule.transformResult = {
|
|
6203
|
+
code: cachedModule.code,
|
|
6204
|
+
map,
|
|
6205
|
+
ssr: true
|
|
6206
|
+
};
|
|
6207
|
+
// we populate the module graph to make the watch mode work because it relies on importers
|
|
6208
|
+
cachedModule.importers.forEach((importer) => {
|
|
6209
|
+
const environmentNode = environment.moduleGraph.getModuleById(importer);
|
|
6210
|
+
if (environmentNode) moduleGraphModule.importers.add(environmentNode);
|
|
6211
|
+
});
|
|
5768
6212
|
}
|
|
6213
|
+
return {
|
|
6214
|
+
cached: true,
|
|
6215
|
+
file: cachedModule.file,
|
|
6216
|
+
id: cachedModule.id,
|
|
6217
|
+
tmp: cachePath,
|
|
6218
|
+
url: cachedModule.url,
|
|
6219
|
+
invalidate: false
|
|
6220
|
+
};
|
|
5769
6221
|
}
|
|
5770
|
-
|
|
6222
|
+
return cachedModule;
|
|
6223
|
+
}
|
|
6224
|
+
async fetchAndProcess(environment, url, importer, moduleGraphModule, options) {
|
|
6225
|
+
const externalize = await this.resolver.shouldExternalize(moduleGraphModule.id);
|
|
6226
|
+
if (externalize) return {
|
|
6227
|
+
externalize,
|
|
6228
|
+
type: "module"
|
|
6229
|
+
};
|
|
6230
|
+
return processResultSource(environment, await fetchModule(environment, url, importer, {
|
|
5771
6231
|
...options,
|
|
5772
6232
|
inlineSourceMap: false
|
|
5773
|
-
}).catch(handleRollupError);
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
const
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
6233
|
+
}).catch(handleRollupError));
|
|
6234
|
+
}
|
|
6235
|
+
async cacheResult(result, cachePath, importers = [], mappings = false) {
|
|
6236
|
+
const returnResult = "code" in result ? getCachedResult(result, cachePath) : result;
|
|
6237
|
+
if (saveCachePromises.has(cachePath)) {
|
|
6238
|
+
await saveCachePromises.get(cachePath);
|
|
6239
|
+
return returnResult;
|
|
6240
|
+
}
|
|
6241
|
+
const savePromise = this.fsCache.saveCachedModule(cachePath, result, importers, mappings).then(() => result).finally(() => {
|
|
6242
|
+
saveCachePromises.delete(cachePath);
|
|
6243
|
+
});
|
|
6244
|
+
saveCachePromises.set(cachePath, savePromise);
|
|
6245
|
+
await savePromise;
|
|
6246
|
+
return returnResult;
|
|
6247
|
+
}
|
|
6248
|
+
readFileConcurrently(file) {
|
|
6249
|
+
if (!readFilePromises.has(file)) readFilePromises.set(
|
|
6250
|
+
file,
|
|
6251
|
+
// virtual file can have a "file" property
|
|
6252
|
+
readFile(file, "utf-8").catch(() => null).finally(() => {
|
|
6253
|
+
readFilePromises.delete(file);
|
|
6254
|
+
})
|
|
6255
|
+
);
|
|
6256
|
+
return readFilePromises.get(file);
|
|
6257
|
+
}
|
|
6258
|
+
}
|
|
6259
|
+
function createFetchModuleFunction(resolver, config, fsCache, traces, tmpProjectDir) {
|
|
6260
|
+
const fetcher = new ModuleFetcher(resolver, config, fsCache, traces, tmpProjectDir);
|
|
6261
|
+
return async (url, importer, environment, cacheFs, options, otelCarrier) => {
|
|
6262
|
+
await traces.waitInit();
|
|
6263
|
+
const context = otelCarrier ? traces.getContextFromCarrier(otelCarrier) : void 0;
|
|
6264
|
+
return traces.$("vitest.module.transform", context ? { context } : {}, (span) => fetcher.fetch(span, url, importer, environment, cacheFs, options));
|
|
5802
6265
|
};
|
|
5803
6266
|
}
|
|
5804
6267
|
let SOURCEMAPPING_URL = "sourceMa";
|
|
@@ -5813,8 +6276,7 @@ function processResultSource(environment, result) {
|
|
|
5813
6276
|
inlineSourceMap(node.transformResult);
|
|
5814
6277
|
return {
|
|
5815
6278
|
...result,
|
|
5816
|
-
code: node?.transformResult?.code || result.code
|
|
5817
|
-
transformResult: node?.transformResult
|
|
6279
|
+
code: node?.transformResult?.code || result.code
|
|
5818
6280
|
};
|
|
5819
6281
|
}
|
|
5820
6282
|
const OTHER_SOURCE_MAP_REGEXP = new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,([A-Za-z0-9+/=]+)$`, "gm");
|
|
@@ -5850,6 +6312,20 @@ function getCachedResult(result, tmp) {
|
|
|
5850
6312
|
invalidate: result.invalidate
|
|
5851
6313
|
};
|
|
5852
6314
|
}
|
|
6315
|
+
const MODULE_RUNNER_SOURCEMAPPING_REGEXP = /* @__PURE__ */ new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json;base64,(.+)`);
|
|
6316
|
+
function extractSourceMap(code) {
|
|
6317
|
+
const pattern = `//# ${SOURCEMAPPING_URL}=data:application/json;base64,`;
|
|
6318
|
+
const lastIndex = code.lastIndexOf(pattern);
|
|
6319
|
+
if (lastIndex === -1) return null;
|
|
6320
|
+
const mapString = MODULE_RUNNER_SOURCEMAPPING_REGEXP.exec(code.slice(lastIndex))?.[1];
|
|
6321
|
+
if (!mapString) return null;
|
|
6322
|
+
const sourceMap = JSON.parse(Buffer.from(mapString, "base64").toString("utf-8"));
|
|
6323
|
+
// remove source map mapping added by "inlineSourceMap" to keep the original behaviour of transformRequest
|
|
6324
|
+
if (sourceMap.mappings.startsWith("AAAA,CAAA;"))
|
|
6325
|
+
// 9 because we want to only remove "AAAA,CAAA", but keep ; at the start
|
|
6326
|
+
sourceMap.mappings = sourceMap.mappings.slice(9);
|
|
6327
|
+
return sourceMap;
|
|
6328
|
+
}
|
|
5853
6329
|
// serialize rollup error on server to preserve details as a test error
|
|
5854
6330
|
function handleRollupError(e) {
|
|
5855
6331
|
if (e instanceof Error && ("plugin" in e || "frame" in e || "id" in e))
|
|
@@ -5868,31 +6344,6 @@ function handleRollupError(e) {
|
|
|
5868
6344
|
};
|
|
5869
6345
|
throw e;
|
|
5870
6346
|
}
|
|
5871
|
-
/**
|
|
5872
|
-
* Performs an atomic write operation using the write-then-rename pattern.
|
|
5873
|
-
*
|
|
5874
|
-
* Why we need this:
|
|
5875
|
-
* - Ensures file integrity by never leaving partially written files on disk
|
|
5876
|
-
* - Prevents other processes from reading incomplete data during writes
|
|
5877
|
-
* - Particularly important for test files where incomplete writes could cause test failures
|
|
5878
|
-
*
|
|
5879
|
-
* The implementation writes to a temporary file first, then renames it to the target path.
|
|
5880
|
-
* This rename operation is atomic on most filesystems (including POSIX-compliant ones),
|
|
5881
|
-
* guaranteeing that other processes will only ever see the complete file.
|
|
5882
|
-
*
|
|
5883
|
-
* Added in https://github.com/vitest-dev/vitest/pull/7531
|
|
5884
|
-
*/
|
|
5885
|
-
async function atomicWriteFile(realFilePath, data) {
|
|
5886
|
-
const tmpFilePath = join(dirname(realFilePath), `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
5887
|
-
try {
|
|
5888
|
-
await writeFile(tmpFilePath, data, "utf-8");
|
|
5889
|
-
await rename(tmpFilePath, realFilePath);
|
|
5890
|
-
} finally {
|
|
5891
|
-
try {
|
|
5892
|
-
if (await stat(tmpFilePath)) await unlink(tmpFilePath);
|
|
5893
|
-
} catch {}
|
|
5894
|
-
}
|
|
5895
|
-
}
|
|
5896
6347
|
|
|
5897
6348
|
// this is copy pasted from vite
|
|
5898
6349
|
function normalizeResolvedIdToUrl(environment, resolvedId) {
|
|
@@ -5926,7 +6377,15 @@ class ServerModuleRunner extends ModuleRunner {
|
|
|
5926
6377
|
if (name === "getBuiltins") return await environment.hot.handleInvoke(event);
|
|
5927
6378
|
if (name !== "fetchModule") return { error: /* @__PURE__ */ new Error(`Unknown method: ${name}. Expected "fetchModule".`) };
|
|
5928
6379
|
try {
|
|
5929
|
-
|
|
6380
|
+
const result = await fetcher(data[0], data[1], environment, false, data[2]);
|
|
6381
|
+
if ("tmp" in result) {
|
|
6382
|
+
const code = await readFile(result.tmp);
|
|
6383
|
+
return { result: {
|
|
6384
|
+
...result,
|
|
6385
|
+
code
|
|
6386
|
+
} };
|
|
6387
|
+
}
|
|
6388
|
+
return { result };
|
|
5930
6389
|
} catch (error) {
|
|
5931
6390
|
return { error };
|
|
5932
6391
|
}
|
|
@@ -6214,7 +6673,7 @@ class VitestPackageInstaller {
|
|
|
6214
6673
|
if (/* @__PURE__ */ isPackageExists(dependency, { paths: [root, __dirname$1] })) return true;
|
|
6215
6674
|
process.stderr.write(c.red(`${c.inverse(c.red(" MISSING DEPENDENCY "))} Cannot find dependency '${dependency}'\n\n`));
|
|
6216
6675
|
if (!isTTY) return false;
|
|
6217
|
-
const { install } = await (await import('./index.
|
|
6676
|
+
const { install } = await (await import('./index.D4KonVSU.js').then(function (n) { return n.i; })).default({
|
|
6218
6677
|
type: "confirm",
|
|
6219
6678
|
name: "install",
|
|
6220
6679
|
message: c.reset(`Do you want to install ${c.green(dependency)}?`)
|
|
@@ -6283,7 +6742,7 @@ async function getSpecificationsEnvironments(specifications) {
|
|
|
6283
6742
|
// reuse if projects have the same test files
|
|
6284
6743
|
let code = cache.get(filepath);
|
|
6285
6744
|
if (!code) {
|
|
6286
|
-
code = await promises
|
|
6745
|
+
code = await promises.readFile(filepath, "utf-8");
|
|
6287
6746
|
cache.set(filepath, code);
|
|
6288
6747
|
}
|
|
6289
6748
|
// 1. Check for control comments in the file
|
|
@@ -6514,7 +6973,7 @@ class BrowserPool {
|
|
|
6514
6973
|
return;
|
|
6515
6974
|
}
|
|
6516
6975
|
debug?.("[%s] error during %s test run: %s", sessionId, file, error);
|
|
6517
|
-
this.reject(error);
|
|
6976
|
+
this.reject(new Error(`Failed to run the test ${file.filepath}.`, { cause: error }));
|
|
6518
6977
|
});
|
|
6519
6978
|
}).catch((err) => this.reject(err));
|
|
6520
6979
|
}
|
|
@@ -6541,9 +7000,9 @@ function shouldIgnoreDebugger(provider, browser) {
|
|
|
6541
7000
|
return browser !== "chromium";
|
|
6542
7001
|
}
|
|
6543
7002
|
|
|
6544
|
-
function createMethodsRPC(project,
|
|
7003
|
+
function createMethodsRPC(project, methodsOptions = {}) {
|
|
6545
7004
|
const vitest = project.vitest;
|
|
6546
|
-
const cacheFs =
|
|
7005
|
+
const cacheFs = methodsOptions.cacheFs ?? false;
|
|
6547
7006
|
project.vitest.state.metadata[project.name] ??= {
|
|
6548
7007
|
externalized: {},
|
|
6549
7008
|
duration: {},
|
|
@@ -6552,11 +7011,11 @@ function createMethodsRPC(project, options = {}) {
|
|
|
6552
7011
|
if (project.config.dumpDir && !existsSync(project.config.dumpDir)) mkdirSync(project.config.dumpDir, { recursive: true });
|
|
6553
7012
|
project.vitest.state.metadata[project.name].dumpDir = project.config.dumpDir;
|
|
6554
7013
|
return {
|
|
6555
|
-
async fetch(url, importer, environmentName, options) {
|
|
7014
|
+
async fetch(url, importer, environmentName, options, otelCarrier) {
|
|
6556
7015
|
const environment = project.vite.environments[environmentName];
|
|
6557
7016
|
if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
|
|
6558
7017
|
const start = performance.now();
|
|
6559
|
-
return await project._fetcher(url, importer, environment, cacheFs, options).then((result) => {
|
|
7018
|
+
return await project._fetcher(url, importer, environment, cacheFs, options, otelCarrier).then((result) => {
|
|
6560
7019
|
const duration = performance.now() - start;
|
|
6561
7020
|
project.vitest.state.transformTime += duration;
|
|
6562
7021
|
const metadata = project.vitest.state.metadata[project.name];
|
|
@@ -6597,25 +7056,25 @@ function createMethodsRPC(project, options = {}) {
|
|
|
6597
7056
|
return { code: (await environment.transformRequest(url).catch(handleRollupError))?.code };
|
|
6598
7057
|
},
|
|
6599
7058
|
async onQueued(file) {
|
|
6600
|
-
if (
|
|
7059
|
+
if (methodsOptions.collect) vitest.state.collectFiles(project, [file]);
|
|
6601
7060
|
else await vitest._testRun.enqueued(project, file);
|
|
6602
7061
|
},
|
|
6603
7062
|
async onCollected(files) {
|
|
6604
|
-
if (
|
|
7063
|
+
if (methodsOptions.collect) vitest.state.collectFiles(project, files);
|
|
6605
7064
|
else await vitest._testRun.collected(project, files);
|
|
6606
7065
|
},
|
|
6607
7066
|
onAfterSuiteRun(meta) {
|
|
6608
7067
|
vitest.coverageProvider?.onAfterSuiteRun(meta);
|
|
6609
7068
|
},
|
|
6610
|
-
async
|
|
6611
|
-
return vitest._testRun.
|
|
7069
|
+
async onTaskArtifactRecord(testId, artifact) {
|
|
7070
|
+
return vitest._testRun.recordArtifact(testId, artifact);
|
|
6612
7071
|
},
|
|
6613
7072
|
async onTaskUpdate(packs, events) {
|
|
6614
|
-
if (
|
|
7073
|
+
if (methodsOptions.collect) vitest.state.updateTasks(packs);
|
|
6615
7074
|
else await vitest._testRun.updated(packs, events);
|
|
6616
7075
|
},
|
|
6617
7076
|
async onUserConsoleLog(log) {
|
|
6618
|
-
if (
|
|
7077
|
+
if (methodsOptions.collect) vitest.state.updateUserLog(log);
|
|
6619
7078
|
else await vitest._testRun.log(log);
|
|
6620
7079
|
},
|
|
6621
7080
|
onUnhandledError(err, type) {
|
|
@@ -6648,11 +7107,18 @@ class PoolRunner {
|
|
|
6648
7107
|
environment;
|
|
6649
7108
|
_state = RunnerState.IDLE;
|
|
6650
7109
|
_operationLock = null;
|
|
7110
|
+
_terminatePromise = createDefer();
|
|
6651
7111
|
_eventEmitter = new EventEmitter();
|
|
7112
|
+
_offCancel;
|
|
6652
7113
|
_rpc;
|
|
7114
|
+
_otel = null;
|
|
7115
|
+
_traces;
|
|
6653
7116
|
get isTerminated() {
|
|
6654
7117
|
return this._state === RunnerState.STOPPED;
|
|
6655
7118
|
}
|
|
7119
|
+
waitForTerminated() {
|
|
7120
|
+
return this._terminatePromise;
|
|
7121
|
+
}
|
|
6656
7122
|
get isStarted() {
|
|
6657
7123
|
return this._state === RunnerState.STARTED;
|
|
6658
7124
|
}
|
|
@@ -6660,22 +7126,65 @@ class PoolRunner {
|
|
|
6660
7126
|
this.worker = worker;
|
|
6661
7127
|
this.project = options.project;
|
|
6662
7128
|
this.environment = options.environment;
|
|
7129
|
+
const vitest = this.project.vitest;
|
|
7130
|
+
this._traces = vitest._traces;
|
|
7131
|
+
if (this._traces.isEnabled()) {
|
|
7132
|
+
const { span: workerSpan, context } = this._traces.startContextSpan("vitest.worker");
|
|
7133
|
+
this._otel = {
|
|
7134
|
+
span: workerSpan,
|
|
7135
|
+
workerContext: context,
|
|
7136
|
+
files: []
|
|
7137
|
+
};
|
|
7138
|
+
this._otel.span.setAttributes({
|
|
7139
|
+
"vitest.worker.name": this.worker.name,
|
|
7140
|
+
"vitest.project": this.project.name,
|
|
7141
|
+
"vitest.environment": this.environment.name
|
|
7142
|
+
});
|
|
7143
|
+
}
|
|
6663
7144
|
this._rpc = createBirpc(createMethodsRPC(this.project, {
|
|
6664
7145
|
collect: options.method === "collect",
|
|
6665
7146
|
cacheFs: worker.cacheFs
|
|
6666
7147
|
}), {
|
|
6667
7148
|
eventNames: ["onCancel"],
|
|
6668
|
-
post: (request) =>
|
|
7149
|
+
post: (request) => {
|
|
7150
|
+
if (this._state !== RunnerState.STOPPING && this._state !== RunnerState.STOPPED) this.postMessage(request);
|
|
7151
|
+
},
|
|
6669
7152
|
on: (callback) => this._eventEmitter.on("rpc", callback),
|
|
6670
7153
|
timeout: -1
|
|
6671
7154
|
});
|
|
6672
|
-
this.
|
|
7155
|
+
this._offCancel = vitest.onCancel((reason) => this._rpc.onCancel(reason));
|
|
6673
7156
|
}
|
|
6674
7157
|
postMessage(message) {
|
|
6675
7158
|
// Only send messages when runner is active (not fully stopped)
|
|
6676
7159
|
// Allow sending during STOPPING state for the 'stop' message itself
|
|
6677
7160
|
if (this._state !== RunnerState.STOPPED) return this.worker.send(message);
|
|
6678
7161
|
}
|
|
7162
|
+
startTracesSpan(name) {
|
|
7163
|
+
const traces = this._traces;
|
|
7164
|
+
if (!this._otel) return traces.startSpan(name);
|
|
7165
|
+
const { span, context } = traces.startContextSpan(name, this._otel.workerContext);
|
|
7166
|
+
this._otel.currentContext = context;
|
|
7167
|
+
const end = span.end.bind(span);
|
|
7168
|
+
span.end = (endTime) => {
|
|
7169
|
+
this._otel.currentContext = void 0;
|
|
7170
|
+
return end(endTime);
|
|
7171
|
+
};
|
|
7172
|
+
return span;
|
|
7173
|
+
}
|
|
7174
|
+
request(method, context) {
|
|
7175
|
+
this._otel?.files.push(...context.files.map((f) => f.filepath));
|
|
7176
|
+
return this.postMessage({
|
|
7177
|
+
__vitest_worker_request__: true,
|
|
7178
|
+
type: method,
|
|
7179
|
+
context,
|
|
7180
|
+
poolId: this.poolId,
|
|
7181
|
+
otelCarrier: this.getOTELCarrier()
|
|
7182
|
+
});
|
|
7183
|
+
}
|
|
7184
|
+
getOTELCarrier() {
|
|
7185
|
+
const activeContext = this._otel?.currentContext || this._otel?.workerContext;
|
|
7186
|
+
return activeContext ? this._traces.getContextCarrier(activeContext) : void 0;
|
|
7187
|
+
}
|
|
6679
7188
|
async start() {
|
|
6680
7189
|
// Wait for any ongoing operation to complete
|
|
6681
7190
|
if (this._operationLock) await this._operationLock;
|
|
@@ -6683,15 +7192,21 @@ class PoolRunner {
|
|
|
6683
7192
|
if (this._state === RunnerState.STOPPED) throw new Error("[vitest-pool-runner]: Cannot start a stopped runner");
|
|
6684
7193
|
// Create operation lock to prevent concurrent start/stop
|
|
6685
7194
|
this._operationLock = createDefer();
|
|
7195
|
+
let startSpan;
|
|
6686
7196
|
try {
|
|
6687
7197
|
this._state = RunnerState.STARTING;
|
|
6688
|
-
await this.worker.start();
|
|
7198
|
+
await this._traces.$(`vitest.${this.worker.name}.start`, { context: this._otel?.workerContext }, () => this.worker.start());
|
|
6689
7199
|
// Attach event listeners AFTER starting worker to avoid issues
|
|
6690
7200
|
// if worker.start() fails
|
|
6691
7201
|
this.worker.on("error", this.emitWorkerError);
|
|
6692
7202
|
this.worker.on("exit", this.emitUnexpectedExit);
|
|
6693
7203
|
this.worker.on("message", this.emitWorkerMessage);
|
|
7204
|
+
startSpan = this.startTracesSpan("vitest.worker.start");
|
|
6694
7205
|
const startPromise = this.withTimeout(this.waitForStart(), START_TIMEOUT);
|
|
7206
|
+
const globalConfig = this.project.vitest.config.experimental.openTelemetry;
|
|
7207
|
+
const projectConfig = this.project.config.experimental.openTelemetry;
|
|
7208
|
+
const tracesEnabled = projectConfig?.enabled ?? globalConfig?.enabled === true;
|
|
7209
|
+
const tracesSdk = projectConfig?.sdkPath ?? globalConfig?.sdkPath;
|
|
6695
7210
|
this.postMessage({
|
|
6696
7211
|
type: "start",
|
|
6697
7212
|
__vitest_worker_request__: true,
|
|
@@ -6703,14 +7218,21 @@ class PoolRunner {
|
|
|
6703
7218
|
},
|
|
6704
7219
|
config: this.project.serializedConfig,
|
|
6705
7220
|
pool: this.worker.name
|
|
7221
|
+
},
|
|
7222
|
+
traces: {
|
|
7223
|
+
enabled: tracesEnabled,
|
|
7224
|
+
sdkPath: tracesSdk,
|
|
7225
|
+
otelCarrier: this.getOTELCarrier()
|
|
6706
7226
|
}
|
|
6707
7227
|
});
|
|
6708
7228
|
await startPromise;
|
|
6709
7229
|
this._state = RunnerState.STARTED;
|
|
6710
7230
|
} catch (error) {
|
|
6711
7231
|
this._state = RunnerState.IDLE;
|
|
7232
|
+
startSpan?.recordException(error);
|
|
6712
7233
|
throw error;
|
|
6713
7234
|
} finally {
|
|
7235
|
+
startSpan?.end();
|
|
6714
7236
|
this._operationLock.resolve();
|
|
6715
7237
|
this._operationLock = null;
|
|
6716
7238
|
}
|
|
@@ -6719,7 +7241,9 @@ class PoolRunner {
|
|
|
6719
7241
|
// Wait for any ongoing operation to complete
|
|
6720
7242
|
if (this._operationLock) await this._operationLock;
|
|
6721
7243
|
if (this._state === RunnerState.STOPPED || this._state === RunnerState.STOPPING) return;
|
|
7244
|
+
this._otel?.span.setAttribute("vitest.worker.files", this._otel.files);
|
|
6722
7245
|
if (this._state === RunnerState.IDLE) {
|
|
7246
|
+
this._otel?.span.end();
|
|
6723
7247
|
this._state = RunnerState.STOPPED;
|
|
6724
7248
|
return;
|
|
6725
7249
|
}
|
|
@@ -6729,10 +7253,14 @@ class PoolRunner {
|
|
|
6729
7253
|
this._state = RunnerState.STOPPING;
|
|
6730
7254
|
// Remove exit listener early to avoid "unexpected exit" errors during shutdown
|
|
6731
7255
|
this.worker.off("exit", this.emitUnexpectedExit);
|
|
7256
|
+
const stopSpan = this.startTracesSpan("vitest.worker.stop");
|
|
6732
7257
|
await this.withTimeout(new Promise((resolve) => {
|
|
6733
7258
|
const onStop = (response) => {
|
|
6734
7259
|
if (response.type === "stopped") {
|
|
6735
|
-
if (response.error)
|
|
7260
|
+
if (response.error) {
|
|
7261
|
+
stopSpan.recordException(response.error);
|
|
7262
|
+
this.project.vitest.state.catchError(response.error, "Teardown Error");
|
|
7263
|
+
}
|
|
6736
7264
|
resolve();
|
|
6737
7265
|
this.off("message", onStop);
|
|
6738
7266
|
}
|
|
@@ -6740,14 +7268,18 @@ class PoolRunner {
|
|
|
6740
7268
|
this.on("message", onStop);
|
|
6741
7269
|
this.postMessage({
|
|
6742
7270
|
type: "stop",
|
|
6743
|
-
__vitest_worker_request__: true
|
|
7271
|
+
__vitest_worker_request__: true,
|
|
7272
|
+
otelCarrier: this.getOTELCarrier()
|
|
6744
7273
|
});
|
|
6745
|
-
}), STOP_TIMEOUT)
|
|
7274
|
+
}), STOP_TIMEOUT).finally(() => {
|
|
7275
|
+
stopSpan.end();
|
|
7276
|
+
});
|
|
6746
7277
|
this._eventEmitter.removeAllListeners();
|
|
7278
|
+
this._offCancel();
|
|
6747
7279
|
this._rpc.$close(/* @__PURE__ */ new Error("[vitest-pool-runner]: Pending methods while closing rpc"));
|
|
6748
7280
|
// Stop the worker process (this sets _fork/_thread to undefined)
|
|
6749
7281
|
// Worker's event listeners (error, message) are implicitly removed when worker terminates
|
|
6750
|
-
await this.worker.stop();
|
|
7282
|
+
await this._traces.$(`vitest.${this.worker.name}.stop`, { context: this._otel?.workerContext }, () => this.worker.stop());
|
|
6751
7283
|
this._state = RunnerState.STOPPED;
|
|
6752
7284
|
} catch (error) {
|
|
6753
7285
|
// Ensure we transition to stopped state even on error
|
|
@@ -6756,6 +7288,8 @@ class PoolRunner {
|
|
|
6756
7288
|
} finally {
|
|
6757
7289
|
this._operationLock.resolve();
|
|
6758
7290
|
this._operationLock = null;
|
|
7291
|
+
this._otel?.span.end();
|
|
7292
|
+
this._terminatePromise.resolve();
|
|
6759
7293
|
}
|
|
6760
7294
|
}
|
|
6761
7295
|
on(event, callback) {
|
|
@@ -6835,14 +7369,14 @@ class ForksPoolWorker {
|
|
|
6835
7369
|
this.fork.off(event, callback);
|
|
6836
7370
|
}
|
|
6837
7371
|
send(message) {
|
|
6838
|
-
|
|
6839
|
-
this.fork.send(v8.serialize(message));
|
|
7372
|
+
this.fork.send(message);
|
|
6840
7373
|
}
|
|
6841
7374
|
async start() {
|
|
6842
7375
|
this._fork ||= fork(this.entrypoint, [], {
|
|
6843
7376
|
env: this.env,
|
|
6844
7377
|
execArgv: this.execArgv,
|
|
6845
|
-
stdio: "pipe"
|
|
7378
|
+
stdio: "pipe",
|
|
7379
|
+
serialization: "advanced"
|
|
6846
7380
|
});
|
|
6847
7381
|
if (this._fork.stdout) {
|
|
6848
7382
|
this.stdout.setMaxListeners(1 + this.stdout.getMaxListeners());
|
|
@@ -6880,41 +7414,13 @@ class ForksPoolWorker {
|
|
|
6880
7414
|
this._fork = void 0;
|
|
6881
7415
|
}
|
|
6882
7416
|
deserialize(data) {
|
|
6883
|
-
|
|
6884
|
-
return v8.deserialize(Buffer.from(data));
|
|
6885
|
-
} catch (error) {
|
|
6886
|
-
let stringified = "";
|
|
6887
|
-
try {
|
|
6888
|
-
stringified = `\nReceived value: ${JSON.stringify(data)}`;
|
|
6889
|
-
} catch {}
|
|
6890
|
-
throw new Error(`[vitest-pool]: Unexpected call to process.send(). Make sure your test cases are not interfering with process's channel.${stringified}`, { cause: error });
|
|
6891
|
-
}
|
|
7417
|
+
return data;
|
|
6892
7418
|
}
|
|
6893
7419
|
get fork() {
|
|
6894
7420
|
if (!this._fork) throw new Error(`The child process was torn down or never initialized. This is a bug in Vitest.`);
|
|
6895
7421
|
return this._fork;
|
|
6896
7422
|
}
|
|
6897
7423
|
}
|
|
6898
|
-
/**
|
|
6899
|
-
* Prepares `SerializedConfig` for serialization, e.g. `node:v8.serialize`
|
|
6900
|
-
* - Unwrapping done in {@link file://./../../../runtime/workers/init-forks.ts}
|
|
6901
|
-
*/
|
|
6902
|
-
function wrapSerializableConfig(config) {
|
|
6903
|
-
let testNamePattern = config.testNamePattern;
|
|
6904
|
-
let defines = config.defines;
|
|
6905
|
-
// v8 serialize does not support regex
|
|
6906
|
-
if (testNamePattern && typeof testNamePattern !== "string") testNamePattern = `$$vitest:${testNamePattern.toString()}`;
|
|
6907
|
-
// v8 serialize drops properties with undefined value
|
|
6908
|
-
if (defines) defines = {
|
|
6909
|
-
keys: Object.keys(defines),
|
|
6910
|
-
original: defines
|
|
6911
|
-
};
|
|
6912
|
-
return {
|
|
6913
|
-
...config,
|
|
6914
|
-
testNamePattern,
|
|
6915
|
-
defines
|
|
6916
|
-
};
|
|
6917
|
-
}
|
|
6918
7424
|
|
|
6919
7425
|
/** @experimental */
|
|
6920
7426
|
class ThreadsPoolWorker {
|
|
@@ -7215,8 +7721,9 @@ class Pool {
|
|
|
7215
7721
|
cancelTask
|
|
7216
7722
|
};
|
|
7217
7723
|
this.activeTasks.push(activeTask);
|
|
7724
|
+
// active tasks receive cancel signal and shut down gracefully
|
|
7218
7725
|
async function cancelTask() {
|
|
7219
|
-
await runner.
|
|
7726
|
+
await runner.waitForTerminated();
|
|
7220
7727
|
resolver.reject(/* @__PURE__ */ new Error("Cancelled"));
|
|
7221
7728
|
}
|
|
7222
7729
|
const onFinished = (message) => {
|
|
@@ -7237,14 +7744,15 @@ class Pool {
|
|
|
7237
7744
|
}
|
|
7238
7745
|
const poolId = runner.poolId ?? this.getWorkerId();
|
|
7239
7746
|
runner.poolId = poolId;
|
|
7747
|
+
const span = runner.startTracesSpan(`vitest.worker.${method}`);
|
|
7240
7748
|
// Start running the test in the worker
|
|
7241
|
-
runner.
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7749
|
+
runner.request(method, task.context);
|
|
7750
|
+
await resolver.promise.catch((error) => {
|
|
7751
|
+
span.recordException(error);
|
|
7752
|
+
throw error;
|
|
7753
|
+
}).finally(() => {
|
|
7754
|
+
span.end();
|
|
7246
7755
|
});
|
|
7247
|
-
await resolver.promise;
|
|
7248
7756
|
const index = this.activeTasks.indexOf(activeTask);
|
|
7249
7757
|
if (index !== -1) this.activeTasks.splice(index, 1);
|
|
7250
7758
|
if (!task.isolate && !isMemoryLimitReached && this.queue[0]?.task.isolate === false && isEqualRunner(runner, this.queue[0].task)) {
|
|
@@ -7680,7 +8188,8 @@ function serializeConfig(project) {
|
|
|
7680
8188
|
standalone: config.standalone,
|
|
7681
8189
|
printConsoleTrace: config.printConsoleTrace ?? globalConfig.printConsoleTrace,
|
|
7682
8190
|
benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples },
|
|
7683
|
-
serializedDefines: config.browser.enabled ? "" : project._serializedDefines || ""
|
|
8191
|
+
serializedDefines: config.browser.enabled ? "" : project._serializedDefines || "",
|
|
8192
|
+
experimental: { fsModuleCache: config.experimental.fsModuleCache ?? false }
|
|
7684
8193
|
};
|
|
7685
8194
|
}
|
|
7686
8195
|
|
|
@@ -8731,13 +9240,19 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
8731
9240
|
return project.vitest.matchesProjectFilter(name);
|
|
8732
9241
|
})) throw new VitestFilteredOutProjectError();
|
|
8733
9242
|
}
|
|
9243
|
+
const vitestConfig = { name: {
|
|
9244
|
+
label: name,
|
|
9245
|
+
color
|
|
9246
|
+
} };
|
|
9247
|
+
// always inherit the global `fsModuleCache` value even without `extends: true`
|
|
9248
|
+
if (testConfig.experimental?.fsModuleCache == null && project.vitest.config.experimental?.fsModuleCache !== null) {
|
|
9249
|
+
vitestConfig.experimental ??= {};
|
|
9250
|
+
vitestConfig.experimental.fsModuleCache = project.vitest.config.experimental.fsModuleCache;
|
|
9251
|
+
}
|
|
8734
9252
|
return {
|
|
8735
9253
|
base: "/",
|
|
8736
9254
|
environments: { __vitest__: { dev: {} } },
|
|
8737
|
-
test:
|
|
8738
|
-
label: name,
|
|
8739
|
-
color
|
|
8740
|
-
} }
|
|
9255
|
+
test: vitestConfig
|
|
8741
9256
|
};
|
|
8742
9257
|
}
|
|
8743
9258
|
},
|
|
@@ -8822,16 +9337,21 @@ class VitestResolver {
|
|
|
8822
9337
|
options;
|
|
8823
9338
|
externalizeCache = /* @__PURE__ */ new Map();
|
|
8824
9339
|
constructor(cacheDir, config) {
|
|
9340
|
+
// sorting to make cache consistent
|
|
9341
|
+
const inline = config.server.deps?.inline;
|
|
9342
|
+
if (Array.isArray(inline)) inline.sort();
|
|
9343
|
+
const external = config.server.deps?.external;
|
|
9344
|
+
if (Array.isArray(external)) external.sort();
|
|
8825
9345
|
this.options = {
|
|
8826
|
-
moduleDirectories: config.deps.moduleDirectories,
|
|
9346
|
+
moduleDirectories: config.deps.moduleDirectories?.sort(),
|
|
8827
9347
|
inlineFiles: config.setupFiles.flatMap((file) => {
|
|
8828
9348
|
if (file.startsWith("file://")) return file;
|
|
8829
9349
|
const resolvedId = resolve(file);
|
|
8830
9350
|
return [resolvedId, pathToFileURL(resolvedId).href];
|
|
8831
9351
|
}),
|
|
8832
9352
|
cacheDir,
|
|
8833
|
-
inline
|
|
8834
|
-
external
|
|
9353
|
+
inline,
|
|
9354
|
+
external
|
|
8835
9355
|
};
|
|
8836
9356
|
}
|
|
8837
9357
|
shouldExternalize(file) {
|
|
@@ -8888,7 +9408,7 @@ async function isValidNodeImport(id) {
|
|
|
8888
9408
|
if (/\.(?:\w+-)?esm?(?:-\w+)?\.js$|\/esm?\//.test(id)) return false;
|
|
8889
9409
|
try {
|
|
8890
9410
|
await esModuleLexer.init;
|
|
8891
|
-
const code = await promises
|
|
9411
|
+
const code = await promises.readFile(id, "utf8");
|
|
8892
9412
|
const [, , , hasModuleSyntax] = esModuleLexer.parse(code);
|
|
8893
9413
|
return !hasModuleSyntax;
|
|
8894
9414
|
} catch {
|
|
@@ -9154,15 +9674,24 @@ class TestProject {
|
|
|
9154
9674
|
* @param filters String filters to match the test files.
|
|
9155
9675
|
*/
|
|
9156
9676
|
async globTestFiles(filters = []) {
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9677
|
+
return this.vitest._traces.$("vitest.config.resolve_include_project", async (span) => {
|
|
9678
|
+
const dir = this.config.dir || this.config.root;
|
|
9679
|
+
const { include, exclude, includeSource } = this.config;
|
|
9680
|
+
const typecheck = this.config.typecheck;
|
|
9681
|
+
span.setAttributes({
|
|
9682
|
+
cwd: dir,
|
|
9683
|
+
include,
|
|
9684
|
+
exclude,
|
|
9685
|
+
includeSource,
|
|
9686
|
+
typecheck: typecheck.enabled ? typecheck.include : []
|
|
9687
|
+
});
|
|
9688
|
+
const [testFiles, typecheckTestFiles] = await Promise.all([typecheck.enabled && typecheck.only ? [] : this.globAllTestFiles(include, exclude, includeSource, dir), typecheck.enabled ? this.typecheckFilesList || this.globFiles(typecheck.include, typecheck.exclude, dir) : []]);
|
|
9689
|
+
this.typecheckFilesList = typecheckTestFiles;
|
|
9690
|
+
return {
|
|
9691
|
+
testFiles: this.filterFiles(testFiles, filters, dir),
|
|
9692
|
+
typecheckTestFiles: this.filterFiles(typecheckTestFiles, filters, dir)
|
|
9693
|
+
};
|
|
9694
|
+
});
|
|
9166
9695
|
}
|
|
9167
9696
|
async globAllTestFiles(include, exclude, includeSource, cwd) {
|
|
9168
9697
|
if (this.testFilesList) return this.testFilesList;
|
|
@@ -9171,7 +9700,7 @@ class TestProject {
|
|
|
9171
9700
|
const files = await this.globFiles(includeSource, exclude, cwd);
|
|
9172
9701
|
await Promise.all(files.map(async (file) => {
|
|
9173
9702
|
try {
|
|
9174
|
-
const code = await promises
|
|
9703
|
+
const code = await promises.readFile(file, "utf-8");
|
|
9175
9704
|
if (this.isInSourceTestCode(code)) testFiles.push(file);
|
|
9176
9705
|
} catch {
|
|
9177
9706
|
return null;
|
|
@@ -9323,10 +9852,7 @@ class TestProject {
|
|
|
9323
9852
|
this._resolver = new VitestResolver(server.config.cacheDir, this._config);
|
|
9324
9853
|
this._vite = server;
|
|
9325
9854
|
this._serializedDefines = createDefinesScript(server.config.define);
|
|
9326
|
-
this._fetcher = createFetchModuleFunction(this._resolver, this.
|
|
9327
|
-
dumpFolder: this.config.dumpDir,
|
|
9328
|
-
readFromDump: this.config.server.debug?.load ?? process.env.VITEST_DEBUG_LOAD_DUMP != null
|
|
9329
|
-
});
|
|
9855
|
+
this._fetcher = createFetchModuleFunction(this._resolver, this._config, this.vitest._fsCache, this.vitest._traces, this.tmpDir);
|
|
9330
9856
|
const environment = server.environments.__vitest__;
|
|
9331
9857
|
this.runner = new ServerModuleRunner(environment, this._fetcher, this._config);
|
|
9332
9858
|
}
|
|
@@ -9394,7 +9920,7 @@ function deduped(cb) {
|
|
|
9394
9920
|
}
|
|
9395
9921
|
async function initializeProject(workspacePath, ctx, options) {
|
|
9396
9922
|
const project = new TestProject(ctx, options);
|
|
9397
|
-
const { configFile
|
|
9923
|
+
const { configFile, ...restOptions } = options;
|
|
9398
9924
|
await createViteServer({
|
|
9399
9925
|
...restOptions,
|
|
9400
9926
|
configFile,
|
|
@@ -9567,8 +10093,8 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
|
|
|
9567
10093
|
});
|
|
9568
10094
|
return resolvedProjects.filter((project) => !removeProjects.has(project));
|
|
9569
10095
|
}
|
|
9570
|
-
function cloneConfig(project, { browser
|
|
9571
|
-
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name, provider
|
|
10096
|
+
function cloneConfig(project, { browser, ...config }) {
|
|
10097
|
+
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name, provider, ...overrideConfig } = config;
|
|
9572
10098
|
const currentConfig = project.config.browser;
|
|
9573
10099
|
const clonedConfig = deepClone(project.config);
|
|
9574
10100
|
return mergeConfig({
|
|
@@ -9986,6 +10512,14 @@ class TestCase extends ReportedTaskImplementation {
|
|
|
9986
10512
|
return [...this.task.annotations];
|
|
9987
10513
|
}
|
|
9988
10514
|
/**
|
|
10515
|
+
* @experimental
|
|
10516
|
+
*
|
|
10517
|
+
* Test artifacts recorded via the `recordArtifact` API during the test execution.
|
|
10518
|
+
*/
|
|
10519
|
+
artifacts() {
|
|
10520
|
+
return [...this.task.artifacts];
|
|
10521
|
+
}
|
|
10522
|
+
/**
|
|
9989
10523
|
* Useful information about the test like duration, memory usage, etc.
|
|
9990
10524
|
* Diagnostic is only available after the test has finished.
|
|
9991
10525
|
*/
|
|
@@ -10840,15 +11374,22 @@ class TestRun {
|
|
|
10840
11374
|
this.vitest.state.updateUserLog(log);
|
|
10841
11375
|
await this.vitest.report("onUserConsoleLog", log);
|
|
10842
11376
|
}
|
|
10843
|
-
async
|
|
11377
|
+
async recordArtifact(testId, artifact) {
|
|
10844
11378
|
const task = this.vitest.state.idMap.get(testId);
|
|
10845
11379
|
const entity = task && this.vitest.state.getReportedEntity(task);
|
|
10846
11380
|
assert$1(task && entity, `Entity must be found for task ${task?.name || testId}`);
|
|
10847
|
-
assert$1(entity.type === "test", `
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
11381
|
+
assert$1(entity.type === "test", `Artifacts can only be recorded on a test, instead got ${entity.type}`);
|
|
11382
|
+
// annotations won't resolve as artifacts for backwards compatibility until next major
|
|
11383
|
+
if (artifact.type === "internal:annotation") {
|
|
11384
|
+
await this.resolveTestAttachment(entity, artifact.annotation.attachment, artifact.annotation.message);
|
|
11385
|
+
entity.task.annotations.push(artifact.annotation);
|
|
11386
|
+
await this.vitest.report("onTestCaseAnnotate", entity, artifact.annotation);
|
|
11387
|
+
return artifact;
|
|
11388
|
+
}
|
|
11389
|
+
if (Array.isArray(artifact.attachments)) await Promise.all(artifact.attachments.map((attachment) => this.resolveTestAttachment(entity, attachment)));
|
|
11390
|
+
entity.task.artifacts.push(artifact);
|
|
11391
|
+
await this.vitest.report("onTestCaseArtifactRecord", entity, artifact);
|
|
11392
|
+
return artifact;
|
|
10852
11393
|
}
|
|
10853
11394
|
async updated(update, events) {
|
|
10854
11395
|
this.syncUpdateStacks(update);
|
|
@@ -10935,15 +11476,14 @@ class TestRun {
|
|
|
10935
11476
|
}
|
|
10936
11477
|
}
|
|
10937
11478
|
}
|
|
10938
|
-
async resolveTestAttachment(test,
|
|
11479
|
+
async resolveTestAttachment(test, attachment, filename) {
|
|
10939
11480
|
const project = test.project;
|
|
10940
|
-
const attachment = annotation.attachment;
|
|
10941
11481
|
if (!attachment) return attachment;
|
|
10942
11482
|
const path = attachment.path;
|
|
10943
11483
|
if (path && !path.startsWith("http://") && !path.startsWith("https://")) {
|
|
10944
11484
|
const currentPath = resolve(project.config.root, path);
|
|
10945
11485
|
const hash = createHash("sha1").update(currentPath).digest("hex");
|
|
10946
|
-
const newPath = resolve(project.config.attachmentsDir, `${sanitizeFilePath(
|
|
11486
|
+
const newPath = resolve(project.config.attachmentsDir, `${filename ? `${sanitizeFilePath(filename)}-` : ""}${hash}${extname(currentPath)}`);
|
|
10947
11487
|
if (!existsSync(project.config.attachmentsDir)) await mkdir(project.config.attachmentsDir, { recursive: true });
|
|
10948
11488
|
await copyFile(currentPath, newPath);
|
|
10949
11489
|
attachment.path = newPath;
|
|
@@ -11166,6 +11706,7 @@ class Vitest {
|
|
|
11166
11706
|
/** @internal */ filenamePattern;
|
|
11167
11707
|
/** @internal */ runningPromise;
|
|
11168
11708
|
/** @internal */ closingPromise;
|
|
11709
|
+
/** @internal */ cancelPromise;
|
|
11169
11710
|
/** @internal */ isCancelling = false;
|
|
11170
11711
|
/** @internal */ coreWorkspaceProject;
|
|
11171
11712
|
/** @internal */ _browserSessions = new BrowserSessions();
|
|
@@ -11175,7 +11716,9 @@ class Vitest {
|
|
|
11175
11716
|
/** @internal */ _testRun = void 0;
|
|
11176
11717
|
/** @internal */ _resolver;
|
|
11177
11718
|
/** @internal */ _fetcher;
|
|
11719
|
+
/** @internal */ _fsCache;
|
|
11178
11720
|
/** @internal */ _tmpDir = join(tmpdir(), nanoid());
|
|
11721
|
+
/** @internal */ _traces;
|
|
11179
11722
|
isFirstRun = true;
|
|
11180
11723
|
restartsCount = 0;
|
|
11181
11724
|
specifications;
|
|
@@ -11197,7 +11740,7 @@ class Vitest {
|
|
|
11197
11740
|
_onRestartListeners = [];
|
|
11198
11741
|
_onClose = [];
|
|
11199
11742
|
_onSetServer = [];
|
|
11200
|
-
_onCancelListeners =
|
|
11743
|
+
_onCancelListeners = /* @__PURE__ */ new Set();
|
|
11201
11744
|
_onUserTestsRerun = [];
|
|
11202
11745
|
_onFilterWatchedSpecification = [];
|
|
11203
11746
|
/**
|
|
@@ -11254,15 +11797,19 @@ class Vitest {
|
|
|
11254
11797
|
const resolved = resolveConfig(this, options, server.config);
|
|
11255
11798
|
this._config = resolved;
|
|
11256
11799
|
this._state = new StateManager({ onUnhandledError: resolved.onUnhandledError });
|
|
11257
|
-
this._cache = new VitestCache(this.
|
|
11800
|
+
this._cache = new VitestCache(this.logger);
|
|
11258
11801
|
this._snapshot = new SnapshotManager({ ...resolved.snapshotOptions });
|
|
11259
11802
|
this._testRun = new TestRun(this);
|
|
11803
|
+
const otelSdkPath = resolved.experimental.openTelemetry?.sdkPath;
|
|
11804
|
+
this._traces = new Traces({
|
|
11805
|
+
enabled: !!resolved.experimental.openTelemetry?.enabled,
|
|
11806
|
+
sdkPath: otelSdkPath,
|
|
11807
|
+
watchMode: resolved.watch
|
|
11808
|
+
});
|
|
11260
11809
|
if (this.config.watch) this.watcher.registerWatcher();
|
|
11261
11810
|
this._resolver = new VitestResolver(server.config.cacheDir, resolved);
|
|
11262
|
-
this.
|
|
11263
|
-
|
|
11264
|
-
readFromDump: this.config.server.debug?.load ?? process.env.VITEST_DEBUG_LOAD_DUMP != null
|
|
11265
|
-
});
|
|
11811
|
+
this._fsCache = new FileSystemModuleCache(this);
|
|
11812
|
+
this._fetcher = createFetchModuleFunction(this._resolver, this._config, this._fsCache, this._traces, this._tmpDir);
|
|
11266
11813
|
const environment = server.environments.__vitest__;
|
|
11267
11814
|
this.runner = new ServerModuleRunner(environment, this._fetcher, resolved);
|
|
11268
11815
|
if (this.config.watch) {
|
|
@@ -11295,7 +11842,8 @@ class Vitest {
|
|
|
11295
11842
|
return project.vite.config.getSortedPluginHooks("configureVitest").map((hook) => hook({
|
|
11296
11843
|
project,
|
|
11297
11844
|
vitest: this,
|
|
11298
|
-
injectTestProjects: this.injectTestProject
|
|
11845
|
+
injectTestProjects: this.injectTestProject,
|
|
11846
|
+
experimental_defineCacheKeyGenerator: (callback) => this._fsCache.defineCacheKeyGenerator(callback)
|
|
11299
11847
|
}));
|
|
11300
11848
|
}));
|
|
11301
11849
|
if (this._cliOptions.browser?.enabled) {
|
|
@@ -11313,7 +11861,8 @@ class Vitest {
|
|
|
11313
11861
|
if (!this.coreWorkspaceProject) this.coreWorkspaceProject = TestProject._createBasicProject(this);
|
|
11314
11862
|
if (this.config.testNamePattern) this.configOverride.testNamePattern = this.config.testNamePattern;
|
|
11315
11863
|
this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray(resolved.benchmark?.reporters), this.runner) : await createReporters(resolved.reporters, this);
|
|
11316
|
-
await
|
|
11864
|
+
await this._fsCache.ensureCacheIntegrity();
|
|
11865
|
+
await Promise.all([...this._onSetServer.map((fn) => fn()), this._traces.waitInit()]);
|
|
11317
11866
|
}
|
|
11318
11867
|
/** @internal */
|
|
11319
11868
|
get coverageProvider() {
|
|
@@ -11325,10 +11874,19 @@ class Vitest {
|
|
|
11325
11874
|
this.configOverride.coverage.enabled = true;
|
|
11326
11875
|
await this.createCoverageProvider();
|
|
11327
11876
|
await this.coverageProvider?.onEnabled?.();
|
|
11877
|
+
// onFileTransform is the only thing that affects hash
|
|
11878
|
+
if (this.coverageProvider?.onFileTransform) this.clearAllCachePaths();
|
|
11328
11879
|
}
|
|
11329
11880
|
disableCoverage() {
|
|
11330
11881
|
this.configOverride.coverage ??= {};
|
|
11331
11882
|
this.configOverride.coverage.enabled = false;
|
|
11883
|
+
// onFileTransform is the only thing that affects hash
|
|
11884
|
+
if (this.coverageProvider?.onFileTransform) this.clearAllCachePaths();
|
|
11885
|
+
}
|
|
11886
|
+
clearAllCachePaths() {
|
|
11887
|
+
this.projects.forEach(({ vite, browser }) => {
|
|
11888
|
+
[...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})].forEach((environment) => this._fsCache.invalidateAllCachePaths(environment));
|
|
11889
|
+
});
|
|
11332
11890
|
}
|
|
11333
11891
|
_coverageOverrideCache = /* @__PURE__ */ new WeakMap();
|
|
11334
11892
|
/** @internal */
|
|
@@ -11424,33 +11982,43 @@ class Vitest {
|
|
|
11424
11982
|
return this._coverageProvider;
|
|
11425
11983
|
}
|
|
11426
11984
|
/**
|
|
11985
|
+
* Deletes all Vitest caches, including `experimental.fsModuleCache`.
|
|
11986
|
+
* @experimental
|
|
11987
|
+
*/
|
|
11988
|
+
async experimental_clearCache() {
|
|
11989
|
+
await this.cache.results.clearCache();
|
|
11990
|
+
await this._fsCache.clearCache();
|
|
11991
|
+
}
|
|
11992
|
+
/**
|
|
11427
11993
|
* Merge reports from multiple runs located in the specified directory (value from `--merge-reports` if not specified).
|
|
11428
11994
|
*/
|
|
11429
11995
|
async mergeReports(directory) {
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
const
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11996
|
+
return this._traces.$("vitest.merge_reports", async () => {
|
|
11997
|
+
if (this.reporters.some((r) => r instanceof BlobReporter)) throw new Error("Cannot merge reports when `--reporter=blob` is used. Remove blob reporter from the config first.");
|
|
11998
|
+
const { files, errors, coverages, executionTimes } = await readBlobs(this.version, directory || this.config.mergeReports, this.projects);
|
|
11999
|
+
this.state.blobs = {
|
|
12000
|
+
files,
|
|
12001
|
+
errors,
|
|
12002
|
+
coverages,
|
|
12003
|
+
executionTimes
|
|
12004
|
+
};
|
|
12005
|
+
await this.report("onInit", this);
|
|
12006
|
+
const specifications = [];
|
|
12007
|
+
for (const file of files) {
|
|
12008
|
+
const specification = this.getProjectByName(file.projectName || "").createSpecification(file.filepath, void 0, file.pool);
|
|
12009
|
+
specifications.push(specification);
|
|
12010
|
+
}
|
|
12011
|
+
await this._testRun.start(specifications).catch(noop);
|
|
12012
|
+
for (const file of files) await this._reportFileTask(file);
|
|
12013
|
+
this._checkUnhandledErrors(errors);
|
|
12014
|
+
await this._testRun.end(specifications, errors).catch(noop);
|
|
12015
|
+
await this.initCoverageProvider();
|
|
12016
|
+
await this.coverageProvider?.mergeReports?.(coverages);
|
|
12017
|
+
return {
|
|
12018
|
+
testModules: this.state.getTestModules(),
|
|
12019
|
+
unhandledErrors: this.state.getUnhandledErrors()
|
|
12020
|
+
};
|
|
12021
|
+
});
|
|
11454
12022
|
}
|
|
11455
12023
|
/**
|
|
11456
12024
|
* Returns the seed, if tests are running in a random order.
|
|
@@ -11472,13 +12040,25 @@ class Vitest {
|
|
|
11472
12040
|
await this._testRun.updated(packs, events).catch(noop);
|
|
11473
12041
|
}
|
|
11474
12042
|
async collect(filters) {
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
12043
|
+
return this._traces.$("vitest.collect", async (collectSpan) => {
|
|
12044
|
+
const filenamePattern = filters && filters?.length > 0 ? filters : [];
|
|
12045
|
+
collectSpan.setAttribute("vitest.collect.filters", filenamePattern);
|
|
12046
|
+
const files = await this._traces.$("vitest.config.resolve_include_glob", async () => {
|
|
12047
|
+
const specifications = await this.specifications.getRelevantTestSpecifications(filters);
|
|
12048
|
+
collectSpan.setAttribute("vitest.collect.specifications", specifications.map((s) => {
|
|
12049
|
+
const relativeModuleId = relative(s.project.config.root, s.moduleId);
|
|
12050
|
+
if (s.project.name) return `|${s.project.name}| ${relativeModuleId}`;
|
|
12051
|
+
return relativeModuleId;
|
|
12052
|
+
}));
|
|
12053
|
+
return specifications;
|
|
12054
|
+
});
|
|
12055
|
+
// if run with --changed, don't exit if no tests are found
|
|
12056
|
+
if (!files.length) return {
|
|
12057
|
+
testModules: [],
|
|
12058
|
+
unhandledErrors: []
|
|
12059
|
+
};
|
|
12060
|
+
return this.collectTests(files);
|
|
12061
|
+
});
|
|
11482
12062
|
}
|
|
11483
12063
|
/**
|
|
11484
12064
|
* Returns the list of test files that match the config and filters.
|
|
@@ -11496,49 +12076,67 @@ class Vitest {
|
|
|
11496
12076
|
* @param filters String filters to match the test files
|
|
11497
12077
|
*/
|
|
11498
12078
|
async start(filters) {
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
await this.
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
12079
|
+
return this._traces.$("vitest.start", async (startSpan) => {
|
|
12080
|
+
startSpan.setAttributes({ config: this.vite.config.configFile });
|
|
12081
|
+
try {
|
|
12082
|
+
await this._traces.$("vitest.coverage.init", async () => {
|
|
12083
|
+
await this.initCoverageProvider();
|
|
12084
|
+
await this.coverageProvider?.clean(this._coverageOptions.clean);
|
|
12085
|
+
});
|
|
12086
|
+
} finally {
|
|
12087
|
+
await this.report("onInit", this);
|
|
12088
|
+
}
|
|
12089
|
+
this.filenamePattern = filters && filters?.length > 0 ? filters : void 0;
|
|
12090
|
+
startSpan.setAttribute("vitest.start.filters", this.filenamePattern || []);
|
|
12091
|
+
const files = await this._traces.$("vitest.config.resolve_include_glob", async () => {
|
|
12092
|
+
const specifications = await this.specifications.getRelevantTestSpecifications(filters);
|
|
12093
|
+
startSpan.setAttribute("vitest.start.specifications", specifications.map((s) => {
|
|
12094
|
+
const relativeModuleId = relative(s.project.config.root, s.moduleId);
|
|
12095
|
+
if (s.project.name) return `|${s.project.name}| ${relativeModuleId}`;
|
|
12096
|
+
return relativeModuleId;
|
|
12097
|
+
}));
|
|
12098
|
+
return specifications;
|
|
12099
|
+
});
|
|
12100
|
+
// if run with --changed, don't exit if no tests are found
|
|
12101
|
+
if (!files.length) {
|
|
12102
|
+
await this._traces.$("vitest.test_run", async () => {
|
|
12103
|
+
await this._testRun.start([]);
|
|
12104
|
+
const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true });
|
|
12105
|
+
await this._testRun.end([], [], coverage);
|
|
12106
|
+
// Report coverage for uncovered files
|
|
12107
|
+
await this.reportCoverage(coverage, true);
|
|
12108
|
+
});
|
|
12109
|
+
if (!this.config.watch || !(this.config.changed || this.config.related?.length)) throw new FilesNotFoundError(this.mode);
|
|
12110
|
+
}
|
|
12111
|
+
let testModules = {
|
|
12112
|
+
testModules: [],
|
|
12113
|
+
unhandledErrors: []
|
|
12114
|
+
};
|
|
12115
|
+
if (files.length) {
|
|
12116
|
+
// populate once, update cache on watch
|
|
12117
|
+
await this.cache.stats.populateStats(this.config.root, files);
|
|
12118
|
+
testModules = await this.runFiles(files, true);
|
|
12119
|
+
}
|
|
12120
|
+
if (this.config.watch) await this.report("onWatcherStart");
|
|
12121
|
+
return testModules;
|
|
12122
|
+
});
|
|
11527
12123
|
}
|
|
11528
12124
|
/**
|
|
11529
12125
|
* Initialize reporters and the coverage provider. This method doesn't run any tests.
|
|
11530
12126
|
* If the `--watch` flag is provided, Vitest will still run changed tests even if this method was not called.
|
|
11531
12127
|
*/
|
|
11532
12128
|
async init() {
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
12129
|
+
await this._traces.$("vitest.init", async () => {
|
|
12130
|
+
try {
|
|
12131
|
+
await this.initCoverageProvider();
|
|
12132
|
+
await this.coverageProvider?.clean(this._coverageOptions.clean);
|
|
12133
|
+
} finally {
|
|
12134
|
+
await this.report("onInit", this);
|
|
12135
|
+
}
|
|
12136
|
+
// populate test files cache so watch mode can trigger a file rerun
|
|
12137
|
+
await this.globTestSpecifications();
|
|
12138
|
+
if (this.config.watch) await this.report("onWatcherStart");
|
|
12139
|
+
});
|
|
11542
12140
|
}
|
|
11543
12141
|
/**
|
|
11544
12142
|
* If there is a test run happening, returns a promise that will
|
|
@@ -11588,50 +12186,53 @@ class Vitest {
|
|
|
11588
12186
|
return result;
|
|
11589
12187
|
}
|
|
11590
12188
|
async runFiles(specs, allTestsRun) {
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
const invalidates = Array.from(this.watcher.invalidates);
|
|
11601
|
-
this.watcher.invalidates.clear();
|
|
11602
|
-
this.snapshot.clear();
|
|
11603
|
-
this.state.clearErrors();
|
|
11604
|
-
if (!this.isFirstRun && this._coverageOptions.cleanOnRerun) await this.coverageProvider?.clean();
|
|
11605
|
-
await this.initializeGlobalSetup(specs);
|
|
12189
|
+
return this._traces.$("vitest.test_run", async () => {
|
|
12190
|
+
await this._testRun.start(specs);
|
|
12191
|
+
// previous run
|
|
12192
|
+
await this.cancelPromise;
|
|
12193
|
+
await this.runningPromise;
|
|
12194
|
+
this._onCancelListeners.clear();
|
|
12195
|
+
this.isCancelling = false;
|
|
12196
|
+
// schedule the new run
|
|
12197
|
+
this.runningPromise = (async () => {
|
|
11606
12198
|
try {
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
this.
|
|
12199
|
+
if (!this.pool) this.pool = createPool(this);
|
|
12200
|
+
const invalidates = Array.from(this.watcher.invalidates);
|
|
12201
|
+
this.watcher.invalidates.clear();
|
|
12202
|
+
this.snapshot.clear();
|
|
12203
|
+
this.state.clearErrors();
|
|
12204
|
+
if (!this.isFirstRun && this._coverageOptions.cleanOnRerun) await this.coverageProvider?.clean();
|
|
12205
|
+
await this.initializeGlobalSetup(specs);
|
|
12206
|
+
try {
|
|
12207
|
+
await this.pool.runTests(specs, invalidates);
|
|
12208
|
+
} catch (err) {
|
|
12209
|
+
this.state.catchError(err, "Unhandled Error");
|
|
12210
|
+
}
|
|
12211
|
+
const files = this.state.getFiles();
|
|
12212
|
+
this.cache.results.updateResults(files);
|
|
12213
|
+
try {
|
|
12214
|
+
await this.cache.results.writeToCache();
|
|
12215
|
+
} catch {}
|
|
12216
|
+
return {
|
|
12217
|
+
testModules: this.state.getTestModules(),
|
|
12218
|
+
unhandledErrors: this.state.getUnhandledErrors()
|
|
12219
|
+
};
|
|
12220
|
+
} finally {
|
|
12221
|
+
const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun });
|
|
12222
|
+
const errors = this.state.getUnhandledErrors();
|
|
12223
|
+
this._checkUnhandledErrors(errors);
|
|
12224
|
+
await this._testRun.end(specs, errors, coverage);
|
|
12225
|
+
await this.reportCoverage(coverage, allTestsRun);
|
|
11610
12226
|
}
|
|
11611
|
-
|
|
11612
|
-
this.
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
};
|
|
11620
|
-
} finally {
|
|
11621
|
-
const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun });
|
|
11622
|
-
const errors = this.state.getUnhandledErrors();
|
|
11623
|
-
this._checkUnhandledErrors(errors);
|
|
11624
|
-
await this._testRun.end(specs, errors, coverage);
|
|
11625
|
-
await this.reportCoverage(coverage, allTestsRun);
|
|
11626
|
-
}
|
|
11627
|
-
})().finally(() => {
|
|
11628
|
-
this.runningPromise = void 0;
|
|
11629
|
-
this.isFirstRun = false;
|
|
11630
|
-
// all subsequent runs will treat this as a fresh run
|
|
11631
|
-
this.config.changed = false;
|
|
11632
|
-
this.config.related = void 0;
|
|
12227
|
+
})().finally(() => {
|
|
12228
|
+
this.runningPromise = void 0;
|
|
12229
|
+
this.isFirstRun = false;
|
|
12230
|
+
// all subsequent runs will treat this as a fresh run
|
|
12231
|
+
this.config.changed = false;
|
|
12232
|
+
this.config.related = void 0;
|
|
12233
|
+
});
|
|
12234
|
+
return await this.runningPromise;
|
|
11633
12235
|
});
|
|
11634
|
-
return await this.runningPromise;
|
|
11635
12236
|
}
|
|
11636
12237
|
async experimental_parseSpecifications(specifications, options) {
|
|
11637
12238
|
if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecifications\` does not support "${this.mode}" mode.`);
|
|
@@ -11656,8 +12257,9 @@ class Vitest {
|
|
|
11656
12257
|
const filepaths = specifications.map((spec) => spec.moduleId);
|
|
11657
12258
|
this.state.collectPaths(filepaths);
|
|
11658
12259
|
// previous run
|
|
12260
|
+
await this.cancelPromise;
|
|
11659
12261
|
await this.runningPromise;
|
|
11660
|
-
this._onCancelListeners
|
|
12262
|
+
this._onCancelListeners.clear();
|
|
11661
12263
|
this.isCancelling = false;
|
|
11662
12264
|
// schedule the new run
|
|
11663
12265
|
this.runningPromise = (async () => {
|
|
@@ -11692,7 +12294,8 @@ class Vitest {
|
|
|
11692
12294
|
*/
|
|
11693
12295
|
async cancelCurrentRun(reason) {
|
|
11694
12296
|
this.isCancelling = true;
|
|
11695
|
-
|
|
12297
|
+
this.cancelPromise = Promise.all([...this._onCancelListeners].map((listener) => listener(reason)));
|
|
12298
|
+
await this.cancelPromise.finally(() => this.cancelPromise = void 0);
|
|
11696
12299
|
await this.runningPromise;
|
|
11697
12300
|
}
|
|
11698
12301
|
/** @internal */
|
|
@@ -11817,6 +12420,7 @@ class Vitest {
|
|
|
11817
12420
|
async scheduleRerun(triggerId) {
|
|
11818
12421
|
const currentCount = this.restartsCount;
|
|
11819
12422
|
clearTimeout(this._rerunTimer);
|
|
12423
|
+
await this.cancelPromise;
|
|
11820
12424
|
await this.runningPromise;
|
|
11821
12425
|
clearTimeout(this._rerunTimer);
|
|
11822
12426
|
// server restarted
|
|
@@ -11854,8 +12458,14 @@ class Vitest {
|
|
|
11854
12458
|
*/
|
|
11855
12459
|
invalidateFile(filepath) {
|
|
11856
12460
|
this.projects.forEach(({ vite, browser }) => {
|
|
11857
|
-
[...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})].forEach((
|
|
11858
|
-
moduleGraph
|
|
12461
|
+
[...Object.values(vite.environments), ...Object.values(browser?.vite.environments || {})].forEach((environment) => {
|
|
12462
|
+
const { moduleGraph } = environment;
|
|
12463
|
+
const modules = moduleGraph.getModulesByFile(filepath);
|
|
12464
|
+
if (!modules) return;
|
|
12465
|
+
modules.forEach((module) => {
|
|
12466
|
+
moduleGraph.invalidateModule(module);
|
|
12467
|
+
this._fsCache.invalidateCachePath(environment, module.id);
|
|
12468
|
+
});
|
|
11859
12469
|
});
|
|
11860
12470
|
});
|
|
11861
12471
|
}
|
|
@@ -11893,11 +12503,12 @@ class Vitest {
|
|
|
11893
12503
|
this.pool = void 0;
|
|
11894
12504
|
})());
|
|
11895
12505
|
closePromises.push(...this._onClose.map((fn) => fn()));
|
|
11896
|
-
|
|
12506
|
+
await Promise.allSettled(closePromises).then((results) => {
|
|
11897
12507
|
results.forEach((r) => {
|
|
11898
12508
|
if (r.status === "rejected") this.logger.error("error during close", r.reason);
|
|
11899
12509
|
});
|
|
11900
12510
|
});
|
|
12511
|
+
await this._traces?.finish();
|
|
11901
12512
|
})();
|
|
11902
12513
|
return this.closingPromise;
|
|
11903
12514
|
}
|
|
@@ -11950,7 +12561,10 @@ class Vitest {
|
|
|
11950
12561
|
* Register a handler that will be called when the test run is cancelled with `vitest.cancelCurrentRun`.
|
|
11951
12562
|
*/
|
|
11952
12563
|
onCancel(fn) {
|
|
11953
|
-
this._onCancelListeners.
|
|
12564
|
+
this._onCancelListeners.add(fn);
|
|
12565
|
+
return () => {
|
|
12566
|
+
this._onCancelListeners.delete(fn);
|
|
12567
|
+
};
|
|
11954
12568
|
}
|
|
11955
12569
|
/**
|
|
11956
12570
|
* Register a handler that will be called when the server is closed.
|
|
@@ -12091,7 +12705,7 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
|
|
|
12091
12705
|
options.api = resolveApiServerConfig(options, defaultPort);
|
|
12092
12706
|
// we replace every "import.meta.env" with "process.env"
|
|
12093
12707
|
// to allow reassigning, so we need to put all envs on process.env
|
|
12094
|
-
const { PROD, DEV
|
|
12708
|
+
const { PROD, DEV, ...envs } = viteConfig.env;
|
|
12095
12709
|
// process.env can have only string values and will cast string on it if we pass other type,
|
|
12096
12710
|
// so we are making them truthy
|
|
12097
12711
|
process.env.PROD ??= PROD ? "1" : "";
|
|
@@ -12143,7 +12757,7 @@ async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {
|
|
|
12143
12757
|
const root = slash(resolve$1(options.root || process.cwd()));
|
|
12144
12758
|
const configPath = options.config === false ? false : options.config ? resolveModule(options.config, { paths: [root] }) ?? resolve$1(root, options.config) : any(configFiles, { cwd: root });
|
|
12145
12759
|
options.config = configPath;
|
|
12146
|
-
const { browser: _removeBrowser
|
|
12760
|
+
const { browser: _removeBrowser, ...restOptions } = options;
|
|
12147
12761
|
const server = await createViteServer(mergeConfig({
|
|
12148
12762
|
configFile: configPath,
|
|
12149
12763
|
configLoader: options.configLoader,
|
|
@@ -12471,7 +13085,8 @@ async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, v
|
|
|
12471
13085
|
else ctx.start(cliFilters);
|
|
12472
13086
|
});
|
|
12473
13087
|
try {
|
|
12474
|
-
if (ctx.config.
|
|
13088
|
+
if (ctx.config.clearCache) await ctx.experimental_clearCache();
|
|
13089
|
+
else if (ctx.config.mergeReports) await ctx.mergeReports();
|
|
12475
13090
|
else if (ctx.config.standalone) await ctx.init();
|
|
12476
13091
|
else await ctx.start(cliFilters);
|
|
12477
13092
|
} catch (e) {
|