@williamthorsen/nmr 0.39.0 → 0.40.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.
Files changed (88) hide show
  1. package/.meta/changelog.json +128 -4
  2. package/.readyup/kits/default.js +47 -32
  3. package/.readyup/manifest.json +12 -7
  4. package/CHANGELOG.md +38 -0
  5. package/README.md +41 -73
  6. package/agents/README.md +8 -8
  7. package/agents/guidance/rulebooks/nmr.md +8 -8
  8. package/bin/ensure-prepublish-hooks.js +1 -1
  9. package/bin/nmr-clean.js +1 -1
  10. package/bin/nmr-compile.js +1 -1
  11. package/bin/nmr-fmt.js +1 -1
  12. package/bin/nmr-report-catalog.js +1 -1
  13. package/bin/nmr-report-overrides.js +1 -1
  14. package/bin/nmr-taze.js +1 -1
  15. package/bin/nmr.js +1 -1
  16. package/dist/esm/UserError.d.ts +1 -2
  17. package/dist/esm/check-cache.d.ts +59 -61
  18. package/dist/esm/commands/build-output.d.ts +35 -36
  19. package/dist/esm/commands/build.d.ts +13 -14
  20. package/dist/esm/commands/build.js +3 -3
  21. package/dist/esm/commands/clean.d.ts +6 -6
  22. package/dist/esm/commands/ensure-prepublish-hooks.d.ts +5 -5
  23. package/dist/esm/commands/fmt.d.ts +8 -8
  24. package/dist/esm/commands/fmt.js +1 -1
  25. package/dist/esm/commands/report-catalog.d.ts +5 -7
  26. package/dist/esm/commands/report-catalog.js +1 -1
  27. package/dist/esm/commands/report-overrides.d.ts +0 -2
  28. package/dist/esm/commands/report-overrides.js +1 -1
  29. package/dist/esm/commands/taze.d.ts +9 -8
  30. package/dist/esm/config.d.ts +5 -5
  31. package/dist/esm/config.js +3 -3
  32. package/dist/esm/context.d.ts +1 -2
  33. package/dist/esm/default-scripts.d.ts +5 -6
  34. package/dist/esm/defineConfig.d.ts +1 -1
  35. package/dist/esm/git-ignored-paths.d.ts +9 -0
  36. package/dist/esm/git-ignored-paths.js +10 -0
  37. package/dist/esm/glyphs.d.ts +3 -3
  38. package/dist/esm/help.d.ts +2 -2
  39. package/dist/esm/helpers/clampToBytes.d.ts +4 -5
  40. package/dist/esm/helpers/config-path.d.ts +0 -2
  41. package/dist/esm/helpers/createBoundedBuffer.d.ts +2 -2
  42. package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
  43. package/dist/esm/helpers/duration.d.ts +8 -9
  44. package/dist/esm/helpers/filter-selection.d.ts +19 -19
  45. package/dist/esm/helpers/findClosestName.d.ts +1 -1
  46. package/dist/esm/helpers/hook-name.d.ts +2 -5
  47. package/dist/esm/helpers/package-json.d.ts +9 -8
  48. package/dist/esm/helpers/readStringValues.d.ts +2 -3
  49. package/dist/esm/helpers/reportClosing.d.ts +8 -9
  50. package/dist/esm/helpers/transcript.d.ts +13 -13
  51. package/dist/esm/helpers/type-guards.d.ts +1 -0
  52. package/dist/esm/output-style.d.ts +12 -13
  53. package/dist/esm/prettier.d.ts +2 -2
  54. package/dist/esm/recording.d.ts +20 -15
  55. package/dist/esm/recording.js +7 -7
  56. package/dist/esm/replay-assembly.d.ts +10 -10
  57. package/dist/esm/report-format.d.ts +14 -19
  58. package/dist/esm/reportCliFailure.d.ts +2 -3
  59. package/dist/esm/resolve-scripts.d.ts +4 -5
  60. package/dist/esm/resolveBinStyles.d.ts +4 -5
  61. package/dist/esm/resolver.d.ts +14 -18
  62. package/dist/esm/runCli.d.ts +6 -6
  63. package/dist/esm/runCli.js +21 -21
  64. package/dist/esm/runner.d.ts +19 -19
  65. package/dist/esm/steps.d.ts +43 -42
  66. package/dist/esm/taze.d.ts +1 -1
  67. package/dist/esm/tests.d.ts +18 -13
  68. package/dist/esm/tests.js +2 -2
  69. package/dist/esm/tiers.d.ts +27 -23
  70. package/dist/esm/tiers.js +6 -2
  71. package/dist/esm/types.d.ts +5 -5
  72. package/dist/esm/verbosity.d.ts +22 -20
  73. package/dist/esm/verdict.d.ts +33 -33
  74. package/dist/esm/verdict.js +1 -1
  75. package/dist/esm/vitest-source-resolution.d.ts +11 -11
  76. package/dist/esm/vitest-source-resolution.js +2 -2
  77. package/dist/esm/vitest.d.ts +25 -24
  78. package/dist/esm/vitest.js +16 -8
  79. package/dist/esm/workspace.d.ts +4 -4
  80. package/docs/check-cache.md +57 -57
  81. package/docs/prettier.md +10 -10
  82. package/docs/reporting.md +46 -46
  83. package/docs/scripts.md +29 -29
  84. package/docs/upgrades.md +18 -18
  85. package/docs/utilities.md +13 -13
  86. package/docs/vitest.md +64 -62
  87. package/docs/workspace.md +19 -19
  88. package/package.json +5 -4
@@ -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 what it cost. */
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 under; a hit is this matching the key computed now. */
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 carried when the pass was recorded. Build output is
19
- * git-ignored, so the tree hash cannot describe it; comparing these is what separates output built from this
20
- * tree from output another tree left behind.
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 carries its constituents' lines, and a nested one's are
43
- * spliced into its parent's, where the attribution cannot be re-derived from the entry that holds them.
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 is what admits a constituent's line into the assembly its parent records: a run writes it when
46
- * it records an excerpt, and restamps it when it recalls one and replays it.
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 from, keyed by package name. */
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 on, folded into every key and recorded alongside every pass. */
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 on purpose. `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` carry a cache
81
- * of their own. `fix`, `fmt`, `lint`, and `upgrade` mutate the tree they are asked about. `test:all` reaches
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
- /** Carries one run's identity down the spawned chain, so an entry can name the run that vouched for it. */
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
- /** Carries the top-level tree snapshot down the spawned chain, so one invocation hashes the tree once. */
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 this run certified
94
- * can join the assembly a composite above it records.
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: the pass key matched, so the excerpt describes this tree,
97
- * and the caller reaches this only where the retention key matched too, so it describes this presentation
98
- * environment. Everything else the entry records stands -- the instant and the duration belong to the run that
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, so that failure goes to the debug note.
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 in, nmr's own version, the interpreter, what is installed, and
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 the
138
- * command's output streams ran on, and the environment variables a tool presents itself through.
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
- * Taking the pass key as an ingredient rather than recomputing its parts is what keeps the two from drifting
141
- * apart. The channel kind is what keeps a run at a terminal from replaying a piped recording; folding in raw
142
- * TTY-ness instead would be wrong under quiet mode, where the child sees pipes at a terminal and the
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 landed after the command name, where it is an argument to the
160
- * command rather than a flag to nmr. Passing it on unchanged is the honest thing to do with an argument, so
161
- * the warning is all that separates this from a silently un-bypassed run.
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 is what separates a command with no recording
166
- * from one that could never have had one.
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 excluded here rather than at one caller: it is not a command anyone asks for, so nothing records
169
- * it, and a repo naming one in `extraCommands` must not make the gate and a reader of its entries disagree.
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
- * A package whose `build` or `compile` is overridden emits somewhere this does not know about, so it is left
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 it rides on: an excerpt of a shape this cannot read is
186
- * dropped, leaving a pass that skips cleanly and reports its verdict alone. Voiding the pass instead would
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` where it retained none.
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 is what says which tree the bytes describe, and a caller
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 stand beside an
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. Extending rather than replacing means
213
- * declaring one command cannot silently drop the defaults; excluding is how a repo retires a name whose chain
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 to: the one an ancestor nmr process passed down,
219
- * and otherwise a fresh one, this invocation being where the run starts.
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 where the tree snapshot is bounded by a HEAD comparison: a process that outlives its run hands a
222
- * stale identity to the invocations it later makes, and what keeps that harmless is the tree hash every
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 on: the one a parent nmr process already took, when there
228
- * is one, and otherwise a fresh hash of the working tree. Reports a reason instead when no snapshot can be
229
- * had, which disables the gate.
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 holding the monorepo inside a subdirectory has
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 through, both siblings of the emit directory. */
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 shipped code, excluded from entry-point selection so a
17
- * package does not publish its own helpers. Deliberately not the vitest factory's `COVERAGE_EXCLUDE`: helpers
18
- * live in `test-utils/` precisely so they stay inside the coverage include set, so the two lists overlap
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 is what keeps a production module that uses a helper from emitting a
23
- * dangling specifier. Widening this list can therefore only drop files nothing in production reaches.
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 rule the
29
- * build applies, on the same options the build was given, so a caller probing for output and the build deciding
30
- * whether to rebuild cannot disagree: a package whose entry points emit nothing expects no output, and reports
31
- * present. Passing options the build was not given is what would make the two disagree.
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. Entry points that
36
- * emit nothing expect no output, so their absent outdir is not deleted output: a `src` tree holding only
37
- * declaration files, or none at all, would otherwise be reported as missing output and recompiled forever.
38
- * The emit is what makes an outdir, so what counts is whether any entry point emits, not how many there are.
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 from, or `undefined` when the package
43
- * has never been built. Two packages holding the same sources report the same digest, and a package whose output
44
- * came from a different tree reports a different one, which is what tells a stale `dist` from a current one.
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 hold the path where every previously written entry already sits: the `.hash` extension, the
53
- * package's own directory name as the slug, and the absence of `discriminators`, which would otherwise fold
54
- * into the digest. Changing any of them strands every entry on disk.
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 through: `staging`, which the emit is written to, and
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. That removal is what a
64
- * unique name would need a sweep to accomplish, and a sweep cannot tell a directory orphaned by a killed run
65
- * from one a concurrent build is still writing. A directory left behind by a failed build is cleared the same
66
- * way, so the failure path needs no cleanup of its own.
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`, which joins the build digest beside the compiler
71
- * version: the same sources emit differently across nmr versions as they do across TypeScript versions. It is
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
- * folding it would make the key a function of its own previous value, which never settles: nmr would rebuild on
78
- * every invocation and move the fingerprint every other package reads.
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
- * What the fingerprint names is the nmr that produced the output, not the sources sitting in `src`, so a package
81
- * the pre-rebuild binary compiled keeps the old fingerprint and rebuilds on the run after.
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 from: the resolved compiler and the nmr running it. Both join the build
5
- * hash, because the same sources can emit differently across either.
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 in. */
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 no input has changed
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: every emit replaces it wholesale, so `dist` is a function of the
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 detected.
29
- * The toolchain joins it because the same sources emit differently across TypeScript versions, and across the
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 (where `target`, `module`, `paths`,
37
- * `lib`, and `strict` are actually defined) must be in the cache's hashed input set; otherwise a change to a base
38
- * config would not bust the cache and stale output could ship. Paths are returned relative to `packageDir`, so
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 sit below the package root.');
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 = [...edits].sort((a, b) => b.startOffset - a.startOffset);
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 'Emitted no output.';
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 no state survives to
4
- * make the next build skip. Removal is idempotent: an already-clean package is a silent no-op.
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, shipped as a bin rather than delegating to `rimraf`. Under pnpm's
14
- * isolated layout, `pnpm exec` resolves only bins of the consuming project's own direct dependencies, so
15
- * a `rimraf` in nmr's dependency tree would be unreachable — while nmr's own bins are linked into the
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
- * Check (and optionally fix) whether all publishable workspace packages
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 what the run came to. Private packages appear in
25
- * neither: they publish nothing, so a `prepublishOnly` is not theirs to carry.
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 is what carries a failure. A report split
28
- * across two streams leaves no reader holding the whole list.
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
- * File selection is anchored at `cwd` and ignore discovery at the repository root. That split is the point:
30
- * scoping still follows where the caller stands, while the rules applied to a given file no longer do.
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, so they carry git pathspec semantics
33
- * rather than shell glob semantics.
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
- * The CLI runs under this process's own Node rather than through its shebang, so it does not depend on
51
- * the file's execute bit or on PATH holding a `node`.
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
  */
@@ -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: no formattable files matched: ${parsedArgs.pathspecs.join(', ')}`);
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 cannot see.
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 it sits at the working directory, so a pass run
6
- * inside a package drops every dependency a catalog declares -- and a package whose dependencies are all
7
- * catalogued reports itself up to date. Naming them, and the root a covering pass runs from, is what keeps
8
- * that from reading as a clean bill of health.
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 from:'));
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 reads no `pnpm.overrides` from package.json, so these pin nothing while an upgrade run with `--write` goes on rewriting them:',
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'));