@williamthorsen/nmr 2.0.1 → 2.0.3
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 +107 -0
- package/.readyup/kits/default.js +41 -5
- package/.readyup/manifest.json +14 -9
- package/CHANGELOG.md +30 -0
- package/README.md +2 -8
- package/dist/esm/{check-cache-build-output.d.ts → check-cache/check-cache-build-output.d.ts} +1 -1
- package/dist/esm/{check-cache-build-output.js → check-cache/check-cache-build-output.js} +6 -6
- package/dist/esm/{check-cache-delegation.d.ts → check-cache/check-cache-delegation.d.ts} +3 -3
- package/dist/esm/{check-cache-delegation.js → check-cache/check-cache-delegation.js} +2 -2
- package/dist/esm/{check-cache-entries.js → check-cache/check-cache-entries.js} +1 -1
- package/dist/esm/{check-cache-gate.d.ts → check-cache/check-cache-gate.d.ts} +5 -4
- package/dist/esm/{check-cache-gate.js → check-cache/check-cache-gate.js} +10 -10
- package/dist/esm/{check-cache-key.d.ts → check-cache/check-cache-key.d.ts} +1 -1
- package/dist/esm/{check-cache-record.d.ts → check-cache/check-cache-record.d.ts} +6 -5
- package/dist/esm/{check-cache-record.js → check-cache/check-cache-record.js} +7 -6
- package/dist/esm/{check-cache.d.ts → check-cache/check-cache.d.ts} +10 -2
- package/dist/esm/{check-cache.js → check-cache/check-cache.js} +7 -4
- package/dist/esm/{config-check-cache.d.ts → check-cache/config-check-cache.d.ts} +1 -1
- package/dist/esm/{config-check-cache.js → check-cache/config-check-cache.js} +4 -4
- package/dist/esm/{pass-manifest.d.ts → check-cache/pass-manifest.d.ts} +1 -1
- package/dist/esm/{pass-manifest.js → check-cache/pass-manifest.js} +1 -1
- package/dist/esm/{recording.js → check-cache/recording.js} +2 -2
- package/dist/esm/{replay-assembly.d.ts → check-cache/replay-assembly.d.ts} +1 -1
- package/dist/esm/{replay-assembly.js → check-cache/replay-assembly.js} +2 -2
- package/dist/esm/child-env.d.ts +1 -1
- package/dist/esm/child-env.js +2 -2
- package/dist/esm/commands/actionlint.d.ts +2 -7
- package/dist/esm/commands/actionlint.js +5 -11
- package/dist/esm/commands/build-output.d.ts +2 -1
- package/dist/esm/commands/clean.js +1 -1
- package/dist/esm/commands/fmt-cache.js +3 -2
- package/dist/esm/commands/fmt.d.ts +6 -13
- package/dist/esm/commands/fmt.js +47 -30
- package/dist/esm/commands/node-pin.d.ts +15 -5
- package/dist/esm/commands/node-pin.js +43 -10
- package/dist/esm/commands/package-manifest.d.ts +28 -0
- package/dist/esm/commands/package-manifest.js +32 -0
- package/dist/esm/commands/run-vitest.js +21 -20
- package/dist/esm/commands/shellcheck.d.ts +2 -7
- package/dist/esm/commands/shellcheck.js +3 -11
- package/dist/esm/commands/spawn-inherited.d.ts +21 -0
- package/dist/esm/commands/spawn-inherited.js +11 -0
- package/dist/esm/commands/taze.d.ts +6 -6
- package/dist/esm/commands/taze.js +3 -7
- package/dist/esm/config-validation.js +1 -1
- package/dist/esm/config.js +3 -12
- package/dist/esm/{delegate-host.d.ts → delegation/delegate-host.d.ts} +3 -3
- package/dist/esm/{delegate-host.js → delegation/delegate-host.js} +9 -8
- package/dist/esm/{delegation-target.js → delegation/delegation-target.js} +1 -1
- package/dist/esm/{remote-probe.js → delegation/remote-probe.js} +2 -2
- package/dist/esm/{remote-run.js → delegation/remote-run.js} +5 -5
- package/dist/esm/{remote-session.js → delegation/remote-session.js} +1 -1
- package/dist/esm/{user-config.js → delegation/user-config.js} +4 -13
- package/dist/esm/env-switch.d.ts +25 -0
- package/dist/esm/env-switch.js +26 -0
- package/dist/esm/fan-out.d.ts +4 -1
- package/dist/esm/fan-out.js +1 -3
- package/dist/esm/help.js +1 -1
- package/dist/esm/helpers/filter-selection.d.ts +2 -0
- package/dist/esm/helpers/importConfigDefault.d.ts +7 -0
- package/dist/esm/helpers/importConfigDefault.js +45 -0
- package/dist/esm/invocation.d.ts +7 -3
- package/dist/esm/machine-lock.js +2 -1
- package/dist/esm/route.d.ts +6 -0
- package/dist/esm/route.js +15 -2
- package/dist/esm/run-chain.d.ts +1 -1
- package/dist/esm/run-chain.js +1 -1
- package/dist/esm/runCli.d.ts +9 -0
- package/dist/esm/runCli.js +24 -9
- package/dist/esm/runner.d.ts +2 -0
- package/dist/esm/runner.js +1 -1
- package/dist/esm/verdict.d.ts +1 -1
- package/docs/check-cache.md +7 -7
- package/docs/delegate.md +2 -2
- package/docs/machine-lock.md +4 -4
- package/docs/scripts.md +4 -0
- package/docs/upgrades.md +9 -1
- package/docs/utilities.md +4 -2
- package/package.json +2 -2
- /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
|
@@ -4,9 +4,9 @@ import path from 'node:path';
|
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import { isRecord } from '@williamthorsen/nmr-core';
|
|
6
6
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
7
|
+
import { MAX_EXCERPT_BYTES } from "../helpers/deriveExcerpt.js";
|
|
7
8
|
import { isCacheableCommand, PASS_MANIFEST_DIR_ENV_VAR, writeDebugNote } from "./check-cache.js";
|
|
8
9
|
import { isReplayLine } from "./check-cache-entries.js";
|
|
9
|
-
import { MAX_EXCERPT_BYTES } from "./helpers/deriveExcerpt.js";
|
|
10
10
|
export function formatManifestScope(monorepoRoot, anchorDir) {
|
|
11
11
|
const scope = path.relative(monorepoRoot, anchorDir);
|
|
12
12
|
return scope === '' ? '.' : scope.split(path.sep).join('/');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatGlyphLine, STATUS_GLYPHS } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { NMR_GLYPHS } from "../glyphs.js";
|
|
3
|
+
import { formatDuration } from "../helpers/duration.js";
|
|
2
4
|
import { readCheckCacheEntry, readTranscript } from "./check-cache-entries.js";
|
|
3
|
-
import { NMR_GLYPHS } from "./glyphs.js";
|
|
4
|
-
import { formatDuration } from "./helpers/duration.js";
|
|
5
5
|
export function renderRecording(options) {
|
|
6
6
|
const { entry } = options.recording;
|
|
7
7
|
const age = formatDuration(Math.max(0, Date.now() - Date.parse(entry.recordedAt)));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type Step } from '../steps.js';
|
|
1
2
|
import { type ReplayLine } from './check-cache-entries.js';
|
|
2
|
-
import { type Step } from './steps.js';
|
|
3
3
|
/**
|
|
4
4
|
* Assembles what a composite's skip replays: the excerpts recorded by its constituents, in the order in which
|
|
5
5
|
* its own steps name them.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { readNmrStep } from "../steps.js";
|
|
2
|
+
import { getWorkspacePackageDirs } from "../workspace.js";
|
|
1
3
|
import { readCheckCacheEntry } from "./check-cache-entries.js";
|
|
2
|
-
import { readNmrStep } from "./steps.js";
|
|
3
|
-
import { getWorkspacePackageDirs } from "./workspace.js";
|
|
4
4
|
export async function assembleReplay(options) {
|
|
5
5
|
const { anchorDir, monorepoRoot, runId, steps, treeHash } = options;
|
|
6
6
|
const candidates = steps.flatMap((step) => {
|
package/dist/esm/child-env.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { type TreeSnapshot } from './check-cache.js';
|
|
2
|
+
import { type TreeSnapshot } from './check-cache/check-cache.js';
|
|
3
3
|
import type { Invocation } from './invocation.js';
|
|
4
4
|
import { type MachineLock } from './machine-lock.js';
|
|
5
5
|
import { type ReportFormat } from './report-format.js';
|
package/dist/esm/child-env.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { DEBUG_ENV_VAR, encodeTreeSnapshot, NO_CACHE_ENV_VAR, RUN_ID_ENV_VAR, TREE_SNAPSHOT_ENV_VAR, } from "./check-cache.js";
|
|
1
|
+
import { DEBUG_ENV_VAR, encodeTreeSnapshot, NO_CACHE_ENV_VAR, RUN_ID_ENV_VAR, TREE_SNAPSHOT_ENV_VAR, } from "./check-cache/check-cache.js";
|
|
2
|
+
import { DELEGATE_ENV_VAR } from "./delegation/remote-run.js";
|
|
2
3
|
import { acquireMachineLock, formatMisplacedNoLockWarning, HEAVY_COMMANDS, LOCK_TOKEN_ENV_VAR, NO_LOCK_ENV_VAR, } from "./machine-lock.js";
|
|
3
4
|
import { formatFlagSpelling } from "./nmr-flags.js";
|
|
4
5
|
import { OUTPUT_STYLE_ENV_VAR } from "./output-style.js";
|
|
5
|
-
import { DELEGATE_ENV_VAR } from "./remote-run.js";
|
|
6
6
|
import { REPORT_FORMAT_ENV_VAR } from "./report-format.js";
|
|
7
7
|
import { COMMAND_VERBOSITY_ENV_VAR } from "./verbosity.js";
|
|
8
8
|
export function addLockEnv(env, lock) {
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
+
import { type SpawnInherited } from './spawn-inherited.js';
|
|
1
2
|
import { type WorkflowTool } from './workflow-tools.js';
|
|
2
|
-
/** How a spawned actionlint ended. */
|
|
3
|
-
export interface ActionlintOutcome {
|
|
4
|
-
error?: Error | undefined;
|
|
5
|
-
signal: NodeJS.Signals | null;
|
|
6
|
-
status: number | null;
|
|
7
|
-
}
|
|
8
3
|
export interface RunActionlintOptions {
|
|
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
|
-
spawn?:
|
|
7
|
+
spawn?: SpawnInherited;
|
|
13
8
|
}
|
|
14
9
|
/**
|
|
15
10
|
* Lints the monorepo's GitHub Actions workflows with the pinned actionlint, running its shellcheck pass with the
|
|
@@ -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 { 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
|
const WORKFLOWS_DIR = path.join('.github', 'workflows');
|
|
8
7
|
export async function runActionlint(monorepoRoot, args, options = {}) {
|
|
@@ -14,19 +13,14 @@ export async function runActionlint(monorepoRoot, args, options = {}) {
|
|
|
14
13
|
const ensure = options.ensure ?? ((tool, roots) => ensureTool(tool, { cacheRoots: roots }));
|
|
15
14
|
const actionlintPath = await ensure('actionlint', cacheRoots);
|
|
16
15
|
const shellcheckPath = await ensure('shellcheck', cacheRoots);
|
|
17
|
-
const outcome = (options.spawn ?? spawnInherited)(actionlintPath, buildActionlintArgs(shellcheckPath, args),
|
|
16
|
+
const outcome = (options.spawn ?? spawnInherited)(actionlintPath, buildActionlintArgs(shellcheckPath, args), {
|
|
17
|
+
cwd: monorepoRoot,
|
|
18
|
+
});
|
|
18
19
|
if (outcome.error !== undefined) {
|
|
19
20
|
throw new UserError(`Cannot run actionlint: ${outcome.error.message}`, { cause: outcome.error });
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
return describeSignalExitCode(outcome.signal);
|
|
23
|
-
}
|
|
24
|
-
return outcome.status ?? 1;
|
|
22
|
+
return resolveInheritedExitCode(outcome);
|
|
25
23
|
}
|
|
26
24
|
export function buildActionlintArgs(shellcheckPath, args) {
|
|
27
25
|
return [`-shellcheck=${shellcheckPath}`, '-pyflakes=', ...args];
|
|
28
26
|
}
|
|
29
|
-
function spawnInherited(file, args, cwd) {
|
|
30
|
-
const result = spawnSync(file, args, { cwd, stdio: 'inherit' });
|
|
31
|
-
return { error: result.error, signal: result.signal, status: result.status };
|
|
32
|
-
}
|
|
@@ -63,7 +63,8 @@ export declare function resolveEntryPoints(packageDir: string, options?: BuildOp
|
|
|
63
63
|
* rebuild on every invocation and change the fingerprint that every other package reads.
|
|
64
64
|
*
|
|
65
65
|
* Because the fingerprint names the nmr that produced the output, not the sources in `src`, a package compiled by
|
|
66
|
-
* the pre-rebuild binary keeps the old fingerprint and rebuilds on the next run
|
|
66
|
+
* the pre-rebuild binary keeps the old fingerprint and rebuilds on the next run; a workspace that orders nmr's
|
|
67
|
+
* build ahead of the packages that it compiles avoids that rebuild.
|
|
67
68
|
*/
|
|
68
69
|
export declare function resolveToolchainFingerprint(packageDir: string, selfDir?: string): Promise<string>;
|
|
69
70
|
/** Records how long the emit took for the build whose digest is `digest`. */
|
|
@@ -4,7 +4,7 @@ import process from 'node:process';
|
|
|
4
4
|
import { UserError } from '@williamthorsen/nmr-core';
|
|
5
5
|
import { defineCommand, runCli } from '@williamthorsen/nmr-core/cli';
|
|
6
6
|
import { NMR_CLEAN_BIN } from "../bins.js";
|
|
7
|
-
import { removeCheckCache } from "../check-cache-entries.js";
|
|
7
|
+
import { removeCheckCache } from "../check-cache/check-cache-entries.js";
|
|
8
8
|
import { loadRootConfig } from "../config.js";
|
|
9
9
|
import { findContainingPackageDir } from "../context.js";
|
|
10
10
|
import { reportClosing } from "../helpers/reportClosing.js";
|
|
@@ -2,7 +2,8 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, statSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
|
-
import { NO_CACHE_ENV_VAR } from "../check-cache.js";
|
|
5
|
+
import { NO_CACHE_ENV_VAR } from "../check-cache/check-cache.js";
|
|
6
|
+
import { isEnvSwitchOn } from "../env-switch.js";
|
|
6
7
|
export const FMT_CACHE_DIRECTORY = path.join('node_modules', '.cache', 'prettier', 'nmr-fmt');
|
|
7
8
|
const LOCKFILE_NAMES = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'bun.lock', 'bun.lockb'];
|
|
8
9
|
const NO_LOCKFILE_MARKER = 'nolock';
|
|
@@ -10,7 +11,7 @@ const HASH_LENGTH = 16;
|
|
|
10
11
|
const CACHE_FILE_PATTERN = /^([0-9a-f]{16})\.([0-9a-f]{16}|nolock)\.json$/;
|
|
11
12
|
export function prepareFmtCache(options) {
|
|
12
13
|
const { cwd, env, repositoryRoot, shouldBypassCache } = options;
|
|
13
|
-
if (shouldBypassCache || env
|
|
14
|
+
if (shouldBypassCache || isEnvSwitchOn(env, NO_CACHE_ENV_VAR))
|
|
14
15
|
return { kind: 'uncached' };
|
|
15
16
|
try {
|
|
16
17
|
if (statSync(path.join(repositoryRoot, 'node_modules'), { throwIfNoEntry: false })?.isDirectory() !== true) {
|
|
@@ -1,24 +1,17 @@
|
|
|
1
|
+
import { type SpawnInherited } from './spawn-inherited.js';
|
|
1
2
|
export type FormatMode = 'check' | 'write';
|
|
2
3
|
/**
|
|
3
4
|
* Runs `nmr-fmt` against `argv` and returns the exit code to report: It formats or checks the files that git
|
|
4
5
|
* reports for `cwd`. A repository without a Prettier config of its own is formatted with the house config. The run
|
|
5
|
-
* uses Prettier's content-keyed cache unless `--no-cache` or `NMR_NO_CACHE=1` bypasses it
|
|
6
|
+
* uses Prettier's content-keyed cache unless `--no-cache` or `NMR_NO_CACHE=1` bypasses it, and an `NMR_NO_CACHE`
|
|
7
|
+
* that is neither on nor off is rejected as a usage error.
|
|
6
8
|
*
|
|
7
9
|
* Trailing arguments are git pathspecs, not Prettier flags; an unrecognized option is rejected rather
|
|
8
10
|
* than passed along, since git would read it as a pathspec matching nothing and the run would report
|
|
9
11
|
* success over an empty selection. Every argument after `--` is a pathspec, even one that starts
|
|
10
12
|
* with `-`.
|
|
11
13
|
*/
|
|
12
|
-
export declare function runFmt(argv: string[], cwd?: string): Promise<number>;
|
|
13
|
-
/**
|
|
14
|
-
* How a spawned Prettier process ended.
|
|
15
|
-
*
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
|
-
export interface PrettierOutcome {
|
|
19
|
-
error?: Error | undefined;
|
|
20
|
-
status: number | null;
|
|
21
|
-
}
|
|
14
|
+
export declare function runFmt(argv: string[], cwd?: string, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
22
15
|
/** @internal */
|
|
23
16
|
export interface RunPrettierOptions {
|
|
24
17
|
cliPath: string;
|
|
@@ -32,8 +25,8 @@ export interface RunPrettierOptions {
|
|
|
32
25
|
cwd: string;
|
|
33
26
|
/** Overridable so that the multi-batch path is reachable without a fixture that hits the real ceiling. */
|
|
34
27
|
budgetBytes?: number;
|
|
35
|
-
/** Runs the Prettier CLI
|
|
36
|
-
spawn?:
|
|
28
|
+
/** Runs the Prettier CLI on the caller's stdio. */
|
|
29
|
+
spawn?: SpawnInherited;
|
|
37
30
|
}
|
|
38
31
|
/**
|
|
39
32
|
* Runs Prettier over the selection, returning the first non-zero exit code. Every batch runs even after
|
package/dist/esm/commands/fmt.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
4
2
|
import { createRequire } from 'node:module';
|
|
5
3
|
import path from 'node:path';
|
|
6
4
|
import process from 'node:process';
|
|
@@ -9,8 +7,12 @@ import { isRecord, reportError } from '@williamthorsen/nmr-core';
|
|
|
9
7
|
import { defineCommand, runCli, UsageError } from '@williamthorsen/nmr-core/cli';
|
|
10
8
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
11
9
|
import { NMR_FMT_BIN } from "../bins.js";
|
|
10
|
+
import { NO_CACHE_ENV_VAR } from "../check-cache/check-cache.js";
|
|
11
|
+
import { readEnvSwitch } from "../env-switch.js";
|
|
12
12
|
import { prepareFmtCache } from "./fmt-cache.js";
|
|
13
13
|
import { resolveFormatTargets } from "./format-targets.js";
|
|
14
|
+
import { BROKEN_MANIFEST_FIX, findPackageManifest, resolveDeclaredCli, } from "./package-manifest.js";
|
|
15
|
+
import { resolveInheritedExitCode, spawnInherited } from "./spawn-inherited.js";
|
|
14
16
|
const PRETTIER_PACKAGE = 'prettier';
|
|
15
17
|
const DEFAULT_CONFIG_PATH = fileURLToPath(new URL(`../prettier-default.config${path.extname(fileURLToPath(import.meta.url))}`, import.meta.url));
|
|
16
18
|
const ARGUMENT_BUDGET_BYTES = 100_000;
|
|
@@ -18,7 +20,7 @@ const MODE_ARGS = {
|
|
|
18
20
|
check: ['--check'],
|
|
19
21
|
write: ['--list-different', '--write'],
|
|
20
22
|
};
|
|
21
|
-
export async function runFmt(argv, cwd = process.cwd()) {
|
|
23
|
+
export async function runFmt(argv, cwd = process.cwd(), env = process.env) {
|
|
22
24
|
const command = defineCommand({
|
|
23
25
|
summary: 'Format or check the files that git lists',
|
|
24
26
|
flags: {
|
|
@@ -31,12 +33,20 @@ export async function runFmt(argv, cwd = process.cwd()) {
|
|
|
31
33
|
if (flags.check === flags.write) {
|
|
32
34
|
throw new UsageError(flags.check ? 'Pass --check or --write, not both.' : 'Pass --check or --write.');
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
const noCache = readEnvSwitch(env, NO_CACHE_ENV_VAR);
|
|
37
|
+
if (!noCache.ok) {
|
|
38
|
+
throw new UsageError(noCache.error);
|
|
39
|
+
}
|
|
40
|
+
return formatFiles({
|
|
41
|
+
mode: flags.check ? 'check' : 'write',
|
|
42
|
+
pathspecs: operands.pathspec,
|
|
43
|
+
shouldBypassCache: flags.noCache || noCache.isOn,
|
|
44
|
+
}, cwd, env);
|
|
35
45
|
},
|
|
36
46
|
});
|
|
37
47
|
return runCli(argv, command, { name: NMR_FMT_BIN, stdout: process.stdout, stderr: process.stderr, baseDir: cwd });
|
|
38
48
|
}
|
|
39
|
-
async function formatFiles(request, cwd) {
|
|
49
|
+
async function formatFiles(request, cwd, env) {
|
|
40
50
|
const resolvedTargets = resolveFormatTargets(cwd, request.pathspecs);
|
|
41
51
|
if (!resolvedTargets.ok) {
|
|
42
52
|
reportError(NMR_FMT_BIN, resolvedTargets.error);
|
|
@@ -70,7 +80,7 @@ async function formatFiles(request, cwd) {
|
|
|
70
80
|
}
|
|
71
81
|
const cache = prepareFmtCache({
|
|
72
82
|
cwd,
|
|
73
|
-
env
|
|
83
|
+
env,
|
|
74
84
|
repositoryRoot,
|
|
75
85
|
shouldBypassCache: request.shouldBypassCache,
|
|
76
86
|
});
|
|
@@ -92,17 +102,36 @@ function resolvePrettierCli(cwd) {
|
|
|
92
102
|
ok: false,
|
|
93
103
|
error: `\`${PRETTIER_PACKAGE}\` could not be resolved from this repository or from nmr's own installation, which declares it as a dependency. Reinstall \`@williamthorsen/nmr\`.`,
|
|
94
104
|
};
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
const repositoryLookup = findPackageManifest(path.join(cwd, 'package.json'), PRETTIER_PACKAGE);
|
|
106
|
+
const copy = repositoryLookup.kind === 'absent'
|
|
107
|
+
? {
|
|
108
|
+
lookup: findPackageManifest(import.meta.url, PRETTIER_PACKAGE),
|
|
109
|
+
origin: "nmr's installation",
|
|
110
|
+
reinstallStep: 'Reinstall `@williamthorsen/nmr`.',
|
|
111
|
+
}
|
|
112
|
+
: {
|
|
113
|
+
lookup: repositoryLookup,
|
|
114
|
+
origin: 'this repository',
|
|
115
|
+
reinstallStep: `Reinstall this repository's \`${PRETTIER_PACKAGE}\` dependency.`,
|
|
116
|
+
};
|
|
117
|
+
const { lookup } = copy;
|
|
118
|
+
if (lookup.kind === 'absent')
|
|
104
119
|
return missingResult;
|
|
105
|
-
|
|
120
|
+
if (lookup.kind === 'broken') {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: `\`${PRETTIER_PACKAGE}\` could not be loaded from ${copy.origin}: ${lookup.cause}. ${BROKEN_MANIFEST_FIX}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const { manifest, manifestPath } = lookup;
|
|
127
|
+
const cliPath = resolveDeclaredCli(manifest, manifestPath, PRETTIER_PACKAGE);
|
|
128
|
+
if (cliPath === undefined) {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
error: `\`${PRETTIER_PACKAGE}\` at ${path.dirname(manifestPath)} does not declare a CLI. ${copy.reinstallStep}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return { ok: true, cliPath, manifestPath };
|
|
106
135
|
}
|
|
107
136
|
async function findRepositoryConfig(manifestPath, cwd) {
|
|
108
137
|
try {
|
|
@@ -136,12 +165,12 @@ export function runPrettier(options) {
|
|
|
136
165
|
];
|
|
137
166
|
let firstFailureStatus = 0;
|
|
138
167
|
for (const batch of batchWithinBudget(files, budgetBytes)) {
|
|
139
|
-
const result = spawn(process.execPath, [...prettierArgs, ...batch], cwd);
|
|
168
|
+
const result = spawn(process.execPath, [...prettierArgs, ...batch], { cwd });
|
|
140
169
|
if (result.error) {
|
|
141
170
|
reportError(NMR_FMT_BIN, `could not run \`${PRETTIER_PACKAGE}\` (${result.error.message}).`);
|
|
142
171
|
return 1;
|
|
143
172
|
}
|
|
144
|
-
const status = result
|
|
173
|
+
const status = resolveInheritedExitCode(result);
|
|
145
174
|
if (status !== 0 && firstFailureStatus === 0) {
|
|
146
175
|
firstFailureStatus = status;
|
|
147
176
|
}
|
|
@@ -166,15 +195,3 @@ function batchWithinBudget(files, budgetBytes) {
|
|
|
166
195
|
batches.push(batch);
|
|
167
196
|
return batches;
|
|
168
197
|
}
|
|
169
|
-
function resolveFrom(parent, specifier) {
|
|
170
|
-
try {
|
|
171
|
-
return createRequire(parent).resolve(specifier);
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return undefined;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function spawnInherited(file, args, cwd) {
|
|
178
|
-
const { error, status } = spawnSync(file, args, { cwd, stdio: 'inherit' });
|
|
179
|
-
return { error, status };
|
|
180
|
-
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|