@williamthorsen/nmr 2.0.1 → 2.0.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/.meta/changelog.json +90 -0
- package/.readyup/kits/default.js +41 -5
- package/.readyup/manifest.json +14 -9
- package/CHANGELOG.md +24 -0
- package/README.md +9 -3
- package/dist/esm/{check-cache-build-output.d.ts → check-cache/check-cache-build-output.d.ts} +1 -1
- package/dist/esm/{check-cache-build-output.js → check-cache/check-cache-build-output.js} +6 -6
- package/dist/esm/{check-cache-delegation.d.ts → check-cache/check-cache-delegation.d.ts} +3 -3
- package/dist/esm/{check-cache-delegation.js → check-cache/check-cache-delegation.js} +2 -2
- package/dist/esm/{check-cache-entries.js → check-cache/check-cache-entries.js} +1 -1
- package/dist/esm/{check-cache-gate.d.ts → check-cache/check-cache-gate.d.ts} +5 -4
- package/dist/esm/{check-cache-gate.js → check-cache/check-cache-gate.js} +10 -10
- package/dist/esm/{check-cache-key.d.ts → check-cache/check-cache-key.d.ts} +1 -1
- package/dist/esm/{check-cache-record.d.ts → check-cache/check-cache-record.d.ts} +6 -5
- package/dist/esm/{check-cache-record.js → check-cache/check-cache-record.js} +7 -6
- package/dist/esm/{check-cache.d.ts → check-cache/check-cache.d.ts} +10 -2
- package/dist/esm/{check-cache.js → check-cache/check-cache.js} +7 -4
- package/dist/esm/{config-check-cache.d.ts → check-cache/config-check-cache.d.ts} +1 -1
- package/dist/esm/{config-check-cache.js → check-cache/config-check-cache.js} +4 -4
- package/dist/esm/{pass-manifest.d.ts → check-cache/pass-manifest.d.ts} +1 -1
- package/dist/esm/{pass-manifest.js → check-cache/pass-manifest.js} +1 -1
- package/dist/esm/{recording.js → check-cache/recording.js} +2 -2
- package/dist/esm/{replay-assembly.d.ts → check-cache/replay-assembly.d.ts} +1 -1
- package/dist/esm/{replay-assembly.js → check-cache/replay-assembly.js} +2 -2
- package/dist/esm/child-env.d.ts +1 -1
- package/dist/esm/child-env.js +2 -2
- package/dist/esm/commands/actionlint.d.ts +2 -7
- package/dist/esm/commands/actionlint.js +5 -11
- package/dist/esm/commands/clean.js +1 -1
- package/dist/esm/commands/fmt-cache.js +3 -2
- package/dist/esm/commands/fmt.d.ts +6 -13
- package/dist/esm/commands/fmt.js +47 -30
- package/dist/esm/commands/node-pin.d.ts +15 -5
- package/dist/esm/commands/node-pin.js +43 -10
- package/dist/esm/commands/package-manifest.d.ts +28 -0
- package/dist/esm/commands/package-manifest.js +32 -0
- package/dist/esm/commands/run-vitest.js +21 -20
- package/dist/esm/commands/shellcheck.d.ts +2 -7
- package/dist/esm/commands/shellcheck.js +3 -11
- package/dist/esm/commands/spawn-inherited.d.ts +21 -0
- package/dist/esm/commands/spawn-inherited.js +11 -0
- package/dist/esm/commands/taze.d.ts +6 -6
- package/dist/esm/commands/taze.js +3 -7
- package/dist/esm/config-validation.js +1 -1
- package/dist/esm/config.js +3 -12
- package/dist/esm/{delegate-host.d.ts → delegation/delegate-host.d.ts} +3 -3
- package/dist/esm/{delegate-host.js → delegation/delegate-host.js} +9 -8
- package/dist/esm/{delegation-target.js → delegation/delegation-target.js} +1 -1
- package/dist/esm/{remote-probe.js → delegation/remote-probe.js} +2 -2
- package/dist/esm/{remote-run.js → delegation/remote-run.js} +5 -5
- package/dist/esm/{remote-session.js → delegation/remote-session.js} +1 -1
- package/dist/esm/{user-config.js → delegation/user-config.js} +4 -13
- package/dist/esm/env-switch.d.ts +25 -0
- package/dist/esm/env-switch.js +26 -0
- package/dist/esm/fan-out.d.ts +4 -1
- package/dist/esm/fan-out.js +1 -3
- package/dist/esm/help.js +1 -1
- package/dist/esm/helpers/filter-selection.d.ts +2 -0
- package/dist/esm/helpers/importConfigDefault.d.ts +7 -0
- package/dist/esm/helpers/importConfigDefault.js +45 -0
- package/dist/esm/invocation.d.ts +7 -3
- package/dist/esm/machine-lock.js +2 -1
- package/dist/esm/route.d.ts +6 -0
- package/dist/esm/route.js +15 -2
- package/dist/esm/run-chain.d.ts +1 -1
- package/dist/esm/run-chain.js +1 -1
- package/dist/esm/runCli.d.ts +9 -0
- package/dist/esm/runCli.js +24 -9
- package/dist/esm/runner.d.ts +2 -0
- package/dist/esm/runner.js +1 -1
- package/dist/esm/verdict.d.ts +1 -1
- package/docs/check-cache.md +7 -7
- package/docs/delegate.md +2 -2
- package/docs/machine-lock.md +4 -4
- package/docs/scripts.md +4 -0
- package/docs/upgrades.md +9 -1
- package/docs/utilities.md +4 -2
- package/package.json +1 -1
- /package/dist/esm/{check-cache-entries.d.ts → check-cache/check-cache-entries.d.ts} +0 -0
- /package/dist/esm/{check-cache-key.js → check-cache/check-cache-key.js} +0 -0
- /package/dist/esm/{recording.d.ts → check-cache/recording.d.ts} +0 -0
- /package/dist/esm/{delegation-target.d.ts → delegation/delegation-target.d.ts} +0 -0
- /package/dist/esm/{remote-host-session.d.ts → delegation/remote-host-session.d.ts} +0 -0
- /package/dist/esm/{remote-host-session.js → delegation/remote-host-session.js} +0 -0
- /package/dist/esm/{remote-probe-host.d.ts → delegation/remote-probe-host.d.ts} +0 -0
- /package/dist/esm/{remote-probe-host.js → delegation/remote-probe-host.js} +0 -0
- /package/dist/esm/{remote-probe.d.ts → delegation/remote-probe.d.ts} +0 -0
- /package/dist/esm/{remote-run.d.ts → delegation/remote-run.d.ts} +0 -0
- /package/dist/esm/{remote-session.d.ts → delegation/remote-session.d.ts} +0 -0
- /package/dist/esm/{remote-snapshot.d.ts → delegation/remote-snapshot.d.ts} +0 -0
- /package/dist/esm/{remote-snapshot.js → delegation/remote-snapshot.js} +0 -0
- /package/dist/esm/{user-config.d.ts → delegation/user-config.d.ts} +0 -0
package/.meta/changelog.json
CHANGED
|
@@ -1,4 +1,94 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"date": "2026-11-12",
|
|
5
|
+
"sections": [
|
|
6
|
+
{
|
|
7
|
+
"title": "🐛 Bug fixes",
|
|
8
|
+
"audience": "all",
|
|
9
|
+
"items": [
|
|
10
|
+
{
|
|
11
|
+
"description": "Fixes the issue that `nmr-fmt` and `nmr-taze` exited with 1 when a signal ended Prettier or taze, and makes each exit with 128 plus the signal number, as the other commands that nmr runs already did. (#1281)",
|
|
12
|
+
"hash": "9999694c843db32f1fc2851617402b61794abd3d",
|
|
13
|
+
"entry": 1
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"description": "Fixes the issue that `nmr-fmt` formatted with nmr's own copy of Prettier when the repository's copy had a malformed or unreadable manifest, and makes `nmr-fmt` fail with an error that names that manifest instead. (#1282)",
|
|
17
|
+
"hash": "73319582559a85130ba65e810483acb24954e0ac",
|
|
18
|
+
"entry": 1
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"description": "Fixes the issue that `nmr-vitest` reported a Vitest whose manifest is malformed or unreadable as not installed, and makes `nmr-vitest` name that manifest and the repair instead. (#1282)",
|
|
22
|
+
"hash": "73319582559a85130ba65e810483acb24954e0ac",
|
|
23
|
+
"entry": 2
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "Fixes the issue that `nmr-fmt` and `nmr-vitest` crashed on a Prettier or Vitest manifest that starts with a byte-order mark, which Node accepts. (#1282)",
|
|
27
|
+
"hash": "73319582559a85130ba65e810483acb24954e0ac",
|
|
28
|
+
"entry": 3
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"description": "Fixes the issue that a `.config/nmr.config.ts` or `~/.config/nmr/config.ts` that failed to parse or threw while loading crashed `nmr` with an internal error that did not name the file, and makes `nmr` report an error that names the config and, for a syntax error, the file and line that Node reports, including those of a module that the config imports. (#1296)",
|
|
32
|
+
"hash": "3d120c5ce21b5cae276030dee7db1b63514fbe29",
|
|
33
|
+
"entry": 1
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"description": "Fixes the issue that `nmr` read any value of `NMR_DEBUG`, `NMR_NO_CACHE`, `NMR_NO_LOCK`, or `NMR_RUN_IF_PRESENT` other than `1`, such as `true`, as off without reporting it, by making `nmr` and `nmr-fmt` exit 2 with a message that names the variable and the values that it accepts. (#1297)",
|
|
37
|
+
"migration": "Set each of these variables to `1` to turn it on, and to `0` or an empty value, or leave it unset, to turn it off. Any other value now stops the run with exit 2 before a command runs.",
|
|
38
|
+
"hash": "3a396f8788b37d60546eebf5b4c33859064314c6",
|
|
39
|
+
"entry": 1
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"description": "Fixes the issue that `nmr upgrade --write` rewrote the `nodejs` pin in `.tool-versions` to a release that asdf could not install yet, by limiting the check to releases that node-build defines and reporting a newer release without a definition as held back. (#1300)",
|
|
43
|
+
"hash": "d97eab803118bb0055b9acd781f928c4ec548a55",
|
|
44
|
+
"entry": 1
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "♻️ Refactoring",
|
|
50
|
+
"audience": "dev",
|
|
51
|
+
"items": [
|
|
52
|
+
{
|
|
53
|
+
"description": "Replaces the copies of the inherited-stdio spawn and exit-code mapping in `nmr-actionlint`, `nmr-shellcheck`, and `nmr-vitest` with the shared `spawnInherited` and `resolveInheritedExitCode` in `spawn-inherited.ts`, so that the copies can no longer change independently. (#1281)",
|
|
54
|
+
"hash": "9999694c843db32f1fc2851617402b61794abd3d",
|
|
55
|
+
"entry": 2
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"description": "Replaces the separate `bin` parsing in `resolveVitestCli` and `resolvePrettierCli` with one `resolveDeclaredCli` in `package-manifest.ts`, which takes `bin` either as a bare path or as a map keyed by the package name, so `nmr-vitest` now also accepts a Vitest manifest whose `bin` is a bare path. (#1283)",
|
|
59
|
+
"hash": "31ebafea1ceb209f1f8993520f90f10cb87bf88d",
|
|
60
|
+
"entry": 1
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "Groups nmr's check-cache modules under `packages/nmr/src/check-cache/` and its delegation modules under `packages/nmr/src/delegation/`, each directory containing its own tests in `__tests__/`. (#1293)",
|
|
64
|
+
"hash": "91f900f679d4e84687e1ba74ece7d42bf089abc3",
|
|
65
|
+
"entry": 1
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"description": "Makes `runCli` accept `runSteps` and `readFilterSelection` as optional dependencies that default to the real functions, and passes them on the `Invocation` to fan-out and the check-cache gate, which no longer import the real functions. (#1295)",
|
|
69
|
+
"hash": "40098138b5136fdb4c6824d03e1873ab15583693",
|
|
70
|
+
"entry": 1
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"title": "🧪 Tests",
|
|
76
|
+
"audience": "dev",
|
|
77
|
+
"items": [
|
|
78
|
+
{
|
|
79
|
+
"description": "Adds `createRunCliHarness` and a shared `it` in `test-utils/run-cli-fixtures.ts` that gives each test a workspace `tree` and fresh fakes, and moves the five `runCli` unit suites onto them, so that the suites no longer depend on the file layout of the modules that they used to mock. (#1295)",
|
|
80
|
+
"hash": "40098138b5136fdb4c6824d03e1873ab15583693",
|
|
81
|
+
"entry": 2
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"description": "Covers each decision branch of the check-cache gate with unit suites for lookup, recording, scope, and the command's own stated outcome, which inject a fake tree reader through a new `readTree` option on `runCli`, and removes the tool cases that they supersede, including `check-cache-gate.build-output.tool.test.ts`. (#1298)",
|
|
85
|
+
"hash": "2b3163471bc0adf4b70b8fe5b386e341241aaaae",
|
|
86
|
+
"entry": 1
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
2
92
|
{
|
|
3
93
|
"version": "2.0.1",
|
|
4
94
|
"date": "2026-11-11",
|
package/.readyup/kits/default.js
CHANGED
|
@@ -8019,7 +8019,6 @@ import { isRecord as isRecord5 } from "readyup/check-utils";
|
|
|
8019
8019
|
|
|
8020
8020
|
// src/config.ts
|
|
8021
8021
|
import { existsSync as existsSync3 } from "node:fs";
|
|
8022
|
-
import { pathToFileURL } from "node:url";
|
|
8023
8022
|
|
|
8024
8023
|
// src/helpers/findClosestName.ts
|
|
8025
8024
|
function findClosestName(name, candidates) {
|
|
@@ -8673,7 +8672,7 @@ function buildRootRegistry(config, monorepoRoot) {
|
|
|
8673
8672
|
};
|
|
8674
8673
|
}
|
|
8675
8674
|
|
|
8676
|
-
// src/config-check-cache.ts
|
|
8675
|
+
// src/check-cache/config-check-cache.ts
|
|
8677
8676
|
function assertResolvableCheckCacheCommands(config, configPath, baseDir) {
|
|
8678
8677
|
const { checkCache } = config;
|
|
8679
8678
|
if (checkCache === void 0 || !isMonorepoRoot(baseDir)) {
|
|
@@ -8968,6 +8967,44 @@ function escapeControlCharacters(text) {
|
|
|
8968
8967
|
return escapedText;
|
|
8969
8968
|
}
|
|
8970
8969
|
|
|
8970
|
+
// src/helpers/importConfigDefault.ts
|
|
8971
|
+
import { realpathSync } from "node:fs";
|
|
8972
|
+
import { pathToFileURL } from "node:url";
|
|
8973
|
+
async function importConfigDefault(configPath) {
|
|
8974
|
+
let importedModule;
|
|
8975
|
+
try {
|
|
8976
|
+
importedModule = await import(pathToFileURL(configPath).href);
|
|
8977
|
+
} catch (error) {
|
|
8978
|
+
const location = readSyntaxErrorLocation(error);
|
|
8979
|
+
const isInConfig = location !== void 0 && isConfigFile(location.file, configPath);
|
|
8980
|
+
const configLocation = isInConfig ? `${configPath}:${location.line}` : configPath;
|
|
8981
|
+
const foreignLocation = location === void 0 || isInConfig ? "" : `${location.file}:${location.line}: `;
|
|
8982
|
+
throw new UserError(
|
|
8983
|
+
`Cannot load nmr config at ${configLocation}: ${foreignLocation}${describeError(error)}. Fix the file and rerun.`,
|
|
8984
|
+
{ cause: error }
|
|
8985
|
+
);
|
|
8986
|
+
}
|
|
8987
|
+
return isRecord2(importedModule) ? importedModule["default"] : void 0;
|
|
8988
|
+
}
|
|
8989
|
+
function isConfigFile(file, configPath) {
|
|
8990
|
+
return file === configPath || file === resolveRealPath(configPath);
|
|
8991
|
+
}
|
|
8992
|
+
function readSyntaxErrorLocation(error) {
|
|
8993
|
+
if (!isError(error) || error.stack === void 0) {
|
|
8994
|
+
return void 0;
|
|
8995
|
+
}
|
|
8996
|
+
const match = /^(?<file>.+):(?<line>\d+)$/u.exec(error.stack.split("\n", 1)[0] ?? "");
|
|
8997
|
+
const { file, line } = match?.groups ?? {};
|
|
8998
|
+
return file === void 0 || line === void 0 ? void 0 : { file, line };
|
|
8999
|
+
}
|
|
9000
|
+
function resolveRealPath(filePath) {
|
|
9001
|
+
try {
|
|
9002
|
+
return realpathSync(filePath);
|
|
9003
|
+
} catch {
|
|
9004
|
+
return void 0;
|
|
9005
|
+
}
|
|
9006
|
+
}
|
|
9007
|
+
|
|
8971
9008
|
// src/config.ts
|
|
8972
9009
|
var SCRIPT_FIELDS = [
|
|
8973
9010
|
{ field: "rootScripts", scope: "root" },
|
|
@@ -8978,8 +9015,7 @@ async function loadConfig(baseDir) {
|
|
|
8978
9015
|
if (!existsSync3(configPath)) {
|
|
8979
9016
|
return {};
|
|
8980
9017
|
}
|
|
8981
|
-
const
|
|
8982
|
-
const loadedConfig = isRecord2(importedModule) ? importedModule["default"] : void 0;
|
|
9018
|
+
const loadedConfig = await importConfigDefault(configPath);
|
|
8983
9019
|
return validateConfig(loadedConfig, configPath, baseDir);
|
|
8984
9020
|
}
|
|
8985
9021
|
async function loadRootConfig(monorepoRoot) {
|
|
@@ -9595,7 +9631,7 @@ function hasSupportedStrictLintVersion() {
|
|
|
9595
9631
|
});
|
|
9596
9632
|
}
|
|
9597
9633
|
function getMinVersion() {
|
|
9598
|
-
const pickedFields = { "version": "2.0.
|
|
9634
|
+
const pickedFields = { "version": "2.0.2" };
|
|
9599
9635
|
if (typeof pickedFields["version"] !== "string") {
|
|
9600
9636
|
throw new TypeError("nmr/package.json: 'version' must be a string");
|
|
9601
9637
|
}
|
package/.readyup/manifest.json
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"checklist": "nmr",
|
|
33
33
|
"checks": [
|
|
34
34
|
"@williamthorsen/nmr in devDependencies",
|
|
35
|
-
"@williamthorsen/nmr >= 2.0.
|
|
35
|
+
"@williamthorsen/nmr >= 2.0.2",
|
|
36
36
|
"pnpm-workspace.yaml exists",
|
|
37
37
|
"catalogued dependencies use catalog:",
|
|
38
38
|
"package.json has packageManager field",
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
"path": "lib/workspace-build.ts"
|
|
237
237
|
},
|
|
238
238
|
{
|
|
239
|
-
"hash": "
|
|
239
|
+
"hash": "cf90a835",
|
|
240
240
|
"kind": "inline",
|
|
241
241
|
"path": "../package.json",
|
|
242
242
|
"paths": [
|
|
@@ -244,22 +244,22 @@
|
|
|
244
244
|
]
|
|
245
245
|
},
|
|
246
246
|
{
|
|
247
|
-
"hash": "
|
|
247
|
+
"hash": "a34d9027",
|
|
248
248
|
"kind": "module",
|
|
249
|
-
"path": "../src/
|
|
249
|
+
"path": "../src/check-cache/config-check-cache.ts"
|
|
250
250
|
},
|
|
251
251
|
{
|
|
252
|
-
"hash": "
|
|
252
|
+
"hash": "eb0306ed",
|
|
253
253
|
"kind": "module",
|
|
254
|
-
"path": "../src/
|
|
254
|
+
"path": "../src/commands/ensure-prepublish-hooks.ts"
|
|
255
255
|
},
|
|
256
256
|
{
|
|
257
|
-
"hash": "
|
|
257
|
+
"hash": "0555552a",
|
|
258
258
|
"kind": "module",
|
|
259
259
|
"path": "../src/config-validation.ts"
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
|
-
"hash": "
|
|
262
|
+
"hash": "f5e9e2fd",
|
|
263
263
|
"kind": "module",
|
|
264
264
|
"path": "../src/config.ts"
|
|
265
265
|
},
|
|
@@ -293,6 +293,11 @@
|
|
|
293
293
|
"kind": "module",
|
|
294
294
|
"path": "../src/helpers/hook-name.ts"
|
|
295
295
|
},
|
|
296
|
+
{
|
|
297
|
+
"hash": "d6455b2e",
|
|
298
|
+
"kind": "module",
|
|
299
|
+
"path": "../src/helpers/importConfigDefault.ts"
|
|
300
|
+
},
|
|
296
301
|
{
|
|
297
302
|
"hash": "62caa37e",
|
|
298
303
|
"kind": "module",
|
|
@@ -359,7 +364,7 @@
|
|
|
359
364
|
"readyupVersion": "0.45.1",
|
|
360
365
|
"source": "kits/default.ts",
|
|
361
366
|
"sourceHash": "9fd61bf4",
|
|
362
|
-
"targetHash": "
|
|
367
|
+
"targetHash": "9895b0df"
|
|
363
368
|
}
|
|
364
369
|
]
|
|
365
370
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 2.0.2 — 2026-11-12
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug fixes
|
|
8
|
+
|
|
9
|
+
- Fixes the issue that `nmr-fmt` and `nmr-taze` exited with 1 when a signal ended Prettier or taze, and makes each exit with 128 plus the signal number, as the other commands that nmr runs already did. (#1281)
|
|
10
|
+
- Fixes the issue that `nmr-fmt` formatted with nmr's own copy of Prettier when the repository's copy had a malformed or unreadable manifest, and makes `nmr-fmt` fail with an error that names that manifest instead. (#1282)
|
|
11
|
+
- Fixes the issue that `nmr-vitest` reported a Vitest whose manifest is malformed or unreadable as not installed, and makes `nmr-vitest` name that manifest and the repair instead. (#1282)
|
|
12
|
+
- Fixes the issue that `nmr-fmt` and `nmr-vitest` crashed on a Prettier or Vitest manifest that starts with a byte-order mark, which Node accepts. (#1282)
|
|
13
|
+
- Fixes the issue that a `.config/nmr.config.ts` or `~/.config/nmr/config.ts` that failed to parse or threw while loading crashed `nmr` with an internal error that did not name the file, and makes `nmr` report an error that names the config and, for a syntax error, the file and line that Node reports, including those of a module that the config imports. (#1296)
|
|
14
|
+
- Fixes the issue that `nmr` read any value of `NMR_DEBUG`, `NMR_NO_CACHE`, `NMR_NO_LOCK`, or `NMR_RUN_IF_PRESENT` other than `1`, such as `true`, as off without reporting it, by making `nmr` and `nmr-fmt` exit 2 with a message that names the variable and the values that it accepts. (#1297)
|
|
15
|
+
- Fixes the issue that `nmr upgrade --write` rewrote the `nodejs` pin in `.tool-versions` to a release that asdf could not install yet, by limiting the check to releases that node-build defines and reporting a newer release without a definition as held back. (#1300)
|
|
16
|
+
|
|
17
|
+
### ♻️ Refactoring
|
|
18
|
+
|
|
19
|
+
- Replaces the copies of the inherited-stdio spawn and exit-code mapping in `nmr-actionlint`, `nmr-shellcheck`, and `nmr-vitest` with the shared `spawnInherited` and `resolveInheritedExitCode` in `spawn-inherited.ts`, so that the copies can no longer change independently. (#1281)
|
|
20
|
+
- Replaces the separate `bin` parsing in `resolveVitestCli` and `resolvePrettierCli` with one `resolveDeclaredCli` in `package-manifest.ts`, which takes `bin` either as a bare path or as a map keyed by the package name, so `nmr-vitest` now also accepts a Vitest manifest whose `bin` is a bare path. (#1283)
|
|
21
|
+
- Groups nmr's check-cache modules under `packages/nmr/src/check-cache/` and its delegation modules under `packages/nmr/src/delegation/`, each directory containing its own tests in `__tests__/`. (#1293)
|
|
22
|
+
- Makes `runCli` accept `runSteps` and `readFilterSelection` as optional dependencies that default to the real functions, and passes them on the `Invocation` to fan-out and the check-cache gate, which no longer import the real functions. (#1295)
|
|
23
|
+
|
|
24
|
+
### 🧪 Tests
|
|
25
|
+
|
|
26
|
+
- Adds `createRunCliHarness` and a shared `it` in `test-utils/run-cli-fixtures.ts` that gives each test a workspace `tree` and fresh fakes, and moves the five `runCli` unit suites onto them, so that the suites no longer depend on the file layout of the modules that they used to mock. (#1295)
|
|
27
|
+
- Covers each decision branch of the check-cache gate with unit suites for lookup, recording, scope, and the command's own stated outcome, which inject a fake tree reader through a new `readTree` option on `runCli`, and removes the tool cases that they supersede, including `check-cache-gate.build-output.tool.test.ts`. (#1298)
|
|
28
|
+
|
|
5
29
|
## 2.0.1 — 2026-11-11
|
|
6
30
|
|
|
7
31
|
### 🐛 Bug fixes
|
package/README.md
CHANGED
|
@@ -5,11 +5,17 @@
|
|
|
5
5
|
Context-aware script runner for pnpm monorepos. Includes an `nmr` (node-monorepo run) binary that provides centralized, consistent script execution across workspace packages and the monorepo root.
|
|
6
6
|
|
|
7
7
|
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v2.0.
|
|
8
|
+
## Release notes — v2.0.2 (2026-11-12)
|
|
9
9
|
|
|
10
10
|
### 🐛 Bug fixes
|
|
11
11
|
|
|
12
|
-
- Fixes the issue that `nmr-
|
|
12
|
+
- Fixes the issue that `nmr-fmt` and `nmr-taze` exited with 1 when a signal ended Prettier or taze, and makes each exit with 128 plus the signal number, as the other commands that nmr runs already did. (#1281)
|
|
13
|
+
- Fixes the issue that `nmr-fmt` formatted with nmr's own copy of Prettier when the repository's copy had a malformed or unreadable manifest, and makes `nmr-fmt` fail with an error that names that manifest instead. (#1282)
|
|
14
|
+
- Fixes the issue that `nmr-vitest` reported a Vitest whose manifest is malformed or unreadable as not installed, and makes `nmr-vitest` name that manifest and the repair instead. (#1282)
|
|
15
|
+
- Fixes the issue that `nmr-fmt` and `nmr-vitest` crashed on a Prettier or Vitest manifest that starts with a byte-order mark, which Node accepts. (#1282)
|
|
16
|
+
- Fixes the issue that a `.config/nmr.config.ts` or `~/.config/nmr/config.ts` that failed to parse or threw while loading crashed `nmr` with an internal error that did not name the file, and makes `nmr` report an error that names the config and, for a syntax error, the file and line that Node reports, including those of a module that the config imports. (#1296)
|
|
17
|
+
- Fixes the issue that `nmr` read any value of `NMR_DEBUG`, `NMR_NO_CACHE`, `NMR_NO_LOCK`, or `NMR_RUN_IF_PRESENT` other than `1`, such as `true`, as off without reporting it, by making `nmr` and `nmr-fmt` exit 2 with a message that names the variable and the values that it accepts. (#1297)
|
|
18
|
+
- Fixes the issue that `nmr upgrade --write` rewrote the `nodejs` pin in `.tool-versions` to a release that asdf could not install yet, by limiting the check to releases that node-build defines and reporting a newer release without a definition as held back. (#1300)
|
|
13
19
|
<!-- /section:release-notes -->
|
|
14
20
|
|
|
15
21
|
## Installation
|
|
@@ -189,7 +195,7 @@ $ nmr check
|
|
|
189
195
|
nmr: waiting for test in /Users/me/repos/other-repo (pid 41872) to finish
|
|
190
196
|
```
|
|
191
197
|
|
|
192
|
-
The lock lives in `/tmp/nmr-<uid>/`, or in `NMR_LOCK_DIR`; a sandboxed session needs write access to it. `nmr --no-lock <command>` or `NMR_NO_LOCK=1` runs without waiting. [Machine lock](docs/machine-lock.md) covers which commands take it, nesting and fan-outs, reclaiming a lock that its holder left behind, and CI.
|
|
198
|
+
The lock lives in `/tmp/nmr-<uid>/`, or in `NMR_LOCK_DIR`; a sandboxed session needs write access to it. `nmr --no-lock <command>` or `NMR_NO_LOCK=1` runs without waiting; [environment switches](docs/scripts.md#environment-switches) lists the values that the variable accepts. [Machine lock](docs/machine-lock.md) covers which commands take it, nesting and fan-outs, reclaiming a lock that its holder left behind, and CI.
|
|
193
199
|
|
|
194
200
|
## Delegating to another machine
|
|
195
201
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { hasBuildOutput, readBuildDigest } from "
|
|
3
|
-
import { loadWorkspaceConfig } from "
|
|
4
|
-
import { getDefaultWorkspaceScripts } from "
|
|
5
|
-
import { buildWorkspaceRegistry, resolveScript } from "
|
|
6
|
-
import { renderChain } from "
|
|
7
|
-
import { getWorkspacePackageDirs } from "
|
|
2
|
+
import { hasBuildOutput, readBuildDigest } from "../commands/build-output.js";
|
|
3
|
+
import { loadWorkspaceConfig } from "../config.js";
|
|
4
|
+
import { getDefaultWorkspaceScripts } from "../resolve-scripts.js";
|
|
5
|
+
import { buildWorkspaceRegistry, resolveScript } from "../resolver.js";
|
|
6
|
+
import { renderChain } from "../steps.js";
|
|
7
|
+
import { getWorkspacePackageDirs } from "../workspace.js";
|
|
8
8
|
const BUILT_IN_COMPILE = 'nmr-compile';
|
|
9
9
|
export function findStaleBuildOutput(earlierDigests, laterDigests) {
|
|
10
10
|
const names = [...new Set([...Object.keys(earlierDigests), ...Object.keys(laterDigests)])].toSorted();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { HostRunResult } from '../delegation/delegate-host.js';
|
|
2
|
+
import { type RunPlan } from '../invocation.js';
|
|
3
|
+
import type { OutputChannels } from '../output-channel.js';
|
|
1
4
|
import { type TreeSnapshot } from './check-cache.js';
|
|
2
5
|
import type { BuildOutputState } from './check-cache-build-output.js';
|
|
3
|
-
import type { HostRunResult } from './delegate-host.js';
|
|
4
|
-
import { type RunPlan } from './invocation.js';
|
|
5
|
-
import type { OutputChannels } from './output-channel.js';
|
|
6
6
|
/** What the check-result cache gates a run on: the key, the tree, and the build output that the run saw. */
|
|
7
7
|
export interface Gate {
|
|
8
8
|
buildOutputBefore: BuildOutputState;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { VERSION } from "../invocation.js";
|
|
2
3
|
import { resolveTreeSnapshot, writeDebugNote } from "./check-cache.js";
|
|
3
4
|
import { computeCacheKey, computeRetentionKey } from "./check-cache-key.js";
|
|
4
5
|
import { recordHostPass } from "./check-cache-record.js";
|
|
5
|
-
import { VERSION } from "./invocation.js";
|
|
6
6
|
import { formatManifestScope, validatePassManifest } from "./pass-manifest.js";
|
|
7
7
|
export async function recordDelegatedPass(plan, gate, hostRun) {
|
|
8
8
|
const { anchorDir, command, context, env, stderr } = plan.invocation;
|
|
@@ -11,7 +11,7 @@ export async function recordDelegatedPass(plan, gate, hostRun) {
|
|
|
11
11
|
writeDebugNote(`not recording ${command}: the pushed tree is not the tree that the gate keyed on`, env, stderr);
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const currentRead = resolveTreeSnapshot({ monorepoRoot, env: {} });
|
|
14
|
+
const currentRead = resolveTreeSnapshot({ monorepoRoot, env: {}, readTree: plan.invocation.readTree });
|
|
15
15
|
if (!currentRead.ok) {
|
|
16
16
|
writeDebugNote(`not recording ${command}: ${currentRead.reason}`, env, stderr);
|
|
17
17
|
return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { isRecord, readCacheEntry, readJsonCacheEntry, removeCacheDir, removeCacheEntry, resolveCacheEntryPath, writeCacheEntry, } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
|
+
import { isStringRecord } from "../helpers/type-guards.js";
|
|
4
5
|
import { writeDebugNote } from "./check-cache.js";
|
|
5
|
-
import { isStringRecord } from "./helpers/type-guards.js";
|
|
6
6
|
const CACHE_TOOL = 'nmr-check';
|
|
7
7
|
const UNSAFE_SLUG_CHARACTERS = /[^\w.-]+/g;
|
|
8
8
|
export async function certifyRetention(options) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
2
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
|
-
import { type
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
import type
|
|
3
|
+
import { type RunPlan } from '../invocation.js';
|
|
4
|
+
import type { NmrConfig } from '../types.js';
|
|
5
|
+
import type { VerdictOutcome } from '../verdict.js';
|
|
6
|
+
import { type TreeReader, type TreeSnapshot } from './check-cache.js';
|
|
7
7
|
/**
|
|
8
8
|
* Decides whether the check-result cache covers this invocation, and takes the tree snapshot on which it would
|
|
9
9
|
* gate.
|
|
@@ -20,6 +20,7 @@ export declare function openGate(options: {
|
|
|
20
20
|
env: NodeJS.ProcessEnv;
|
|
21
21
|
monorepoRoot: string;
|
|
22
22
|
passthrough: string[];
|
|
23
|
+
readTree: TreeReader;
|
|
23
24
|
stderr: Writable;
|
|
24
25
|
style: OutputStyle;
|
|
25
26
|
}): TreeSnapshot | undefined;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { startTimer } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { addLockEnv, takeMachineLock } from "../child-env.js";
|
|
3
|
+
import { runPlanOnHost } from "../delegation/delegate-host.js";
|
|
4
|
+
import { VERSION } from "../invocation.js";
|
|
5
|
+
import { formatFlagSpelling } from "../nmr-flags.js";
|
|
6
|
+
import { resolveChannel } from "../output-channel.js";
|
|
7
|
+
import { splitSegments } from "../shell-syntax.js";
|
|
2
8
|
import { formatMisplacedNoCacheWarning, isCacheableCommand, resolveTreeSnapshot, writeDebugNote, } from "./check-cache.js";
|
|
3
9
|
import { findStaleBuildOutput, readBuildOutputState } from "./check-cache-build-output.js";
|
|
4
10
|
import { recordDelegatedPass } from "./check-cache-delegation.js";
|
|
5
11
|
import { certifyRetention, readCheckCacheEntry } from "./check-cache-entries.js";
|
|
6
12
|
import { computeCacheKey, computeRetentionKey } from "./check-cache-key.js";
|
|
7
13
|
import { recordPass } from "./check-cache-record.js";
|
|
8
|
-
import { addLockEnv, takeMachineLock } from "./child-env.js";
|
|
9
|
-
import { runPlanOnHost } from "./delegate-host.js";
|
|
10
|
-
import { VERSION } from "./invocation.js";
|
|
11
|
-
import { formatFlagSpelling } from "./nmr-flags.js";
|
|
12
|
-
import { resolveChannel } from "./output-channel.js";
|
|
13
14
|
import { writePassManifestEntry } from "./pass-manifest.js";
|
|
14
|
-
import { runSteps } from "./runner.js";
|
|
15
|
-
import { splitSegments } from "./shell-syntax.js";
|
|
16
15
|
export function openGate(options) {
|
|
17
|
-
const { command, config, env, monorepoRoot, passthrough, stderr, style } = options;
|
|
16
|
+
const { command, config, env, monorepoRoot, passthrough, readTree, stderr, style } = options;
|
|
18
17
|
if (!isCacheableCommand(config.checkCache, command)) {
|
|
19
18
|
return undefined;
|
|
20
19
|
}
|
|
@@ -25,7 +24,7 @@ export function openGate(options) {
|
|
|
25
24
|
writeDebugNote(`gate disabled: ${command} was passed arguments`, env, stderr);
|
|
26
25
|
return undefined;
|
|
27
26
|
}
|
|
28
|
-
const snapshot = resolveTreeSnapshot({ monorepoRoot, env });
|
|
27
|
+
const snapshot = resolveTreeSnapshot({ monorepoRoot, env, readTree });
|
|
29
28
|
if (!snapshot.ok) {
|
|
30
29
|
writeDebugNote(`gate disabled: ${snapshot.reason}`, env, stderr);
|
|
31
30
|
return undefined;
|
|
@@ -83,7 +82,7 @@ export async function runGated(plan) {
|
|
|
83
82
|
}
|
|
84
83
|
const runOptions = { ...plan.runOptions, env: addLockEnv(plan.runOptions.env ?? env, lock) };
|
|
85
84
|
const readElapsedMs = startTimer();
|
|
86
|
-
const { exitCode, retainedOutput, statedOutcomes } = await runSteps(plan.steps, anchorDir, runOptions);
|
|
85
|
+
const { exitCode, retainedOutput, statedOutcomes } = await plan.invocation.runSteps(plan.steps, anchorDir, runOptions);
|
|
87
86
|
const durationMs = readElapsedMs();
|
|
88
87
|
if (exitCode === 0 && gate !== undefined) {
|
|
89
88
|
await recordPass({
|
|
@@ -97,6 +96,7 @@ export async function runGated(plan) {
|
|
|
97
96
|
key: gate.key,
|
|
98
97
|
monorepoRoot,
|
|
99
98
|
ownSteps: plan.ownSteps,
|
|
99
|
+
readTree: plan.invocation.readTree,
|
|
100
100
|
retainedOutput,
|
|
101
101
|
retentionKey: gate.retentionKey,
|
|
102
102
|
runId: plan.runId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { OutputChannels } from '../output-channel.js';
|
|
1
2
|
import type { TreeSnapshot } from './check-cache.js';
|
|
2
|
-
import type { OutputChannels } from './output-channel.js';
|
|
3
3
|
/** The parts of the running interpreter that can change what a check concludes. */
|
|
4
4
|
export interface RuntimeIdentity {
|
|
5
5
|
arch: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
-
import { type
|
|
2
|
+
import { type RunCliResult, type RunPlan } from '../invocation.js';
|
|
3
|
+
import type { RetainedOutput } from '../runner.js';
|
|
4
|
+
import type { Step } from '../steps.js';
|
|
5
|
+
import type { NmrConfig } from '../types.js';
|
|
6
|
+
import { type TreeReader, type TreeSnapshot } from './check-cache.js';
|
|
3
7
|
import { type BuildOutputState } from './check-cache-build-output.js';
|
|
4
8
|
import { type EntryRef, type Retention } from './check-cache-entries.js';
|
|
5
|
-
import { type RunCliResult, type RunPlan } from './invocation.js';
|
|
6
|
-
import type { RetainedOutput } from './runner.js';
|
|
7
|
-
import type { Step } from './steps.js';
|
|
8
|
-
import type { NmrConfig } from './types.js';
|
|
9
9
|
/**
|
|
10
10
|
* Records a pass that `host` earned, unless a pass earned here under the same key would still be recalled, because
|
|
11
11
|
* that pass satisfies every invocation that the host pass would. One whose build output is missing or came from
|
|
@@ -53,6 +53,7 @@ export declare function recordPass(options: {
|
|
|
53
53
|
monorepoRoot: string;
|
|
54
54
|
retainedOutput: RetainedOutput | undefined;
|
|
55
55
|
ownSteps: readonly Step[];
|
|
56
|
+
readTree: TreeReader;
|
|
56
57
|
retentionKey: string;
|
|
57
58
|
runId: string;
|
|
58
59
|
snapshot: TreeSnapshot;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
|
-
import {
|
|
3
|
+
import { isEnvSwitchOn } from "../env-switch.js";
|
|
4
|
+
import { deriveExcerpt } from "../helpers/deriveExcerpt.js";
|
|
5
|
+
import { composeTranscript } from "../helpers/transcript.js";
|
|
6
|
+
import { RUN_IF_PRESENT_ENV_VAR, VERSION } from "../invocation.js";
|
|
7
|
+
import { isCacheableCommand, resolveTreeSnapshot, writeDebugNote, } from "./check-cache.js";
|
|
4
8
|
import { findStaleBuildOutput, readBuildOutputState } from "./check-cache-build-output.js";
|
|
5
9
|
import { readCheckCacheEntry, recordTranscript, writeCheckCacheEntry, } from "./check-cache-entries.js";
|
|
6
10
|
import { CURRENT_RUNTIME } from "./check-cache-key.js";
|
|
7
|
-
import { deriveExcerpt } from "./helpers/deriveExcerpt.js";
|
|
8
|
-
import { composeTranscript } from "./helpers/transcript.js";
|
|
9
|
-
import { RUN_IF_PRESENT_ENV_VAR, VERSION } from "./invocation.js";
|
|
10
11
|
import { writePassManifestEntry } from "./pass-manifest.js";
|
|
11
12
|
import { renderRecording, renderRefusal, resolveRecording } from "./recording.js";
|
|
12
13
|
import { assembleReplay } from "./replay-assembly.js";
|
|
@@ -43,7 +44,7 @@ export async function recordHostPass(options) {
|
|
|
43
44
|
}
|
|
44
45
|
export async function recordPass(options) {
|
|
45
46
|
const { anchorDir, command, env, monorepoRoot, snapshot, stderr } = options;
|
|
46
|
-
const currentRead = resolveTreeSnapshot({ monorepoRoot, env: {} });
|
|
47
|
+
const currentRead = resolveTreeSnapshot({ monorepoRoot, env: {}, readTree: options.readTree });
|
|
47
48
|
if (!currentRead.ok) {
|
|
48
49
|
writeDebugNote(`not recording ${command}: ${currentRead.reason}`, env, stderr);
|
|
49
50
|
return;
|
|
@@ -124,7 +125,7 @@ export async function reportRecording(plan) {
|
|
|
124
125
|
});
|
|
125
126
|
if (!lookup.ok) {
|
|
126
127
|
stderr.write(`${renderRefusal({ command, refusal: lookup.refusal, scope, style: styles.stderr })}\n`);
|
|
127
|
-
return { exitCode: env
|
|
128
|
+
return { exitCode: isEnvSwitchOn(env, RUN_IF_PRESENT_ENV_VAR) ? 0 : 1 };
|
|
128
129
|
}
|
|
129
130
|
stdout.write(renderRecording({ command, recording: lookup.recording, scope, style: styles.stdout }));
|
|
130
131
|
return { exitCode: 0 };
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
-
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
|
-
import type { CheckCacheConfig } from '
|
|
2
|
+
import { hashWorkingTree, type OutputStyle, readHeadSha } from '@williamthorsen/nmr-core';
|
|
3
|
+
import type { CheckCacheConfig } from '../types.js';
|
|
4
4
|
/** One working tree, observed once per top-level invocation and shared with every process below it. */
|
|
5
5
|
export interface TreeSnapshot {
|
|
6
6
|
treeId: string;
|
|
7
7
|
headSha: string;
|
|
8
8
|
}
|
|
9
|
+
/** The git reads behind a tree snapshot, which a test replaces with a fake. */
|
|
10
|
+
export interface TreeReader {
|
|
11
|
+
hashWorkingTree: typeof hashWorkingTree;
|
|
12
|
+
readHeadSha: typeof readHeadSha;
|
|
13
|
+
}
|
|
14
|
+
/** The tree reader that asks git. */
|
|
15
|
+
export declare const GIT_TREE_READER: TreeReader;
|
|
9
16
|
/** Set to `1` to hear why the gate did not skip, or why it is disabled. */
|
|
10
17
|
export declare const DEBUG_ENV_VAR = "NMR_DEBUG";
|
|
11
18
|
/**
|
|
@@ -72,6 +79,7 @@ export declare function resolveRunId(env: NodeJS.ProcessEnv): string;
|
|
|
72
79
|
export declare function resolveTreeSnapshot(options: {
|
|
73
80
|
monorepoRoot: string;
|
|
74
81
|
env: NodeJS.ProcessEnv;
|
|
82
|
+
readTree: TreeReader;
|
|
75
83
|
}): {
|
|
76
84
|
ok: true;
|
|
77
85
|
snapshot: TreeSnapshot;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
3
|
import { hashWorkingTree, readHeadSha, STATUS_GLYPHS } from '@williamthorsen/nmr-core';
|
|
4
|
-
import {
|
|
4
|
+
import { isEnvSwitchOn } from "../env-switch.js";
|
|
5
|
+
import { isHookName } from "../helpers/hook-name.js";
|
|
6
|
+
export const GIT_TREE_READER = { hashWorkingTree, readHeadSha };
|
|
5
7
|
export const DEBUG_ENV_VAR = 'NMR_DEBUG';
|
|
6
8
|
export const DEFAULT_CACHEABLE_COMMANDS = [
|
|
7
9
|
'check',
|
|
@@ -54,10 +56,11 @@ export function resolveRunId(env) {
|
|
|
54
56
|
}
|
|
55
57
|
export function resolveTreeSnapshot(options) {
|
|
56
58
|
const inheritedSnapshot = decodeTreeSnapshot(options.env[TREE_SNAPSHOT_ENV_VAR]);
|
|
57
|
-
if (inheritedSnapshot !== undefined &&
|
|
59
|
+
if (inheritedSnapshot !== undefined &&
|
|
60
|
+
options.readTree.readHeadSha(options.monorepoRoot) === inheritedSnapshot.headSha) {
|
|
58
61
|
return { ok: true, snapshot: inheritedSnapshot };
|
|
59
62
|
}
|
|
60
|
-
const hashedTree = hashWorkingTree(options.monorepoRoot);
|
|
63
|
+
const hashedTree = options.readTree.hashWorkingTree(options.monorepoRoot);
|
|
61
64
|
if (!hashedTree.ok) {
|
|
62
65
|
return hashedTree;
|
|
63
66
|
}
|
|
@@ -74,7 +77,7 @@ export function resolveTreeSnapshot(options) {
|
|
|
74
77
|
return { ok: true, snapshot: { treeId: hashedTree.treeId, headSha: hashedTree.headSha } };
|
|
75
78
|
}
|
|
76
79
|
export function writeDebugNote(message, env, stderr) {
|
|
77
|
-
if (env
|
|
80
|
+
if (isEnvSwitchOn(env, DEBUG_ENV_VAR)) {
|
|
78
81
|
stderr.write(`nmr check-cache: ${message}\n`);
|
|
79
82
|
}
|
|
80
83
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NmrConfig } from '
|
|
1
|
+
import type { NmrConfig } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Rejects a `checkCache` name that does not resolve to any command. Both fields are read by name alone, so a
|
|
4
4
|
* misspelt entry is inert: The command that it meant to name goes on running, indistinguishable from one that
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { UserError } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { findClosestName } from "
|
|
3
|
-
import { isHookName } from "
|
|
4
|
-
import { buildRootRegistry, buildWorkspaceRegistry, readPackageJsonScripts } from "
|
|
5
|
-
import { getWorkspacePackageDirs, isMonorepoRoot } from "
|
|
2
|
+
import { findClosestName } from "../helpers/findClosestName.js";
|
|
3
|
+
import { isHookName } from "../helpers/hook-name.js";
|
|
4
|
+
import { buildRootRegistry, buildWorkspaceRegistry, readPackageJsonScripts } from "../resolver.js";
|
|
5
|
+
import { getWorkspacePackageDirs, isMonorepoRoot } from "../workspace.js";
|
|
6
6
|
export function assertResolvableCheckCacheCommands(config, configPath, baseDir) {
|
|
7
7
|
const { checkCache } = config;
|
|
8
8
|
if (checkCache === undefined || !isMonorepoRoot(baseDir)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
+
import type { CheckCacheConfig } from '../types.js';
|
|
2
3
|
import { type ReplayLine } from './check-cache-entries.js';
|
|
3
|
-
import type { CheckCacheConfig } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* One pass that an nmr process on a host recorded or recalled during a delegated run, as the host session returns it
|
|
6
6
|
* to the delegating machine.
|
|
@@ -4,9 +4,9 @@ import path from 'node:path';
|
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import { isRecord } from '@williamthorsen/nmr-core';
|
|
6
6
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
7
|
+
import { MAX_EXCERPT_BYTES } from "../helpers/deriveExcerpt.js";
|
|
7
8
|
import { isCacheableCommand, PASS_MANIFEST_DIR_ENV_VAR, writeDebugNote } from "./check-cache.js";
|
|
8
9
|
import { isReplayLine } from "./check-cache-entries.js";
|
|
9
|
-
import { MAX_EXCERPT_BYTES } from "./helpers/deriveExcerpt.js";
|
|
10
10
|
export function formatManifestScope(monorepoRoot, anchorDir) {
|
|
11
11
|
const scope = path.relative(monorepoRoot, anchorDir);
|
|
12
12
|
return scope === '' ? '.' : scope.split(path.sep).join('/');
|