@williamthorsen/nmr 0.38.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 (112) hide show
  1. package/.meta/changelog.json +212 -4
  2. package/.readyup/kits/default.js +124 -104
  3. package/.readyup/manifest.json +12 -7
  4. package/CHANGELOG.md +108 -0
  5. package/README.md +41 -47
  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 +61 -63
  18. package/dist/esm/check-cache.js +24 -24
  19. package/dist/esm/cli-ensure-prepublish-hooks.js +4 -4
  20. package/dist/esm/commands/build-output.d.ts +37 -38
  21. package/dist/esm/commands/build-output.js +9 -9
  22. package/dist/esm/commands/build.d.ts +13 -14
  23. package/dist/esm/commands/build.js +67 -65
  24. package/dist/esm/commands/clean.d.ts +6 -6
  25. package/dist/esm/commands/clean.js +11 -11
  26. package/dist/esm/commands/ensure-prepublish-hooks.d.ts +7 -7
  27. package/dist/esm/commands/ensure-prepublish-hooks.js +41 -41
  28. package/dist/esm/commands/fmt.d.ts +8 -8
  29. package/dist/esm/commands/fmt.js +33 -33
  30. package/dist/esm/commands/report-catalog.d.ts +5 -7
  31. package/dist/esm/commands/report-catalog.js +10 -8
  32. package/dist/esm/commands/report-overrides.d.ts +0 -2
  33. package/dist/esm/commands/report-overrides.js +8 -8
  34. package/dist/esm/commands/taze.d.ts +9 -8
  35. package/dist/esm/config.d.ts +5 -5
  36. package/dist/esm/config.js +27 -27
  37. package/dist/esm/context.d.ts +1 -2
  38. package/dist/esm/context.js +8 -8
  39. package/dist/esm/default-scripts.d.ts +5 -6
  40. package/dist/esm/defineConfig.d.ts +1 -1
  41. package/dist/esm/git-ignored-paths.d.ts +9 -0
  42. package/dist/esm/git-ignored-paths.js +10 -0
  43. package/dist/esm/glyphs.d.ts +3 -3
  44. package/dist/esm/help.d.ts +4 -4
  45. package/dist/esm/help.js +21 -21
  46. package/dist/esm/helpers/clampToBytes.d.ts +4 -5
  47. package/dist/esm/helpers/clampToBytes.js +3 -3
  48. package/dist/esm/helpers/config-path.d.ts +0 -2
  49. package/dist/esm/helpers/createBoundedBuffer.d.ts +3 -3
  50. package/dist/esm/helpers/createBoundedBuffer.js +2 -2
  51. package/dist/esm/helpers/deriveExcerpt.d.ts +4 -5
  52. package/dist/esm/helpers/duration.d.ts +10 -11
  53. package/dist/esm/helpers/duration.js +7 -7
  54. package/dist/esm/helpers/filter-selection.d.ts +19 -19
  55. package/dist/esm/helpers/filter-selection.js +12 -12
  56. package/dist/esm/helpers/findClosestName.d.ts +1 -1
  57. package/dist/esm/helpers/findClosestName.js +15 -15
  58. package/dist/esm/helpers/hook-name.d.ts +2 -5
  59. package/dist/esm/helpers/package-json.d.ts +11 -10
  60. package/dist/esm/helpers/package-json.js +26 -25
  61. package/dist/esm/helpers/readStringValues.d.ts +2 -3
  62. package/dist/esm/helpers/reportClosing.d.ts +8 -9
  63. package/dist/esm/helpers/transcript.d.ts +15 -15
  64. package/dist/esm/helpers/transcript.js +14 -14
  65. package/dist/esm/helpers/type-guards.d.ts +1 -0
  66. package/dist/esm/output-style.d.ts +12 -13
  67. package/dist/esm/prettier.d.ts +2 -2
  68. package/dist/esm/prettier.js +5 -5
  69. package/dist/esm/recording.d.ts +25 -20
  70. package/dist/esm/recording.js +28 -21
  71. package/dist/esm/replay-assembly.d.ts +10 -10
  72. package/dist/esm/report-format.d.ts +15 -20
  73. package/dist/esm/report-format.js +7 -7
  74. package/dist/esm/reportCliFailure.d.ts +2 -3
  75. package/dist/esm/resolve-scripts.d.ts +4 -5
  76. package/dist/esm/resolveBinStyles.d.ts +4 -5
  77. package/dist/esm/resolveBinStyles.js +3 -3
  78. package/dist/esm/resolver.d.ts +16 -20
  79. package/dist/esm/resolver.js +15 -15
  80. package/dist/esm/runCli.d.ts +6 -6
  81. package/dist/esm/runCli.js +261 -195
  82. package/dist/esm/runner.d.ts +20 -20
  83. package/dist/esm/runner.js +18 -18
  84. package/dist/esm/steps.d.ts +51 -38
  85. package/dist/esm/steps.js +32 -27
  86. package/dist/esm/taze.d.ts +1 -1
  87. package/dist/esm/tests.d.ts +23 -18
  88. package/dist/esm/tests.js +21 -9
  89. package/dist/esm/tiers.d.ts +28 -24
  90. package/dist/esm/tiers.js +13 -9
  91. package/dist/esm/types.d.ts +5 -5
  92. package/dist/esm/verbosity.d.ts +23 -21
  93. package/dist/esm/verbosity.js +7 -7
  94. package/dist/esm/verdict.d.ts +39 -32
  95. package/dist/esm/verdict.js +63 -50
  96. package/dist/esm/vitest-source-resolution.d.ts +11 -11
  97. package/dist/esm/vitest-source-resolution.js +49 -47
  98. package/dist/esm/vitest.d.ts +27 -26
  99. package/dist/esm/vitest.js +90 -33
  100. package/dist/esm/workspace.d.ts +6 -22
  101. package/dist/esm/workspace.js +9 -67
  102. package/docs/check-cache.md +57 -57
  103. package/docs/prettier.md +10 -10
  104. package/docs/reporting.md +48 -44
  105. package/docs/scripts.md +29 -29
  106. package/docs/upgrades.md +18 -18
  107. package/docs/utilities.md +13 -13
  108. package/docs/vitest.md +68 -66
  109. package/docs/workspace.md +27 -4
  110. package/package.json +6 -6
  111. package/dist/esm/helpers/workspace-patterns.d.ts +0 -8
  112. package/dist/esm/helpers/workspace-patterns.js +0 -29
package/dist/esm/help.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isHookName } from "./helpers/hook-name.js";
2
2
  import { OUTPUT_STYLE_ENV_VAR, OUTPUT_STYLE_FLAG } from "./output-style.js";
3
3
  import { buildRootRegistry, buildWorkspaceRegistry, describeScript, isSelfReferential, readPackageJsonScripts, } from "./resolver.js";
4
- export function generateHelp(config, packageDir, useRoot) {
4
+ export function generateHelp(config, packageDir, shouldUseRoot) {
5
5
  const lines = [
6
6
  'Usage: nmr [flags] <command> [args...]',
7
7
  '',
@@ -26,16 +26,16 @@ export function generateHelp(config, packageDir, useRoot) {
26
26
  const overrides = packageDir === undefined ? {} : collectOverrides(packageDir);
27
27
  let hadOverride = false;
28
28
  const workspaceRegistry = filterHooks(buildWorkspaceRegistry(config));
29
- const workspaceMarked = !useRoot ? applyOverrides(workspaceRegistry, overrides) : new Set();
30
- if (workspaceMarked.size > 0)
29
+ const workspaceMarkedNames = !shouldUseRoot ? applyOverrides(workspaceRegistry, overrides) : new Set();
30
+ if (workspaceMarkedNames.size > 0)
31
31
  hadOverride = true;
32
- formatRegistry(workspaceRegistry, workspaceMarked, lines);
32
+ formatRegistry(workspaceRegistry, workspaceMarkedNames, lines);
33
33
  lines.push('', 'Root commands:');
34
34
  const rootRegistry = filterHooks(buildRootRegistry(config));
35
- const rootMarked = useRoot ? applyOverrides(rootRegistry, overrides) : new Set();
36
- if (rootMarked.size > 0)
35
+ const rootMarkedNames = shouldUseRoot ? applyOverrides(rootRegistry, overrides) : new Set();
36
+ if (rootMarkedNames.size > 0)
37
37
  hadOverride = true;
38
- formatRegistry(rootRegistry, rootMarked, lines);
38
+ formatRegistry(rootRegistry, rootMarkedNames, lines);
39
39
  if (hadOverride) {
40
40
  lines.push('', '* Overridden by package.json');
41
41
  }
@@ -45,43 +45,43 @@ function collectOverrides(packageDir) {
45
45
  const scripts = readPackageJsonScripts(packageDir);
46
46
  if (!scripts)
47
47
  return {};
48
- const filtered = {};
48
+ const candidateOverrides = {};
49
49
  for (const [name, value] of Object.entries(scripts)) {
50
50
  if (isHookName(name))
51
51
  continue;
52
52
  if (isSelfReferential(value, name, packageDir))
53
53
  continue;
54
- filtered[name] = value;
54
+ candidateOverrides[name] = value;
55
55
  }
56
- return filtered;
56
+ return candidateOverrides;
57
57
  }
58
58
  function applyOverrides(registry, overrides) {
59
- const marked = new Set();
59
+ const markedNames = new Set();
60
60
  for (const [name, value] of Object.entries(overrides)) {
61
61
  if (!Object.hasOwn(registry, name)) {
62
62
  continue;
63
63
  }
64
64
  registry[name] = value;
65
- marked.add(name);
65
+ markedNames.add(name);
66
66
  }
67
- return marked;
67
+ return markedNames;
68
68
  }
69
69
  function filterHooks(registry) {
70
- const filtered = {};
70
+ const hooklessRegistry = {};
71
71
  for (const [key, value] of Object.entries(registry)) {
72
72
  if (!isHookName(key))
73
- filtered[key] = value;
73
+ hooklessRegistry[key] = value;
74
74
  }
75
- return filtered;
75
+ return hooklessRegistry;
76
76
  }
77
- function formatRegistry(registry, marked, lines) {
77
+ function formatRegistry(registry, markedNames, lines) {
78
78
  const keys = Object.keys(registry);
79
79
  if (keys.length === 0)
80
80
  return;
81
- const maxKeyLen = Math.max(...keys.map((k) => k.length));
82
- const pad = Math.max(maxKeyLen + 1 + 2, 20);
81
+ const maxKeyLength = Math.max(...keys.map((k) => k.length));
82
+ const padWidth = Math.max(maxKeyLength + 1 + 2, 20);
83
83
  for (const [key, value] of Object.entries(registry)) {
84
- const marker = marked.has(key) ? '*' : ' ';
85
- lines.push(` ${(key + marker).padEnd(pad)} ${describeScript(value)}`);
84
+ const marker = markedNames.has(key) ? '*' : ' ';
85
+ lines.push(` ${(key + marker).padEnd(padWidth)} ${describeScript(value)}`);
86
86
  }
87
87
  }
@@ -1,14 +1,13 @@
1
- /** Marks a value that was cut, so a reader can tell a truncated one from a complete one. */
1
+ /** Marks a value that was cut, so that a reader can tell a truncated one from a complete one. */
2
2
  export declare const TRUNCATION_MARK = "\u2026";
3
3
  /**
4
4
  * Cuts a string that would overrun a byte budget, marking the cut. What comes back is at most `budgetBytes`
5
5
  * long, the mark included.
6
6
  *
7
7
  * Cuts between code points rather than between bytes, so a multi-byte character is never left in halves.
8
- * Graphemes are left unconsidered, which can separate an emoji from a modifier following it -- a cost paid
9
- * only at a budget no caller here comes near.
8
+ * Graphemes are left unconsidered, which can separate an emoji from a modifier following it -- a risk only
9
+ * at a budget that the callers here never come near.
10
10
  *
11
- * A budget too small to hold the mark yields nothing at all, rather than a return that overruns what the
12
- * caller asked for.
11
+ * The return never exceeds the budget: A budget too small to hold the mark yields an empty string.
13
12
  */
14
13
  export declare function clampToBytes(value: string, budgetBytes: number): string;
@@ -7,14 +7,14 @@ export function clampToBytes(value, budgetBytes) {
7
7
  if (budget < 0) {
8
8
  return '';
9
9
  }
10
- let kept = '';
10
+ let keptText = '';
11
11
  let bytes = 0;
12
12
  for (const character of value) {
13
13
  const size = Buffer.byteLength(character);
14
14
  if (bytes + size > budget)
15
15
  break;
16
- kept += character;
16
+ keptText += character;
17
17
  bytes += size;
18
18
  }
19
- return `${kept}${TRUNCATION_MARK}`;
19
+ return `${keptText}${TRUNCATION_MARK}`;
20
20
  }
@@ -2,7 +2,5 @@
2
2
  export declare const CONFIG_RELATIVE_PATH: string;
3
3
  /**
4
4
  * Resolves the config-file path for a directory, whether that is the monorepo root or a package.
5
- * Callers that only need to know whether a config exists -- the build, which folds it into its cache digest -- go
6
- * through this rather than spelling the path again, so that the two cannot drift into hashing a file nothing reads.
7
5
  */
8
6
  export declare function resolveConfigPath(baseDir: string): string;
@@ -6,11 +6,11 @@ export interface BoundedBuffer {
6
6
  /** Retains a chunk, dropping bytes from the middle once both ends are full. */
7
7
  append: (chunk: Buffer) => void;
8
8
  /** Renders the retained bytes, with a marker naming the dropped count standing in for what was dropped. */
9
- toBuffer: () => Buffer;
9
+ readBuffer: () => Buffer;
10
10
  }
11
11
  /**
12
- * Creates an accumulator that retains the first and last bytes it is fed and drops the middle.
13
- * Both ends are kept because a failing command's first error and its trailing summary each carry signal
12
+ * Creates an accumulator that retains the first and last bytes that it is fed and drops the middle.
13
+ * Both ends are kept because a failing command's first error and its trailing summary each contain information
14
14
  * that the other end does not.
15
15
  */
16
16
  export declare function createBoundedBuffer(options?: BoundedBufferOptions): BoundedBuffer;
@@ -40,7 +40,7 @@ export function createBoundedBuffer(options = {}) {
40
40
  tailOffset = (tailOffset + chunk.length) % tailLimitBytes;
41
41
  tailBytes = Math.min(tailLimitBytes, tailBytes + chunk.length);
42
42
  }
43
- function toBuffer() {
43
+ function readBuffer() {
44
44
  const headBuffer = Buffer.concat(headChunks);
45
45
  if (tailRing === undefined) {
46
46
  return headBuffer;
@@ -53,5 +53,5 @@ export function createBoundedBuffer(options = {}) {
53
53
  }
54
54
  return Buffer.concat([headBuffer, Buffer.from(formatElisionMarker(elidedBytes)), tailBuffer]);
55
55
  }
56
- return { append, toBuffer };
56
+ return { append, readBuffer };
57
57
  }
@@ -1,11 +1,10 @@
1
1
  /**
2
- * Reduces a transcript to the one line a verdict's detail slot carries, or `undefined` when it held nothing.
2
+ * Reduces a transcript to the one line shown in a verdict's detail slot, or `undefined` when it contained nothing.
3
3
  *
4
4
  * The excerpt is the last blank-line-delimited block: a tool that prints progress separates its closing
5
- * statement with a blank line, so the blank line is the tool naming where its summary starts. A fixed count of
6
- * trailing lines fits vitest's four-line summary and v8's six-line coverage summary only by coincidence.
5
+ * statement with a blank line, and that blank line is the tool's own mark of where its summary starts.
7
6
  *
8
- * The line carries a byte bound of its own, so what a caller persists is bounded. The 512-byte ceiling a
9
- * verdict line is held to is not applied here, which leaves that line's grammar with the module that owns it.
7
+ * The line is bounded by `MAX_EXCERPT_BYTES` alone; the ceiling on a verdict line is applied where that line is
8
+ * composed.
10
9
  */
11
10
  export declare function deriveExcerpt(transcript: string): string | undefined;
@@ -1,17 +1,16 @@
1
1
  /**
2
- * Renders a duration at the two coarsest units that say something, for a line a reader skims.
2
+ * Renders a duration at the two coarsest units that say something, for a line that a reader skims.
3
3
  *
4
- * Truncates at every unit rather than rounding, so a value never reads as more time than elapsed: 90 seconds is
5
- * `1m 30s`, where rounding would claim `2m`. Below a minute the tenth is kept, since that is the scale at which
6
- * one check's runtime differs from another's; a whole number sheds the `.0` rather than reading `12.0s`.
4
+ * Truncates at every unit rather than rounding, so that a value never reads as more time than elapsed: 119.9
5
+ * seconds is `1m 59s`, whereas rounding would claim `2m`. Below a minute the tenth is kept, since that is the
6
+ * scale at which one check's runtime differs from another's; a whole number is rendered without the `.0`, as
7
+ * `12s` rather than `12.0s`.
7
8
  */
8
- export declare function formatDuration(milliseconds: number): string;
9
+ export declare function formatDuration(durationMs: number): string;
9
10
  /**
10
- * Renders the clause a skip spends on the time it saved, or `undefined` when there is no saving worth naming.
11
- * A skip that can say what it saved earns the clause; one that cannot says nothing, and the caller drops it
12
- * rather than printing a saving of `~0s`.
11
+ * Renders the clause stating the time that a skip saved, or `undefined` for a saving under a second.
13
12
  *
14
- * Guards its own input rather than trusting the caller's, because the clause is shared: a non-finite duration
15
- * compares false against the threshold, and would reach a reader as `saved ~NaNs`.
13
+ * A non-finite duration also returns `undefined`: It compares false against the threshold and would otherwise
14
+ * render as `saved ~NaNs`.
16
15
  */
17
- export declare function formatSaving(milliseconds: number): string | undefined;
16
+ export declare function formatSaving(durationMs: number): string | undefined;
@@ -1,5 +1,5 @@
1
- export function formatDuration(milliseconds) {
2
- const total = Math.max(0, milliseconds);
1
+ export function formatDuration(durationMs) {
2
+ const total = Math.max(0, durationMs);
3
3
  if (total < MILLISECONDS_PER_MINUTE) {
4
4
  return `${truncateToTenth(total)}s`;
5
5
  }
@@ -10,11 +10,11 @@ export function formatDuration(milliseconds) {
10
10
  }
11
11
  return joinUnits(`${Math.floor(totalMinutes / MINUTES_PER_HOUR)}h`, totalMinutes % MINUTES_PER_HOUR, 'm');
12
12
  }
13
- export function formatSaving(milliseconds) {
14
- if (!Number.isFinite(milliseconds) || milliseconds < MILLISECONDS_PER_SECOND) {
13
+ export function formatSaving(durationMs) {
14
+ if (!Number.isFinite(durationMs) || durationMs < MILLISECONDS_PER_SECOND) {
15
15
  return undefined;
16
16
  }
17
- return `saved ~${formatDuration(milliseconds)}`;
17
+ return `saved ~${formatDuration(durationMs)}`;
18
18
  }
19
19
  const MILLISECONDS_PER_MINUTE = 60_000;
20
20
  const MILLISECONDS_PER_SECOND = 1_000;
@@ -23,6 +23,6 @@ const SECONDS_PER_MINUTE = 60;
23
23
  function joinUnits(major, minor, minorUnit) {
24
24
  return minor === 0 ? major : `${major} ${minor}${minorUnit}`;
25
25
  }
26
- function truncateToTenth(milliseconds) {
27
- return Math.floor(milliseconds / 100) / 10;
26
+ function truncateToTenth(durationMs) {
27
+ return Math.floor(durationMs / 100) / 10;
28
28
  }
@@ -1,40 +1,40 @@
1
1
  /**
2
- * Asks pnpm what a `-F` pattern selects, so an invocation that would select nothing is caught before it
3
- * delegates, and one selecting several packages is told from one selecting a single package.
2
+ * Asks pnpm what a `-F` pattern selects, so that an invocation that would select nothing is caught before it
3
+ * delegates, and one selecting several packages is distinguished from one selecting a single package.
4
4
  *
5
- * pnpm's own selector answers rather than a matcher of nmr's: `-F` means the whole selector language, names
6
- * and globs alongside `./path`, `{dir}`, `pkg...`, `!negation`, and `[since]`, and a matcher here would leave
7
- * every form but the simplest silently unchecked.
5
+ * pnpm answers because `-F` accepts pnpm's whole selector language: names and globs alongside `./path`, `{dir}`,
6
+ * `pkg...`, `!negation`, and `[since]`.
8
7
  *
9
- * Runs from the monorepo root, where the delegate runs, so a path pattern resolves against the same directory
10
- * in the probe as in the run it stands in for.
8
+ * Runs from the monorepo root, where the delegate runs, so that a path pattern resolves against the same
9
+ * directory in the probe as in the run that it stands in for.
11
10
  *
12
- * The listing counts the root project wherever the filter leaves it standing, while the `exec` a delegation
13
- * composes runs there only where the pattern selects the root positively: `-F '!./packages/*'` lists the root
14
- * and executes nowhere. A listing of the root alone is therefore the one reading the listing cannot settle,
15
- * and it is put to `exec` itself. Every other listing settles on its own, and counts the packages beside the
16
- * root: two of them run in two scopes, while one beside the root may run in that package alone.
11
+ * The listing counts the root project wherever the filter does not exclude it, while the `exec` that a
12
+ * delegation composes runs there only when the pattern selects the root positively: `-F '!./packages/*'` lists
13
+ * the root and executes nowhere. A listing of the root alone is therefore the one reading that the listing
14
+ * cannot settle, and that reading is checked against `exec` itself. Every other listing settles on its own, and
15
+ * counts the packages beside the root: two of them run in two scopes, while one beside the root may run in that
16
+ * package alone.
17
17
  */
18
18
  export declare function readFilterSelection(pattern: string, monorepoRoot: string): FilterSelection;
19
19
  /**
20
20
  * Reduces a listing to what it proves about the selection.
21
21
  *
22
- * Every outcome the probe cannot read resolves to `unresolved`, which claims nothing about the selection, and the
23
- * invocation delegates as it would have. A pattern pnpm rejects rather than resolves, such as a bad git ref in
24
- * a changed-since selector, is pnpm's to report, and reporting it here as a match failure would name a
25
- * different fault than the one the user has.
22
+ * Every outcome that the probe cannot read resolves to `unresolved`, which claims nothing about the selection,
23
+ * and the invocation delegates as it would have. A pattern that pnpm rejects rather than resolves, such as a bad
24
+ * git ref in a changed-since selector, is pnpm's to report, and reporting it here as a match failure would name a
25
+ * different fault than the one that the user has.
26
26
  */
27
27
  export declare function interpretSelectionProbe(probe: SelectionProbe, monorepoRoot: string): ProbeReading;
28
28
  /**
29
- * Reduces a marked delegate run to what it proves: a scope that wrote the marker is a scope the run reached. The
30
- * run is made only for a listing of the root alone, so that scope is the only one.
29
+ * Reduces a marked delegate run to what it proves: a scope that wrote the marker is a scope that the run
30
+ * reached. Because the run is made only for a listing of the root alone, that scope is the only one.
31
31
  */
32
32
  export declare function interpretDelegateProbe(probe: SelectionProbe): FilterSelection;
33
33
  /** What a `-F` pattern selected, as far as pnpm could be asked. `multiple` is two or more packages beside the root. */
34
34
  export type FilterSelection = 'empty' | 'multiple' | 'single' | 'unresolved';
35
35
  /** What a listing proves, `root-only` being the reading that only the delegate can settle. */
36
36
  export type ProbeReading = FilterSelection | 'root-only';
37
- /** The part of a probe's completion a selection is read from. */
37
+ /** The part of a probe's completion from which a selection is read. */
38
38
  export interface SelectionProbe {
39
39
  error: Error | undefined;
40
40
  status: number | null;
@@ -5,31 +5,31 @@ import process from 'node:process';
5
5
  import { isObject } from "./type-guards.js";
6
6
  const SELECTION_MARKER = 'nmr-selected-scope';
7
7
  export function readFilterSelection(pattern, monorepoRoot) {
8
- const listing = spawnSync('pnpm', ['ls', '--filter', pattern, '--depth', '-1', '--json'], {
8
+ const listingRun = spawnSync('pnpm', ['ls', '--filter', pattern, '--depth', '-1', '--json'], {
9
9
  cwd: monorepoRoot,
10
10
  encoding: 'utf8',
11
11
  });
12
- const reading = interpretSelectionProbe({ error: listing.error, status: listing.status, stdout: listing.stdout }, monorepoRoot);
13
- return reading === 'root-only' ? readDelegateSelection(pattern, monorepoRoot) : reading;
12
+ const probeReading = interpretSelectionProbe({ error: listingRun.error, status: listingRun.status, stdout: listingRun.stdout }, monorepoRoot);
13
+ return probeReading === 'root-only' ? readDelegateSelection(pattern, monorepoRoot) : probeReading;
14
14
  }
15
15
  export function interpretSelectionProbe(probe, monorepoRoot) {
16
16
  if (probe.error !== undefined || probe.status !== 0) {
17
17
  return 'unresolved';
18
18
  }
19
- let parsed;
19
+ let parsedListing;
20
20
  try {
21
- parsed = JSON.parse(probe.stdout);
21
+ parsedListing = JSON.parse(probe.stdout);
22
22
  }
23
23
  catch {
24
24
  return 'unresolved';
25
25
  }
26
- if (!Array.isArray(parsed)) {
26
+ if (!Array.isArray(parsedListing)) {
27
27
  return 'unresolved';
28
28
  }
29
- if (parsed.length === 0) {
29
+ if (parsedListing.length === 0) {
30
30
  return 'empty';
31
31
  }
32
- const packageCount = parsed.filter((entry) => !isRootEntry(entry, monorepoRoot)).length;
32
+ const packageCount = parsedListing.filter((entry) => !isRootEntry(entry, monorepoRoot)).length;
33
33
  if (packageCount === 0) {
34
34
  return 'root-only';
35
35
  }
@@ -45,9 +45,9 @@ function isRootEntry(entry, monorepoRoot) {
45
45
  if (!isObject(entry) || typeof entry['path'] !== 'string') {
46
46
  return false;
47
47
  }
48
- return readRealPath(entry['path']) === readRealPath(monorepoRoot);
48
+ return resolveRealPath(entry['path']) === resolveRealPath(monorepoRoot);
49
49
  }
50
- function readRealPath(target) {
50
+ function resolveRealPath(target) {
51
51
  try {
52
52
  return realpathSync(target);
53
53
  }
@@ -56,6 +56,6 @@ function readRealPath(target) {
56
56
  }
57
57
  }
58
58
  function readDelegateSelection(pattern, monorepoRoot) {
59
- const probe = spawnSync('pnpm', ['--filter', pattern, 'exec', process.execPath, '--eval', `process.stdout.write('${SELECTION_MARKER}')`], { cwd: monorepoRoot, encoding: 'utf8' });
60
- return interpretDelegateProbe({ error: probe.error, status: probe.status, stdout: probe.stdout });
59
+ const probeRun = spawnSync('pnpm', ['--filter', pattern, 'exec', process.execPath, '--eval', `process.stdout.write('${SELECTION_MARKER}')`], { cwd: monorepoRoot, encoding: 'utf8' });
60
+ return interpretDelegateProbe({ error: probeRun.error, status: probeRun.status, stdout: probeRun.stdout });
61
61
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Finds the candidate closest to a name, or `undefined` where none is near enough to be worth naming.
2
+ * Finds the candidate closest to a name, or `undefined` when none is near enough to be worth naming.
3
3
  *
4
4
  * The ceiling scales with the name's length, so a short name admits one slip and a long one a few. A fixed
5
5
  * ceiling would either miss a typo in `root:test:coverage` or offer `fix` for `ci`.
@@ -1,33 +1,33 @@
1
1
  export function findClosestName(name, candidates) {
2
2
  const ceiling = Math.max(1, Math.floor(name.length / 4));
3
- let closest;
4
- let shortest = Infinity;
3
+ let closestName;
4
+ let shortestDistance = Infinity;
5
5
  for (const candidate of candidates) {
6
6
  const distance = measureDistance(name, candidate);
7
- if (distance < shortest) {
8
- shortest = distance;
9
- closest = candidate;
7
+ if (distance < shortestDistance) {
8
+ shortestDistance = distance;
9
+ closestName = candidate;
10
10
  }
11
11
  }
12
- return shortest <= ceiling ? closest : undefined;
12
+ return shortestDistance <= ceiling ? closestName : undefined;
13
13
  }
14
14
  function measureDistance(first, second) {
15
15
  if (first.length === 0 || second.length === 0) {
16
16
  return Math.max(first.length, second.length);
17
17
  }
18
- let previous = Array.from({ length: second.length + 1 }, (_unused, column) => column);
19
- let current = Array.from({ length: second.length + 1 }, () => 0);
18
+ let previousRow = Array.from({ length: second.length + 1 }, (_unused, column) => column);
19
+ let currentRow = Array.from({ length: second.length + 1 }, () => 0);
20
20
  for (let row = 1; row <= first.length; row += 1) {
21
- current[0] = row;
21
+ currentRow[0] = row;
22
22
  for (let column = 1; column <= second.length; column += 1) {
23
- const substitution = readCell(previous, column - 1) + (first[row - 1] === second[column - 1] ? 0 : 1);
24
- current[column] = Math.min(readCell(current, column - 1) + 1, readCell(previous, column) + 1, substitution);
23
+ const substitution = readCell(previousRow, column - 1) + (first[row - 1] === second[column - 1] ? 0 : 1);
24
+ currentRow[column] = Math.min(readCell(currentRow, column - 1) + 1, readCell(previousRow, column) + 1, substitution);
25
25
  }
26
- const completed = current;
27
- current = previous;
28
- previous = completed;
26
+ const completedRow = currentRow;
27
+ currentRow = previousRow;
28
+ previousRow = completedRow;
29
29
  }
30
- return readCell(previous, second.length);
30
+ return readCell(previousRow, second.length);
31
31
  }
32
32
  function readCell(row, index) {
33
33
  const value = row[index];
@@ -1,9 +1,6 @@
1
1
  /**
2
- * Returns true when `key` is a hook script name — that is, ends with the
3
- * `:pre` or `:post` suffix preceded by at least one character.
2
+ * Reports whether `key` is a hook script name: a `:pre` or `:post` suffix preceded by at least one character.
4
3
  *
5
- * Bare `:pre` and `:post` (no parent name) return false. The predicate is
6
- * purely about suffix shape; it does not consult the registry or care
7
- * whether the parent command exists.
4
+ * Reads the suffix alone, without consulting the registry for the parent command.
8
5
  */
9
6
  export declare function isHookName(key: string): boolean;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The `package.json` fields that carry a dependency's version specifier.
2
+ * The `package.json` fields that contain a dependency's version specifier.
3
3
  *
4
4
  * pnpm accepts the `catalog:` protocol in all four, so a reader looking for catalogued dependencies has to
5
- * sweep the whole set rather than the two that carry most of them.
5
+ * sweep the whole set rather than the two that contain most of them.
6
6
  */
7
7
  export declare const DEPENDENCY_FIELDS: readonly ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
8
8
  export type DependencyField = (typeof DEPENDENCY_FIELDS)[number];
@@ -19,26 +19,27 @@ export type PackageJson = {
19
19
  [K in DependencyField]?: Record<string, string>;
20
20
  };
21
21
  /**
22
- * Reads and parses the package.json at the given directory, keeping only the fields nmr reads.
22
+ * Reads and parses the package.json at the given directory, keeping only the fields that nmr reads.
23
23
  */
24
24
  export declare function readPackageJson(dir: string): PackageJson;
25
25
  /**
26
- * Returns the `pnpm.overrides` block a package.json declares, every key it holds and each value as written.
26
+ * Returns the `pnpm.overrides` block declared by a package.json, every key that it contains and each value as
27
+ * written.
27
28
  */
28
- export declare function getPnpmOverrides(pkg: PackageJson): Record<string, unknown> | undefined;
29
+ export declare function getPnpmOverrides(packageJson: PackageJson): Record<string, unknown> | undefined;
29
30
  /**
30
31
  * Parses a `package.json`'s text, rejecting what does not parse.
31
32
  *
32
- * A file the user wrote by hand is theirs to fix, so the failure names it rather than reporting the parser's
33
+ * A file that the user wrote by hand is theirs to fix, so the failure names it rather than reporting the parser's
33
34
  * own stack.
34
35
  */
35
- export declare function parsePackageJson(raw: string, file: string): unknown;
36
+ export declare function parsePackageJson(rawText: string, file: string): unknown;
36
37
  /**
37
38
  * Narrows a `scripts` record to strings, rejecting any value that is not one.
38
39
  *
39
- * npm and pnpm read a script as a string too, so a value of any other type is malformed however it got there.
40
- * Dropping one silently would let a caller run something else in its place and report nothing.
40
+ * A value of any other type is malformed however it got there, because npm and pnpm read a script as a string
41
+ * too. Dropping one silently would let a caller run something else in its place and report nothing.
41
42
  */
42
43
  export declare function readScriptRecord(dir: string, scripts: Record<string, unknown>): Record<string, string>;
43
- /** Resolves the `package.json` path for a directory: the file tier-3 scripts are read from. */
44
+ /** Resolves the `package.json` path for a directory: the file from which tier-3 scripts are read. */
44
45
  export declare function resolvePackageJsonPath(dir: string): string;
@@ -14,45 +14,46 @@ export const DEPENDENCY_FIELDS = [
14
14
  ];
15
15
  export function readPackageJson(dir) {
16
16
  const file = resolvePackageJsonPath(dir);
17
- const parsed = parsePackageJson(readFileSync(file, 'utf8'), file);
18
- if (!isObject(parsed)) {
17
+ const parsedManifest = parsePackageJson(readFileSync(file, 'utf8'), file);
18
+ if (!isObject(parsedManifest)) {
19
19
  throw new UserError(`Invalid package.json in ${dir}: expected an object`);
20
20
  }
21
- const pkg = {};
22
- if (typeof parsed['name'] === 'string')
23
- pkg.name = parsed['name'];
24
- if (parsed['private'] === true)
25
- pkg.private = true;
26
- if (typeof parsed['version'] === 'string')
27
- pkg.version = parsed['version'];
28
- if (typeof parsed['packageManager'] === 'string')
29
- pkg.packageManager = parsed['packageManager'];
30
- if (isObject(parsed['scripts'])) {
31
- pkg.scripts = readScriptRecord(dir, parsed['scripts']);
21
+ const packageJson = {};
22
+ if (typeof parsedManifest['name'] === 'string')
23
+ packageJson.name = parsedManifest['name'];
24
+ if (parsedManifest['private'] === true)
25
+ packageJson.private = true;
26
+ if (typeof parsedManifest['version'] === 'string')
27
+ packageJson.version = parsedManifest['version'];
28
+ if (typeof parsedManifest['packageManager'] === 'string') {
29
+ packageJson.packageManager = parsedManifest['packageManager'];
32
30
  }
33
- if (isObject(parsed['pnpm'])) {
34
- const pnpm = parsed['pnpm'];
31
+ if (isObject(parsedManifest['scripts'])) {
32
+ packageJson.scripts = readScriptRecord(dir, parsedManifest['scripts']);
33
+ }
34
+ if (isObject(parsedManifest['pnpm'])) {
35
+ const pnpm = parsedManifest['pnpm'];
35
36
  if (isObject(pnpm['overrides'])) {
36
- pkg.pnpm = { overrides: pnpm['overrides'] };
37
+ packageJson.pnpm = { overrides: pnpm['overrides'] };
37
38
  }
38
39
  }
39
40
  for (const field of DEPENDENCY_FIELDS) {
40
- const declared = parsed[field];
41
- if (isObject(declared)) {
42
- pkg[field] = readStringValues(declared);
41
+ const declaredDependencies = parsedManifest[field];
42
+ if (isObject(declaredDependencies)) {
43
+ packageJson[field] = readStringValues(declaredDependencies);
43
44
  }
44
45
  }
45
- return pkg;
46
+ return packageJson;
46
47
  }
47
- export function getPnpmOverrides(pkg) {
48
- if (!isObject(pkg.pnpm))
48
+ export function getPnpmOverrides(packageJson) {
49
+ if (!isObject(packageJson.pnpm))
49
50
  return undefined;
50
- const overrides = pkg.pnpm.overrides;
51
+ const overrides = packageJson.pnpm.overrides;
51
52
  return isObject(overrides) ? overrides : undefined;
52
53
  }
53
- export function parsePackageJson(raw, file) {
54
+ export function parsePackageJson(rawText, file) {
54
55
  try {
55
- return JSON.parse(raw);
56
+ return JSON.parse(rawText);
56
57
  }
57
58
  catch (error) {
58
59
  throw new UserError(`Invalid package.json at ${file}: ${describeError(error)}`);
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Keeps the entries whose value is a string, dropping the rest.
3
3
  *
4
- * A manifest reader that rejected the whole record over one bad value would report nothing about the entries
5
- * beside it, which for a reporter is silence where there is something to say. YAML's implicit typing makes
6
- * that easy to reach without malformed intent: an unquoted `18` parses as a number.
4
+ * Drops rather than rejects, so that one malformed value does not hide the entries beside it. YAML's implicit
5
+ * typing makes such a value easy to write: An unquoted `18` parses as a number.
7
6
  */
8
7
  export declare function readStringValues(record: Record<string, unknown>): Record<string, string>;
@@ -1,16 +1,15 @@
1
1
  /**
2
2
  * Reports the line that closes a bin's output, separated from the items above it by a blank line.
3
3
  *
4
- * A bin that prints one line per item leaves a reader who reaches the end holding whichever item happened to
5
- * be last. The convention this writes: the header says what the list is, and the closing statement says what
6
- * the run came to, so the count and the remedy live here rather than in the header. The blank line is what
7
- * makes the statement findable once the lines are flattened.
4
+ * When a bin prints one line per item, the last line that a reader sees is whichever item happened to be last.
5
+ * The convention that this implements: The header says what the list is, and the closing statement says what
6
+ * the run concluded, so the count and the remedy are stated here rather than in the header. The blank line makes
7
+ * the statement findable once the lines are flattened.
8
8
  *
9
- * The blank line rides in the same call as the statement, so the closer is one write and cannot be
10
- * interleaved. `log` is what puts the closer on the stream its items went to: a bin reporting to stderr
11
- * passes `console.warn`.
9
+ * The closer is one write and cannot be interleaved: The blank line goes in the same call as the statement.
10
+ * `log` writes the closer to the stream that its items went to: A bin reporting to stderr passes `console.warn`.
12
11
  *
13
- * A bin whose whole output is one or two statements about one subject has no items to be separated from, and
14
- * closes by printing plainly rather than through here.
12
+ * A bin whose whole output is one or two statements about one subject does not have any items to be separated
13
+ * from, and closes by printing plainly rather than through here.
15
14
  */
16
15
  export declare function reportClosing(message: string, log?: (line: string) => void): void;