@williamthorsen/release-kit 15.0.3 → 16.0.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 (135) hide show
  1. package/.meta/changelog.json +66 -0
  2. package/.readyup/kits/default.js +13 -13
  3. package/.readyup/kits/npm-auto-publish.js +4 -4
  4. package/.readyup/manifest.json +12 -12
  5. package/CHANGELOG.md +19 -0
  6. package/README.md +7 -1
  7. package/dist/esm/applyChangelogOverrides.js +6 -6
  8. package/dist/esm/assertReleasesOnNpm.js +11 -11
  9. package/dist/esm/assertTaggedBaseline.js +5 -5
  10. package/dist/esm/bin/release-kit.js +16 -16
  11. package/dist/esm/buildChangelogEntries.d.ts +19 -19
  12. package/dist/esm/buildChangelogEntries.js +40 -36
  13. package/dist/esm/buildChangelogItem.js +24 -24
  14. package/dist/esm/buildDependencyGraph.js +23 -23
  15. package/dist/esm/buildReleasedResult.js +21 -19
  16. package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
  17. package/dist/esm/bumpVersion.js +8 -8
  18. package/dist/esm/changelogArchive.d.ts +1 -1
  19. package/dist/esm/changelogArchive.js +2 -2
  20. package/dist/esm/changelogJsonFile.js +5 -5
  21. package/dist/esm/changelogJsonUtils.js +3 -3
  22. package/dist/esm/changelogOverrides.d.ts +5 -5
  23. package/dist/esm/changelogOverrides.js +23 -23
  24. package/dist/esm/classifyChangelogCommit.js +4 -4
  25. package/dist/esm/commitCommand.js +3 -3
  26. package/dist/esm/compareVersions.js +13 -13
  27. package/dist/esm/configSchema.d.ts +2 -2
  28. package/dist/esm/configSchema.js +1 -1
  29. package/dist/esm/createGithubRelease.d.ts +4 -4
  30. package/dist/esm/createGithubRelease.js +10 -10
  31. package/dist/esm/createGithubReleaseCommand.js +9 -9
  32. package/dist/esm/createTags.d.ts +2 -2
  33. package/dist/esm/createTags.js +8 -8
  34. package/dist/esm/decideDirectReleases.d.ts +5 -5
  35. package/dist/esm/decideDirectReleases.js +25 -25
  36. package/dist/esm/decideRelease.d.ts +1 -1
  37. package/dist/esm/decideRelease.js +2 -2
  38. package/dist/esm/deriveDirectoryConfig.d.ts +1 -1
  39. package/dist/esm/deriveDirectoryConfig.js +18 -18
  40. package/dist/esm/deriveWorkspaceConfig.js +4 -4
  41. package/dist/esm/detectPackageManager.js +5 -5
  42. package/dist/esm/detectUndeclaredTagPrefixes.js +6 -6
  43. package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
  44. package/dist/esm/enumerateReleaseWindows.js +7 -7
  45. package/dist/esm/findTransitiveDependents.js +5 -5
  46. package/dist/esm/findUnroutedEntryScopes.js +11 -11
  47. package/dist/esm/format.d.ts +1 -1
  48. package/dist/esm/format.js +1 -1
  49. package/dist/esm/formatPrepareFiles.d.ts +3 -3
  50. package/dist/esm/formatPrepareFiles.js +10 -10
  51. package/dist/esm/formatPrepareWarnings.js +6 -6
  52. package/dist/esm/hasPrettierConfig.js +2 -2
  53. package/dist/esm/init/checks.js +5 -3
  54. package/dist/esm/init/detectRepoType.js +3 -3
  55. package/dist/esm/init/initCommand.d.ts +4 -4
  56. package/dist/esm/init/initCommand.js +7 -7
  57. package/dist/esm/init/parseJsonRecord.d.ts +1 -1
  58. package/dist/esm/init/parseJsonRecord.js +4 -4
  59. package/dist/esm/init/scaffold.d.ts +4 -4
  60. package/dist/esm/init/scaffold.js +15 -15
  61. package/dist/esm/init/templates.d.ts +10 -10
  62. package/dist/esm/init/templates.js +5 -5
  63. package/dist/esm/init/updateTemplatesCommand.d.ts +2 -2
  64. package/dist/esm/init/updateTemplatesCommand.js +14 -12
  65. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +1 -1
  66. package/dist/esm/injectReleaseNotesIntoReadme.js +6 -6
  67. package/dist/esm/injectSection.js +8 -8
  68. package/dist/esm/loadChangelogOverrides.d.ts +1 -1
  69. package/dist/esm/loadChangelogOverrides.js +9 -9
  70. package/dist/esm/loadConfig.d.ts +3 -3
  71. package/dist/esm/loadConfig.js +10 -10
  72. package/dist/esm/loadValidatedConfig.js +4 -4
  73. package/dist/esm/mergeConfig.d.ts +1 -1
  74. package/dist/esm/mergeConfig.js +27 -27
  75. package/dist/esm/monorepoConfigAssertions.js +15 -15
  76. package/dist/esm/npmRegistry.js +4 -4
  77. package/dist/esm/packsPath.js +3 -3
  78. package/dist/esm/parseChangeRecordBlock.js +33 -33
  79. package/dist/esm/parseCommitMessage.js +5 -5
  80. package/dist/esm/planChangelogFiles.js +8 -8
  81. package/dist/esm/planReleaseNotesPreviews.js +10 -10
  82. package/dist/esm/planVersionBump.js +24 -16
  83. package/dist/esm/prepareArgs.d.ts +9 -9
  84. package/dist/esm/prepareArgs.js +11 -11
  85. package/dist/esm/prepareCommand.js +23 -23
  86. package/dist/esm/prepareComposite.d.ts +4 -4
  87. package/dist/esm/prepareComposite.js +16 -16
  88. package/dist/esm/{releasePrepareTargets.d.ts → prepareReleaseTargets.d.ts} +9 -9
  89. package/dist/esm/{releasePrepareTargets.js → prepareReleaseTargets.js} +40 -40
  90. package/dist/esm/prepareRunContext.d.ts +1 -1
  91. package/dist/esm/prepareRunContext.js +5 -5
  92. package/dist/esm/prepareTarget.d.ts +3 -3
  93. package/dist/esm/prepareTarget.js +41 -39
  94. package/dist/esm/previewTagPrefixes.js +19 -19
  95. package/dist/esm/propagateBumps.js +8 -8
  96. package/dist/esm/publish.d.ts +2 -2
  97. package/dist/esm/publish.js +5 -5
  98. package/dist/esm/publishCommand.js +20 -20
  99. package/dist/esm/pushCommand.js +8 -8
  100. package/dist/esm/pushRelease.d.ts +1 -1
  101. package/dist/esm/pushRelease.js +2 -2
  102. package/dist/esm/readChangelogSections.d.ts +1 -1
  103. package/dist/esm/readChangelogSections.js +16 -16
  104. package/dist/esm/releasePlan.js +25 -25
  105. package/dist/esm/renderChangelogMarkdown.d.ts +4 -4
  106. package/dist/esm/renderChangelogMarkdown.js +18 -15
  107. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  108. package/dist/esm/renderReleaseNotes.js +16 -16
  109. package/dist/esm/reportPrepare.js +34 -34
  110. package/dist/esm/resolveCommandTags.js +9 -9
  111. package/dist/esm/resolveReleaseDeclaration.d.ts +2 -2
  112. package/dist/esm/resolveReleaseDeclaration.js +22 -22
  113. package/dist/esm/resolveReleaseTags.js +17 -15
  114. package/dist/esm/resolveReleaseTargets.js +2 -2
  115. package/dist/esm/showTagPrefixesCommand.js +4 -4
  116. package/dist/esm/sync-labels/generateCommand.d.ts +3 -3
  117. package/dist/esm/sync-labels/generateCommand.js +10 -10
  118. package/dist/esm/sync-labels/initCommand.d.ts +3 -3
  119. package/dist/esm/sync-labels/initCommand.js +15 -15
  120. package/dist/esm/sync-labels/presets.js +3 -3
  121. package/dist/esm/sync-labels/resolveLabels.js +13 -13
  122. package/dist/esm/sync-labels/templates.d.ts +2 -2
  123. package/dist/esm/sync-labels/templates.js +1 -1
  124. package/dist/esm/tagCommand.js +4 -4
  125. package/dist/esm/topologicalSort.d.ts +2 -2
  126. package/dist/esm/topologicalSort.js +7 -7
  127. package/dist/esm/types.d.ts +3 -3
  128. package/dist/esm/validateAllChangelogOverrides.js +9 -9
  129. package/dist/esm/validateConfig.d.ts +1 -1
  130. package/dist/esm/validateConfig.js +60 -50
  131. package/dist/esm/validateOverridesCommand.js +2 -2
  132. package/docs/changelogs.md +5 -5
  133. package/docs/configuration.md +1 -1
  134. package/docs/work-types.md +1 -1
  135. package/package.json +1 -1
@@ -18,16 +18,16 @@ export function loadChangelogOverrides(path) {
18
18
  errors: [`Failed to read override file ${path}: ${describeError(error)}`],
19
19
  };
20
20
  }
21
- let parsed;
21
+ let parsedJson;
22
22
  try {
23
- parsed = JSON.parse(content);
23
+ parsedJson = JSON.parse(content);
24
24
  }
25
25
  catch (error) {
26
26
  return {
27
27
  errors: [`Failed to parse override file ${path}: ${describeError(error)}`],
28
28
  };
29
29
  }
30
- const result = validateChangelogOverrides(parsed);
30
+ const result = validateChangelogOverrides(parsedJson);
31
31
  if (result.errors.length > 0) {
32
32
  return { errors: result.errors };
33
33
  }
@@ -41,14 +41,14 @@ export function parseOverrideKey(key) {
41
41
  }
42
42
  return { hashPrefix: key.slice(0, separatorIndex), entry: Number(key.slice(separatorIndex + 1)) };
43
43
  }
44
- export function validateChangelogOverrides(raw) {
44
+ export function validateChangelogOverrides(rawValue) {
45
45
  const overrides = new Map();
46
46
  const errors = [];
47
- if (!isRecord(raw)) {
47
+ if (!isRecord(rawValue)) {
48
48
  errors.push('Override file: top-level value must be an object keyed by commit hash');
49
49
  return { overrides, errors };
50
50
  }
51
- for (const [key, rawEntry] of Object.entries(raw)) {
51
+ for (const [key, rawEntry] of Object.entries(rawValue)) {
52
52
  if (key === SCHEMA_KEY) {
53
53
  if (typeof rawEntry !== 'string') {
54
54
  errors.push(`Override file: '${SCHEMA_KEY}' must be a string`);
@@ -59,9 +59,9 @@ export function validateChangelogOverrides(raw) {
59
59
  errors.push(`overrides['${key}']: key must be a lowercase hex commit hash or prefix, optionally followed by ':<n>'`);
60
60
  continue;
61
61
  }
62
- const validated = validateSingleOverride(key, rawEntry, errors);
63
- if (validated !== undefined) {
64
- overrides.set(key, validated);
62
+ const validatedOverride = validateSingleOverride(key, rawEntry, errors);
63
+ if (validatedOverride !== undefined) {
64
+ overrides.set(key, validatedOverride);
65
65
  }
66
66
  }
67
67
  return { overrides, errors };
@@ -3,13 +3,13 @@ export declare const ROOT_PACKAGE_JSON_PATH = "package.json";
3
3
  /**
4
4
  * Reads the root `package.json` and returns its `version` field.
5
5
  *
6
- * Returns `{ exists: false }` when the file is missing, `{ exists: true, version: undefined }`
7
- * when the file exists but does not have a `version` field, and `{ exists: true, version }`
6
+ * Returns `{ doesExist: false }` when the file is missing, `{ doesExist: true, version: undefined }`
7
+ * when the file exists but does not have a `version` field, and `{ doesExist: true, version }`
8
8
  * when both are present; `mergeMonorepoConfig` decides whether the situation is an error.
9
9
  * Throws when the file cannot be read or parsed.
10
10
  */
11
11
  export declare function readRootPackageVersion(): {
12
- exists: boolean;
12
+ doesExist: boolean;
13
13
  version: string | undefined;
14
14
  };
15
15
  /** The path where the consumer-facing config file is expected. */
@@ -15,7 +15,7 @@ export const ROOT_PACKAGE_JSON_PATH = 'package.json';
15
15
  export function readRootPackageVersion() {
16
16
  const absolutePath = path.resolve(process.cwd(), ROOT_PACKAGE_JSON_PATH);
17
17
  if (!existsSync(absolutePath)) {
18
- return { exists: false, version: undefined };
18
+ return { doesExist: false, version: undefined };
19
19
  }
20
20
  let contents;
21
21
  try {
@@ -24,17 +24,17 @@ export function readRootPackageVersion() {
24
24
  catch (error) {
25
25
  throw chainError(`Failed to read root ${ROOT_PACKAGE_JSON_PATH}`, error);
26
26
  }
27
- let parsed;
27
+ let parsedJson;
28
28
  try {
29
- parsed = JSON.parse(contents);
29
+ parsedJson = JSON.parse(contents);
30
30
  }
31
31
  catch (error) {
32
32
  throw chainError(`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}`, error);
33
33
  }
34
- if (!isRecord(parsed)) {
35
- return { exists: true, version: undefined };
34
+ if (!isRecord(parsedJson)) {
35
+ return { doesExist: true, version: undefined };
36
36
  }
37
- return { exists: true, version: typeof parsed['version'] === 'string' ? parsed['version'] : undefined };
37
+ return { doesExist: true, version: typeof parsedJson['version'] === 'string' ? parsedJson['version'] : undefined };
38
38
  }
39
39
  export const CONFIG_FILE_PATH = '.config/release-kit.config.ts';
40
40
  export async function loadConfig(configPath) {
@@ -45,10 +45,10 @@ export async function loadConfig(configPath) {
45
45
  }
46
46
  throw new Error(`Config file not found: ${absoluteConfigPath}`);
47
47
  }
48
- const imported = await import(__rewriteRelativeImportExtension(pathToFileURL(absoluteConfigPath).href));
49
- const resolved = isRecord(imported) ? (imported['default'] ?? imported['config']) : undefined;
50
- if (resolved === undefined) {
48
+ const importedModule = await import(__rewriteRelativeImportExtension(pathToFileURL(absoluteConfigPath).href));
49
+ const resolvedConfig = isRecord(importedModule) ? (importedModule['default'] ?? importedModule['config']) : undefined;
50
+ if (resolvedConfig === undefined) {
51
51
  throw new Error('Config file must have a default export or a named `config` export (e.g., `export default { ... }` or `export const config = { ... }`)');
52
52
  }
53
- return resolved;
53
+ return resolvedConfig;
54
54
  }
@@ -4,17 +4,17 @@ import { CONFIG_FILE_PATH, loadConfig } from "./loadConfig.js";
4
4
  import { validateConfig } from "./validateConfig.js";
5
5
  export async function loadValidatedConfig(configPath) {
6
6
  const configFilePath = configPath ?? CONFIG_FILE_PATH;
7
- let raw;
7
+ let rawConfig;
8
8
  try {
9
- raw = await loadConfig(configPath);
9
+ rawConfig = await loadConfig(configPath);
10
10
  }
11
11
  catch (error) {
12
12
  return { status: 'invalid', configFilePath, problem: { kind: 'load', message: describeError(error) } };
13
13
  }
14
- if (raw === undefined) {
14
+ if (rawConfig === undefined) {
15
15
  return { status: 'missing', configFilePath };
16
16
  }
17
- const { config, errors, warnings } = validateConfig(raw);
17
+ const { config, errors, warnings } = validateConfig(rawConfig);
18
18
  if (errors.length > 0) {
19
19
  return { status: 'invalid', configFilePath, problem: { kind: 'validation', errors } };
20
20
  }
@@ -36,6 +36,6 @@ export declare function resolveWorkTypes(userWorkTypes?: ReleaseKitConfig['workT
36
36
  * `mergeMonorepoConfig` does not perform any I/O.
37
37
  */
38
38
  export interface RootPackageInfo {
39
- exists: boolean;
39
+ doesExist: boolean;
40
40
  version: string | undefined;
41
41
  }
@@ -5,10 +5,10 @@ import { ROOT_PACKAGE_JSON_PATH } from "./loadConfig.js";
5
5
  import { assertLegacyIdentityDoesNotMatchCurrent, assertNoSharedVersionFiles, assertNoTagPrefixCollisions, assertRetiredPackagesDoNotCollideWithActive, assertUniqueTagPrefixes, } from "./monorepoConfigAssertions.js";
6
6
  import { resolveReleaseDeclaration } from "./resolveReleaseDeclaration.js";
7
7
  export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
8
- const discovered = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
9
- assertUniqueTagPrefixes(discovered);
10
- const { retained: workspaces, excluded, directories: declaredDirectories, composite, } = resolveReleaseDeclaration(discovered, userConfig);
11
- const directories = declaredDirectories.map((declared) => deriveDirectoryConfig(declared));
8
+ const discoveredWorkspaces = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
9
+ assertUniqueTagPrefixes(discoveredWorkspaces);
10
+ const { retainedWorkspaces: workspaces, excludedWorkspaces, directories: declaredDirectories, composite: declaredComposite, } = resolveReleaseDeclaration(discoveredWorkspaces, userConfig);
11
+ const directories = declaredDirectories.map((declaredDirectory) => deriveDirectoryConfig(declaredDirectory));
12
12
  for (const workspace of workspaces) {
13
13
  if (workspace.legacyIdentities !== undefined) {
14
14
  assertLegacyIdentityDoesNotMatchCurrent(workspace.dir, workspace.name, workspace.tagPrefix, workspace.legacyIdentities);
@@ -17,13 +17,13 @@ export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
17
17
  if (userConfig?.retiredPackages !== undefined) {
18
18
  assertRetiredPackagesDoNotCollideWithActive(workspaces, userConfig.retiredPackages);
19
19
  }
20
- const project = resolveProjectConfig(composite, rootPackage, workspaces);
20
+ const projectConfig = resolveProjectConfig(declaredComposite, rootPackage, workspaces);
21
21
  const workTypes = resolveWorkTypes(userConfig?.workTypes);
22
22
  const versionPatterns = userConfig?.versionPatterns === undefined ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
23
23
  const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
24
24
  const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
25
- assertNoSharedVersionFiles(workspaces, directories, project);
26
- assertNoTagPrefixCollisions(workspaces, directories, userConfig?.retiredPackages, project);
25
+ assertNoSharedVersionFiles(workspaces, directories, projectConfig);
26
+ assertNoTagPrefixCollisions(workspaces, directories, userConfig?.retiredPackages, projectConfig);
27
27
  const result = {
28
28
  workspaces,
29
29
  workTypes,
@@ -31,14 +31,14 @@ export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
31
31
  changelogJson,
32
32
  releaseNotes,
33
33
  };
34
- if (excluded.length > 0) {
35
- result.excludedWorkspaces = excluded;
34
+ if (excludedWorkspaces.length > 0) {
35
+ result.excludedWorkspaces = excludedWorkspaces;
36
36
  }
37
37
  if (directories.length > 0) {
38
38
  result.directories = directories;
39
39
  }
40
- if (project !== undefined) {
41
- result.project = project;
40
+ if (projectConfig !== undefined) {
41
+ result.project = projectConfig;
42
42
  }
43
43
  applyOptionalPassthroughFields(result, userConfig);
44
44
  return result;
@@ -80,39 +80,39 @@ function applyOptionalPassthroughFields(result, userConfig) {
80
80
  result.breakingPolicies = userConfig.breakingPolicies;
81
81
  }
82
82
  }
83
- function mergeChangelogJsonConfig(partial) {
84
- if (partial === undefined) {
83
+ function mergeChangelogJsonConfig(partialConfig) {
84
+ if (partialConfig === undefined) {
85
85
  return { ...DEFAULT_CHANGELOG_JSON_CONFIG };
86
86
  }
87
87
  return {
88
- enabled: partial.enabled ?? DEFAULT_CHANGELOG_JSON_CONFIG.enabled,
89
- outputPath: partial.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
90
- devOnlySections: partial.devOnlySections ?? [...DEFAULT_CHANGELOG_JSON_CONFIG.devOnlySections],
88
+ enabled: partialConfig.enabled ?? DEFAULT_CHANGELOG_JSON_CONFIG.enabled,
89
+ outputPath: partialConfig.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
90
+ devOnlySections: partialConfig.devOnlySections ?? [...DEFAULT_CHANGELOG_JSON_CONFIG.devOnlySections],
91
91
  };
92
92
  }
93
- function mergeReleaseNotesConfig(partial) {
94
- if (partial === undefined) {
93
+ function mergeReleaseNotesConfig(partialConfig) {
94
+ if (partialConfig === undefined) {
95
95
  return { ...DEFAULT_RELEASE_NOTES_CONFIG };
96
96
  }
97
97
  return {
98
- shouldInjectIntoReadme: partial.shouldInjectIntoReadme ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldInjectIntoReadme,
98
+ shouldInjectIntoReadme: partialConfig.shouldInjectIntoReadme ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldInjectIntoReadme,
99
99
  };
100
100
  }
101
- function resolveProjectConfig(composite, rootPackage, workspaces) {
102
- if (composite === undefined) {
101
+ function resolveProjectConfig(declaredComposite, rootPackage, workspaces) {
102
+ if (declaredComposite === undefined) {
103
103
  return undefined;
104
104
  }
105
- const subject = composite.name === undefined ? 'project block' : `composite release '${composite.name}'`;
106
- if (rootPackage === undefined || !rootPackage.exists) {
105
+ const subject = declaredComposite.name === undefined ? 'project block' : `composite release '${declaredComposite.name}'`;
106
+ if (rootPackage === undefined || !rootPackage.doesExist) {
107
107
  throw new Error(`${subject} requires a root ${ROOT_PACKAGE_JSON_PATH}; create one with a 'version' field at the repo root`);
108
108
  }
109
109
  if (rootPackage.version === undefined) {
110
110
  throw new Error(`${subject} requires root ${ROOT_PACKAGE_JSON_PATH} to have a 'version' field; add a 'version' field to the root package.json`);
111
111
  }
112
112
  return {
113
- paths: composite.paths ?? workspaces.flatMap((workspace) => workspace.paths),
114
- tagPrefix: composite.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX,
115
- ...(composite.name !== undefined && { name: composite.name }),
116
- ...(composite.members !== undefined && { members: composite.members }),
113
+ paths: declaredComposite.paths ?? workspaces.flatMap((workspace) => workspace.paths),
114
+ tagPrefix: declaredComposite.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX,
115
+ ...(declaredComposite.name !== undefined && { name: declaredComposite.name }),
116
+ ...(declaredComposite.members !== undefined && { members: declaredComposite.members }),
117
117
  };
118
118
  }
@@ -63,10 +63,10 @@ export function assertNoTagPrefixCollisions(workspaces, directories, retiredPack
63
63
  }
64
64
  }
65
65
  const retiredEntries = (retiredPackages ?? []).entries();
66
- for (const [index, retired] of retiredEntries) {
66
+ for (const [index, retiredPackage] of retiredEntries) {
67
67
  sources.push({
68
- prefix: retired.tagPrefix,
69
- label: `retiredPackages entry (name='${retired.name}')`,
68
+ prefix: retiredPackage.tagPrefix,
69
+ label: `retiredPackages entry (name='${retiredPackage.name}')`,
70
70
  owner: `retired:${index}`,
71
71
  });
72
72
  }
@@ -77,10 +77,10 @@ export function assertNoTagPrefixCollisions(workspaces, directories, retiredPack
77
77
  owner: 'project',
78
78
  });
79
79
  }
80
- for (let i = 0; i < sources.length; i++) {
81
- for (let j = i + 1; j < sources.length; j++) {
82
- const a = sources[i];
83
- const b = sources[j];
80
+ for (let aIndex = 0; aIndex < sources.length; aIndex++) {
81
+ for (let bIndex = aIndex + 1; bIndex < sources.length; bIndex++) {
82
+ const a = sources[aIndex];
83
+ const b = sources[bIndex];
84
84
  if (a === undefined || b === undefined || a.owner === b.owner)
85
85
  continue;
86
86
  if (isPrefixCollision(a.prefix, b.prefix)) {
@@ -96,23 +96,23 @@ export function assertRetiredPackagesDoNotCollideWithActive(workspaces, retiredP
96
96
  for (const workspace of workspaces) {
97
97
  workspaceByDerivedPrefix.set(workspace.tagPrefix, workspace);
98
98
  }
99
- for (const retired of retiredPackages) {
100
- const active = workspaceByDerivedPrefix.get(retired.tagPrefix);
101
- if (active !== undefined) {
102
- throw new Error(`retiredPackages: tagPrefix '${retired.tagPrefix}' collides with active workspace '${active.dir}' ` +
103
- `(derived prefix '${active.tagPrefix}'). A retired package's tagPrefix cannot belong to an active workspace.`);
99
+ for (const retiredPackage of retiredPackages) {
100
+ const activeWorkspace = workspaceByDerivedPrefix.get(retiredPackage.tagPrefix);
101
+ if (activeWorkspace !== undefined) {
102
+ throw new Error(`retiredPackages: tagPrefix '${retiredPackage.tagPrefix}' collides with active workspace '${activeWorkspace.dir}' ` +
103
+ `(derived prefix '${activeWorkspace.tagPrefix}'). A retired package's tagPrefix cannot belong to an active workspace.`);
104
104
  }
105
105
  }
106
106
  }
107
107
  export function assertUniqueTagPrefixes(workspaces) {
108
108
  const pathsByPrefix = new Map();
109
109
  for (const workspace of workspaces) {
110
- const existing = pathsByPrefix.get(workspace.tagPrefix);
111
- if (existing === undefined) {
110
+ const existingPaths = pathsByPrefix.get(workspace.tagPrefix);
111
+ if (existingPaths === undefined) {
112
112
  pathsByPrefix.set(workspace.tagPrefix, [workspace.workspacePath]);
113
113
  }
114
114
  else {
115
- existing.push(workspace.workspacePath);
115
+ existingPaths.push(workspace.workspacePath);
116
116
  }
117
117
  }
118
118
  for (const [prefix, paths] of pathsByPrefix) {
@@ -34,17 +34,17 @@ export function lookUpNpmPackage(packageName, registry) {
34
34
  return classifyNpmPackageLookup(runNpmJson(args));
35
35
  }
36
36
  export function readNpmError(stdout) {
37
- let parsed;
37
+ let parsedJson;
38
38
  try {
39
- parsed = JSON.parse(stdout);
39
+ parsedJson = JSON.parse(stdout);
40
40
  }
41
41
  catch {
42
42
  return undefined;
43
43
  }
44
- if (!isRecord(parsed) || !isRecord(parsed['error']) || typeof parsed['error']['code'] !== 'string') {
44
+ if (!isRecord(parsedJson) || !isRecord(parsedJson['error']) || typeof parsedJson['error']['code'] !== 'string') {
45
45
  return undefined;
46
46
  }
47
- const { code, summary } = parsed['error'];
47
+ const { code, summary } = parsedJson['error'];
48
48
  return { code, summary: typeof summary === 'string' ? summary : '' };
49
49
  }
50
50
  export function runNpmJson(args) {
@@ -11,10 +11,10 @@ const GLOB_TOKEN_PATTERN = /\*\*\/|\*\*|\*|\?|[^*?]+/g;
11
11
  function compileEntry(entry) {
12
12
  if (entry.startsWith('!'))
13
13
  return undefined;
14
- const normalized = entry.replace(/^\.?\//, '').replace(/\/+$/, '');
15
- if (normalized === '')
14
+ const normalizedEntry = entry.replace(/^\.?\//, '').replace(/\/+$/, '');
15
+ if (normalizedEntry === '')
16
16
  return undefined;
17
- return new RegExp(`^${translateGlob(normalized)}$`);
17
+ return new RegExp(`^${translateGlob(normalizedEntry)}$`);
18
18
  }
19
19
  function isStringArray(value) {
20
20
  return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
@@ -6,10 +6,10 @@ export function parseChangeRecordBlock(message) {
6
6
  if (fence === undefined) {
7
7
  return { kind: 'absent' };
8
8
  }
9
- if (fence.close === undefined) {
9
+ if (fence.closeIndex === undefined) {
10
10
  return { kind: 'malformed', reason: 'the block opens but never closes' };
11
11
  }
12
- const document = parseDocument(lines.slice(fence.open + 1, fence.close).join('\n'));
12
+ const document = parseDocument(lines.slice(fence.openIndex + 1, fence.closeIndex).join('\n'));
13
13
  const [error] = document.errors;
14
14
  if (error !== undefined) {
15
15
  const detail = (error.message.split('\n', 1)[0] ?? '').replace(/:$/, '');
@@ -21,60 +21,60 @@ export function stripChangeRecordBlocks(message) {
21
21
  const lines = message.split(/\r?\n/);
22
22
  const fences = findFences(lines);
23
23
  return lines
24
- .filter((_line, index) => fences.every((fence) => index < fence.open || index > (fence.close ?? lines.length)))
24
+ .filter((_line, index) => fences.every((fence) => index < fence.openIndex || index > (fence.closeIndex ?? lines.length)))
25
25
  .join('\n');
26
26
  }
27
27
  const FENCE = '```';
28
28
  function findFences(lines) {
29
29
  const fences = [];
30
- let open;
30
+ let openIndex;
31
31
  for (const [index, line] of lines.entries()) {
32
- if (open === undefined) {
32
+ if (openIndex === undefined) {
33
33
  if (line.trim() === `${FENCE}${INFO_STRING}`) {
34
- open = index;
34
+ openIndex = index;
35
35
  }
36
36
  }
37
37
  else if (line.trim() === FENCE) {
38
- fences.push({ close: index, open });
39
- open = undefined;
38
+ fences.push({ closeIndex: index, openIndex });
39
+ openIndex = undefined;
40
40
  }
41
41
  }
42
- if (open !== undefined) {
43
- fences.push({ open });
42
+ if (openIndex !== undefined) {
43
+ fences.push({ openIndex });
44
44
  }
45
45
  return fences;
46
46
  }
47
47
  const INFO_STRING = 'change-record';
48
48
  function readEntry(item, index) {
49
- const at = `entries[${index}]`;
49
+ const entryPath = `entries[${index}]`;
50
50
  if (!isRecord(item)) {
51
- return { reason: `\`${at}\` is not a mapping` };
51
+ return { reason: `\`${entryPath}\` is not a mapping` };
52
52
  }
53
53
  const { breaking, migration, scopes } = item;
54
54
  if (breaking !== undefined && breaking !== null && typeof breaking !== 'boolean') {
55
- return { reason: `\`${at}.breaking\` is not a boolean` };
55
+ return { reason: `\`${entryPath}.breaking\` is not a boolean` };
56
56
  }
57
57
  if (migration !== undefined && migration !== null && typeof migration !== 'string') {
58
- return { reason: `\`${at}.migration\` is not a string` };
58
+ return { reason: `\`${entryPath}.migration\` is not a string` };
59
59
  }
60
60
  if (scopes !== undefined && scopes !== null && !Array.isArray(scopes)) {
61
- return { reason: `\`${at}.scopes\` is not a list` };
61
+ return { reason: `\`${entryPath}.scopes\` is not a list` };
62
62
  }
63
63
  const declaredScopes = scopes ?? [];
64
64
  const readScopes = [];
65
65
  for (const [position, scope] of declaredScopes.entries()) {
66
66
  if (typeof scope !== 'string') {
67
- return { reason: `\`${at}.scopes[${position}]\` is not a string` };
67
+ return { reason: `\`${entryPath}.scopes[${position}]\` is not a string` };
68
68
  }
69
69
  readScopes.push(scope.trim());
70
70
  }
71
- const type = readRequiredString(item, at, 'type');
72
- if ('reason' in type) {
73
- return type;
71
+ const typeResult = readRequiredString(item, entryPath, 'type');
72
+ if ('reason' in typeResult) {
73
+ return typeResult;
74
74
  }
75
- const text = readRequiredString(item, at, 'text');
76
- if ('reason' in text) {
77
- return text;
75
+ const textResult = readRequiredString(item, entryPath, 'text');
76
+ if ('reason' in textResult) {
77
+ return textResult;
78
78
  }
79
79
  const trimmedMigration = migration?.trim();
80
80
  return {
@@ -82,8 +82,8 @@ function readEntry(item, index) {
82
82
  breaking: breaking === true,
83
83
  ...(trimmedMigration !== undefined && trimmedMigration !== '' && { migration: trimmedMigration }),
84
84
  scopes: readScopes,
85
- text: text.value,
86
- type: type.value,
85
+ text: textResult.value,
86
+ type: typeResult.value,
87
87
  },
88
88
  };
89
89
  }
@@ -106,11 +106,11 @@ function readPayload(payload) {
106
106
  return { kind: 'malformed', reason: '`entries` is not a list' };
107
107
  }
108
108
  for (const [index, item] of entries.entries()) {
109
- const read = readEntry(item, index);
110
- if ('reason' in read) {
111
- return { kind: 'malformed', reason: read.reason };
109
+ const readResult = readEntry(item, index);
110
+ if ('reason' in readResult) {
111
+ return { kind: 'malformed', reason: readResult.reason };
112
112
  }
113
- readEntries.push(read.entry);
113
+ readEntries.push(readResult.entry);
114
114
  }
115
115
  }
116
116
  const trimmedTicketRef = ticketRef?.trim();
@@ -121,14 +121,14 @@ function readPayload(payload) {
121
121
  ...(trimmedTicketRef !== undefined && trimmedTicketRef !== '' && { ticketRef: trimmedTicketRef }),
122
122
  };
123
123
  }
124
- function readRequiredString(item, at, name) {
124
+ function readRequiredString(item, entryPath, name) {
125
125
  const value = item[name];
126
126
  if (value === undefined || value === null) {
127
- return { reason: `\`${at}.${name}\` is missing` };
127
+ return { reason: `\`${entryPath}.${name}\` is missing` };
128
128
  }
129
129
  if (typeof value !== 'string') {
130
- return { reason: `\`${at}.${name}\` is not a string` };
130
+ return { reason: `\`${entryPath}.${name}\` is not a string` };
131
131
  }
132
- const trimmed = value.trim();
133
- return trimmed === '' ? { reason: `\`${at}.${name}\` is blank` } : { value: trimmed };
132
+ const trimmedValue = value.trim();
133
+ return trimmedValue === '' ? { reason: `\`${entryPath}.${name}\` is blank` } : { value: trimmedValue };
134
134
  }
@@ -30,7 +30,7 @@ export function parseCommitMessage(message, hash, workTypes, scopeAliases, optio
30
30
  };
31
31
  }
32
32
  export function parseSubject(subject, workTypes) {
33
- const taxonomy = toTaxonomy(workTypes);
33
+ const taxonomy = adaptToTaxonomy(workTypes);
34
34
  for (const nodes of SUBJECT_TEMPLATES) {
35
35
  const record = parse(nodes, subject, taxonomy);
36
36
  if (record !== undefined) {
@@ -54,14 +54,14 @@ export function evaluateBreakingPolicy(inputs) {
54
54
  return hasPrefixBreaking || hasFooterBreaking;
55
55
  }
56
56
  export function resolveType(rawType, workTypes) {
57
- const lowered = rawType.toLowerCase();
57
+ const loweredType = rawType.toLowerCase();
58
58
  for (const [key, config] of Object.entries(workTypes)) {
59
- if (key === lowered) {
59
+ if (key === loweredType) {
60
60
  return key;
61
61
  }
62
62
  if (config.aliases !== undefined) {
63
63
  for (const alias of config.aliases) {
64
- if (alias === lowered) {
64
+ if (alias === loweredType) {
65
65
  return key;
66
66
  }
67
67
  }
@@ -69,7 +69,7 @@ export function resolveType(rawType, workTypes) {
69
69
  }
70
70
  return undefined;
71
71
  }
72
- function toTaxonomy(workTypes) {
72
+ function adaptToTaxonomy(workTypes) {
73
73
  return {
74
74
  tiers: [],
75
75
  types: Object.entries(workTypes).map(([key, config]) => ({ aliases: config.aliases ?? [], key, tier: '' })),
@@ -13,16 +13,16 @@ export function planChangelogFiles(changelogDir, newEntries, settings, sectionOr
13
13
  changelogJsonFiles.push(jsonPath);
14
14
  }
15
15
  const changelogFile = joinPath(changelogDir, 'CHANGELOG.md');
16
- const preserved = planPreservedSections(changelogDir, entries);
17
- const keepVersions = settings.changelogMarkdown?.keepVersions;
18
- const rendered = renderChangelogFiles(entries, {
16
+ const preservedPlan = planPreservedSections(changelogDir, entries);
17
+ const keptVersionCount = settings.changelogMarkdown?.keptVersionCount;
18
+ const renderedFiles = renderChangelogFiles(entries, {
19
19
  sectionOrder,
20
- preservedSections: preserved.sections,
21
- ...(keepVersions !== undefined && { keepVersions }),
20
+ preservedSections: preservedPlan.sections,
21
+ ...(keptVersionCount !== undefined && { keptVersionCount }),
22
22
  });
23
- writes.push({ path: changelogFile, content: rendered.main });
23
+ writes.push({ path: changelogFile, content: renderedFiles.main });
24
24
  const archiveFiles = [];
25
- for (const { major, content } of rendered.archives) {
25
+ for (const { major, content } of renderedFiles.archives) {
26
26
  const path = joinPath(changelogDir, describeArchivePath(major));
27
27
  writes.push({ path, content });
28
28
  archiveFiles.push(path);
@@ -34,7 +34,7 @@ export function planChangelogFiles(changelogDir, newEntries, settings, sectionOr
34
34
  changelogFiles: [changelogFile, ...archiveFiles],
35
35
  changelogJsonFiles,
36
36
  entries,
37
- preservation: preserved.preservation,
37
+ preservation: preservedPlan.preservation,
38
38
  removals,
39
39
  writes,
40
40
  };
@@ -13,21 +13,21 @@ export function planReleaseNotesPreviews(options) {
13
13
  warnings.push(describePreviewSkip(workspacePath, result.reason, result.version));
14
14
  return { writes: [], warnings };
15
15
  }
16
- const { rendered } = result;
17
- if (!readme.exists && !readme.unreadable) {
16
+ const { renderedReadme } = result;
17
+ if (!readme.doesExist && !readme.unreadable) {
18
18
  warnings.push(`${readmePath} not found; skipping injected-README preview but still writing standalone release notes`);
19
19
  }
20
20
  const version = extractVersion(tag);
21
21
  const docsDir = path.join(workspacePath, 'docs');
22
22
  const writes = [];
23
- if (readme.exists) {
24
- writes.push({ path: path.join(docsDir, `README.v${version}.md`), content: rendered.injectedReadme });
23
+ if (readme.doesExist) {
24
+ writes.push({ path: path.join(docsDir, `README.v${version}.md`), content: renderedReadme.injectedReadme });
25
25
  }
26
26
  writes.push({
27
27
  path: path.join(docsDir, `RELEASE_NOTES.v${version}.md`),
28
- content: rendered.releaseNotesMarkdown.endsWith('\n')
29
- ? rendered.releaseNotesMarkdown
30
- : `${rendered.releaseNotesMarkdown}\n`,
28
+ content: renderedReadme.releaseNotesMarkdown.endsWith('\n')
29
+ ? renderedReadme.releaseNotesMarkdown
30
+ : `${renderedReadme.releaseNotesMarkdown}\n`,
31
31
  });
32
32
  return { writes, warnings };
33
33
  }
@@ -39,14 +39,14 @@ function describePreviewSkip(workspacePath, reason, version) {
39
39
  }
40
40
  function readWorkspaceReadme(readmePath, warnings) {
41
41
  if (!existsSync(readmePath)) {
42
- return { content: '', exists: false, unreadable: false };
42
+ return { content: '', doesExist: false, unreadable: false };
43
43
  }
44
44
  try {
45
- return { content: readFileSync(readmePath, 'utf8'), exists: true, unreadable: false };
45
+ return { content: readFileSync(readmePath, 'utf8'), doesExist: true, unreadable: false };
46
46
  }
47
47
  catch (error) {
48
48
  const message = describeError(error);
49
49
  warnings.push(`failed to read ${readmePath}: ${message}; skipping injected-README preview`);
50
- return { content: '', exists: false, unreadable: true };
50
+ return { content: '', doesExist: false, unreadable: true };
51
51
  }
52
52
  }