@williamthorsen/nmr 0.39.0 → 0.41.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 +145 -4
- package/.readyup/kits/default.js +47 -32
- package/.readyup/manifest.json +12 -7
- package/CHANGELOG.md +44 -0
- package/README.md +36 -75
- package/agents/README.md +8 -8
- package/agents/guidance/rulebooks/nmr.md +8 -8
- package/bin/ensure-prepublish-hooks.js +1 -1
- package/bin/nmr-clean.js +1 -1
- package/bin/nmr-compile.js +1 -1
- package/bin/nmr-fmt.js +1 -1
- package/bin/nmr-report-catalog.js +1 -1
- package/bin/nmr-report-overrides.js +1 -1
- package/bin/nmr-taze.js +1 -1
- package/bin/nmr.js +1 -1
- package/dist/esm/UserError.d.ts +1 -2
- package/dist/esm/check-cache.d.ts +59 -61
- package/dist/esm/commands/build-output.d.ts +35 -36
- package/dist/esm/commands/build.d.ts +13 -14
- package/dist/esm/commands/build.js +3 -3
- package/dist/esm/commands/clean.d.ts +6 -6
- package/dist/esm/commands/ensure-prepublish-hooks.d.ts +5 -5
- package/dist/esm/commands/fmt.d.ts +8 -8
- package/dist/esm/commands/fmt.js +1 -1
- package/dist/esm/commands/report-catalog.d.ts +5 -7
- package/dist/esm/commands/report-catalog.js +1 -1
- package/dist/esm/commands/report-overrides.d.ts +0 -2
- package/dist/esm/commands/report-overrides.js +1 -1
- package/dist/esm/commands/taze.d.ts +9 -8
- package/dist/esm/config.d.ts +5 -5
- package/dist/esm/config.js +3 -3
- package/dist/esm/context.d.ts +1 -2
- package/dist/esm/default-scripts.d.ts +5 -6
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/git-ignored-paths.d.ts +9 -0
- package/dist/esm/git-ignored-paths.js +10 -0
- package/dist/esm/glyphs.d.ts +3 -3
- package/dist/esm/help.d.ts +2 -2
- package/dist/esm/helpers/clampToBytes.d.ts +4 -5
- package/dist/esm/helpers/config-path.d.ts +0 -2
- package/dist/esm/helpers/createBoundedBuffer.d.ts +2 -2
- package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
- package/dist/esm/helpers/duration.d.ts +8 -9
- package/dist/esm/helpers/filter-selection.d.ts +19 -19
- package/dist/esm/helpers/findClosestName.d.ts +1 -1
- package/dist/esm/helpers/hook-name.d.ts +2 -5
- package/dist/esm/helpers/package-json.d.ts +9 -8
- package/dist/esm/helpers/readStringValues.d.ts +2 -3
- package/dist/esm/helpers/reportClosing.d.ts +8 -9
- package/dist/esm/helpers/transcript.d.ts +13 -13
- package/dist/esm/helpers/type-guards.d.ts +1 -0
- package/dist/esm/output-style.d.ts +12 -13
- package/dist/esm/prettier.d.ts +2 -2
- package/dist/esm/recording.d.ts +20 -15
- package/dist/esm/recording.js +7 -7
- package/dist/esm/replay-assembly.d.ts +10 -10
- package/dist/esm/report-format.d.ts +14 -19
- package/dist/esm/reportCliFailure.d.ts +2 -3
- package/dist/esm/resolve-scripts.d.ts +4 -5
- package/dist/esm/resolveBinStyles.d.ts +4 -5
- package/dist/esm/resolver.d.ts +14 -18
- package/dist/esm/runCli.d.ts +6 -6
- package/dist/esm/runCli.js +104 -27
- package/dist/esm/runner.d.ts +19 -19
- package/dist/esm/steps.d.ts +45 -42
- package/dist/esm/steps.js +6 -6
- package/dist/esm/taze.d.ts +1 -1
- package/dist/esm/tests.d.ts +18 -13
- package/dist/esm/tests.js +2 -2
- package/dist/esm/tiers.d.ts +27 -23
- package/dist/esm/tiers.js +6 -2
- package/dist/esm/types.d.ts +5 -5
- package/dist/esm/verbosity.d.ts +22 -20
- package/dist/esm/verdict.d.ts +33 -33
- package/dist/esm/verdict.js +1 -1
- package/dist/esm/vitest-source-resolution.d.ts +11 -11
- package/dist/esm/vitest-source-resolution.js +2 -2
- package/dist/esm/vitest.d.ts +25 -24
- package/dist/esm/vitest.js +16 -8
- package/dist/esm/workspace.d.ts +4 -4
- package/docs/check-cache.md +57 -57
- package/docs/prettier.md +10 -10
- package/docs/reporting.md +46 -46
- package/docs/scripts.md +29 -29
- package/docs/upgrades.md +18 -18
- package/docs/utilities.md +13 -13
- package/docs/vitest.md +64 -62
- package/docs/workspace.md +19 -19
- package/package.json +6 -5
|
@@ -2,9 +2,9 @@ import type { Writable } from 'node:stream';
|
|
|
2
2
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
3
3
|
import type { OutputChannels } from './runner.js';
|
|
4
4
|
import type { CheckCacheConfig, NmrConfig } from './types.js';
|
|
5
|
-
/** A recorded pass: what ran, on which tree, and
|
|
5
|
+
/** A recorded pass: what ran, on which tree, and how long it took. */
|
|
6
6
|
export interface CheckCacheEntry {
|
|
7
|
-
/** The full cache key the pass was recorded
|
|
7
|
+
/** The full cache key under which the pass was recorded; a hit is this key matching the key computed now. */
|
|
8
8
|
key: string;
|
|
9
9
|
treeHash: string;
|
|
10
10
|
headSha: string;
|
|
@@ -15,12 +15,12 @@ export interface CheckCacheEntry {
|
|
|
15
15
|
/** ISO-8601 instant the pass completed. */
|
|
16
16
|
recordedAt: string;
|
|
17
17
|
/**
|
|
18
|
-
* The build digest each covered package's output
|
|
19
|
-
* git-ignored, so the tree hash cannot describe it; comparing these
|
|
20
|
-
*
|
|
18
|
+
* The build digest that each covered package's output had when the pass was recorded. Build output is
|
|
19
|
+
* git-ignored, so the tree hash cannot describe it; comparing these separates output built from this tree from
|
|
20
|
+
* output that another tree left behind.
|
|
21
21
|
*/
|
|
22
22
|
buildDigests: Record<string, string>;
|
|
23
|
-
/** What a skip replays in place of the run it recalls, absent on a pass that retained nothing. */
|
|
23
|
+
/** What a skip replays in place of the run that it recalls, absent on a pass that retained nothing. */
|
|
24
24
|
retention?: Retention;
|
|
25
25
|
}
|
|
26
26
|
/** What locates one command's recorded pass at one scope, and the transcript beside it. */
|
|
@@ -39,11 +39,11 @@ export interface ReplayLine {
|
|
|
39
39
|
* What a recalled pass replays, the key certifying the excerpts describe this environment's output, and the
|
|
40
40
|
* run that last vouched for them.
|
|
41
41
|
*
|
|
42
|
-
* A list rather than one excerpt: a composite's entry
|
|
43
|
-
* spliced into its parent's,
|
|
42
|
+
* A list rather than one excerpt: a composite's entry contains its constituents' lines, and a nested one's are
|
|
43
|
+
* spliced into its parent's, after which the attribution cannot be re-derived from the entry that contains them.
|
|
44
44
|
*
|
|
45
|
-
* The witness
|
|
46
|
-
*
|
|
45
|
+
* The witness admits a constituent's line into the assembly that its parent records: a run writes it when it
|
|
46
|
+
* records an excerpt, and restamps it when it recalls one and replays it.
|
|
47
47
|
*/
|
|
48
48
|
export interface Retention {
|
|
49
49
|
key: string;
|
|
@@ -54,7 +54,7 @@ export interface Retention {
|
|
|
54
54
|
export interface BuildOutputState {
|
|
55
55
|
/** Covered packages whose output is absent. */
|
|
56
56
|
missingPackages: string[];
|
|
57
|
-
/** The digest of the inputs each covered package's output was built
|
|
57
|
+
/** The digest of the inputs from which each covered package's output was built, keyed by package name. */
|
|
58
58
|
digests: Record<string, string>;
|
|
59
59
|
}
|
|
60
60
|
/** The parts of the running interpreter that can change what a check concludes. */
|
|
@@ -68,7 +68,7 @@ export interface TreeSnapshot {
|
|
|
68
68
|
hash: string;
|
|
69
69
|
headSha: string;
|
|
70
70
|
}
|
|
71
|
-
/** The interpreter this process is running
|
|
71
|
+
/** The interpreter on which this process is running, folded into every key and recorded alongside every pass. */
|
|
72
72
|
export declare const CURRENT_RUNTIME: RuntimeIdentity;
|
|
73
73
|
/** Set to `1` to hear why the gate did not skip, or why it is disabled. */
|
|
74
74
|
export declare const DEBUG_ENV_VAR = "NMR_DEBUG";
|
|
@@ -76,29 +76,30 @@ export declare const DEBUG_ENV_VAR = "NMR_DEBUG";
|
|
|
76
76
|
* The commands cacheable without configuration: those whose whole contribution is an exit status, and that
|
|
77
77
|
* reach nothing beyond a checkout and an install.
|
|
78
78
|
*
|
|
79
|
-
* Excluded
|
|
80
|
-
* (`prepush`'s `ci` constituent still skips while its `audit` always runs). `build` and `compile`
|
|
81
|
-
* of their own. `fix`, `fmt`, `lint`, and `upgrade` mutate the tree they are asked
|
|
79
|
+
* Excluded commands: `audit` and `prepush` consult a vulnerability database that changes without the tree
|
|
80
|
+
* (`prepush`'s `ci` constituent still skips while its `audit` always runs). `build` and `compile` have a cache
|
|
81
|
+
* of their own. `fix`, `fmt`, `lint`, and `upgrade` mutate the tree about which they are asked. `test:all` reaches
|
|
82
82
|
* whatever the environment supplies. Anything a repo adds here promises exit-status-only semantics through its
|
|
83
83
|
* whole chain, hooks included.
|
|
84
84
|
*/
|
|
85
85
|
export declare const DEFAULT_CACHEABLE_COMMANDS: string[];
|
|
86
86
|
/** Set to `1` for the standing equivalent of `--no-cache`: skip the lookup, still record on success. */
|
|
87
87
|
export declare const NO_CACHE_ENV_VAR = "NMR_NO_CACHE";
|
|
88
|
-
/**
|
|
88
|
+
/** Passes one run's identity down the spawned chain, so that an entry can name the run that vouched for it. */
|
|
89
89
|
export declare const RUN_ID_ENV_VAR = "NMR_RUN_ID";
|
|
90
|
-
/**
|
|
90
|
+
/** Passes the top-level tree snapshot down the spawned chain, to make one invocation hash the tree only once. */
|
|
91
91
|
export declare const TREE_SNAPSHOT_ENV_VAR = "NMR_TREE_SNAPSHOT";
|
|
92
92
|
/**
|
|
93
|
-
* Restamps a recalled entry's retention with the run that is replaying it, so an excerpt
|
|
94
|
-
* can join the assembly a composite above it
|
|
93
|
+
* Restamps a recalled entry's retention with the run that is replaying it, so that an excerpt certified by this
|
|
94
|
+
* run can join the assembly recorded by a composite above it.
|
|
95
95
|
*
|
|
96
|
-
* A recall certifies as surely as a recording does
|
|
97
|
-
*
|
|
98
|
-
* environment. Everything else the entry records stands -- the instant and the duration belong to
|
|
99
|
-
* earned the pass, and a recall must not make it read as later or longer than it was.
|
|
96
|
+
* A recall certifies as surely as a recording does. Because the pass key matched, the excerpt describes this
|
|
97
|
+
* tree; because the caller calls this only when the retention key matched too, the excerpt also describes this
|
|
98
|
+
* presentation environment. Everything else the entry records stands -- the instant and the duration belong to
|
|
99
|
+
* the run that earned the pass, and a recall must not make it read as later or longer than it was.
|
|
100
100
|
*
|
|
101
|
-
* A cache that cannot be written is not worth failing a green run over
|
|
101
|
+
* A cache that cannot be written is not worth failing a green run over: `certifyRetention` reports that failure
|
|
102
|
+
* in the debug note.
|
|
102
103
|
*/
|
|
103
104
|
export declare function certifyRetention(options: {
|
|
104
105
|
anchorDir: string;
|
|
@@ -111,8 +112,8 @@ export declare function certifyRetention(options: {
|
|
|
111
112
|
}): Promise<void>;
|
|
112
113
|
/**
|
|
113
114
|
* Folds everything that can change what a command concludes into one key: the tree's content, the command
|
|
114
|
-
* string that would run, the scope it would run
|
|
115
|
-
* the environment variables a check can read. A hit is this key matching a recorded one, so an ingredient left
|
|
115
|
+
* string that would run, the scope in which it would run, nmr's own version, the interpreter, what is installed,
|
|
116
|
+
* and the environment variables that a check can read. A hit is this key matching a recorded one, so an ingredient left
|
|
116
117
|
* out here is an ingredient that could change while the cache still claims a pass.
|
|
117
118
|
*
|
|
118
119
|
* Reports a reason instead of a key when the install fingerprint cannot be read, which disables the gate.
|
|
@@ -134,13 +135,12 @@ export declare function computeCacheKey(options: {
|
|
|
134
135
|
reason: string;
|
|
135
136
|
};
|
|
136
137
|
/**
|
|
137
|
-
* Folds what changes a transcript without changing a conclusion onto the pass key: the channel each of
|
|
138
|
-
* command's output streams ran
|
|
138
|
+
* Folds what changes a transcript without changing a conclusion onto the pass key: the channel on which each of
|
|
139
|
+
* the command's output streams ran, and the environment variables through which a tool presents itself.
|
|
139
140
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* transcript is reproducible.
|
|
141
|
+
* The channel kind keeps a run at a terminal from replaying a piped recording. It is the channel's kind and not
|
|
142
|
+
* whether a terminal is attached: under quiet mode the child writes to pipes at a terminal, and its transcript
|
|
143
|
+
* is reproducible.
|
|
144
144
|
*/
|
|
145
145
|
export declare function computeRetentionKey(options: {
|
|
146
146
|
channels: OutputChannels;
|
|
@@ -152,39 +152,38 @@ export declare function encodeTreeSnapshot(snapshot: TreeSnapshot): string;
|
|
|
152
152
|
/**
|
|
153
153
|
* Names a covered package whose output differs between two observations of it, or `undefined` when every
|
|
154
154
|
* package agrees. A package that has appeared or disappeared between them counts as a disagreement, because
|
|
155
|
-
* the output the earlier observation describes is not the output the later one found.
|
|
155
|
+
* the output that the earlier observation describes is not the output that the later one found.
|
|
156
156
|
*/
|
|
157
157
|
export declare function findStaleBuildOutput(earlierDigests: Record<string, string>, laterDigests: Record<string, string>): string | undefined;
|
|
158
158
|
/**
|
|
159
|
-
* Renders the warning for a `--no-cache` that
|
|
160
|
-
* command rather than a flag to nmr.
|
|
161
|
-
*
|
|
159
|
+
* Renders the warning for a `--no-cache` that appears after the command name, which makes it an argument to
|
|
160
|
+
* the command rather than a flag to nmr. nmr passes the argument on unchanged, so the warning is the only sign
|
|
161
|
+
* that nmr did not read it.
|
|
162
162
|
*/
|
|
163
163
|
export declare function formatMisplacedNoCacheWarning(command: string, style: OutputStyle): string;
|
|
164
164
|
/**
|
|
165
|
-
* Reports whether a command's passes are recorded at all, which
|
|
166
|
-
*
|
|
165
|
+
* Reports whether a command's passes are recorded at all, which separates a command without a recording from
|
|
166
|
+
* one that could never have had one.
|
|
167
167
|
*
|
|
168
|
-
* A hook is
|
|
169
|
-
* it
|
|
168
|
+
* A hook is never cacheable, even when a repo names one in `extraCommands`: nothing records a hook, and
|
|
169
|
+
* excluding it here keeps the gate and every reader of its entries in agreement.
|
|
170
170
|
*/
|
|
171
171
|
export declare function isCacheableCommand(checkCache: CheckCacheConfig | undefined, command: string): boolean;
|
|
172
172
|
/**
|
|
173
|
-
* Reads the state of the build output nmr's own build covers. Build output is git-ignored, so the tree hash
|
|
173
|
+
* Reads the state of the build output that nmr's own build covers. Build output is git-ignored, so the tree hash
|
|
174
174
|
* says nothing about it: a `ci` whose `build` constituent is cached would otherwise skip on a tree whose `dist`
|
|
175
175
|
* had been deleted, or whose `dist` was compiled from a different tree, and hand back a green exit over a
|
|
176
176
|
* repository that cannot run.
|
|
177
177
|
*
|
|
178
|
-
*
|
|
179
|
-
* out rather than made a permanent miss.
|
|
178
|
+
* Because a package whose `build` or `compile` is overridden emits to a location that this function does not
|
|
179
|
+
* know about, it is left out rather than made a permanent miss.
|
|
180
180
|
*/
|
|
181
181
|
export declare function readBuildOutputState(monorepoRoot: string, config: NmrConfig): Promise<BuildOutputState>;
|
|
182
182
|
/**
|
|
183
183
|
* Reads the entry recorded for one command at one scope, or `undefined` when there is none to trust.
|
|
184
184
|
*
|
|
185
|
-
* Retention is vouched for separately from the pass
|
|
186
|
-
* dropped, leaving a pass that skips cleanly and reports its verdict alone.
|
|
187
|
-
* cost a full run to avoid a line nobody would have printed.
|
|
185
|
+
* Retention is vouched for separately from the pass that contains it: an excerpt of a shape that this function
|
|
186
|
+
* cannot read is dropped, leaving a pass that skips cleanly and reports its verdict alone.
|
|
188
187
|
*/
|
|
189
188
|
export declare function readCheckCacheEntry(options: {
|
|
190
189
|
anchorDir: string;
|
|
@@ -192,9 +191,9 @@ export declare function readCheckCacheEntry(options: {
|
|
|
192
191
|
monorepoRoot: string;
|
|
193
192
|
}): Promise<CheckCacheEntry | undefined>;
|
|
194
193
|
/**
|
|
195
|
-
* Reads the whole output one recorded pass retained, or `undefined`
|
|
194
|
+
* Reads the whole output that one recorded pass retained, or `undefined` when it retained none.
|
|
196
195
|
*
|
|
197
|
-
* Held to nothing on its own: the entry beside it
|
|
196
|
+
* Held to nothing on its own: the entry beside it says which tree the bytes describe, and a caller
|
|
198
197
|
* that has not matched the entry's key is reading a transcript of some other tree.
|
|
199
198
|
*/
|
|
200
199
|
export declare function readTranscript(ref: EntryRef): Promise<string | undefined>;
|
|
@@ -202,33 +201,32 @@ export declare function readTranscript(ref: EntryRef): Promise<string | undefine
|
|
|
202
201
|
* Makes the transcript beside one entry be exactly what this pass retained, removing what an earlier pass
|
|
203
202
|
* left when this one retained nothing.
|
|
204
203
|
*
|
|
205
|
-
* A composite retains nothing of its own, so without the removal a leaf's transcript would
|
|
204
|
+
* A composite retains nothing of its own, so without the removal a leaf's transcript would remain beside an
|
|
206
205
|
* entry that never produced it, and `--log` would date another run's bytes by this one's instant.
|
|
207
206
|
*/
|
|
208
207
|
export declare function recordTranscript(ref: EntryRef, transcript: string | undefined): Promise<void>;
|
|
209
208
|
/** Removes every recorded pass for a monorepo, or for a standalone package outside one. */
|
|
210
209
|
export declare function removeCheckCache(scopeDir: string): Promise<void>;
|
|
211
210
|
/**
|
|
212
|
-
* Merges a repo's `checkCache` configuration into the default set
|
|
213
|
-
*
|
|
214
|
-
* turned out to do more than report an exit status.
|
|
211
|
+
* Merges a repo's `checkCache` configuration into the default set: `extraCommands` adds to the defaults, and
|
|
212
|
+
* `excludeCommands` removes a command whose chain does more than report an exit status.
|
|
215
213
|
*/
|
|
216
214
|
export declare function resolveCacheableCommands(checkCache: CheckCacheConfig | undefined): Set<string>;
|
|
217
215
|
/**
|
|
218
|
-
* Resolves the identity of the run this invocation belongs
|
|
219
|
-
* and otherwise a fresh one,
|
|
216
|
+
* Resolves the identity of the run to which this invocation belongs: the one that an ancestor nmr process passed
|
|
217
|
+
* down, and otherwise a fresh one, because the run starts at this invocation.
|
|
220
218
|
*
|
|
221
|
-
* Unbounded
|
|
222
|
-
* stale identity to the invocations it later makes, and
|
|
223
|
-
* constituent entry is held to before its excerpt joins an assembly.
|
|
219
|
+
* Unbounded, whereas the tree snapshot is bounded by a HEAD comparison: a process that outlives its run hands a
|
|
220
|
+
* stale identity to the invocations that it later makes, and the tree hash keeps that harmless, because every
|
|
221
|
+
* constituent entry is held to it before its excerpt joins an assembly.
|
|
224
222
|
*/
|
|
225
223
|
export declare function resolveRunId(env: NodeJS.ProcessEnv): string;
|
|
226
224
|
/**
|
|
227
|
-
* Resolves the tree snapshot this invocation gates
|
|
228
|
-
* is one, and otherwise a fresh hash of the working tree. Reports a reason instead when
|
|
229
|
-
*
|
|
225
|
+
* Resolves the tree snapshot on which this invocation gates: the one that a parent nmr process already took,
|
|
226
|
+
* when there is one, and otherwise a fresh hash of the working tree. Reports a reason instead when a snapshot
|
|
227
|
+
* cannot be taken, which disables the gate.
|
|
230
228
|
*
|
|
231
|
-
* The monorepo root must be the git toplevel. A repository
|
|
229
|
+
* The monorepo root must be the git toplevel. A repository containing the monorepo inside a subdirectory has
|
|
232
230
|
* content outside it that the checks may still read, and a hash covering more than the monorepo would move
|
|
233
231
|
* for edits that cannot affect it.
|
|
234
232
|
*/
|
|
@@ -6,42 +6,42 @@ export interface BuildOptions {
|
|
|
6
6
|
ignorePatterns?: string[];
|
|
7
7
|
outdir?: string;
|
|
8
8
|
}
|
|
9
|
-
/** The pair of directories a build publishes
|
|
9
|
+
/** The pair of directories through which a build publishes, both siblings of the emit directory. */
|
|
10
10
|
export interface ScratchDirs {
|
|
11
11
|
previousDir: string;
|
|
12
12
|
stagingDir: string;
|
|
13
13
|
}
|
|
14
14
|
export declare const DEFAULT_ENTRY_GLOBS: string[];
|
|
15
15
|
/**
|
|
16
|
-
* Directories holding test scaffolding rather than
|
|
17
|
-
* package does not publish its own helpers.
|
|
18
|
-
*
|
|
19
|
-
* without converging and neither can be derived from the other.
|
|
16
|
+
* Directories holding test scaffolding rather than published code, excluded from entry-point selection so that a
|
|
17
|
+
* package does not publish its own helpers. The list differs from the vitest factory's `COVERAGE_EXCLUDE`, which
|
|
18
|
+
* leaves `test-utils/` inside the coverage include set, and neither list can be derived from the other.
|
|
20
19
|
*
|
|
21
20
|
* Ignoring a file removes it as an entry point, not from the emit. The compiler still emits whatever the
|
|
22
|
-
* surviving entry points import, which
|
|
23
|
-
*
|
|
21
|
+
* surviving entry points import, which keeps a production module that uses a helper from emitting a dangling
|
|
22
|
+
* specifier. Widening this list can therefore only drop files that production code does not import.
|
|
24
23
|
*/
|
|
25
24
|
export declare const DEFAULT_IGNORE_PATTERNS: string[];
|
|
26
25
|
export declare const DEFAULT_OUTDIR = "dist/esm/";
|
|
27
26
|
/**
|
|
28
|
-
* Reports whether the output a build of `packageDir` would produce is currently on disk. Applies the
|
|
29
|
-
*
|
|
30
|
-
* whether to rebuild cannot disagree:
|
|
31
|
-
* present. Passing options the build was not given
|
|
27
|
+
* Reports whether the output that a build of `packageDir` would produce is currently on disk. Applies the build's
|
|
28
|
+
* own rule, on the same options that the build was given, so a caller probing for output and the build deciding
|
|
29
|
+
* whether to rebuild cannot disagree: A package whose entry points emit nothing does not expect any output, and
|
|
30
|
+
* reports present. Passing options that the build was not given would make the two disagree.
|
|
32
31
|
*/
|
|
33
32
|
export declare function hasBuildOutput(packageDir: string, options?: BuildOptions): Promise<boolean>;
|
|
34
33
|
/**
|
|
35
|
-
* Reports whether the output a previous build would have produced is still on disk.
|
|
36
|
-
* emit nothing expect
|
|
37
|
-
* declaration files, or none at all, would otherwise be reported as missing output and recompiled forever.
|
|
38
|
-
* The emit
|
|
34
|
+
* Reports whether the output that a previous build would have produced is still on disk. Because entry points
|
|
35
|
+
* that emit nothing do not expect any output, their absent outdir is not deleted output: A `src` tree holding
|
|
36
|
+
* only declaration files, or none at all, would otherwise be reported as missing output and recompiled forever.
|
|
37
|
+
* The emit creates an outdir, so the test is whether any entry point emits, not how many there are.
|
|
39
38
|
*/
|
|
40
39
|
export declare function hasExpectedBuildOutput(packageDir: string, outdir: string, entryPoints: string[]): boolean;
|
|
41
40
|
/**
|
|
42
|
-
* Returns the digest of the inputs the output currently on disk was built
|
|
43
|
-
* has never been built. Two packages
|
|
44
|
-
* came from a different tree reports a different one, which
|
|
41
|
+
* Returns the digest of the inputs from which the output currently on disk was built, or `undefined` when the
|
|
42
|
+
* package has never been built. Two packages containing the same sources report the same digest, and a package
|
|
43
|
+
* whose output came from a different tree reports a different one, which distinguishes a stale `dist` from a
|
|
44
|
+
* current one.
|
|
45
45
|
*/
|
|
46
46
|
export declare function readBuildDigest(packageDir: string): Promise<string | undefined>;
|
|
47
47
|
/**
|
|
@@ -49,35 +49,34 @@ export declare function readBuildDigest(packageDir: string): Promise<string | un
|
|
|
49
49
|
* directory is the entry's scope, so the store keys the file to it and packages sharing a hoisted
|
|
50
50
|
* `node_modules` never collide.
|
|
51
51
|
*
|
|
52
|
-
* Three things
|
|
53
|
-
* package's own directory name as the slug, and the absence of `discriminators`, which would otherwise
|
|
54
|
-
*
|
|
52
|
+
* Three things keep the path pointing at every previously written entry: the `.hash` extension, the
|
|
53
|
+
* package's own directory name as the slug, and the absence of `discriminators`, which would otherwise be added
|
|
54
|
+
* to the digest. Changing any of them leaves every entry on disk unreachable.
|
|
55
55
|
*/
|
|
56
56
|
export declare function resolveBuildCachePath(packageDir: string): string;
|
|
57
57
|
/**
|
|
58
|
-
* Resolves the two scratch directories a build publishes
|
|
59
|
-
* `previous`, which the outgoing output is renamed aside to. Both are siblings of the emit directory, so a
|
|
60
|
-
* rename between them never crosses a filesystem, and both are dot-prefixed so a leftover stays out of the
|
|
58
|
+
* Resolves the two scratch directories through which a build publishes: `staging`, which the emit is written to,
|
|
59
|
+
* and `previous`, which the outgoing output is renamed aside to. Both are siblings of the emit directory, so a
|
|
60
|
+
* rename between them never crosses a filesystem, and both are dot-prefixed so that a leftover stays out of the
|
|
61
61
|
* globs that select sources.
|
|
62
62
|
*
|
|
63
|
-
* The names are fixed rather than unique because the build removes both before use.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
63
|
+
* The names are fixed rather than unique because the build removes both before use. A unique name would need a
|
|
64
|
+
* sweep to accomplish that removal, and a sweep cannot tell a directory orphaned by a killed run from one that a
|
|
65
|
+
* concurrent build is still writing. Because a directory left behind by a failed build is cleared the same way,
|
|
66
|
+
* the failure path does not need any cleanup of its own.
|
|
67
67
|
*/
|
|
68
68
|
export declare function resolveScratchDirs(emitDir: string): ScratchDirs;
|
|
69
69
|
/**
|
|
70
|
-
* Resolves the fingerprint of the nmr compiling `packageDir`,
|
|
71
|
-
*
|
|
72
|
-
* nmr's own build digest where one is on disk, which is what moves on a dev-loop edit the version does not
|
|
70
|
+
* Resolves the fingerprint of the nmr compiling `packageDir`, the `BuildToolchain` member that identifies nmr. It
|
|
71
|
+
* is nmr's own build digest when one is on disk, which changes on a dev-loop edit that the version does not
|
|
73
72
|
* follow, and nmr's package version otherwise -- the case in a consuming repo, whose installed copy was built
|
|
74
73
|
* elsewhere.
|
|
75
74
|
*
|
|
76
|
-
* A build of nmr itself takes the version too. Its digest is the entry that build is about to overwrite, so
|
|
77
|
-
*
|
|
78
|
-
* every invocation and
|
|
75
|
+
* A build of nmr itself takes the version too. Its digest is the entry that the build is about to overwrite, so
|
|
76
|
+
* including it would make the key a function of its own previous value, which never stops changing: nmr would
|
|
77
|
+
* rebuild on every invocation and change the fingerprint that every other package reads.
|
|
79
78
|
*
|
|
80
|
-
*
|
|
81
|
-
* the pre-rebuild binary
|
|
79
|
+
* Because the fingerprint names the nmr that produced the output, not the sources in `src`, a package compiled by
|
|
80
|
+
* the pre-rebuild binary keeps the old fingerprint and rebuilds on the next run.
|
|
82
81
|
*/
|
|
83
82
|
export declare function resolveToolchainFingerprint(packageDir: string, selfDir?: string): Promise<string>;
|
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { type BuildOptions } from './build-output.js';
|
|
3
3
|
/**
|
|
4
|
-
* The toolchain a build's emit comes
|
|
5
|
-
* hash, because the same sources can
|
|
4
|
+
* The toolchain from which a build's emit comes: the resolved compiler and the nmr running it. `computeBuildHash`
|
|
5
|
+
* folds both into the build hash, because the same sources can compile to different output under a different
|
|
6
|
+
* compiler or nmr.
|
|
6
7
|
*/
|
|
7
8
|
export interface BuildToolchain {
|
|
8
9
|
compilerVersion: string;
|
|
9
10
|
fingerprint: string;
|
|
10
11
|
}
|
|
11
|
-
/** What a build needs beyond its sources: the options shaping the emit, and the style it reports
|
|
12
|
+
/** What a build needs beyond its sources: the options shaping the emit, and the style in which it reports. */
|
|
12
13
|
export interface BuildPackageOptions extends BuildOptions {
|
|
13
14
|
style: OutputStyle;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* Compiles a package's `src` tree to `dist/esm` with the TypeScript compiler API, emitting `.js` and
|
|
17
18
|
* `.d.ts` from two programs and rewriting relative `.ts` specifiers and tsconfig `paths` aliases to
|
|
18
|
-
* runnable relative `.js` specifiers in both outputs. Skips the build only when
|
|
19
|
-
* and the previous output is still on disk.
|
|
19
|
+
* runnable relative `.js` specifiers in both outputs. Skips the build only when every input is
|
|
20
|
+
* unchanged and the previous output is still on disk.
|
|
20
21
|
*
|
|
21
|
-
* The build owns its output directory:
|
|
22
|
+
* The build owns its output directory: Every emit replaces it wholesale, so `dist` is a function of the
|
|
22
23
|
* current inputs rather than an accumulation of every build that ever ran. Assets belong outside it, or in a
|
|
23
24
|
* `build:post` hook, which runs after the output is published.
|
|
24
25
|
*/
|
|
25
26
|
export declare function buildPackage(packageDir: string, options: BuildPackageOptions): Promise<void>;
|
|
26
27
|
/**
|
|
27
28
|
* Produces a digest of the given files (paths and contents), the emit config, and the toolchain.
|
|
28
|
-
* The file list is sorted so the digest is order-invariant, and each path is folded in so renames are
|
|
29
|
-
*
|
|
30
|
-
* nmr versions whose emit logic drives them.
|
|
29
|
+
* The file list is sorted so that the digest is order-invariant, and each path is folded in so that renames are
|
|
30
|
+
* detected.
|
|
31
31
|
*/
|
|
32
32
|
export declare function computeBuildHash(packageDir: string, files: string[], emitConfig: object, toolchain: BuildToolchain): Promise<string>;
|
|
33
33
|
/**
|
|
34
34
|
* Resolves a package's full tsconfig `extends` chain, returning every config file in it (the leaf `tsconfig.json` and
|
|
35
|
-
* each base it transitively extends, up to the repo root) as paths relative to `packageDir`.
|
|
36
|
-
* Emit is driven by the fully-resolved compiler options, so the base configs
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `computeBuildHash` reads them and folds a stable, location-independent path string into the digest.
|
|
35
|
+
* each base that it transitively extends, up to the repo root) as paths relative to `packageDir`.
|
|
36
|
+
* Emit is driven by the fully-resolved compiler options, so the base configs must be in the cache's hashed input
|
|
37
|
+
* set; otherwise a change to a base config would not bust the cache and stale output could be released. The
|
|
38
|
+
* relative paths give `computeBuildHash` a location-independent path string to fold into the digest.
|
|
40
39
|
*/
|
|
41
40
|
export declare function resolveTsconfigChain(packageDir: string, configFileName?: string): string[];
|
|
@@ -142,7 +142,7 @@ function resolveEmitDir(packageDir, outdir) {
|
|
|
142
142
|
relativePathFromPackage.startsWith('..') ||
|
|
143
143
|
path.isAbsolute(relativePathFromPackage)) {
|
|
144
144
|
throw new Error(`nmr-compile: refusing to build into '${outdir}', which does not resolve inside the package. ` +
|
|
145
|
-
'The build replaces its output directory on each emit, so the directory must
|
|
145
|
+
'The build replaces its output directory on each emit, so the directory must be below the package root.');
|
|
146
146
|
}
|
|
147
147
|
return resolvedDir;
|
|
148
148
|
}
|
|
@@ -216,7 +216,7 @@ function rewriteSpecifiers(outputFile, text, compilerOptions, sourceRoot) {
|
|
|
216
216
|
if (edits.length === 0) {
|
|
217
217
|
return text;
|
|
218
218
|
}
|
|
219
|
-
const orderedEdits =
|
|
219
|
+
const orderedEdits = edits.toSorted((a, b) => b.startOffset - a.startOffset);
|
|
220
220
|
let updatedText = text;
|
|
221
221
|
for (const edit of orderedEdits) {
|
|
222
222
|
updatedText = updatedText.slice(0, edit.startOffset) + edit.text + updatedText.slice(edit.endOffset);
|
|
@@ -309,7 +309,7 @@ function collectAliasPrefixes(compilerOptions) {
|
|
|
309
309
|
}
|
|
310
310
|
function describeEmit(fileCount, outdir) {
|
|
311
311
|
if (fileCount === 0) {
|
|
312
|
-
return '
|
|
312
|
+
return 'Did not emit any output.';
|
|
313
313
|
}
|
|
314
314
|
return `Compiled ${fileCount} files to ${outdir.replace(/\/+$/u, '')}.`;
|
|
315
315
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
2
2
|
/**
|
|
3
|
-
* Removes a package's build output and the build-cache entry that describes it, so
|
|
4
|
-
* make the next build skip. Removal is idempotent:
|
|
3
|
+
* Removes a package's build output and the build-cache entry that describes it, so the package does not keep
|
|
4
|
+
* any state that would make the next build skip. Removal is idempotent: An already-clean package is a silent
|
|
5
|
+
* no-op.
|
|
5
6
|
*/
|
|
6
7
|
export declare function cleanPackage(packageDir: string, style: OutputStyle): Promise<void>;
|
|
7
8
|
/**
|
|
@@ -10,9 +11,8 @@ export declare function cleanPackage(packageDir: string, style: OutputStyle): Pr
|
|
|
10
11
|
* From within a package this is the built-in clean itself: `nmr` has already resolved `clean` to this bin
|
|
11
12
|
* before invoking it, so resolving again would apply the package's own override twice.
|
|
12
13
|
*
|
|
13
|
-
* This is the default `clean` script,
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* consumer's `node_modules/.bin`, nmr being a direct dependency.
|
|
14
|
+
* This is the default `clean` script, and it is published as a bin because, under pnpm's isolated layout,
|
|
15
|
+
* `pnpm exec` resolves only the bins of the consuming project's direct dependencies: pnpm links nmr's own bins
|
|
16
|
+
* into the consumer's `node_modules/.bin`, but not the bins of nmr's dependencies.
|
|
17
17
|
*/
|
|
18
18
|
export declare function runClean(cwd: string, style: OutputStyle): Promise<void>;
|
|
@@ -12,7 +12,7 @@ export interface EnsurePrepublishHooksResult {
|
|
|
12
12
|
}
|
|
13
13
|
export declare const DEFAULT_HOOK = "npm run build";
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Checks (and optionally fixes) whether all publishable workspace packages
|
|
16
16
|
* have a `prepublishOnly` script.
|
|
17
17
|
*/
|
|
18
18
|
export declare function ensurePrepublishHooks(monorepoRoot: string, options: {
|
|
@@ -21,10 +21,10 @@ export declare function ensurePrepublishHooks(monorepoRoot: string, options: {
|
|
|
21
21
|
command?: string;
|
|
22
22
|
}): EnsurePrepublishHooksResult;
|
|
23
23
|
/**
|
|
24
|
-
* Reports one line per publishable package and closes with
|
|
25
|
-
* neither:
|
|
24
|
+
* Reports one line per publishable package and closes with a summary of the run. Private packages appear in
|
|
25
|
+
* neither: They publish nothing, so they do not need a `prepublishOnly`.
|
|
26
26
|
*
|
|
27
|
-
* Every line goes to stdout, a failure's included, and the exit code
|
|
28
|
-
* across two streams
|
|
27
|
+
* Every line goes to stdout, a failure's included, and the exit code signals a failure. When a report is split
|
|
28
|
+
* across two streams, a reader of either stream does not see the whole list.
|
|
29
29
|
*/
|
|
30
30
|
export declare function reportPrepublishHooks(result: EnsurePrepublishHooksResult, hookCommand: string, style: OutputStyle): void;
|
|
@@ -13,7 +13,7 @@ export type ResolveTargetsResult = {
|
|
|
13
13
|
error: string;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
|
-
* Formats or checks the files git reports for `cwd`, and returns the exit code to report.
|
|
16
|
+
* Formats or checks the files that git reports for `cwd`, and returns the exit code to report.
|
|
17
17
|
*
|
|
18
18
|
* Trailing arguments are git pathspecs, not Prettier flags; an unrecognized option is rejected rather
|
|
19
19
|
* than passed along, since git would read it as a pathspec matching nothing and the run would report
|
|
@@ -26,11 +26,11 @@ export declare function runFmt(argv: string[], cwd?: string): number;
|
|
|
26
26
|
* pattern in `packages/<pkg>/.gitignore` is invisible to a root-level run; git knows the whole hierarchy,
|
|
27
27
|
* along with `.git/info/exclude` and `core.excludesFile`, which Prettier cannot read under any flag.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Because file selection is anchored at `cwd` and ignore discovery at the repository root, scoping follows
|
|
30
|
+
* `cwd` while the ignore rules applied to a given file are the same from every directory.
|
|
31
31
|
*
|
|
32
|
-
* `pathspecs` narrow the selection and are passed to git verbatim
|
|
33
|
-
*
|
|
32
|
+
* `pathspecs` narrow the selection and are passed to git verbatim: They have git pathspec semantics rather
|
|
33
|
+
* than shell glob semantics.
|
|
34
34
|
*/
|
|
35
35
|
export declare function resolveFormatTargets(cwd: string, pathspecs?: string[]): ResolveTargetsResult;
|
|
36
36
|
/** @internal */
|
|
@@ -40,15 +40,15 @@ export interface RunPrettierOptions {
|
|
|
40
40
|
files: string[];
|
|
41
41
|
ignorePaths: string[];
|
|
42
42
|
cwd: string;
|
|
43
|
-
/** Overridable so the multi-batch path is reachable without a fixture that hits the real ceiling. */
|
|
43
|
+
/** Overridable so that the multi-batch path is reachable without a fixture that hits the real ceiling. */
|
|
44
44
|
budgetBytes?: number;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Runs Prettier over the selection, returning the first non-zero exit code. Every batch runs even after
|
|
48
48
|
* one fails, so a check reports every offending file rather than only those in the first batch.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
* the file's execute bit or on PATH
|
|
50
|
+
* Because the CLI runs under this process's own Node rather than through its shebang, it does not depend
|
|
51
|
+
* on the file's execute bit or on PATH containing a `node`.
|
|
52
52
|
*
|
|
53
53
|
* @internal
|
|
54
54
|
*/
|
package/dist/esm/commands/fmt.js
CHANGED
|
@@ -32,7 +32,7 @@ export function runFmt(argv, cwd = process.cwd()) {
|
|
|
32
32
|
const { files, ignorePaths } = resolvedTargets.targets;
|
|
33
33
|
if (files.length === 0) {
|
|
34
34
|
if (parsedArgs.pathspecs.length > 0) {
|
|
35
|
-
reportError(`nmr-fmt:
|
|
35
|
+
reportError(`nmr-fmt: the pathspecs did not match any formattable file: ${parsedArgs.pathspecs.join(', ')}`);
|
|
36
36
|
return 1;
|
|
37
37
|
}
|
|
38
38
|
return 0;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
2
2
|
/**
|
|
3
|
-
* Reports the catalogued dependencies a package-scoped upgrade pass
|
|
3
|
+
* Reports the catalogued dependencies that a package-scoped upgrade pass does not read.
|
|
4
4
|
*
|
|
5
|
-
* The upgrade tool reads `pnpm-workspace.yaml` only when
|
|
6
|
-
* inside a package drops every dependency a catalog
|
|
7
|
-
* catalogued
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Runs ahead of the report produced by the workspace `upgrade` script.
|
|
5
|
+
* The upgrade tool reads `pnpm-workspace.yaml` only when the file is in the working directory, so a pass run
|
|
6
|
+
* inside a package drops every dependency declared by a catalog -- and the tool reports a package whose
|
|
7
|
+
* dependencies are all catalogued as up to date. This report names them, and the root from which a covering
|
|
8
|
+
* pass runs, to keep the reader from taking that result to mean that every dependency is up to date.
|
|
11
9
|
*/
|
|
12
10
|
export declare function reportCatalog(cwd: string, style: OutputStyle): void;
|
|
@@ -15,7 +15,7 @@ export function reportCatalog(cwd, style) {
|
|
|
15
15
|
if (dependencies.length === 0) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
console.warn(formatGlyphLine(NMR_GLYPHS, style, 'catalog', 'WARN: A package-scoped upgrade does not read the catalogs these come
|
|
18
|
+
console.warn(formatGlyphLine(NMR_GLYPHS, style, 'catalog', 'WARN: A package-scoped upgrade does not read the catalogs from which these come:'));
|
|
19
19
|
for (const { name, specifier } of dependencies) {
|
|
20
20
|
console.warn(`- ${name} → ${specifier}`);
|
|
21
21
|
}
|
|
@@ -2,7 +2,5 @@ import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
|
2
2
|
/**
|
|
3
3
|
* Reports the pnpm dependency overrides declared in the monorepo root's `pnpm-workspace.yaml`, and rejects a
|
|
4
4
|
* `pnpm.overrides` block left behind in the root `package.json`.
|
|
5
|
-
*
|
|
6
|
-
* Runs ahead of the report produced by the root `upgrade` script.
|
|
7
5
|
*/
|
|
8
6
|
export declare function reportOverrides(monorepoRoot: string, style: OutputStyle): void;
|
|
@@ -29,7 +29,7 @@ function rejectLegacyOverrides(monorepoRoot) {
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
throw new UserError([
|
|
32
|
-
'pnpm 11
|
|
32
|
+
'pnpm 11 does not read `pnpm.overrides` from package.json, so these pin nothing while an upgrade run with `--write` goes on rewriting them:',
|
|
33
33
|
...legacyOverrides.map(([name, version]) => `- ${name} → ${String(version)}`),
|
|
34
34
|
'Move them to the `overrides` block in pnpm-workspace.yaml, quoting each version, or run `pnpx codemod run pnpm-v10-to-v11`.',
|
|
35
35
|
].join('\n'));
|