@cldmv/vitest-runner 1.4.2 → 1.4.4

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