@williamthorsen/nmr 2.2.1 → 2.2.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 +33 -0
- package/.readyup/kits/default.js +36 -36
- package/.readyup/manifest.json +12 -12
- package/CHANGELOG.md +11 -0
- package/README.md +1 -7
- package/dist/esm/commands/build.js +25 -25
- package/dist/esm/commands/fmt.js +62 -62
- package/dist/esm/commands/node-pin.js +3 -3
- package/dist/esm/commands/planTazePasses.js +6 -6
- package/dist/esm/commands/resolveFormatTargets.js +3 -3
- package/dist/esm/config-validation.js +20 -20
- package/dist/esm/delegation/delegate-host.js +13 -13
- package/dist/esm/describeHelp.js +22 -22
- package/dist/esm/helpers/filter-selection.js +4 -4
- package/dist/esm/machine-lock.js +8 -8
- package/dist/esm/prettier.js +11 -11
- package/dist/esm/resolver.js +10 -10
- package/dist/esm/runCli.js +33 -33
- package/dist/esm/runner.js +23 -23
- package/dist/esm/verdict.js +37 -37
- package/dist/esm/vitest.js +48 -48
- package/package.json +4 -4
package/.meta/changelog.json
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "2.2.2",
|
|
4
|
+
"date": "2026-11-14",
|
|
5
|
+
"sections": [
|
|
6
|
+
{
|
|
7
|
+
"title": "♻️ Refactoring",
|
|
8
|
+
"audience": "dev",
|
|
9
|
+
"items": [
|
|
10
|
+
{
|
|
11
|
+
"description": "Moves the unexported helpers in `nmr`'s source modules into a trailing region of each module, sorted alphabetically, so that each module leads with its exports. (#1345)",
|
|
12
|
+
"hash": "80000b47998deb1d42a585f9a675a98045afb880",
|
|
13
|
+
"entry": 1
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"title": "🧪 Tests",
|
|
19
|
+
"audience": "dev",
|
|
20
|
+
"items": [
|
|
21
|
+
{
|
|
22
|
+
"description": "Moves the module-level helpers in `nmr`'s test files below the `describe` blocks, into a sorted `Helpers` region, so that each file leads with its tests. (#1345)",
|
|
23
|
+
"hash": "80000b47998deb1d42a585f9a675a98045afb880",
|
|
24
|
+
"entry": 2
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"description": "Fixes the issue that `removeStaleCheckouts.unit.test.ts` failed intermittently with `ENOENT` when the detached `rm` deleted the moved checkout after the test listed the directory, by checking the inode only of the entries that still exist. (#1352)",
|
|
28
|
+
"hash": "18c12fa0b8eec0583f9bf4b3f321bb57516feb1c",
|
|
29
|
+
"entry": 1
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
2
35
|
{
|
|
3
36
|
"version": "2.2.1",
|
|
4
37
|
"date": "2026-11-13",
|
package/.readyup/kits/default.js
CHANGED
|
@@ -8161,11 +8161,11 @@ function formatMalformedScript(dir, key, value) {
|
|
|
8161
8161
|
return `${rejection} A step list belongs in \`${CONFIG_RELATIVE_PATH}\` under \`${field}\`.`;
|
|
8162
8162
|
}
|
|
8163
8163
|
|
|
8164
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8164
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/parseArgs.js
|
|
8165
8165
|
import path6 from "node:path";
|
|
8166
8166
|
import process2 from "node:process";
|
|
8167
8167
|
|
|
8168
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8168
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/UsageError.js
|
|
8169
8169
|
var UsageError = class extends Error {
|
|
8170
8170
|
constructor(message, options) {
|
|
8171
8171
|
super(message, options);
|
|
@@ -8173,7 +8173,7 @@ var UsageError = class extends Error {
|
|
|
8173
8173
|
}
|
|
8174
8174
|
};
|
|
8175
8175
|
|
|
8176
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8176
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/ParseError.js
|
|
8177
8177
|
var ParseError = class extends UsageError {
|
|
8178
8178
|
kind;
|
|
8179
8179
|
token;
|
|
@@ -8185,12 +8185,12 @@ var ParseError = class extends UsageError {
|
|
|
8185
8185
|
}
|
|
8186
8186
|
};
|
|
8187
8187
|
|
|
8188
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8188
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/resolveLongName.js
|
|
8189
8189
|
function resolveLongName(key, definition) {
|
|
8190
8190
|
return definition.long ?? key.replaceAll(/[A-Z]/g, (capital) => `-${capital.toLowerCase()}`);
|
|
8191
8191
|
}
|
|
8192
8192
|
|
|
8193
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8193
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/tokenizeArgs.js
|
|
8194
8194
|
import { parseArgs as nodeParseArgs } from "node:util";
|
|
8195
8195
|
function tokenizeArgs(argv, schema, extraBooleans = []) {
|
|
8196
8196
|
const nodeOptions = {};
|
|
@@ -8220,7 +8220,7 @@ function findFlagEntry(flagsByName, token) {
|
|
|
8220
8220
|
return entry.definition.short === token.rawName.slice(1) ? entry : void 0;
|
|
8221
8221
|
}
|
|
8222
8222
|
|
|
8223
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8223
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/validateSpec.js
|
|
8224
8224
|
var CAMEL_CASE_KEY = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/;
|
|
8225
8225
|
function validateSpec(spec) {
|
|
8226
8226
|
validateFlags(spec.flags ?? {});
|
|
@@ -8301,7 +8301,7 @@ function validateOperands(operands) {
|
|
|
8301
8301
|
}
|
|
8302
8302
|
}
|
|
8303
8303
|
|
|
8304
|
-
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.
|
|
8304
|
+
// ../../node_modules/.pnpm/@williamthorsen+toolbelt.cli@0.10.0/node_modules/@williamthorsen/toolbelt.cli/dist/esm/3-candidate/parseArgs.js
|
|
8305
8305
|
function parseArgs(argv, spec, options = {}) {
|
|
8306
8306
|
validateSpec(spec);
|
|
8307
8307
|
return parseValidatedArgs(argv, spec, options);
|
|
@@ -8632,16 +8632,6 @@ function readNmrTarget(tokens) {
|
|
|
8632
8632
|
function describeScript(script) {
|
|
8633
8633
|
return typeof script === "string" ? script : `[${script.map(describeElement).join(", ")}]`;
|
|
8634
8634
|
}
|
|
8635
|
-
function describeElement(element) {
|
|
8636
|
-
if (typeof element === "string") {
|
|
8637
|
-
return element;
|
|
8638
|
-
}
|
|
8639
|
-
const notes = [
|
|
8640
|
-
...element.shouldRunInBackground === true ? ["background"] : [],
|
|
8641
|
-
...element.shouldDeclineArguments === true ? ["no args"] : []
|
|
8642
|
-
];
|
|
8643
|
-
return notes.length === 0 ? element.run : `${element.run} (${notes.join(", ")})`;
|
|
8644
|
-
}
|
|
8645
8635
|
function readPackageJsonScripts(packageDir) {
|
|
8646
8636
|
const file = resolvePackageJsonPath(packageDir);
|
|
8647
8637
|
let rawText;
|
|
@@ -8671,6 +8661,16 @@ function buildRootRegistry(config, monorepoRoot) {
|
|
|
8671
8661
|
...config.rootScripts
|
|
8672
8662
|
};
|
|
8673
8663
|
}
|
|
8664
|
+
function describeElement(element) {
|
|
8665
|
+
if (typeof element === "string") {
|
|
8666
|
+
return element;
|
|
8667
|
+
}
|
|
8668
|
+
const notes = [
|
|
8669
|
+
...element.shouldRunInBackground === true ? ["background"] : [],
|
|
8670
|
+
...element.shouldDeclineArguments === true ? ["no args"] : []
|
|
8671
|
+
];
|
|
8672
|
+
return notes.length === 0 ? element.run : `${element.run} (${notes.join(", ")})`;
|
|
8673
|
+
}
|
|
8674
8674
|
|
|
8675
8675
|
// src/assertResolvableCommandLists.ts
|
|
8676
8676
|
function assertResolvableCommandLists(config, configPath, baseDir) {
|
|
@@ -8838,6 +8838,11 @@ function formatKeyList(keys, prefix) {
|
|
|
8838
8838
|
function isOptionalBoolean(value) {
|
|
8839
8839
|
return value === void 0 || typeof value === "boolean";
|
|
8840
8840
|
}
|
|
8841
|
+
function isScriptElement(value) {
|
|
8842
|
+
if (typeof value === "string") return true;
|
|
8843
|
+
if (!isRecord2(value) || typeof value["run"] !== "string") return false;
|
|
8844
|
+
return isOptionalBoolean(value["shouldDeclineArguments"]) && isOptionalBoolean(value["shouldRunInBackground"]);
|
|
8845
|
+
}
|
|
8841
8846
|
function isScriptRecord(value) {
|
|
8842
8847
|
if (!isRecord2(value)) return false;
|
|
8843
8848
|
for (const v of Object.values(value)) {
|
|
@@ -8846,11 +8851,6 @@ function isScriptRecord(value) {
|
|
|
8846
8851
|
}
|
|
8847
8852
|
return true;
|
|
8848
8853
|
}
|
|
8849
|
-
function isScriptElement(value) {
|
|
8850
|
-
if (typeof value === "string") return true;
|
|
8851
|
-
if (!isRecord2(value) || typeof value["run"] !== "string") return false;
|
|
8852
|
-
return isOptionalBoolean(value["shouldDeclineArguments"]) && isOptionalBoolean(value["shouldRunInBackground"]);
|
|
8853
|
-
}
|
|
8854
8854
|
function isStringArray(value) {
|
|
8855
8855
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
8856
8856
|
}
|
|
@@ -8953,6 +8953,19 @@ function validateOutputField(value, configPath) {
|
|
|
8953
8953
|
}
|
|
8954
8954
|
return config;
|
|
8955
8955
|
}
|
|
8956
|
+
function validateScriptField(value, fieldName, configPath) {
|
|
8957
|
+
if (!Object.hasOwn(value, fieldName) || value[fieldName] === void 0) {
|
|
8958
|
+
return void 0;
|
|
8959
|
+
}
|
|
8960
|
+
const scripts = value[fieldName];
|
|
8961
|
+
if (!isScriptRecord(scripts)) {
|
|
8962
|
+
throw new UserError(
|
|
8963
|
+
`Invalid nmr config at ${configPath}: \`${fieldName}\` must be a Record<string, string | element[]>, where an element is a command string or \`{ run: string, shouldDeclineArguments?: boolean, shouldRunInBackground?: boolean }\``
|
|
8964
|
+
);
|
|
8965
|
+
}
|
|
8966
|
+
assertValidElements(scripts, fieldName, configPath);
|
|
8967
|
+
return scripts;
|
|
8968
|
+
}
|
|
8956
8969
|
function validateSelfHostedRunnerField(value, configPath) {
|
|
8957
8970
|
const selfHostedRunner = value["selfHostedRunner"];
|
|
8958
8971
|
if (selfHostedRunner === void 0) {
|
|
@@ -8970,19 +8983,6 @@ function validateSelfHostedRunnerField(value, configPath) {
|
|
|
8970
8983
|
}
|
|
8971
8984
|
return { exempt };
|
|
8972
8985
|
}
|
|
8973
|
-
function validateScriptField(value, fieldName, configPath) {
|
|
8974
|
-
if (!Object.hasOwn(value, fieldName) || value[fieldName] === void 0) {
|
|
8975
|
-
return void 0;
|
|
8976
|
-
}
|
|
8977
|
-
const scripts = value[fieldName];
|
|
8978
|
-
if (!isScriptRecord(scripts)) {
|
|
8979
|
-
throw new UserError(
|
|
8980
|
-
`Invalid nmr config at ${configPath}: \`${fieldName}\` must be a Record<string, string | element[]>, where an element is a command string or \`{ run: string, shouldDeclineArguments?: boolean, shouldRunInBackground?: boolean }\``
|
|
8981
|
-
);
|
|
8982
|
-
}
|
|
8983
|
-
assertValidElements(scripts, fieldName, configPath);
|
|
8984
|
-
return scripts;
|
|
8985
|
-
}
|
|
8986
8986
|
|
|
8987
8987
|
// src/helpers/escapeControlCharacters.ts
|
|
8988
8988
|
var NAMED_ESCAPES = /* @__PURE__ */ new Map([
|
|
@@ -9662,7 +9662,7 @@ function hasSupportedStrictLintVersion() {
|
|
|
9662
9662
|
});
|
|
9663
9663
|
}
|
|
9664
9664
|
function getMinVersion() {
|
|
9665
|
-
const pickedFields = { "version": "2.2.
|
|
9665
|
+
const pickedFields = { "version": "2.2.2" };
|
|
9666
9666
|
if (typeof pickedFields["version"] !== "string") {
|
|
9667
9667
|
throw new TypeError("nmr/package.json: 'version' must be a string");
|
|
9668
9668
|
}
|
package/.readyup/manifest.json
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
"kits": [
|
|
4
4
|
{
|
|
5
5
|
"bundledDependencies": {
|
|
6
|
-
"@williamthorsen/toolbelt.arrays": "6.5.
|
|
7
|
-
"@williamthorsen/toolbelt.cli": "0.
|
|
8
|
-
"@williamthorsen/toolbelt.datetime": "4.1.
|
|
6
|
+
"@williamthorsen/toolbelt.arrays": "6.5.2",
|
|
7
|
+
"@williamthorsen/toolbelt.cli": "0.10.0",
|
|
8
|
+
"@williamthorsen/toolbelt.datetime": "4.1.5",
|
|
9
9
|
"@williamthorsen/toolbelt.errors": "0.10.1",
|
|
10
|
-
"@williamthorsen/toolbelt.filesystem": "0.
|
|
10
|
+
"@williamthorsen/toolbelt.filesystem": "0.11.0",
|
|
11
11
|
"@williamthorsen/toolbelt.guards": "4.2.3",
|
|
12
|
-
"@williamthorsen/toolbelt.numbers": "8.0.
|
|
12
|
+
"@williamthorsen/toolbelt.numbers": "8.0.2",
|
|
13
13
|
"@williamthorsen/toolbelt.objects": "4.4.3",
|
|
14
|
-
"@williamthorsen/toolbelt.packaging": "0.
|
|
14
|
+
"@williamthorsen/toolbelt.packaging": "0.8.0",
|
|
15
15
|
"@williamthorsen/toolbelt.sets": "3.1.1",
|
|
16
|
-
"@williamthorsen/toolbelt.strings": "
|
|
16
|
+
"@williamthorsen/toolbelt.strings": "9.0.0",
|
|
17
17
|
"@williamthorsen/toolbelt.terminal": "0.3.1",
|
|
18
18
|
"ansi-regex": "6.6.0",
|
|
19
19
|
"ansi-styles": "6.2.3, 7.0.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"checklist": "nmr",
|
|
32
32
|
"checks": [
|
|
33
33
|
"@williamthorsen/nmr in devDependencies",
|
|
34
|
-
"@williamthorsen/nmr >= 2.2.
|
|
34
|
+
"@williamthorsen/nmr >= 2.2.2",
|
|
35
35
|
"pnpm-workspace.yaml exists",
|
|
36
36
|
"catalogued dependencies use catalog:",
|
|
37
37
|
"package.json has packageManager field",
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"path": "lib/workspace-build.ts"
|
|
236
236
|
},
|
|
237
237
|
{
|
|
238
|
-
"hash": "
|
|
238
|
+
"hash": "c1260e70",
|
|
239
239
|
"kind": "inline",
|
|
240
240
|
"path": "../package.json",
|
|
241
241
|
"paths": [
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
"path": "../src/commands/ensure-prepublish-hooks.ts"
|
|
254
254
|
},
|
|
255
255
|
{
|
|
256
|
-
"hash": "
|
|
256
|
+
"hash": "4ab792b6",
|
|
257
257
|
"kind": "module",
|
|
258
258
|
"path": "../src/config-validation.ts"
|
|
259
259
|
},
|
|
@@ -323,7 +323,7 @@
|
|
|
323
323
|
"path": "../src/resolve-scripts.ts"
|
|
324
324
|
},
|
|
325
325
|
{
|
|
326
|
-
"hash": "
|
|
326
|
+
"hash": "3e3aff50",
|
|
327
327
|
"kind": "module",
|
|
328
328
|
"path": "../src/resolver.ts"
|
|
329
329
|
},
|
|
@@ -363,7 +363,7 @@
|
|
|
363
363
|
"readyupVersion": "0.46.2",
|
|
364
364
|
"source": "kits/default.ts",
|
|
365
365
|
"sourceHash": "9fd61bf4",
|
|
366
|
-
"targetHash": "
|
|
366
|
+
"targetHash": "9d07e5f2"
|
|
367
367
|
}
|
|
368
368
|
]
|
|
369
369
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 2.2.2 — 2026-11-14
|
|
6
|
+
|
|
7
|
+
### ♻️ Refactoring
|
|
8
|
+
|
|
9
|
+
- Moves the unexported helpers in `nmr`'s source modules into a trailing region of each module, sorted alphabetically, so that each module leads with its exports. (#1345)
|
|
10
|
+
|
|
11
|
+
### 🧪 Tests
|
|
12
|
+
|
|
13
|
+
- Moves the module-level helpers in `nmr`'s test files below the `describe` blocks, into a sorted `Helpers` region, so that each file leads with its tests. (#1345)
|
|
14
|
+
- Fixes the issue that `removeStaleCheckouts.unit.test.ts` failed intermittently with `ENOENT` when the detached `rm` deleted the moved checkout after the test listed the directory, by checking the inode only of the entries that still exist. (#1352)
|
|
15
|
+
|
|
5
16
|
## 2.2.1 — 2026-11-13
|
|
6
17
|
|
|
7
18
|
### 🐛 Bug fixes
|
package/README.md
CHANGED
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
|
|
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
|
-
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v2.2.1 (2026-11-13)
|
|
9
|
-
|
|
10
|
-
### 🐛 Bug fixes
|
|
11
|
-
|
|
12
|
-
- Fixes the issue that a delegated `nmr` run on a host whose Node comes from a version manager's shims ran on the Node chosen before the checkout, rather than the one pinned by the snapshot, on the first delegation from a clone and the first after a repo changed its pin. (#1337)
|
|
13
|
-
<!-- /section:release-notes -->
|
|
7
|
+
<!-- section:release-notes --><!-- /section:release-notes -->
|
|
14
8
|
|
|
15
9
|
## Installation
|
|
16
10
|
|
|
@@ -57,6 +57,31 @@ export async function computeBuildHash(packageDir, files, emitConfig, toolchain)
|
|
|
57
57
|
hash.update(toolchain.fingerprint);
|
|
58
58
|
return hash.digest('hex');
|
|
59
59
|
}
|
|
60
|
+
export async function recordUnchangedBuild(packageDir, digest, env) {
|
|
61
|
+
const savedMs = await readEmitDuration(packageDir, digest);
|
|
62
|
+
await writeStatedOutcome(env, { kind: 'build-unchanged', ...(savedMs !== undefined && { savedMs }) });
|
|
63
|
+
return savedMs;
|
|
64
|
+
}
|
|
65
|
+
function assertEmitSucceeded(result) {
|
|
66
|
+
if (result.emitSkipped) {
|
|
67
|
+
throw new UserError(`emit failed.\n${formatDiagnostics(result.diagnostics)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function createCachingCompilerHost(compilerOptions) {
|
|
71
|
+
const ts = getCompiler();
|
|
72
|
+
const host = ts.createCompilerHost(compilerOptions);
|
|
73
|
+
const parsedFiles = new Map();
|
|
74
|
+
const readSourceFile = host.getSourceFile.bind(host);
|
|
75
|
+
host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
|
|
76
|
+
if (parsedFiles.has(fileName)) {
|
|
77
|
+
return parsedFiles.get(fileName);
|
|
78
|
+
}
|
|
79
|
+
const sourceFile = readSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
|
|
80
|
+
parsedFiles.set(fileName, sourceFile);
|
|
81
|
+
return sourceFile;
|
|
82
|
+
};
|
|
83
|
+
return host;
|
|
84
|
+
}
|
|
60
85
|
async function emitPackage(packageDir, entryPoints, outdir) {
|
|
61
86
|
const ts = getCompiler();
|
|
62
87
|
const compilerOptions = synthesizeCompilerOptions(packageDir, outdir);
|
|
@@ -79,26 +104,6 @@ async function emitPackage(packageDir, entryPoints, outdir) {
|
|
|
79
104
|
}
|
|
80
105
|
return publishOutput(emitDir, stagedFiles);
|
|
81
106
|
}
|
|
82
|
-
function assertEmitSucceeded(result) {
|
|
83
|
-
if (result.emitSkipped) {
|
|
84
|
-
throw new UserError(`emit failed.\n${formatDiagnostics(result.diagnostics)}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function createCachingCompilerHost(compilerOptions) {
|
|
88
|
-
const ts = getCompiler();
|
|
89
|
-
const host = ts.createCompilerHost(compilerOptions);
|
|
90
|
-
const parsedFiles = new Map();
|
|
91
|
-
const readSourceFile = host.getSourceFile.bind(host);
|
|
92
|
-
host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
|
|
93
|
-
if (parsedFiles.has(fileName)) {
|
|
94
|
-
return parsedFiles.get(fileName);
|
|
95
|
-
}
|
|
96
|
-
const sourceFile = readSourceFile(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
|
|
97
|
-
parsedFiles.set(fileName, sourceFile);
|
|
98
|
-
return sourceFile;
|
|
99
|
-
};
|
|
100
|
-
return host;
|
|
101
|
-
}
|
|
102
107
|
function resolveEmitDir(packageDir, outdir) {
|
|
103
108
|
const resolvedDir = path.resolve(packageDir, outdir);
|
|
104
109
|
const relativePathFromPackage = path.relative(packageDir, resolvedDir);
|
|
@@ -144,8 +149,3 @@ async function detectBuildChanges(packageDir, files, emitConfig, toolchain, cach
|
|
|
144
149
|
}
|
|
145
150
|
return { reason: isOutputPresent ? 'unchanged' : 'output-missing', currentHash };
|
|
146
151
|
}
|
|
147
|
-
export async function recordUnchangedBuild(packageDir, digest, env) {
|
|
148
|
-
const savedMs = await readEmitDuration(packageDir, digest);
|
|
149
|
-
await writeStatedOutcome(env, { kind: 'build-unchanged', ...(savedMs !== undefined && { savedMs }) });
|
|
150
|
-
return savedMs;
|
|
151
|
-
}
|
package/dist/esm/commands/fmt.js
CHANGED
|
@@ -46,6 +46,68 @@ export async function runFmt(argv, cwd = process.cwd(), env = process.env) {
|
|
|
46
46
|
});
|
|
47
47
|
return runCli(argv, command, { name: NMR_FMT_BIN, stdout: process.stdout, stderr: process.stderr, baseDir: cwd });
|
|
48
48
|
}
|
|
49
|
+
export function runPrettier(options) {
|
|
50
|
+
const { cacheLocation, cliPath, configPath, mode, files, ignorePaths, cwd, budgetBytes = ARGUMENT_BUDGET_BYTES, spawn = spawnInherited, } = options;
|
|
51
|
+
const prettierArgs = [
|
|
52
|
+
cliPath,
|
|
53
|
+
...(configPath === undefined ? [] : ['--config', configPath]),
|
|
54
|
+
'--ignore-unknown',
|
|
55
|
+
...ignorePaths.flatMap((ignorePath) => ['--ignore-path', ignorePath]),
|
|
56
|
+
...(cacheLocation === undefined
|
|
57
|
+
? []
|
|
58
|
+
: ['--cache', '--cache-strategy', 'content', '--cache-location', cacheLocation]),
|
|
59
|
+
...MODE_ARGS[mode],
|
|
60
|
+
'--',
|
|
61
|
+
];
|
|
62
|
+
let firstFailureStatus = 0;
|
|
63
|
+
for (const batch of batchWithinBudget(files, budgetBytes)) {
|
|
64
|
+
const result = spawn(process.execPath, [...prettierArgs, ...batch], { cwd });
|
|
65
|
+
if (result.error) {
|
|
66
|
+
reportError(NMR_FMT_BIN, `could not run \`${PRETTIER_PACKAGE}\` (${result.error.message}).`);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
const status = resolveInheritedExitCode(result);
|
|
70
|
+
if (status !== 0 && firstFailureStatus === 0) {
|
|
71
|
+
firstFailureStatus = status;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return firstFailureStatus;
|
|
75
|
+
}
|
|
76
|
+
function batchWithinBudget(files, budgetBytes) {
|
|
77
|
+
const batches = [];
|
|
78
|
+
let batch = [];
|
|
79
|
+
let sizeBytes = 0;
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
const costBytes = Buffer.byteLength(file) + 1;
|
|
82
|
+
if (batch.length > 0 && sizeBytes + costBytes > budgetBytes) {
|
|
83
|
+
batches.push(batch);
|
|
84
|
+
batch = [];
|
|
85
|
+
sizeBytes = 0;
|
|
86
|
+
}
|
|
87
|
+
batch.push(file);
|
|
88
|
+
sizeBytes += costBytes;
|
|
89
|
+
}
|
|
90
|
+
if (batch.length > 0)
|
|
91
|
+
batches.push(batch);
|
|
92
|
+
return batches;
|
|
93
|
+
}
|
|
94
|
+
async function findRepositoryConfig(manifestPath, cwd) {
|
|
95
|
+
try {
|
|
96
|
+
const prettier = createRequire(manifestPath)(PRETTIER_PACKAGE);
|
|
97
|
+
const resolveConfigFile = isRecord(prettier) ? prettier['resolveConfigFile'] : undefined;
|
|
98
|
+
if (typeof resolveConfigFile !== 'function') {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
error: `\`${PRETTIER_PACKAGE}\` at ${path.dirname(manifestPath)} does not export \`resolveConfigFile\`.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const configFile = await Reflect.apply(resolveConfigFile, undefined, [path.join(cwd, 'placeholder')]);
|
|
105
|
+
return { ok: true, found: configFile !== null };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return { ok: false, error: `could not search for a Prettier config: ${describeError(error)}` };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
49
111
|
async function formatFiles(request, cwd, env) {
|
|
50
112
|
const resolvedTargets = resolveFormatTargets(cwd, request.pathspecs);
|
|
51
113
|
if (!resolvedTargets.ok) {
|
|
@@ -133,65 +195,3 @@ function resolvePrettierCli(cwd) {
|
|
|
133
195
|
}
|
|
134
196
|
return { ok: true, cliPath, manifestPath };
|
|
135
197
|
}
|
|
136
|
-
async function findRepositoryConfig(manifestPath, cwd) {
|
|
137
|
-
try {
|
|
138
|
-
const prettier = createRequire(manifestPath)(PRETTIER_PACKAGE);
|
|
139
|
-
const resolveConfigFile = isRecord(prettier) ? prettier['resolveConfigFile'] : undefined;
|
|
140
|
-
if (typeof resolveConfigFile !== 'function') {
|
|
141
|
-
return {
|
|
142
|
-
ok: false,
|
|
143
|
-
error: `\`${PRETTIER_PACKAGE}\` at ${path.dirname(manifestPath)} does not export \`resolveConfigFile\`.`,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
const configFile = await Reflect.apply(resolveConfigFile, undefined, [path.join(cwd, 'placeholder')]);
|
|
147
|
-
return { ok: true, found: configFile !== null };
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
return { ok: false, error: `could not search for a Prettier config: ${describeError(error)}` };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
export function runPrettier(options) {
|
|
154
|
-
const { cacheLocation, cliPath, configPath, mode, files, ignorePaths, cwd, budgetBytes = ARGUMENT_BUDGET_BYTES, spawn = spawnInherited, } = options;
|
|
155
|
-
const prettierArgs = [
|
|
156
|
-
cliPath,
|
|
157
|
-
...(configPath === undefined ? [] : ['--config', configPath]),
|
|
158
|
-
'--ignore-unknown',
|
|
159
|
-
...ignorePaths.flatMap((ignorePath) => ['--ignore-path', ignorePath]),
|
|
160
|
-
...(cacheLocation === undefined
|
|
161
|
-
? []
|
|
162
|
-
: ['--cache', '--cache-strategy', 'content', '--cache-location', cacheLocation]),
|
|
163
|
-
...MODE_ARGS[mode],
|
|
164
|
-
'--',
|
|
165
|
-
];
|
|
166
|
-
let firstFailureStatus = 0;
|
|
167
|
-
for (const batch of batchWithinBudget(files, budgetBytes)) {
|
|
168
|
-
const result = spawn(process.execPath, [...prettierArgs, ...batch], { cwd });
|
|
169
|
-
if (result.error) {
|
|
170
|
-
reportError(NMR_FMT_BIN, `could not run \`${PRETTIER_PACKAGE}\` (${result.error.message}).`);
|
|
171
|
-
return 1;
|
|
172
|
-
}
|
|
173
|
-
const status = resolveInheritedExitCode(result);
|
|
174
|
-
if (status !== 0 && firstFailureStatus === 0) {
|
|
175
|
-
firstFailureStatus = status;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return firstFailureStatus;
|
|
179
|
-
}
|
|
180
|
-
function batchWithinBudget(files, budgetBytes) {
|
|
181
|
-
const batches = [];
|
|
182
|
-
let batch = [];
|
|
183
|
-
let sizeBytes = 0;
|
|
184
|
-
for (const file of files) {
|
|
185
|
-
const costBytes = Buffer.byteLength(file) + 1;
|
|
186
|
-
if (batch.length > 0 && sizeBytes + costBytes > budgetBytes) {
|
|
187
|
-
batches.push(batch);
|
|
188
|
-
batch = [];
|
|
189
|
-
sizeBytes = 0;
|
|
190
|
-
}
|
|
191
|
-
batch.push(file);
|
|
192
|
-
sizeBytes += costBytes;
|
|
193
|
-
}
|
|
194
|
-
if (batch.length > 0)
|
|
195
|
-
batches.push(batch);
|
|
196
|
-
return batches;
|
|
197
|
-
}
|
|
@@ -143,6 +143,9 @@ function findNodePin(content) {
|
|
|
143
143
|
function isMissingFileError(error) {
|
|
144
144
|
return isRecord(error) && error['code'] === 'ENOENT';
|
|
145
145
|
}
|
|
146
|
+
function parseVersion(version) {
|
|
147
|
+
return version.split('.').map(Number);
|
|
148
|
+
}
|
|
146
149
|
async function requestNodeBuildDefinition(version) {
|
|
147
150
|
const response = await fetch(`${NODE_BUILD_DEFINITION_URL}${version}`, {
|
|
148
151
|
method: 'HEAD',
|
|
@@ -154,6 +157,3 @@ async function requestNodeBuildDefinition(version) {
|
|
|
154
157
|
throw new Error(`HTTP ${response.status}`);
|
|
155
158
|
return true;
|
|
156
159
|
}
|
|
157
|
-
function parseVersion(version) {
|
|
158
|
-
return version.split('.').map(Number);
|
|
159
|
-
}
|
|
@@ -29,6 +29,12 @@ function composeComplementFilter(key, earlierKeys) {
|
|
|
29
29
|
function convertGlobToRegexBody(glob) {
|
|
30
30
|
return glob.replaceAll(/[.+?^${}()|[\]\\]/g, String.raw `\$&`).replaceAll(/\*+/g, '.*?');
|
|
31
31
|
}
|
|
32
|
+
function isPlainObject(value) {
|
|
33
|
+
if (typeof value !== 'object' || value === null)
|
|
34
|
+
return false;
|
|
35
|
+
const prototype = Object.getPrototypeOf(value);
|
|
36
|
+
return prototype === Object.prototype || prototype === null;
|
|
37
|
+
}
|
|
32
38
|
function validatePackageMaturityPeriod(value) {
|
|
33
39
|
if (value === undefined)
|
|
34
40
|
return [];
|
|
@@ -51,9 +57,3 @@ function validatePackageMaturityPeriod(value) {
|
|
|
51
57
|
return [key, days];
|
|
52
58
|
});
|
|
53
59
|
}
|
|
54
|
-
function isPlainObject(value) {
|
|
55
|
-
if (typeof value !== 'object' || value === null)
|
|
56
|
-
return false;
|
|
57
|
-
const prototype = Object.getPrototypeOf(value);
|
|
58
|
-
return prototype === Object.prototype || prototype === null;
|
|
59
|
-
}
|
|
@@ -63,9 +63,9 @@ function classifyListedFile(cwd, file) {
|
|
|
63
63
|
return { kind: 'failed', error: `could not stat ${file}: ${describeError(error)}` };
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
function splitNulSeparated(output) {
|
|
67
|
-
return output.split('\0').filter((entry) => entry !== '');
|
|
68
|
-
}
|
|
69
66
|
function dedupe(items) {
|
|
70
67
|
return [...new Set(items)];
|
|
71
68
|
}
|
|
69
|
+
function splitNulSeparated(output) {
|
|
70
|
+
return output.split('\0').filter((entry) => entry !== '');
|
|
71
|
+
}
|
|
@@ -98,6 +98,13 @@ function formatKeyList(keys, prefix) {
|
|
|
98
98
|
function isOptionalBoolean(value) {
|
|
99
99
|
return value === undefined || typeof value === 'boolean';
|
|
100
100
|
}
|
|
101
|
+
function isScriptElement(value) {
|
|
102
|
+
if (typeof value === 'string')
|
|
103
|
+
return true;
|
|
104
|
+
if (!isRecord(value) || typeof value['run'] !== 'string')
|
|
105
|
+
return false;
|
|
106
|
+
return isOptionalBoolean(value['shouldDeclineArguments']) && isOptionalBoolean(value['shouldRunInBackground']);
|
|
107
|
+
}
|
|
101
108
|
function isScriptRecord(value) {
|
|
102
109
|
if (!isRecord(value))
|
|
103
110
|
return false;
|
|
@@ -109,13 +116,6 @@ function isScriptRecord(value) {
|
|
|
109
116
|
}
|
|
110
117
|
return true;
|
|
111
118
|
}
|
|
112
|
-
function isScriptElement(value) {
|
|
113
|
-
if (typeof value === 'string')
|
|
114
|
-
return true;
|
|
115
|
-
if (!isRecord(value) || typeof value['run'] !== 'string')
|
|
116
|
-
return false;
|
|
117
|
-
return isOptionalBoolean(value['shouldDeclineArguments']) && isOptionalBoolean(value['shouldRunInBackground']);
|
|
118
|
-
}
|
|
119
119
|
function isStringArray(value) {
|
|
120
120
|
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
121
121
|
}
|
|
@@ -215,6 +215,19 @@ function validateOutputField(value, configPath) {
|
|
|
215
215
|
}
|
|
216
216
|
return config;
|
|
217
217
|
}
|
|
218
|
+
function validateScriptField(value, fieldName, configPath) {
|
|
219
|
+
if (!Object.hasOwn(value, fieldName) || value[fieldName] === undefined) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const scripts = value[fieldName];
|
|
223
|
+
if (!isScriptRecord(scripts)) {
|
|
224
|
+
throw new UserError(`Invalid nmr config at ${configPath}: \`${fieldName}\` must be a Record<string, string | element[]>, ` +
|
|
225
|
+
'where an element is a command string or ' +
|
|
226
|
+
'`{ run: string, shouldDeclineArguments?: boolean, shouldRunInBackground?: boolean }`');
|
|
227
|
+
}
|
|
228
|
+
assertValidElements(scripts, fieldName, configPath);
|
|
229
|
+
return scripts;
|
|
230
|
+
}
|
|
218
231
|
function validateSelfHostedRunnerField(value, configPath) {
|
|
219
232
|
const selfHostedRunner = value['selfHostedRunner'];
|
|
220
233
|
if (selfHostedRunner === undefined) {
|
|
@@ -230,16 +243,3 @@ function validateSelfHostedRunnerField(value, configPath) {
|
|
|
230
243
|
}
|
|
231
244
|
return { exempt };
|
|
232
245
|
}
|
|
233
|
-
function validateScriptField(value, fieldName, configPath) {
|
|
234
|
-
if (!Object.hasOwn(value, fieldName) || value[fieldName] === undefined) {
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
const scripts = value[fieldName];
|
|
238
|
-
if (!isScriptRecord(scripts)) {
|
|
239
|
-
throw new UserError(`Invalid nmr config at ${configPath}: \`${fieldName}\` must be a Record<string, string | element[]>, ` +
|
|
240
|
-
'where an element is a command string or ' +
|
|
241
|
-
'`{ run: string, shouldDeclineArguments?: boolean, shouldRunInBackground?: boolean }`');
|
|
242
|
-
}
|
|
243
|
-
assertValidElements(scripts, fieldName, configPath);
|
|
244
|
-
return scripts;
|
|
245
|
-
}
|
|
@@ -98,19 +98,6 @@ function createTee(stream, buffer) {
|
|
|
98
98
|
},
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
|
-
function describeDelegationSource(target, parsedArgs, env) {
|
|
102
|
-
switch (target.source) {
|
|
103
|
-
case 'config':
|
|
104
|
-
return `${formatDisplayPath(target.configPath, env)} (when: ${target.when})`;
|
|
105
|
-
case 'env':
|
|
106
|
-
return DELEGATE_ENV_VAR;
|
|
107
|
-
case 'flag':
|
|
108
|
-
return parsedArgs.delegateHosts === undefined ? DELEGATE_FLAG : DELEGATE_TO_FLAG;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function describeUnreachableHost(host, sshMessage) {
|
|
112
|
-
return sshMessage === '' ? `${host} did not answer` : `${host} did not answer: ${sshMessage}`;
|
|
113
|
-
}
|
|
114
101
|
async function delegate(options) {
|
|
115
102
|
const { invocation, remoteEnv, target } = options;
|
|
116
103
|
const { command, context, cwd, env, parsedArgs, stderr, stdout } = invocation;
|
|
@@ -178,6 +165,19 @@ async function delegate(options) {
|
|
|
178
165
|
: { outcome: 'failed', durationMs, exitCode: result.exitCode };
|
|
179
166
|
return { ...ran, exitCode: result.exitCode, outcome, passes: result.passes };
|
|
180
167
|
}
|
|
168
|
+
function describeDelegationSource(target, parsedArgs, env) {
|
|
169
|
+
switch (target.source) {
|
|
170
|
+
case 'config':
|
|
171
|
+
return `${formatDisplayPath(target.configPath, env)} (when: ${target.when})`;
|
|
172
|
+
case 'env':
|
|
173
|
+
return DELEGATE_ENV_VAR;
|
|
174
|
+
case 'flag':
|
|
175
|
+
return parsedArgs.delegateHosts === undefined ? DELEGATE_FLAG : DELEGATE_TO_FLAG;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function describeUnreachableHost(host, sshMessage) {
|
|
179
|
+
return sshMessage === '' ? `${host} did not answer` : `${host} did not answer: ${sshMessage}`;
|
|
180
|
+
}
|
|
181
181
|
function formatRunHereLine(command, listedHosts, reason) {
|
|
182
182
|
return `nmr: running ${command} here rather than on ${listedHosts}: ${reason}\n`;
|
|
183
183
|
}
|
package/dist/esm/describeHelp.js
CHANGED
|
@@ -57,6 +57,17 @@ const ENVIRONMENT_AND_FILES_LINES = [
|
|
|
57
57
|
' ($XDG_CONFIG_HOME/nmr/config.ts when that is set)',
|
|
58
58
|
' A command that it sends runs here when no host answers',
|
|
59
59
|
];
|
|
60
|
+
function applyOverrides(registry, overrides) {
|
|
61
|
+
const markedNames = new Set();
|
|
62
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
63
|
+
if (!Object.hasOwn(registry, name)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
registry[name] = value;
|
|
67
|
+
markedNames.add(name);
|
|
68
|
+
}
|
|
69
|
+
return markedNames;
|
|
70
|
+
}
|
|
60
71
|
function collectOverrides(packageScripts) {
|
|
61
72
|
const candidateOverrides = {};
|
|
62
73
|
for (const [name, value] of Object.entries(packageScripts)) {
|
|
@@ -74,28 +85,6 @@ function collectUnregisteredScripts(packageScripts, registry) {
|
|
|
74
85
|
}
|
|
75
86
|
return unregisteredScripts;
|
|
76
87
|
}
|
|
77
|
-
function readRunnableScripts(packageDir) {
|
|
78
|
-
const scripts = readPackageJsonScripts(packageDir);
|
|
79
|
-
if (!scripts)
|
|
80
|
-
return {};
|
|
81
|
-
const runnableScripts = {};
|
|
82
|
-
for (const [name, value] of Object.entries(scripts)) {
|
|
83
|
-
if (!isSelfReferential(value, name, packageDir))
|
|
84
|
-
runnableScripts[name] = value;
|
|
85
|
-
}
|
|
86
|
-
return runnableScripts;
|
|
87
|
-
}
|
|
88
|
-
function applyOverrides(registry, overrides) {
|
|
89
|
-
const markedNames = new Set();
|
|
90
|
-
for (const [name, value] of Object.entries(overrides)) {
|
|
91
|
-
if (!Object.hasOwn(registry, name)) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
registry[name] = value;
|
|
95
|
-
markedNames.add(name);
|
|
96
|
-
}
|
|
97
|
-
return markedNames;
|
|
98
|
-
}
|
|
99
88
|
function filterHooks(registry) {
|
|
100
89
|
const hooklessRegistry = {};
|
|
101
90
|
for (const [key, value] of Object.entries(registry)) {
|
|
@@ -115,3 +104,14 @@ function formatRegistry(registry, markedNames, lines) {
|
|
|
115
104
|
lines.push(` ${(key + marker).padEnd(padWidth)} ${describeScript(value)}`);
|
|
116
105
|
}
|
|
117
106
|
}
|
|
107
|
+
function readRunnableScripts(packageDir) {
|
|
108
|
+
const scripts = readPackageJsonScripts(packageDir);
|
|
109
|
+
if (!scripts)
|
|
110
|
+
return {};
|
|
111
|
+
const runnableScripts = {};
|
|
112
|
+
for (const [name, value] of Object.entries(scripts)) {
|
|
113
|
+
if (!isSelfReferential(value, name, packageDir))
|
|
114
|
+
runnableScripts[name] = value;
|
|
115
|
+
}
|
|
116
|
+
return runnableScripts;
|
|
117
|
+
}
|
|
@@ -47,6 +47,10 @@ function isRootEntry(entry, monorepoRoot) {
|
|
|
47
47
|
}
|
|
48
48
|
return resolveRealPath(entry['path']) === resolveRealPath(monorepoRoot);
|
|
49
49
|
}
|
|
50
|
+
function readDelegateSelection(pattern, monorepoRoot) {
|
|
51
|
+
const probeRun = spawnSync('pnpm', ['--filter', pattern, 'exec', process.execPath, '--eval', `process.stdout.write('${SELECTION_MARKER}')`], { cwd: monorepoRoot, encoding: 'utf8' });
|
|
52
|
+
return interpretDelegateProbe({ error: probeRun.error, status: probeRun.status, stdout: probeRun.stdout });
|
|
53
|
+
}
|
|
50
54
|
function resolveRealPath(target) {
|
|
51
55
|
try {
|
|
52
56
|
return realpathSync(target);
|
|
@@ -55,7 +59,3 @@ function resolveRealPath(target) {
|
|
|
55
59
|
return path.resolve(target);
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
|
-
function readDelegateSelection(pattern, monorepoRoot) {
|
|
59
|
-
const probeRun = spawnSync('pnpm', ['--filter', pattern, 'exec', process.execPath, '--eval', `process.stdout.write('${SELECTION_MARKER}')`], { cwd: monorepoRoot, encoding: 'utf8' });
|
|
60
|
-
return interpretDelegateProbe({ error: probeRun.error, status: probeRun.status, stdout: probeRun.stdout });
|
|
61
|
-
}
|
package/dist/esm/machine-lock.js
CHANGED
|
@@ -140,14 +140,6 @@ function isHolderRecord(value) {
|
|
|
140
140
|
typeof value['startedAt'] === 'string' &&
|
|
141
141
|
typeof value['token'] === 'string');
|
|
142
142
|
}
|
|
143
|
-
async function isUnderAncestorHold(lockDir, env) {
|
|
144
|
-
const inheritedToken = env[LOCK_TOKEN_ENV_VAR];
|
|
145
|
-
if (inheritedToken === undefined || inheritedToken === '') {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
const state = await readLockState(path.join(lockDir, LOCK_FILE));
|
|
149
|
-
return state?.holder?.token === inheritedToken;
|
|
150
|
-
}
|
|
151
143
|
function isProcessGone(pid) {
|
|
152
144
|
try {
|
|
153
145
|
process.kill(pid, 0);
|
|
@@ -163,6 +155,14 @@ function isStale(state, staleAfterMs) {
|
|
|
163
155
|
}
|
|
164
156
|
return state.holder !== undefined && isProcessGone(state.holder.pid);
|
|
165
157
|
}
|
|
158
|
+
async function isUnderAncestorHold(lockDir, env) {
|
|
159
|
+
const inheritedToken = env[LOCK_TOKEN_ENV_VAR];
|
|
160
|
+
if (inheritedToken === undefined || inheritedToken === '') {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
const state = await readLockState(path.join(lockDir, LOCK_FILE));
|
|
164
|
+
return state?.holder?.token === inheritedToken;
|
|
165
|
+
}
|
|
166
166
|
function parseHolder(content) {
|
|
167
167
|
try {
|
|
168
168
|
const value = JSON.parse(content);
|
package/dist/esm/prettier.js
CHANGED
|
@@ -54,26 +54,26 @@ export function definePrettierConfig(options = {}) {
|
|
|
54
54
|
plugins: [buildShellPlugin(), ...additionalPlugins],
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
function assertNoReplacement(options) {
|
|
58
|
+
if ('overrides' in options)
|
|
59
|
+
throw buildOwnedKeyError('overrides', 'additionalOverrides');
|
|
60
|
+
if ('plugins' in options)
|
|
61
|
+
throw buildOwnedKeyError('plugins', 'additionalPlugins');
|
|
62
|
+
}
|
|
63
|
+
function buildOwnedKeyError(key, seam) {
|
|
64
|
+
return new TypeError(`definePrettierConfig: \`${key}\` is owned by this config. Use \`${seam}\` to append to it.`);
|
|
65
|
+
}
|
|
57
66
|
function buildShellPlugin() {
|
|
58
67
|
return {
|
|
59
68
|
...shPlugin,
|
|
60
69
|
languages: shPlugin.languages.filter(isInferredLanguage).map(dropExcludedFilenames),
|
|
61
70
|
};
|
|
62
71
|
}
|
|
63
|
-
function isInferredLanguage(language) {
|
|
64
|
-
return INFERRED_LANGUAGES.has(language.name);
|
|
65
|
-
}
|
|
66
72
|
function dropExcludedFilenames(language) {
|
|
67
73
|
if (language.filenames === undefined)
|
|
68
74
|
return language;
|
|
69
75
|
return { ...language, filenames: language.filenames.filter((name) => !EXCLUDED_FILENAMES.has(name)) };
|
|
70
76
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
throw buildOwnedKeyError('overrides', 'additionalOverrides');
|
|
74
|
-
if ('plugins' in options)
|
|
75
|
-
throw buildOwnedKeyError('plugins', 'additionalPlugins');
|
|
76
|
-
}
|
|
77
|
-
function buildOwnedKeyError(key, seam) {
|
|
78
|
-
return new TypeError(`definePrettierConfig: \`${key}\` is owned by this config. Use \`${seam}\` to append to it.`);
|
|
77
|
+
function isInferredLanguage(language) {
|
|
78
|
+
return INFERRED_LANGUAGES.has(language.name);
|
|
79
79
|
}
|
package/dist/esm/resolver.js
CHANGED
|
@@ -18,16 +18,6 @@ export function expandScript(script, isWorkspaceRoot) {
|
|
|
18
18
|
export function describeScript(script) {
|
|
19
19
|
return typeof script === 'string' ? script : `[${script.map(describeElement).join(', ')}]`;
|
|
20
20
|
}
|
|
21
|
-
function describeElement(element) {
|
|
22
|
-
if (typeof element === 'string') {
|
|
23
|
-
return element;
|
|
24
|
-
}
|
|
25
|
-
const notes = [
|
|
26
|
-
...(element.shouldRunInBackground === true ? ['background'] : []),
|
|
27
|
-
...(element.shouldDeclineArguments === true ? ['no args'] : []),
|
|
28
|
-
];
|
|
29
|
-
return notes.length === 0 ? element.run : `${element.run} (${notes.join(', ')})`;
|
|
30
|
-
}
|
|
31
21
|
export function readPackageJsonScripts(packageDir) {
|
|
32
22
|
const file = resolvePackageJsonPath(packageDir);
|
|
33
23
|
let rawText;
|
|
@@ -101,6 +91,16 @@ export function resolveScript(commandName, registry, packageDir, isWorkspaceRoot
|
|
|
101
91
|
}
|
|
102
92
|
return { origin: { tier: 'registry', key: commandName }, steps: expandScript(registryEntry, isWorkspaceRoot) };
|
|
103
93
|
}
|
|
94
|
+
function describeElement(element) {
|
|
95
|
+
if (typeof element === 'string') {
|
|
96
|
+
return element;
|
|
97
|
+
}
|
|
98
|
+
const notes = [
|
|
99
|
+
...(element.shouldRunInBackground === true ? ['background'] : []),
|
|
100
|
+
...(element.shouldDeclineArguments === true ? ['no args'] : []),
|
|
101
|
+
];
|
|
102
|
+
return notes.length === 0 ? element.run : `${element.run} (${notes.join(', ')})`;
|
|
103
|
+
}
|
|
104
104
|
function readPackageScope(packageDir) {
|
|
105
105
|
return isMonorepoRoot(packageDir) ? 'root' : 'package';
|
|
106
106
|
}
|
package/dist/esm/runCli.js
CHANGED
|
@@ -47,29 +47,20 @@ export async function runCli(options) {
|
|
|
47
47
|
const routedArgs = isBare ? [...args, '--help'] : args;
|
|
48
48
|
return { exitCode: await runRouter(routedArgs, root, routerOptions) };
|
|
49
49
|
}
|
|
50
|
-
async function
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
let target = resolvedTarget.value;
|
|
56
|
-
const { filter, recursive } = invocation.parsedArgs;
|
|
57
|
-
if (target.kind === 'remote' && (filter !== undefined || recursive)) {
|
|
58
|
-
const delegated = await runInvocationOnHost(invocation, target);
|
|
59
|
-
if (delegated !== undefined) {
|
|
60
|
-
return delegated.exitCode;
|
|
61
|
-
}
|
|
62
|
-
target = { kind: 'local' };
|
|
50
|
+
async function executePlan(plan) {
|
|
51
|
+
const { command, format, parsedArgs, scope, stdout, styles } = plan.invocation;
|
|
52
|
+
if (parsedArgs.shouldPrintLog) {
|
|
53
|
+
return reportRecording(plan);
|
|
63
54
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const hint = formatMisplacedFlagHint(options.args, invocation.parsedArgs.passthrough, invocation.styles.stderr);
|
|
68
|
-
if (hint !== undefined) {
|
|
69
|
-
options.stderr.write(`${hint}\n`);
|
|
70
|
-
}
|
|
55
|
+
const { exitCode, host, outcome } = await runGated(plan);
|
|
56
|
+
if (outcome !== undefined) {
|
|
57
|
+
reportVerdict({ command, ...(host !== undefined && { host }), scope, ...outcome }, stdout, format, styles.stdout);
|
|
71
58
|
}
|
|
72
|
-
return exitCode;
|
|
59
|
+
return { exitCode };
|
|
60
|
+
}
|
|
61
|
+
function isTopLevelInvocation(env) {
|
|
62
|
+
const runId = env[RUN_ID_ENV_VAR];
|
|
63
|
+
return runId === undefined || runId === '';
|
|
73
64
|
}
|
|
74
65
|
async function planRun(invocation, target) {
|
|
75
66
|
const { anchorDir, command, context, env, parsedArgs, stderr, stdout, styles } = invocation;
|
|
@@ -176,18 +167,27 @@ async function planRun(invocation, target) {
|
|
|
176
167
|
},
|
|
177
168
|
};
|
|
178
169
|
}
|
|
179
|
-
async function
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
return
|
|
170
|
+
async function runInvocation(invocation, options) {
|
|
171
|
+
const resolvedTarget = await resolveExecutionTarget(invocation);
|
|
172
|
+
if (resolvedTarget.settled) {
|
|
173
|
+
return resolvedTarget.result.exitCode;
|
|
183
174
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
let target = resolvedTarget.value;
|
|
176
|
+
const { filter, recursive } = invocation.parsedArgs;
|
|
177
|
+
if (target.kind === 'remote' && (filter !== undefined || recursive)) {
|
|
178
|
+
const delegated = await runInvocationOnHost(invocation, target);
|
|
179
|
+
if (delegated !== undefined) {
|
|
180
|
+
return delegated.exitCode;
|
|
181
|
+
}
|
|
182
|
+
target = { kind: 'local' };
|
|
187
183
|
}
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
const planned = await planRun(invocation, target);
|
|
185
|
+
const { exitCode, isUnknownCommand = false } = planned.settled ? planned.result : await executePlan(planned.value);
|
|
186
|
+
if (exitCode !== 0 && !isUnknownCommand && isTopLevelInvocation(options.env)) {
|
|
187
|
+
const hint = formatMisplacedFlagHint(options.args, invocation.parsedArgs.passthrough, invocation.styles.stderr);
|
|
188
|
+
if (hint !== undefined) {
|
|
189
|
+
options.stderr.write(`${hint}\n`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return exitCode;
|
|
193
193
|
}
|
package/dist/esm/runner.js
CHANGED
|
@@ -116,24 +116,10 @@ export async function runSteps(steps, cwd, options) {
|
|
|
116
116
|
export async function runCommand(command, cwd, options) {
|
|
117
117
|
return runSpawned({ args: [], file: command, input: 'inherit', shouldUseShell: true }, cwd, options);
|
|
118
118
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const { quiet, stderr, stdout } = options;
|
|
124
|
-
if (step.kind === 'opaque') {
|
|
125
|
-
return startSpawned({ args: [], file: step.command, input: 'inherit', shouldUseShell: true }, cwd, {
|
|
126
|
-
...options,
|
|
127
|
-
channels: { stderr: resolveChannel(stderr, quiet), stdout: resolveChannel(stdout, quiet) },
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
const [file, ...args] = step.argv;
|
|
131
|
-
const input = step.shouldWithholdInput === true ? 'ignore' : 'inherit';
|
|
132
|
-
return startSpawned({ args, file, input, shouldUseShell: false }, cwd, {
|
|
133
|
-
...options,
|
|
134
|
-
quiet: false,
|
|
135
|
-
channels: { stderr: resolveInheritedChannel(stderr), stdout: resolveInheritedChannel(stdout) },
|
|
136
|
-
});
|
|
119
|
+
function buildBackgroundEnv(options) {
|
|
120
|
+
const { env } = options;
|
|
121
|
+
const shouldForceColor = isTerminal(options.stdout) && (env['NO_COLOR'] ?? '') === '' && env['FORCE_COLOR'] === undefined;
|
|
122
|
+
return shouldForceColor ? { ...env, FORCE_COLOR: '1' } : env;
|
|
137
123
|
}
|
|
138
124
|
function composeRetainedOutput(parts, isCaptureWhole) {
|
|
139
125
|
if (!isCaptureWhole || parts.length === 0) {
|
|
@@ -146,14 +132,12 @@ function composeRetainedOutput(parts, isCaptureWhole) {
|
|
|
146
132
|
},
|
|
147
133
|
};
|
|
148
134
|
}
|
|
149
|
-
function buildBackgroundEnv(options) {
|
|
150
|
-
const { env } = options;
|
|
151
|
-
const shouldForceColor = isTerminal(options.stdout) && (env['NO_COLOR'] ?? '') === '' && env['FORCE_COLOR'] === undefined;
|
|
152
|
-
return shouldForceColor ? { ...env, FORCE_COLOR: '1' } : env;
|
|
153
|
-
}
|
|
154
135
|
async function recordResult(run, result) {
|
|
155
136
|
run.result = await result;
|
|
156
137
|
}
|
|
138
|
+
function runSpawned(spec, cwd, options) {
|
|
139
|
+
return startSpawned(spec, cwd, options).result;
|
|
140
|
+
}
|
|
157
141
|
function selectExitCode(foregroundExitCode, receivedSignal, backgroundRuns) {
|
|
158
142
|
if (foregroundExitCode !== 0)
|
|
159
143
|
return foregroundExitCode;
|
|
@@ -174,3 +158,19 @@ function startBackgroundRun(step, options, launch) {
|
|
|
174
158
|
run.settled = recordResult(run, started.result);
|
|
175
159
|
return run;
|
|
176
160
|
}
|
|
161
|
+
function startStep(step, cwd, options) {
|
|
162
|
+
const { quiet, stderr, stdout } = options;
|
|
163
|
+
if (step.kind === 'opaque') {
|
|
164
|
+
return startSpawned({ args: [], file: step.command, input: 'inherit', shouldUseShell: true }, cwd, {
|
|
165
|
+
...options,
|
|
166
|
+
channels: { stderr: resolveChannel(stderr, quiet), stdout: resolveChannel(stdout, quiet) },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const [file, ...args] = step.argv;
|
|
170
|
+
const input = step.shouldWithholdInput === true ? 'ignore' : 'inherit';
|
|
171
|
+
return startSpawned({ args, file, input, shouldUseShell: false }, cwd, {
|
|
172
|
+
...options,
|
|
173
|
+
quiet: false,
|
|
174
|
+
channels: { stderr: resolveInheritedChannel(stderr), stdout: resolveInheritedChannel(stdout) },
|
|
175
|
+
});
|
|
176
|
+
}
|
package/dist/esm/verdict.js
CHANGED
|
@@ -21,6 +21,20 @@ const NEWLINE_BYTES = 1;
|
|
|
21
21
|
const LINE_BUDGET_BYTES = VERDICT_LINE_LIMIT_BYTES - NEWLINE_BYTES;
|
|
22
22
|
const MIN_CUT_BYTES = Buffer.byteLength(TRUNCATION_MARK);
|
|
23
23
|
const REPLAY_MARKER = 'replayed:';
|
|
24
|
+
function describeNoOpReason(reason) {
|
|
25
|
+
switch (reason) {
|
|
26
|
+
case 'empty-override':
|
|
27
|
+
return 'the override is empty';
|
|
28
|
+
case 'empty-workspace':
|
|
29
|
+
return 'the workspace does not declare any package';
|
|
30
|
+
case 'noop-override':
|
|
31
|
+
return 'the override is a no-op';
|
|
32
|
+
default: {
|
|
33
|
+
const unhandledReason = reason;
|
|
34
|
+
throw new Error(`Unhandled no-op reason: ${String(unhandledReason)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
24
38
|
function describeOutcome(verdict, style) {
|
|
25
39
|
const statuses = STATUS_GLYPHS[style];
|
|
26
40
|
const onHost = verdict.host === undefined ? '' : ` on ${formatHost(verdict.host, style)}`;
|
|
@@ -49,41 +63,6 @@ function describeOutcome(verdict, style) {
|
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
}
|
|
52
|
-
function describeNoOpReason(reason) {
|
|
53
|
-
switch (reason) {
|
|
54
|
-
case 'empty-override':
|
|
55
|
-
return 'the override is empty';
|
|
56
|
-
case 'empty-workspace':
|
|
57
|
-
return 'the workspace does not declare any package';
|
|
58
|
-
case 'noop-override':
|
|
59
|
-
return 'the override is a no-op';
|
|
60
|
-
default: {
|
|
61
|
-
const unhandledReason = reason;
|
|
62
|
-
throw new Error(`Unhandled no-op reason: ${String(unhandledReason)}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function renderReplay(verdict) {
|
|
67
|
-
if (verdict.outcome !== 'recalled' || verdict.replay === undefined || verdict.replay.length === 0) {
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
const [first] = verdict.replay;
|
|
71
|
-
if (verdict.replay.length === 1 && first?.command === verdict.command && first.scope === verdict.scope) {
|
|
72
|
-
return `${REPLAY_MARKER} ${first.excerpt}`;
|
|
73
|
-
}
|
|
74
|
-
const lines = verdict.replay.map((line) => `${line.scope}: ${line.command}: ${line.excerpt}`);
|
|
75
|
-
return `${REPLAY_MARKER} ${lines.join('; ')}`;
|
|
76
|
-
}
|
|
77
|
-
function renderStated(verdict) {
|
|
78
|
-
if (verdict.outcome !== 'passed' || verdict.stated === undefined) {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
const saving = formatSaving(verdict.stated.savedMs ?? 0);
|
|
82
|
-
return saving === undefined ? 'build skipped as unchanged' : `build skipped as unchanged, ${saving}`;
|
|
83
|
-
}
|
|
84
|
-
function flattenDetail(detail) {
|
|
85
|
-
return detail.replaceAll(/[\r\n]+/gu, ' ').trim();
|
|
86
|
-
}
|
|
87
66
|
function findCutTarget(sizesBytes, longestSizeBytes, overrunBytes) {
|
|
88
67
|
const smallerSizesBytes = sizesBytes.filter((size) => size < longestSizeBytes && size > MIN_CUT_BYTES);
|
|
89
68
|
const share = Math.ceil(overrunBytes / sizesBytes.filter((size) => size === longestSizeBytes).length);
|
|
@@ -92,13 +71,16 @@ function findCutTarget(sizesBytes, longestSizeBytes, overrunBytes) {
|
|
|
92
71
|
: Math.max(...smallerSizesBytes, longestSizeBytes - overrunBytes);
|
|
93
72
|
return Math.max(MIN_CUT_BYTES, target);
|
|
94
73
|
}
|
|
95
|
-
function
|
|
96
|
-
return
|
|
74
|
+
function flattenDetail(detail) {
|
|
75
|
+
return detail.replaceAll(/[\r\n]+/gu, ' ').trim();
|
|
97
76
|
}
|
|
98
77
|
function formatHost(host, style) {
|
|
99
78
|
const glyph = NMR_GLYPHS[style].host.text;
|
|
100
79
|
return glyph === '' ? host : `${glyph} ${host}`;
|
|
101
80
|
}
|
|
81
|
+
function isWithinBudget(renderedLine) {
|
|
82
|
+
return Buffer.byteLength(renderedLine) <= LINE_BUDGET_BYTES;
|
|
83
|
+
}
|
|
102
84
|
function readAttribution(verdict) {
|
|
103
85
|
if (verdict.outcome !== 'recalled' || verdict.replay === undefined) {
|
|
104
86
|
return [];
|
|
@@ -126,6 +108,24 @@ function renderClamped(record) {
|
|
|
126
108
|
}
|
|
127
109
|
return renderedLine;
|
|
128
110
|
}
|
|
111
|
+
function renderReplay(verdict) {
|
|
112
|
+
if (verdict.outcome !== 'recalled' || verdict.replay === undefined || verdict.replay.length === 0) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const [first] = verdict.replay;
|
|
116
|
+
if (verdict.replay.length === 1 && first?.command === verdict.command && first.scope === verdict.scope) {
|
|
117
|
+
return `${REPLAY_MARKER} ${first.excerpt}`;
|
|
118
|
+
}
|
|
119
|
+
const lines = verdict.replay.map((line) => `${line.scope}: ${line.command}: ${line.excerpt}`);
|
|
120
|
+
return `${REPLAY_MARKER} ${lines.join('; ')}`;
|
|
121
|
+
}
|
|
122
|
+
function renderStated(verdict) {
|
|
123
|
+
if (verdict.outcome !== 'passed' || verdict.stated === undefined) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const saving = formatSaving(verdict.stated.savedMs ?? 0);
|
|
127
|
+
return saving === undefined ? 'build skipped as unchanged' : `build skipped as unchanged, ${saving}`;
|
|
128
|
+
}
|
|
129
129
|
function renderWithinBudget(verdict) {
|
|
130
130
|
const shortenedVerdict = shortenCuttableText(verdict);
|
|
131
131
|
const shortenedLine = JSON.stringify(shortenedVerdict);
|
package/dist/esm/vitest.js
CHANGED
|
@@ -58,6 +58,50 @@ export function defineRootVitestConfig(...layers) {
|
|
|
58
58
|
projectRoot: monorepoRoot,
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
+
function applyLayer(project, layer, tier) {
|
|
62
|
+
const withProject = layer.project ? mergeConfig(project, { test: layer.project }) : project;
|
|
63
|
+
const targetedOverrides = layer.tiers?.[tier];
|
|
64
|
+
return targetedOverrides ? mergeConfig(withProject, { test: targetedOverrides }) : withProject;
|
|
65
|
+
}
|
|
66
|
+
function assertKnownTiers(layers) {
|
|
67
|
+
const knownTiers = TIER_NAMES;
|
|
68
|
+
for (const { tiers } of layers) {
|
|
69
|
+
const declaredTiers = Object.keys(tiers ?? {});
|
|
70
|
+
for (const name of declaredTiers) {
|
|
71
|
+
if (!knownTiers.includes(name)) {
|
|
72
|
+
throw new TypeError(`Unknown tier "${name}" in \`tiers\`. Valid tiers: ${TIER_NAMES.join(', ')}.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function assertNoRetiredOptions(layer) {
|
|
78
|
+
for (const key of Object.keys(layer)) {
|
|
79
|
+
const replacement = RETIRED_OPTION_KEYS.get(key);
|
|
80
|
+
if (replacement !== undefined) {
|
|
81
|
+
throw new TypeError(`Invalid Vitest config: \`${key}\` was renamed to \`${replacement}\`.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function assertOptionKeys(layers, lastLayerKeys) {
|
|
86
|
+
const lastIndex = layers.length - 1;
|
|
87
|
+
layers.forEach((layer, index) => {
|
|
88
|
+
if (layer === undefined)
|
|
89
|
+
return;
|
|
90
|
+
assertNoRetiredOptions(layer);
|
|
91
|
+
const recognizedKeys = index === lastIndex ? lastLayerKeys : RECOGNIZED_OPTION_KEYS;
|
|
92
|
+
const unrecognizedKeys = Object.keys(layer).filter((key) => !recognizedKeys.includes(key));
|
|
93
|
+
if (unrecognizedKeys.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
throw new TypeError(`Invalid Vitest config: unrecognized ${unrecognizedKeys.length === 1 ? 'option' : 'options'} ` +
|
|
96
|
+
`${formatOptionKeys(unrecognizedKeys)}. Recognized: ${formatOptionKeys(recognizedKeys)}.`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function buildCollectionExclude(layers, ignoredPathsRoot) {
|
|
100
|
+
const declaredDirs = layers.flatMap((layer) => layer.testCollectionExclude ?? []);
|
|
101
|
+
const globs = [...TEST_COLLECTION_EXCLUDE, ...declaredDirs].map((dir) => `**/${dir}/**`);
|
|
102
|
+
const ignoredGlobs = listGitIgnoredPaths(ignoredPathsRoot).map(buildIgnoredPathGlob);
|
|
103
|
+
return [...new Set([...defaultExclude, ...globs, ...ignoredGlobs])];
|
|
104
|
+
}
|
|
61
105
|
function buildConfig(declaredLayers, { coverageInclude, ignoredPathsRoot, projectExclude = [], projectRoot }) {
|
|
62
106
|
const layers = declaredLayers.filter((layer) => layer !== undefined);
|
|
63
107
|
assertKnownTiers(layers);
|
|
@@ -90,6 +134,10 @@ function buildConfig(declaredLayers, { coverageInclude, ignoredPathsRoot, projec
|
|
|
90
134
|
}
|
|
91
135
|
return mergedConfig;
|
|
92
136
|
}
|
|
137
|
+
function buildIgnoredPathGlob(ignoredPath) {
|
|
138
|
+
const escapedPath = ignoredPath.replaceAll(GLOB_SYNTAX_PATTERN, String.raw `\$&`);
|
|
139
|
+
return escapedPath.endsWith('/') ? `${escapedPath}**` : escapedPath;
|
|
140
|
+
}
|
|
93
141
|
function buildProjects(layers, extraExclude, projectRoot, ignoredPathsRoot) {
|
|
94
142
|
const projectTiers = [
|
|
95
143
|
{ exclude: TIERED_PATTERNS, include: ALL_TEST_PATTERNS, name: RESIDUAL_TIER },
|
|
@@ -129,54 +177,6 @@ function buildProjects(layers, extraExclude, projectRoot, ignoredPathsRoot) {
|
|
|
129
177
|
: mergeConfig(mergedProject, { test: { maxWorkers: flagMaxWorkers } });
|
|
130
178
|
});
|
|
131
179
|
}
|
|
132
|
-
function applyLayer(project, layer, tier) {
|
|
133
|
-
const withProject = layer.project ? mergeConfig(project, { test: layer.project }) : project;
|
|
134
|
-
const targetedOverrides = layer.tiers?.[tier];
|
|
135
|
-
return targetedOverrides ? mergeConfig(withProject, { test: targetedOverrides }) : withProject;
|
|
136
|
-
}
|
|
137
|
-
function assertKnownTiers(layers) {
|
|
138
|
-
const knownTiers = TIER_NAMES;
|
|
139
|
-
for (const { tiers } of layers) {
|
|
140
|
-
const declaredTiers = Object.keys(tiers ?? {});
|
|
141
|
-
for (const name of declaredTiers) {
|
|
142
|
-
if (!knownTiers.includes(name)) {
|
|
143
|
-
throw new TypeError(`Unknown tier "${name}" in \`tiers\`. Valid tiers: ${TIER_NAMES.join(', ')}.`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function assertNoRetiredOptions(layer) {
|
|
149
|
-
for (const key of Object.keys(layer)) {
|
|
150
|
-
const replacement = RETIRED_OPTION_KEYS.get(key);
|
|
151
|
-
if (replacement !== undefined) {
|
|
152
|
-
throw new TypeError(`Invalid Vitest config: \`${key}\` was renamed to \`${replacement}\`.`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function assertOptionKeys(layers, lastLayerKeys) {
|
|
157
|
-
const lastIndex = layers.length - 1;
|
|
158
|
-
layers.forEach((layer, index) => {
|
|
159
|
-
if (layer === undefined)
|
|
160
|
-
return;
|
|
161
|
-
assertNoRetiredOptions(layer);
|
|
162
|
-
const recognizedKeys = index === lastIndex ? lastLayerKeys : RECOGNIZED_OPTION_KEYS;
|
|
163
|
-
const unrecognizedKeys = Object.keys(layer).filter((key) => !recognizedKeys.includes(key));
|
|
164
|
-
if (unrecognizedKeys.length === 0)
|
|
165
|
-
return;
|
|
166
|
-
throw new TypeError(`Invalid Vitest config: unrecognized ${unrecognizedKeys.length === 1 ? 'option' : 'options'} ` +
|
|
167
|
-
`${formatOptionKeys(unrecognizedKeys)}. Recognized: ${formatOptionKeys(recognizedKeys)}.`);
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
function buildCollectionExclude(layers, ignoredPathsRoot) {
|
|
171
|
-
const declaredDirs = layers.flatMap((layer) => layer.testCollectionExclude ?? []);
|
|
172
|
-
const globs = [...TEST_COLLECTION_EXCLUDE, ...declaredDirs].map((dir) => `**/${dir}/**`);
|
|
173
|
-
const ignoredGlobs = listGitIgnoredPaths(ignoredPathsRoot).map(buildIgnoredPathGlob);
|
|
174
|
-
return [...new Set([...defaultExclude, ...globs, ...ignoredGlobs])];
|
|
175
|
-
}
|
|
176
|
-
function buildIgnoredPathGlob(ignoredPath) {
|
|
177
|
-
const escapedPath = ignoredPath.replaceAll(GLOB_SYNTAX_PATTERN, String.raw `\$&`);
|
|
178
|
-
return escapedPath.endsWith('/') ? `${escapedPath}**` : escapedPath;
|
|
179
|
-
}
|
|
180
180
|
function formatOptionKeys(keys) {
|
|
181
181
|
return keys
|
|
182
182
|
.toSorted()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/nmr",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Context-aware script runner for pnpm monorepos",
|
|
6
6
|
"keywords": [
|
|
@@ -82,9 +82,9 @@
|
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"@williamthorsen/nmr-core": "0.16.2",
|
|
84
84
|
"@williamthorsen/toolbelt.errors": "0.10.1",
|
|
85
|
-
"@williamthorsen/toolbelt.filesystem": "0.
|
|
86
|
-
"glob": "14.0.
|
|
87
|
-
"prettier": "3.10.
|
|
85
|
+
"@williamthorsen/toolbelt.filesystem": "0.11.0",
|
|
86
|
+
"glob": "14.0.6",
|
|
87
|
+
"prettier": "3.10.1",
|
|
88
88
|
"prettier-plugin-sh": "0.20.2",
|
|
89
89
|
"taze": "21.3.0",
|
|
90
90
|
"unconfig": "7.5.0"
|