@cldmv/vitest-runner 1.1.0 → 1.4.2
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 +141 -78
- package/bin/vitest-runner.mjs +1549 -85
- package/dist/index.cjs +1389 -0
- package/dist/index.mjs +1335 -0
- package/package.json +41 -15
- package/types/src/bin/vitest-runner.d.mts +2 -0
- package/types/src/cli/args.d.mts +16 -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 +23 -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 -134
- package/src/cli/help.mjs +0 -84
- 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 -696
- 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/package.json
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/vitest-runner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.2",
|
|
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
|
-
"lint": "eslint
|
|
31
|
+
"lint": "eslint --config .configs/eslint.config.mjs .",
|
|
32
|
+
"format": "prettier --write . --config .configs/.prettierrc",
|
|
33
|
+
"format:check": "prettier --check . --config .configs/.prettierrc",
|
|
28
34
|
"test": "vitest run --config .configs/vitest.config.mjs",
|
|
29
35
|
"test:watch": "vitest --config .configs/vitest.config.mjs",
|
|
30
36
|
"test:coverage": "vitest run --coverage --config .configs/vitest.config.mjs",
|
|
31
37
|
"ci:coverage": "vitest run --coverage --reporter=dot --maxWorkers=1 --config .configs/vitest.config.mjs",
|
|
32
38
|
"types:build": "tsc -p .configs/tsconfig.json",
|
|
33
|
-
"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"
|
|
34
44
|
},
|
|
35
45
|
"keywords": [
|
|
36
46
|
"vitest",
|
|
@@ -39,10 +49,18 @@
|
|
|
39
49
|
"oom"
|
|
40
50
|
],
|
|
41
51
|
"devDependencies": {
|
|
52
|
+
"@cldmv/fix-headers": "^1.3.12",
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
54
|
+
"@eslint/json": "^2.0.0",
|
|
55
|
+
"@eslint/markdown": "^8.0.2",
|
|
42
56
|
"@types/node": "^25.3.0",
|
|
43
|
-
"@vitest/coverage-v8": "^
|
|
57
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
58
|
+
"eslint": "^10.5.0",
|
|
59
|
+
"globals": "^17.6.0",
|
|
60
|
+
"prettier": "^3.8.4",
|
|
61
|
+
"tsup": "^8.5.1",
|
|
44
62
|
"typescript": "^5.9.3",
|
|
45
|
-
"vitest": "^
|
|
63
|
+
"vitest": "^5.0.1"
|
|
46
64
|
},
|
|
47
65
|
"dependencies": {
|
|
48
66
|
"chalk": "^5.4.1"
|
|
@@ -51,10 +69,18 @@
|
|
|
51
69
|
"vitest": ">=1.0.0"
|
|
52
70
|
},
|
|
53
71
|
"engines": {
|
|
54
|
-
"node": ">=
|
|
72
|
+
"node": ">=20.19.0"
|
|
55
73
|
},
|
|
56
74
|
"publishConfig": {
|
|
57
75
|
"access": "public"
|
|
58
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",
|
|
59
85
|
"license": "MIT"
|
|
60
86
|
}
|
package/types/src/cli/args.d.mts
CHANGED
|
@@ -45,6 +45,14 @@ export type ParsedArgs = {
|
|
|
45
45
|
* - Emit a JSON run report instead of text output (`--json`).
|
|
46
46
|
*/
|
|
47
47
|
json: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* - Directory for per-file coverage blobs (`--blobs-dir`); defaults to `<cwd>/.vitest-coverage-blobs`.
|
|
50
|
+
*/
|
|
51
|
+
blobsDir: string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* - `false` when `--no-merge-reports` was passed; leaves blobs in `blobsDir` without merging.
|
|
54
|
+
*/
|
|
55
|
+
mergeReports: boolean;
|
|
48
56
|
/**
|
|
49
57
|
* - Whether `--help` / `-h` was passed.
|
|
50
58
|
*/
|
|
@@ -69,4 +77,12 @@ export type ParsedArgs = {
|
|
|
69
77
|
* - Non-flag positional arguments (file / folder patterns).
|
|
70
78
|
*/
|
|
71
79
|
testPatterns: string[];
|
|
80
|
+
/**
|
|
81
|
+
* - Keep the run's scratch directory instead of removing it on completion (`--keep-tmp`).
|
|
82
|
+
*/
|
|
83
|
+
keepTmp: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* - Per-run scratch root, relative to `cwd` (`--scratch-dir`); defaults to `tmp/vitest-runner`.
|
|
86
|
+
*/
|
|
87
|
+
scratchDir: string | undefined;
|
|
72
88
|
};
|
|
@@ -21,6 +21,29 @@ export function discoverFilesInDir(dir: string, cwd: string, pattern?: RegExp):
|
|
|
21
21
|
* sortWithPriority(files, ['listener-cleanup/']);
|
|
22
22
|
*/
|
|
23
23
|
export function sortWithPriority(files: string[], earlyRunPatterns?: string[]): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Compute, for every file in `files`, the set of *other* files that Vitest's
|
|
26
|
+
* own CLI filter would spuriously also match when that file's path is passed
|
|
27
|
+
* as the filter argument.
|
|
28
|
+
*
|
|
29
|
+
* Vitest's `vitest run <filter>` matches any discovered test file whose path
|
|
30
|
+
* *contains* `<filter>` as a substring — not just an exact-path match. Two
|
|
31
|
+
* files that share the same basename (and immediate parent directory) at
|
|
32
|
+
* different depths — e.g. `tests/contract.test.vitest.mjs` and
|
|
33
|
+
* `packages/a/tests/contract.test.vitest.mjs` — collide because the shorter
|
|
34
|
+
* path is a trailing substring of the longer one, so filtering on the shorter
|
|
35
|
+
* path's exact string also matches the longer path's file. Passing an
|
|
36
|
+
* absolute path does not help: Vitest normalises to a root-relative path
|
|
37
|
+
* before matching.
|
|
38
|
+
*
|
|
39
|
+
* @param {string[]} files - File paths relative to the project root, as returned by discovery.
|
|
40
|
+
* @returns {Map<string, string[]>} Map from each file to the other files it would spuriously match (empty array when unambiguous).
|
|
41
|
+
* @example
|
|
42
|
+
* const conflicts = computeFilterConflicts(["tests/a.mjs", "pkg/tests/a.mjs"]);
|
|
43
|
+
* conflicts.get("tests/a.mjs"); // ["pkg/tests/a.mjs"]
|
|
44
|
+
* conflicts.get("pkg/tests/a.mjs"); // []
|
|
45
|
+
*/
|
|
46
|
+
export function computeFilterConflicts(files: string[]): Map<string, string[]>;
|
|
24
47
|
/**
|
|
25
48
|
* @typedef {Object} DiscoverOptions
|
|
26
49
|
* @property {string} cwd - Project root directory.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a process is still alive.
|
|
3
|
+
*
|
|
4
|
+
* @param {number} pid
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function isPidAlive(pid: number): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Remove scratch roots left behind by runs whose owning process is no longer alive
|
|
10
|
+
* (a crash or `kill -9` skips the normal exit-time cleanup).
|
|
11
|
+
*
|
|
12
|
+
* @param {string} cwd - Project root.
|
|
13
|
+
* @param {string} scratchDir - Scratch base directory, relative to `cwd` (or absolute).
|
|
14
|
+
* @returns {Promise<void>}
|
|
15
|
+
*/
|
|
16
|
+
export function sweepStaleScratchRoots(cwd: string, scratchDir: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Create this run's scratch root: `<cwd>/<scratchDir>/<pid>-<timestamp>/`.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} cwd - Project root.
|
|
21
|
+
* @param {string} scratchDir - Scratch base directory, relative to `cwd` (or absolute).
|
|
22
|
+
* @returns {Promise<string>} Absolute path to the created root.
|
|
23
|
+
*/
|
|
24
|
+
export function createRunScratchRoot(cwd: string, scratchDir: string): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Create a scratch subdirectory under the run root for one file invocation.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} runRoot - This run's scratch root, from `createRunScratchRoot`.
|
|
29
|
+
* @param {number} index - A unique index for this invocation within the run.
|
|
30
|
+
* @returns {Promise<string>} Absolute path to the created directory.
|
|
31
|
+
*/
|
|
32
|
+
export function createFileScratchDir(runRoot: string, index: number): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Remove a run's scratch root (async, best-effort).
|
|
35
|
+
*
|
|
36
|
+
* @param {string} root
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
export function removeScratchRoot(root: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Remove a run's scratch root synchronously (best-effort) — used from a signal
|
|
42
|
+
* handler right before `process.exit()`, where async cleanup can't be awaited.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} root
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
export function removeScratchRootSync(root: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Create a fresh, uniquely-named scratch subdirectory for the calling test file.
|
|
50
|
+
*
|
|
51
|
+
* Reads the run's scratch directory from `process.env.VITEST_RUNNER_TMP`, which
|
|
52
|
+
* vitest-runner sets in every spawned child. Call this from a test file to get
|
|
53
|
+
* isolated scratch space per test case without managing your own `mkdtemp` base.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} label - A short, human-readable label used as the directory name prefix.
|
|
56
|
+
* @returns {string} Absolute path to the newly created directory.
|
|
57
|
+
* @throws {Error} When `VITEST_RUNNER_TMP` is not set (not running under vitest-runner).
|
|
58
|
+
* @example
|
|
59
|
+
* import { makeRunTmpDir } from "@cldmv/vitest-runner";
|
|
60
|
+
* const dir = makeRunTmpDir("my-fixture");
|
|
61
|
+
*/
|
|
62
|
+
export function makeRunTmpDir(label: string): string;
|
|
63
|
+
/** Default scratch root, relative to `cwd`. */
|
|
64
|
+
export const DEFAULT_SCRATCH_DIR: "tmp/vitest-runner";
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Run a single Vitest test file in a child process and return parsed results.
|
|
17
17
|
*
|
|
18
18
|
* @param {string} filePath - Test file path (relative to `cwd` or absolute).
|
|
19
|
-
* @param {SpawnBaseOptions & { vitestArgs?: string[], streamOutput?: boolean }} opts
|
|
19
|
+
* @param {SpawnBaseOptions & { vitestArgs?: string[], streamOutput?: boolean, excludePaths?: string[], extraEnv?: NodeJS.ProcessEnv }} opts
|
|
20
20
|
* @returns {Promise<SingleFileResult>}
|
|
21
21
|
* @example
|
|
22
22
|
* const result = await runSingleFile('src/tests/foo.test.vitest.mjs', {
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
export function runSingleFile(filePath: string, opts: SpawnBaseOptions & {
|
|
29
29
|
vitestArgs?: string[];
|
|
30
30
|
streamOutput?: boolean;
|
|
31
|
+
excludePaths?: string[];
|
|
32
|
+
extraEnv?: NodeJS.ProcessEnv;
|
|
31
33
|
}): Promise<SingleFileResult>;
|
|
32
34
|
/**
|
|
33
35
|
* Run Vitest directly (all files in one process) with inherited stdio.
|
package/types/src/runner.d.mts
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
* Run all discovered Vitest test files sequentially (with a configurable worker
|
|
3
3
|
* pool for the non-solo phase) and return an exit code.
|
|
4
4
|
*
|
|
5
|
+
* Owns the run's scratch directory lifecycle (`scratchDir`/`keepTmp`): sweeps stale
|
|
6
|
+
* roots from dead prior runs, creates this run's root, and removes it on every exit
|
|
7
|
+
* path — normal completion, a thrown error, or SIGINT/SIGTERM — unless `keepTmp` is
|
|
8
|
+
* set. The actual run logic lives in {@link runImpl}; this wrapper only exists to
|
|
9
|
+
* guarantee that cleanup regardless of how `runImpl` returns or throws.
|
|
10
|
+
*
|
|
5
11
|
* @param {RunOptions} opts
|
|
6
12
|
* @returns {Promise<number|object>} `0`/`1` by default; JSON report object when `opts.json` is true.
|
|
7
13
|
*/
|
|
8
14
|
export function run(opts: RunOptions): Promise<number | object>;
|
|
9
15
|
export { formatDuration } from "./utils/duration.mjs";
|
|
10
16
|
export { buildNodeOptions } from "./utils/env.mjs";
|
|
17
|
+
export { makeRunTmpDir } from "./core/scratch.mjs";
|
|
11
18
|
export type PerFileHeapOverride = {
|
|
12
19
|
/**
|
|
13
20
|
* - Substring matched against the normalised file path.
|
|
@@ -79,6 +86,14 @@ export type RunOptions = {
|
|
|
79
86
|
* - Rows in the worst-coverage table (0 = disable).
|
|
80
87
|
*/
|
|
81
88
|
worstCoverageCount?: number;
|
|
89
|
+
/**
|
|
90
|
+
* - Directory for per-file coverage blobs (default `<cwd>/.vitest-coverage-blobs`). Relative paths resolve against `cwd`. Always cleared at the start of a coverage run.
|
|
91
|
+
*/
|
|
92
|
+
blobsDir?: string;
|
|
93
|
+
/**
|
|
94
|
+
* - When `true`, blobs are merged via `vitest --mergeReports`, the coverage summary is printed, and `blobsDir` is deleted at the end. When `false`, the run stops after producing blobs: no merge, no summary, and `blobsDir` is left populated for an external merge step.
|
|
95
|
+
*/
|
|
96
|
+
mergeReports?: boolean;
|
|
82
97
|
/**
|
|
83
98
|
* - Global `--max-old-space-size` ceiling; per-file overrides may raise it.
|
|
84
99
|
*/
|
|
@@ -99,6 +114,14 @@ export type RunOptions = {
|
|
|
99
114
|
* - Value for `NODE_ENV` in child processes.
|
|
100
115
|
*/
|
|
101
116
|
nodeEnv?: string;
|
|
117
|
+
/**
|
|
118
|
+
* - Per-run scratch root, relative to `cwd` (or absolute). A subdirectory is created per file invocation and exposed to it via `VITEST_RUNNER_TMP`.
|
|
119
|
+
*/
|
|
120
|
+
scratchDir?: string;
|
|
121
|
+
/**
|
|
122
|
+
* - Keep the run's scratch root instead of removing it on completion (normal exit, failure, or SIGINT/SIGTERM).
|
|
123
|
+
*/
|
|
124
|
+
keepTmp?: boolean;
|
|
102
125
|
/**
|
|
103
126
|
* -
|
|
104
127
|
*/
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/vitest-runner
|
|
3
|
+
* @Filename: /src/utils/ansi.mjs
|
|
4
|
+
* @Date: 2026-02-24T22:33:55-08:00 (1772001235)
|
|
5
|
+
* @Author: Shinrai <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2026-09-27 08:51:31 -07:00 (1790524291)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
1
13
|
/**
|
|
2
14
|
* @fileoverview ANSI escape-code helpers.
|
|
3
15
|
* @module vitest-runner/src/utils/ansi
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/vitest-runner
|
|
3
|
+
* @Filename: /src/utils/duration.mjs
|
|
4
|
+
* @Date: 2026-02-24T22:33:55-08:00 (1772001235)
|
|
5
|
+
* @Author: Shinrai <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2026-09-27 08:51:31 -07:00 (1790524291)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
1
13
|
/**
|
|
2
14
|
* @fileoverview Duration formatting utilities.
|
|
3
15
|
* @module vitest-runner/src/utils/duration
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/vitest-runner
|
|
3
|
+
* @Filename: /src/utils/env.mjs
|
|
4
|
+
* @Date: 2026-02-24T22:33:55-08:00 (1772001235)
|
|
5
|
+
* @Author: Shinrai <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Shinrai <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2026-09-27 08:51:31 -07:00 (1790524291)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
1
13
|
/**
|
|
2
14
|
* @fileoverview NODE_OPTIONS / environment helpers.
|
|
3
15
|
* @module vitest-runner/src/utils/env
|
package/index.cjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview CJS shim — dynamically imports the ESM entry point so that
|
|
3
|
-
* CommonJS callers can `require('vitest-runner')`.
|
|
4
|
-
*
|
|
5
|
-
* Because the package is pure ESM (`"type": "module"`) we cannot use `module.exports =`
|
|
6
|
-
* directly; instead we export a promise and re-attach named exports once resolved.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // CommonJS usage
|
|
10
|
-
* const { run } = await require('vitest-runner');
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
"use strict";
|
|
14
|
-
|
|
15
|
-
// Async shim: re-export everything from the ESM module.
|
|
16
|
-
// Callers must await the result or use .then().
|
|
17
|
-
module.exports = import("./index.mjs");
|
package/index.mjs
DELETED
package/src/cli/args.mjs
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview CLI argument parsing for the vitest-runner binary.
|
|
3
|
-
* @module vitest-runner/src/cli/args
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} ParsedArgs
|
|
8
|
-
* @property {string|undefined} testListFile - Path to a JSON file of test paths to run (`--test-list`).
|
|
9
|
-
* @property {boolean} showErrorDetails - `false` when `--no-error-details` was passed.
|
|
10
|
-
* @property {boolean} coverageQuiet - Whether `--coverage-quiet` was passed.
|
|
11
|
-
* @property {string|undefined} logFile - Path for the coverage run log (`--log-file`); defaults to `coverage/coverage-run.log`.
|
|
12
|
-
* @property {boolean} suppressFileOutput - Suppress per-file runner output blocks (`--suppress-file-output`).
|
|
13
|
-
* @property {boolean} suppressPassingFiles - Suppress the passed-files section in the final summary (`--suppress-passing-files`).
|
|
14
|
-
* @property {boolean} topSummary - Show top-summary sections for memory and duration (`true` by default, disabled by `--no-top-summary`).
|
|
15
|
-
* @property {boolean} json - Emit a JSON run report instead of text output (`--json`).
|
|
16
|
-
* @property {boolean} help - Whether `--help` / `-h` was passed.
|
|
17
|
-
* @property {number|undefined} workers - Worker count from `--workers <n>`, or undefined.
|
|
18
|
-
* @property {string[]} soloPatterns - Path substrings from `--solo-pattern <pattern>` (repeatable).
|
|
19
|
-
* @property {RegExp|undefined} testFilePattern - Compiled regex from `--file-pattern <regex>`, or undefined.
|
|
20
|
-
* @property {string[]} vitestPassthroughArgs - Flags forwarded verbatim to vitest.
|
|
21
|
-
* @property {string[]} testPatterns - Non-flag positional arguments (file / folder patterns).
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/** Runner-owned flags that must not be forwarded to vitest. */
|
|
25
|
-
const RUNNER_FLAGS = new Set([
|
|
26
|
-
"--test-list",
|
|
27
|
-
"--no-error-details",
|
|
28
|
-
"--coverage-quiet",
|
|
29
|
-
"--log-file",
|
|
30
|
-
"--suppress-file-output",
|
|
31
|
-
"--suppress-passing-files",
|
|
32
|
-
"--no-top-summary",
|
|
33
|
-
"--json",
|
|
34
|
-
"--workers",
|
|
35
|
-
"--solo-pattern",
|
|
36
|
-
"--file-pattern",
|
|
37
|
-
"--help",
|
|
38
|
-
"-h"
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Parse raw CLI arguments into structured runner options.
|
|
43
|
-
*
|
|
44
|
-
* Runner-specific flags are extracted; everything else (flags and their
|
|
45
|
-
* optional values) is forwarded to vitest as passthrough args.
|
|
46
|
-
* A flag that takes a value (where the next token does not start with `-`)
|
|
47
|
-
* consumes that token too.
|
|
48
|
-
*
|
|
49
|
-
* @param {string[]} args - Raw argument array (typically `process.argv.slice(2)`).
|
|
50
|
-
* @returns {ParsedArgs}
|
|
51
|
-
* @example
|
|
52
|
-
* parseArguments(['--test-list', 'tests.json', '--workers', '2', '--reporter=verbose']);
|
|
53
|
-
*/
|
|
54
|
-
export function parseArguments(args) {
|
|
55
|
-
const vitestPassthroughArgs = [];
|
|
56
|
-
const testPatterns = [];
|
|
57
|
-
const soloPatterns = [];
|
|
58
|
-
let testListFile;
|
|
59
|
-
let showErrorDetails = true;
|
|
60
|
-
let coverageQuiet = false;
|
|
61
|
-
let logFile;
|
|
62
|
-
let suppressFileOutput = false;
|
|
63
|
-
let suppressPassingFiles = false;
|
|
64
|
-
let topSummary = true;
|
|
65
|
-
let json = false;
|
|
66
|
-
let workers;
|
|
67
|
-
let help = false;
|
|
68
|
-
let testFilePattern;
|
|
69
|
-
for (let i = 0; i < args.length; i++) {
|
|
70
|
-
const arg = args[i];
|
|
71
|
-
|
|
72
|
-
if (arg === "--test-list") {
|
|
73
|
-
testListFile = args[++i];
|
|
74
|
-
} else if (arg.startsWith("--test-list=")) {
|
|
75
|
-
testListFile = arg.slice("--test-list=".length);
|
|
76
|
-
} else if (arg === "--no-error-details") {
|
|
77
|
-
showErrorDetails = false;
|
|
78
|
-
} else if (arg === "--coverage-quiet") {
|
|
79
|
-
coverageQuiet = true;
|
|
80
|
-
} else if (arg === "--log-file") {
|
|
81
|
-
logFile = args[++i];
|
|
82
|
-
} else if (arg.startsWith("--log-file=")) {
|
|
83
|
-
logFile = arg.slice("--log-file=".length);
|
|
84
|
-
} else if (arg === "--suppress-file-output") {
|
|
85
|
-
suppressFileOutput = true;
|
|
86
|
-
} else if (arg === "--suppress-passing-files") {
|
|
87
|
-
suppressPassingFiles = true;
|
|
88
|
-
} else if (arg === "--no-top-summary") {
|
|
89
|
-
topSummary = false;
|
|
90
|
-
} else if (arg === "--json") {
|
|
91
|
-
json = true;
|
|
92
|
-
} else if (arg === "--workers") {
|
|
93
|
-
workers = parseInt(args[++i], 10);
|
|
94
|
-
} else if (arg.startsWith("--workers=")) {
|
|
95
|
-
workers = parseInt(arg.slice("--workers=".length), 10);
|
|
96
|
-
} else if (arg === "--solo-pattern") {
|
|
97
|
-
soloPatterns.push(args[++i]);
|
|
98
|
-
} else if (arg.startsWith("--solo-pattern=")) {
|
|
99
|
-
soloPatterns.push(arg.slice("--solo-pattern=".length));
|
|
100
|
-
} else if (arg === "--file-pattern") {
|
|
101
|
-
testFilePattern = new RegExp(args[++i], "i");
|
|
102
|
-
} else if (arg.startsWith("--file-pattern=")) {
|
|
103
|
-
testFilePattern = new RegExp(arg.slice("--file-pattern=".length), "i");
|
|
104
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
105
|
-
help = true;
|
|
106
|
-
} else if ((arg.startsWith("--") || arg.startsWith("-")) && !RUNNER_FLAGS.has(arg)) {
|
|
107
|
-
vitestPassthroughArgs.push(arg);
|
|
108
|
-
// Consume the next token if it looks like a value (not another flag)
|
|
109
|
-
if (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
110
|
-
vitestPassthroughArgs.push(args[++i]);
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
// Any remaining token (cannot start with '-'; those are caught above)
|
|
114
|
-
testPatterns.push(arg);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
testListFile,
|
|
120
|
-
showErrorDetails,
|
|
121
|
-
coverageQuiet,
|
|
122
|
-
logFile,
|
|
123
|
-
suppressFileOutput,
|
|
124
|
-
suppressPassingFiles,
|
|
125
|
-
topSummary,
|
|
126
|
-
json,
|
|
127
|
-
help,
|
|
128
|
-
workers,
|
|
129
|
-
soloPatterns,
|
|
130
|
-
testFilePattern,
|
|
131
|
-
vitestPassthroughArgs,
|
|
132
|
-
testPatterns
|
|
133
|
-
};
|
|
134
|
-
}
|
package/src/cli/help.mjs
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview CLI help text for the vitest-runner binary.
|
|
3
|
-
* @module vitest-runner/src/cli/help
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Print the full CLI help message to stdout.
|
|
10
|
-
* @returns {void}
|
|
11
|
-
* @example
|
|
12
|
-
* showHelp();
|
|
13
|
-
*/
|
|
14
|
-
export function showHelp() {
|
|
15
|
-
console.log(`
|
|
16
|
-
${chalk.bold("Vitest Sequential Runner")}
|
|
17
|
-
Runs each test file in its own Vitest process to avoid OOM issues.
|
|
18
|
-
|
|
19
|
-
${chalk.bold("USAGE:")}
|
|
20
|
-
vitest-runner [OPTIONS] [PATTERNS]
|
|
21
|
-
|
|
22
|
-
${chalk.bold("SPECIAL FLAGS:")}
|
|
23
|
-
--test-list <file> Run only the files listed in a JSON array file
|
|
24
|
-
--file-pattern <regex> Override the file discovery regex (default: \.test\.vitest\.(?:js|mjs|cjs)$)
|
|
25
|
-
--workers <n> Number of parallel workers (default: 4 or VITEST_WORKERS)
|
|
26
|
-
--solo-pattern <pat> Run files matching this path substring solo first (repeatable)
|
|
27
|
-
--no-error-details Hide detailed error output (show only counts)
|
|
28
|
-
--coverage-quiet Implies --coverage; show progress bar + final summaries only
|
|
29
|
-
--log-file <path> Path for mirrored runner output (default: coverage/coverage-run.log with --coverage-quiet)
|
|
30
|
-
--suppress-file-output Suppress per-file runner output blocks
|
|
31
|
-
--suppress-passing-files Hide the PASSED TEST FILES section in the final summary
|
|
32
|
-
--no-top-summary Hide TOP MEMORY USERS and TOP DURATION sections
|
|
33
|
-
--json Emit JSON summary output instead of text logs
|
|
34
|
-
--help, -h Show this help message
|
|
35
|
-
|
|
36
|
-
${chalk.bold("TEST PATTERNS:")}
|
|
37
|
-
[file] Run a specific test file (supports partial paths)
|
|
38
|
-
[folder] Run all tests in a folder
|
|
39
|
-
|
|
40
|
-
Examples:
|
|
41
|
-
src/tests/config/background.test.vitest.mjs
|
|
42
|
-
src/tests/metadata
|
|
43
|
-
background.test.vitest.mjs
|
|
44
|
-
|
|
45
|
-
${chalk.bold("VITEST FLAGS:")}
|
|
46
|
-
All standard Vitest CLI flags are supported and passed through:
|
|
47
|
-
-t, --testNamePattern Filter tests by name pattern (regex)
|
|
48
|
-
--reporter Change reporter (verbose, dot, json, etc.)
|
|
49
|
-
--coverage Run full-suite coverage (blob-per-file + mergeReports)
|
|
50
|
-
--bail Stop on first failure
|
|
51
|
-
|
|
52
|
-
See the Vitest documentation for the full list.
|
|
53
|
-
|
|
54
|
-
${chalk.bold("ENVIRONMENT VARIABLES:")}
|
|
55
|
-
VITEST_HEAP_MB Set max heap size per test (default: Node.js default)
|
|
56
|
-
VITEST_WORKERS Number of parallel workers (default: 4, overridden by --workers)
|
|
57
|
-
# Run all tests
|
|
58
|
-
vitest-runner
|
|
59
|
-
|
|
60
|
-
# Run a specific file (partial path ok)
|
|
61
|
-
vitest-runner src/tests/config/background.test.vitest.mjs
|
|
62
|
-
|
|
63
|
-
# Run only the files listed in a JSON file
|
|
64
|
-
vitest-runner --test-list my-tests.json
|
|
65
|
-
|
|
66
|
-
# Filter by test name
|
|
67
|
-
vitest-runner src/tests/metadata -t "lazy materialization"
|
|
68
|
-
|
|
69
|
-
# Run with 2 workers
|
|
70
|
-
vitest-runner --workers 2
|
|
71
|
-
|
|
72
|
-
# Run files matching a pattern solo first, then the rest in parallel
|
|
73
|
-
vitest-runner --solo-pattern listener-cleanup/ --solo-pattern heavy/
|
|
74
|
-
|
|
75
|
-
# Hide detailed errors
|
|
76
|
-
vitest-runner --no-error-details
|
|
77
|
-
|
|
78
|
-
# Coverage with quiet output + progress bar
|
|
79
|
-
vitest-runner --coverage --coverage-quiet
|
|
80
|
-
|
|
81
|
-
# Custom heap and workers
|
|
82
|
-
VITEST_HEAP_MB=8192 vitest-runner --workers 2 src/tests/heavy
|
|
83
|
-
`);
|
|
84
|
-
}
|