@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 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 with a CJS shim for `require()` compatibility
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 } = await require("vitest-runner");
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
- index.mjs ← ESM entry (re-exports src/runner.mjs)
332
- index.cjs ← CJS shim (dynamic import of index.mjs)
333
- bin/
334
- vitest-runner.mjs ← CLI binary
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/vitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
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/vitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
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/vitest-runner?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
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
@@ -1,97 +1,124 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * @fileoverview CLI entry point for the vitest-runner binary.
4
- * @module vitest-runner/bin/vitest-runner
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
- import { createWriteStream, mkdirSync } from "node:fs";
11
- import path from "node:path";
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
- const args = parseArguments(process.argv.slice(2));
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
- if (args.help) {
20
- showHelp();
21
- process.exit(0);
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
- // Mirror all output (excluding progress bar lines) to a log file
25
- // when running in --coverage-quiet mode (or when --log-file is set).
26
- if ((args.coverageQuiet || args.logFile) && !args.json) {
27
- const cwd = process.cwd();
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
- * Determine if a chunk is a progress-bar write that should be excluded from the log.
40
- * TTY mode uses `\r` to overwrite in place; non-TTY prints "progress N.N% ..." lines.
41
- * @param {Buffer|string} chunk
42
- * @returns {boolean}
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
- process.stdout.write = (chunk, enc, cb) => {
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
- process.stderr.write = (chunk, enc, cb) => {
55
- if (!isProgressChunk(chunk)) logStream.write(stripAnsi(chunk.toString()));
56
- return origStderrWrite(chunk, enc, cb);
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
- process.on("exit", () => logStream.end());
60
- }
54
+ # Run a specific file (partial path ok)
55
+ vitest-runner src/tests/config/background.test.vitest.mjs
61
56
 
62
- const cwd = process.cwd();
57
+ # Run only the files listed in a JSON file
58
+ vitest-runner --test-list my-tests.json
63
59
 
64
- const vitestArgs = [...args.vitestPassthroughArgs];
65
- if (args.coverageQuiet && !vitestArgs.some((a) => a === "--coverage" || a.startsWith("--coverage."))) {
66
- vitestArgs.unshift("--coverage");
67
- }
60
+ # Filter by test name
61
+ vitest-runner src/tests/metadata -t "lazy materialization"
68
62
 
69
- try {
70
- const runResult = await run({
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
- if (args.json) {
89
- process.stdout.write(`${JSON.stringify(runResult, null, 2)}\n`);
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
- process.exit(typeof runResult === "number" ? runResult : 1);
94
- } catch (err) {
95
- console.error("Fatal error:", err);
96
- process.exit(1);
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.2.0",
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": "./bin/vitest-runner.mjs"
21
+ "vitest-runner": "bin/vitest-runner.mjs"
18
22
  },
19
23
  "files": [
20
- "index.mjs",
21
- "index.cjs",
24
+ "dist/",
25
+ "!dist/**/*.map",
22
26
  "types/",
23
- "src/",
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": "^4.1.9",
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": "^4.1.9"
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
  }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};