Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 18 additions & 5 deletions src/cjs-shim.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Nate Corcoran <CLDMV> (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.
*
Expand All @@ -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
Expand All @@ -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");
54 changes: 54 additions & 0 deletions tests/cjs/entry.test.cjs
Original file line number Diff line number Diff line change
@@ -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 <CLDMV>
* @Email: <Shinrai@users.noreply.github.com>
* -----
* @Last modified by: Nate Corcoran <CLDMV> (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\(\)/);
});
Loading