@williamthorsen/nmr 0.38.0 → 0.39.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 (64) hide show
  1. package/.meta/changelog.json +84 -0
  2. package/.readyup/kits/default.js +84 -79
  3. package/.readyup/manifest.json +5 -5
  4. package/CHANGELOG.md +70 -0
  5. package/README.md +37 -11
  6. package/dist/esm/check-cache.d.ts +2 -2
  7. package/dist/esm/check-cache.js +24 -24
  8. package/dist/esm/cli-ensure-prepublish-hooks.js +4 -4
  9. package/dist/esm/commands/build-output.d.ts +2 -2
  10. package/dist/esm/commands/build-output.js +9 -9
  11. package/dist/esm/commands/build.js +65 -63
  12. package/dist/esm/commands/clean.js +11 -11
  13. package/dist/esm/commands/ensure-prepublish-hooks.d.ts +2 -2
  14. package/dist/esm/commands/ensure-prepublish-hooks.js +41 -41
  15. package/dist/esm/commands/fmt.js +33 -33
  16. package/dist/esm/commands/report-catalog.js +9 -7
  17. package/dist/esm/commands/report-overrides.js +7 -7
  18. package/dist/esm/config.js +25 -25
  19. package/dist/esm/context.js +8 -8
  20. package/dist/esm/help.d.ts +2 -2
  21. package/dist/esm/help.js +21 -21
  22. package/dist/esm/helpers/clampToBytes.js +3 -3
  23. package/dist/esm/helpers/createBoundedBuffer.d.ts +1 -1
  24. package/dist/esm/helpers/createBoundedBuffer.js +2 -2
  25. package/dist/esm/helpers/duration.d.ts +2 -2
  26. package/dist/esm/helpers/duration.js +7 -7
  27. package/dist/esm/helpers/filter-selection.js +12 -12
  28. package/dist/esm/helpers/findClosestName.js +15 -15
  29. package/dist/esm/helpers/package-json.d.ts +2 -2
  30. package/dist/esm/helpers/package-json.js +26 -25
  31. package/dist/esm/helpers/transcript.d.ts +2 -2
  32. package/dist/esm/helpers/transcript.js +14 -14
  33. package/dist/esm/prettier.js +5 -5
  34. package/dist/esm/recording.d.ts +5 -5
  35. package/dist/esm/recording.js +21 -14
  36. package/dist/esm/report-format.d.ts +1 -1
  37. package/dist/esm/report-format.js +7 -7
  38. package/dist/esm/resolveBinStyles.js +3 -3
  39. package/dist/esm/resolver.d.ts +2 -2
  40. package/dist/esm/resolver.js +15 -15
  41. package/dist/esm/runCli.js +252 -186
  42. package/dist/esm/runner.d.ts +1 -1
  43. package/dist/esm/runner.js +18 -18
  44. package/dist/esm/steps.d.ts +13 -1
  45. package/dist/esm/steps.js +32 -27
  46. package/dist/esm/tests.d.ts +7 -7
  47. package/dist/esm/tests.js +20 -8
  48. package/dist/esm/tiers.d.ts +1 -1
  49. package/dist/esm/tiers.js +8 -8
  50. package/dist/esm/verbosity.d.ts +1 -1
  51. package/dist/esm/verbosity.js +7 -7
  52. package/dist/esm/verdict.d.ts +9 -2
  53. package/dist/esm/verdict.js +63 -50
  54. package/dist/esm/vitest-source-resolution.js +48 -46
  55. package/dist/esm/vitest.d.ts +2 -2
  56. package/dist/esm/vitest.js +74 -25
  57. package/dist/esm/workspace.d.ts +2 -18
  58. package/dist/esm/workspace.js +9 -67
  59. package/docs/reporting.md +6 -2
  60. package/docs/vitest.md +6 -6
  61. package/docs/workspace.md +24 -1
  62. package/package.json +3 -4
  63. package/dist/esm/helpers/workspace-patterns.d.ts +0 -8
  64. package/dist/esm/helpers/workspace-patterns.js +0 -29
@@ -11,33 +11,33 @@ export function ensurePrepublishHooks(monorepoRoot, options) {
11
11
  const packageDirs = getWorkspacePackageDirs(monorepoRoot);
12
12
  const packages = [];
13
13
  for (const packageDir of packageDirs) {
14
- const pkg = readPackageJson(packageDir);
15
- const packageName = pkg.name ?? path.basename(packageDir);
16
- const isPrivate = pkg.private === true;
14
+ const packageJson = readPackageJson(packageDir);
15
+ const packageName = packageJson.name ?? path.basename(packageDir);
16
+ const isPrivate = packageJson.private === true;
17
17
  if (isPrivate) {
18
18
  packages.push({
19
19
  packageName,
20
20
  packageDir,
21
21
  isPrivate: true,
22
- prepublishOnly: pkg.scripts?.['prepublishOnly'],
22
+ prepublishOnly: packageJson.scripts?.['prepublishOnly'],
23
23
  action: 'ok',
24
24
  });
25
25
  continue;
26
26
  }
27
- const existing = pkg.scripts?.['prepublishOnly'];
28
- if (existing) {
27
+ const existingHook = packageJson.scripts?.['prepublishOnly'];
28
+ if (existingHook) {
29
29
  packages.push({
30
30
  packageName,
31
31
  packageDir,
32
32
  isPrivate: false,
33
- prepublishOnly: existing,
33
+ prepublishOnly: existingHook,
34
34
  action: 'ok',
35
35
  });
36
36
  continue;
37
37
  }
38
- if (options.fix) {
39
- const action = options.dryRun ? 'would-fix' : 'fixed';
40
- if (!options.dryRun) {
38
+ if (options.shouldFix) {
39
+ const action = options.isDryRun ? 'would-fix' : 'fixed';
40
+ if (!options.isDryRun) {
41
41
  addPrepublishOnly(packageDir, hookCommand);
42
42
  }
43
43
  packages.push({
@@ -62,57 +62,57 @@ export function ensurePrepublishHooks(monorepoRoot, options) {
62
62
  return { packages, hasFailures };
63
63
  }
64
64
  export function reportPrepublishHooks(result, hookCommand, style) {
65
- const publishable = result.packages.filter((pkg) => !pkg.isPrivate);
66
- if (publishable.length === 0) {
65
+ const publishablePackages = result.packages.filter((packageStatus) => !packageStatus.isPrivate);
66
+ if (publishablePackages.length === 0) {
67
67
  console.info('No publishable packages found.');
68
68
  return;
69
69
  }
70
- for (const pkg of publishable) {
71
- console.info(renderHookStatus(pkg, hookCommand, style));
70
+ for (const packageStatus of publishablePackages) {
71
+ console.info(renderHookStatus(packageStatus, hookCommand, style));
72
72
  }
73
- reportClosing(describeHookRun(publishable));
73
+ reportClosing(describeHookRun(publishablePackages));
74
74
  }
75
75
  function addPrepublishOnly(packageDir, command) {
76
76
  const filePath = path.join(packageDir, 'package.json');
77
- const raw = readFileSync(filePath, 'utf8');
78
- const parsed = JSON.parse(raw);
79
- if (!isObject(parsed)) {
77
+ const rawText = readFileSync(filePath, 'utf8');
78
+ const parsedManifest = JSON.parse(rawText);
79
+ if (!isObject(parsedManifest)) {
80
80
  throw new TypeError(`Invalid package.json in ${packageDir}: expected an object`);
81
81
  }
82
- const scripts = isObject(parsed['scripts']) ? parsed['scripts'] : {};
82
+ const scripts = isObject(parsedManifest['scripts']) ? parsedManifest['scripts'] : {};
83
83
  scripts['prepublishOnly'] = command;
84
- parsed['scripts'] = scripts;
85
- writeFileSync(filePath, JSON.stringify(parsed, null, 2) + '\n', 'utf8');
84
+ parsedManifest['scripts'] = scripts;
85
+ writeFileSync(filePath, JSON.stringify(parsedManifest, null, 2) + '\n', 'utf8');
86
86
  }
87
- function countAction(publishable, action) {
88
- return publishable.filter((pkg) => pkg.action === action).length;
87
+ function countAction(publishablePackages, action) {
88
+ return publishablePackages.filter((packageStatus) => packageStatus.action === action).length;
89
89
  }
90
- function describeHookRun(publishable) {
91
- const packages = publishable.length === 1 ? '1 publishable package' : `${publishable.length} publishable packages`;
92
- const missing = countAction(publishable, 'missing');
93
- if (missing > 0) {
94
- return `${missing} of ${packages} ${missing === 1 ? 'is' : 'are'} missing prepublishOnly. Run with --fix to add it.`;
90
+ function describeHookRun(publishablePackages) {
91
+ const packages = publishablePackages.length === 1 ? '1 publishable package' : `${publishablePackages.length} publishable packages`;
92
+ const missingCount = countAction(publishablePackages, 'missing');
93
+ if (missingCount > 0) {
94
+ return `${missingCount} of ${packages} ${missingCount === 1 ? 'is' : 'are'} missing prepublishOnly. Run with --fix to add it.`;
95
95
  }
96
- const fixed = countAction(publishable, 'fixed');
97
- if (fixed > 0) {
98
- return `Added prepublishOnly to ${fixed} of ${packages}.`;
96
+ const fixedCount = countAction(publishablePackages, 'fixed');
97
+ if (fixedCount > 0) {
98
+ return `Added prepublishOnly to ${fixedCount} of ${packages}.`;
99
99
  }
100
- const wouldFix = countAction(publishable, 'would-fix');
101
- if (wouldFix > 0) {
102
- return `Would add prepublishOnly to ${wouldFix} of ${packages}.`;
100
+ const wouldFixCount = countAction(publishablePackages, 'would-fix');
101
+ if (wouldFixCount > 0) {
102
+ return `Would add prepublishOnly to ${wouldFixCount} of ${packages}.`;
103
103
  }
104
- return `${packages} ${publishable.length === 1 ? 'has' : 'have'} prepublishOnly.`;
104
+ return `${packages} ${publishablePackages.length === 1 ? 'has' : 'have'} prepublishOnly.`;
105
105
  }
106
- function renderHookStatus(pkg, hookCommand, style) {
106
+ function renderHookStatus(packageStatus, hookCommand, style) {
107
107
  const statuses = STATUS_GLYPHS[style];
108
- switch (pkg.action) {
108
+ switch (packageStatus.action) {
109
109
  case 'ok':
110
- return `${statuses.passed.text} ${pkg.packageName}: prepublishOnly = "${pkg.prepublishOnly}"`;
110
+ return `${statuses.passed.text} ${packageStatus.packageName}: prepublishOnly = "${packageStatus.prepublishOnly}"`;
111
111
  case 'missing':
112
- return `${statuses.failed.text} ${pkg.packageName}: missing prepublishOnly`;
112
+ return `${statuses.failed.text} ${packageStatus.packageName}: missing prepublishOnly`;
113
113
  case 'fixed':
114
- return `${statuses.passed.text} ${pkg.packageName}: added prepublishOnly = "${hookCommand}"`;
114
+ return `${statuses.passed.text} ${packageStatus.packageName}: added prepublishOnly = "${hookCommand}"`;
115
115
  case 'would-fix':
116
- return `~ ${pkg.packageName}: would add prepublishOnly = "${hookCommand}"`;
116
+ return `~ ${packageStatus.packageName}: would add prepublishOnly = "${hookCommand}"`;
117
117
  }
118
118
  }
@@ -18,21 +18,21 @@ const MODE_ARGS = {
18
18
  write: ['--list-different', '--write'],
19
19
  };
20
20
  export function runFmt(argv, cwd = process.cwd()) {
21
- const parsed = parseFmtArgs(argv);
22
- if (!parsed.ok) {
23
- reportError(`nmr-fmt: ${parsed.error}`);
21
+ const parsedArgs = parseFmtArgs(argv);
22
+ if (!parsedArgs.ok) {
23
+ reportError(`nmr-fmt: ${parsedArgs.error}`);
24
24
  process.stderr.write(`${USAGE}\n`);
25
25
  return 1;
26
26
  }
27
- const resolved = resolveFormatTargets(cwd, parsed.pathspecs);
28
- if (!resolved.ok) {
29
- reportError(`nmr-fmt: ${resolved.error}`);
27
+ const resolvedTargets = resolveFormatTargets(cwd, parsedArgs.pathspecs);
28
+ if (!resolvedTargets.ok) {
29
+ reportError(`nmr-fmt: ${resolvedTargets.error}`);
30
30
  return 1;
31
31
  }
32
- const { files, ignorePaths } = resolved.targets;
32
+ const { files, ignorePaths } = resolvedTargets.targets;
33
33
  if (files.length === 0) {
34
- if (parsed.pathspecs.length > 0) {
35
- reportError(`nmr-fmt: no formattable files matched: ${parsed.pathspecs.join(', ')}`);
34
+ if (parsedArgs.pathspecs.length > 0) {
35
+ reportError(`nmr-fmt: no formattable files matched: ${parsedArgs.pathspecs.join(', ')}`);
36
36
  return 1;
37
37
  }
38
38
  return 0;
@@ -42,29 +42,29 @@ export function runFmt(argv, cwd = process.cwd()) {
42
42
  reportError(`nmr-fmt: ${cli.error}`);
43
43
  return 1;
44
44
  }
45
- return runPrettier({ cliPath: cli.cliPath, mode: parsed.mode, files, ignorePaths, cwd });
45
+ return runPrettier({ cliPath: cli.cliPath, mode: parsedArgs.mode, files, ignorePaths, cwd });
46
46
  }
47
47
  export function resolveFormatTargets(cwd, pathspecs = []) {
48
48
  const toplevel = runGit(['rev-parse', '--show-toplevel'], cwd);
49
49
  if (!toplevel.ok)
50
50
  return toplevel;
51
51
  const repositoryRoot = toplevel.stdout.trim();
52
- const listed = runGit([...LIST_FILES_ARGS, '--', ...pathspecs], cwd);
53
- if (!listed.ok)
54
- return listed;
52
+ const listedFiles = runGit([...LIST_FILES_ARGS, '--', ...pathspecs], cwd);
53
+ if (!listedFiles.ok)
54
+ return listedFiles;
55
55
  const ignoreFiles = runGit([...LIST_FILES_ARGS, '--', IGNORE_FILE_PATHSPEC], repositoryRoot);
56
56
  if (!ignoreFiles.ok)
57
57
  return ignoreFiles;
58
- const discovered = splitNulSeparated(ignoreFiles.stdout)
58
+ const discoveredIgnorePaths = splitNulSeparated(ignoreFiles.stdout)
59
59
  .filter((file) => path.basename(file) === IGNORE_FILENAME)
60
60
  .map((file) => path.resolve(repositoryRoot, file));
61
61
  return {
62
62
  ok: true,
63
63
  targets: {
64
- files: dedupe(splitNulSeparated(listed.stdout))
64
+ files: dedupe(splitNulSeparated(listedFiles.stdout))
65
65
  .filter((file) => isFormattableFile(cwd, file))
66
66
  .toSorted(),
67
- ignorePaths: dedupe([path.join(repositoryRoot, IGNORE_FILENAME), ...discovered]),
67
+ ignorePaths: dedupe([path.join(repositoryRoot, IGNORE_FILENAME), ...discoveredIgnorePaths]),
68
68
  },
69
69
  };
70
70
  }
@@ -76,11 +76,11 @@ function parseFmtArgs(argv) {
76
76
  const pathspecs = [];
77
77
  for (const arg of argv) {
78
78
  if (arg === '--check' || arg === '--write') {
79
- const requested = arg === '--check' ? 'check' : 'write';
80
- if (mode !== undefined && mode !== requested) {
79
+ const requestedMode = arg === '--check' ? 'check' : 'write';
80
+ if (mode !== undefined && mode !== requestedMode) {
81
81
  return { ok: false, error: 'Pass --check or --write, not both.' };
82
82
  }
83
- mode = requested;
83
+ mode = requestedMode;
84
84
  continue;
85
85
  }
86
86
  if (arg.startsWith('-')) {
@@ -94,7 +94,7 @@ function parseFmtArgs(argv) {
94
94
  return { ok: true, mode, pathspecs };
95
95
  }
96
96
  function resolvePrettierCli() {
97
- const missing = {
97
+ const missingResult = {
98
98
  ok: false,
99
99
  error: `\`${PRETTIER_PACKAGE}\` (${PRETTIER_RANGE}) could not be resolved. Install it in this repository.`,
100
100
  };
@@ -103,14 +103,14 @@ function resolvePrettierCli() {
103
103
  manifestPath = createRequire(import.meta.url).resolve(`${PRETTIER_PACKAGE}/package.json`);
104
104
  }
105
105
  catch {
106
- return missing;
106
+ return missingResult;
107
107
  }
108
108
  const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
109
109
  if (!isObject(manifest))
110
- return missing;
110
+ return missingResult;
111
111
  const bin = isObject(manifest['bin']) ? manifest['bin'][PRETTIER_PACKAGE] : manifest['bin'];
112
112
  if (typeof bin !== 'string')
113
- return missing;
113
+ return missingResult;
114
114
  return { ok: true, cliPath: path.resolve(path.dirname(manifestPath), bin) };
115
115
  }
116
116
  export function runPrettier(options) {
@@ -121,7 +121,7 @@ export function runPrettier(options) {
121
121
  ...ignorePaths.flatMap((ignorePath) => ['--ignore-path', ignorePath]),
122
122
  ...MODE_ARGS[mode],
123
123
  ];
124
- let firstFailure = 0;
124
+ let firstFailureStatus = 0;
125
125
  for (const batch of batchWithinBudget(files, budgetBytes)) {
126
126
  const result = spawnSync(process.execPath, [...prettierArgs, ...batch], { cwd, stdio: 'inherit' });
127
127
  if (result.error) {
@@ -129,25 +129,25 @@ export function runPrettier(options) {
129
129
  return 1;
130
130
  }
131
131
  const status = result.status ?? 1;
132
- if (status !== 0 && firstFailure === 0) {
133
- firstFailure = status;
132
+ if (status !== 0 && firstFailureStatus === 0) {
133
+ firstFailureStatus = status;
134
134
  }
135
135
  }
136
- return firstFailure;
136
+ return firstFailureStatus;
137
137
  }
138
- function batchWithinBudget(files, budget) {
138
+ function batchWithinBudget(files, budgetBytes) {
139
139
  const batches = [];
140
140
  let batch = [];
141
- let size = 0;
141
+ let sizeBytes = 0;
142
142
  for (const file of files) {
143
- const cost = Buffer.byteLength(file) + 1;
144
- if (batch.length > 0 && size + cost > budget) {
143
+ const costBytes = Buffer.byteLength(file) + 1;
144
+ if (batch.length > 0 && sizeBytes + costBytes > budgetBytes) {
145
145
  batches.push(batch);
146
146
  batch = [];
147
- size = 0;
147
+ sizeBytes = 0;
148
148
  }
149
149
  batch.push(file);
150
- size += cost;
150
+ sizeBytes += costBytes;
151
151
  }
152
152
  if (batch.length > 0)
153
153
  batches.push(batch);
@@ -27,15 +27,17 @@ function describeCatalog(count, monorepoRoot) {
27
27
  return `${subject} unread. Run \`nmr upgrade\` from ${monorepoRoot} to include ${object}.`;
28
28
  }
29
29
  function findCataloguedDependencies(packageDir) {
30
- const pkg = readPackageJson(packageDir);
31
- const found = new Map();
30
+ const packageJson = readPackageJson(packageDir);
31
+ const foundDependencies = new Map();
32
32
  for (const field of DEPENDENCY_FIELDS) {
33
- const declared = Object.entries(pkg[field] ?? {});
34
- for (const [name, specifier] of declared) {
35
- if (specifier.startsWith(CATALOG_PROTOCOL) && !found.has(name)) {
36
- found.set(name, specifier);
33
+ const declaredEntries = Object.entries(packageJson[field] ?? {});
34
+ for (const [name, specifier] of declaredEntries) {
35
+ if (specifier.startsWith(CATALOG_PROTOCOL) && !foundDependencies.has(name)) {
36
+ foundDependencies.set(name, specifier);
37
37
  }
38
38
  }
39
39
  }
40
- return [...found].map(([name, specifier]) => ({ name, specifier })).toSorted((a, b) => a.name.localeCompare(b.name));
40
+ return [...foundDependencies]
41
+ .map(([name, specifier]) => ({ name, specifier }))
42
+ .toSorted((a, b) => a.name.localeCompare(b.name));
41
43
  }
@@ -5,13 +5,13 @@ import { reportClosing } from "../helpers/reportClosing.js";
5
5
  import { UserError } from "../UserError.js";
6
6
  import { readWorkspaceOverrides } from "../workspace.js";
7
7
  export function reportOverrides(monorepoRoot, style) {
8
- const declared = listEntries(readWorkspaceOverrides(monorepoRoot));
9
- if (declared.length > 0) {
8
+ const declaredOverrides = listEntries(readWorkspaceOverrides(monorepoRoot));
9
+ if (declaredOverrides.length > 0) {
10
10
  console.warn(formatGlyphLine(NMR_GLYPHS, style, 'overrides', 'WARN: pnpm overrides are active:'));
11
- for (const [name, version] of declared) {
11
+ for (const [name, version] of declaredOverrides) {
12
12
  console.warn(`- ${name} → ${version}`);
13
13
  }
14
- reportClosing(formatGlyphLine(NMR_GLYPHS, style, 'overrides', describeOverrides(declared.length)), console.warn);
14
+ reportClosing(formatGlyphLine(NMR_GLYPHS, style, 'overrides', describeOverrides(declaredOverrides.length)), console.warn);
15
15
  }
16
16
  rejectLegacyOverrides(monorepoRoot);
17
17
  }
@@ -24,13 +24,13 @@ function listEntries(overrides) {
24
24
  return Object.entries(overrides ?? {}).toSorted(([a], [b]) => a.localeCompare(b));
25
25
  }
26
26
  function rejectLegacyOverrides(monorepoRoot) {
27
- const legacy = listEntries(getPnpmOverrides(readPackageJson(monorepoRoot)));
28
- if (legacy.length === 0) {
27
+ const legacyOverrides = listEntries(getPnpmOverrides(readPackageJson(monorepoRoot)));
28
+ if (legacyOverrides.length === 0) {
29
29
  return;
30
30
  }
31
31
  throw new UserError([
32
32
  'pnpm 11 reads no `pnpm.overrides` from package.json, so these pin nothing while an upgrade run with `--write` goes on rewriting them:',
33
- ...legacy.map(([name, version]) => `- ${name} → ${String(version)}`),
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'));
36
36
  }
@@ -113,12 +113,12 @@ function validateCheckCacheField(value, configPath) {
113
113
  }
114
114
  assertRecognizedKeys(checkCache, RECOGNIZED_CHECK_CACHE_KEYS, configPath, 'checkCache.');
115
115
  const config = {};
116
- const enabled = checkCache['enabled'];
117
- if (enabled !== undefined) {
118
- if (typeof enabled !== 'boolean') {
116
+ const enabledValue = checkCache['enabled'];
117
+ if (enabledValue !== undefined) {
118
+ if (typeof enabledValue !== 'boolean') {
119
119
  throw new UserError(`Invalid nmr config at ${configPath}: \`checkCache.enabled\` must be a boolean`);
120
120
  }
121
- config.enabled = enabled;
121
+ config.enabled = enabledValue;
122
122
  }
123
123
  for (const field of ['excludeCommands', 'extraCommands']) {
124
124
  const commands = checkCache[field];
@@ -144,22 +144,22 @@ function assertResolvableCheckCacheCommands(config, configPath, baseDir) {
144
144
  throw new UserError(`Invalid nmr config at ${configPath}: \`checkCache.${hook.field}\` names the hook \`${hook.command}\`, ` +
145
145
  'which is never gated on its own: it runs as part of the chain of the command it wraps.');
146
146
  }
147
- const registered = new Set([
147
+ const registeredNames = new Set([
148
148
  ...Object.keys(buildRootRegistry(config)),
149
149
  ...Object.keys(buildWorkspaceRegistry(config)),
150
150
  ]);
151
- const missing = entries.filter(({ command }) => !registered.has(command));
152
- if (missing.length === 0) {
151
+ const missingEntries = entries.filter(({ command }) => !registeredNames.has(command));
152
+ if (missingEntries.length === 0) {
153
153
  return;
154
154
  }
155
- const declared = readDeclaredScriptNames(baseDir);
156
- const unresolvable = missing.find(({ command }) => !declared.has(command));
157
- if (unresolvable === undefined) {
155
+ const declaredNames = readDeclaredScriptNames(baseDir);
156
+ const unresolvableEntry = missingEntries.find(({ command }) => !declaredNames.has(command));
157
+ if (unresolvableEntry === undefined) {
158
158
  return;
159
159
  }
160
- const closest = findClosestName(unresolvable.command, [...registered, ...declared]);
161
- throw new UserError(`Invalid nmr config at ${configPath}: \`checkCache.${unresolvable.field}\` names no command: ` +
162
- `\`${unresolvable.command}\`.${closest === undefined ? '' : ` Did you mean \`${closest}\`?`}`);
160
+ const closestName = findClosestName(unresolvableEntry.command, [...registeredNames, ...declaredNames]);
161
+ throw new UserError(`Invalid nmr config at ${configPath}: \`checkCache.${unresolvableEntry.field}\` names no command: ` +
162
+ `\`${unresolvableEntry.command}\`.${closestName === undefined ? '' : ` Did you mean \`${closestName}\`?`}`);
163
163
  }
164
164
  function readDeclaredScriptNames(monorepoRoot) {
165
165
  let packageDirs;
@@ -203,8 +203,8 @@ function validateOutputField(value, configPath) {
203
203
  const commandVerbosity = output['commandVerbosity'];
204
204
  if (commandVerbosity !== undefined) {
205
205
  if (typeof commandVerbosity !== 'string' || !isCommandVerbosity(commandVerbosity)) {
206
- const rendered = typeof commandVerbosity === 'string' ? commandVerbosity : JSON.stringify(commandVerbosity);
207
- throw new UserError(`Invalid nmr config at ${configPath}: ${formatVerbosityRejection('`output.commandVerbosity`', rendered)}`);
206
+ const renderedValue = typeof commandVerbosity === 'string' ? commandVerbosity : JSON.stringify(commandVerbosity);
207
+ throw new UserError(`Invalid nmr config at ${configPath}: ${formatVerbosityRejection('`output.commandVerbosity`', renderedValue)}`);
208
208
  }
209
209
  config.commandVerbosity = commandVerbosity;
210
210
  }
@@ -258,9 +258,9 @@ export async function loadConfig(baseDir) {
258
258
  if (!existsSync(configPath)) {
259
259
  return {};
260
260
  }
261
- const imported = await import(__rewriteRelativeImportExtension(pathToFileURL(configPath).href));
262
- const loaded = isObject(imported) ? imported['default'] : undefined;
263
- return validateConfig(loaded, configPath, baseDir);
261
+ const importedModule = await import(__rewriteRelativeImportExtension(pathToFileURL(configPath).href));
262
+ const loadedConfig = isObject(importedModule) ? importedModule['default'] : undefined;
263
+ return validateConfig(loadedConfig, configPath, baseDir);
264
264
  }
265
265
  export async function loadRootConfig(monorepoRoot) {
266
266
  const config = await loadConfig(monorepoRoot);
@@ -281,20 +281,20 @@ function assertNoRetiredKeys(value, retiredKeys, configPath, prefix) {
281
281
  }
282
282
  }
283
283
  function assertRecognizedKeys(value, recognizedKeys, configPath, prefix = '') {
284
- const unrecognized = Object.keys(value).filter((key) => !recognizedKeys.includes(key));
285
- if (unrecognized.length === 0) {
284
+ const unrecognizedKeys = Object.keys(value).filter((key) => !recognizedKeys.includes(key));
285
+ if (unrecognizedKeys.length === 0) {
286
286
  return;
287
287
  }
288
- throw new UserError(`Invalid nmr config at ${configPath}: unrecognized ${unrecognized.length === 1 ? 'key' : 'keys'} ` +
289
- `${formatKeyList(unrecognized, prefix)}. Recognized: ${formatKeyList(recognizedKeys, prefix)}.`);
288
+ throw new UserError(`Invalid nmr config at ${configPath}: unrecognized ${unrecognizedKeys.length === 1 ? 'key' : 'keys'} ` +
289
+ `${formatKeyList(unrecognizedKeys, prefix)}. Recognized: ${formatKeyList(recognizedKeys, prefix)}.`);
290
290
  }
291
291
  function assertTierKeys(config, tier, configPath) {
292
- const unsupported = Object.keys(config).filter((key) => !tier.honoredKeys.includes(key));
293
- if (unsupported.length === 0) {
292
+ const unsupportedKeys = Object.keys(config).filter((key) => !tier.honoredKeys.includes(key));
293
+ if (unsupportedKeys.length === 0) {
294
294
  return;
295
295
  }
296
296
  throw new UserError(`Invalid nmr config at ${configPath}: ${tier.label} honors ${tier.honoredKeys.toSorted().join(', ')} alone, ` +
297
- `not ${unsupported.toSorted().join(', ')}. Move those keys to ${tier.elsewhere}.`);
297
+ `not ${unsupportedKeys.toSorted().join(', ')}. Move those keys to ${tier.elsewhere}.`);
298
298
  }
299
299
  function formatKeyList(keys, prefix) {
300
300
  return keys
@@ -2,17 +2,17 @@ import path from 'node:path';
2
2
  import { loadRootConfig } from "./config.js";
3
3
  import { findMonorepoRoot, getWorkspacePackageDirs } from "./workspace.js";
4
4
  export function findContainingPackageDir(dir, workspacePackageDirs) {
5
- const resolved = path.resolve(dir);
6
- let nearest;
7
- for (const pkgDir of workspacePackageDirs) {
8
- const resolvedPkgDir = path.resolve(pkgDir);
9
- if (resolved !== resolvedPkgDir && !resolved.startsWith(resolvedPkgDir + path.sep))
5
+ const resolvedDir = path.resolve(dir);
6
+ let nearestDir;
7
+ for (const packageDir of workspacePackageDirs) {
8
+ const resolvedPackageDir = path.resolve(packageDir);
9
+ if (resolvedDir !== resolvedPackageDir && !resolvedDir.startsWith(resolvedPackageDir + path.sep))
10
10
  continue;
11
- if (nearest === undefined || resolvedPkgDir.length > nearest.length) {
12
- nearest = resolvedPkgDir;
11
+ if (nearestDir === undefined || resolvedPackageDir.length > nearestDir.length) {
12
+ nearestDir = resolvedPackageDir;
13
13
  }
14
14
  }
15
- return nearest;
15
+ return nearestDir;
16
16
  }
17
17
  export async function resolveContext(cwd) {
18
18
  const resolvedCwd = path.resolve(cwd ?? process.cwd());
@@ -8,8 +8,8 @@ import type { NmrConfig } from './types.js';
8
8
  * `package.json:scripts` that match a registry name in the active section
9
9
  * are inlined as overrides: the registry value is replaced with the
10
10
  * override value and the row's command name is suffixed with `*`. The
11
- * active section is the root section when `useRoot` is true (root cwd
11
+ * active section is the root section when `shouldUseRoot` is true (root cwd
12
12
  * or `-w`), otherwise the workspace section. A footnote is appended once
13
13
  * if any override marker was rendered.
14
14
  */
15
- export declare function generateHelp(config: NmrConfig, packageDir: string | undefined, useRoot: boolean): string;
15
+ export declare function generateHelp(config: NmrConfig, packageDir: string | undefined, shouldUseRoot: boolean): string;
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
  }
@@ -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
  }
@@ -6,7 +6,7 @@ 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
12
  * Creates an accumulator that retains the first and last bytes it is fed and drops the middle.
@@ -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
  }
@@ -5,7 +5,7 @@
5
5
  * `1m 30s`, where rounding would claim `2m`. Below a minute the tenth is kept, since that is the scale at which
6
6
  * one check's runtime differs from another's; a whole number sheds the `.0` rather than reading `12.0s`.
7
7
  */
8
- export declare function formatDuration(milliseconds: number): string;
8
+ export declare function formatDuration(durationMs: number): string;
9
9
  /**
10
10
  * Renders the clause a skip spends on the time it saved, or `undefined` when there is no saving worth naming.
11
11
  * A skip that can say what it saved earns the clause; one that cannot says nothing, and the caller drops it
@@ -14,4 +14,4 @@ export declare function formatDuration(milliseconds: number): string;
14
14
  * Guards its own input rather than trusting the caller's, because the clause is shared: a non-finite duration
15
15
  * compares false against the threshold, and would reach a reader as `saved ~NaNs`.
16
16
  */
17
- export declare function formatSaving(milliseconds: number): string | undefined;
17
+ export declare function formatSaving(durationMs: number): string | undefined;