@williamthorsen/nmr 0.34.0 → 0.36.0
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 +1416 -0
- package/.readyup/kits/default.js +174 -19
- package/.readyup/manifest.json +11 -8
- package/CHANGELOG.md +894 -0
- package/README.md +91 -18
- package/agents/guidance/rulebooks/nmr.md +1 -1
- package/dist/esm/UserError.d.ts +6 -0
- package/dist/esm/check-cache.d.ts +145 -0
- package/dist/esm/commands/build-output.d.ts +64 -0
- package/dist/esm/commands/build.d.ts +29 -1
- package/dist/esm/commands/build.js +26 -6
- package/dist/esm/commands/clean.d.ts +15 -0
- package/dist/esm/commands/ensure-prepublish-hooks.d.ts +11 -0
- package/dist/esm/commands/fmt.d.ts +32 -0
- package/dist/esm/commands/report-catalog.d.ts +10 -0
- package/dist/esm/commands/report-overrides.d.ts +6 -0
- package/dist/esm/commands/taze.d.ts +18 -0
- package/dist/esm/config.d.ts +19 -2
- package/dist/esm/config.js +66 -9
- package/dist/esm/context.d.ts +11 -0
- package/dist/esm/default-scripts.d.ts +15 -0
- package/dist/esm/defineConfig.d.ts +9 -0
- package/dist/esm/help.d.ts +13 -0
- package/dist/esm/helpers/clampToBytes.d.ts +12 -0
- package/dist/esm/helpers/config-path.d.ts +8 -0
- package/dist/esm/helpers/config-path.js +7 -0
- package/dist/esm/helpers/createBoundedBuffer.d.ts +7 -0
- package/dist/esm/helpers/deriveExcerpt.d.ts +10 -0
- package/dist/esm/helpers/duration.d.ts +15 -0
- package/dist/esm/helpers/findClosestName.d.ts +7 -0
- package/dist/esm/helpers/findClosestName.js +38 -0
- package/dist/esm/helpers/hook-name.d.ts +8 -0
- package/dist/esm/helpers/package-json.d.ts +25 -0
- package/dist/esm/helpers/package-json.js +1 -1
- package/dist/esm/helpers/readStringValues.d.ts +7 -0
- package/dist/esm/helpers/reportClosing.d.ts +15 -0
- package/dist/esm/helpers/transcript.d.ts +25 -0
- package/dist/esm/helpers/type-guards.d.ts +1 -0
- package/dist/esm/helpers/workspace-patterns.d.ts +7 -0
- package/dist/esm/prettier.d.ts +8 -0
- package/dist/esm/recording.d.ts +39 -1
- package/dist/esm/replay-assembly.d.ts +18 -2
- package/dist/esm/report-format.d.ts +33 -0
- package/dist/esm/reportCliFailure.d.ts +8 -0
- package/dist/esm/resolve-scripts.d.ts +10 -1
- package/dist/esm/resolver.d.ts +67 -2
- package/dist/esm/resolver.js +1 -1
- package/dist/esm/runCli.d.ts +17 -0
- package/dist/esm/runCli.js +2 -2
- package/dist/esm/runner.d.ts +52 -0
- package/dist/esm/scripts.d.ts +1 -0
- package/dist/esm/steps.d.ts +71 -0
- package/dist/esm/taze.d.ts +16 -0
- package/dist/esm/tests.d.ts +40 -0
- package/dist/esm/tests.js +25 -0
- package/dist/esm/tiers.d.ts +56 -1
- package/dist/esm/tiers.js +22 -9
- package/dist/esm/types.d.ts +28 -0
- package/dist/esm/verbosity.d.ts +37 -0
- package/dist/esm/verdict.d.ts +45 -0
- package/dist/esm/vitest-git-isolation.js +6 -0
- package/dist/esm/vitest.d.ts +72 -2
- package/dist/esm/vitest.js +17 -9
- package/dist/esm/workspace.d.ts +18 -0
- package/package.json +20 -10
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import type { RetainedOutput } from '../runner.js';
|
|
2
|
+
/**
|
|
3
|
+
* The bound on what one recorded pass persists, and so on what `nmr --log` can print back.
|
|
4
|
+
*
|
|
5
|
+
* A quantity of its own, distinct from the two that bound the same bytes elsewhere: the in-memory copy a
|
|
6
|
+
* command's capture keeps is bounded per stream, for a failure this run may still have to report, and a
|
|
7
|
+
* verdict line is bounded at what a pipe carries atomically. This one bounds what a cache directory
|
|
8
|
+
* accumulates over every scope and command a repo records.
|
|
9
|
+
*/
|
|
2
10
|
export declare const TRANSCRIPT_LIMIT_BYTES = 262144;
|
|
11
|
+
/**
|
|
12
|
+
* Reduces a command's raw output to the form nmr persists: escape sequences dropped, and a line the command
|
|
13
|
+
* redrew reduced to what a reader was left looking at.
|
|
14
|
+
*
|
|
15
|
+
* Applied where the bytes are stored rather than where they are captured. The copy a quiet run writes to
|
|
16
|
+
* stderr on failure is the command's own output going to a stream that may be a terminal, and stripping its
|
|
17
|
+
* color there would be a loss.
|
|
18
|
+
*/
|
|
3
19
|
export declare function cleanTranscript(raw: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Composes what a pass persists from what its command wrote, or `undefined` where it wrote nothing worth
|
|
22
|
+
* keeping.
|
|
23
|
+
*
|
|
24
|
+
* The streams follow one another rather than interleaving, in the order a quiet run already dumps them on
|
|
25
|
+
* failure: nmr reads each through a pipe of its own, so their true interleaving is not among the facts it
|
|
26
|
+
* holds. A stream carrying nothing contributes neither content nor marker.
|
|
27
|
+
*/
|
|
4
28
|
export declare function composeTranscript(retained: RetainedOutput): string | undefined;
|
|
29
|
+
/** Renders the line that stands in for dropped bytes, in nmr's voice so it is not mistaken for the tool's. */
|
|
5
30
|
export declare function formatElisionMarker(byteCount: number): string;
|
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves pnpm workspace patterns to absolute package directories.
|
|
3
|
+
*
|
|
4
|
+
* Applies pnpm's algorithm: every pattern is rewritten to match a manifest, `!`-prefixed patterns become
|
|
5
|
+
* exclusions, and the matcher decides the rest. Exclusions filter the entire positive match set
|
|
6
|
+
* irrespective of declaration order.
|
|
7
|
+
*/
|
|
1
8
|
export declare function resolvePackageDirs(monorepoRoot: string, patterns: string[]): string[];
|
package/dist/esm/prettier.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { Config } from 'prettier';
|
|
2
2
|
export interface PrettierConfigOptions extends Omit<Config, 'overrides' | 'plugins'> {
|
|
3
|
+
/** Appended to the plugins this config registers. */
|
|
3
4
|
additionalPlugins?: NonNullable<Config['plugins']>;
|
|
5
|
+
/** Appended to the overrides this config declares, so a later entry wins over nmr's own. */
|
|
4
6
|
additionalOverrides?: NonNullable<Config['overrides']>;
|
|
7
|
+
/** Replacing the override list is inexpressible; append through `additionalOverrides`. */
|
|
5
8
|
overrides?: never;
|
|
9
|
+
/** Replacing the plugin list is inexpressible; append through `additionalPlugins`. */
|
|
6
10
|
plugins?: never;
|
|
7
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Builds the shared Prettier config, registering shell and Dockerfile support alongside the house options.
|
|
14
|
+
* Every option other than `plugins` and `overrides` spreads over the defaults.
|
|
15
|
+
*/
|
|
8
16
|
export declare function definePrettierConfig(options?: PrettierConfigOptions): Config;
|
package/dist/esm/recording.d.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CheckCacheEntry } from './check-cache.js';
|
|
2
|
+
/**
|
|
3
|
+
* What this invocation would be recorded under, insofar as an entry records the same facts. A mismatch is read
|
|
4
|
+
* against these, so a refusal names the ingredient that moved rather than blaming the tree for all of them.
|
|
5
|
+
*/
|
|
2
6
|
export interface RunIdentity {
|
|
3
7
|
commandString: string;
|
|
4
8
|
nmrVersion: string;
|
|
5
9
|
nodeVersion: string;
|
|
10
|
+
/** Absent where no snapshot was taken, which leaves the tree unattributable rather than assumed equal. */
|
|
6
11
|
treeHash: string | undefined;
|
|
7
12
|
}
|
|
13
|
+
/** A recorded pass and what it left a reader: its command's own transcript, or the assembly a composite holds. */
|
|
8
14
|
export interface Recording {
|
|
9
15
|
entry: CheckCacheEntry;
|
|
16
|
+
/** What the command wrote, absent on a composite, which retains nothing of its own. */
|
|
10
17
|
transcript?: string;
|
|
11
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Which of the pass key's ingredients a recorded pass and this invocation disagree on, insofar as an entry
|
|
21
|
+
* records enough to tell: the residual covers what it does not, which is the install, the platform, and the
|
|
22
|
+
* environment variables the key folds in.
|
|
23
|
+
*/
|
|
12
24
|
export type KeyDifference = {
|
|
13
25
|
ingredient: 'tree';
|
|
14
26
|
} | {
|
|
@@ -24,6 +36,7 @@ export type KeyDifference = {
|
|
|
24
36
|
} | {
|
|
25
37
|
ingredient: 'other';
|
|
26
38
|
};
|
|
39
|
+
/** What `--log` found for one command at one scope. */
|
|
27
40
|
export type RecordingLookup = {
|
|
28
41
|
ok: true;
|
|
29
42
|
recording: Recording;
|
|
@@ -31,6 +44,11 @@ export type RecordingLookup = {
|
|
|
31
44
|
ok: false;
|
|
32
45
|
refusal: RecordingRefusal;
|
|
33
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Why there is nothing to print. Each is a separate answer to a reader asking for the last run's output, and
|
|
49
|
+
* each names a different next move: run the command, run it on this tree, run it through a pipe, or stop
|
|
50
|
+
* asking for a command no run records.
|
|
51
|
+
*/
|
|
34
52
|
export type RecordingRefusal = {
|
|
35
53
|
kind: 'uncacheable';
|
|
36
54
|
} | {
|
|
@@ -45,16 +63,36 @@ export type RecordingRefusal = {
|
|
|
45
63
|
kind: 'no-output';
|
|
46
64
|
ageMs: number;
|
|
47
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* Renders a recording as the reader sees it: a header dating what follows, the command string that produced
|
|
68
|
+
* it, and then the run's own bytes.
|
|
69
|
+
*
|
|
70
|
+
* The header is what presents the body as a recording rather than as this invocation's output, which is what
|
|
71
|
+
* lets a reader at a terminal be shown what a piped run wrote. The command string is the whole chain, hooks
|
|
72
|
+
* included, so the header names what earned the pass and not merely what was typed.
|
|
73
|
+
*/
|
|
48
74
|
export declare function renderRecording(options: {
|
|
49
75
|
command: string;
|
|
50
76
|
recording: Recording;
|
|
51
77
|
scope: string;
|
|
52
78
|
}): string;
|
|
79
|
+
/**
|
|
80
|
+
* Renders a refusal on the one line a fan-out can attribute, in the grammar a verdict uses: the scope, the
|
|
81
|
+
* command, and what is missing.
|
|
82
|
+
*/
|
|
53
83
|
export declare function renderRefusal(options: {
|
|
54
84
|
command: string;
|
|
55
85
|
refusal: RecordingRefusal;
|
|
56
86
|
scope: string;
|
|
57
87
|
}): string;
|
|
88
|
+
/**
|
|
89
|
+
* Resolves what one scope has to show for one command.
|
|
90
|
+
*
|
|
91
|
+
* Admitted on the pass key alone, so `--log` prints exactly what a skip would have recalled and never a
|
|
92
|
+
* recording of some other tree. The retention key is deliberately not consulted: it certifies that a recording
|
|
93
|
+
* describes this presentation environment, which is what a replayed excerpt needs and what a dated recording
|
|
94
|
+
* does not.
|
|
95
|
+
*/
|
|
58
96
|
export declare function resolveRecording(options: {
|
|
59
97
|
anchorDir: string;
|
|
60
98
|
command: string;
|
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type ReplayLine } from './check-cache.js';
|
|
2
|
+
import { type Step } from './steps.js';
|
|
3
|
+
/**
|
|
4
|
+
* Assembles what a composite's skip replays: the excerpts its constituents recorded, in the order its own
|
|
5
|
+
* steps name them.
|
|
6
|
+
*
|
|
7
|
+
* A constituent that is itself a composite holds a flat, attributed list of its own, so splicing one in keeps
|
|
8
|
+
* the assembly flat and a skipped `ci` replays a package's `test` rather than one opaque `check:strict`
|
|
9
|
+
* line. Nothing is computed and nothing is inferred: a constituent with no admissible entry is absent from
|
|
10
|
+
* the assembly.
|
|
11
|
+
*
|
|
12
|
+
* A constituent is looked up at the scope its own process anchors at: the composite's anchor, or the monorepo
|
|
13
|
+
* root for an element carrying `-w`, which is how a package-scoped composite reaches a root command.
|
|
14
|
+
*
|
|
15
|
+
* An entry is admissible where the run that certified it is this one and the tree it describes is this one.
|
|
16
|
+
* The witness is what a run stamps on an excerpt it records and on one it recalls and replays; the tree hash
|
|
17
|
+
* is what bounds an identity a process carried out of the run that issued it.
|
|
18
|
+
*/
|
|
3
19
|
export declare function assembleReplay(options: {
|
|
4
20
|
anchorDir: string;
|
|
5
21
|
monorepoRoot: string;
|
|
@@ -1,9 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the rejection of a value off the format ladder, naming where it was written. Every source that
|
|
3
|
+
* accepts a format renders through this, so no two of them can come to name different ladders.
|
|
4
|
+
*/
|
|
1
5
|
export declare function formatReportFormatRejection(source: string, value: string): string;
|
|
6
|
+
/** Narrows a raw value to a point on the format ladder. */
|
|
2
7
|
export declare function isReportFormat(value: string): value is ReportFormat;
|
|
8
|
+
/**
|
|
9
|
+
* Reads the format the environment names. An unrecognized value resolves to nothing at all, since falling back
|
|
10
|
+
* would pick a rendering nobody chose and hide a misspelling for the life of the shell.
|
|
11
|
+
*
|
|
12
|
+
* An unset or empty variable names no format rather than resolving to `text`, which keeps the read symmetric
|
|
13
|
+
* with the one the loudness ladder performs and leaves room for a level below the environment.
|
|
14
|
+
*/
|
|
3
15
|
export declare function readReportFormatEnv(env: NodeJS.ProcessEnv): ReportFormatRead;
|
|
16
|
+
/**
|
|
17
|
+
* Carries the resolved format down the spawned chain, so `--json` reaches every process rather than the first.
|
|
18
|
+
* Deliberately not a keyed variable: it changes how a run reports and never what a command concludes, so
|
|
19
|
+
* folding it into the cache key would stop a machine-readable run from hitting a pass a prose one recorded.
|
|
20
|
+
*/
|
|
4
21
|
export declare const REPORT_FORMAT_ENV_VAR = "NMR_REPORT_FORMAT";
|
|
22
|
+
/**
|
|
23
|
+
* The points on the format ladder. Both are spelled out rather than leaving `text` implicit in the variable's
|
|
24
|
+
* absence, so a format exported for a shell is overridable on one invocation in either direction.
|
|
25
|
+
*/
|
|
5
26
|
export declare const REPORT_FORMATS: readonly ["text", "json"];
|
|
27
|
+
/** How nmr renders its own verdicts, which is never the output of the commands it runs. */
|
|
6
28
|
export type ReportFormat = (typeof REPORT_FORMATS)[number];
|
|
29
|
+
/** The format the environment names, if any, or the message naming why its value could not be read. */
|
|
7
30
|
export type ReportFormatRead = {
|
|
8
31
|
ok: true;
|
|
9
32
|
format?: ReportFormat;
|
|
@@ -11,6 +34,16 @@ export type ReportFormatRead = {
|
|
|
11
34
|
ok: false;
|
|
12
35
|
error: string;
|
|
13
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the format this process reports in: the flag, then the environment, then `text`.
|
|
39
|
+
*
|
|
40
|
+
* Two levels where the loudness ladder has four. A repo wanting JSON for everyone working in it is not a case
|
|
41
|
+
* anyone has, and detecting a harness would hand every agent JSON in place of the prose the shipped guidance
|
|
42
|
+
* teaches; either is additive later.
|
|
43
|
+
*
|
|
44
|
+
* Total by construction. The environment was validated as it was read, so no rejection is left for this to
|
|
45
|
+
* report and the whole ladder reads in one place.
|
|
46
|
+
*/
|
|
14
47
|
export declare function resolveReportFormat(options: ResolveReportFormatOptions): ReportFormat;
|
|
15
48
|
export interface ResolveReportFormatOptions {
|
|
16
49
|
envFormat: ReportFormat | undefined;
|
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
+
/**
|
|
3
|
+
* Writes a failure to the CLI's error stream: as its message alone when the user's own declaration caused it,
|
|
4
|
+
* and with its stack otherwise.
|
|
5
|
+
*
|
|
6
|
+
* A stack records where nmr noticed a problem, which says nothing about the file a user has to edit; for nmr's
|
|
7
|
+
* own fault it is where a report has to start. `describeError` covers an `Error` carrying no stack and a thrown
|
|
8
|
+
* non-`Error` alike.
|
|
9
|
+
*/
|
|
2
10
|
export declare function reportCliFailure(error: unknown, stderr: Writable): void;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ScriptRegistry } from './default-scripts.js';
|
|
2
2
|
export type { ScriptRegistry, ScriptValue, StepSpec } from './default-scripts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Return the default workspace scripts.
|
|
5
|
+
*
|
|
6
|
+
* A function rather than the exported table itself: callers merge overrides into the result, and `generateHelp`
|
|
7
|
+
* rewrites entries in place, so each caller needs a copy the module does not share.
|
|
8
|
+
*/
|
|
3
9
|
export declare function getDefaultWorkspaceScripts(): ScriptRegistry;
|
|
10
|
+
/**
|
|
11
|
+
* Return the default root scripts.
|
|
12
|
+
*/
|
|
4
13
|
export declare function getDefaultRootScripts(): ScriptRegistry;
|
package/dist/esm/resolver.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type ScriptRegistry, type ScriptValue } from './resolve-scripts.js';
|
|
2
|
+
import { type Step } from './steps.js';
|
|
3
3
|
import type { NmrConfig } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Replace the first token of a command with a `devBin` substitute.
|
|
6
|
+
* Relative paths in the replacement are resolved from `monorepoRoot`.
|
|
7
|
+
*/
|
|
4
8
|
export declare function applyDevBin(command: string, devBin: Record<string, string> | undefined, monorepoRoot: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Applies a `devBin` substitution to each opaque step, leaving nmr's own compositions alone.
|
|
11
|
+
*
|
|
12
|
+
* A structural step names a command nmr resolves for itself, so substituting its first token would replace the
|
|
13
|
+
* `nmr` that carries the composite rather than the leaf tool `devBin` documents replacing.
|
|
14
|
+
*/
|
|
5
15
|
export declare function applyDevBinToSteps(steps: readonly Step[], devBin: Record<string, string> | undefined, monorepoRoot: string): readonly Step[];
|
|
16
|
+
/**
|
|
17
|
+
* Where a resolved script was read from.
|
|
18
|
+
*
|
|
19
|
+
* `registry` covers the built-in defaults and the repo-wide config together, which resolution cannot tell
|
|
20
|
+
* apart: it receives the two already merged. A caller holding the config refines the two.
|
|
21
|
+
*/
|
|
6
22
|
export type ScriptOrigin = {
|
|
7
23
|
tier: 'registry';
|
|
8
24
|
key: string;
|
|
@@ -15,11 +31,60 @@ export interface ResolvedScript {
|
|
|
15
31
|
origin: ScriptOrigin;
|
|
16
32
|
steps: readonly Step[];
|
|
17
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Expands a script value into the ordered steps it runs as: a string is one opaque step, and an array is one
|
|
36
|
+
* structural step per element.
|
|
37
|
+
*
|
|
38
|
+
* A bare string element and the `{ run }` spec compose the same step. The spec's only addition is the
|
|
39
|
+
* declaration of what the step does with the invocation's trailing arguments, which position cannot carry.
|
|
40
|
+
*/
|
|
18
41
|
export declare function expandScript(script: ScriptValue, workspaceRoot: boolean): readonly Step[];
|
|
42
|
+
/**
|
|
43
|
+
* Returns a description of a script for help output.
|
|
44
|
+
*/
|
|
19
45
|
export declare function describeScript(script: ScriptValue): string;
|
|
46
|
+
/**
|
|
47
|
+
* Reads a package.json's `scripts`, rejecting any value that is not a string.
|
|
48
|
+
*
|
|
49
|
+
* npm and pnpm read a script as a string too, so a value of any other type is malformed however it got there.
|
|
50
|
+
* Dropping one silently would run the registry's entry in its place, which is what an array written here after
|
|
51
|
+
* being told a step list resolves a shelled-nmr crossing would otherwise do.
|
|
52
|
+
*/
|
|
20
53
|
export declare function readPackageJsonScripts(packageDir: string): Record<string, string> | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Builds the merged workspace script registry:
|
|
56
|
+
* tier 1 (defaults) + tier 2 (config overrides)
|
|
57
|
+
*/
|
|
21
58
|
export declare function buildWorkspaceRegistry(config: NmrConfig): ScriptRegistry;
|
|
59
|
+
/**
|
|
60
|
+
* Builds the merged root script registry:
|
|
61
|
+
* tier 1 (defaults) + tier 2 (config overrides)
|
|
62
|
+
*/
|
|
22
63
|
export declare function buildRootRegistry(config: NmrConfig): ScriptRegistry;
|
|
64
|
+
/**
|
|
65
|
+
* Returns a `package.json` entry that re-invokes the command it is declared under alongside other steps, or
|
|
66
|
+
* `undefined` where the entry declares no such thing.
|
|
67
|
+
*
|
|
68
|
+
* Resolution discards a self-referential entry however it reads, so an entry that chains loses the steps it
|
|
69
|
+
* chained. This is what an invocation rejects on, read where the command runs rather than raised from
|
|
70
|
+
* resolution: the same scripts are resolved speculatively, for packages nobody named.
|
|
71
|
+
*/
|
|
23
72
|
export declare function findChainedSelfReference(packageDir: string | undefined, commandName: string): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Reports whether a `package.json` entry re-invokes the command it is declared under, wherever the
|
|
75
|
+
* re-invocation stands in it, e.g. `"build": "nmr build"` or `"build": "rdy compile && nmr build"`.
|
|
76
|
+
*
|
|
77
|
+
* Honouring one spawns a shell that runs the same command in the same directory, reaching the same entry
|
|
78
|
+
* again without bound, so resolution discards it.
|
|
79
|
+
*/
|
|
24
80
|
export declare function isSelfReferential(script: string, commandName: string, packageDir: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Resolves a script command using the three-tier override system:
|
|
83
|
+
* 1. Package defaults (built-in registry)
|
|
84
|
+
* 2. Repo-wide config (.config/nmr.config.ts)
|
|
85
|
+
* 3. Per-package overrides (package.json scripts)
|
|
86
|
+
*
|
|
87
|
+
* Returns undefined if the command is not found in the registry. A package.json override of `""` (indicating
|
|
88
|
+
* skip) resolves to a single opaque step carrying it, which renders back to the empty string.
|
|
89
|
+
*/
|
|
25
90
|
export declare function resolveScript(commandName: string, registry: ScriptRegistry, packageDir: string | undefined, workspaceRoot: boolean): ResolvedScript | undefined;
|
package/dist/esm/resolver.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { hasErrnoCode } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { parsePackageJson, readScriptRecord, resolvePackageJsonPath } from "./helpers/package-json.js";
|
|
5
5
|
import { isObject } from "./helpers/type-guards.js";
|
|
6
|
-
import { getDefaultRootScripts, getDefaultWorkspaceScripts } from "./resolve-scripts.js";
|
|
6
|
+
import { getDefaultRootScripts, getDefaultWorkspaceScripts, } from "./resolve-scripts.js";
|
|
7
7
|
import { composeNmrStep, readSelfReference } from "./steps.js";
|
|
8
8
|
import { isMonorepoRoot } from "./workspace.js";
|
|
9
9
|
export function applyDevBin(command, devBin, monorepoRoot) {
|
package/dist/esm/runCli.d.ts
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
|
+
/**
|
|
3
|
+
* Marks a run made on a delegating caller's behalf, where a command the registry does not define exits 0
|
|
4
|
+
* rather than failing.
|
|
5
|
+
*/
|
|
2
6
|
export declare const RUN_IF_PRESENT_ENV_VAR = "NMR_RUN_IF_PRESENT";
|
|
7
|
+
/** @internal */
|
|
3
8
|
export interface RunCliOptions {
|
|
9
|
+
/** Post-slice CLI arguments (equivalent to `process.argv.slice(2)`). */
|
|
4
10
|
args: string[];
|
|
11
|
+
/** Working directory used to resolve the nmr execution context. */
|
|
5
12
|
cwd: string;
|
|
13
|
+
/** Environment for `runCli` (used for `NMR_RUN_IF_PRESENT` reads and `-R` writes). */
|
|
6
14
|
env: NodeJS.ProcessEnv;
|
|
15
|
+
/** Stream for normal output (help text, override messages). */
|
|
7
16
|
stdout: Writable;
|
|
17
|
+
/** Stream for error output (unknown command, parse errors). */
|
|
8
18
|
stderr: Writable;
|
|
9
19
|
}
|
|
20
|
+
/** @internal */
|
|
10
21
|
export interface RunCliResult {
|
|
11
22
|
exitCode: number;
|
|
12
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Executes the nmr CLI flow in-process and returns the resulting exit code.
|
|
26
|
+
* Holds no global state, reads no `process.*` globals, never calls `process.exit`.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
13
30
|
export declare function runCli(options: RunCliOptions): Promise<RunCliResult>;
|
package/dist/esm/runCli.js
CHANGED
|
@@ -2,9 +2,9 @@ import path from 'node:path';
|
|
|
2
2
|
import { readPackageVersion, reportError } from '@williamthorsen/nmr-core';
|
|
3
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
4
|
import { certifyRetention, computeCacheKey, computeRetentionKey, CURRENT_RUNTIME, encodeTreeSnapshot, findStaleBuildOutput, formatMisplacedNoCacheWarning, isCacheableCommand, NO_CACHE_ENV_VAR, readBuildOutputState, readCheckCacheEntry, recordTranscript, resolveRunId, resolveTreeSnapshot, RUN_ID_ENV_VAR, TREE_SNAPSHOT_ENV_VAR, writeCheckCacheEntry, writeDebugNote, } from "./check-cache.js";
|
|
5
|
-
import { resolveConfigPath } from "./config.js";
|
|
6
5
|
import { resolveContext } from "./context.js";
|
|
7
6
|
import { generateHelp } from "./help.js";
|
|
7
|
+
import { resolveConfigPath } from "./helpers/config-path.js";
|
|
8
8
|
import { deriveExcerpt } from "./helpers/deriveExcerpt.js";
|
|
9
9
|
import { isHookName } from "./helpers/hook-name.js";
|
|
10
10
|
import { resolvePackageJsonPath } from "./helpers/package-json.js";
|
|
@@ -16,7 +16,7 @@ import { applyDevBinToSteps, buildRootRegistry, buildWorkspaceRegistry, expandSc
|
|
|
16
16
|
import { resolveChannel, runSteps } from "./runner.js";
|
|
17
17
|
import { composeNmrStep, findNmrCrossing, renderChain } from "./steps.js";
|
|
18
18
|
import { UserError } from "./UserError.js";
|
|
19
|
-
import { COMMAND_VERBOSITY_ENV_VAR, readVerbosityEnv, resolveVerbosity } from "./verbosity.js";
|
|
19
|
+
import { COMMAND_VERBOSITY_ENV_VAR, readVerbosityEnv, resolveVerbosity, } from "./verbosity.js";
|
|
20
20
|
import { writeVerdict } from "./verdict.js";
|
|
21
21
|
const VERSION = readPackageVersion(import.meta.url);
|
|
22
22
|
const CROSSING_CONSEQUENCE = "so nmr handles the nested run's output as a tool's.";
|
package/dist/esm/runner.d.ts
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
import type { Writable } from 'node:stream';
|
|
2
2
|
import type { Step } from './steps.js';
|
|
3
|
+
/** Where one of a child's output streams goes: a descriptor the child writes to, or a pipe nmr reads. */
|
|
3
4
|
export type OutputChannel = number | 'pipe';
|
|
5
|
+
/** The channel each of a child's output streams runs on. */
|
|
4
6
|
export interface OutputChannels {
|
|
5
7
|
stderr: OutputChannel;
|
|
6
8
|
stdout: OutputChannel;
|
|
7
9
|
}
|
|
8
10
|
export interface RunCommandOptions {
|
|
11
|
+
/** The channel each of the child's output streams runs on. */
|
|
9
12
|
channels: OutputChannels;
|
|
13
|
+
/** When true, suppress output on success and write captured output to stderr on failure. */
|
|
10
14
|
quiet?: boolean;
|
|
15
|
+
/** Stream that subprocess stdout flows to in non-quiet mode. Defaults to `process.stdout`. */
|
|
11
16
|
stdout?: Writable;
|
|
17
|
+
/** Stream that subprocess stderr flows to (and that quiet-mode failure output is written to). Defaults to `process.stderr`. */
|
|
12
18
|
stderr?: Writable;
|
|
19
|
+
/** Environment for the subprocess. Defaults to `process.env`. */
|
|
13
20
|
env?: NodeJS.ProcessEnv;
|
|
14
21
|
}
|
|
22
|
+
/** What a step list needs to run. Each step's channels follow its kind, so the caller chooses none of them. */
|
|
15
23
|
export type RunStepsOptions = Omit<RunCommandOptions, 'channels'>;
|
|
24
|
+
/** A bounded copy of what a step list's own commands wrote, each stream kept apart from the other. */
|
|
16
25
|
export interface RetainedOutput {
|
|
17
26
|
stderr: Buffer;
|
|
18
27
|
stdout: Buffer;
|
|
19
28
|
}
|
|
20
29
|
export interface RunStepsResult {
|
|
21
30
|
exitCode: number;
|
|
31
|
+
/**
|
|
32
|
+
* What the list's opaque steps produced, absent unless every one of them was captured whole and there was
|
|
33
|
+
* at least one to capture.
|
|
34
|
+
*/
|
|
22
35
|
retained?: RetainedOutput;
|
|
23
36
|
}
|
|
37
|
+
/** How a command ended, and the exit code that carries that ending to nmr's own caller. */
|
|
24
38
|
type CommandCompletion = {
|
|
25
39
|
outcome: 'exited';
|
|
26
40
|
exitCode: number;
|
|
@@ -34,11 +48,49 @@ type CommandCompletion = {
|
|
|
34
48
|
error: Error;
|
|
35
49
|
};
|
|
36
50
|
export type RunCommandResult = CommandCompletion & {
|
|
51
|
+
/**
|
|
52
|
+
* Output retained while the command ran, elided in the middle when it overran the bound.
|
|
53
|
+
* `undefined` where the stream was handed to the child directly, or where capture stopped early
|
|
54
|
+
* and the retained copy would understate what the command produced.
|
|
55
|
+
*/
|
|
37
56
|
stdout: Buffer | undefined;
|
|
38
57
|
stderr: Buffer | undefined;
|
|
39
58
|
};
|
|
59
|
+
/**
|
|
60
|
+
* Returns the channel a stream runs on: the stream's own descriptor when it is a terminal and output is not
|
|
61
|
+
* being withheld, so the child's terminal detection matches nmr's, and a pipe everywhere else.
|
|
62
|
+
*/
|
|
40
63
|
export declare function resolveChannel(stream: Writable, quiet: boolean): OutputChannel;
|
|
64
|
+
/**
|
|
65
|
+
* Returns the channel a step running an nmr process below this one runs on: nmr's own descriptor whenever it
|
|
66
|
+
* has one, so the child writes where nmr writes and no ancestor stands between them relaying bytes. A stream
|
|
67
|
+
* carrying no descriptor, which is the one a test injects, falls back to a pipe.
|
|
68
|
+
*/
|
|
41
69
|
export declare function resolveInheritedChannel(stream: Writable): OutputChannel;
|
|
70
|
+
/**
|
|
71
|
+
* Runs a step list in order and resolves once one fails or all have run, carrying the `&&` semantics nmr
|
|
72
|
+
* promises: the first non-zero exit ends the sequence and is the code returned.
|
|
73
|
+
*
|
|
74
|
+
* Sequencing here rather than in a spawned shell is what lets each step run on the channels its kind calls
|
|
75
|
+
* for, and what makes a signal to nmr end the run: the steps after it are never reached, where a shell would
|
|
76
|
+
* have gone on running them unsupervised.
|
|
77
|
+
*
|
|
78
|
+
* Carries back a bounded copy of what the list's own commands wrote, so a caller can retain it. Only opaque
|
|
79
|
+
* steps contribute: a structural step is another nmr process reporting for the subtree beneath it, and its
|
|
80
|
+
* output is that subtree's to account for.
|
|
81
|
+
*/
|
|
42
82
|
export declare function runSteps(steps: readonly Step[], cwd: string | undefined, options: RunStepsOptions): Promise<RunStepsResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Runs a command through a shell and resolves once it has ended.
|
|
85
|
+
*
|
|
86
|
+
* The caller chooses each stream's channel. A descriptor is handed to the child and nmr sees none of what
|
|
87
|
+
* flows through it; a pipe is forwarded to the matching option stream as it arrives, with that destination's
|
|
88
|
+
* back-pressure throttling the child, and a bounded copy is retained. Quiet mode withholds the forwarding,
|
|
89
|
+
* discarding the retained copy on success and writing it to `options.stderr` on failure, so it has nothing to
|
|
90
|
+
* report unless the caller chose a pipe on both streams.
|
|
91
|
+
*
|
|
92
|
+
* Piped stdout and stderr are ordered by arrival rather than by a shared descriptor, so their interleaving
|
|
93
|
+
* is approximate.
|
|
94
|
+
*/
|
|
43
95
|
export declare function runCommand(command: string, cwd: string | undefined, options: RunCommandOptions): Promise<RunCommandResult>;
|
|
44
96
|
export {};
|
package/dist/esm/scripts.d.ts
CHANGED
package/dist/esm/steps.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One element of a resolved command chain, carrying how it was composed rather than how its text reads.
|
|
3
|
+
* A `structural` step is nmr's own composition, held as argv; an `opaque` step is a command nmr does not parse.
|
|
4
|
+
*
|
|
5
|
+
* A structural step's argv leads with the file to spawn, so the runner has one to hand `spawn` without a shell.
|
|
6
|
+
*
|
|
7
|
+
* `declinesArgs` travels from the composite element that composed the step to the one reader that acts on it,
|
|
8
|
+
* the binding of the invocation's trailing arguments. Every stage between the two -- the devBin substitution,
|
|
9
|
+
* the chain rendering, the replay assembly, the runner -- passes it through and asks nothing of it.
|
|
10
|
+
*/
|
|
1
11
|
export type Step = {
|
|
2
12
|
kind: 'opaque';
|
|
3
13
|
command: string;
|
|
@@ -6,19 +16,80 @@ export type Step = {
|
|
|
6
16
|
argv: readonly [string, ...(readonly string[])];
|
|
7
17
|
declinesArgs?: boolean;
|
|
8
18
|
};
|
|
19
|
+
/** What a structural step asks of the nmr process it spawns. */
|
|
9
20
|
export interface NmrStepTarget {
|
|
10
21
|
command: string;
|
|
22
|
+
/** Whether the command is fanned out to other scopes, where a `-R` or `-F` sends it. */
|
|
11
23
|
isDelegate: boolean;
|
|
24
|
+
/** Whether the command runs against the root registry, where a `-w` anchors it. */
|
|
12
25
|
isWorkspaceRoot: boolean;
|
|
13
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* How a `package.json` entry names the command it is declared under: as its whole value, or alongside other
|
|
29
|
+
* steps. `sole` covers an entry carrying trailing arguments, which declare no step of their own.
|
|
30
|
+
*/
|
|
14
31
|
export type SelfReference = 'chained' | 'sole';
|
|
32
|
+
/**
|
|
33
|
+
* Composes the structural step that re-invokes nmr for one composite element.
|
|
34
|
+
*
|
|
35
|
+
* The element tokenizes on whitespace, so it may carry nmr's own flags but cannot carry a space-bearing token.
|
|
36
|
+
* `-w` is prepended as its own token, so the child selects the root registry on its own.
|
|
37
|
+
*
|
|
38
|
+
* `declinesArgs` is set only where it holds, so a step that takes the trailing arguments renders and compares
|
|
39
|
+
* exactly as it did before any element declared anything.
|
|
40
|
+
*/
|
|
15
41
|
export declare function composeNmrStep(element: string, workspaceRoot: boolean, declinesArgs?: boolean): Step;
|
|
42
|
+
/**
|
|
43
|
+
* Returns the text of the first opaque step that reaches nmr through a shell, or `undefined` when none does.
|
|
44
|
+
*
|
|
45
|
+
* Recognizes nmr in command position: at the start of the step, after `&&`, `||`, `;`, `|`, or a newline, past
|
|
46
|
+
* any leading environment assignments, and behind a launcher such as `npx` or `pnpm exec`. A separator inside
|
|
47
|
+
* quotes opens no position, so a command merely naming nmr in an argument is not a crossing.
|
|
48
|
+
*
|
|
49
|
+
* Partial by construction, and partial in stated ways rather than arbitrary ones: a value-taking flag standing
|
|
50
|
+
* immediately before the program name hides it (`npx -p foo nmr`), and a launcher outside `LAUNCHERS` goes
|
|
51
|
+
* unreported. What it finds is the boundary below which nmr cannot tell its own processes from the tools it
|
|
52
|
+
* runs.
|
|
53
|
+
*/
|
|
16
54
|
export declare function findNmrCrossing(steps: readonly Step[]): string | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Returns the first token of a composite element that falls outside the grammar, or `undefined` when the
|
|
57
|
+
* element is a command name optionally preceded by nmr's own flags. A token the shell would not read
|
|
58
|
+
* literally is one the rendering would quote whole, turning the element into a command nobody wrote.
|
|
59
|
+
*/
|
|
17
60
|
export declare function findUnexpressibleToken(element: string): string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Returns the command a structural step's nmr process runs, whether the step hands that command to other
|
|
63
|
+
* scopes rather than running it where it stands, and whether it anchors the command at the monorepo root.
|
|
64
|
+
* Reports nothing for a step that names no nmr command.
|
|
65
|
+
*
|
|
66
|
+
* The inverse of `composeNmrStep`, and beside it because the two share one grammar: an element may lead with
|
|
67
|
+
* nmr's own flags, and the command is the first token that is not one.
|
|
68
|
+
*/
|
|
18
69
|
export declare function readNmrStep(step: Step): NmrStepTarget | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Reports whether a `package.json` entry re-invokes the command it is declared under, and whether it declares
|
|
72
|
+
* anything besides. Reports nothing for an entry that names another command, or none.
|
|
73
|
+
*
|
|
74
|
+
* Honouring such an entry would spawn a shell running the same command in the same directory, reaching the
|
|
75
|
+
* same entry again without bound, so resolution discards it however it reads. `chained` is what separates an
|
|
76
|
+
* entry that thereby loses steps from one that declares nothing to lose.
|
|
77
|
+
*
|
|
78
|
+
* A segment that delegates carries the command to other scopes rather than back to this one. `-w` re-enters
|
|
79
|
+
* only from the root, a package's entry reaching the root's registry and `package.json` instead of its own.
|
|
80
|
+
*
|
|
81
|
+
* Partial in the same ways `findNmrCrossing` is, and for the same reason: what goes unrecognized re-enters
|
|
82
|
+
* without bound, which hangs rather than passing quietly.
|
|
83
|
+
*/
|
|
19
84
|
export declare function readSelfReference(options: {
|
|
20
85
|
anchoredAtRoot: boolean;
|
|
21
86
|
commandName: string;
|
|
22
87
|
script: string;
|
|
23
88
|
}): SelfReference | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* Renders a step list as the `&&` chain a shell runs.
|
|
91
|
+
*
|
|
92
|
+
* The sole producer of a chain string: the check-result cache keys on that string, so a change to the rendering
|
|
93
|
+
* invalidates every recorded pass by construction rather than by anyone remembering to.
|
|
94
|
+
*/
|
|
24
95
|
export declare function renderChain(steps: readonly Step[]): string;
|
package/dist/esm/taze.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { CheckOptions } from 'taze';
|
|
2
|
+
/**
|
|
3
|
+
* A taze configuration.
|
|
4
|
+
*
|
|
5
|
+
* Every property admits `undefined` explicitly, unlike `Partial<CheckOptions>`, which rejects it under
|
|
6
|
+
* `exactOptionalPropertyTypes`. Passing `undefined` is how a consumer clears `maturityPeriod`, so the type
|
|
7
|
+
* has to allow it.
|
|
8
|
+
*/
|
|
2
9
|
export type TazeConfig = {
|
|
3
10
|
[K in keyof CheckOptions]?: CheckOptions[K] | undefined;
|
|
4
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Builds a taze configuration from nmr's shared upgrade policy and a repo's own settings.
|
|
14
|
+
*
|
|
15
|
+
* Any property the caller supplies wins, including `0` and an explicit `undefined` — the latter restores
|
|
16
|
+
* taze's inheritance of pnpm's `minimumReleaseAge`, which only applies while `maturityPeriod` is unset.
|
|
17
|
+
*
|
|
18
|
+
* The merge is shallow, which suits a policy of scalar defaults. A nested default would clobber rather
|
|
19
|
+
* than merge, so adding one means revisiting this.
|
|
20
|
+
*/
|
|
5
21
|
export declare function defineConfig(config: TazeConfig): TazeConfig;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declares a suite asserting that a repo's test files hold to nmr's conventions: every collected file names an
|
|
3
|
+
* isolation tier, and every test file sits under a `__tests__` directory.
|
|
4
|
+
*
|
|
5
|
+
* Wire it from a one-line test of the repo's own, so the file stays under the repo's `__tests__` and the repo
|
|
6
|
+
* scopes what the sweep covers. Both halves are silent without it: `unit` is the residual project, so an untiered
|
|
7
|
+
* file runs and passes, and a file outside `__tests__` is collected by nothing at all.
|
|
8
|
+
*
|
|
9
|
+
* `exclude` names directory basenames the sweep prunes at any depth, additive to the ones nmr always prunes. Pass
|
|
10
|
+
* the same array to `defineVitestConfig`'s `testCollectionExclude`, or the sweep and the collection glob describe
|
|
11
|
+
* different trees: a directory pruned here alone still runs the files this suite stopped reporting.
|
|
12
|
+
*/
|
|
13
|
+
export declare function checkTestFileConventions(options?: TestFileConventionsOptions): void;
|
|
14
|
+
/**
|
|
15
|
+
* Sweeps a repo for both halves of the convention, reporting the root it swept alongside what it found.
|
|
16
|
+
*
|
|
17
|
+
* The root is what makes the result trustworthy, and it is the one thing an assertion cannot check: a sweep
|
|
18
|
+
* started from the directory Vitest supplies covers one package, finds nothing, and passes.
|
|
19
|
+
*
|
|
20
|
+
* @internal - Exported only to enable testing
|
|
21
|
+
*/
|
|
22
|
+
export declare function reportTestFileConventions({ exclude, rootDir, }?: TestFileConventionsOptions): TestFileConventionsReport;
|
|
23
|
+
export interface TestFileConventionsOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Directory basenames the sweep prunes at any depth, additive to the ones nmr always prunes. The array a repo
|
|
26
|
+
* passes here is the array it passes to `defineVitestConfig`'s `testCollectionExclude`.
|
|
27
|
+
*/
|
|
28
|
+
exclude?: readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* The directory to sweep. Defaults to the monorepo root found from the working directory, so one call covers the
|
|
31
|
+
* whole repo from whichever package Vitest started in.
|
|
32
|
+
*/
|
|
33
|
+
rootDir?: string;
|
|
34
|
+
}
|
|
35
|
+
/** What one sweep found, and the root it found it in. Paths are relative to that root and POSIX-separated. */
|
|
36
|
+
export interface TestFileConventionsReport {
|
|
37
|
+
misplaced: string[];
|
|
38
|
+
rootDir: string;
|
|
39
|
+
untiered: string[];
|
|
40
|
+
}
|