@cldmv/vitest-runner 1.4.2 → 1.4.3

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/index.mjs CHANGED
@@ -1,1335 +1,47 @@
1
- // src/runner.mjs
2
- import fs5 from "fs/promises";
3
- import path5 from "path";
4
- import chalk3 from "chalk";
5
-
6
- // src/utils/resolve.mjs
7
- import { createRequire } from "module";
8
- import path from "path";
9
- import fs from "fs/promises";
10
- import { pathToFileURL } from "url";
11
- function resolveBin(cwd, pkgName, binName = pkgName) {
12
- const require2 = createRequire(pathToFileURL(path.join(cwd, "package.json")));
13
- const pkgJsonPath = require2.resolve(`${pkgName}/package.json`);
14
- const pkg = require2(pkgJsonPath);
15
- const rel = pkg.bin?.[binName] ?? pkg.bin;
16
- if (!rel) throw new Error(`No bin "${binName}" found in ${pkgName}/package.json`);
17
- return path.join(path.dirname(pkgJsonPath), rel);
18
- }
19
- var DEFAULT_CONFIG_NAMES = Object.freeze([
20
- "vitest.config.ts",
21
- "vitest.config.mts",
22
- "vitest.config.cts",
23
- "vitest.config.mjs",
24
- "vitest.config.js",
25
- "vitest.config.cjs",
26
- "vite.config.ts",
27
- "vite.config.mts",
28
- "vite.config.mjs",
29
- "vite.config.js"
30
- ]);
31
- async function resolveVitestConfig(cwd, configPath) {
32
- if (configPath) {
33
- return path.isAbsolute(configPath) ? configPath : path.resolve(cwd, configPath);
34
- }
35
- for (const name of DEFAULT_CONFIG_NAMES) {
36
- const candidate = path.join(cwd, name);
37
- try {
38
- await fs.access(candidate);
39
- return candidate;
40
- } catch {
41
- }
42
- }
43
- return void 0;
44
- }
45
-
46
- // src/core/discover.mjs
47
- import fs2 from "fs/promises";
48
- import path2 from "path";
49
- var DEFAULT_TEST_FILE_PATTERN = /\.test\.vitest\.(?:js|mjs|cjs)$/i;
50
- async function discoverFilesInDir(dir, cwd, pattern = DEFAULT_TEST_FILE_PATTERN) {
51
- const queue = [dir];
52
- const files = [];
53
- while (queue.length) {
54
- const current = queue.pop();
55
- let entries;
56
- try {
57
- entries = await fs2.readdir(current, { withFileTypes: true });
58
- } catch {
59
- continue;
60
- }
61
- for (const entry of entries) {
62
- if (entry.isDirectory()) {
63
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
64
- queue.push(path2.join(current, entry.name));
65
- continue;
66
- }
67
- if (entry.isFile() && pattern.test(entry.name)) {
68
- files.push(path2.relative(cwd, path2.join(current, entry.name)));
69
- }
70
- }
71
- }
72
- return files;
73
- }
74
- function sortWithPriority(files, earlyRunPatterns = []) {
75
- const early = [];
76
- const rest = [];
77
- for (const file of files) {
78
- const normalized = file.replace(/\\/g, "/");
79
- const priorityIndex = earlyRunPatterns.findIndex((pat) => normalized.includes(pat));
80
- if (priorityIndex !== -1) {
81
- early.push({ file, priorityIndex });
82
- } else {
83
- rest.push(file);
84
- }
85
- }
86
- early.sort((a, b) => a.priorityIndex - b.priorityIndex || a.file.localeCompare(b.file));
87
- rest.sort((a, b) => a.localeCompare(b));
88
- return [...early.map((e) => e.file), ...rest];
89
- }
90
- function computeFilterConflicts(files) {
91
- const normalized = files.map((f) => f.replace(/\\/g, "/"));
92
- const conflicts = /* @__PURE__ */ new Map();
93
- for (let i = 0; i < files.length; i++) {
94
- const matches = [];
95
- for (let j = 0; j < files.length; j++) {
96
- if (i === j) continue;
97
- if (normalized[j].includes(normalized[i])) matches.push(files[j]);
98
- }
99
- conflicts.set(files[i], matches);
100
- }
101
- return conflicts;
102
- }
103
- async function discoverVitestFiles(opts) {
104
- const { cwd, testDir, testPatterns = [], testListFile, testFilePattern = DEFAULT_TEST_FILE_PATTERN, earlyRunPatterns = [] } = opts;
105
- const resolvedTestDir = testDir ? path2.isAbsolute(testDir) ? testDir : path2.resolve(cwd, testDir) : cwd;
106
- if (testListFile) {
107
- const resolvedListPath = path2.isAbsolute(testListFile) ? testListFile : path2.resolve(cwd, testListFile);
108
- let testList;
109
- try {
110
- const content = await fs2.readFile(resolvedListPath, "utf8");
111
- testList = JSON.parse(content);
112
- } catch (err) {
113
- throw new Error(`Failed to read test list file "${resolvedListPath}": ${err.message}`, { cause: err });
114
- }
115
- if (!Array.isArray(testList)) {
116
- throw new Error(`Test list file "${resolvedListPath}" must contain a JSON array of test file paths`);
117
- }
118
- console.log(`\u{1F4CB} Loading test list from: ${path2.relative(cwd, resolvedListPath)}`);
119
- return sortWithPriority(testList, earlyRunPatterns);
120
- }
121
- if (testPatterns.length === 0) {
122
- const files2 = await discoverFilesInDir(resolvedTestDir, cwd, testFilePattern);
123
- return sortWithPriority(files2, earlyRunPatterns);
124
- }
125
- const files = [];
126
- for (const pattern of testPatterns) {
127
- const absPath = path2.isAbsolute(pattern) ? pattern : path2.resolve(cwd, pattern);
128
- let stat = null;
129
- try {
130
- stat = await fs2.stat(absPath);
131
- } catch {
132
- }
133
- if (stat?.isFile()) {
134
- if (testFilePattern.test(absPath)) {
135
- files.push(path2.relative(cwd, absPath));
136
- }
137
- } else if (stat?.isDirectory()) {
138
- files.push(...await discoverFilesInDir(absPath, cwd, testFilePattern));
139
- } else {
140
- const allFiles = await discoverFilesInDir(resolvedTestDir, cwd, testFilePattern);
141
- const matched = allFiles.filter((f) => f.replace(/\\/g, "/").includes(pattern.replace(/\\/g, "/")));
142
- if (matched.length > 0) {
143
- files.push(...matched);
144
- } else {
145
- console.warn(`\u26A0\uFE0F No matches found for: ${pattern}`);
146
- }
147
- }
148
- }
149
- return sortWithPriority([...new Set(files)], earlyRunPatterns);
150
- }
151
-
152
- // src/core/spawn.mjs
153
- import { spawn } from "child_process";
154
-
155
- // src/utils/ansi.mjs
156
- function stripAnsi(text) {
157
- return text.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
158
- }
159
- function colourPct(chalk4, pct) {
160
- const str = pct.toFixed(2).padStart(6);
161
- if (pct >= 80) return chalk4.green(str);
162
- if (pct >= 50) return chalk4.yellow(str);
163
- return chalk4.red(str);
164
- }
165
-
166
- // src/core/parse.mjs
167
- function parseVitestOutput(output) {
168
- const cleanOutput = stripAnsi(output);
169
- const result = {
170
- testFilesPass: 0,
171
- testFilesFail: 0,
172
- testsPass: 0,
173
- testsFail: 0,
174
- testsSkip: 0,
175
- duration: 0,
176
- heapMb: null,
177
- errors: []
178
- };
179
- const testFilesLineMatch = cleanOutput.match(/Test Files\s+(.+)/);
180
- if (testFilesLineMatch) {
181
- const line = testFilesLineMatch[1];
182
- const passMatch = line.match(/(\d+)\s+passed/);
183
- const failMatch = line.match(/(\d+)\s+failed/);
184
- if (passMatch) result.testFilesPass = parseInt(passMatch[1], 10);
185
- if (failMatch) result.testFilesFail = parseInt(failMatch[1], 10);
186
- }
187
- const testsLineMatch = cleanOutput.match(/^\s*Tests\s+(.+)$/m);
188
- if (testsLineMatch) {
189
- const line = testsLineMatch[1];
190
- const passMatch = line.match(/(\d+)\s+passed/);
191
- const failMatch = line.match(/(\d+)\s+failed/);
192
- const skipMatch = line.match(/(\d+)\s+skipped/);
193
- if (passMatch) result.testsPass = parseInt(passMatch[1], 10);
194
- if (failMatch) result.testsFail = parseInt(failMatch[1], 10);
195
- if (skipMatch) result.testsSkip = parseInt(skipMatch[1], 10);
196
- }
197
- const durationMatch = cleanOutput.match(/Duration\s+([\d.]+)s/);
198
- if (durationMatch) {
199
- result.duration = parseFloat(durationMatch[1]) * 1e3;
200
- }
201
- const heapMatch = cleanOutput.match(/(\d+)\s*MB\s+heap\s+used/i);
202
- if (heapMatch) {
203
- result.heapMb = parseInt(heapMatch[1], 10);
204
- }
205
- const failedSectionStart = output.indexOf("Failed Tests");
206
- if (failedSectionStart !== -1) {
207
- const failedSectionEnd = output.indexOf("\n Test Files", failedSectionStart);
208
- const errorSection = failedSectionEnd !== -1 ? output.substring(failedSectionStart, failedSectionEnd) : output.substring(failedSectionStart);
209
- const failPattern = /FAIL\s*(?:\x1B\[[0-9;]*[a-zA-Z]|\s)*tests\//g;
210
- const matches = [...errorSection.matchAll(failPattern)];
211
- for (let i = 0; i < matches.length; i++) {
212
- const matchPos = matches[i].index;
213
- const lineStart = errorSection.lastIndexOf("\n", matchPos);
214
- const actualStart = lineStart === -1 ? 0 : lineStart;
215
- const matchEnd = i < matches.length - 1 ? matches[i + 1].index : errorSection.length;
216
- const nextLineStart = errorSection.lastIndexOf("\n", matchEnd);
217
- const actualEnd = nextLineStart === -1 ? matchEnd : nextLineStart;
218
- const errorBlock = errorSection.substring(actualStart, actualEnd).trim();
219
- if (errorBlock) result.errors.push(errorBlock);
220
- }
221
- }
222
- return result;
223
- }
224
- function deduplicateErrors(errors) {
225
- const fullText = errors.join("\n");
226
- const lines = fullText.split("\n");
227
- const failLineMap = /* @__PURE__ */ new Map();
228
- const lineIndices = /* @__PURE__ */ new Map();
229
- lines.forEach((line, idx) => {
230
- if (line.includes("FAIL") && line.includes("Config:")) {
231
- lineIndices.set(line, idx);
232
- const cleaned = stripAnsi(line);
233
- const match = cleaned.match(/^(.+Config:\s+)'*([^'>]+)'*(.+)$/);
234
- if (match) {
235
- const [, before, config, after] = match;
236
- const pattern = `${before.trim()}|||${after.trim()}`;
237
- if (!failLineMap.has(pattern)) {
238
- failLineMap.set(pattern, { lines: [], configs: [] });
239
- }
240
- failLineMap.get(pattern).lines.push(line);
241
- failLineMap.get(pattern).configs.push(config.replace(/'/g, ""));
242
- }
243
- }
244
- });
245
- const skipIndices = /* @__PURE__ */ new Set();
246
- for (const [, data] of failLineMap.entries()) {
247
- if (data.configs.length > 1) {
248
- for (let i = 1; i < data.lines.length; i++) {
249
- const idx = lineIndices.get(data.lines[i]);
250
- skipIndices.add(idx);
251
- }
252
- const firstLine = data.lines[0];
253
- const configArray = `[${data.configs.map((c) => `'${c}'`).join(",")}]`;
254
- const consolidated = stripAnsi(firstLine).replace(/(Config:\s+)'*[^'>]+'*/, `$1${configArray}`);
255
- const firstIdx = lineIndices.get(firstLine);
256
- lines[firstIdx] = consolidated;
257
- }
258
- }
259
- return lines.filter((_, idx) => !skipIndices.has(idx)).join("\n");
260
- }
261
-
262
- // src/utils/env.mjs
263
- function buildNodeOptions({ maxOldSpaceMb, conditions = [], base = process.env.NODE_OPTIONS ?? "" }) {
264
- let opts = base;
265
- for (const cond of conditions) {
266
- const flag = `--conditions=${cond}`;
267
- if (!opts.includes(flag)) {
268
- opts = opts ? `${opts} ${flag}` : flag;
269
- }
270
- }
271
- if (maxOldSpaceMb && !opts.includes("--max-old-space-size")) {
272
- const flag = `--max-old-space-size=${maxOldSpaceMb}`;
273
- opts = opts ? `${opts} ${flag}` : flag;
274
- }
275
- return opts.trim();
276
- }
277
-
278
- // src/core/spawn.mjs
279
- function buildEnv({ maxOldSpaceMb, conditions = [], nodeEnv = "development", extraEnv = {} }) {
280
- const env = { ...process.env };
281
- if (!env.NODE_ENV) env.NODE_ENV = nodeEnv;
282
- const nodeOptions = buildNodeOptions({ maxOldSpaceMb, conditions, base: env.NODE_OPTIONS ?? "" });
283
- if (nodeOptions) env.NODE_OPTIONS = nodeOptions;
284
- Object.assign(env, extraEnv);
285
- return env;
286
- }
287
- function buildBaseArgs(vitestBin, vitestConfig) {
288
- const configArgs = vitestConfig ? ["--config", vitestConfig] : [];
289
- return [vitestBin, ...configArgs, "run"];
290
- }
291
- function runSingleFile(filePath, opts) {
292
- const {
293
- cwd,
294
- vitestBin,
295
- vitestConfig,
296
- maxOldSpaceMb,
297
- conditions = [],
298
- nodeEnv = "development",
299
- vitestArgs = [],
300
- streamOutput = true,
301
- // Other discovered files whose path would spuriously also match Vitest's
302
- // own substring filter on `filePath` (see discover.mjs computeFilterConflicts).
303
- // Excluded explicitly so this invocation runs exactly `filePath`.
304
- excludePaths = [],
305
- // Extra env vars for the child (e.g. VITEST_RUNNER_TMP — see core/scratch.mjs).
306
- extraEnv = {}
307
- } = opts;
308
- return new Promise((resolve) => {
309
- const startTime = Date.now();
310
- const excludeArgs = excludePaths.flatMap((p) => ["--exclude", p]);
311
- const args = [...buildBaseArgs(vitestBin, vitestConfig), ...vitestArgs, ...excludeArgs, filePath];
312
- const env = buildEnv({ maxOldSpaceMb, conditions, nodeEnv, extraEnv });
313
- const child = spawn(process.execPath, args, { cwd, stdio: ["ignore", "pipe", "pipe"], env });
314
- let stdout = "";
315
- let stderr = "";
316
- child.stdout?.on("data", (data) => {
317
- stdout += data.toString();
318
- if (streamOutput) process.stdout.write(data);
319
- });
320
- child.stderr?.on("data", (data) => {
321
- stderr += data.toString();
322
- if (streamOutput) process.stderr.write(data);
323
- });
324
- child.on("close", (code) => {
325
- const spawnDuration = Date.now() - startTime;
326
- const output = `${stdout}
327
- ${stderr}`;
328
- const parsed = parseVitestOutput(output);
329
- resolve({
330
- file: filePath,
331
- code: code ?? 1,
332
- // c8 ignore next -- false branch verified manually; V8 ternary probe mismatch inside object literal
333
- duration: parsed.duration > 0 ? parsed.duration : spawnDuration,
334
- testFilesPass: parsed.testFilesPass,
335
- testFilesFail: parsed.testFilesFail,
336
- testsPass: parsed.testsPass,
337
- testsFail: parsed.testsFail,
338
- testsSkip: parsed.testsSkip,
339
- heapMb: parsed.heapMb,
340
- errors: parsed.errors,
341
- rawOutput: output
342
- });
343
- });
344
- child.on("error", (err) => {
345
- resolve({
346
- file: filePath,
347
- code: 1,
348
- duration: Date.now() - startTime,
349
- testFilesPass: 0,
350
- testFilesFail: 1,
351
- testsPass: 0,
352
- testsFail: 0,
353
- testsSkip: 0,
354
- heapMb: null,
355
- errors: [err.message],
356
- rawOutput: err.toString()
357
- });
358
- });
359
- });
360
- }
361
- function runVitestDirect(opts) {
362
- const { cwd, vitestBin, vitestConfig, maxOldSpaceMb, conditions = [], nodeEnv = "development", vitestArgs = [] } = opts;
363
- return new Promise((resolve) => {
364
- const args = [...buildBaseArgs(vitestBin, vitestConfig), ...vitestArgs];
365
- const env = buildEnv({ maxOldSpaceMb, conditions, nodeEnv });
366
- const child = spawn(process.execPath, args, { cwd, stdio: "inherit", env });
367
- child.on("close", (code) => resolve(code ?? 1));
368
- child.on("error", () => resolve(1));
369
- });
370
- }
371
- function runMergeReports(blobsDir, opts) {
372
- const {
373
- cwd,
374
- vitestBin,
375
- vitestConfig,
376
- maxOldSpaceMb,
377
- conditions = [],
378
- nodeEnv = "development",
379
- extraCoverageArgs = [],
380
- quietOutput = false
381
- } = opts;
382
- return new Promise((resolve) => {
383
- const configArgs = vitestConfig ? ["--config", vitestConfig] : [];
384
- const mergeReporterArgs = quietOutput ? ["--color"] : [];
385
- const args = [vitestBin, ...configArgs, "--mergeReports", blobsDir, "--run", "--coverage", ...mergeReporterArgs, ...extraCoverageArgs];
386
- const env = buildEnv({ maxOldSpaceMb, conditions, nodeEnv });
387
- const child = spawn(process.execPath, args, {
388
- cwd,
389
- stdio: quietOutput ? ["ignore", "pipe", "pipe"] : "inherit",
390
- env
391
- });
392
- let stdout = "";
393
- let stderr = "";
394
- if (quietOutput) {
395
- child.stdout?.on("data", (data) => stdout += data.toString());
396
- child.stderr?.on("data", (data) => stderr += data.toString());
397
- }
398
- child.on("close", (code) => resolve({ exitCode: code ?? 1, output: `${stdout}
399
- ${stderr}` }));
400
- child.on("error", () => resolve({ exitCode: 1, output: "" }));
401
- });
402
- }
403
-
404
- // src/core/progress.mjs
405
- import chalk from "chalk";
406
-
407
- // src/utils/duration.mjs
408
- function formatDuration(ms) {
409
- const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
410
- const hours = Math.floor(totalSeconds / 3600);
411
- const minutes = Math.floor(totalSeconds % 3600 / 60);
412
- const seconds = totalSeconds % 60;
413
- if (hours > 0) {
414
- return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
415
- }
416
- return `${minutes}:${seconds.toString().padStart(2, "0")}`;
417
- }
418
-
419
- // src/core/progress.mjs
420
- function createCoverageProgressTracker(total) {
421
- const isTTY = Boolean(process.stdout.isTTY);
422
- const spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
423
- const barWidth = 26;
424
- const startTime = Date.now();
425
- let completed = 0;
426
- let active = 0;
427
- let failed = 0;
428
- let frameIndex = 0;
429
- let maxLineLength = 0;
430
- let lastPlainLog = 0;
431
- let spinnerTimer = null;
432
- function buildLine() {
433
- const percent = total === 0 ? 100 : completed / total * 100;
434
- const elapsedMs = Date.now() - startTime;
435
- const avgPerFile = completed > 0 ? elapsedMs / completed : 0;
436
- const etaMs = avgPerFile * Math.max(total - completed, 0);
437
- const filled = Math.round(percent / 100 * barWidth);
438
- const spinner = spinnerFrames[frameIndex % spinnerFrames.length];
439
- const bar = `[${"=".repeat(filled)}${"-".repeat(Math.max(0, barWidth - filled))}]`;
440
- let _percent = percent.toFixed(1).padStart(5);
441
- if (percent < 30) {
442
- _percent = chalk.red(_percent + "%");
443
- } else if (percent < 70) {
444
- _percent = chalk.rgb(255, 136, 0)(_percent + "%");
445
- } else if (percent > 99) {
446
- _percent = chalk.green(_percent + "%");
447
- } else {
448
- _percent = chalk.yellow(_percent + "%");
449
- }
450
- if (isTTY) {
451
- return `${chalk.green(spinner)} ${chalk.green(bar)} ${chalk.bold(_percent)} ${completed}/${total} | active ${active} | failed ${failed} | ETA ${formatDuration(etaMs)} | elapsed ${formatDuration(elapsedMs)}`;
452
- }
453
- return `progress ${percent.toFixed(1)}% ${completed}/${total} | active ${active} | failed ${failed} | eta ${formatDuration(etaMs)} | elapsed ${formatDuration(elapsedMs)}`;
454
- }
455
- function render(forcePlainLog = false) {
456
- const line = buildLine();
457
- frameIndex++;
458
- if (isTTY) {
459
- const visibleLength = line.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").length;
460
- maxLineLength = Math.max(maxLineLength, visibleLength);
461
- process.stdout.write(`\r${line.padEnd(maxLineLength, " ")}`);
462
- return;
463
- }
464
- const now = Date.now();
465
- if (forcePlainLog || now - lastPlainLog >= 2e3) {
466
- console.log(line);
467
- lastPlainLog = now;
468
- }
469
- }
470
- function startSpinnerLoop() {
471
- if (!isTTY || spinnerTimer) return;
472
- spinnerTimer = setInterval(() => {
473
- if (completed >= total && active === 0) return;
474
- render();
475
- }, 120);
476
- spinnerTimer.unref?.();
477
- }
478
- function stopSpinnerLoop() {
479
- if (!spinnerTimer) return;
480
- clearInterval(spinnerTimer);
481
- spinnerTimer = null;
482
- }
483
- render(true);
484
- startSpinnerLoop();
485
- return {
486
- /**
487
- * Call when a file run starts (increments active count).
488
- * @returns {void}
489
- */
490
- onStart() {
491
- active++;
492
- render();
493
- },
494
- /**
495
- * Call when a file run completes.
496
- * @param {boolean} failedRun - Whether the run exited with a non-zero code.
497
- * @returns {void}
498
- */
499
- onComplete(failedRun) {
500
- active = Math.max(0, active - 1);
501
- completed++;
502
- if (failedRun) failed++;
503
- render(true);
504
- },
505
- /**
506
- * Stop the spinner and print the final progress line.
507
- * @returns {void}
508
- */
509
- finish() {
510
- stopSpinnerLoop();
511
- render(true);
512
- if (isTTY) process.stdout.write("\n");
513
- }
514
- };
515
- }
516
- var noopProgressTracker = {
517
- /** @returns {void} */
518
- onStart() {
519
- },
520
- /** @param {boolean} ___failedRun @returns {void} */
521
- onComplete(___failedRun) {
522
- },
523
- /** @returns {void} */
524
- finish() {
525
- }
526
- };
527
-
528
- // src/core/report.mjs
529
- import fs3 from "fs/promises";
530
- import path3 from "path";
531
- import chalk2 from "chalk";
532
- function printQuietCoverageFailureDetails(failedResults) {
533
- if (failedResults.length === 0) return;
534
- console.log(`
535
- ${"=".repeat(80)}`);
536
- console.log(chalk2.bold.red("\u2716 FAILED TEST FILES (VERBOSE OUTPUT)"));
537
- console.log("=".repeat(80));
538
- for (const r of failedResults) {
539
- console.log(`
540
- ${chalk2.red("\u2716")} ${chalk2.red(r.file)} ${chalk2.dim(`(exit ${r.code})`)}`);
541
- if (r.rawOutput?.trim()) {
542
- console.log(r.rawOutput.trimEnd());
543
- } else {
544
- console.log(chalk2.dim("(no child output captured)"));
545
- }
546
- }
547
- console.log(`
548
- ${"=".repeat(80)}`);
549
- }
550
- function printMergeOutput(exitCode, output) {
551
- if (exitCode === 0) {
552
- const marker = "% Coverage report from v8";
553
- const rawLines = output.split("\n");
554
- const markerLineIndex = rawLines.findIndex((line) => stripAnsi(line).includes(marker));
555
- if (markerLineIndex >= 0) {
556
- let endLineIndex = rawLines.length;
557
- for (let i = markerLineIndex + 1; i < rawLines.length; i++) {
558
- const line = stripAnsi(rawLines[i]).trimStart();
559
- if (line.startsWith("stderr |") || line.startsWith("stdout |")) {
560
- endLineIndex = i;
561
- break;
562
- }
563
- }
564
- const coverageBody = rawLines.slice(markerLineIndex + 1, endLineIndex).join("\n").trimEnd();
565
- if (coverageBody) {
566
- const fullBlock = rawLines.slice(markerLineIndex, endLineIndex).join("\n").trimEnd();
567
- console.log(`
568
- ${fullBlock}
569
- `);
570
- }
571
- }
572
- } else {
573
- const trimmed = output.trimEnd();
574
- if (trimmed) console.error(trimmed);
575
- }
576
- }
577
- function computeSummaryFromFinal(finalData) {
578
- const pct = (covered, total) => total === 0 ? 100 : parseFloat((covered / total * 100).toFixed(2));
579
- const summary = {
580
- total: {
581
- statements: { total: 0, covered: 0, pct: 0 },
582
- branches: { total: 0, covered: 0, pct: 0 },
583
- functions: { total: 0, covered: 0, pct: 0 },
584
- lines: { total: 0, covered: 0, pct: 0 }
585
- }
586
- };
587
- for (const [filePath, data] of Object.entries(finalData)) {
588
- const sKeys = Object.keys(data.s ?? {});
589
- const stmtTotal = sKeys.length;
590
- const stmtCovered = sKeys.filter((k) => data.s[k] > 0).length;
591
- const fKeys = Object.keys(data.f ?? {});
592
- const fnTotal = fKeys.length;
593
- const fnCovered = fKeys.filter((k) => data.f[k] > 0).length;
594
- let branchTotal = 0, branchCovered = 0;
595
- for (const counts of Object.values(data.b ?? {})) {
596
- branchTotal += counts.length;
597
- branchCovered += counts.filter((c) => c > 0).length;
598
- }
599
- const coveredLines = /* @__PURE__ */ new Set();
600
- const totalLines = /* @__PURE__ */ new Set();
601
- for (const [sid, loc] of Object.entries(data.statementMap ?? {})) {
602
- const line = loc?.start?.line;
603
- if (line == null) continue;
604
- totalLines.add(line);
605
- if ((data.s ?? {})[sid] > 0) coveredLines.add(line);
606
- }
607
- const fileStats = {
608
- statements: { total: stmtTotal, covered: stmtCovered, pct: pct(stmtCovered, stmtTotal) },
609
- branches: { total: branchTotal, covered: branchCovered, pct: pct(branchCovered, branchTotal) },
610
- functions: { total: fnTotal, covered: fnCovered, pct: pct(fnCovered, fnTotal) },
611
- lines: { total: totalLines.size, covered: coveredLines.size, pct: pct(coveredLines.size, totalLines.size) }
612
- };
613
- summary[filePath] = fileStats;
614
- for (const key of ["statements", "branches", "functions", "lines"]) {
615
- summary.total[key].total += fileStats[key].total;
616
- summary.total[key].covered += fileStats[key].covered;
617
- }
618
- }
619
- for (const key of ["statements", "branches", "functions", "lines"]) {
620
- const { total, covered } = summary.total[key];
621
- summary.total[key].pct = pct(covered, total);
622
- }
623
- return summary;
624
- }
625
- async function printCoverageSummary(cwd, extraCoverageArgs, worstCount = 10, options = {}) {
626
- const { silent = false } = options;
627
- let coverageDir = path3.resolve(cwd, "coverage");
628
- const repoDirArg = extraCoverageArgs.find((a) => a.startsWith("--coverage.reportsDirectory="));
629
- if (repoDirArg) {
630
- const raw = repoDirArg.split("=").slice(1).join("=");
631
- coverageDir = path3.isAbsolute(raw) ? raw : path3.resolve(cwd, raw);
632
- }
633
- let summary;
634
- try {
635
- const content = await fs3.readFile(path3.join(coverageDir, "coverage-summary.json"), "utf8");
636
- summary = JSON.parse(content);
637
- } catch {
638
- try {
639
- const content = await fs3.readFile(path3.join(coverageDir, "coverage-final.json"), "utf8");
640
- summary = computeSummaryFromFinal(JSON.parse(content));
641
- } catch {
642
- if (!silent) console.log(chalk2.dim(" (no coverage JSON found \u2014 skipping summary)"));
643
- return null;
644
- }
645
- }
646
- const { total, ...fileSummaries } = summary;
647
- if (worstCount > 0) {
648
- const fileRows = Object.entries(fileSummaries).map(([absFile, data]) => ({
649
- file: path3.relative(cwd, absFile),
650
- lines: data.lines?.pct ?? 0,
651
- stmts: data.statements?.pct ?? 0,
652
- fns: data.functions?.pct ?? 0,
653
- branches: data.branches?.pct ?? 0
654
- })).filter((r) => Math.min(r.lines, r.stmts, r.fns, r.branches) < 100).sort((a, b) => Math.min(a.lines, a.stmts, a.fns, a.branches) - Math.min(b.lines, b.stmts, b.fns, b.branches));
655
- const rowsToShow = fileRows.slice(0, worstCount);
656
- if (!silent) {
657
- console.log("\n" + chalk2.bold("\u{1F4C9} WORST COVERAGE FILES (lowest metric)"));
658
- console.log("-".repeat(80));
659
- rowsToShow.forEach(({ file, lines, stmts, fns, branches }) => {
660
- const worst = Math.min(lines, stmts, fns, branches);
661
- const extras = chalk2.dim(
662
- `lines ${lines.toFixed(0)}% | stmts ${stmts.toFixed(0)}% | fns ${fns.toFixed(0)}% | branches ${branches.toFixed(0)}%`
663
- );
664
- console.log(` ${colourPct(chalk2, worst)}% ${chalk2.dim(file)} ${extras}`);
665
- });
666
- if (fileRows.length > worstCount) {
667
- console.log(chalk2.dim(` ... and ${fileRows.length - worstCount} more files`));
668
- }
669
- }
670
- const tl2 = total.lines?.pct ?? 0;
671
- const ts2 = total.statements?.pct ?? 0;
672
- const tf2 = total.functions?.pct ?? 0;
673
- const tb2 = total.branches?.pct ?? 0;
674
- if (!silent) {
675
- console.log(
676
- `
677
- ${chalk2.bold("Coverage")} ${colourPct(chalk2, tl2)}% lines ${chalk2.dim("|")} ${colourPct(chalk2, ts2)}% statements ${chalk2.dim("|")} ${colourPct(chalk2, tf2)}% functions ${chalk2.dim("|")} ${colourPct(chalk2, tb2)}% branches`
678
- );
679
- }
680
- return {
681
- coverageDir,
682
- total,
683
- worstFiles: rowsToShow,
684
- worstFilesShown: rowsToShow.length,
685
- worstFilesTotal: fileRows.length,
686
- summary
687
- };
688
- }
689
- const tl = total.lines?.pct ?? 0;
690
- const ts = total.statements?.pct ?? 0;
691
- const tf = total.functions?.pct ?? 0;
692
- const tb = total.branches?.pct ?? 0;
693
- if (!silent) {
694
- console.log(
695
- `
696
- ${chalk2.bold("Coverage")} ${colourPct(chalk2, tl)}% lines ${chalk2.dim("|")} ${colourPct(chalk2, ts)}% statements ${chalk2.dim("|")} ${colourPct(chalk2, tf)}% functions ${chalk2.dim("|")} ${colourPct(chalk2, tb)}% branches`
697
- );
698
- }
699
- return {
700
- coverageDir,
701
- total,
702
- worstFiles: [],
703
- worstFilesShown: 0,
704
- worstFilesTotal: 0,
705
- summary
706
- };
707
- }
708
-
709
- // src/core/scratch.mjs
710
- import fs4 from "fs/promises";
711
- import fsSync from "fs";
712
- import path4 from "path";
713
- var DEFAULT_SCRATCH_DIR = "tmp/vitest-runner";
714
- var RUN_ROOT_NAME_PATTERN = /^(\d+)-\d+$/;
715
- function isPidAlive(pid) {
716
- try {
717
- process.kill(pid, 0);
718
- return true;
719
- } catch (err) {
720
- return err.code !== "ESRCH";
721
- }
722
- }
723
- function sanitizeLabel(label) {
724
- const cleaned = String(label).replace(/[^a-zA-Z0-9_-]+/g, "-").slice(0, 80);
725
- return cleaned || "scratch";
726
- }
727
- async function sweepStaleScratchRoots(cwd, scratchDir) {
728
- const base = path4.resolve(cwd, scratchDir);
729
- let entries;
730
- try {
731
- entries = await fs4.readdir(base, { withFileTypes: true });
732
- } catch {
733
- return;
734
- }
735
- for (const entry of entries) {
736
- if (!entry.isDirectory()) continue;
737
- const match = entry.name.match(RUN_ROOT_NAME_PATTERN);
738
- if (!match) continue;
739
- const pid = parseInt(match[1], 10);
740
- if (isPidAlive(pid)) continue;
741
- await fs4.rm(path4.join(base, entry.name), { recursive: true, force: true }).catch(() => {
742
- });
743
- }
744
- }
745
- async function createRunScratchRoot(cwd, scratchDir) {
746
- const base = path4.resolve(cwd, scratchDir);
747
- const root = path4.join(base, `${process.pid}-${Date.now()}`);
748
- await fs4.mkdir(root, { recursive: true });
749
- return root;
750
- }
751
- async function createFileScratchDir(runRoot, index) {
752
- const dir = path4.join(runRoot, `worker-${index}`);
753
- await fs4.mkdir(dir, { recursive: true });
754
- return dir;
755
- }
756
- async function removeScratchRoot(root) {
757
- await fs4.rm(root, { recursive: true, force: true }).catch(() => {
758
- });
759
- }
760
- function removeScratchRootSync(root) {
761
- try {
762
- fsSync.rmSync(root, { recursive: true, force: true });
763
- } catch {
764
- }
765
- }
766
- function makeRunTmpDir(label) {
767
- const base = process.env.VITEST_RUNNER_TMP;
768
- if (!base) {
769
- throw new Error("makeRunTmpDir() requires VITEST_RUNNER_TMP to be set \u2014 call it from a test file run by vitest-runner.");
770
- }
771
- return fsSync.mkdtempSync(path4.join(base, `${sanitizeLabel(label)}-`));
772
- }
773
-
774
- // src/runner.mjs
775
- function getHeapForFile(filePath, globalMaxMb, overrides) {
776
- const normalized = filePath.replace(/\\/g, "/");
777
- let perFileMb;
778
- for (const { pattern, heapMb } of overrides) {
779
- if (normalized.includes(pattern)) {
780
- perFileMb = heapMb;
781
- break;
782
- }
783
- }
784
- if (perFileMb === void 0 && globalMaxMb === void 0) return void 0;
785
- if (perFileMb === void 0) return globalMaxMb;
786
- if (globalMaxMb === void 0) return perFileMb;
787
- return Math.max(perFileMb, globalMaxMb);
788
- }
789
- async function run(opts) {
790
- const cwd = opts.cwd ?? process.cwd();
791
- const scratchDirOpt = opts.scratchDir ?? DEFAULT_SCRATCH_DIR;
792
- const keepTmp = opts.keepTmp ?? false;
793
- await sweepStaleScratchRoots(cwd, scratchDirOpt);
794
- const scratchRoot = await createRunScratchRoot(cwd, scratchDirOpt);
795
- const onSignal = () => {
796
- removeScratchRootSync(scratchRoot);
797
- process.exitCode = 130;
798
- process.exit();
799
- };
800
- if (!keepTmp) {
801
- process.once("SIGINT", onSignal);
802
- process.once("SIGTERM", onSignal);
803
- }
804
- try {
805
- return await runImpl(opts, scratchRoot);
806
- } finally {
807
- process.removeListener("SIGINT", onSignal);
808
- process.removeListener("SIGTERM", onSignal);
809
- if (!keepTmp) await removeScratchRoot(scratchRoot);
810
- }
811
- }
812
- async function runImpl(opts, scratchRoot) {
813
- const {
814
- cwd = process.cwd(),
815
- testDir,
816
- testPatterns = [],
817
- testListFile,
818
- testFilePattern,
819
- vitestArgs: rawVitestArgs = [],
820
- showErrorDetails = true,
821
- coverageQuiet = false,
822
- suppressFileOutput = false,
823
- suppressPassingFiles = false,
824
- topSummary = true,
825
- json = false,
826
- workers = parseInt(process.env.VITEST_WORKERS ?? "4", 10),
827
- worstCoverageCount = 10,
828
- blobsDir: blobsDirOpt,
829
- mergeReports = true,
830
- earlyRunPatterns = [],
831
- perFileHeapOverrides = [],
832
- conditions = [],
833
- nodeEnv = "development",
834
- /** @internal Inject pre-built results to bypass discovery and spawn (for testing final-report render paths). */
835
- _testResultsOverride = null
836
- } = opts;
837
- let fileScratchIndex = 0;
838
- const maxOldSpaceMb = opts.maxOldSpaceMb ?? (process.env.VITEST_HEAP_MB ? parseInt(process.env.VITEST_HEAP_MB, 10) : void 0);
839
- const emitTextOutput = !json;
840
- const vitestBin = resolveBin(cwd, "vitest", "vitest");
841
- const vitestConfig = await resolveVitestConfig(cwd, opts.vitestConfig);
842
- const spawnBase = { cwd, vitestBin, vitestConfig, conditions, nodeEnv };
843
- const vitestArgs = [...rawVitestArgs];
844
- if (coverageQuiet && !vitestArgs.some((a) => a === "--coverage" || a.startsWith("--coverage."))) {
845
- vitestArgs.unshift("--coverage");
846
- }
847
- const hasCoverage = vitestArgs.some((a) => a === "--coverage" || a.startsWith("--coverage."));
848
- if (hasCoverage) {
849
- const blobsDir = path5.resolve(cwd, blobsDirOpt ?? ".vitest-coverage-blobs");
850
- const coverageTmpBase = path5.resolve(cwd, ".vitest-coverage-tmp");
851
- await Promise.all([fs5.rm(blobsDir, { recursive: true, force: true }), fs5.rm(coverageTmpBase, { recursive: true, force: true })]);
852
- await Promise.all([fs5.mkdir(blobsDir, { recursive: true }), fs5.mkdir(coverageTmpBase, { recursive: true })]);
853
- const allTestFiles = await discoverVitestFiles({ cwd, testDir, testPatterns, testListFile, testFilePattern, earlyRunPatterns });
854
- const filterConflicts2 = computeFilterConflicts(allTestFiles);
855
- if (allTestFiles.length === 0) {
856
- const noTestsMessage = testPatterns.length > 0 ? `\u274C No Vitest test files found matching: ${testPatterns.join(", ")}` : "\u274C No Vitest test files found";
857
- if (emitTextOutput) console.log(noTestsMessage);
858
- if (json) {
859
- return {
860
- exitCode: 1,
861
- mode: "coverage",
862
- message: noTestsMessage,
863
- options: {
864
- cwd,
865
- testDir,
866
- testPatterns,
867
- testListFile,
868
- workers,
869
- coverageQuiet,
870
- suppressFileOutput,
871
- suppressPassingFiles,
872
- topSummary,
873
- vitestArgs
874
- }
875
- };
876
- }
877
- return 1;
878
- }
879
- const extraCoverageArgs = vitestArgs.filter((a) => a !== "--coverage" && a.startsWith("--coverage."));
880
- const nonCoveragePassthrough = vitestArgs.filter((a) => a !== "--coverage" && !a.startsWith("--coverage."));
881
- const soloFiles2 = allTestFiles.filter((f) => earlyRunPatterns.some((p) => f.replace(/\\/g, "/").includes(p)));
882
- const parallelFiles2 = allTestFiles.filter((f) => !earlyRunPatterns.some((p) => f.replace(/\\/g, "/").includes(p)));
883
- const suppressPerFileCoverageOutput = coverageQuiet || suppressFileOutput || json;
884
- const showCoverageProgress = coverageQuiet && !json;
885
- if (!coverageQuiet && !suppressFileOutput && emitTextOutput) {
886
- console.log(`
887
- \u{1F9EA} Running ${allTestFiles.length} test files for coverage (blob + merge mode)`);
888
- console.log(`\u2699\uFE0F Workers: ${workers} (${soloFiles2.length} solo first, then parallel)`);
889
- if (maxOldSpaceMb) console.log(`\u{1F9E0} Heap limit: ${maxOldSpaceMb} MB`);
890
- console.log("");
891
- }
892
- const progress = showCoverageProgress ? createCoverageProgressTracker(allTestFiles.length) : noopProgressTracker;
893
- const coverageResults = [];
894
- let blobIndex = 0;
895
- const runCoverageFile = async (filePath) => {
896
- const blobPath = path5.join(blobsDir, `run-${blobIndex}.blob`);
897
- blobIndex++;
898
- if (!suppressPerFileCoverageOutput && emitTextOutput) {
899
- console.log(`
900
- ${"=".repeat(80)}`);
901
- console.log(`\u25B6\uFE0F ${filePath}`);
902
- console.log("=".repeat(80));
903
- }
904
- progress.onStart();
905
- const tmpCoverageDir = path5.join(coverageTmpBase, `run-${blobIndex}`);
906
- const blobArgs = [
907
- ...nonCoveragePassthrough,
908
- "--coverage",
909
- `--coverage.reportsDirectory=${tmpCoverageDir}`,
910
- "--reporter=default",
911
- "--reporter=blob",
912
- `--outputFile=${blobPath}`
913
- ];
914
- const scratchDir = await createFileScratchDir(scratchRoot, fileScratchIndex++);
915
- const result = await runSingleFile(filePath, {
916
- ...spawnBase,
917
- maxOldSpaceMb: getHeapForFile(filePath, maxOldSpaceMb, perFileHeapOverrides),
918
- vitestArgs: blobArgs,
919
- streamOutput: !suppressPerFileCoverageOutput,
920
- excludePaths: filterConflicts2.get(filePath),
921
- extraEnv: { VITEST_RUNNER_TMP: scratchDir }
922
- });
923
- coverageResults.push(result);
924
- progress.onComplete(result.code !== 0);
925
- if (!suppressPerFileCoverageOutput && emitTextOutput) {
926
- const durationSec = (result.duration / 1e3).toFixed(2);
927
- if (result.code === 0) {
928
- const heapInfo = result.heapMb ? ` | ${result.heapMb} MB heap` : "";
929
- console.log(`
930
- \u2705 PASSED (${durationSec}s${heapInfo})
931
- `);
932
- } else {
933
- console.log(`
934
- \u274C FAILED (exit code ${result.code}, ${durationSec}s)
935
- `);
936
- }
937
- }
938
- };
939
- for (const filePath of soloFiles2) {
940
- await runCoverageFile(filePath).catch((err) => {
941
- if (emitTextOutput) console.error(`Error running ${filePath}:`, err);
942
- });
943
- }
944
- let coverageFileIndex = 0;
945
- const coverageActivePromises = /* @__PURE__ */ new Set();
946
- while (coverageFileIndex < parallelFiles2.length || coverageActivePromises.size > 0) {
947
- while (coverageFileIndex < parallelFiles2.length && coverageActivePromises.size < workers) {
948
- const filePath = parallelFiles2[coverageFileIndex++];
949
- const promise = runCoverageFile(filePath).catch((err) => {
950
- if (emitTextOutput) console.error(`Error running ${filePath}:`, err);
951
- }).finally(() => coverageActivePromises.delete(promise));
952
- coverageActivePromises.add(promise);
953
- }
954
- if (coverageActivePromises.size > 0) await Promise.race(coverageActivePromises);
955
- }
956
- progress.finish();
957
- const blobFiles = (await fs5.readdir(blobsDir).catch(() => [])).filter((f) => f.endsWith(".blob"));
958
- if (blobFiles.length === 0) {
959
- const message = "\u274C No coverage blobs were generated \u2014 coverage report cannot be produced";
960
- if (emitTextOutput) console.error(message);
961
- if (json) {
962
- return {
963
- exitCode: 1,
964
- mode: "coverage",
965
- message,
966
- options: {
967
- cwd,
968
- testDir,
969
- testPatterns,
970
- testListFile,
971
- workers,
972
- coverageQuiet,
973
- suppressFileOutput,
974
- suppressPassingFiles,
975
- topSummary,
976
- showErrorDetails,
977
- worstCoverageCount,
978
- vitestArgs
979
- },
980
- totals: {
981
- testFiles: allTestFiles.length,
982
- failedFiles: coverageResults.filter((r) => r.code !== 0).length,
983
- passedFiles: coverageResults.filter((r) => r.code === 0).length
984
- },
985
- results: {
986
- all: coverageResults,
987
- failed: coverageResults.filter((r) => r.code !== 0)
988
- },
989
- merge: {
990
- blobFiles: 0,
991
- exitCode: 1,
992
- output: ""
993
- },
994
- coverageSummary: null
995
- };
996
- }
997
- return 1;
998
- }
999
- let mergeExitCode = 0;
1000
- let mergeOutput = "";
1001
- let coverageSummary = null;
1002
- if (mergeReports) {
1003
- if (!coverageQuiet && !suppressFileOutput && emitTextOutput) {
1004
- console.log(`
1005
- ${"=".repeat(80)}`);
1006
- console.log(`\u{1F4CA} Merging ${blobFiles.length} coverage blobs into final report...`);
1007
- console.log("=".repeat(80));
1008
- }
1009
- ({ exitCode: mergeExitCode, output: mergeOutput } = await runMergeReports(blobsDir, {
1010
- ...spawnBase,
1011
- maxOldSpaceMb,
1012
- extraCoverageArgs,
1013
- quietOutput: coverageQuiet || json
1014
- }));
1015
- if (coverageQuiet && emitTextOutput) {
1016
- printMergeOutput(mergeExitCode, mergeOutput);
1017
- }
1018
- coverageSummary = await printCoverageSummary(cwd, extraCoverageArgs, worstCoverageCount, { silent: json });
1019
- }
1020
- await Promise.all([
1021
- ...mergeReports ? [fs5.rm(blobsDir, { recursive: true, force: true }).catch(() => {
1022
- })] : [],
1023
- fs5.rm(coverageTmpBase, { recursive: true, force: true }).catch(() => {
1024
- })
1025
- ]);
1026
- const coverageFailed = coverageResults.filter((r) => r.code !== 0);
1027
- if (coverageQuiet && emitTextOutput) printQuietCoverageFailureDetails(coverageFailed);
1028
- const exitCode2 = coverageFailed.length > 0 ? 1 : mergeExitCode;
1029
- if (json) {
1030
- const coverageWithHeap = coverageResults.filter((r) => r.heapMb !== null);
1031
- return {
1032
- exitCode: exitCode2,
1033
- mode: "coverage",
1034
- options: {
1035
- cwd,
1036
- testDir,
1037
- testPatterns,
1038
- testListFile,
1039
- workers,
1040
- coverageQuiet,
1041
- suppressFileOutput,
1042
- suppressPassingFiles,
1043
- topSummary,
1044
- showErrorDetails,
1045
- worstCoverageCount,
1046
- vitestArgs
1047
- },
1048
- totals: {
1049
- testFiles: allTestFiles.length,
1050
- failedFiles: coverageFailed.length,
1051
- passedFiles: coverageResults.length - coverageFailed.length
1052
- },
1053
- results: {
1054
- all: coverageResults,
1055
- failed: coverageFailed
1056
- },
1057
- merge: {
1058
- blobFiles: blobFiles.length,
1059
- exitCode: mergeExitCode,
1060
- output: mergeOutput
1061
- },
1062
- coverageSummary,
1063
- ...topSummary ? {
1064
- topMemoryUsers: [...coverageWithHeap].sort((a, b) => b.heapMb - a.heapMb).slice(0, 10),
1065
- topDuration: [...coverageResults].sort((a, b) => b.duration - a.duration).slice(0, 10)
1066
- } : {}
1067
- };
1068
- }
1069
- return exitCode2;
1070
- }
1071
- const testFiles = await discoverVitestFiles({ cwd, testDir, testPatterns, testListFile, testFilePattern, earlyRunPatterns });
1072
- const filterConflicts = computeFilterConflicts(testFiles);
1073
- if (testFiles.length === 0) {
1074
- const noTestsMessage = testPatterns.length > 0 ? `\u274C No Vitest test files found matching: ${testPatterns.join(", ")}` : "\u274C No Vitest test files found";
1075
- if (emitTextOutput) console.log(noTestsMessage);
1076
- if (json) {
1077
- return {
1078
- exitCode: 1,
1079
- mode: "standard",
1080
- message: noTestsMessage,
1081
- options: {
1082
- cwd,
1083
- testDir,
1084
- testPatterns,
1085
- testListFile,
1086
- workers,
1087
- coverageQuiet,
1088
- suppressFileOutput,
1089
- suppressPassingFiles,
1090
- topSummary,
1091
- vitestArgs
1092
- }
1093
- };
1094
- }
1095
- return 1;
1096
- }
1097
- const soloFiles = testFiles.filter((f) => earlyRunPatterns.some((p) => f.replace(/\\/g, "/").includes(p)));
1098
- const parallelFiles = testFiles.filter((f) => !earlyRunPatterns.some((p) => f.replace(/\\/g, "/").includes(p)));
1099
- const scriptStartTime = Date.now();
1100
- const scriptStartTimeFormatted = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
1101
- if (emitTextOutput && !suppressFileOutput && testPatterns.length > 0) {
1102
- console.log(`
1103
- \u{1F9EA} Running ${testFiles.length} test files matching: ${testPatterns.join(", ")}`);
1104
- } else if (emitTextOutput && !suppressFileOutput) {
1105
- console.log(`
1106
- \u{1F9EA} Running ${testFiles.length} test files (${soloFiles.length} solo first, then parallel)`);
1107
- }
1108
- if (emitTextOutput && !suppressFileOutput) {
1109
- console.log(`\u2699\uFE0F Workers: ${workers}`);
1110
- if (maxOldSpaceMb) console.log(`\u{1F9E0} Heap limit: ${maxOldSpaceMb} MB`);
1111
- if (vitestArgs.length > 0) console.log(`\u{1F527} Vitest args: ${vitestArgs.join(" ")}`);
1112
- console.log("");
1113
- }
1114
- const results = [..._testResultsOverride ?? []];
1115
- const runTestFile = async (filePath) => {
1116
- if (!suppressFileOutput && emitTextOutput) {
1117
- console.log(`
1118
- ${"=".repeat(80)}`);
1119
- console.log(`\u25B6\uFE0F ${filePath}`);
1120
- console.log("=".repeat(80));
1121
- }
1122
- const scratchDir = await createFileScratchDir(scratchRoot, fileScratchIndex++);
1123
- const result = await runSingleFile(filePath, {
1124
- ...spawnBase,
1125
- maxOldSpaceMb: getHeapForFile(filePath, maxOldSpaceMb, perFileHeapOverrides),
1126
- vitestArgs,
1127
- streamOutput: !suppressFileOutput && !json,
1128
- excludePaths: filterConflicts.get(filePath),
1129
- extraEnv: { VITEST_RUNNER_TMP: scratchDir }
1130
- });
1131
- if (!suppressFileOutput && emitTextOutput) {
1132
- const durationSec = (result.duration / 1e3).toFixed(2);
1133
- if (result.code === 0) {
1134
- const heapInfo = result.heapMb ? ` | ${result.heapMb} MB heap` : "";
1135
- console.log(`
1136
- \u2705 PASSED (${durationSec}s${heapInfo})
1137
- `);
1138
- } else {
1139
- console.log(`
1140
- \u274C FAILED (exit code ${result.code}, ${durationSec}s)
1141
- `);
1142
- }
1143
- }
1144
- return result;
1145
- };
1146
- if (!_testResultsOverride) {
1147
- for (const filePath of soloFiles) {
1148
- const result = await runTestFile(filePath).catch((err) => {
1149
- if (emitTextOutput) console.error(`Error running ${filePath}:`, err);
1150
- return null;
1151
- });
1152
- if (result) results.push(result);
1153
- }
1154
- let index = 0;
1155
- const activePromises = /* @__PURE__ */ new Set();
1156
- while (index < parallelFiles.length || activePromises.size > 0) {
1157
- while (index < parallelFiles.length && activePromises.size < workers) {
1158
- const filePath = parallelFiles[index++];
1159
- const promise = runTestFile(filePath).then((result) => results.push(result)).catch((err) => {
1160
- if (emitTextOutput) console.error(`Error running ${filePath}:`, err);
1161
- }).finally(() => activePromises.delete(promise));
1162
- activePromises.add(promise);
1163
- }
1164
- if (activePromises.size > 0) await Promise.race(activePromises);
1165
- }
1166
- }
1167
- const totalTestFilesPass = results.reduce((s, r) => s + r.testFilesPass, 0);
1168
- const totalTestFilesFail = results.reduce((s, r) => s + r.testFilesFail, 0);
1169
- const totalTestsPass = results.reduce((s, r) => s + r.testsPass, 0);
1170
- const totalTestsFail = results.reduce((s, r) => s + r.testsFail, 0);
1171
- const totalTestsSkip = results.reduce((s, r) => s + (r.testsSkip || 0), 0);
1172
- const totalDuration = results.reduce((s, r) => s + r.duration, 0);
1173
- const failedFiles = results.filter((r) => r.code !== 0);
1174
- const passedFiles = results.filter((r) => r.code === 0);
1175
- const exitCode = failedFiles.length > 0 ? 1 : 0;
1176
- const scriptMemory = process.memoryUsage();
1177
- const scriptHeapMb = Math.round(scriptMemory.heapUsed / 1024 / 1024);
1178
- const scriptRssMb = Math.round(scriptMemory.rss / 1024 / 1024);
1179
- const withHeap = results.filter((r) => r.heapMb !== null);
1180
- const actualDurationSec = ((Date.now() - scriptStartTime) / 1e3).toFixed(2);
1181
- const testsDurationSec = (totalDuration / 1e3).toFixed(2);
1182
- if (json) {
1183
- return {
1184
- exitCode,
1185
- mode: "standard",
1186
- options: {
1187
- cwd,
1188
- testDir,
1189
- testPatterns,
1190
- testListFile,
1191
- workers,
1192
- coverageQuiet,
1193
- suppressFileOutput,
1194
- suppressPassingFiles,
1195
- topSummary,
1196
- showErrorDetails,
1197
- worstCoverageCount,
1198
- vitestArgs
1199
- },
1200
- totals: {
1201
- testFilesPass: totalTestFilesPass,
1202
- testFilesFail: totalTestFilesFail,
1203
- testsPass: totalTestsPass,
1204
- testsFail: totalTestsFail,
1205
- testsSkip: totalTestsSkip,
1206
- totalTests: totalTestsPass + totalTestsFail + totalTestsSkip
1207
- },
1208
- timing: {
1209
- startAt: scriptStartTimeFormatted,
1210
- wallClockSeconds: Number(actualDurationSec),
1211
- testsSeconds: Number(testsDurationSec)
1212
- },
1213
- heap: {
1214
- scriptHeapMb,
1215
- scriptRssMb,
1216
- maxHeapMb: withHeap.length > 0 ? Math.max(...withHeap.map((r) => r.heapMb)) : null,
1217
- avgHeapMb: withHeap.length > 0 ? Number((withHeap.reduce((sum, r) => sum + r.heapMb, 0) / withHeap.length).toFixed(0)) : null
1218
- },
1219
- results: {
1220
- all: results,
1221
- passed: passedFiles,
1222
- failed: failedFiles
1223
- },
1224
- ...topSummary ? {
1225
- topMemoryUsers: [...withHeap].sort((a, b) => b.heapMb - a.heapMb).slice(0, 10),
1226
- topDuration: [...results].sort((a, b) => b.duration - a.duration).slice(0, 10)
1227
- } : {}
1228
- };
1229
- }
1230
- console.log("\n" + "=".repeat(80));
1231
- if (topSummary && withHeap.length > 0) {
1232
- console.log("\n" + chalk3.bold("\u{1F9E0} TOP MEMORY USERS"));
1233
- console.log("-".repeat(80));
1234
- [...withHeap].sort((a, b) => b.heapMb - a.heapMb).slice(0, 10).forEach((r) => {
1235
- console.log(` ${String(r.heapMb).padStart(4)} MB ${chalk3.dim(r.file)}`);
1236
- });
1237
- }
1238
- if (topSummary && results.length > 0) {
1239
- console.log("\n" + chalk3.bold("\u23F1\uFE0F TOP DURATION"));
1240
- console.log("-".repeat(80));
1241
- [...results].sort((a, b) => b.duration - a.duration).slice(0, 10).forEach((r) => {
1242
- const sec = (r.duration / 1e3).toFixed(2);
1243
- console.log(` ${(sec + "s").padStart(8)} ${chalk3.dim(r.file)}`);
1244
- });
1245
- }
1246
- if (passedFiles.length > 0 && !suppressPassingFiles) {
1247
- console.log("\n" + "=".repeat(80));
1248
- console.log(chalk3.bold.green("\u2713 PASSED TEST FILES"));
1249
- console.log("=".repeat(80));
1250
- passedFiles.forEach((r) => {
1251
- const durationSec = (r.duration / 1e3).toFixed(2);
1252
- const statsInfo = [...r.heapMb ? [`${r.heapMb} MB`] : [], `${durationSec}s`];
1253
- const testInfo = r.testsPass > 0 ? ` - ${r.testsPass} tests` : "";
1254
- console.log(chalk3.green(`\u2713 ${r.file}${testInfo}`) + chalk3.dim(` (${statsInfo.join(", ")})`));
1255
- });
1256
- }
1257
- console.log("\n" + chalk3.bold("=".repeat(80)));
1258
- if (failedFiles.length > 0) {
1259
- console.log(`
1260
- \u274C ${failedFiles.length} test file(s) failed`);
1261
- console.log(chalk3.bold.red("\nFailed Test Files:"));
1262
- failedFiles.forEach((r) => {
1263
- const durationSec = (r.duration / 1e3).toFixed(2);
1264
- const testCounts = [
1265
- ...r.testsFail > 0 ? [chalk3.red(`${r.testsFail} failed`)] : [],
1266
- ...r.testsPass > 0 ? [chalk3.green(`${r.testsPass} passed`)] : [],
1267
- ...r.testsSkip > 0 ? [chalk3.yellow(`${r.testsSkip} skipped`)] : []
1268
- ];
1269
- const statsInfo = [...r.heapMb ? [`${r.heapMb} MB`] : [], `${durationSec}s`];
1270
- const countStr = testCounts.length > 0 ? ` (${testCounts.join(", ")})` : "";
1271
- console.log(` ${chalk3.red("\u2716")} ${r.file}${countStr}` + chalk3.dim(` [${statsInfo.join(", ")}]`));
1272
- if (showErrorDetails && r.errors.length > 0) {
1273
- const deduped = deduplicateErrors(r.errors).split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
1274
- console.log(deduped);
1275
- console.log("");
1276
- }
1277
- });
1278
- console.log("");
1279
- }
1280
- console.log(chalk3.bold("=".repeat(80)));
1281
- if (totalTestFilesFail > 0 && totalTestFilesPass > 0) {
1282
- console.log(
1283
- ` ${chalk3.bold("Test Files")} ${chalk3.red(`${totalTestFilesFail} failed`)} ${chalk3.dim("|")} ${chalk3.green(`${totalTestFilesPass} passed`)} ${chalk3.dim(`(${totalTestFilesPass + totalTestFilesFail})`)}`
1284
- );
1285
- } else if (totalTestFilesFail > 0) {
1286
- console.log(` ${chalk3.bold("Test Files")} ${chalk3.red(`${totalTestFilesFail} failed`)} ${chalk3.dim(`(${totalTestFilesFail})`)}`);
1287
- } else {
1288
- console.log(` ${chalk3.bold("Test Files")} ${chalk3.green(`${totalTestFilesPass} passed`)} ${chalk3.dim(`(${totalTestFilesPass})`)}`);
1289
- }
1290
- const totalTests = totalTestsPass + totalTestsFail + totalTestsSkip;
1291
- const testsParts = [
1292
- ...totalTestsFail > 0 ? [chalk3.red(`${totalTestsFail} failed`)] : [],
1293
- ...totalTestsPass > 0 ? [chalk3.green(`${totalTestsPass} passed`)] : [],
1294
- ...totalTestsSkip > 0 ? [chalk3.yellow(`${totalTestsSkip} skipped`)] : []
1295
- ];
1296
- console.log(` ${chalk3.bold("Tests")} ${testsParts.join(` ${chalk3.dim("|")} `)} ${chalk3.dim(`(${totalTests})`)}`);
1297
- console.log(` ${chalk3.bold("Start at")} ${scriptStartTimeFormatted}`);
1298
- console.log(` ${chalk3.bold("Duration")} ${actualDurationSec}s ${chalk3.dim(`(tests ${testsDurationSec}s)`)}`);
1299
- if (withHeap.length > 0) {
1300
- const maxHeap = Math.max(...withHeap.map((r) => r.heapMb));
1301
- const avgHeap = (withHeap.reduce((s, r) => s + r.heapMb, 0) / withHeap.length).toFixed(0);
1302
- console.log(` ${chalk3.bold("Heap")} max ${maxHeap} MB | avg ${avgHeap} MB | script ${scriptHeapMb} MB (RSS ${scriptRssMb} MB)`);
1303
- } else {
1304
- console.log(` ${chalk3.bold("Heap")} script ${scriptHeapMb} MB (RSS ${scriptRssMb} MB)`);
1305
- }
1306
- if (failedFiles.length > 0) return 1;
1307
- console.log(`
1308
- \u2705 All ${passedFiles.length} test files passed
1309
- `);
1310
- return 0;
1311
- }
1312
- export {
1313
- buildNodeOptions,
1314
- colourPct,
1315
- createCoverageProgressTracker,
1316
- deduplicateErrors,
1317
- discoverFilesInDir,
1318
- discoverVitestFiles,
1319
- formatDuration,
1320
- makeRunTmpDir,
1321
- noopProgressTracker,
1322
- parseVitestOutput,
1323
- printCoverageSummary,
1324
- printMergeOutput,
1325
- printQuietCoverageFailureDetails,
1326
- resolveBin,
1327
- resolveVitestConfig,
1328
- run,
1329
- runMergeReports,
1330
- runSingleFile,
1331
- runVitestDirect,
1332
- sortWithPriority,
1333
- stripAnsi
1334
- };
1
+ import q from"fs/promises";import de from"path";import h from"chalk";import{createRequire as St}from"module";import K from"path";import bt from"fs/promises";import{pathToFileURL as xt}from"url";function ye(s,o,t=o){let n=St(xt(K.join(s,"package.json"))),e=n.resolve(`${o}/package.json`),r=n(e),a=r.bin?.[t]??r.bin;if(!a)throw new Error(`No bin "${t}" found in ${o}/package.json`);return K.join(K.dirname(e),a)}var wt=Object.freeze(["vitest.config.ts","vitest.config.mts","vitest.config.cts","vitest.config.mjs","vitest.config.js","vitest.config.cjs","vite.config.ts","vite.config.mts","vite.config.mjs","vite.config.js"]);async function Pe(s,o){if(o)return K.isAbsolute(o)?o:K.resolve(s,o);for(let t of wt){let n=K.join(s,t);try{return await bt.access(n),n}catch{}}}import Oe from"fs/promises";import j from"path";var Xe=/\.test\.vitest\.(?:js|mjs|cjs)$/i;async function le(s,o,t=Xe){let n=[s],e=[];for(;n.length;){let r=n.pop(),a;try{a=await Oe.readdir(r,{withFileTypes:!0})}catch{continue}for(let l of a){if(l.isDirectory()){if(l.name==="node_modules"||l.name.startsWith("."))continue;n.push(j.join(r,l.name));continue}l.isFile()&&t.test(l.name)&&e.push(j.relative(o,j.join(r,l.name)))}}return e}function pe(s,o=[]){let t=[],n=[];for(let e of s){let r=e.replace(/\\/g,"/"),a=o.findIndex(l=>r.includes(l));a!==-1?t.push({file:e,priorityIndex:a}):n.push(e)}return t.sort((e,r)=>e.priorityIndex-r.priorityIndex||e.file.localeCompare(r.file)),n.sort((e,r)=>e.localeCompare(r)),[...t.map(e=>e.file),...n]}function De(s){let o=s.map(n=>n.replace(/\\/g,"/")),t=new Map;for(let n=0;n<s.length;n++){let e=[];for(let r=0;r<s.length;r++)n!==r&&o[r].includes(o[n])&&e.push(s[r]);t.set(s[n],e)}return t}async function fe(s){let{cwd:o,testDir:t,testPatterns:n=[],testListFile:e,testFilePattern:r=Xe,earlyRunPatterns:a=[]}=s,l=t?j.isAbsolute(t)?t:j.resolve(o,t):o;if(e){let c=j.isAbsolute(e)?e:j.resolve(o,e),f;try{let p=await Oe.readFile(c,"utf8");f=JSON.parse(p)}catch(p){throw new Error(`Failed to read test list file "${c}": ${p.message}`,{cause:p})}if(!Array.isArray(f))throw new Error(`Test list file "${c}" must contain a JSON array of test file paths`);return console.log(`\u{1F4CB} Loading test list from: ${j.relative(o,c)}`),pe(f,a)}if(n.length===0){let c=await le(l,o,r);return pe(c,a)}let u=[];for(let c of n){let f=j.isAbsolute(c)?c:j.resolve(o,c),p=null;try{p=await Oe.stat(f)}catch{}if(p?.isFile())r.test(f)&&u.push(j.relative(o,f));else if(p?.isDirectory())u.push(...await le(f,o,r));else{let m=(await le(l,o,r)).filter(g=>g.replace(/\\/g,"/").includes(c.replace(/\\/g,"/")));m.length>0?u.push(...m):console.warn(`\u26A0\uFE0F No matches found for: ${c}`)}}return pe([...new Set(u)],a)}import{spawn as Re}from"child_process";function V(s){return s.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function N(s,o){let t=o.toFixed(2).padStart(6);return o>=80?s.green(t):o>=50?s.yellow(t):s.red(t)}function Ie(s){let o=V(s),t={testFilesPass:0,testFilesFail:0,testsPass:0,testsFail:0,testsSkip:0,duration:0,heapMb:null,errors:[]},n=o.match(/Test Files\s+(.+)/);if(n){let u=n[1],c=u.match(/(\d+)\s+passed/),f=u.match(/(\d+)\s+failed/);c&&(t.testFilesPass=parseInt(c[1],10)),f&&(t.testFilesFail=parseInt(f[1],10))}let e=o.match(/^\s*Tests\s+(.+)$/m);if(e){let u=e[1],c=u.match(/(\d+)\s+passed/),f=u.match(/(\d+)\s+failed/),p=u.match(/(\d+)\s+skipped/);c&&(t.testsPass=parseInt(c[1],10)),f&&(t.testsFail=parseInt(f[1],10)),p&&(t.testsSkip=parseInt(p[1],10))}let r=o.match(/Duration\s+([\d.]+)s/);r&&(t.duration=parseFloat(r[1])*1e3);let a=o.match(/(\d+)\s*MB\s+heap\s+used/i);a&&(t.heapMb=parseInt(a[1],10));let l=s.indexOf("Failed Tests");if(l!==-1){let u=s.indexOf(`
2
+ Test Files`,l),c=u!==-1?s.substring(l,u):s.substring(l),f=/FAIL\s*(?:\x1B\[[0-9;]*[a-zA-Z]|\s)*tests\//g,p=[...c.matchAll(f)];for(let $=0;$<p.length;$++){let m=p[$].index,g=c.lastIndexOf(`
3
+ `,m),E=g===-1?0:g,v=$<p.length-1?p[$+1].index:c.length,x=c.lastIndexOf(`
4
+ `,v),w=x===-1?v:x,I=c.substring(E,w).trim();I&&t.errors.push(I)}}return t}function Ce(s){let t=s.join(`
5
+ `).split(`
6
+ `),n=new Map,e=new Map;t.forEach((a,l)=>{if(a.includes("FAIL")&&a.includes("Config:")){e.set(a,l);let c=V(a).match(/^(.+Config:\s+)'*([^'>]+)'*(.+)$/);if(c){let[,f,p,$]=c,m=`${f.trim()}|||${$.trim()}`;n.has(m)||n.set(m,{lines:[],configs:[]}),n.get(m).lines.push(a),n.get(m).configs.push(p.replace(/'/g,""))}}});let r=new Set;for(let[,a]of n.entries())if(a.configs.length>1){for(let p=1;p<a.lines.length;p++){let $=e.get(a.lines[p]);r.add($)}let l=a.lines[0],u=`[${a.configs.map(p=>`'${p}'`).join(",")}]`,c=V(l).replace(/(Config:\s+)'*[^'>]+'*/,`$1${u}`),f=e.get(l);t[f]=c}return t.filter((a,l)=>!r.has(l)).join(`
7
+ `)}function Ae({maxOldSpaceMb:s,conditions:o=[],base:t=process.env.NODE_OPTIONS??""}){let n=t;for(let e of o){let r=`--conditions=${e}`;n.includes(r)||(n=n?`${n} ${r}`:r)}if(s&&!n.includes("--max-old-space-size")){let e=`--max-old-space-size=${s}`;n=n?`${n} ${e}`:e}return n.trim()}function ke({maxOldSpaceMb:s,conditions:o=[],nodeEnv:t="development",extraEnv:n={}}){let e={...process.env};e.NODE_ENV||(e.NODE_ENV=t);let r=Ae({maxOldSpaceMb:s,conditions:o,base:e.NODE_OPTIONS??""});return r&&(e.NODE_OPTIONS=r),Object.assign(e,n),e}function et(s,o){return[s,...o?["--config",o]:[],"run"]}function ue(s,o){let{cwd:t,vitestBin:n,vitestConfig:e,maxOldSpaceMb:r,conditions:a=[],nodeEnv:l="development",vitestArgs:u=[],streamOutput:c=!0,excludePaths:f=[],extraEnv:p={}}=o;return new Promise($=>{let m=Date.now(),g=f.flatMap(S=>["--exclude",S]),E=[...et(n,e),...u,...g,s],v=ke({maxOldSpaceMb:r,conditions:a,nodeEnv:l,extraEnv:p}),x=Re(process.execPath,E,{cwd:t,stdio:["ignore","pipe","pipe"],env:v}),w="",I="";x.stdout?.on("data",S=>{w+=S.toString(),c&&process.stdout.write(S)}),x.stderr?.on("data",S=>{I+=S.toString(),c&&process.stderr.write(S)}),x.on("close",S=>{let O=Date.now()-m,L=`${w}
8
+ ${I}`,T=Ie(L);$({file:s,code:S??1,duration:T.duration>0?T.duration:O,testFilesPass:T.testFilesPass,testFilesFail:T.testFilesFail,testsPass:T.testsPass,testsFail:T.testsFail,testsSkip:T.testsSkip,heapMb:T.heapMb,errors:T.errors,rawOutput:L})}),x.on("error",S=>{$({file:s,code:1,duration:Date.now()-m,testFilesPass:0,testFilesFail:1,testsPass:0,testsFail:0,testsSkip:0,heapMb:null,errors:[S.message],rawOutput:S.toString()})})})}function Mt(s){let{cwd:o,vitestBin:t,vitestConfig:n,maxOldSpaceMb:e,conditions:r=[],nodeEnv:a="development",vitestArgs:l=[]}=s;return new Promise(u=>{let c=[...et(t,n),...l],f=ke({maxOldSpaceMb:e,conditions:r,nodeEnv:a}),p=Re(process.execPath,c,{cwd:o,stdio:"inherit",env:f});p.on("close",$=>u($??1)),p.on("error",()=>u(1))})}function je(s,o){let{cwd:t,vitestBin:n,vitestConfig:e,maxOldSpaceMb:r,conditions:a=[],nodeEnv:l="development",extraCoverageArgs:u=[],quietOutput:c=!1}=o;return new Promise(f=>{let p=e?["--config",e]:[],$=c?["--color"]:[],m=[n,...p,"--mergeReports",s,"--run","--coverage",...$,...u],g=ke({maxOldSpaceMb:r,conditions:a,nodeEnv:l}),E=Re(process.execPath,m,{cwd:t,stdio:c?["ignore","pipe","pipe"]:"inherit",env:g}),v="",x="";c&&(E.stdout?.on("data",w=>v+=w.toString()),E.stderr?.on("data",w=>x+=w.toString())),E.on("close",w=>f({exitCode:w??1,output:`${v}
9
+ ${x}`})),E.on("error",()=>f({exitCode:1,output:""}))})}import W from"chalk";function Y(s){let o=Math.max(0,Math.floor(s/1e3)),t=Math.floor(o/3600),n=Math.floor(o%3600/60),e=o%60;return t>0?`${t}:${n.toString().padStart(2,"0")}:${e.toString().padStart(2,"0")}`:`${n}:${e.toString().padStart(2,"0")}`}function Ne(s){let o=!!process.stdout.isTTY,t=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],n=26,e=Date.now(),r=0,a=0,l=0,u=0,c=0,f=0,p=null;function $(){let v=s===0?100:r/s*100,x=Date.now()-e,I=(r>0?x/r:0)*Math.max(s-r,0),S=Math.round(v/100*n),O=t[u%t.length],L=`[${"=".repeat(S)}${"-".repeat(Math.max(0,n-S))}]`,T=v.toFixed(1).padStart(5);return v<30?T=W.red(T+"%"):v<70?T=W.rgb(255,136,0)(T+"%"):v>99?T=W.green(T+"%"):T=W.yellow(T+"%"),o?`${W.green(O)} ${W.green(L)} ${W.bold(T)} ${r}/${s} | active ${a} | failed ${l} | ETA ${Y(I)} | elapsed ${Y(x)}`:`progress ${v.toFixed(1)}% ${r}/${s} | active ${a} | failed ${l} | eta ${Y(I)} | elapsed ${Y(x)}`}function m(v=!1){let x=$();if(u++,o){let I=x.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"").length;c=Math.max(c,I),process.stdout.write(`\r${x.padEnd(c," ")}`);return}let w=Date.now();(v||w-f>=2e3)&&(console.log(x),f=w)}function g(){!o||p||(p=setInterval(()=>{r>=s&&a===0||m()},120),p.unref?.())}function E(){p&&(clearInterval(p),p=null)}return m(!0),g(),{onStart(){a++,m()},onComplete(v){a=Math.max(0,a-1),r++,v&&l++,m(!0)},finish(){E(),m(!0),o&&process.stdout.write(`
10
+ `)}}}var Le={onStart(){},onComplete(s){},finish(){}};import tt from"fs/promises";import X from"path";import M from"chalk";function _e(s){if(s.length!==0){console.log(`
11
+ ${"=".repeat(80)}`),console.log(M.bold.red("\u2716 FAILED TEST FILES (VERBOSE OUTPUT)")),console.log("=".repeat(80));for(let o of s)console.log(`
12
+ ${M.red("\u2716")} ${M.red(o.file)} ${M.dim(`(exit ${o.code})`)}`),o.rawOutput?.trim()?console.log(o.rawOutput.trimEnd()):console.log(M.dim("(no child output captured)"));console.log(`
13
+ ${"=".repeat(80)}`)}}function Be(s,o){if(s===0){let t="% Coverage report from v8",n=o.split(`
14
+ `),e=n.findIndex(r=>V(r).includes(t));if(e>=0){let r=n.length;for(let l=e+1;l<n.length;l++){let u=V(n[l]).trimStart();if(u.startsWith("stderr |")||u.startsWith("stdout |")){r=l;break}}if(n.slice(e+1,r).join(`
15
+ `).trimEnd()){let l=n.slice(e,r).join(`
16
+ `).trimEnd();console.log(`
17
+ ${l}
18
+ `)}}}else{let t=o.trimEnd();t&&console.error(t)}}function Tt(s){let o=(n,e)=>e===0?100:parseFloat((n/e*100).toFixed(2)),t={total:{statements:{total:0,covered:0,pct:0},branches:{total:0,covered:0,pct:0},functions:{total:0,covered:0,pct:0},lines:{total:0,covered:0,pct:0}}};for(let[n,e]of Object.entries(s)){let r=Object.keys(e.s??{}),a=r.length,l=r.filter(v=>e.s[v]>0).length,u=Object.keys(e.f??{}),c=u.length,f=u.filter(v=>e.f[v]>0).length,p=0,$=0;for(let v of Object.values(e.b??{}))p+=v.length,$+=v.filter(x=>x>0).length;let m=new Set,g=new Set;for(let[v,x]of Object.entries(e.statementMap??{})){let w=x?.start?.line;w!=null&&(g.add(w),(e.s??{})[v]>0&&m.add(w))}let E={statements:{total:a,covered:l,pct:o(l,a)},branches:{total:p,covered:$,pct:o($,p)},functions:{total:c,covered:f,pct:o(f,c)},lines:{total:g.size,covered:m.size,pct:o(m.size,g.size)}};t[n]=E;for(let v of["statements","branches","functions","lines"])t.total[v].total+=E[v].total,t.total[v].covered+=E[v].covered}for(let n of["statements","branches","functions","lines"]){let{total:e,covered:r}=t.total[n];t.total[n].pct=o(r,e)}return t}async function Ve(s,o,t=10,n={}){let{silent:e=!1}=n,r=X.resolve(s,"coverage"),a=o.find(g=>g.startsWith("--coverage.reportsDirectory="));if(a){let g=a.split("=").slice(1).join("=");r=X.isAbsolute(g)?g:X.resolve(s,g)}let l;try{let g=await tt.readFile(X.join(r,"coverage-summary.json"),"utf8");l=JSON.parse(g)}catch{try{let g=await tt.readFile(X.join(r,"coverage-final.json"),"utf8");l=Tt(JSON.parse(g))}catch{return e||console.log(M.dim(" (no coverage JSON found \u2014 skipping summary)")),null}}let{total:u,...c}=l;if(t>0){let g=Object.entries(c).map(([S,O])=>({file:X.relative(s,S),lines:O.lines?.pct??0,stmts:O.statements?.pct??0,fns:O.functions?.pct??0,branches:O.branches?.pct??0})).filter(S=>Math.min(S.lines,S.stmts,S.fns,S.branches)<100).sort((S,O)=>Math.min(S.lines,S.stmts,S.fns,S.branches)-Math.min(O.lines,O.stmts,O.fns,O.branches)),E=g.slice(0,t);e||(console.log(`
19
+ `+M.bold("\u{1F4C9} WORST COVERAGE FILES (lowest metric)")),console.log("-".repeat(80)),E.forEach(({file:S,lines:O,stmts:L,fns:T,branches:_})=>{let P=Math.min(O,L,T,_),ge=M.dim(`lines ${O.toFixed(0)}% | stmts ${L.toFixed(0)}% | fns ${T.toFixed(0)}% | branches ${_.toFixed(0)}%`);console.log(` ${N(M,P)}% ${M.dim(S)} ${ge}`)}),g.length>t&&console.log(M.dim(` ... and ${g.length-t} more files`)));let v=u.lines?.pct??0,x=u.statements?.pct??0,w=u.functions?.pct??0,I=u.branches?.pct??0;return e||console.log(`
20
+ ${M.bold("Coverage")} ${N(M,v)}% lines ${M.dim("|")} ${N(M,x)}% statements ${M.dim("|")} ${N(M,w)}% functions ${M.dim("|")} ${N(M,I)}% branches`),{coverageDir:r,total:u,worstFiles:E,worstFilesShown:E.length,worstFilesTotal:g.length,summary:l}}let f=u.lines?.pct??0,p=u.statements?.pct??0,$=u.functions?.pct??0,m=u.branches?.pct??0;return e||console.log(`
21
+ ${M.bold("Coverage")} ${N(M,f)}% lines ${M.dim("|")} ${N(M,p)}% statements ${M.dim("|")} ${N(M,$)}% functions ${M.dim("|")} ${N(M,m)}% branches`),{coverageDir:r,total:u,worstFiles:[],worstFilesShown:0,worstFilesTotal:0,summary:l}}import ie from"fs/promises";import ot from"fs";import ee from"path";var st="tmp/vitest-runner",Et=/^(\d+)-\d+$/;function yt(s){try{return process.kill(s,0),!0}catch(o){return o.code!=="ESRCH"}}function Pt(s){return String(s).replace(/[^a-zA-Z0-9_-]+/g,"-").slice(0,80)||"scratch"}async function nt(s,o){let t=ee.resolve(s,o),n;try{n=await ie.readdir(t,{withFileTypes:!0})}catch{return}for(let e of n){if(!e.isDirectory())continue;let r=e.name.match(Et);if(!r)continue;let a=parseInt(r[1],10);yt(a)||await ie.rm(ee.join(t,e.name),{recursive:!0,force:!0}).catch(()=>{})}}async function rt(s,o){let t=ee.resolve(s,o),n=ee.join(t,`${process.pid}-${Date.now()}`);return await ie.mkdir(n,{recursive:!0}),n}async function ze(s,o){let t=ee.join(s,`worker-${o}`);return await ie.mkdir(t,{recursive:!0}),t}async function it(s){await ie.rm(s,{recursive:!0,force:!0}).catch(()=>{})}function ct(s){try{ot.rmSync(s,{recursive:!0,force:!0})}catch{}}function Ot(s){let o=process.env.VITEST_RUNNER_TMP;if(!o)throw new Error("makeRunTmpDir() requires VITEST_RUNNER_TMP to be set \u2014 call it from a test file run by vitest-runner.");return ot.mkdtempSync(ee.join(o,`${Pt(s)}-`))}function at(s,o,t){let n=s.replace(/\\/g,"/"),e;for(let{pattern:r,heapMb:a}of t)if(n.includes(r)){e=a;break}if(!(e===void 0&&o===void 0))return e===void 0?o:o===void 0?e:Math.max(e,o)}async function $o(s){let o=s.cwd??process.cwd(),t=s.scratchDir??st,n=s.keepTmp??!1;await nt(o,t);let e=await rt(o,t),r=()=>{ct(e),process.exitCode=130,process.exit()};n||(process.once("SIGINT",r),process.once("SIGTERM",r));try{return await Dt(s,e)}finally{process.removeListener("SIGINT",r),process.removeListener("SIGTERM",r),n||await it(e)}}async function Dt(s,o){let{cwd:t=process.cwd(),testDir:n,testPatterns:e=[],testListFile:r,testFilePattern:a,vitestArgs:l=[],showErrorDetails:u=!0,coverageQuiet:c=!1,suppressFileOutput:f=!1,suppressPassingFiles:p=!1,topSummary:$=!0,json:m=!1,workers:g=parseInt(process.env.VITEST_WORKERS??"4",10),worstCoverageCount:E=10,blobsDir:v,mergeReports:x=!0,earlyRunPatterns:w=[],perFileHeapOverrides:I=[],conditions:S=[],nodeEnv:O="development",_testResultsOverride:L=null}=s,T=0,_=s.maxOldSpaceMb??(process.env.VITEST_HEAP_MB?parseInt(process.env.VITEST_HEAP_MB,10):void 0),P=!m,ge=ye(t,"vitest","vitest"),lt=await Pe(t,s.vitestConfig),me={cwd:t,vitestBin:ge,vitestConfig:lt,conditions:S,nodeEnv:O},A=[...l];if(c&&!A.some(i=>i==="--coverage"||i.startsWith("--coverage."))&&A.unshift("--coverage"),A.some(i=>i==="--coverage"||i.startsWith("--coverage."))){let i=de.resolve(t,v??".vitest-coverage-blobs"),d=de.resolve(t,".vitest-coverage-tmp");await Promise.all([q.rm(i,{recursive:!0,force:!0}),q.rm(d,{recursive:!0,force:!0})]),await Promise.all([q.mkdir(i,{recursive:!0}),q.mkdir(d,{recursive:!0})]);let b=await fe({cwd:t,testDir:n,testPatterns:e,testListFile:r,testFilePattern:a,earlyRunPatterns:w}),D=De(b);if(b.length===0){let F=e.length>0?`\u274C No Vitest test files found matching: ${e.join(", ")}`:"\u274C No Vitest test files found";return P&&console.log(F),m?{exitCode:1,mode:"coverage",message:F,options:{cwd:t,testDir:n,testPatterns:e,testListFile:r,workers:g,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:p,topSummary:$,vitestArgs:A}}:1}let k=A.filter(F=>F!=="--coverage"&&F.startsWith("--coverage.")),Fe=A.filter(F=>F!=="--coverage"&&!F.startsWith("--coverage.")),ne=b.filter(F=>w.some(y=>F.replace(/\\/g,"/").includes(y))),Se=b.filter(F=>!w.some(y=>F.replace(/\\/g,"/").includes(y))),be=c||f||m,ht=c&&!m;!c&&!f&&P&&(console.log(`
22
+ \u{1F9EA} Running ${b.length} test files for coverage (blob + merge mode)`),console.log(`\u2699\uFE0F Workers: ${g} (${ne.length} solo first, then parallel)`),_&&console.log(`\u{1F9E0} Heap limit: ${_} MB`),console.log(""));let xe=ht?Ne(b.length):Le,B=[],we=0,Qe=async F=>{let y=de.join(i,`run-${we}.blob`);we++,!be&&P&&(console.log(`
23
+ ${"=".repeat(80)}`),console.log(`\u25B6\uFE0F ${F}`),console.log("=".repeat(80))),xe.onStart();let U=de.join(d,`run-${we}`),vt=[...Fe,"--coverage",`--coverage.reportsDirectory=${U}`,"--reporter=default","--reporter=blob",`--outputFile=${y}`],$t=await ze(o,T++),H=await ue(F,{...me,maxOldSpaceMb:at(F,_,I),vitestArgs:vt,streamOutput:!be,excludePaths:D.get(F),extraEnv:{VITEST_RUNNER_TMP:$t}});if(B.push(H),xe.onComplete(H.code!==0),!be&&P){let Ye=(H.duration/1e3).toFixed(2);if(H.code===0){let Ft=H.heapMb?` | ${H.heapMb} MB heap`:"";console.log(`
24
+ \u2705 PASSED (${Ye}s${Ft})
25
+ `)}else console.log(`
26
+ \u274C FAILED (exit code ${H.code}, ${Ye}s)
27
+ `)}};for(let F of ne)await Qe(F).catch(y=>{P&&console.error(`Error running ${F}:`,y)});let Me=0,Z=new Set;for(;Me<Se.length||Z.size>0;){for(;Me<Se.length&&Z.size<g;){let F=Se[Me++],y=Qe(F).catch(U=>{P&&console.error(`Error running ${F}:`,U)}).finally(()=>Z.delete(y));Z.add(y)}Z.size>0&&await Promise.race(Z)}xe.finish();let Te=(await q.readdir(i).catch(()=>[])).filter(F=>F.endsWith(".blob"));if(Te.length===0){let F="\u274C No coverage blobs were generated \u2014 coverage report cannot be produced";return P&&console.error(F),m?{exitCode:1,mode:"coverage",message:F,options:{cwd:t,testDir:n,testPatterns:e,testListFile:r,workers:g,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:p,topSummary:$,showErrorDetails:u,worstCoverageCount:E,vitestArgs:A},totals:{testFiles:b.length,failedFiles:B.filter(y=>y.code!==0).length,passedFiles:B.filter(y=>y.code===0).length},results:{all:B,failed:B.filter(y=>y.code!==0)},merge:{blobFiles:0,exitCode:1,output:""},coverageSummary:null}:1}let ae=0,Ee="",Ze=null;x&&(!c&&!f&&P&&(console.log(`
28
+ ${"=".repeat(80)}`),console.log(`\u{1F4CA} Merging ${Te.length} coverage blobs into final report...`),console.log("=".repeat(80))),{exitCode:ae,output:Ee}=await je(i,{...me,maxOldSpaceMb:_,extraCoverageArgs:k,quietOutput:c||m}),c&&P&&Be(ae,Ee),Ze=await Ve(t,k,E,{silent:m})),await Promise.all([...x?[q.rm(i,{recursive:!0,force:!0}).catch(()=>{})]:[],q.rm(d,{recursive:!0,force:!0}).catch(()=>{})]);let re=B.filter(F=>F.code!==0);c&&P&&_e(re);let Ke=re.length>0?1:ae;if(m){let F=B.filter(y=>y.heapMb!==null);return{exitCode:Ke,mode:"coverage",options:{cwd:t,testDir:n,testPatterns:e,testListFile:r,workers:g,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:p,topSummary:$,showErrorDetails:u,worstCoverageCount:E,vitestArgs:A},totals:{testFiles:b.length,failedFiles:re.length,passedFiles:B.length-re.length},results:{all:B,failed:re},merge:{blobFiles:Te.length,exitCode:ae,output:Ee},coverageSummary:Ze,...$?{topMemoryUsers:[...F].sort((y,U)=>U.heapMb-y.heapMb).slice(0,10),topDuration:[...B].sort((y,U)=>U.duration-y.duration).slice(0,10)}:{}}}return Ke}let G=await fe({cwd:t,testDir:n,testPatterns:e,testListFile:r,testFilePattern:a,earlyRunPatterns:w}),pt=De(G);if(G.length===0){let i=e.length>0?`\u274C No Vitest test files found matching: ${e.join(", ")}`:"\u274C No Vitest test files found";return P&&console.log(i),m?{exitCode:1,mode:"standard",message:i,options:{cwd:t,testDir:n,testPatterns:e,testListFile:r,workers:g,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:p,topSummary:$,vitestArgs:A}}:1}let Ue=G.filter(i=>w.some(d=>i.replace(/\\/g,"/").includes(d))),he=G.filter(i=>!w.some(d=>i.replace(/\\/g,"/").includes(d))),ft=Date.now(),He=new Date().toLocaleTimeString("en-US",{hour12:!1});P&&!f&&e.length>0?console.log(`
29
+ \u{1F9EA} Running ${G.length} test files matching: ${e.join(", ")}`):P&&!f&&console.log(`
30
+ \u{1F9EA} Running ${G.length} test files (${Ue.length} solo first, then parallel)`),P&&!f&&(console.log(`\u2699\uFE0F Workers: ${g}`),_&&console.log(`\u{1F9E0} Heap limit: ${_} MB`),A.length>0&&console.log(`\u{1F527} Vitest args: ${A.join(" ")}`),console.log(""));let C=[...L??[]],We=async i=>{!f&&P&&(console.log(`
31
+ ${"=".repeat(80)}`),console.log(`\u25B6\uFE0F ${i}`),console.log("=".repeat(80)));let d=await ze(o,T++),b=await ue(i,{...me,maxOldSpaceMb:at(i,_,I),vitestArgs:A,streamOutput:!f&&!m,excludePaths:pt.get(i),extraEnv:{VITEST_RUNNER_TMP:d}});if(!f&&P){let D=(b.duration/1e3).toFixed(2);if(b.code===0){let k=b.heapMb?` | ${b.heapMb} MB heap`:"";console.log(`
32
+ \u2705 PASSED (${D}s${k})
33
+ `)}else console.log(`
34
+ \u274C FAILED (exit code ${b.code}, ${D}s)
35
+ `)}return b};if(!L){for(let b of Ue){let D=await We(b).catch(k=>(P&&console.error(`Error running ${b}:`,k),null));D&&C.push(D)}let i=0,d=new Set;for(;i<he.length||d.size>0;){for(;i<he.length&&d.size<g;){let b=he[i++],D=We(b).then(k=>C.push(k)).catch(k=>{P&&console.error(`Error running ${b}:`,k)}).finally(()=>d.delete(D));d.add(D)}d.size>0&&await Promise.race(d)}}let J=C.reduce((i,d)=>i+d.testFilesPass,0),z=C.reduce((i,d)=>i+d.testFilesFail,0),te=C.reduce((i,d)=>i+d.testsPass,0),oe=C.reduce((i,d)=>i+d.testsFail,0),se=C.reduce((i,d)=>i+(d.testsSkip||0),0),ut=C.reduce((i,d)=>i+d.duration,0),Q=C.filter(i=>i.code!==0),ce=C.filter(i=>i.code===0),dt=Q.length>0?1:0,qe=process.memoryUsage(),ve=Math.round(qe.heapUsed/1024/1024),$e=Math.round(qe.rss/1024/1024),R=C.filter(i=>i.heapMb!==null),Ge=((Date.now()-ft)/1e3).toFixed(2),Je=(ut/1e3).toFixed(2);if(m)return{exitCode:dt,mode:"standard",options:{cwd:t,testDir:n,testPatterns:e,testListFile:r,workers:g,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:p,topSummary:$,showErrorDetails:u,worstCoverageCount:E,vitestArgs:A},totals:{testFilesPass:J,testFilesFail:z,testsPass:te,testsFail:oe,testsSkip:se,totalTests:te+oe+se},timing:{startAt:He,wallClockSeconds:Number(Ge),testsSeconds:Number(Je)},heap:{scriptHeapMb:ve,scriptRssMb:$e,maxHeapMb:R.length>0?Math.max(...R.map(i=>i.heapMb)):null,avgHeapMb:R.length>0?Number((R.reduce((i,d)=>i+d.heapMb,0)/R.length).toFixed(0)):null},results:{all:C,passed:ce,failed:Q},...$?{topMemoryUsers:[...R].sort((i,d)=>d.heapMb-i.heapMb).slice(0,10),topDuration:[...C].sort((i,d)=>d.duration-i.duration).slice(0,10)}:{}};console.log(`
36
+ `+"=".repeat(80)),$&&R.length>0&&(console.log(`
37
+ `+h.bold("\u{1F9E0} TOP MEMORY USERS")),console.log("-".repeat(80)),[...R].sort((i,d)=>d.heapMb-i.heapMb).slice(0,10).forEach(i=>{console.log(` ${String(i.heapMb).padStart(4)} MB ${h.dim(i.file)}`)})),$&&C.length>0&&(console.log(`
38
+ `+h.bold("\u23F1\uFE0F TOP DURATION")),console.log("-".repeat(80)),[...C].sort((i,d)=>d.duration-i.duration).slice(0,10).forEach(i=>{let d=(i.duration/1e3).toFixed(2);console.log(` ${(d+"s").padStart(8)} ${h.dim(i.file)}`)})),ce.length>0&&!p&&(console.log(`
39
+ `+"=".repeat(80)),console.log(h.bold.green("\u2713 PASSED TEST FILES")),console.log("=".repeat(80)),ce.forEach(i=>{let d=(i.duration/1e3).toFixed(2),b=[...i.heapMb?[`${i.heapMb} MB`]:[],`${d}s`],D=i.testsPass>0?` - ${i.testsPass} tests`:"";console.log(h.green(`\u2713 ${i.file}${D}`)+h.dim(` (${b.join(", ")})`))})),console.log(`
40
+ `+h.bold("=".repeat(80))),Q.length>0&&(console.log(`
41
+ \u274C ${Q.length} test file(s) failed`),console.log(h.bold.red(`
42
+ Failed Test Files:`)),Q.forEach(i=>{let d=(i.duration/1e3).toFixed(2),b=[...i.testsFail>0?[h.red(`${i.testsFail} failed`)]:[],...i.testsPass>0?[h.green(`${i.testsPass} passed`)]:[],...i.testsSkip>0?[h.yellow(`${i.testsSkip} skipped`)]:[]],D=[...i.heapMb?[`${i.heapMb} MB`]:[],`${d}s`],k=b.length>0?` (${b.join(", ")})`:"";if(console.log(` ${h.red("\u2716")} ${i.file}${k}`+h.dim(` [${D.join(", ")}]`)),u&&i.errors.length>0){let Fe=Ce(i.errors).split(`
43
+ `).map(ne=>ne.trim()?` ${ne}`:"").join(`
44
+ `);console.log(Fe),console.log("")}}),console.log("")),console.log(h.bold("=".repeat(80))),z>0&&J>0?console.log(` ${h.bold("Test Files")} ${h.red(`${z} failed`)} ${h.dim("|")} ${h.green(`${J} passed`)} ${h.dim(`(${J+z})`)}`):z>0?console.log(` ${h.bold("Test Files")} ${h.red(`${z} failed`)} ${h.dim(`(${z})`)}`):console.log(` ${h.bold("Test Files")} ${h.green(`${J} passed`)} ${h.dim(`(${J})`)}`);let gt=te+oe+se,mt=[...oe>0?[h.red(`${oe} failed`)]:[],...te>0?[h.green(`${te} passed`)]:[],...se>0?[h.yellow(`${se} skipped`)]:[]];if(console.log(` ${h.bold("Tests")} ${mt.join(` ${h.dim("|")} `)} ${h.dim(`(${gt})`)}`),console.log(` ${h.bold("Start at")} ${He}`),console.log(` ${h.bold("Duration")} ${Ge}s ${h.dim(`(tests ${Je}s)`)}`),R.length>0){let i=Math.max(...R.map(b=>b.heapMb)),d=(R.reduce((b,D)=>b+D.heapMb,0)/R.length).toFixed(0);console.log(` ${h.bold("Heap")} max ${i} MB | avg ${d} MB | script ${ve} MB (RSS ${$e} MB)`)}else console.log(` ${h.bold("Heap")} script ${ve} MB (RSS ${$e} MB)`);return Q.length>0?1:(console.log(`
45
+ \u2705 All ${ce.length} test files passed
46
+ `),0)}export{Ae as buildNodeOptions,N as colourPct,Ne as createCoverageProgressTracker,Ce as deduplicateErrors,le as discoverFilesInDir,fe as discoverVitestFiles,Y as formatDuration,Ot as makeRunTmpDir,Le as noopProgressTracker,Ie as parseVitestOutput,Ve as printCoverageSummary,Be as printMergeOutput,_e as printQuietCoverageFailureDetails,ye as resolveBin,Pe as resolveVitestConfig,$o as run,je as runMergeReports,ue as runSingleFile,Mt as runVitestDirect,pe as sortWithPriority,V as stripAnsi};
1335
47
  //# sourceMappingURL=index.mjs.map