@cldmv/vitest-runner 1.2.0 → 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/README.md +50 -11
- package/bin/vitest-runner.mjs +110 -83
- package/dist/index.cjs +21 -0
- package/dist/index.mjs +47 -0
- package/package.json +31 -13
- package/types/src/bin/vitest-runner.d.mts +2 -0
- package/types/src/cli/args.d.mts +8 -0
- package/types/src/core/discover.d.mts +23 -0
- package/types/src/core/scratch.d.mts +64 -0
- package/types/src/core/spawn.d.mts +3 -1
- package/types/src/runner.d.mts +15 -0
- package/types/src/utils/ansi.d.mts +12 -0
- package/types/src/utils/duration.d.mts +12 -0
- package/types/src/utils/env.d.mts +12 -0
- package/index.cjs +0 -17
- package/index.mjs +0 -5
- package/src/cli/args.mjs +0 -148
- package/src/cli/help.mjs +0 -86
- package/src/core/discover.mjs +0 -167
- package/src/core/parse.mjs +0 -167
- package/src/core/progress.mjs +0 -164
- package/src/core/report.mjs +0 -255
- package/src/core/spawn.mjs +0 -219
- package/src/runner.mjs +0 -708
- package/src/utils/ansi.mjs +0 -32
- package/src/utils/duration.mjs +0 -25
- package/src/utils/env.mjs +0 -38
- package/src/utils/resolve.mjs +0 -86
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Sequential Vitest runner that spawns each test file in its own child process to
|
|
|
7
7
|
- Auto-detects your vitest config; accepts an explicit path if needed
|
|
8
8
|
- All standard Vitest CLI flags are forwarded unchanged
|
|
9
9
|
- Usable as a **CLI binary** or as a **programmatic Node.js API**
|
|
10
|
-
- Pure ESM
|
|
10
|
+
- Pure ESM source, bundled to a real CJS build for `require()` compatibility
|
|
11
11
|
|
|
12
12
|
[![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] <!-- [![GitHub release]][github_release_url] -->[![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] <!-- [![Release date]][release_date_url] -->[![npm last update]][npm_last_update_url] [![Coverage]][coverage_url]
|
|
13
13
|
|
|
@@ -15,6 +15,23 @@ Sequential Vitest runner that spawns each test file in its own child process to
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## ✨ What's New
|
|
19
|
+
|
|
20
|
+
### Latest: v1.4.3 (September 2026)
|
|
21
|
+
|
|
22
|
+
- **The published build is actually minified now** — `dist/index.mjs` drops from ~50KB to ~25KB and `bin/vitest-runner.mjs` from ~58KB to ~30KB; sourcemaps stay generated for local debugging regardless.
|
|
23
|
+
- **`dist/index.cjs` is a thin shim instead of a duplicate bundle** — it used to contain a full second copy of the module's logic (27.5KB); it's now a 912-byte `require(esm)` wrapper around the real ESM build, matching `@cldmv/uuid`'s `index.cjs` pattern, and it can't drift from `runner.mjs`'s actual exports.
|
|
24
|
+
- [View full v1.4.3 Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.3.md)
|
|
25
|
+
|
|
26
|
+
### Recent Releases
|
|
27
|
+
|
|
28
|
+
- **v1.4.2** (September 2026) — Fixed npm publish rejecting every release over missing `repository`/`bugs`/`homepage` fields ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.2.md))
|
|
29
|
+
- **v1.4.1** (September 2026) — Completed the vitest 5 upgrade, fixed the README npm badges, and fixed a `prepack` bug crashing every publish job ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.1.md))
|
|
30
|
+
- **v1.4.0** (September 2026) — Real bundled `dist/`/`bin/` build, a working CLI, and a runner-owned scratch-directory lifecycle ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.0.md))
|
|
31
|
+
- **v1.3.3** (September 2026) — Dependency bump (`brace-expansion`, `postcss`) via Dependabot.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
18
35
|
## Requirements
|
|
19
36
|
|
|
20
37
|
- Node.js ≥ 18
|
|
@@ -60,6 +77,8 @@ vitest-runner [OPTIONS] [PATTERNS...]
|
|
|
60
77
|
| `--json` | Print a JSON run report (no runner text output) |
|
|
61
78
|
| `--blobs-dir <path>` | Directory for per-file coverage blobs (default: `.vitest-coverage-blobs`, relative to `cwd`) |
|
|
62
79
|
| `--no-merge-reports` | Produce the coverage blobs but skip the merge and summary, leaving them in `--blobs-dir` for an external merge step |
|
|
80
|
+
| `--keep-tmp` | Keep this run's scratch directory instead of removing it on completion |
|
|
81
|
+
| `--scratch-dir <path>` | Per-run scratch root, relative to `cwd` (default: `tmp/vitest-runner`) |
|
|
63
82
|
| `--help`, `-h` | Print this help and exit |
|
|
64
83
|
|
|
65
84
|
### Test patterns
|
|
@@ -150,7 +169,7 @@ vitest-runner --json --no-top-summary
|
|
|
150
169
|
import { run } from "vitest-runner";
|
|
151
170
|
|
|
152
171
|
// CommonJS
|
|
153
|
-
const { run } =
|
|
172
|
+
const { run } = require("vitest-runner");
|
|
154
173
|
```
|
|
155
174
|
|
|
156
175
|
### `run(options)` → `Promise<number | object>`
|
|
@@ -193,6 +212,22 @@ process.exit(code);
|
|
|
193
212
|
| `perFileHeapOverrides` | `PerFileHeapOverride[]` | `[]` | Per-file minimum heap ceilings; the maximum of this and `maxOldSpaceMb` wins |
|
|
194
213
|
| `conditions` | `string[]` | `[]` | Additional `--conditions` Node flags forwarded to children |
|
|
195
214
|
| `nodeEnv` | `string` | `'development'` | Value written to `NODE_ENV` in child processes |
|
|
215
|
+
| `scratchDir` | `string` | `'tmp/vitest-runner'` | Per-run scratch root, relative to `cwd` (or absolute). A subdirectory is created per file invocation and exposed to it via `VITEST_RUNNER_TMP` |
|
|
216
|
+
| `keepTmp` | `boolean` | `false` | Keep the run's scratch root instead of removing it on completion (normal exit, failure, or SIGINT/SIGTERM) |
|
|
217
|
+
|
|
218
|
+
### Scratch directories (`VITEST_RUNNER_TMP` / `makeRunTmpDir`)
|
|
219
|
+
|
|
220
|
+
Every run gets its own scratch root (`<scratchDir>/<pid>-<timestamp>/`), created before any file runs and removed once the run completes — on success, on failure, and on SIGINT/SIGTERM — unless `keepTmp` is set. Each file invocation gets its own subdirectory under that root, exposed to the child as `process.env.VITEST_RUNNER_TMP`. Stale roots left by a crashed prior run (dead PID) are swept at the start of the next run.
|
|
221
|
+
|
|
222
|
+
From a test file, use `makeRunTmpDir(label)` to get a fresh, uniquely-named subdirectory instead of managing your own `mkdtemp` base:
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
import { makeRunTmpDir } from "vitest-runner";
|
|
226
|
+
|
|
227
|
+
const dir = makeRunTmpDir("my-fixture"); // a fresh directory under VITEST_RUNNER_TMP
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
CLI flags: `--scratch-dir <path>` and `--keep-tmp` (see [Runner flags](#runner-flags) below).
|
|
196
231
|
|
|
197
232
|
#### `PerFileHeapOverride`
|
|
198
233
|
|
|
@@ -328,29 +363,33 @@ await run({ cwd, testDir: "src", testFilePattern: /\.spec\.ts$/i });
|
|
|
328
363
|
## Source layout
|
|
329
364
|
|
|
330
365
|
```text
|
|
331
|
-
|
|
332
|
-
index.
|
|
333
|
-
|
|
334
|
-
|
|
366
|
+
dist/ ← built library entry (npm run build / tsup) — generated, not committed
|
|
367
|
+
index.mjs ← bundled ESM entry
|
|
368
|
+
index.cjs ← bundled CJS entry (real sync require, generated from the same source)
|
|
369
|
+
bin/ ← built CLI binary (npm run build / tsup) — generated, not committed
|
|
370
|
+
vitest-runner.mjs ← bundled CLI, from src/bin/vitest-runner.mjs (shebang preserved)
|
|
335
371
|
src/
|
|
336
372
|
runner.mjs ← main run() API + re-exports
|
|
373
|
+
bin/
|
|
374
|
+
vitest-runner.mjs ← CLI entry SOURCE — run this directly for source-level dev/testing
|
|
337
375
|
utils/
|
|
338
376
|
ansi.mjs ← stripAnsi, colourPct
|
|
339
377
|
duration.mjs ← formatDuration
|
|
340
378
|
env.mjs ← buildNodeOptions
|
|
341
379
|
resolve.mjs ← resolveBin, resolveVitestConfig
|
|
342
380
|
core/
|
|
343
|
-
discover.mjs ← discoverVitestFiles, sortWithPriority
|
|
381
|
+
discover.mjs ← discoverVitestFiles, sortWithPriority, computeFilterConflicts
|
|
344
382
|
parse.mjs ← parseVitestOutput, deduplicateErrors
|
|
345
383
|
spawn.mjs ← runSingleFile, runVitestDirect, runMergeReports
|
|
346
384
|
report.mjs ← printCoverageSummary, printMergeOutput
|
|
347
385
|
progress.mjs ← createCoverageProgressTracker
|
|
386
|
+
scratch.mjs ← makeRunTmpDir + the scratch-directory lifecycle
|
|
348
387
|
cli/
|
|
349
388
|
args.mjs ← parseArguments
|
|
350
389
|
help.mjs ← showHelp
|
|
351
390
|
```
|
|
352
391
|
|
|
353
|
-
All sub-module utilities are re-exported from the root entry point, so deep imports are optional.
|
|
392
|
+
`src/` is not published — only `dist/`, `bin/`, and `types/` ship (see [Programmatic API](#programmatic-api) for the `vitest-runner-dev` export condition, used when developing against a workspace/local checkout instead of the published package). All sub-module utilities are re-exported from the root entry point, so deep imports are optional.
|
|
354
393
|
|
|
355
394
|
---
|
|
356
395
|
|
|
@@ -364,15 +403,15 @@ MIT
|
|
|
364
403
|
<!-- [release date]: https://img.shields.io/github/release-date/CLDMV/vitest-runner?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 -->
|
|
365
404
|
<!-- [release_date_url]: https://github.com/CLDMV/vitest-runner/releases -->
|
|
366
405
|
|
|
367
|
-
[npm version]: https://img.shields.io/npm/v
|
|
406
|
+
[npm version]: https://img.shields.io/npm/v/%40cldmv%2Fvitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
|
|
368
407
|
[npm_version_url]: https://www.npmjs.com/package/@cldmv/vitest-runner
|
|
369
|
-
[npm downloads]: https://img.shields.io/npm/dm
|
|
408
|
+
[npm downloads]: https://img.shields.io/npm/dm/%40cldmv%2Fvitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
|
|
370
409
|
[npm_downloads_url]: https://www.npmjs.com/package/@cldmv/vitest-runner
|
|
371
410
|
[github downloads]: https://img.shields.io/github/downloads/CLDMV/vitest-runner/total?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
|
|
372
411
|
[github_downloads_url]: https://github.com/CLDMV/vitest-runner/releases
|
|
373
412
|
[last commit]: https://img.shields.io/github/last-commit/CLDMV/vitest-runner?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
|
|
374
413
|
[last_commit_url]: https://github.com/CLDMV/vitest-runner/commits
|
|
375
|
-
[npm last update]: https://img.shields.io/npm/last-update
|
|
414
|
+
[npm last update]: https://img.shields.io/npm/last-update/%40cldmv%2Fvitest-runner?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
|
|
376
415
|
[npm_last_update_url]: https://www.npmjs.com/package/@cldmv/vitest-runner
|
|
377
416
|
[coverage]: https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FCLDMV%2Fvitest-runner%2Fbadges%2Fcoverage.json&style=for-the-badge&logo=vitest&logoColor=white
|
|
378
417
|
[coverage_url]: https://github.com/CLDMV/vitest-runner/blob/badges/coverage.json
|
package/bin/vitest-runner.mjs
CHANGED
|
@@ -1,97 +1,124 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* Mirrors its own output to a log file when --coverage-quiet or --log-file is set, then
|
|
7
|
-
* delegates all logic to `src/runner.mjs` via the programmatic `run()` API.
|
|
8
|
-
*/
|
|
2
|
+
import{createWriteStream as jt,mkdirSync as Nt}from"fs";import he from"path";var Et=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 tt(e){let o=[],s=[],r=[],t,n=!0,a=!1,p,u=!1,c=!1,f=!0,d=!1,b,h=!0,m,M=!1,v,w=!1,F;for(let E=0;E<e.length;E++){let l=e[E];l==="--test-list"?t=e[++E]:l.startsWith("--test-list=")?t=l.slice(12):l==="--no-error-details"?n=!1:l==="--coverage-quiet"?a=!0:l==="--log-file"?p=e[++E]:l.startsWith("--log-file=")?p=l.slice(11):l==="--suppress-file-output"?u=!0:l==="--suppress-passing-files"?c=!0:l==="--no-top-summary"?f=!1:l==="--json"?d=!0:l==="--blobs-dir"?b=e[++E]:l.startsWith("--blobs-dir=")?b=l.slice(12):l==="--no-merge-reports"?h=!1:l==="--workers"?m=parseInt(e[++E],10):l.startsWith("--workers=")?m=parseInt(l.slice(10),10):l==="--solo-pattern"?r.push(e[++E]):l.startsWith("--solo-pattern=")?r.push(l.slice(15)):l==="--file-pattern"?v=new RegExp(e[++E],"i"):l.startsWith("--file-pattern=")?v=new RegExp(l.slice(15),"i"):l==="--keep-tmp"?w=!0:l==="--scratch-dir"?F=e[++E]:l.startsWith("--scratch-dir=")?F=l.slice(14):l==="--help"||l==="-h"?M=!0:(l.startsWith("--")||l.startsWith("-"))&&!Et.has(l)?(o.push(l),E+1<e.length&&!e[E+1].startsWith("-")&&o.push(e[++E])):s.push(l)}return{testListFile:t,showErrorDetails:n,coverageQuiet:a,logFile:p,suppressFileOutput:u,suppressPassingFiles:c,topSummary:f,json:d,blobsDir:b,mergeReports:h,help:M,workers:m,soloPatterns:r,testFilePattern:v,vitestPassthroughArgs:o,testPatterns:s,keepTmp:w,scratchDir:F}}import Z from"chalk";function st(){console.log(`
|
|
3
|
+
${Z.bold("Vitest Sequential Runner")}
|
|
4
|
+
Runs each test file in its own Vitest process to avoid OOM issues.
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { parseArguments } from "../src/cli/args.mjs";
|
|
13
|
-
import { showHelp } from "../src/cli/help.mjs";
|
|
14
|
-
import { run } from "../src/runner.mjs";
|
|
15
|
-
import { stripAnsi } from "../src/utils/ansi.mjs";
|
|
6
|
+
${Z.bold("USAGE:")}
|
|
7
|
+
vitest-runner [OPTIONS] [PATTERNS]
|
|
16
8
|
|
|
17
|
-
|
|
9
|
+
${Z.bold("SPECIAL FLAGS:")}
|
|
10
|
+
--test-list <file> Run only the files listed in a JSON array file
|
|
11
|
+
--file-pattern <regex> Override the file discovery regex (default: \\.test\\.vitest\\.(?:js|mjs|cjs)$)
|
|
12
|
+
--workers <n> Number of parallel workers (default: 4 or VITEST_WORKERS)
|
|
13
|
+
--solo-pattern <pat> Run files matching this path substring solo first (repeatable)
|
|
14
|
+
--no-error-details Hide detailed error output (show only counts)
|
|
15
|
+
--coverage-quiet Implies --coverage; show progress bar + final summaries only
|
|
16
|
+
--log-file <path> Path for mirrored runner output (default: coverage/coverage-run.log with --coverage-quiet)
|
|
17
|
+
--suppress-file-output Suppress per-file runner output blocks
|
|
18
|
+
--suppress-passing-files Hide the PASSED TEST FILES section in the final summary
|
|
19
|
+
--no-top-summary Hide TOP MEMORY USERS and TOP DURATION sections
|
|
20
|
+
--json Emit JSON summary output instead of text logs
|
|
21
|
+
--blobs-dir <path> Directory for per-file coverage blobs (default: .vitest-coverage-blobs)
|
|
22
|
+
--no-merge-reports Leave coverage blobs in --blobs-dir without merging (for an external merge)
|
|
23
|
+
--keep-tmp Keep this run's scratch directory instead of removing it on completion
|
|
24
|
+
--scratch-dir <path> Per-run scratch root, relative to cwd (default: tmp/vitest-runner)
|
|
25
|
+
--help, -h Show this help message
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
27
|
+
${Z.bold("TEST PATTERNS:")}
|
|
28
|
+
[file] Run a specific test file (supports partial paths)
|
|
29
|
+
[folder] Run all tests in a folder
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const resolvedLogFile = args.logFile
|
|
29
|
-
? path.isAbsolute(args.logFile)
|
|
30
|
-
? args.logFile
|
|
31
|
-
: path.resolve(cwd, args.logFile)
|
|
32
|
-
: path.join(cwd, "coverage", "coverage-run.log");
|
|
33
|
-
mkdirSync(path.dirname(resolvedLogFile), { recursive: true });
|
|
34
|
-
const logStream = createWriteStream(resolvedLogFile, { flags: "a" });
|
|
35
|
-
const origStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
36
|
-
const origStderrWrite = process.stderr.write.bind(process.stderr);
|
|
31
|
+
Examples:
|
|
32
|
+
src/tests/config/background.test.vitest.mjs
|
|
33
|
+
src/tests/metadata
|
|
34
|
+
background.test.vitest.mjs
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function isProgressChunk(chunk) {
|
|
45
|
-
const str = chunk.toString();
|
|
46
|
-
return str.startsWith("\r") || /^progress \d+\.\d+%/.test(str);
|
|
47
|
-
}
|
|
36
|
+
${Z.bold("VITEST FLAGS:")}
|
|
37
|
+
All standard Vitest CLI flags are supported and passed through:
|
|
38
|
+
-t, --testNamePattern Filter tests by name pattern (regex)
|
|
39
|
+
--reporter Change reporter (verbose, dot, json, etc.)
|
|
40
|
+
--coverage Run full-suite coverage (blob-per-file + mergeReports)
|
|
41
|
+
--bail Stop on first failure
|
|
48
42
|
|
|
49
|
-
|
|
50
|
-
if (!isProgressChunk(chunk)) logStream.write(stripAnsi(chunk.toString()));
|
|
51
|
-
return origStdoutWrite(chunk, enc, cb);
|
|
52
|
-
};
|
|
43
|
+
See the Vitest documentation for the full list.
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
${Z.bold("ENVIRONMENT VARIABLES:")}
|
|
46
|
+
VITEST_HEAP_MB Set max heap size per test (default: Node.js default)
|
|
47
|
+
VITEST_WORKERS Number of parallel workers (default: 4, overridden by --workers)
|
|
48
|
+
VITEST_RUNNER_TMP Set by the runner in each child \u2014 this run's per-file scratch
|
|
49
|
+
directory. Use makeRunTmpDir(label) from a test file instead
|
|
50
|
+
of reading it directly when you want a fresh subdirectory.
|
|
51
|
+
# Run all tests
|
|
52
|
+
vitest-runner
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
# Run a specific file (partial path ok)
|
|
55
|
+
vitest-runner src/tests/config/background.test.vitest.mjs
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
# Run only the files listed in a JSON file
|
|
58
|
+
vitest-runner --test-list my-tests.json
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
vitestArgs.unshift("--coverage");
|
|
67
|
-
}
|
|
60
|
+
# Filter by test name
|
|
61
|
+
vitest-runner src/tests/metadata -t "lazy materialization"
|
|
68
62
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
cwd,
|
|
72
|
-
testPatterns: args.testPatterns,
|
|
73
|
-
testListFile: args.testListFile,
|
|
74
|
-
testFilePattern: args.testFilePattern,
|
|
75
|
-
vitestArgs,
|
|
76
|
-
showErrorDetails: args.showErrorDetails,
|
|
77
|
-
coverageQuiet: args.coverageQuiet,
|
|
78
|
-
suppressFileOutput: args.suppressFileOutput,
|
|
79
|
-
suppressPassingFiles: args.suppressPassingFiles,
|
|
80
|
-
topSummary: args.topSummary,
|
|
81
|
-
json: args.json,
|
|
82
|
-
mergeReports: args.mergeReports,
|
|
83
|
-
...(args.blobsDir !== undefined && { blobsDir: args.blobsDir }),
|
|
84
|
-
...(args.workers !== undefined && { workers: args.workers }),
|
|
85
|
-
...(args.soloPatterns.length > 0 && { earlyRunPatterns: args.soloPatterns })
|
|
86
|
-
});
|
|
63
|
+
# Run with 2 workers
|
|
64
|
+
vitest-runner --workers 2
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
process.exit(typeof runResult === "object" && runResult !== null && "exitCode" in runResult ? runResult.exitCode : 1);
|
|
91
|
-
}
|
|
66
|
+
# Run files matching a pattern solo first, then the rest in parallel
|
|
67
|
+
vitest-runner --solo-pattern listener-cleanup/ --solo-pattern heavy/
|
|
92
68
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
69
|
+
# Hide detailed errors
|
|
70
|
+
vitest-runner --no-error-details
|
|
71
|
+
|
|
72
|
+
# Coverage with quiet output + progress bar
|
|
73
|
+
vitest-runner --coverage --coverage-quiet
|
|
74
|
+
|
|
75
|
+
# Custom heap and workers
|
|
76
|
+
VITEST_HEAP_MB=8192 vitest-runner --workers 2 src/tests/heavy
|
|
77
|
+
`)}import G from"fs/promises";import me from"path";import $ from"chalk";import{createRequire as yt}from"module";import X from"path";import Mt from"fs/promises";import{pathToFileURL as Pt}from"url";function Oe(e,o,s=o){let r=yt(Pt(X.join(e,"package.json"))),t=r.resolve(`${o}/package.json`),n=r(t),a=n.bin?.[s]??n.bin;if(!a)throw new Error(`No bin "${s}" found in ${o}/package.json`);return X.join(X.dirname(t),a)}var Rt=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,o){if(o)return X.isAbsolute(o)?o:X.resolve(e,o);for(let s of Rt){let r=X.join(e,s);try{return await Mt.access(r),r}catch{}}}import Ae from"fs/promises";import N from"path";var ot=/\.test\.vitest\.(?:js|mjs|cjs)$/i;async function ue(e,o,s=ot){let r=[e],t=[];for(;r.length;){let n=r.pop(),a;try{a=await Ae.readdir(n,{withFileTypes:!0})}catch{continue}for(let p of a){if(p.isDirectory()){if(p.name==="node_modules"||p.name.startsWith("."))continue;r.push(N.join(n,p.name));continue}p.isFile()&&s.test(p.name)&&t.push(N.relative(o,N.join(n,p.name)))}}return t}function fe(e,o=[]){let s=[],r=[];for(let t of e){let n=t.replace(/\\/g,"/"),a=o.findIndex(p=>n.includes(p));a!==-1?s.push({file:t,priorityIndex:a}):r.push(t)}return s.sort((t,n)=>t.priorityIndex-n.priorityIndex||t.file.localeCompare(n.file)),r.sort((t,n)=>t.localeCompare(n)),[...s.map(t=>t.file),...r]}function Ie(e){let o=e.map(r=>r.replace(/\\/g,"/")),s=new Map;for(let r=0;r<e.length;r++){let t=[];for(let n=0;n<e.length;n++)r!==n&&o[n].includes(o[r])&&t.push(e[n]);s.set(e[r],t)}return s}async function de(e){let{cwd:o,testDir:s,testPatterns:r=[],testListFile:t,testFilePattern:n=ot,earlyRunPatterns:a=[]}=e,p=s?N.isAbsolute(s)?s:N.resolve(o,s):o;if(t){let c=N.isAbsolute(t)?t:N.resolve(o,t),f;try{let d=await Ae.readFile(c,"utf8");f=JSON.parse(d)}catch(d){throw new Error(`Failed to read test list file "${c}": ${d.message}`,{cause:d})}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: ${N.relative(o,c)}`),fe(f,a)}if(r.length===0){let c=await ue(p,o,n);return fe(c,a)}let u=[];for(let c of r){let f=N.isAbsolute(c)?c:N.resolve(o,c),d=null;try{d=await Ae.stat(f)}catch{}if(d?.isFile())n.test(f)&&u.push(N.relative(o,f));else if(d?.isDirectory())u.push(...await ue(f,o,n));else{let h=(await ue(p,o,n)).filter(m=>m.replace(/\\/g,"/").includes(c.replace(/\\/g,"/")));h.length>0?u.push(...h):console.warn(`\u26A0\uFE0F No matches found for: ${c}`)}}return fe([...new Set(u)],a)}import{spawn as rt}from"child_process";function L(e){return e.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"")}function _(e,o){let s=o.toFixed(2).padStart(6);return o>=80?e.green(s):o>=50?e.yellow(s):e.red(s)}function ke(e){let o=L(e),s={testFilesPass:0,testFilesFail:0,testsPass:0,testsFail:0,testsSkip:0,duration:0,heapMb:null,errors:[]},r=o.match(/Test Files\s+(.+)/);if(r){let u=r[1],c=u.match(/(\d+)\s+passed/),f=u.match(/(\d+)\s+failed/);c&&(s.testFilesPass=parseInt(c[1],10)),f&&(s.testFilesFail=parseInt(f[1],10))}let t=o.match(/^\s*Tests\s+(.+)$/m);if(t){let u=t[1],c=u.match(/(\d+)\s+passed/),f=u.match(/(\d+)\s+failed/),d=u.match(/(\d+)\s+skipped/);c&&(s.testsPass=parseInt(c[1],10)),f&&(s.testsFail=parseInt(f[1],10)),d&&(s.testsSkip=parseInt(d[1],10))}let n=o.match(/Duration\s+([\d.]+)s/);n&&(s.duration=parseFloat(n[1])*1e3);let a=o.match(/(\d+)\s*MB\s+heap\s+used/i);a&&(s.heapMb=parseInt(a[1],10));let p=e.indexOf("Failed Tests");if(p!==-1){let u=e.indexOf(`
|
|
78
|
+
Test Files`,p),c=u!==-1?e.substring(p,u):e.substring(p),f=/FAIL\s*(?:\x1B\[[0-9;]*[a-zA-Z]|\s)*tests\//g,d=[...c.matchAll(f)];for(let b=0;b<d.length;b++){let h=d[b].index,m=c.lastIndexOf(`
|
|
79
|
+
`,h),M=m===-1?0:m,v=b<d.length-1?d[b+1].index:c.length,w=c.lastIndexOf(`
|
|
80
|
+
`,v),F=w===-1?v:w,E=c.substring(M,F).trim();E&&s.errors.push(E)}}return s}function Ce(e){let s=e.join(`
|
|
81
|
+
`).split(`
|
|
82
|
+
`),r=new Map,t=new Map;s.forEach((a,p)=>{if(a.includes("FAIL")&&a.includes("Config:")){t.set(a,p);let c=L(a).match(/^(.+Config:\s+)'*([^'>]+)'*(.+)$/);if(c){let[,f,d,b]=c,h=`${f.trim()}|||${b.trim()}`;r.has(h)||r.set(h,{lines:[],configs:[]}),r.get(h).lines.push(a),r.get(h).configs.push(d.replace(/'/g,""))}}});let n=new Set;for(let[,a]of r.entries())if(a.configs.length>1){for(let d=1;d<a.lines.length;d++){let b=t.get(a.lines[d]);n.add(b)}let p=a.lines[0],u=`[${a.configs.map(d=>`'${d}'`).join(",")}]`,c=L(p).replace(/(Config:\s+)'*[^'>]+'*/,`$1${u}`),f=t.get(p);s[f]=c}return s.filter((a,p)=>!n.has(p)).join(`
|
|
83
|
+
`)}function je({maxOldSpaceMb:e,conditions:o=[],base:s=process.env.NODE_OPTIONS??""}){let r=s;for(let t of o){let n=`--conditions=${t}`;r.includes(n)||(r=r?`${r} ${n}`:n)}if(e&&!r.includes("--max-old-space-size")){let t=`--max-old-space-size=${e}`;r=r?`${r} ${t}`:t}return r.trim()}function nt({maxOldSpaceMb:e,conditions:o=[],nodeEnv:s="development",extraEnv:r={}}){let t={...process.env};t.NODE_ENV||(t.NODE_ENV=s);let n=je({maxOldSpaceMb:e,conditions:o,base:t.NODE_OPTIONS??""});return n&&(t.NODE_OPTIONS=n),Object.assign(t,r),t}function Ot(e,o){return[e,...o?["--config",o]:[],"run"]}function ge(e,o){let{cwd:s,vitestBin:r,vitestConfig:t,maxOldSpaceMb:n,conditions:a=[],nodeEnv:p="development",vitestArgs:u=[],streamOutput:c=!0,excludePaths:f=[],extraEnv:d={}}=o;return new Promise(b=>{let h=Date.now(),m=f.flatMap(l=>["--exclude",l]),M=[...Ot(r,t),...u,...m,e],v=nt({maxOldSpaceMb:n,conditions:a,nodeEnv:p,extraEnv:d}),w=rt(process.execPath,M,{cwd:s,stdio:["ignore","pipe","pipe"],env:v}),F="",E="";w.stdout?.on("data",l=>{F+=l.toString(),c&&process.stdout.write(l)}),w.stderr?.on("data",l=>{E+=l.toString(),c&&process.stderr.write(l)}),w.on("close",l=>{let D=Date.now()-h,V=`${F}
|
|
84
|
+
${E}`,P=ke(V);b({file:e,code:l??1,duration:P.duration>0?P.duration:D,testFilesPass:P.testFilesPass,testFilesFail:P.testFilesFail,testsPass:P.testsPass,testsFail:P.testsFail,testsSkip:P.testsSkip,heapMb:P.heapMb,errors:P.errors,rawOutput:V})}),w.on("error",l=>{b({file:e,code:1,duration:Date.now()-h,testFilesPass:0,testFilesFail:1,testsPass:0,testsFail:0,testsSkip:0,heapMb:null,errors:[l.message],rawOutput:l.toString()})})})}function Ne(e,o){let{cwd:s,vitestBin:r,vitestConfig:t,maxOldSpaceMb:n,conditions:a=[],nodeEnv:p="development",extraCoverageArgs:u=[],quietOutput:c=!1}=o;return new Promise(f=>{let d=t?["--config",t]:[],b=c?["--color"]:[],h=[r,...d,"--mergeReports",e,"--run","--coverage",...b,...u],m=nt({maxOldSpaceMb:n,conditions:a,nodeEnv:p}),M=rt(process.execPath,h,{cwd:s,stdio:c?["ignore","pipe","pipe"]:"inherit",env:m}),v="",w="";c&&(M.stdout?.on("data",F=>v+=F.toString()),M.stderr?.on("data",F=>w+=F.toString())),M.on("close",F=>f({exitCode:F??1,output:`${v}
|
|
85
|
+
${w}`})),M.on("error",()=>f({exitCode:1,output:""}))})}import q from"chalk";function ee(e){let o=Math.max(0,Math.floor(e/1e3)),s=Math.floor(o/3600),r=Math.floor(o%3600/60),t=o%60;return s>0?`${s}:${r.toString().padStart(2,"0")}:${t.toString().padStart(2,"0")}`:`${r}:${t.toString().padStart(2,"0")}`}function Le(e){let o=!!process.stdout.isTTY,s=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],r=26,t=Date.now(),n=0,a=0,p=0,u=0,c=0,f=0,d=null;function b(){let v=e===0?100:n/e*100,w=Date.now()-t,E=(n>0?w/n:0)*Math.max(e-n,0),l=Math.round(v/100*r),D=s[u%s.length],V=`[${"=".repeat(l)}${"-".repeat(Math.max(0,r-l))}]`,P=v.toFixed(1).padStart(5);return v<30?P=q.red(P+"%"):v<70?P=q.rgb(255,136,0)(P+"%"):v>99?P=q.green(P+"%"):P=q.yellow(P+"%"),o?`${q.green(D)} ${q.green(V)} ${q.bold(P)} ${n}/${e} | active ${a} | failed ${p} | ETA ${ee(E)} | elapsed ${ee(w)}`:`progress ${v.toFixed(1)}% ${n}/${e} | active ${a} | failed ${p} | eta ${ee(E)} | elapsed ${ee(w)}`}function h(v=!1){let w=b();if(u++,o){let E=w.replace(/\x1B\[[0-9;]*[a-zA-Z]/g,"").length;c=Math.max(c,E),process.stdout.write(`\r${w.padEnd(c," ")}`);return}let F=Date.now();(v||F-f>=2e3)&&(console.log(w),f=F)}function m(){!o||d||(d=setInterval(()=>{n>=e&&a===0||h()},120),d.unref?.())}function M(){d&&(clearInterval(d),d=null)}return h(!0),m(),{onStart(){a++,h()},onComplete(v){a=Math.max(0,a-1),n++,v&&p++,h(!0)},finish(){M(),h(!0),o&&process.stdout.write(`
|
|
86
|
+
`)}}}var _e={onStart(){},onComplete(e){},finish(){}};import it from"fs/promises";import te from"path";import y from"chalk";function Ve(e){if(e.length!==0){console.log(`
|
|
87
|
+
${"=".repeat(80)}`),console.log(y.bold.red("\u2716 FAILED TEST FILES (VERBOSE OUTPUT)")),console.log("=".repeat(80));for(let o of e)console.log(`
|
|
88
|
+
${y.red("\u2716")} ${y.red(o.file)} ${y.dim(`(exit ${o.code})`)}`),o.rawOutput?.trim()?console.log(o.rawOutput.trimEnd()):console.log(y.dim("(no child output captured)"));console.log(`
|
|
89
|
+
${"=".repeat(80)}`)}}function Be(e,o){if(e===0){let s="% Coverage report from v8",r=o.split(`
|
|
90
|
+
`),t=r.findIndex(n=>L(n).includes(s));if(t>=0){let n=r.length;for(let p=t+1;p<r.length;p++){let u=L(r[p]).trimStart();if(u.startsWith("stderr |")||u.startsWith("stdout |")){n=p;break}}if(r.slice(t+1,n).join(`
|
|
91
|
+
`).trimEnd()){let p=r.slice(t,n).join(`
|
|
92
|
+
`).trimEnd();console.log(`
|
|
93
|
+
${p}
|
|
94
|
+
`)}}}else{let s=o.trimEnd();s&&console.error(s)}}function Dt(e){let o=(r,t)=>t===0?100:parseFloat((r/t*100).toFixed(2)),s={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[r,t]of Object.entries(e)){let n=Object.keys(t.s??{}),a=n.length,p=n.filter(v=>t.s[v]>0).length,u=Object.keys(t.f??{}),c=u.length,f=u.filter(v=>t.f[v]>0).length,d=0,b=0;for(let v of Object.values(t.b??{}))d+=v.length,b+=v.filter(w=>w>0).length;let h=new Set,m=new Set;for(let[v,w]of Object.entries(t.statementMap??{})){let F=w?.start?.line;F!=null&&(m.add(F),(t.s??{})[v]>0&&h.add(F))}let M={statements:{total:a,covered:p,pct:o(p,a)},branches:{total:d,covered:b,pct:o(b,d)},functions:{total:c,covered:f,pct:o(f,c)},lines:{total:m.size,covered:h.size,pct:o(h.size,m.size)}};s[r]=M;for(let v of["statements","branches","functions","lines"])s.total[v].total+=M[v].total,s.total[v].covered+=M[v].covered}for(let r of["statements","branches","functions","lines"]){let{total:t,covered:n}=s.total[r];s.total[r].pct=o(n,t)}return s}async function We(e,o,s=10,r={}){let{silent:t=!1}=r,n=te.resolve(e,"coverage"),a=o.find(m=>m.startsWith("--coverage.reportsDirectory="));if(a){let m=a.split("=").slice(1).join("=");n=te.isAbsolute(m)?m:te.resolve(e,m)}let p;try{let m=await it.readFile(te.join(n,"coverage-summary.json"),"utf8");p=JSON.parse(m)}catch{try{let m=await it.readFile(te.join(n,"coverage-final.json"),"utf8");p=Dt(JSON.parse(m))}catch{return t||console.log(y.dim(" (no coverage JSON found \u2014 skipping summary)")),null}}let{total:u,...c}=p;if(s>0){let m=Object.entries(c).map(([l,D])=>({file:te.relative(e,l),lines:D.lines?.pct??0,stmts:D.statements?.pct??0,fns:D.functions?.pct??0,branches:D.branches?.pct??0})).filter(l=>Math.min(l.lines,l.stmts,l.fns,l.branches)<100).sort((l,D)=>Math.min(l.lines,l.stmts,l.fns,l.branches)-Math.min(D.lines,D.stmts,D.fns,D.branches)),M=m.slice(0,s);t||(console.log(`
|
|
95
|
+
`+y.bold("\u{1F4C9} WORST COVERAGE FILES (lowest metric)")),console.log("-".repeat(80)),M.forEach(({file:l,lines:D,stmts:V,fns:P,branches:B})=>{let O=Math.min(D,V,P,B),ve=y.dim(`lines ${D.toFixed(0)}% | stmts ${V.toFixed(0)}% | fns ${P.toFixed(0)}% | branches ${B.toFixed(0)}%`);console.log(` ${_(y,O)}% ${y.dim(l)} ${ve}`)}),m.length>s&&console.log(y.dim(` ... and ${m.length-s} more files`)));let v=u.lines?.pct??0,w=u.statements?.pct??0,F=u.functions?.pct??0,E=u.branches?.pct??0;return t||console.log(`
|
|
96
|
+
${y.bold("Coverage")} ${_(y,v)}% lines ${y.dim("|")} ${_(y,w)}% statements ${y.dim("|")} ${_(y,F)}% functions ${y.dim("|")} ${_(y,E)}% branches`),{coverageDir:n,total:u,worstFiles:M,worstFilesShown:M.length,worstFilesTotal:m.length,summary:p}}let f=u.lines?.pct??0,d=u.statements?.pct??0,b=u.functions?.pct??0,h=u.branches?.pct??0;return t||console.log(`
|
|
97
|
+
${y.bold("Coverage")} ${_(y,f)}% lines ${y.dim("|")} ${_(y,d)}% statements ${y.dim("|")} ${_(y,b)}% functions ${y.dim("|")} ${_(y,h)}% branches`),{coverageDir:n,total:u,worstFiles:[],worstFilesShown:0,worstFilesTotal:0,summary:p}}import le from"fs/promises";import At from"fs";import ae from"path";var lt="tmp/vitest-runner",It=/^(\d+)-\d+$/;function kt(e){try{return process.kill(e,0),!0}catch(o){return o.code!=="ESRCH"}}async function at(e,o){let s=ae.resolve(e,o),r;try{r=await le.readdir(s,{withFileTypes:!0})}catch{return}for(let t of r){if(!t.isDirectory())continue;let n=t.name.match(It);if(!n)continue;let a=parseInt(n[1],10);kt(a)||await le.rm(ae.join(s,t.name),{recursive:!0,force:!0}).catch(()=>{})}}async function ct(e,o){let s=ae.resolve(e,o),r=ae.join(s,`${process.pid}-${Date.now()}`);return await le.mkdir(r,{recursive:!0}),r}async function He(e,o){let s=ae.join(e,`worker-${o}`);return await le.mkdir(s,{recursive:!0}),s}async function pt(e){await le.rm(e,{recursive:!0,force:!0}).catch(()=>{})}function ut(e){try{At.rmSync(e,{recursive:!0,force:!0})}catch{}}function ft(e,o,s){let r=e.replace(/\\/g,"/"),t;for(let{pattern:n,heapMb:a}of s)if(r.includes(n)){t=a;break}if(!(t===void 0&&o===void 0))return t===void 0?o:o===void 0?t:Math.max(t,o)}async function dt(e){let o=e.cwd??process.cwd(),s=e.scratchDir??lt,r=e.keepTmp??!1;await at(o,s);let t=await ct(o,s),n=()=>{ut(t),process.exitCode=130,process.exit()};r||(process.once("SIGINT",n),process.once("SIGTERM",n));try{return await Ct(e,t)}finally{process.removeListener("SIGINT",n),process.removeListener("SIGTERM",n),r||await pt(t)}}async function Ct(e,o){let{cwd:s=process.cwd(),testDir:r,testPatterns:t=[],testListFile:n,testFilePattern:a,vitestArgs:p=[],showErrorDetails:u=!0,coverageQuiet:c=!1,suppressFileOutput:f=!1,suppressPassingFiles:d=!1,topSummary:b=!0,json:h=!1,workers:m=parseInt(process.env.VITEST_WORKERS??"4",10),worstCoverageCount:M=10,blobsDir:v,mergeReports:w=!0,earlyRunPatterns:F=[],perFileHeapOverrides:E=[],conditions:l=[],nodeEnv:D="development",_testResultsOverride:V=null}=e,P=0,B=e.maxOldSpaceMb??(process.env.VITEST_HEAP_MB?parseInt(process.env.VITEST_HEAP_MB,10):void 0),O=!h,ve=Oe(s,"vitest","vitest"),gt=await De(s,e.vitestConfig),be={cwd:s,vitestBin:ve,vitestConfig:gt,conditions:l,nodeEnv:D},k=[...p];if(c&&!k.some(i=>i==="--coverage"||i.startsWith("--coverage."))&&k.unshift("--coverage"),k.some(i=>i==="--coverage"||i.startsWith("--coverage."))){let i=me.resolve(s,v??".vitest-coverage-blobs"),g=me.resolve(s,".vitest-coverage-tmp");await Promise.all([G.rm(i,{recursive:!0,force:!0}),G.rm(g,{recursive:!0,force:!0})]),await Promise.all([G.mkdir(i,{recursive:!0}),G.mkdir(g,{recursive:!0})]);let x=await de({cwd:s,testDir:r,testPatterns:t,testListFile:n,testFilePattern:a,earlyRunPatterns:F}),A=Ie(x);if(x.length===0){let S=t.length>0?`\u274C No Vitest test files found matching: ${t.join(", ")}`:"\u274C No Vitest test files found";return O&&console.log(S),h?{exitCode:1,mode:"coverage",message:S,options:{cwd:s,testDir:r,testPatterns:t,testListFile:n,workers:m,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:d,topSummary:b,vitestArgs:k}}:1}let j=k.filter(S=>S!=="--coverage"&&S.startsWith("--coverage.")),we=k.filter(S=>S!=="--coverage"&&!S.startsWith("--coverage.")),ne=x.filter(S=>F.some(R=>S.replace(/\\/g,"/").includes(R))),xe=x.filter(S=>!F.some(R=>S.replace(/\\/g,"/").includes(R))),Te=c||f||h,Ft=c&&!h;!c&&!f&&O&&(console.log(`
|
|
98
|
+
\u{1F9EA} Running ${x.length} test files for coverage (blob + merge mode)`),console.log(`\u2699\uFE0F Workers: ${m} (${ne.length} solo first, then parallel)`),B&&console.log(`\u{1F9E0} Heap limit: ${B} MB`),console.log(""));let Ee=Ft?Le(x.length):_e,W=[],ye=0,Ye=async S=>{let R=me.join(i,`run-${ye}.blob`);ye++,!Te&&O&&(console.log(`
|
|
99
|
+
${"=".repeat(80)}`),console.log(`\u25B6\uFE0F ${S}`),console.log("=".repeat(80))),Ee.onStart();let U=me.join(g,`run-${ye}`),wt=[...we,"--coverage",`--coverage.reportsDirectory=${U}`,"--reporter=default","--reporter=blob",`--outputFile=${R}`],xt=await He(o,P++),z=await ge(S,{...be,maxOldSpaceMb:ft(S,B,E),vitestArgs:wt,streamOutput:!Te,excludePaths:A.get(S),extraEnv:{VITEST_RUNNER_TMP:xt}});if(W.push(z),Ee.onComplete(z.code!==0),!Te&&O){let et=(z.duration/1e3).toFixed(2);if(z.code===0){let Tt=z.heapMb?` | ${z.heapMb} MB heap`:"";console.log(`
|
|
100
|
+
\u2705 PASSED (${et}s${Tt})
|
|
101
|
+
`)}else console.log(`
|
|
102
|
+
\u274C FAILED (exit code ${z.code}, ${et}s)
|
|
103
|
+
`)}};for(let S of ne)await Ye(S).catch(R=>{O&&console.error(`Error running ${S}:`,R)});let Me=0,Y=new Set;for(;Me<xe.length||Y.size>0;){for(;Me<xe.length&&Y.size<m;){let S=xe[Me++],R=Ye(S).catch(U=>{O&&console.error(`Error running ${S}:`,U)}).finally(()=>Y.delete(R));Y.add(R)}Y.size>0&&await Promise.race(Y)}Ee.finish();let Pe=(await G.readdir(i).catch(()=>[])).filter(S=>S.endsWith(".blob"));if(Pe.length===0){let S="\u274C No coverage blobs were generated \u2014 coverage report cannot be produced";return O&&console.error(S),h?{exitCode:1,mode:"coverage",message:S,options:{cwd:s,testDir:r,testPatterns:t,testListFile:n,workers:m,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:d,topSummary:b,showErrorDetails:u,worstCoverageCount:M,vitestArgs:k},totals:{testFiles:x.length,failedFiles:W.filter(R=>R.code!==0).length,passedFiles:W.filter(R=>R.code===0).length},results:{all:W,failed:W.filter(R=>R.code!==0)},merge:{blobFiles:0,exitCode:1,output:""},coverageSummary:null}:1}let pe=0,Re="",Ze=null;w&&(!c&&!f&&O&&(console.log(`
|
|
104
|
+
${"=".repeat(80)}`),console.log(`\u{1F4CA} Merging ${Pe.length} coverage blobs into final report...`),console.log("=".repeat(80))),{exitCode:pe,output:Re}=await Ne(i,{...be,maxOldSpaceMb:B,extraCoverageArgs:j,quietOutput:c||h}),c&&O&&Be(pe,Re),Ze=await We(s,j,M,{silent:h})),await Promise.all([...w?[G.rm(i,{recursive:!0,force:!0}).catch(()=>{})]:[],G.rm(g,{recursive:!0,force:!0}).catch(()=>{})]);let ie=W.filter(S=>S.code!==0);c&&O&&Ve(ie);let Xe=ie.length>0?1:pe;if(h){let S=W.filter(R=>R.heapMb!==null);return{exitCode:Xe,mode:"coverage",options:{cwd:s,testDir:r,testPatterns:t,testListFile:n,workers:m,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:d,topSummary:b,showErrorDetails:u,worstCoverageCount:M,vitestArgs:k},totals:{testFiles:x.length,failedFiles:ie.length,passedFiles:W.length-ie.length},results:{all:W,failed:ie},merge:{blobFiles:Pe.length,exitCode:pe,output:Re},coverageSummary:Ze,...b?{topMemoryUsers:[...S].sort((R,U)=>U.heapMb-R.heapMb).slice(0,10),topDuration:[...W].sort((R,U)=>U.duration-R.duration).slice(0,10)}:{}}}return Xe}let J=await de({cwd:s,testDir:r,testPatterns:t,testListFile:n,testFilePattern:a,earlyRunPatterns:F}),mt=Ie(J);if(J.length===0){let i=t.length>0?`\u274C No Vitest test files found matching: ${t.join(", ")}`:"\u274C No Vitest test files found";return O&&console.log(i),h?{exitCode:1,mode:"standard",message:i,options:{cwd:s,testDir:r,testPatterns:t,testListFile:n,workers:m,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:d,topSummary:b,vitestArgs:k}}:1}let ze=J.filter(i=>F.some(g=>i.replace(/\\/g,"/").includes(g))),$e=J.filter(i=>!F.some(g=>i.replace(/\\/g,"/").includes(g))),ht=Date.now(),qe=new Date().toLocaleTimeString("en-US",{hour12:!1});O&&!f&&t.length>0?console.log(`
|
|
105
|
+
\u{1F9EA} Running ${J.length} test files matching: ${t.join(", ")}`):O&&!f&&console.log(`
|
|
106
|
+
\u{1F9EA} Running ${J.length} test files (${ze.length} solo first, then parallel)`),O&&!f&&(console.log(`\u2699\uFE0F Workers: ${m}`),B&&console.log(`\u{1F9E0} Heap limit: ${B} MB`),k.length>0&&console.log(`\u{1F527} Vitest args: ${k.join(" ")}`),console.log(""));let I=[...V??[]],Ge=async i=>{!f&&O&&(console.log(`
|
|
107
|
+
${"=".repeat(80)}`),console.log(`\u25B6\uFE0F ${i}`),console.log("=".repeat(80)));let g=await He(o,P++),x=await ge(i,{...be,maxOldSpaceMb:ft(i,B,E),vitestArgs:k,streamOutput:!f&&!h,excludePaths:mt.get(i),extraEnv:{VITEST_RUNNER_TMP:g}});if(!f&&O){let A=(x.duration/1e3).toFixed(2);if(x.code===0){let j=x.heapMb?` | ${x.heapMb} MB heap`:"";console.log(`
|
|
108
|
+
\u2705 PASSED (${A}s${j})
|
|
109
|
+
`)}else console.log(`
|
|
110
|
+
\u274C FAILED (exit code ${x.code}, ${A}s)
|
|
111
|
+
`)}return x};if(!V){for(let x of ze){let A=await Ge(x).catch(j=>(O&&console.error(`Error running ${x}:`,j),null));A&&I.push(A)}let i=0,g=new Set;for(;i<$e.length||g.size>0;){for(;i<$e.length&&g.size<m;){let x=$e[i++],A=Ge(x).then(j=>I.push(j)).catch(j=>{O&&console.error(`Error running ${x}:`,j)}).finally(()=>g.delete(A));g.add(A)}g.size>0&&await Promise.race(g)}}let Q=I.reduce((i,g)=>i+g.testFilesPass,0),H=I.reduce((i,g)=>i+g.testFilesFail,0),se=I.reduce((i,g)=>i+g.testsPass,0),oe=I.reduce((i,g)=>i+g.testsFail,0),re=I.reduce((i,g)=>i+(g.testsSkip||0),0),vt=I.reduce((i,g)=>i+g.duration,0),K=I.filter(i=>i.code!==0),ce=I.filter(i=>i.code===0),bt=K.length>0?1:0,Je=process.memoryUsage(),Se=Math.round(Je.heapUsed/1024/1024),Fe=Math.round(Je.rss/1024/1024),C=I.filter(i=>i.heapMb!==null),Qe=((Date.now()-ht)/1e3).toFixed(2),Ke=(vt/1e3).toFixed(2);if(h)return{exitCode:bt,mode:"standard",options:{cwd:s,testDir:r,testPatterns:t,testListFile:n,workers:m,coverageQuiet:c,suppressFileOutput:f,suppressPassingFiles:d,topSummary:b,showErrorDetails:u,worstCoverageCount:M,vitestArgs:k},totals:{testFilesPass:Q,testFilesFail:H,testsPass:se,testsFail:oe,testsSkip:re,totalTests:se+oe+re},timing:{startAt:qe,wallClockSeconds:Number(Qe),testsSeconds:Number(Ke)},heap:{scriptHeapMb:Se,scriptRssMb:Fe,maxHeapMb:C.length>0?Math.max(...C.map(i=>i.heapMb)):null,avgHeapMb:C.length>0?Number((C.reduce((i,g)=>i+g.heapMb,0)/C.length).toFixed(0)):null},results:{all:I,passed:ce,failed:K},...b?{topMemoryUsers:[...C].sort((i,g)=>g.heapMb-i.heapMb).slice(0,10),topDuration:[...I].sort((i,g)=>g.duration-i.duration).slice(0,10)}:{}};console.log(`
|
|
112
|
+
`+"=".repeat(80)),b&&C.length>0&&(console.log(`
|
|
113
|
+
`+$.bold("\u{1F9E0} TOP MEMORY USERS")),console.log("-".repeat(80)),[...C].sort((i,g)=>g.heapMb-i.heapMb).slice(0,10).forEach(i=>{console.log(` ${String(i.heapMb).padStart(4)} MB ${$.dim(i.file)}`)})),b&&I.length>0&&(console.log(`
|
|
114
|
+
`+$.bold("\u23F1\uFE0F TOP DURATION")),console.log("-".repeat(80)),[...I].sort((i,g)=>g.duration-i.duration).slice(0,10).forEach(i=>{let g=(i.duration/1e3).toFixed(2);console.log(` ${(g+"s").padStart(8)} ${$.dim(i.file)}`)})),ce.length>0&&!d&&(console.log(`
|
|
115
|
+
`+"=".repeat(80)),console.log($.bold.green("\u2713 PASSED TEST FILES")),console.log("=".repeat(80)),ce.forEach(i=>{let g=(i.duration/1e3).toFixed(2),x=[...i.heapMb?[`${i.heapMb} MB`]:[],`${g}s`],A=i.testsPass>0?` - ${i.testsPass} tests`:"";console.log($.green(`\u2713 ${i.file}${A}`)+$.dim(` (${x.join(", ")})`))})),console.log(`
|
|
116
|
+
`+$.bold("=".repeat(80))),K.length>0&&(console.log(`
|
|
117
|
+
\u274C ${K.length} test file(s) failed`),console.log($.bold.red(`
|
|
118
|
+
Failed Test Files:`)),K.forEach(i=>{let g=(i.duration/1e3).toFixed(2),x=[...i.testsFail>0?[$.red(`${i.testsFail} failed`)]:[],...i.testsPass>0?[$.green(`${i.testsPass} passed`)]:[],...i.testsSkip>0?[$.yellow(`${i.testsSkip} skipped`)]:[]],A=[...i.heapMb?[`${i.heapMb} MB`]:[],`${g}s`],j=x.length>0?` (${x.join(", ")})`:"";if(console.log(` ${$.red("\u2716")} ${i.file}${j}`+$.dim(` [${A.join(", ")}]`)),u&&i.errors.length>0){let we=Ce(i.errors).split(`
|
|
119
|
+
`).map(ne=>ne.trim()?` ${ne}`:"").join(`
|
|
120
|
+
`);console.log(we),console.log("")}}),console.log("")),console.log($.bold("=".repeat(80))),H>0&&Q>0?console.log(` ${$.bold("Test Files")} ${$.red(`${H} failed`)} ${$.dim("|")} ${$.green(`${Q} passed`)} ${$.dim(`(${Q+H})`)}`):H>0?console.log(` ${$.bold("Test Files")} ${$.red(`${H} failed`)} ${$.dim(`(${H})`)}`):console.log(` ${$.bold("Test Files")} ${$.green(`${Q} passed`)} ${$.dim(`(${Q})`)}`);let $t=se+oe+re,St=[...oe>0?[$.red(`${oe} failed`)]:[],...se>0?[$.green(`${se} passed`)]:[],...re>0?[$.yellow(`${re} skipped`)]:[]];if(console.log(` ${$.bold("Tests")} ${St.join(` ${$.dim("|")} `)} ${$.dim(`(${$t})`)}`),console.log(` ${$.bold("Start at")} ${qe}`),console.log(` ${$.bold("Duration")} ${Qe}s ${$.dim(`(tests ${Ke}s)`)}`),C.length>0){let i=Math.max(...C.map(x=>x.heapMb)),g=(C.reduce((x,A)=>x+A.heapMb,0)/C.length).toFixed(0);console.log(` ${$.bold("Heap")} max ${i} MB | avg ${g} MB | script ${Se} MB (RSS ${Fe} MB)`)}else console.log(` ${$.bold("Heap")} script ${Se} MB (RSS ${Fe} MB)`);return K.length>0?1:(console.log(`
|
|
121
|
+
\u2705 All ${ce.length} test files passed
|
|
122
|
+
`),0)}var T=tt(process.argv.slice(2));T.help&&(st(),process.exit(0));if((T.coverageQuiet||T.logFile)&&!T.json){let n=function(a){let p=a.toString();return p.startsWith("\r")||/^progress \d+\.\d+%/.test(p)},e=process.cwd(),o=T.logFile?he.isAbsolute(T.logFile)?T.logFile:he.resolve(e,T.logFile):he.join(e,"coverage","coverage-run.log");Nt(he.dirname(o),{recursive:!0});let s=jt(o,{flags:"a"}),r=process.stdout.write.bind(process.stdout),t=process.stderr.write.bind(process.stderr);process.stdout.write=(a,p,u)=>(n(a)||s.write(L(a.toString())),r(a,p,u)),process.stderr.write=(a,p,u)=>(n(a)||s.write(L(a.toString())),t(a,p,u)),process.on("exit",()=>s.end())}var Lt=process.cwd(),Ue=[...T.vitestPassthroughArgs];T.coverageQuiet&&!Ue.some(e=>e==="--coverage"||e.startsWith("--coverage."))&&Ue.unshift("--coverage");try{let e=await dt({cwd:Lt,testPatterns:T.testPatterns,testListFile:T.testListFile,testFilePattern:T.testFilePattern,vitestArgs:Ue,showErrorDetails:T.showErrorDetails,coverageQuiet:T.coverageQuiet,suppressFileOutput:T.suppressFileOutput,suppressPassingFiles:T.suppressPassingFiles,topSummary:T.topSummary,json:T.json,mergeReports:T.mergeReports,keepTmp:T.keepTmp,...T.blobsDir!==void 0&&{blobsDir:T.blobsDir},...T.workers!==void 0&&{workers:T.workers},...T.soloPatterns.length>0&&{earlyRunPatterns:T.soloPatterns},...T.scratchDir!==void 0&&{scratchDir:T.scratchDir}});T.json&&(process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
123
|
+
`),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)}
|
|
124
|
+
//# sourceMappingURL=vitest-runner.mjs.map
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommonJS entry point — a thin, synchronous re-export of the real ESM build.
|
|
3
|
+
*
|
|
4
|
+
* Node's `require()` can load an ES module synchronously and get back its
|
|
5
|
+
* exports directly (no Promise, no top-level await involved on this module's
|
|
6
|
+
* side) as long as the target module doesn't itself use top-level await —
|
|
7
|
+
* `src/runner.mjs` doesn't. This mirrors @cldmv/uuid's index.cjs pattern
|
|
8
|
+
* instead of tsup bundling a second, independent copy of the whole module
|
|
9
|
+
* for the CJS format.
|
|
10
|
+
*
|
|
11
|
+
* This file is copied verbatim into dist/index.cjs by tsup's onSuccess hook
|
|
12
|
+
* (see tsup.config.mjs) — it never passes through esbuild itself, so it
|
|
13
|
+
* stays exactly this small regardless of how much src/runner.mjs grows.
|
|
14
|
+
*
|
|
15
|
+
* @module @cldmv/vitest-runner
|
|
16
|
+
*/
|
|
17
|
+
"use strict";
|
|
18
|
+
const { createRequire } = require("node:module");
|
|
19
|
+
const requireESM = createRequire(__filename);
|
|
20
|
+
|
|
21
|
+
module.exports = requireESM("./index.mjs");
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
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};
|
|
47
|
+
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/vitest-runner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Sequential Vitest runner to avoid OOM issues with large test suites",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "index.cjs",
|
|
7
|
-
"module": "index.mjs",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./types/index.d.mts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
+
"vitest-runner-dev": {
|
|
12
|
+
"types": "./types/index.d.mts",
|
|
13
|
+
"import": "./src/runner.mjs"
|
|
14
|
+
},
|
|
11
15
|
"types": "./types/index.d.mts",
|
|
12
|
-
"import": "./index.mjs",
|
|
13
|
-
"require": "./index.cjs"
|
|
16
|
+
"import": "./dist/index.mjs",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
14
18
|
}
|
|
15
19
|
},
|
|
16
20
|
"bin": {
|
|
17
|
-
"vitest-runner": "
|
|
21
|
+
"vitest-runner": "bin/vitest-runner.mjs"
|
|
18
22
|
},
|
|
19
23
|
"files": [
|
|
20
|
-
"
|
|
21
|
-
"
|
|
24
|
+
"dist/",
|
|
25
|
+
"!dist/**/*.map",
|
|
22
26
|
"types/",
|
|
23
|
-
"
|
|
24
|
-
"bin
|
|
27
|
+
"bin/",
|
|
28
|
+
"!bin/**/*.map"
|
|
25
29
|
],
|
|
26
30
|
"scripts": {
|
|
27
31
|
"lint": "eslint --config .configs/eslint.config.mjs .",
|
|
@@ -32,7 +36,11 @@
|
|
|
32
36
|
"test:coverage": "vitest run --coverage --config .configs/vitest.config.mjs",
|
|
33
37
|
"ci:coverage": "vitest run --coverage --reporter=dot --maxWorkers=1 --config .configs/vitest.config.mjs",
|
|
34
38
|
"types:build": "tsc -p .configs/tsconfig.json",
|
|
35
|
-
"types:check": "tsc -p .configs/tsconfig.json --noEmit"
|
|
39
|
+
"types:check": "tsc -p .configs/tsconfig.json --noEmit",
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"build:ci": "npm run lint && npm run format:check && npm run types:build && npm run build",
|
|
42
|
+
"prepack": "node -e \"const fs=require('node:fs');const hasTsup=fs.existsSync('node_modules/.bin/tsup')||fs.existsSync('node_modules/.bin/tsup.cmd');if(hasTsup){require('node:child_process').execSync('npm run build',{stdio:'inherit'})}else if(!fs.existsSync('dist/index.mjs')){throw new Error('dist/ is missing and tsup is unavailable to build it (expected in a bare package-contents publish dir with a prebuilt dist/; unexpected anywhere else)')}\"",
|
|
43
|
+
"fix:headers": "node tools/fix-headers.mjs"
|
|
36
44
|
},
|
|
37
45
|
"keywords": [
|
|
38
46
|
"vitest",
|
|
@@ -41,16 +49,18 @@
|
|
|
41
49
|
"oom"
|
|
42
50
|
],
|
|
43
51
|
"devDependencies": {
|
|
52
|
+
"@cldmv/fix-headers": "^1.3.12",
|
|
44
53
|
"@eslint/js": "^10.0.1",
|
|
45
54
|
"@eslint/json": "^2.0.0",
|
|
46
55
|
"@eslint/markdown": "^8.0.2",
|
|
47
56
|
"@types/node": "^25.3.0",
|
|
48
|
-
"@vitest/coverage-v8": "^
|
|
57
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
49
58
|
"eslint": "^10.5.0",
|
|
50
59
|
"globals": "^17.6.0",
|
|
51
60
|
"prettier": "^3.8.4",
|
|
61
|
+
"tsup": "^8.5.1",
|
|
52
62
|
"typescript": "^5.9.3",
|
|
53
|
-
"vitest": "^
|
|
63
|
+
"vitest": "^5.0.1"
|
|
54
64
|
},
|
|
55
65
|
"dependencies": {
|
|
56
66
|
"chalk": "^5.4.1"
|
|
@@ -64,5 +74,13 @@
|
|
|
64
74
|
"publishConfig": {
|
|
65
75
|
"access": "public"
|
|
66
76
|
},
|
|
77
|
+
"repository": {
|
|
78
|
+
"type": "git",
|
|
79
|
+
"url": "git+https://github.com/CLDMV/vitest-runner.git"
|
|
80
|
+
},
|
|
81
|
+
"bugs": {
|
|
82
|
+
"url": "https://github.com/CLDMV/vitest-runner/issues"
|
|
83
|
+
},
|
|
84
|
+
"homepage": "https://github.com/CLDMV/vitest-runner#readme",
|
|
67
85
|
"license": "MIT"
|
|
68
86
|
}
|