@williamthorsen/nmr 2.0.0 → 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 +164 -0
- package/.readyup/kits/default.js +51 -15
- package/.readyup/manifest.json +26 -21
- package/CHANGELOG.md +45 -0
- package/README.md +10 -4
- 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/cli-build.js +3 -10
- package/dist/esm/cli-clean.js +1 -2
- package/dist/esm/cli-report-catalog.js +2 -3
- package/dist/esm/cli-report-overrides.js +3 -5
- package/dist/esm/commands/actionlint.d.ts +2 -7
- package/dist/esm/commands/actionlint.js +5 -11
- package/dist/esm/commands/build-output.d.ts +7 -32
- package/dist/esm/commands/build-output.js +8 -13
- package/dist/esm/commands/build-report.d.ts +6 -0
- package/dist/esm/commands/build-report.js +21 -0
- package/dist/esm/commands/build.d.ts +27 -4
- package/dist/esm/commands/build.js +24 -86
- package/dist/esm/commands/clean-report.d.ts +6 -0
- package/dist/esm/commands/clean-report.js +20 -0
- package/dist/esm/commands/clean.d.ts +39 -7
- package/dist/esm/commands/clean.js +55 -31
- package/dist/esm/commands/compile.d.ts +5 -0
- package/dist/esm/commands/compile.js +32 -0
- package/dist/esm/commands/fmt-cache.js +3 -2
- package/dist/esm/commands/fmt.d.ts +6 -2
- package/dist/esm/commands/fmt.js +48 -27
- 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/publish-output.d.ts +40 -0
- package/dist/esm/commands/publish-output.js +66 -0
- package/dist/esm/commands/report-catalog.d.ts +5 -0
- package/dist/esm/commands/report-catalog.js +19 -0
- package/dist/esm/commands/report-overrides.d.ts +5 -0
- package/dist/esm/commands/report-overrides.js +20 -1
- 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 +10 -2
- package/docs/utilities.md +6 -4
- package/package.json +7 -7
- /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
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
2
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
|
+
/**
|
|
4
|
+
* Prefix of node-build's definition files on the branch from which asdf-nodejs updates node-build before an install.
|
|
5
|
+
* A version is installable through asdf once a file named for it exists here.
|
|
6
|
+
*/
|
|
7
|
+
export declare const NODE_BUILD_DEFINITION_URL = "https://raw.githubusercontent.com/nodenv/node-build/main/share/node-build/";
|
|
3
8
|
/** Node's release index, which lists every release with its version and its UTC release day. */
|
|
4
9
|
export declare const NODE_RELEASE_INDEX_URL = "https://nodejs.org/dist/index.json";
|
|
5
10
|
export interface CheckNodePinOptions {
|
|
6
11
|
/** Returns the parsed release index. Injected by tests; defaults to fetching `NODE_RELEASE_INDEX_URL`. */
|
|
7
12
|
fetchReleases?: () => Promise<unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Reports whether node-build defines a version. Injected by tests; defaults to requesting the version's file under
|
|
15
|
+
* `NODE_BUILD_DEFINITION_URL`.
|
|
16
|
+
*/
|
|
17
|
+
isNodeBuildDefined?: (version: string) => Promise<boolean>;
|
|
8
18
|
/** Suppresses the report, but not the warnings. */
|
|
9
19
|
isSilent: boolean;
|
|
10
20
|
/** The current time, against which the soak is measured. Injected by tests. */
|
|
@@ -16,15 +26,15 @@ export interface CheckNodePinOptions {
|
|
|
16
26
|
}
|
|
17
27
|
/**
|
|
18
28
|
* Reports the newest release in the major line of the `nodejs` pin in `dir`'s `.tool-versions` that is newer than
|
|
19
|
-
* the pin
|
|
29
|
+
* the pin, at least 24 hours old, and defined by node-build, and rewrites the pin to it when `shouldWrite` is set.
|
|
30
|
+
* Reports the newest newer release that node-build does not define yet as held back.
|
|
20
31
|
*
|
|
21
32
|
* Reports nothing when the file or its `nodejs` entry is absent, or when the pin is current. Every failure is a
|
|
22
33
|
* warning, so that the check never fails the upgrade that runs it.
|
|
23
34
|
*/
|
|
24
35
|
export declare function checkNodePin(dir: string, options: CheckNodePinOptions): Promise<void>;
|
|
25
36
|
/**
|
|
26
|
-
* Returns
|
|
27
|
-
*
|
|
28
|
-
* a `YYYY-MM-DD` date.
|
|
37
|
+
* Returns every plain `X.Y.Z` release in `pin`'s major line that is newer than `pin` and whose UTC release day began
|
|
38
|
+
* at least two days before `now`, newest first. Ignores an entry that lacks a plain version or a `YYYY-MM-DD` date.
|
|
29
39
|
*/
|
|
30
|
-
export declare function
|
|
40
|
+
export declare function selectNodeUpgradeCandidates(pin: string, releases: readonly unknown[], now: Date): string[];
|
|
@@ -4,6 +4,7 @@ import process from 'node:process';
|
|
|
4
4
|
import { formatGlyphLine, isRecord } from '@williamthorsen/nmr-core';
|
|
5
5
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
6
6
|
import { NMR_GLYPHS } from "../glyphs.js";
|
|
7
|
+
export const NODE_BUILD_DEFINITION_URL = 'https://raw.githubusercontent.com/nodenv/node-build/main/share/node-build/';
|
|
7
8
|
export const NODE_RELEASE_INDEX_URL = 'https://nodejs.org/dist/index.json';
|
|
8
9
|
const TOOL_VERSIONS_FILE = '.tool-versions';
|
|
9
10
|
const SOAK_MS = 2 * 24 * 60 * 60 * 1000;
|
|
@@ -51,13 +52,30 @@ export async function checkNodePin(dir, options) {
|
|
|
51
52
|
warn(`${NODE_RELEASE_INDEX_URL} did not return a list of releases`);
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
|
-
const
|
|
55
|
-
|
|
55
|
+
const candidates = selectNodeUpgradeCandidates(pin.version, releases, options.now ?? new Date());
|
|
56
|
+
let target;
|
|
57
|
+
try {
|
|
58
|
+
target = await findNewestDefined(candidates, options.isNodeBuildDefined ?? requestNodeBuildDefinition);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
warn(`${NODE_BUILD_DEFINITION_URL} is unreachable (${describeError(error)})`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const heldBack = candidates[0] === target ? undefined : candidates[0];
|
|
65
|
+
function reportHeldBack() {
|
|
66
|
+
if (heldBack !== undefined && !options.isSilent) {
|
|
67
|
+
report(`nodejs ${heldBack} is held back: node-build does not define it yet.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (target === undefined) {
|
|
71
|
+
reportHeldBack();
|
|
56
72
|
return;
|
|
73
|
+
}
|
|
57
74
|
if (!options.shouldWrite) {
|
|
58
75
|
if (!options.isSilent) {
|
|
59
76
|
report(`nodejs ${pin.version} → ${target} is available. Run with --write to update ${TOOL_VERSIONS_FILE}.`);
|
|
60
77
|
}
|
|
78
|
+
reportHeldBack();
|
|
61
79
|
return;
|
|
62
80
|
}
|
|
63
81
|
const updated = content.slice(0, pin.start) + target + content.slice(pin.start + pin.version.length);
|
|
@@ -70,11 +88,11 @@ export async function checkNodePin(dir, options) {
|
|
|
70
88
|
}
|
|
71
89
|
if (!options.isSilent)
|
|
72
90
|
report(`Updated nodejs ${pin.version} → ${target} in ${TOOL_VERSIONS_FILE}.`);
|
|
91
|
+
reportHeldBack();
|
|
73
92
|
}
|
|
74
|
-
export function
|
|
93
|
+
export function selectNodeUpgradeCandidates(pin, releases, now) {
|
|
75
94
|
const pinParts = parseVersion(pin);
|
|
76
|
-
|
|
77
|
-
let bestVersion;
|
|
95
|
+
const candidates = [];
|
|
78
96
|
for (const release of releases) {
|
|
79
97
|
if (!isRecord(release))
|
|
80
98
|
continue;
|
|
@@ -89,12 +107,9 @@ export function selectNodeUpgrade(pin, releases, now) {
|
|
|
89
107
|
continue;
|
|
90
108
|
if (Date.parse(`${date}T00:00:00Z`) + SOAK_MS > now.getTime())
|
|
91
109
|
continue;
|
|
92
|
-
|
|
93
|
-
continue;
|
|
94
|
-
best = parts;
|
|
95
|
-
bestVersion = bareVersion;
|
|
110
|
+
candidates.push({ parts, version: bareVersion });
|
|
96
111
|
}
|
|
97
|
-
return
|
|
112
|
+
return candidates.toSorted((a, b) => compareVersions(b.parts, a.parts)).map((candidate) => candidate.version);
|
|
98
113
|
}
|
|
99
114
|
function compareVersions(a, b) {
|
|
100
115
|
for (let index = 0; index < 3; index++) {
|
|
@@ -111,6 +126,13 @@ async function fetchNodeReleaseIndex() {
|
|
|
111
126
|
const body = await response.json();
|
|
112
127
|
return body;
|
|
113
128
|
}
|
|
129
|
+
async function findNewestDefined(candidates, isDefined) {
|
|
130
|
+
for (const candidate of candidates) {
|
|
131
|
+
if (await isDefined(candidate))
|
|
132
|
+
return candidate;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
114
136
|
function findNodePin(content) {
|
|
115
137
|
const match = NODEJS_ENTRY_PATTERN.exec(content);
|
|
116
138
|
const version = match?.[1];
|
|
@@ -121,6 +143,17 @@ function findNodePin(content) {
|
|
|
121
143
|
function isMissingFileError(error) {
|
|
122
144
|
return isRecord(error) && error['code'] === 'ENOENT';
|
|
123
145
|
}
|
|
146
|
+
async function requestNodeBuildDefinition(version) {
|
|
147
|
+
const response = await fetch(`${NODE_BUILD_DEFINITION_URL}${version}`, {
|
|
148
|
+
method: 'HEAD',
|
|
149
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
150
|
+
});
|
|
151
|
+
if (response.status === 404)
|
|
152
|
+
return false;
|
|
153
|
+
if (response.status !== 200)
|
|
154
|
+
throw new Error(`HTTP ${response.status}`);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
124
157
|
function parseVersion(version) {
|
|
125
158
|
return version.split('.').map(Number);
|
|
126
159
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** The fix for a broken lookup, whose cause can name the manifest of the resolving package as well as the tool's. */
|
|
2
|
+
export declare const BROKEN_MANIFEST_FIX = "Repair that manifest, or reinstall the package that contains it.";
|
|
3
|
+
/**
|
|
4
|
+
* Looks up the manifest of `packageName` as `require` from `parent` resolves it, a file path or a file URL.
|
|
5
|
+
*
|
|
6
|
+
* Only `MODULE_NOT_FOUND` means that the package is absent: Node rejects an installed package whose manifest is
|
|
7
|
+
* malformed or unreadable with another code, and the message of that error, which `cause` repeats, names the
|
|
8
|
+
* manifest at fault. A failure to read or parse the manifest after resolution prefixes `cause` with its path.
|
|
9
|
+
* `cause` does not end with a period, so that a caller can end the sentence that it composes around it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findPackageManifest(parent: string, packageName: string): ManifestLookup;
|
|
12
|
+
/** What a lookup of a package's manifest found: the parsed manifest, no installed package, or a broken one. */
|
|
13
|
+
export type ManifestLookup = {
|
|
14
|
+
kind: 'found';
|
|
15
|
+
manifestPath: string;
|
|
16
|
+
manifest: unknown;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'absent';
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'broken';
|
|
21
|
+
cause: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns the absolute path of the CLI that the manifest at `manifestPath` declares for `packageName`, or `undefined`
|
|
25
|
+
* when it does not declare one. `bin` may be a bare path, which names the package's lone CLI, or a map keyed by
|
|
26
|
+
* command name.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveDeclaredCli(manifest: unknown, manifestPath: string, packageName: string): string | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { hasErrnoCode, isRecord } from '@williamthorsen/nmr-core';
|
|
5
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
6
|
+
export const BROKEN_MANIFEST_FIX = 'Repair that manifest, or reinstall the package that contains it.';
|
|
7
|
+
export function findPackageManifest(parent, packageName) {
|
|
8
|
+
let manifestPath;
|
|
9
|
+
try {
|
|
10
|
+
manifestPath = createRequire(parent).resolve(`${packageName}/package.json`);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (hasErrnoCode(error, 'MODULE_NOT_FOUND'))
|
|
14
|
+
return { kind: 'absent' };
|
|
15
|
+
return { kind: 'broken', cause: describeCause(error) };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const source = readFileSync(manifestPath, 'utf8').replace(/^\u{FEFF}/u, '');
|
|
19
|
+
return { kind: 'found', manifestPath, manifest: JSON.parse(source) };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return { kind: 'broken', cause: `${manifestPath}: ${describeCause(error)}` };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function resolveDeclaredCli(manifest, manifestPath, packageName) {
|
|
26
|
+
const declaredBin = isRecord(manifest) ? manifest['bin'] : undefined;
|
|
27
|
+
const bin = isRecord(declaredBin) ? declaredBin[packageName] : declaredBin;
|
|
28
|
+
return typeof bin === 'string' ? path.resolve(path.dirname(manifestPath), bin) : undefined;
|
|
29
|
+
}
|
|
30
|
+
function describeCause(error) {
|
|
31
|
+
return describeError(error).replace(/\.$/, '');
|
|
32
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** The pair of directories through which a build publishes, both siblings of the emit directory. */
|
|
2
|
+
export interface ScratchDirs {
|
|
3
|
+
previousDir: string;
|
|
4
|
+
stagingDir: string;
|
|
5
|
+
}
|
|
6
|
+
/** One file emitted by the compiler, kept in memory until the whole emit is ready to publish. */
|
|
7
|
+
export interface StagedFile {
|
|
8
|
+
text: string;
|
|
9
|
+
writeByteOrderMark: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Removes both scratch directories of `emitDir` without failing the caller. `force` suppresses only a missing
|
|
13
|
+
* top-level path and `rm` does not retry, so a removal racing another build's leaves `ENOTEMPTY`, `EBUSY`, and
|
|
14
|
+
* `EPERM` live.
|
|
15
|
+
*/
|
|
16
|
+
export declare function discardScratchDirs(emitDir: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Publishes the staged emit to `emitDir` atomically: It writes the files to a staging directory and swaps that
|
|
19
|
+
* into place by rename. Returns how many files it published.
|
|
20
|
+
*
|
|
21
|
+
* An emit that produces nothing removes `emitDir` instead, because swapping an empty directory into place would
|
|
22
|
+
* leave a `dist` behind for a package whose entry points do not emit any output.
|
|
23
|
+
*
|
|
24
|
+
* A failure while staging precedes the first rename, so it leaves the previous output exactly as it was.
|
|
25
|
+
*/
|
|
26
|
+
export declare function publishOutput(emitDir: string, stagedFiles: Map<string, StagedFile>): Promise<number>;
|
|
27
|
+
/** Removes both scratch directories of `emitDir`, tolerating their absence. */
|
|
28
|
+
export declare function removeScratchDirs(emitDir: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Resolves the two scratch directories through which a build publishes: `staging`, which the emit is written to,
|
|
31
|
+
* and `previous`, which the outgoing output is renamed aside to. Both are siblings of the emit directory, so a
|
|
32
|
+
* rename between them never crosses a filesystem, and both are dot-prefixed so that a leftover stays out of the
|
|
33
|
+
* globs that select sources.
|
|
34
|
+
*
|
|
35
|
+
* The names are fixed rather than unique because the build removes both before use. A unique name would need a
|
|
36
|
+
* sweep to accomplish that removal, and a sweep cannot tell a directory orphaned by a killed run from one that a
|
|
37
|
+
* concurrent build is still writing. Because a directory left behind by a failed build is cleared the same way,
|
|
38
|
+
* the failure path does not need any cleanup of its own.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveScratchDirs(emitDir: string): ScratchDirs;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { rename, rm } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
5
|
+
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
6
|
+
import { getCompiler } from "./compiler.js";
|
|
7
|
+
import { isWithin } from "./rewrite-specifiers.js";
|
|
8
|
+
export async function discardScratchDirs(emitDir) {
|
|
9
|
+
try {
|
|
10
|
+
await removeScratchDirs(emitDir);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function publishOutput(emitDir, stagedFiles) {
|
|
16
|
+
if (stagedFiles.size === 0) {
|
|
17
|
+
await rm(emitDir, { force: true, recursive: true });
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
writeStagedOutput(stagedFiles, emitDir);
|
|
21
|
+
await swapIntoPlace(emitDir);
|
|
22
|
+
return stagedFiles.size;
|
|
23
|
+
}
|
|
24
|
+
export async function removeScratchDirs(emitDir) {
|
|
25
|
+
const { previousDir, stagingDir } = resolveScratchDirs(emitDir);
|
|
26
|
+
await rm(previousDir, { force: true, recursive: true });
|
|
27
|
+
await rm(stagingDir, { force: true, recursive: true });
|
|
28
|
+
}
|
|
29
|
+
export function resolveScratchDirs(emitDir) {
|
|
30
|
+
const parent = path.dirname(emitDir);
|
|
31
|
+
const name = path.basename(emitDir);
|
|
32
|
+
return {
|
|
33
|
+
previousDir: path.join(parent, `.${name}.previous`),
|
|
34
|
+
stagingDir: path.join(parent, `.${name}.staging`),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function swapIntoPlace(emitDir) {
|
|
38
|
+
const { previousDir, stagingDir } = resolveScratchDirs(emitDir);
|
|
39
|
+
const hadPreviousOutput = existsSync(emitDir);
|
|
40
|
+
if (hadPreviousOutput) {
|
|
41
|
+
await rename(emitDir, previousDir);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await rename(stagingDir, emitDir);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (hadPreviousOutput && !existsSync(emitDir)) {
|
|
48
|
+
await rename(previousDir, emitDir);
|
|
49
|
+
}
|
|
50
|
+
throw new UserError(`failed to move the built output into ${emitDir}: ${describeError(error)}`, {
|
|
51
|
+
cause: error,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
await discardScratchDirs(emitDir);
|
|
55
|
+
}
|
|
56
|
+
function writeStagedOutput(stagedFiles, emitDir) {
|
|
57
|
+
const ts = getCompiler();
|
|
58
|
+
const { stagingDir } = resolveScratchDirs(emitDir);
|
|
59
|
+
for (const [fileName, file] of stagedFiles) {
|
|
60
|
+
if (!isWithin(emitDir, fileName)) {
|
|
61
|
+
throw new Error(`the compiler emitted ${fileName}, which is outside the output directory ${emitDir}. ` +
|
|
62
|
+
"Verify the resolved tsconfig's 'outDir' and 'declarationDir'.");
|
|
63
|
+
}
|
|
64
|
+
ts.sys.writeFile(path.join(stagingDir, path.relative(emitDir, fileName)), file.text, file.writeByteOrderMark);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -8,3 +8,8 @@ import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
|
8
8
|
* pass runs, to keep the reader from taking that result to mean that every dependency is up to date.
|
|
9
9
|
*/
|
|
10
10
|
export declare function reportCatalog(cwd: string, style: OutputStyle): void;
|
|
11
|
+
/**
|
|
12
|
+
* Runs `nmr-report-catalog` against `argv` from `cwd` and returns the exit code to report. The command takes neither
|
|
13
|
+
* an option nor an operand, so anything but `--help` is a usage error and reports nothing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runReportCatalog(argv: string[], cwd: string): Promise<number>;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
1
2
|
import { formatGlyphLine } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { defineCommand, runCli } from '@williamthorsen/nmr-core/cli';
|
|
4
|
+
import { NMR_REPORT_CATALOG_BIN } from "../bins.js";
|
|
2
5
|
import { findContainingPackageDir } from "../context.js";
|
|
3
6
|
import { NMR_GLYPHS } from "../glyphs.js";
|
|
4
7
|
import { DEPENDENCY_FIELDS, readPackageJson } from "../helpers/package-json.js";
|
|
5
8
|
import { reportClosing } from "../helpers/reportClosing.js";
|
|
9
|
+
import { resolveBinStyles } from "../resolveBinStyles.js";
|
|
6
10
|
import { findMonorepoRoot, getWorkspacePackageDirs } from "../workspace.js";
|
|
7
11
|
const CATALOG_PROTOCOL = 'catalog:';
|
|
8
12
|
export function reportCatalog(cwd, style) {
|
|
@@ -21,6 +25,21 @@ export function reportCatalog(cwd, style) {
|
|
|
21
25
|
}
|
|
22
26
|
reportClosing(formatGlyphLine(NMR_GLYPHS, style, 'catalog', describeCatalog(dependencies.length, monorepoRoot)), console.warn);
|
|
23
27
|
}
|
|
28
|
+
export async function runReportCatalog(argv, cwd) {
|
|
29
|
+
const command = defineCommand({
|
|
30
|
+
summary: 'Report the catalogued dependencies that a package-scoped upgrade does not read',
|
|
31
|
+
run: () => {
|
|
32
|
+
reportCatalog(cwd, resolveBinStyles().stderr);
|
|
33
|
+
return 0;
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
return runCli(argv, command, {
|
|
37
|
+
name: NMR_REPORT_CATALOG_BIN,
|
|
38
|
+
stdout: process.stdout,
|
|
39
|
+
stderr: process.stderr,
|
|
40
|
+
baseDir: cwd,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
24
43
|
function describeCatalog(count, monorepoRoot) {
|
|
25
44
|
const subject = count === 1 ? '1 catalogued dependency went' : `${count} catalogued dependencies went`;
|
|
26
45
|
const object = count === 1 ? 'it' : 'them';
|
|
@@ -4,3 +4,8 @@ import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
|
4
4
|
* `pnpm.overrides` block left behind in the root `package.json`.
|
|
5
5
|
*/
|
|
6
6
|
export declare function reportOverrides(monorepoRoot: string, style: OutputStyle): void;
|
|
7
|
+
/**
|
|
8
|
+
* Runs `nmr-report-overrides` against `argv` for the monorepo containing `cwd` and returns the exit code to report.
|
|
9
|
+
* The command takes neither an option nor an operand, so anything but `--help` is a usage error and reports nothing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runReportOverrides(argv: string[], cwd: string): Promise<number>;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
1
2
|
import { formatGlyphLine, UserError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { defineCommand, runCli } from '@williamthorsen/nmr-core/cli';
|
|
4
|
+
import { NMR_REPORT_OVERRIDES_BIN } from "../bins.js";
|
|
2
5
|
import { NMR_GLYPHS } from "../glyphs.js";
|
|
3
6
|
import { getPnpmOverrides, readPackageJson } from "../helpers/package-json.js";
|
|
4
7
|
import { reportClosing } from "../helpers/reportClosing.js";
|
|
5
|
-
import {
|
|
8
|
+
import { resolveBinStyles } from "../resolveBinStyles.js";
|
|
9
|
+
import { findMonorepoRoot, readWorkspaceOverrides } from "../workspace.js";
|
|
6
10
|
export function reportOverrides(monorepoRoot, style) {
|
|
7
11
|
const declaredOverrides = listEntries(readWorkspaceOverrides(monorepoRoot));
|
|
8
12
|
if (declaredOverrides.length > 0) {
|
|
@@ -14,6 +18,21 @@ export function reportOverrides(monorepoRoot, style) {
|
|
|
14
18
|
}
|
|
15
19
|
rejectLegacyOverrides(monorepoRoot);
|
|
16
20
|
}
|
|
21
|
+
export async function runReportOverrides(argv, cwd) {
|
|
22
|
+
const command = defineCommand({
|
|
23
|
+
summary: 'Report the pnpm overrides that the monorepo declares',
|
|
24
|
+
run: () => {
|
|
25
|
+
reportOverrides(findMonorepoRoot(cwd), resolveBinStyles().stderr);
|
|
26
|
+
return 0;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
return runCli(argv, command, {
|
|
30
|
+
name: NMR_REPORT_OVERRIDES_BIN,
|
|
31
|
+
stdout: process.stdout,
|
|
32
|
+
stderr: process.stderr,
|
|
33
|
+
baseDir: cwd,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
17
36
|
function describeOverrides(count) {
|
|
18
37
|
return count === 1
|
|
19
38
|
? '1 override is active. Check whether it is still needed.'
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
4
2
|
import path from 'node:path';
|
|
5
3
|
import process from 'node:process';
|
|
6
|
-
import {
|
|
4
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
7
5
|
import { findMonorepoRoot as findMonorepoRootOrNothing } from '@williamthorsen/nmr-core/workspace';
|
|
8
6
|
import { NMR_VITEST_BIN } from "../bins.js";
|
|
9
|
-
import { describeSignalExitCode } from "../child-process.js";
|
|
10
7
|
import { omitOutcomeFile } from "../stated-outcome.js";
|
|
8
|
+
import { BROKEN_MANIFEST_FIX, findPackageManifest, resolveDeclaredCli } from "./package-manifest.js";
|
|
9
|
+
import { resolveInheritedExitCode, spawnInherited } from "./spawn-inherited.js";
|
|
11
10
|
const VITEST_PACKAGE = 'vitest';
|
|
12
11
|
const SUPPORTED_VITEST_RANGE = '>=4.0.0 <6';
|
|
13
12
|
const CONFIG_EXTENSIONS = ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs'];
|
|
@@ -17,18 +16,15 @@ export function runNmrVitest(argv, cwd = process.cwd()) {
|
|
|
17
16
|
reportError(NMR_VITEST_BIN, cli.error);
|
|
18
17
|
return 1;
|
|
19
18
|
}
|
|
20
|
-
const result =
|
|
19
|
+
const result = spawnInherited(process.execPath, [cli.cliPath, ...chooseVitestConfigArgs(argv, cwd)], {
|
|
21
20
|
cwd,
|
|
22
21
|
env: omitOutcomeFile(process.env),
|
|
23
|
-
stdio: 'inherit',
|
|
24
22
|
});
|
|
25
23
|
if (result.error) {
|
|
26
24
|
reportError(NMR_VITEST_BIN, `could not run \`${VITEST_PACKAGE}\` (${result.error.message}).`);
|
|
27
25
|
return 1;
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
return describeSignalExitCode(result.signal);
|
|
31
|
-
return result.status ?? 1;
|
|
27
|
+
return resolveInheritedExitCode(result);
|
|
32
28
|
}
|
|
33
29
|
export function chooseVitestConfigArgs(argv, cwd) {
|
|
34
30
|
if (namesConfig(argv))
|
|
@@ -65,17 +61,22 @@ function resolveVitestCli(cwd) {
|
|
|
65
61
|
ok: false,
|
|
66
62
|
error: `requires Vitest ${SUPPORTED_VITEST_RANGE}, but \`${VITEST_PACKAGE}\` could not be resolved from ${cwd}. Install a version in that range as the \`${VITEST_PACKAGE}\` peer dependency.`,
|
|
67
63
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
manifestPath = createRequire(path.join(cwd, 'package.json')).resolve(`${VITEST_PACKAGE}/package.json`);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
64
|
+
const lookup = findPackageManifest(path.join(cwd, 'package.json'), VITEST_PACKAGE);
|
|
65
|
+
if (lookup.kind === 'absent')
|
|
73
66
|
return missingResult;
|
|
67
|
+
if (lookup.kind === 'broken') {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
error: `\`${VITEST_PACKAGE}\` could not be loaded from ${cwd}: ${lookup.cause}. ${BROKEN_MANIFEST_FIX}`,
|
|
71
|
+
};
|
|
74
72
|
}
|
|
75
|
-
const manifest =
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
return {
|
|
73
|
+
const { manifest, manifestPath } = lookup;
|
|
74
|
+
const cliPath = resolveDeclaredCli(manifest, manifestPath, VITEST_PACKAGE);
|
|
75
|
+
if (cliPath === undefined) {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
error: `\`${VITEST_PACKAGE}\` at ${path.dirname(manifestPath)} does not declare a CLI. Reinstall the \`${VITEST_PACKAGE}\` peer dependency.`,
|
|
79
|
+
};
|
|
79
80
|
}
|
|
80
|
-
return { ok: true, cliPath
|
|
81
|
+
return { ok: true, cliPath };
|
|
81
82
|
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
+
import { type SpawnInherited } from './spawn-inherited.js';
|
|
1
2
|
import { type WorkflowTool } from './workflow-tools.js';
|
|
2
|
-
/** How a spawned shellcheck ended. */
|
|
3
|
-
export interface ShellcheckOutcome {
|
|
4
|
-
error?: Error | undefined;
|
|
5
|
-
signal: NodeJS.Signals | null;
|
|
6
|
-
status: number | null;
|
|
7
|
-
}
|
|
8
3
|
export interface RunShellcheckOptions {
|
|
9
4
|
/** Directories under which the pinned tools are cached, in lookup order; defaults to `resolveToolCacheRoots`. */
|
|
10
5
|
cacheRoots?: readonly [string, ...string[]];
|
|
11
6
|
ensure?: (tool: WorkflowTool, cacheRoots: readonly [string, ...string[]]) => Promise<string>;
|
|
12
7
|
/** Lists the tracked `*.sh` files under the root, relative to it. */
|
|
13
8
|
listFiles?: (monorepoRoot: string) => string[];
|
|
14
|
-
spawn?:
|
|
9
|
+
spawn?: SpawnInherited;
|
|
15
10
|
}
|
|
16
11
|
/**
|
|
17
12
|
* Lints the monorepo's tracked shell scripts with the pinned shellcheck and returns its exit status. Trailing
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { runGit, UserError } from '@williamthorsen/nmr-core';
|
|
5
|
-
import {
|
|
4
|
+
import { resolveInheritedExitCode, spawnInherited } from "./spawn-inherited.js";
|
|
6
5
|
import { ensureTool, resolveToolCacheRoots } from "./workflow-tools.js";
|
|
7
6
|
export async function runShellcheck(monorepoRoot, args, options = {}) {
|
|
8
7
|
const shellcheckArgs = args.length > 0 ? [...args] : listPresentScripts(monorepoRoot, options.listFiles);
|
|
@@ -13,14 +12,11 @@ export async function runShellcheck(monorepoRoot, args, options = {}) {
|
|
|
13
12
|
const cacheRoots = options.cacheRoots ?? resolveToolCacheRoots(monorepoRoot);
|
|
14
13
|
const ensure = options.ensure ?? ((tool, roots) => ensureTool(tool, { cacheRoots: roots }));
|
|
15
14
|
const shellcheckPath = await ensure('shellcheck', cacheRoots);
|
|
16
|
-
const outcome = (options.spawn ?? spawnInherited)(shellcheckPath, shellcheckArgs, monorepoRoot);
|
|
15
|
+
const outcome = (options.spawn ?? spawnInherited)(shellcheckPath, shellcheckArgs, { cwd: monorepoRoot });
|
|
17
16
|
if (outcome.error !== undefined) {
|
|
18
17
|
throw new UserError(`Cannot run shellcheck: ${outcome.error.message}`, { cause: outcome.error });
|
|
19
18
|
}
|
|
20
|
-
|
|
21
|
-
return describeSignalExitCode(outcome.signal);
|
|
22
|
-
}
|
|
23
|
-
return outcome.status ?? 1;
|
|
19
|
+
return resolveInheritedExitCode(outcome);
|
|
24
20
|
}
|
|
25
21
|
export function listTrackedScripts(monorepoRoot) {
|
|
26
22
|
const result = runGit(['ls-files', '-z', '--', '*.sh'], monorepoRoot);
|
|
@@ -32,7 +28,3 @@ export function listTrackedScripts(monorepoRoot) {
|
|
|
32
28
|
function listPresentScripts(monorepoRoot, listFiles = listTrackedScripts) {
|
|
33
29
|
return listFiles(monorepoRoot).filter((file) => fs.existsSync(path.join(monorepoRoot, file)));
|
|
34
30
|
}
|
|
35
|
-
function spawnInherited(file, args, cwd) {
|
|
36
|
-
const result = spawnSync(file, args, { cwd, stdio: 'inherit' });
|
|
37
|
-
return { error: result.error, signal: result.signal, status: result.status };
|
|
38
|
-
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** How a program spawned on the caller's stdio ended. */
|
|
2
|
+
export interface InheritedOutcome {
|
|
3
|
+
error?: Error | undefined;
|
|
4
|
+
signal: NodeJS.Signals | null;
|
|
5
|
+
status: number | null;
|
|
6
|
+
}
|
|
7
|
+
export interface SpawnInheritedOptions {
|
|
8
|
+
/** Directory in which the program runs. Defaults to the current one. */
|
|
9
|
+
cwd?: string;
|
|
10
|
+
/** Environment for the program. Defaults to `process.env`. */
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
}
|
|
13
|
+
/** A function that runs a program on the caller's stdio, the shape of each command's injectable spawn. */
|
|
14
|
+
export type SpawnInherited = typeof spawnInherited;
|
|
15
|
+
/** Returns the exit code that reports how the program ended: the shell's code for a death by signal, else its status. */
|
|
16
|
+
export declare function resolveInheritedExitCode({ signal, status }: Pick<InheritedOutcome, 'signal' | 'status'>): number;
|
|
17
|
+
/**
|
|
18
|
+
* Runs a program on the caller's stdio, so that it sees the caller's terminal: It colors its output, renders
|
|
19
|
+
* progress, and reads input as it would when run directly.
|
|
20
|
+
*/
|
|
21
|
+
export declare function spawnInherited(file: string, args: readonly string[], options?: SpawnInheritedOptions): InheritedOutcome;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { describeSignalExitCode } from "../child-process.js";
|
|
3
|
+
export function resolveInheritedExitCode({ signal, status }) {
|
|
4
|
+
if (signal !== null)
|
|
5
|
+
return describeSignalExitCode(signal);
|
|
6
|
+
return status ?? 1;
|
|
7
|
+
}
|
|
8
|
+
export function spawnInherited(file, args, options = {}) {
|
|
9
|
+
const result = spawnSync(file, args, { cwd: options.cwd, env: options.env, stdio: 'inherit' });
|
|
10
|
+
return { error: result.error, signal: result.signal, status: result.status };
|
|
11
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
2
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { type CheckNodePinOptions } from './node-pin.js';
|
|
4
|
-
|
|
5
|
-
status: number | null;
|
|
6
|
-
error?: Error | undefined;
|
|
7
|
-
}
|
|
4
|
+
import { type SpawnInherited } from './spawn-inherited.js';
|
|
8
5
|
export interface RunTazeOptions {
|
|
9
6
|
/** Checks the `.tool-versions` Node pin in a directory. Injected by tests. */
|
|
10
7
|
checkNodePin?: (dir: string, options: CheckNodePinOptions) => Promise<void>;
|
|
@@ -12,8 +9,11 @@ export interface RunTazeOptions {
|
|
|
12
9
|
loadConfig?: (cwd: string) => Promise<unknown>;
|
|
13
10
|
/** Resolves the absolute path of taze's CLI entry. Injected by tests. */
|
|
14
11
|
resolveCliPath?: () => string;
|
|
15
|
-
/**
|
|
16
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Runs the CLI on the caller's stdio, so that taze's progress rendering, cursor restore, and `--interactive` mode
|
|
14
|
+
* see the caller's TTY. Injected by tests.
|
|
15
|
+
*/
|
|
16
|
+
spawn?: SpawnInherited;
|
|
17
17
|
/** Stream that error output and pass headings are written to. Defaults to `process.stderr`. */
|
|
18
18
|
stderr?: Writable;
|
|
19
19
|
/** Style of `stderr`, in which the Node pin check reports. Defaults to `'plain'`. */
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import process from 'node:process';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
@@ -7,6 +6,7 @@ import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
|
7
6
|
import { NMR_TAZE_BIN } from "../bins.js";
|
|
8
7
|
import { loadPackageMaturityPeriod } from "./load-package-maturity-period.js";
|
|
9
8
|
import { checkNodePin as checkNodePinDefault } from "./node-pin.js";
|
|
9
|
+
import { resolveInheritedExitCode, spawnInherited } from "./spawn-inherited.js";
|
|
10
10
|
import { isSilent, readFlag } from "./taze-args.js";
|
|
11
11
|
import { planTazePasses } from "./taze-passes.js";
|
|
12
12
|
const TAZE_CLI_SPECIFIER = 'taze/cli';
|
|
@@ -17,7 +17,7 @@ const NODE_PIN_SKIP_FLAGS = ['--no-node-version', '--help', '-h', '--version', '
|
|
|
17
17
|
export async function runTaze(args, options = {}) {
|
|
18
18
|
const stderr = options.stderr ?? process.stderr;
|
|
19
19
|
const resolveCliPath = options.resolveCliPath ?? resolveTazeCliPath;
|
|
20
|
-
const spawn = options.spawn ??
|
|
20
|
+
const spawn = options.spawn ?? spawnInherited;
|
|
21
21
|
const loadConfig = options.loadConfig ?? loadPackageMaturityPeriod;
|
|
22
22
|
let cliPath;
|
|
23
23
|
try {
|
|
@@ -59,7 +59,7 @@ export async function runTaze(args, options = {}) {
|
|
|
59
59
|
reportError(NMR_TAZE_BIN, `Failed to run taze: ${outcome.error.message}`, stderr);
|
|
60
60
|
return 1;
|
|
61
61
|
}
|
|
62
|
-
const status = outcome
|
|
62
|
+
const status = resolveInheritedExitCode(outcome);
|
|
63
63
|
if (exitCode === 0)
|
|
64
64
|
exitCode = status;
|
|
65
65
|
}
|
|
@@ -74,7 +74,3 @@ function composeRequestTimeoutArgs(args) {
|
|
|
74
74
|
function resolveConfigDir(args) {
|
|
75
75
|
return path.resolve(readFlag(args, ['--cwd', '-C'])?.value ?? '.');
|
|
76
76
|
}
|
|
77
|
-
function spawnNode(nodePath, argv) {
|
|
78
|
-
const result = spawnSync(nodePath, argv, { stdio: 'inherit' });
|
|
79
|
-
return { status: result.status, error: result.error };
|
|
80
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { assertResolvableCheckCacheCommands } from "./config-check-cache.js";
|
|
2
|
+
import { assertResolvableCheckCacheCommands } from "./check-cache/config-check-cache.js";
|
|
3
3
|
import { findUnexpressibleToken } from "./steps.js";
|
|
4
4
|
import { formatVerbosityRejection, isCommandVerbosity } from "./verbosity.js";
|
|
5
5
|
export const CONFIG_TIERS = {
|