From 32337cc4f77616ee685b7f4242a6986ae78dddaa Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 10:42:43 -0700 Subject: [PATCH 1/7] fix(cjs): fail clearly on Node without require(esm) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/cjs-shim.cjs loaded dist/index.mjs via createRequire(__filename), but a .cjs file already has a plain require() available — createRequire is unnecessary and breaks static-require detection in bundlers like esbuild/webpack. Switched to a plain require("./index.mjs"). On Node.js versions without require(esm) (before 20.19 / 22.12), that plain require() would fail with a bare, confusing ERR_REQUIRE_ESM. Check process.features.require_module up front and throw a clear message pointing at import() instead. tests/cjs: node:test checks run by npm test/coverage after Vitest: require() returns the same named exports as import, and the version check fires when require(esm) is off. --- package.json | 7 +++--- src/cjs-shim.cjs | 23 +++++++++++++---- tests/cjs/entry.test.cjs | 54 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 tests/cjs/entry.test.cjs diff --git a/package.json b/package.json index 451ccd9..4145667 100644 --- a/package.json +++ b/package.json @@ -31,10 +31,11 @@ "lint": "eslint --config .configs/eslint.config.mjs .", "format": "prettier --write . --config .configs/.prettierrc", "format:check": "prettier --check . --config .configs/.prettierrc", - "test": "vitest run --config .configs/vitest.config.mjs", + "test": "vitest run --config .configs/vitest.config.mjs && npm run test:cjs", + "test:cjs": "npm run build && node --test tests/cjs/entry.test.cjs", "test:watch": "vitest --config .configs/vitest.config.mjs", - "test:coverage": "vitest run --coverage --config .configs/vitest.config.mjs", - "ci:coverage": "vitest run --coverage --reporter=dot --maxWorkers=1 --config .configs/vitest.config.mjs", + "test:coverage": "vitest run --coverage --config .configs/vitest.config.mjs && npm run test:cjs", + "ci:coverage": "vitest run --coverage --reporter=dot --maxWorkers=1 --config .configs/vitest.config.mjs && npm run test:cjs", "types:build": "tsc -p .configs/tsconfig.json", "types:check": "tsc -p .configs/tsconfig.json --noEmit", "build": "tsup", diff --git a/src/cjs-shim.cjs b/src/cjs-shim.cjs index bf3b04a..274890a 100644 --- a/src/cjs-shim.cjs +++ b/src/cjs-shim.cjs @@ -7,7 +7,7 @@ * @Email: * ----- * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) - * @Last modified time: 2026-10-02T15:35:18-07:00 (1790980518) + * @Last modified time: 2026-10-03T10:36:56-07:00 (1791049016) * ----- * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. * @@ -21,7 +21,14 @@ * side) as long as the target module doesn't itself use top-level await — * `src/runner.mjs` doesn't. This mirrors @cldmv/uuid's index.cjs pattern * instead of tsup bundling a second, independent copy of the whole module - * for the CJS format. + * for the CJS format. In a .cjs file `require` already exists, so a plain + * `require("./index.mjs")` is used — `createRequire` is unnecessary here and + * breaks bundling by tools (esbuild/webpack) that need a static `require()` + * call to detect the dependency. + * + * Node.js versions without require(esm) (before 20.19 / 22.12) would fail with + * a bare ERR_REQUIRE_ESM, so the check below fails early with a message that + * says what to do instead. * * This file is copied verbatim into dist/index.cjs by tsup's onSuccess hook * (see tsup.config.mjs) — it never passes through esbuild itself, so it @@ -30,7 +37,13 @@ * @module @cldmv/vitest-runner */ "use strict"; -const { createRequire } = require("node:module"); -const requireESM = createRequire(__filename); -module.exports = requireESM("./index.mjs"); +if (!process.features?.require_module) { + const error = new Error( + `@cldmv/vitest-runner: require() needs Node.js ^20.19.0 or >=22.12.0 (this is ${process.version}). On older Node.js, load the package with import() instead.` + ); + error.code = "ERR_REQUIRE_ESM"; + throw error; +} + +module.exports = require("./index.mjs"); diff --git a/tests/cjs/entry.test.cjs b/tests/cjs/entry.test.cjs new file mode 100644 index 0000000..26f768c --- /dev/null +++ b/tests/cjs/entry.test.cjs @@ -0,0 +1,54 @@ +/** + * + * @Project: @cldmv/vitest-runner + * @Filename: /tests/cjs/entry.test.cjs + * @Date: 2026-10-03T10:33:01-07:00 (1791048781) + * @Author: Nate Corcoran + * @Email: + * ----- + * @Last modified by: Nate Corcoran (Shinrai@users.noreply.github.com) + * @Last modified time: 2026-10-03T10:34:18-07:00 (1791048858) + * ----- + * @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved. + * + */ + +/** + * CommonJS entry tests. These run under Node's own test runner (`node --test`), not Vitest: + * Vitest loads files through its own module runner, so it cannot show whether a plain + * `require()` of the package works the way it does for a CommonJS consumer. + */ +"use strict"; + +const { test } = require("node:test"); +const assert = require("node:assert/strict"); +const { spawnSync } = require("node:child_process"); +const path = require("node:path"); + +const repoRoot = path.resolve(__dirname, "../.."); + +test("require() returns the same named exports as import", async () => { + const required = require("../../dist/index.cjs"); + const esm = await import("../../dist/index.mjs"); + + assert.equal(typeof required.run, "function"); + assert.equal(required.run, esm.run); + assert.equal(required.resolveBin, esm.resolveBin); + assert.equal(required.discoverVitestFiles, esm.discoverVitestFiles); + assert.equal(required.formatDuration, esm.formatDuration); + assert.deepEqual(Object.keys(required).sort(), Object.keys(esm).sort()); +}); + +test("require() fails with a clear message where Node.js has no require(esm)", () => { + // --no-experimental-require-module turns require(esm) off, which is what Node.js + // versions before 20.19 / 22.12 look like to the entry. + const res = spawnSync(process.execPath, ["--no-experimental-require-module", "-e", "require('./dist/index.cjs')"], { + cwd: repoRoot, + encoding: "utf8" + }); + + assert.notEqual(res.status, 0); + assert.match(res.stderr, /ERR_REQUIRE_ESM/); + assert.match(res.stderr, /require\(\) needs Node\.js \^20\.19\.0 or >=22\.12\.0/); + assert.match(res.stderr, /import\(\)/); +}); From 8cf47029814a03a3f884f73344c4955c09586b0f Mon Sep 17 00:00:00 2001 From: "cldmv-bot[bot]" <230771808+cldmv-bot[bot]@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:13:43 +0000 Subject: [PATCH 2/7] chore: bump version to 1.5.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67036c1..3ac8b6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cldmv/vitest-runner", - "version": "1.5.2", + "version": "1.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cldmv/vitest-runner", - "version": "1.5.2", + "version": "1.5.3", "license": "MIT", "dependencies": { "chalk": "^6.0.1" diff --git a/package.json b/package.json index 4145667..b499f4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cldmv/vitest-runner", - "version": "1.5.2", + "version": "1.5.3", "description": "Sequential Vitest runner to avoid OOM issues with large test suites", "type": "module", "main": "./dist/index.cjs", From fea6698f815b7efca0844a05970b0ef75be40577 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 16:55:48 -0700 Subject: [PATCH 3/7] =?UTF-8?q?docs(changelog):=20backfill=20changelogs=20?= =?UTF-8?q?for=20v1.0.0=E2=80=93v1.3.3=20and=20v1.5.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These releases shipped without a per-version changelog file. Each file is written from the diff against the previous release, notes that v1.3.0–v1.3.3 never reached npm (their changes first shipped in v1.4.2), and records the behaviour changes in minor releases: v1.1.0 stopped --log-file from enabling coverage, and v1.2.0 raised engines.node to >=20.19.0. The v1.5.2 notes record that its chalk 6 dependency declares Node.js >=22 while engines.node still reads >=20.19.0. --- docs/changelog/v1/v1.0.0.md | 69 +++++++++++++++++++++++++++++++++++++ docs/changelog/v1/v1.0.1.md | 23 +++++++++++++ docs/changelog/v1/v1.0.2.md | 24 +++++++++++++ docs/changelog/v1/v1.0.3.md | 24 +++++++++++++ docs/changelog/v1/v1.1.0.md | 69 +++++++++++++++++++++++++++++++++++++ docs/changelog/v1/v1.2.0.md | 66 +++++++++++++++++++++++++++++++++++ docs/changelog/v1/v1.3.0.md | 32 +++++++++++++++++ docs/changelog/v1/v1.3.1.md | 27 +++++++++++++++ docs/changelog/v1/v1.3.2.md | 27 +++++++++++++++ docs/changelog/v1/v1.3.3.md | 27 +++++++++++++++ docs/changelog/v1/v1.5.2.md | 36 +++++++++++++++++++ 11 files changed, 424 insertions(+) create mode 100644 docs/changelog/v1/v1.0.0.md create mode 100644 docs/changelog/v1/v1.0.1.md create mode 100644 docs/changelog/v1/v1.0.2.md create mode 100644 docs/changelog/v1/v1.0.3.md create mode 100644 docs/changelog/v1/v1.1.0.md create mode 100644 docs/changelog/v1/v1.2.0.md create mode 100644 docs/changelog/v1/v1.3.0.md create mode 100644 docs/changelog/v1/v1.3.1.md create mode 100644 docs/changelog/v1/v1.3.2.md create mode 100644 docs/changelog/v1/v1.3.3.md create mode 100644 docs/changelog/v1/v1.5.2.md diff --git a/docs/changelog/v1/v1.0.0.md b/docs/changelog/v1/v1.0.0.md new file mode 100644 index 0000000..a384901 --- /dev/null +++ b/docs/changelog/v1/v1.0.0.md @@ -0,0 +1,69 @@ +# vitest-runner v1.0.0 Changelog + +**Release Date**: February 2026 +**Release Type**: Major + +--- + +## Overview + +First published release of `@cldmv/vitest-runner`, a sequential Vitest runner that spawns each test file in its own child process so that large suites do not run out of memory in a single vitest process. It is usable both as a `vitest-runner` CLI and as a programmatic Node.js API (`run()`), and is published to npm under the `@cldmv` scope. + +This release is the initial feature set; there is no previous version to compare against. The package is pure ESM with a small CommonJS shim (`index.cjs`) so `require()` consumers can load it through a dynamic import. + +--- + +## ✨ Features + +### Per-file child processes, sequential or in a worker pool + +Every discovered test file is run in its own `vitest` child process. Files run one at a time or in a parallel worker pool; the pool size is `--workers ` (CLI), the `workers` option (API) or the `VITEST_WORKERS` environment variable, defaulting to `4`. Files matching `--solo-pattern ` (repeatable; API: `earlyRunPatterns`) run solo, one at a time, before the worker pool starts. The runner auto-detects the vitest config (`vitest.config.ts`, `vite.config.ts`, and the other standard names) or accepts an explicit `vitestConfig` path. + +### Out-of-memory-safe coverage via blob-per-file merging + +Passing `--coverage` switches the runner to a blob-per-file strategy: each file runs with `--coverage --reporter=blob` into its own temporary directory, and after every file has finished a single `vitest --mergeReports` call combines the blobs into one coverage report. Temporary blob and coverage directories are removed automatically. After the merge the runner prints a coverage summary and a "worst coverage files" table (`worstCoverageCount`, default `10`, `0` disables it). + +`--coverage-quiet` implies `--coverage`, suppresses per-file output and renders a live progress bar followed by the final summaries. In this mode output is mirrored with ANSI codes stripped to `coverage/coverage-run.log`; the path is changed with `--log-file `, and passing `--log-file` on its own also enables quiet mode. + +### Memory controls + +`VITEST_HEAP_MB` (or the `maxOldSpaceMb` option) sets a `--max-old-space-size` ceiling for every child process. `perFileHeapOverrides` (`{ pattern, heapMb }` entries, first substring match wins) raises the ceiling for individual files; the larger of the override and the global value is used. The `conditions` option forwards extra `--conditions` Node flags and `nodeEnv` sets `NODE_ENV` in children (default `development`). + +### File discovery + +By default the runner discovers `*.test.vitest.js`, `*.test.vitest.mjs` and `*.test.vitest.cjs`, skipping `node_modules` and hidden directories. The pattern is overridden with `--file-pattern ` or the `testFilePattern` option. `--test-list ` (API: `testListFile`) runs exactly the files in a JSON array and skips scanning. Positional arguments (`testPatterns`) filter by file path, partial path or directory. + +### CLI + +```sh +vitest-runner [OPTIONS] [PATTERNS...] +``` + +Runner flags: `--test-list`, `--file-pattern`, `--workers`, `--solo-pattern`, `--no-error-details`, `--coverage-quiet`, `--log-file`, `--help`/`-h`. Any other flag, and its value when the next token does not start with `-`, is forwarded verbatim to every vitest child process (for example `--reporter=verbose`, `-t "name"`, `--bail`). + +### Programmatic API + +```js +import { run } from "@cldmv/vitest-runner"; + +const code = await run({ testDir: "src/tests" }); +process.exit(code); +``` + +`run(options)` resolves to an exit code (`0` all passed, `1` any failure) and never calls `process.exit` itself. Options: `cwd`, `testDir`, `vitestConfig`, `testPatterns`, `testListFile`, `testFilePattern`, `vitestArgs`, `showErrorDetails`, `coverageQuiet`, `workers`, `worstCoverageCount`, `maxOldSpaceMb`, `earlyRunPatterns`, `perFileHeapOverrides`, `conditions`, `nodeEnv`. The helpers used internally (discovery, parsing, spawning, reporting, progress) are re-exported from the package root. + +--- + +## 🔧 Dependencies + +- `chalk` ^5.4.1 (runtime) +- `vitest` >=1.0.0 (peer dependency) +- `vitest` ^4.0.18, `@vitest/coverage-v8` ^4.0.18, `typescript` ^5.9.3, `@types/node` ^25.3.0 (dev) + +--- + +## Upgrade notes + +- First release; nothing to migrate. Requires Node.js >=18 (as declared by `engines`) and a project-local `vitest`. +- Install with `npm install --save-dev @cldmv/vitest-runner`. The bundled README still shows the unscoped `vitest-runner` name in its install and import snippets at this release; the published package name is `@cldmv/vitest-runner`. +- Type declarations ship in `types/`. The package's `bin` field uses a leading `./` path, which npm strips on publish, so the `vitest-runner` command is not installed by this release (see [v1.4.0](./v1.4.0.md)). diff --git a/docs/changelog/v1/v1.0.1.md b/docs/changelog/v1/v1.0.1.md new file mode 100644 index 0000000..75a228f --- /dev/null +++ b/docs/changelog/v1/v1.0.1.md @@ -0,0 +1,23 @@ +# vitest-runner v1.0.1 Changelog + +**Release Date**: February 2026 +**Release Type**: Patch + +--- + +## Overview + +Documentation-only release. The README's npm package links (version, downloads and last-update badges) now point at the scoped package page `https://www.npmjs.com/package/@cldmv/vitest-runner` instead of the unscoped `vitest-runner` name. No runtime code changed. + +--- + +## 📚 Documentation + +- `README.md`: the three npm badge link targets (`npm_version_url`, `npm_downloads_url`, `npm_last_update_url`) now include the `@cldmv` scope. The badge image URLs themselves still use the unscoped name. + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.0.0](./v1.0.0.md). +- No runtime code changed. diff --git a/docs/changelog/v1/v1.0.2.md b/docs/changelog/v1/v1.0.2.md new file mode 100644 index 0000000..4ead319 --- /dev/null +++ b/docs/changelog/v1/v1.0.2.md @@ -0,0 +1,24 @@ +# vitest-runner v1.0.2 Changelog + +**Release Date**: March 2026 +**Release Type**: Patch + +--- + +## Overview + +A small fix to the "worst coverage files" table printed after a coverage run. The table now lists only files that are actually below 100% and ranks them by their weakest metric rather than by line coverage alone. Only `src/core/report.mjs` changed. + +--- + +## 🐛 Bug Fixes + +### Worst-coverage table no longer lists fully covered files + +Previously every file in the coverage summary was a candidate and the list was sorted by line percentage, so a file with 100% lines but poor branch coverage ranked as healthy, and fully covered files could pad the table when few files were weak. The rows are now filtered to files where `min(lines, statements, functions, branches) < 100` and sorted ascending by that minimum, so the files with the most room to improve appear first. The `worstCoverageCount` limit and the "... and N more files" trailer behave as before. + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.0.1](./v1.0.1.md). Only the ordering and contents of the printed worst-coverage table change. diff --git a/docs/changelog/v1/v1.0.3.md b/docs/changelog/v1/v1.0.3.md new file mode 100644 index 0000000..a136e99 --- /dev/null +++ b/docs/changelog/v1/v1.0.3.md @@ -0,0 +1,24 @@ +# vitest-runner v1.0.3 Changelog + +**Release Date**: March 2026 +**Release Type**: Patch + +--- + +## Overview + +Follow-up to [v1.0.2](./v1.0.2.md): the worst-coverage table is now labelled and rendered consistently with how it is ranked. Only `src/core/report.mjs` changed. + +--- + +## 🐛 Bug Fixes + +### Worst-coverage table headline and percentage matched the ranking + +In v1.0.2 the rows were ranked by the lowest of the four metrics, but the heading still read `WORST COVERAGE FILES (lines)` and the highlighted percentage on each row was the line percentage, so a row's headline number could be higher than the metric that put it in the table. The heading is now `WORST COVERAGE FILES (lowest metric)`, the highlighted (colourised) percentage is the file's lowest metric, and the dimmed detail now lists all four values as `lines | stmts | fns | branches` (previously `lines` was the highlighted number and only stmts, fns and branches were listed). + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.0.2](./v1.0.2.md). Anything that parses the printed table text will see the new heading and detail format. diff --git a/docs/changelog/v1/v1.1.0.md b/docs/changelog/v1/v1.1.0.md new file mode 100644 index 0000000..66ad3a8 --- /dev/null +++ b/docs/changelog/v1/v1.1.0.md @@ -0,0 +1,69 @@ +# vitest-runner v1.1.0 Changelog + +**Release Date**: March 2026 +**Release Type**: Minor + +--- + +## Overview + +This release adds machine-readable output and finer control over what the runner prints: a `--json` mode that returns a structured run report, plus switches to suppress per-file output, the passed-files list and the top memory/duration sections. It also changes how `--log-file` behaves, which is a small behavioural break for CLI users who relied on the old implied coverage mode. + +--- + +## 💥 Breaking Changes + +### `--log-file` no longer injects `--coverage` + +In v1.0.x, passing `--log-file ` by itself made the CLI add `--coverage` to the vitest arguments (the README described this as implying quiet mode). It now only enables log mirroring; coverage runs only when `--coverage`, `--coverage-quiet` or another `--coverage.*` flag is present. The default log location is still `coverage/coverage-run.log` when `--coverage-quiet` is active. + +Upgrade steps: if a script relied on `--log-file` alone to get a coverage run, add `--coverage` (or `--coverage-quiet` for the progress bar) explicitly (for example `vitest-runner --coverage-quiet --log-file out.log`). `--help` text and the README were updated to match. + +--- + +## ✨ Features + +### JSON run reports (`--json` / `json: true`) + +With `--json` the CLI prints a single JSON document to stdout and no runner text output (log mirroring is also skipped), and exits with the report's `exitCode`. Through the API, `run({ json: true })` resolves to the report object instead of an exit code, so callers must read `report.exitCode` themselves. + +```sh +vitest-runner --json +vitest-runner --json --no-top-summary +``` + +```js +const report = await run({ testDir: "src/tests", json: true }); +console.log(report.exitCode); +``` + +The report has a `mode` of `"standard"` or `"coverage"` and includes: + +- `exitCode` and an `options` echo of the effective settings. +- Standard runs: `totals` (`testFilesPass`, `testFilesFail`, `testsPass`, `testsFail`, `testsSkip`, `totalTests`), `timing`, `heap`, and `results.all`/`results.passed`/`results.failed`. +- Coverage runs: `totals` (`testFiles`, `failedFiles`, `passedFiles`), `results.all`/`results.failed`, `merge` (`blobFiles`, `exitCode`, `output`) and `coverageSummary`. +- `topMemoryUsers` and `topDuration` arrays (top 10 each) unless `--no-top-summary` is set. +- A `message` field and `exitCode: 1` when no test files are found, or when a coverage run produced no blobs. + +### Output suppression flags + +- `--suppress-file-output` (`suppressFileOutput`): hide the per-file output blocks, header banner and per-file pass/fail lines in any mode, not only in `--coverage-quiet`. +- `--suppress-passing-files` (`suppressPassingFiles`): hide the `PASSED TEST FILES` section of the final summary. +- `--no-top-summary` (`topSummary: false`): hide the `TOP MEMORY USERS` and `TOP DURATION` sections (and the matching JSON arrays). + +### Coverage summary returns data + +`printCoverageSummary` now accepts an `{ silent }` option and returns an object (`coverageDir`, `total`, `worstFiles`, `worstFilesShown`, `worstFilesTotal`, `summary`) instead of `undefined`, or `null` when no coverage JSON is found. This is what feeds the JSON report; text output is unchanged when `silent` is not set. + +--- + +## 🧪 Tests + +New and extended tests cover the CLI argument parser, help text, `bin` behaviour, the coverage report return value, the integration run and the mocked spawn paths for the new options. + +--- + +## Upgrade notes + +- Review any script that uses `--log-file` without `--coverage-quiet` (see Breaking Changes). Otherwise this is a drop-in upgrade from [v1.0.3](./v1.0.3.md). +- The new flags and options are all opt-in; defaults for text output are unchanged. diff --git a/docs/changelog/v1/v1.2.0.md b/docs/changelog/v1/v1.2.0.md new file mode 100644 index 0000000..e3cd5bc --- /dev/null +++ b/docs/changelog/v1/v1.2.0.md @@ -0,0 +1,66 @@ +# vitest-runner v1.2.0 Changelog + +**Release Date**: June 2026 +**Release Type**: Minor + +--- + +## Overview + +The runtime addition in this release is control over where coverage blobs are written and whether the runner merges them, so a second blob set (for example from a browser-mode run) can be merged together with the node-mode blobs in one external step. The rest of the release is tooling: ESLint and Prettier are wired into the repo and CI, the CI matrix covers Node 20, 22 and 24, and `engines.node` is raised. + +--- + +## 💥 Breaking Changes + +### Minimum Node.js is now 20.19.0 + +`engines.node` changed from `>=18.0.0` to `>=20.19.0`. Node 18 is no longer supported, and installs on older Node versions will emit an engine warning (or fail under `engine-strict`). + +Upgrade steps: run the package on Node 20.19.0 or newer; CI now tests Node 20, 22 and 24. + +--- + +## ✨ Features + +### `blobsDir` and `mergeReports` coverage options + +- `--blobs-dir ` / `blobsDir`: directory for per-file coverage blobs. Relative paths resolve against `cwd`; the default is `/.vitest-coverage-blobs`. The directory is cleared at the start of every coverage run. +- `--no-merge-reports` / `mergeReports: false` (default `true`): stop after the per-file blobs are written. The internal `vitest --mergeReports` call and the coverage summary are skipped, and `blobsDir` is left populated instead of being deleted. The exit code still reflects test pass/fail, and in a JSON report `coverageSummary` is `null`. + +```js +await run({ + testDir: "src/tests", + vitestArgs: ["--coverage"], + blobsDir: ".coverage-blobs/node", + mergeReports: false +}); +// later, merge every blob set in one step: +// vitest --mergeReports .coverage-blobs --coverage +``` + +The README gains a "Producing blobs for an external merge" section describing this workflow. + +--- + +## 🔧 CI & tooling + +- ESLint flat config (`.configs/eslint.config.mjs`, modelled on `@cldmv/slothlet`) and Prettier (`.configs/.prettierrc`, `.prettierignore`) are added. The `lint` script now lints the whole repo with that config, and new `format` and `format:check` scripts are available. +- `ci.yml` runs `npm run lint` and `npm run format:check` before the type check, and the Node matrix grows from `[20]` to `[20, 22, 24]`. +- Small lint-driven source edits with no behaviour change: the thrown "Failed to read test list file" error now carries `{ cause }`, unused imports are removed from `src/runner.mjs`, and an unused parameter in the no-op progress tracker is renamed. +- The README was reformatted by Prettier (tables, quote style, indentation) and the `--help` text for `--file-pattern` now escapes the regex backslashes correctly. + +--- + +## 🔧 Dependencies + +- `vitest` ^4.0.18 → ^4.1.9 (dev) +- `@vitest/coverage-v8` ^4.0.18 → ^4.1.9 (dev) +- Added `eslint` ^10.5.0, `@eslint/js` ^10.0.1, `@eslint/json` ^2.0.0, `@eslint/markdown` ^8.0.2, `globals` ^17.6.0, `prettier` ^3.8.4 (dev) +- Runtime dependencies (`chalk`) are unchanged. + +--- + +## Upgrade notes + +- Node.js 20.19.0 or newer is required. Otherwise this is a drop-in upgrade from [v1.1.0](./v1.1.0.md); `blobsDir` and `mergeReports` are opt-in and default to the previous behaviour. diff --git a/docs/changelog/v1/v1.3.0.md b/docs/changelog/v1/v1.3.0.md new file mode 100644 index 0000000..837cb4e --- /dev/null +++ b/docs/changelog/v1/v1.3.0.md @@ -0,0 +1,32 @@ +# vitest-runner v1.3.0 Changelog + +**Release Date**: July 2026 +**Release Type**: Minor + +--- + +## Overview + +This release onboards the repository onto the CLDMV v4 staging-branch release workflows (release PR [#3](https://github.com/CLDMV/vitest-runner/pull/3)). The work is almost entirely CI and release automation; no runtime code changed, and the library and CLI behave exactly as in v1.2.0. + +This version was tagged on `master` but never published to npm; its changes first reached npm in [v1.4.2](./v1.4.2.md). + +--- + +## 🔧 CI & tooling + +### v4 release workflows + +The single hand-written CI workflow was replaced by the CLDMV v4 set of thin caller workflows. `ci.yml` was rewritten to call the shared reusable CI (test matrix, coverage badge, PR coverage comment, required-check mirror). New workflows cover the release flow (`next-release.yml`, `hotfixes-release.yml`, `next-reset.yml`, `hotfix-redirector.yml`, `feature-pr.yml`, `pr-title-normalizer.yml`, `master-commit-audit.yml`, `update-major-version-tags.yml`, `v4-bootstrap.yml`), publishing (`publish.yml`) and security scanning (`codeql.yml`, `scorecard.yml`). + +### New npm scripts + +- `build:ci` runs `npm run lint && npm run format:check && npm run types:build`. +- `build` is a placeholder that only echoes a message. It exists so the reusable coverage-badge job, which runs `npm run build` by default, has something to call. + +--- + +## Upgrade notes + +- No breaking changes — drop-in for v1.2.0. +- No runtime code changed. Only `package.json` scripts and the version field differ from v1.2.0 among package files. diff --git a/docs/changelog/v1/v1.3.1.md b/docs/changelog/v1/v1.3.1.md new file mode 100644 index 0000000..e55868e --- /dev/null +++ b/docs/changelog/v1/v1.3.1.md @@ -0,0 +1,27 @@ +# vitest-runner v1.3.1 Changelog + +**Release Date**: August 2026 +**Release Type**: Patch + +--- + +## Overview + +A CI-only patch (release PR [#6](https://github.com/CLDMV/vitest-runner/pull/6)) that changes the concurrency policy of `ci.yml` so runs that matter for a release are never cancelled. No runtime code changed. + +This version was released on `master` but never published to npm; its changes first reached npm in [v1.4.2](./v1.4.2.md). + +--- + +## 🔧 CI & tooling + +### Release-relevant CI runs are no longer superseded + +Previously `ci.yml` cancelled in-progress runs on every branch except `master`/`main`. During a release, a burst of pushes to `next` or `hotfixes` (the feature merge, the sync merge, the bot's version bump) could cancel an earlier run and leave a red cancelled check on the release PR. The concurrency group is now unique per run (`run_id` appended) for pushes to the release base branch, `next` and `hotfixes`, and for release PRs whose head is `next` or `hotfixes`. Feature branches and feature PRs keep the previous cancel-superseded behaviour. The release base branch is taken from the `CLDMV_RELEASE_BASE` repository variable, falling back to the repository's default branch. + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.3.0](./v1.3.0.md). +- No runtime code changed. diff --git a/docs/changelog/v1/v1.3.2.md b/docs/changelog/v1/v1.3.2.md new file mode 100644 index 0000000..e532bc4 --- /dev/null +++ b/docs/changelog/v1/v1.3.2.md @@ -0,0 +1,27 @@ +# vitest-runner v1.3.2 Changelog + +**Release Date**: September 2026 +**Release Type**: Patch + +--- + +## Overview + +A CI-only patch (release PR [#12](https://github.com/CLDMV/vitest-runner/pull/12)) that passes the bot identity secrets through to the v4 release and feature-PR workflows. No runtime code changed. + +This version was tagged on `master` but never published to npm; its changes first reached npm in [v1.4.2](./v1.4.2.md). + +--- + +## 🔧 CI & tooling + +### Bot identity and signing secrets passed to the v4 workflows + +`feature-pr.yml`, `hotfixes-release.yml` and `next-release.yml` now map `CLDMV_BOT_NAME` and `CLDMV_BOT_EMAIL` to the `BOT_NAME` and `BOT_EMAIL` secrets the reusable workflows expect. `hotfix-redirector.yml` additionally maps `CLDMV_BOT_GPG_PRIVATE_KEY` and `CLDMV_BOT_GPG_PASSPHRASE` to `BOT_GPG_PRIVATE_KEY` and `BOT_GPG_PASSPHRASE`, so commits the redirector creates can be signed. + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.3.1](./v1.3.1.md). +- No runtime code changed. diff --git a/docs/changelog/v1/v1.3.3.md b/docs/changelog/v1/v1.3.3.md new file mode 100644 index 0000000..d07e36d --- /dev/null +++ b/docs/changelog/v1/v1.3.3.md @@ -0,0 +1,27 @@ +# vitest-runner v1.3.3 Changelog + +**Release Date**: September 2026 +**Release Type**: Patch + +--- + +## Overview + +A dependency-only patch (release PR [#19](https://github.com/CLDMV/vitest-runner/pull/19)) that rolls several Dependabot security updates for transitive development dependencies into one release. Only `package-lock.json` and the version field changed; no runtime code changed. + +This version was tagged on `master` but never published to npm: its publish job failed, an investigation described in [v1.4.0](./v1.4.0.md). Its changes first reached npm in [v1.4.2](./v1.4.2.md). + +--- + +## 🔧 Dependencies + +- `brace-expansion` 5.0.6 → 5.0.9 (dev, transitive) +- `nanoid` 3.3.12 → 3.3.19 (dev, transitive) +- `postcss` 8.5.15 → 8.5.28 (dev, transitive) + +--- + +## Upgrade notes + +- No breaking changes — drop-in for [v1.3.2](./v1.3.2.md). +- No runtime code changed. The updated packages are development-only and are not shipped to consumers. diff --git a/docs/changelog/v1/v1.5.2.md b/docs/changelog/v1/v1.5.2.md new file mode 100644 index 0000000..b10d4ad --- /dev/null +++ b/docs/changelog/v1/v1.5.2.md @@ -0,0 +1,36 @@ +# vitest-runner v1.5.2 Changelog + +**Release Date**: October 2026 +**Release Type**: Patch + +--- + +## Overview + +A maintenance release. The one runtime change is the `chalk` dependency moving from 5.x to 6.0.1; the runner's own code is unchanged apart from re-stamped file headers. The `✅ Required PR Check` mirror job in `ci.yml` was reworked twice so that an in-repo pull request can no longer be merged while its tests are still running, and the repository now maintains its file headers with the shared CLDMV fix-headers config instead of a local wrapper script. + +`chalk` 6 declares `engines.node` `>=22`, while this package's own `engines.node` still reads `>=20.19.0`. On Node.js 20.19+ npm prints an `EBADENGINE` warning for `chalk` when installing this version; CI tests from Node.js 22.12 upward, so Node.js 20 is not exercised. + +--- + +## 🔧 CI & tooling + +### The skipped PR-run mirror no longer satisfies `✅ Required PR Check` (#65, #68) + +An in-repo feature PR gets two `ci.yml` runs on the same commit: a `push` run on the head branch and a `pull_request` run. The `pull_request` run used to skip its `required-check` job, but a skipped job still posts a check run under its name and GitHub treats a skipped required check as passing, so the ruleset could read green before the push run's tests finished. #65 made the job's `name:` an expression so the skipped path no longer carries the required name. GitHub does not evaluate the name of a skipped job, though, so that path showed the raw expression text; #68 changed the job to always run. On the paths that own the status (push events, fork PRs, and the `next` and `hotfixes` release PRs) it reports `✅ Required PR Check` mirroring the test result; on the in-repo `pull_request` path it reports as `⏭️ Required PR Check (reported by the push run)` and passes as a no-op. Synced from the `CLDMV/.github` templates. + +### Shared CLDMV fix-headers config (#67) + +`npm run fix:headers` now runs the `fix-headers` CLI from `@cldmv/fix-headers` 2.x against a new `.configs/fix-headers.json`, which only extends `@cldmv/configs/fix-headers.json`. The local `tools/fix-headers.mjs` wrapper and `tools/lib/header-config.mjs` are removed. Every source, test, config, type and workflow file was re-stamped with the uniform header layout (ISO 8601 dates, framing lines, a uniform `@Author`); these are comment-only edits. + +## 🔧 Dependencies + +- `chalk` 5.6.2 → 6.0.1 (runtime; `package.json` range `^5.4.1` → `^6.0.1`; #62). `chalk` 6 requires Node.js 22 or later according to its `engines` field. +- `@types/node` 25.3.0 → 26.6.3 (dev; #63). +- `@cldmv/fix-headers` `^1.3.12` → `^2.1.2` and `@cldmv/configs` added (dev; #67). + +--- + +## Upgrade notes + +- No API or CLI change: drop-in for [v1.5.1](./v1.5.1.md) on Node.js 22.12 and later. On Node.js 20.19–22.11, expect an `EBADENGINE` warning for `chalk` on install; that combination is not covered by CI. From e69e9c7cc6c7034f5bf2b5ea997b07f577e947a6 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 16:55:48 -0700 Subject: [PATCH 4/7] docs: add the v1.5.3 changelog and restructure the README Adds docs/changelog/v1/v1.5.3.md for the pending release (#69: the CommonJS entry uses a plain require() and fails clearly on Node.js without require(esm)) and promotes v1.5.3 to the README What's New Latest block. The README follows the CLDMV layout: intro and tagline, badges, What's New, Key Features, Installation with Node.js requirements, Quick Start, then the existing CLI, API, coverage and layout reference, followed by Documentation, Contributing, Links and License. Install commands and code examples now use the scoped @cldmv/vitest-runner name, the Node.js requirement matches engines.node instead of claiming Node.js 18, and the source layout describes dist/index.cjs as the thin shim it is. --- README.md | 156 ++++++++++++++++++++++++++++-------- docs/changelog/v1/v1.5.3.md | 52 ++++++++++++ 2 files changed, 173 insertions(+), 35 deletions(-) create mode 100644 docs/changelog/v1/v1.5.3.md diff --git a/README.md b/README.md index 07e9dfa..140a54c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,10 @@ -# vitest-runner +# @cldmv/vitest-runner -Sequential Vitest runner that spawns each test file in its own child process to avoid out-of-memory crashes in large test suites. +**@cldmv/vitest-runner** is a sequential [Vitest](https://vitest.dev/) runner that spawns each test file in its own child process, so a large test suite never has to fit in one Vitest process's memory. It runs files one at a time or in a parallel worker pool, gives heavy files their own heap ceiling or a solo slot, and prints one combined summary of results, memory use and duration at the end. -- Runs files one-at-a-time or in a configurable parallel worker pool -- Supports full coverage mode via blob-per-file + `--mergeReports` (no OOM) -- Auto-detects your vitest config; accepts an explicit path if needed -- All standard Vitest CLI flags are forwarded unchanged -- Usable as a **CLI binary** or as a **programmatic Node.js API** -- Pure ESM source, bundled to a real CJS build for `require()` compatibility +Coverage stays out-of-memory-safe too: each file writes a coverage blob, and a single `vitest --mergeReports` step combines them into one report, with a worst-coverage table after it. The runner auto-detects your Vitest config, forwards every standard Vitest flag unchanged, and works as a `vitest-runner` CLI or as a programmatic `run()` API from ESM and CommonJS. + +> _Big suites, small processes: every test file gets its own Vitest, and the results come back as one run._ [![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] [![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] [![npm last update]][npm_last_update_url] [![Coverage]][coverage_url] @@ -17,43 +14,86 @@ Sequential Vitest runner that spawns each test file in its own child process to ## ✨ What's New -### Latest: v1.5.1 (September 2026) +### Latest: v1.5.3 (October 2026) -- **Backfilled the v1.5.0 changelog** — v1.5.0 (the `exclude` discovery option) shipped through the automated release gate before its changelog and this section had landed on `next`, so it released with a raw auto-generated notes dump. This release adds the curated v1.5.0 changelog and promotes this section; no code changed. -- [View full v1.5.1 Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.1.md) +- **A bundler-friendly CommonJS entry that fails clearly on old Node.js** — `dist/index.cjs` now loads the ES module build with a plain `require("./index.mjs")` instead of going through `createRequire`, so bundlers such as esbuild and webpack can see the dependency. On a Node.js version without synchronous `require(esm)` it throws an `ERR_REQUIRE_ESM` error that names the package, the versions `require()` needs (^20.19.0 or >=22.12.0) and the running version, and points at `import()` ([#69](https://github.com/CLDMV/vitest-runner/pull/69)). New `node:test` checks run the built CommonJS entry on every test and coverage run. +- [View full v1.5.3 Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.3.md) ### Recent Releases +- **v1.5.2** (October 2026) — `chalk` moves to 6.0.1, a skipped PR run can no longer satisfy `✅ Required PR Check`, and the repository adopts the shared CLDMV fix-headers config ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.2.md)) +- **v1.5.1** (September 2026) — Documentation-only release that backfilled the missing v1.5.0 changelog ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.1.md)) - **v1.5.0** (September 2026) — Added an `exclude` option (API + repeatable `--exclude ` CLI flag) so discovery can skip directories/files like `tmp/**` worktrees or `dist/**` build output ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.0.md)) - **v1.4.4** (September 2026) — Dropped the `prepack` tsup-availability workaround now that the underlying gap is fixed upstream, and re-armed the release-merge gate for every check-producing workflow ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.4.md)) -- **v1.4.3** (September 2026) — The published build is actually minified now, and `dist/index.cjs` is a thin shim instead of a duplicate bundle ([Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.4.3.md)) -- **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)) + +📚 For complete release notes, see the [docs/changelog/](https://github.com/CLDMV/vitest-runner/tree/master/docs/changelog/) folder. + +--- + +## 🚀 Key Features + +- Runs every test file in its own child process, one at a time or in a configurable parallel worker pool +- Solo slots for heavy files (`--solo-pattern`) and per-file heap ceilings (`perFileHeapOverrides`) +- Full coverage mode via blob-per-file + `--mergeReports` (no OOM), with a worst-coverage table +- Auto-detects your vitest config; accepts an explicit path if needed +- All standard Vitest CLI flags are forwarded unchanged +- Usable as a **CLI binary** or as a **programmatic Node.js API**, with an optional JSON run report +- A per-run scratch directory for every test file (`VITEST_RUNNER_TMP` / `makeRunTmpDir`), cleaned up on exit +- ES module build with a thin CommonJS entry for `require()` --- -## Requirements +## 📦 Installation + +### Requirements -- Node.js ≥ 18 +- **Node.js 20.19.0 or later** (`engines.node` is `>=20.19.0`). CI tests Node.js 22.12 and later, and the `chalk` 6 dependency declares Node.js 22 or later, so Node.js 22.12+ is the tested range. +- The package is an ES module and loads with `import`. `require("@cldmv/vitest-runner")` loads the ES module build synchronously, which needs Node.js ^20.19.0 or >=22.12.0; on older Node.js, use `import()` instead. - `vitest` ≥ 1.0 (peer dependency, installed in your project) - `chalk` (bundled dependency — no action needed) +### Install + +```sh +npm install --save-dev @cldmv/vitest-runner +``` + +Or to use the CLI globally: + +```sh +npm install -g @cldmv/vitest-runner +``` + --- -## Installation +## 🚀 Quick Start + +Run every discovered `*.test.vitest.{js,mjs,cjs}` file, each in its own process: ```sh -npm install --save-dev vitest-runner +npx vitest-runner ``` -Or to use the CLI globally: +Run with OOM-safe coverage and a live progress bar: ```sh -npm install -g vitest-runner +npx vitest-runner --coverage-quiet ``` +From code: + +```js +import { run } from "@cldmv/vitest-runner"; + +const code = await run({ testDir: "src/tests" }); +process.exit(code); +``` + +The full flag list is under [CLI usage](#-cli-usage) and every option under [Programmatic API](#-programmatic-api). + --- -## CLI usage +## 💻 CLI usage ```sh vitest-runner [OPTIONS] [PATTERNS...] @@ -166,21 +206,25 @@ vitest-runner --json --no-top-summary --- -## Programmatic API +## 🔧 Programmatic API ```js -import { run } from "vitest-runner"; +import { run } from "@cldmv/vitest-runner"; // CommonJS -const { run } = require("vitest-runner"); +const { run } = require("@cldmv/vitest-runner"); ``` +`require()` loads the ES module build synchronously through Node's `require(esm)`, which needs Node.js ^20.19.0 or >=22.12.0; on older Node.js it throws a clear `ERR_REQUIRE_ESM` error, and `import()` is the way to load the package there. + +When developing against a local checkout or workspace copy instead of the published package, run Node with `--conditions=vitest-runner-dev` so that `import` of the package resolves to its `src/` entry (`src/runner.mjs`) instead of the built `dist/`. + ### `run(options)` → `Promise` Runs the test suite and resolves with an exit code (`0` = all passed, `1` = any failure) by default. When `json: true` is passed, it returns a structured JSON report object (including `exitCode`) instead of printing runner text output. ```js -import { run } from "vitest-runner"; +import { run } from "@cldmv/vitest-runner"; const code = await run({ testDir: "src/tests" @@ -194,7 +238,7 @@ process.exit(code); | Option | Type | Default | Description | | ---------------------- | ----------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cwd` | `string` | `process.cwd()` | Absolute project root directory | -| `testDir` | `string` | `cwd` | Directory (absolute or relative to `cwd`) to scan for `*.test.vitest.{js,mjs}` files | +| `testDir` | `string` | `cwd` | Directory (absolute or relative to `cwd`) to scan for `*.test.vitest.{js,mjs,cjs}` files | | `vitestConfig` | `string` | auto-detect | Explicit vitest config path; when omitted the runner walks standard config names (`vitest.config.ts`, `vite.config.ts`, etc.) relative to `cwd` | | `testPatterns` | `string[]` | `[]` | File / folder patterns to filter — empty means all files in `testDir` | | `testListFile` | `string` | `undefined` | Path to a JSON array of test file paths; when set, scanning is skipped entirely | @@ -226,12 +270,12 @@ Every run gets its own scratch root (`/-/`), created From a test file, use `makeRunTmpDir(label)` to get a fresh, uniquely-named subdirectory instead of managing your own `mkdtemp` base: ```js -import { makeRunTmpDir } from "vitest-runner"; +import { makeRunTmpDir } from "@cldmv/vitest-runner"; const dir = makeRunTmpDir("my-fixture"); // a fresh directory under VITEST_RUNNER_TMP ``` -CLI flags: `--scratch-dir ` and `--keep-tmp` (see [Runner flags](#runner-flags) below). +CLI flags: `--scratch-dir ` and `--keep-tmp` (see [Runner flags](#runner-flags) above). #### `PerFileHeapOverride` @@ -287,7 +331,7 @@ await run({ --- -## Coverage mode +## 📊 Coverage mode When `--coverage` (or `coverageQuiet: true`) is passed, the runner uses a blob-per-file strategy: @@ -333,7 +377,7 @@ The exit code still reflects test pass/fail; there is just no coverage-merge res --- -## Test list files +## 📋 Test list files A test list file is a plain JSON array of test file paths (relative to `cwd`): @@ -345,7 +389,7 @@ Pass `--test-list ` (CLI) or `testListFile: 'path/to/list.json'` (API) to --- -## Test file naming +## 🔍 Test file naming By default, the runner discovers files matching: @@ -370,16 +414,17 @@ await run({ cwd, testDir: "src", testFilePattern: /\.spec\.ts$/i }); --- -## Source layout +## 📁 Source layout ```text dist/ ← built library entry (npm run build / tsup) — generated, not committed index.mjs ← bundled ESM entry - index.cjs ← bundled CJS entry (real sync require, generated from the same source) + index.cjs ← thin CJS entry, copied verbatim from src/cjs-shim.cjs; require()s index.mjs bin/ ← built CLI binary (npm run build / tsup) — generated, not committed vitest-runner.mjs ← bundled CLI, from src/bin/vitest-runner.mjs (shebang preserved) src/ runner.mjs ← main run() API + re-exports + cjs-shim.cjs ← CommonJS entry source (copied to dist/index.cjs by the build) bin/ vitest-runner.mjs ← CLI entry SOURCE — run this directly for source-level dev/testing utils/ @@ -399,13 +444,42 @@ src/ help.mjs ← showHelp ``` -`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. +`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. + +--- + +## 📚 Documentation + +- **[Changelog](https://github.com/CLDMV/vitest-runner/tree/master/docs/changelog/)** — release notes for every version +- **[Release notes on GitHub](https://github.com/CLDMV/vitest-runner/releases)** — the same notes attached to each release tag + +[![CodeFactor]][codefactor_url] [![OpenSSF Scorecard]][ossf_scorecard_url] [![npms.io score]][npms_url] [![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url] --- -## License +## 🤝 Contributing + +Bug reports and pull requests are welcome on [GitHub](https://github.com/CLDMV/vitest-runner/issues). Pull requests target the `next` branch; releases ship from `next` to `master`. + +[![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url] + +--- + +## 🔗 Links + +- **npm**: [@cldmv/vitest-runner](https://www.npmjs.com/package/@cldmv/vitest-runner) +- **GitHub**: [CLDMV/vitest-runner](https://github.com/CLDMV/vitest-runner) +- **Issues**: [GitHub Issues](https://github.com/CLDMV/vitest-runner/issues) +- **Changelog**: [docs/changelog/](https://github.com/CLDMV/vitest-runner/tree/master/docs/changelog/) +- **Releases**: [GitHub Releases](https://github.com/CLDMV/vitest-runner/releases) + +--- + +## 📄 License + +[![npm license]][npm_license_url] -MIT +MIT © Shinrai / CLDMV @@ -429,3 +503,15 @@ MIT [contributors_url]: https://github.com/CLDMV/vitest-runner/graphs/contributors [sponsor shinrai]: https://img.shields.io/github/sponsors/shinrai?style=for-the-badge&logo=githubsponsors&logoColor=white&labelColor=EA4AAA&label=Sponsor [sponsor_url]: https://github.com/sponsors/shinrai +[codefactor]: https://img.shields.io/codefactor/grade/github/CLDMV/vitest-runner?style=for-the-badge&logo=codefactor&logoColor=white&labelColor=F44A6A +[codefactor_url]: https://www.codefactor.io/repository/github/cldmv/vitest-runner +[openssf scorecard]: https://img.shields.io/ossf-scorecard/github.com/CLDMV/vitest-runner?style=for-the-badge&label=OpenSSF%20Scorecard +[ossf_scorecard_url]: https://scorecard.dev/viewer/?uri=github.com/CLDMV/vitest-runner +[npms.io score]: https://img.shields.io/npms-io/final-score/%40cldmv%2Fvitest-runner?style=for-the-badge&logo=npms&logoColor=white&labelColor=0B5D57 +[npms_url]: https://npms.io/search?q=%40cldmv%2Fvitest-runner +[npm unpacked size]: https://img.shields.io/npm/unpacked-size/%40cldmv%2Fvitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 +[npm_size_url]: https://www.npmjs.com/package/@cldmv/vitest-runner +[repo size]: https://img.shields.io/github/repo-size/CLDMV/vitest-runner?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 +[repo_size_url]: https://github.com/CLDMV/vitest-runner +[npm license]: https://img.shields.io/npm/l/%40cldmv%2Fvitest-runner.svg?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837 +[npm_license_url]: https://www.npmjs.com/package/@cldmv/vitest-runner diff --git a/docs/changelog/v1/v1.5.3.md b/docs/changelog/v1/v1.5.3.md new file mode 100644 index 0000000..e7a1de9 --- /dev/null +++ b/docs/changelog/v1/v1.5.3.md @@ -0,0 +1,52 @@ +# vitest-runner v1.5.3 Changelog + +**Release Date**: October 2026 +**Release Type**: Patch +**Branch**: `release/1.5.3` + +--- + +## Overview + +This release changes how `dist/index.cjs` loads the ES module build. The CommonJS entry no longer goes through `createRequire`, which kept bundlers from seeing the dependency, and it now fails with a clear, actionable error on a Node.js version that cannot `require()` an ES module. New `node:test` checks run the built CommonJS entry point on every test and coverage run. + +The ES module build, the CLI and the `run()` API are unchanged, and on Node.js versions with `require(esm)` (20.19+ and 22.12+), `require("@cldmv/vitest-runner")` returns the same named exports as before. + +--- + +## 🐛 Bug Fixes + +### The CommonJS entry uses a plain `require()` (#69) + +`src/cjs-shim.cjs`, which the build copies verbatim to `dist/index.cjs`, loaded `./index.mjs` through `createRequire(__filename)`. A `.cjs` file already has `require`, so `createRequire` added nothing, and it hid the dependency from bundlers such as esbuild and webpack, which detect dependencies from static `require("…")` calls. The shim now calls `require("./index.mjs")` directly and exports its result. + +### `require()` fails clearly on Node.js without `require(esm)` (#69) + +On a Node.js version without synchronous `require(esm)` (before 20.19 on the 20.x line, or before 22.12), the shim's `require()` threw a generic `ERR_REQUIRE_ESM` that pointed at the package's internals. The shim now checks `process.features.require_module` first and, when it is missing, throws an error with the same `ERR_REQUIRE_ESM` code and a message naming the package, the required versions and the running version: + +```text +@cldmv/vitest-runner: require() needs Node.js ^20.19.0 or >=22.12.0 (this is v20.18.0). On older Node.js, load the package with import() instead. +``` + +Code that catches `ERR_REQUIRE_ESM` by `code` keeps working. + +## 🧪 Tests + +- New `tests/cjs/entry.test.cjs`, run with `node --test` by a new `test:cjs` script after a fresh `npm run build`. It checks that `require()` of the built package returns the same named exports as `import`, and that the version check throws the new error when `require(esm)` is unavailable. +- `npm test`, `npm run test:coverage` and `npm run ci:coverage` now run `test:cjs` after Vitest, so CI exercises the published CommonJS entry point. + +## 📚 Documentation + +- **NEW:** [docs/changelog/v1/v1.5.3.md](./v1.5.3.md): this changelog. +- **NEW:** changelog files for every earlier release that shipped without one: [v1.0.0](./v1.0.0.md), [v1.0.1](./v1.0.1.md), [v1.0.2](./v1.0.2.md), [v1.0.3](./v1.0.3.md), [v1.1.0](./v1.1.0.md), [v1.2.0](./v1.2.0.md), [v1.3.0](./v1.3.0.md), [v1.3.1](./v1.3.1.md), [v1.3.2](./v1.3.2.md), [v1.3.3](./v1.3.3.md) and [v1.5.2](./v1.5.2.md). +- README restructured to the CLDMV layout (badges, **What's New**, Key Features, Installation with Node.js requirements, Quick Start, then the existing CLI, API and coverage reference). The install commands now use the scoped package name `@cldmv/vitest-runner`, and the Node.js requirement matches `engines.node`. + +## 🔧 Dependencies + +_No dependency updates._ + +--- + +## Upgrade notes + +- No breaking changes: drop-in for [v1.5.2](./v1.5.2.md). Only Node.js versions that `engines.node` already excludes see a different error, with a clearer message and the same `ERR_REQUIRE_ESM` code. From 9546830c04342498ee5d36c57244514559f6127d Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 17:14:50 -0700 Subject: [PATCH 5/7] chore: relicense under Apache-2.0 Replace the MIT license with the Apache License 2.0 in LICENSE, package.json, the README and the v1.5.3 changelog. --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++ README.md | 2 +- docs/changelog/v1/v1.5.3.md | 4 + package.json | 2 +- 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 140a54c..0d21654 100644 --- a/README.md +++ b/README.md @@ -479,7 +479,7 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/CLDMV/v [![npm license]][npm_license_url] -MIT © Shinrai / CLDMV +Apache-2.0 © Shinrai / CLDMV. See [LICENSE](https://github.com/CLDMV/vitest-runner/blob/master/LICENSE) for the full text. diff --git a/docs/changelog/v1/v1.5.3.md b/docs/changelog/v1/v1.5.3.md index e7a1de9..7a17cf6 100644 --- a/docs/changelog/v1/v1.5.3.md +++ b/docs/changelog/v1/v1.5.3.md @@ -35,6 +35,10 @@ Code that catches `ERR_REQUIRE_ESM` by `code` keeps working. - New `tests/cjs/entry.test.cjs`, run with `node --test` by a new `test:cjs` script after a fresh `npm run build`. It checks that `require()` of the built package returns the same named exports as `import`, and that the version check throws the new error when `require(esm)` is unavailable. - `npm test`, `npm run test:coverage` and `npm run ci:coverage` now run `test:cjs` after Vitest, so CI exercises the published CommonJS entry point. +## 📄 License + +- The package is relicensed from MIT to [Apache-2.0](https://github.com/CLDMV/vitest-runner/blob/master/LICENSE): `LICENSE` now carries the Apache License 2.0 text and `package.json` declares `"license": "Apache-2.0"`. + ## 📚 Documentation - **NEW:** [docs/changelog/v1/v1.5.3.md](./v1.5.3.md): this changelog. diff --git a/package.json b/package.json index b499f4f..46aa41c 100644 --- a/package.json +++ b/package.json @@ -84,5 +84,5 @@ "url": "https://github.com/CLDMV/vitest-runner/issues" }, "homepage": "https://github.com/CLDMV/vitest-runner#readme", - "license": "MIT" + "license": "Apache-2.0" } From 434a868989d54d76bc9c443d6f2b21d29cc50431 Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 17:19:41 -0700 Subject: [PATCH 6/7] fix: raise engines.node to >=22.12.0 The runtime dependency chalk 6 declares node >=22, the current vitest peer (5.x) needs ^22.12.0 || ^24 || >=26, and CI tests from 22.12.0, so the declared >=20.19.0 floor was never true: Node.js 20 installs warned with EBADENGINE and failed under engine-strict. Fixes #72 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ac8b6d..47385f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "vitest": "^5.0.1" }, "engines": { - "node": ">=20.19.0" + "node": ">=22.12.0" }, "peerDependencies": { "vitest": ">=1.0.0" diff --git a/package.json b/package.json index b499f4f..83f6b49 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "vitest": ">=1.0.0" }, "engines": { - "node": ">=20.19.0" + "node": ">=22.12.0" }, "publishConfig": { "access": "public" From 95279d0ab31c7874b6429dbd4f9f20288b87679b Mon Sep 17 00:00:00 2001 From: Shinrai Date: Sat, 3 Oct 2026 17:20:47 -0700 Subject: [PATCH 7/7] docs: note the Node.js 22.12 floor in the v1.5.3 notes and README #73 raises engines.node to >=22.12.0 (fixes #72). Record it under Breaking Changes with the upgrade path (stay on v1.5.1 for Node.js 20), and update the README Requirements and the What's New Latest block. --- README.md | 3 ++- docs/changelog/v1/v1.5.3.md | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0d21654..4491963 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Coverage stays out-of-memory-safe too: each file writes a coverage blob, and a s ### Latest: v1.5.3 (October 2026) - **A bundler-friendly CommonJS entry that fails clearly on old Node.js** — `dist/index.cjs` now loads the ES module build with a plain `require("./index.mjs")` instead of going through `createRequire`, so bundlers such as esbuild and webpack can see the dependency. On a Node.js version without synchronous `require(esm)` it throws an `ERR_REQUIRE_ESM` error that names the package, the versions `require()` needs (^20.19.0 or >=22.12.0) and the running version, and points at `import()` ([#69](https://github.com/CLDMV/vitest-runner/pull/69)). New `node:test` checks run the built CommonJS entry on every test and coverage run. +- **Node.js 22.12 or later** — `engines.node` moves from `>=20.19.0` to `>=22.12.0` ([#73](https://github.com/CLDMV/vitest-runner/pull/73)). The `chalk` 6 dependency and the `vitest` 5 peer already needed it, so Node.js 20 had stopped installing cleanly in v1.5.2; the declared floor now matches. - [View full v1.5.3 Changelog](https://github.com/CLDMV/vitest-runner/blob/master/docs/changelog/v1/v1.5.3.md) ### Recent Releases @@ -47,7 +48,7 @@ Coverage stays out-of-memory-safe too: each file writes a coverage blob, and a s ### Requirements -- **Node.js 20.19.0 or later** (`engines.node` is `>=20.19.0`). CI tests Node.js 22.12 and later, and the `chalk` 6 dependency declares Node.js 22 or later, so Node.js 22.12+ is the tested range. +- **Node.js 22.12.0 or later** (`engines.node` is `>=22.12.0`, matching the `chalk` 6 dependency, the `vitest` 5 peer and the CI matrix). Node.js 20 is not supported from v1.5.3; stay on v1.5.1 there. - The package is an ES module and loads with `import`. `require("@cldmv/vitest-runner")` loads the ES module build synchronously, which needs Node.js ^20.19.0 or >=22.12.0; on older Node.js, use `import()` instead. - `vitest` ≥ 1.0 (peer dependency, installed in your project) - `chalk` (bundled dependency — no action needed) diff --git a/docs/changelog/v1/v1.5.3.md b/docs/changelog/v1/v1.5.3.md index 7a17cf6..ed54839 100644 --- a/docs/changelog/v1/v1.5.3.md +++ b/docs/changelog/v1/v1.5.3.md @@ -12,8 +12,16 @@ This release changes how `dist/index.cjs` loads the ES module build. The CommonJ The ES module build, the CLI and the `run()` API are unchanged, and on Node.js versions with `require(esm)` (20.19+ and 22.12+), `require("@cldmv/vitest-runner")` returns the same named exports as before. +It also raises `engines.node` to `>=22.12.0` ([#73](https://github.com/CLDMV/vitest-runner/pull/73)). The old `>=20.19.0` floor was never true: the runtime dependency `chalk` 6 needs Node.js 22, the current `vitest` 5 peer needs `^22.12.0`, and CI only ever tested 22.12 and later. + --- +## 💥 Breaking Changes + +### Node.js 20 is no longer supported ([#73](https://github.com/CLDMV/vitest-runner/pull/73), fixes [#72](https://github.com/CLDMV/vitest-runner/issues/72)) + +`engines.node` moves from `>=20.19.0` to `>=22.12.0`, despite this being a patch release. In practice Node.js 20 already stopped working cleanly in v1.5.2: `chalk` 6.0.1 declares Node.js 22 or later, so Node.js 20 installs printed an `EBADENGINE` warning and failed outright under `engine-strict`, and `vitest` 5 does not install on Node.js 20 at all. The floor now says what the package actually needs. + ## 🐛 Bug Fixes ### The CommonJS entry uses a plain `require()` (#69) @@ -53,4 +61,5 @@ _No dependency updates._ ## Upgrade notes -- No breaking changes: drop-in for [v1.5.2](./v1.5.2.md). Only Node.js versions that `engines.node` already excludes see a different error, with a clearer message and the same `ERR_REQUIRE_ESM` code. +- **Node.js 22.12 or later is required.** On Node.js 20, stay on v1.5.1 (the last release before `chalk` 6), or upgrade Node.js. Everyone already on Node.js 22.12+ gets a drop-in update from [v1.5.2](./v1.5.2.md). +- On a Node.js version without `require(esm)`, `require()` now fails with a clearer message and the same `ERR_REQUIRE_ESM` code.