@williamthorsen/release-kit 12.0.0 → 13.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 (168) hide show
  1. package/.meta/changelog.json +212 -0
  2. package/.readyup/kits/default.js +18 -17
  3. package/.readyup/kits/npm-auto-publish.js +24 -23
  4. package/.readyup/manifest.json +16 -11
  5. package/CHANGELOG.md +58 -0
  6. package/README.md +55 -48
  7. package/bin/release-kit.js +1 -1
  8. package/dist/esm/assertCleanWorkingTree.d.ts +1 -5
  9. package/dist/esm/assertTaggedBaseline.d.ts +29 -0
  10. package/dist/esm/assertTaggedBaseline.js +41 -0
  11. package/dist/esm/attachChangelogDiagnostics.d.ts +1 -1
  12. package/dist/esm/attachChangelogDiagnostics.js +3 -0
  13. package/dist/esm/bin/release-kit.js +85 -22
  14. package/dist/esm/buildChangelogEntries.d.ts +23 -13
  15. package/dist/esm/buildChangelogEntries.js +36 -17
  16. package/dist/esm/buildDependencyGraph.d.ts +6 -6
  17. package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -8
  18. package/dist/esm/buildReleaseSummary.d.ts +2 -8
  19. package/dist/esm/buildSyntheticChangelogEntry.d.ts +3 -5
  20. package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
  21. package/dist/esm/bumpVersion.d.ts +1 -8
  22. package/dist/esm/changelogJsonFile.d.ts +5 -7
  23. package/dist/esm/changelogJsonFile.js +2 -17
  24. package/dist/esm/changelogJsonUtils.d.ts +7 -10
  25. package/dist/esm/changelogOverrides.d.ts +59 -98
  26. package/dist/esm/classifyChangelogCommit.d.ts +7 -9
  27. package/dist/esm/classifyChangelogCommit.js +3 -2
  28. package/dist/esm/commitCommand.d.ts +2 -4
  29. package/dist/esm/compareVersions.d.ts +10 -4
  30. package/dist/esm/compareVersions.js +16 -0
  31. package/dist/esm/configFlagSchema.d.ts +1 -1
  32. package/dist/esm/createGithubRelease.d.ts +7 -19
  33. package/dist/esm/createGithubReleaseCommand.d.ts +5 -5
  34. package/dist/esm/createGithubReleaseCommand.js +9 -9
  35. package/dist/esm/createTags.d.ts +2 -3
  36. package/dist/esm/decideRelease.d.ts +2 -6
  37. package/dist/esm/defaults.d.ts +5 -24
  38. package/dist/esm/defaults.js +4 -5
  39. package/dist/esm/defineConfig.d.ts +1 -1
  40. package/dist/esm/deleteFileIfExists.d.ts +1 -1
  41. package/dist/esm/deriveReleaseNotesConfig.d.ts +20 -0
  42. package/dist/esm/deriveReleaseNotesConfig.js +21 -0
  43. package/dist/esm/deriveWorkspaceConfig.d.ts +3 -5
  44. package/dist/esm/detectPackageManager.d.ts +2 -4
  45. package/dist/esm/detectUndeclaredTagPrefixes.d.ts +4 -6
  46. package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
  47. package/dist/esm/determineBumpType.d.ts +1 -1
  48. package/dist/esm/discoverWorkspaces.d.ts +14 -15
  49. package/dist/esm/discoverWorkspaces.js +12 -12
  50. package/dist/esm/enterRepoRoot.d.ts +12 -0
  51. package/dist/esm/enterRepoRoot.js +16 -0
  52. package/dist/esm/enumerateReleaseWindows.d.ts +8 -8
  53. package/dist/esm/extractMigration.d.ts +6 -8
  54. package/dist/esm/findUnroutedEntryScopes.d.ts +16 -0
  55. package/dist/esm/findUnroutedEntryScopes.js +57 -0
  56. package/dist/esm/formatExcludedSkip.d.ts +3 -0
  57. package/dist/esm/formatExcludedSkip.js +3 -0
  58. package/dist/esm/formatPrivateSkip.d.ts +1 -6
  59. package/dist/esm/generateChangelogs.d.ts +10 -5
  60. package/dist/esm/glyphs.d.ts +1 -1
  61. package/dist/esm/help/prepareHelp.d.ts +3 -4
  62. package/dist/esm/help/prepareHelp.js +4 -3
  63. package/dist/esm/init/checks.d.ts +3 -3
  64. package/dist/esm/init/checks.js +4 -1
  65. package/dist/esm/init/detectRepoType.d.ts +2 -3
  66. package/dist/esm/init/hasProvenance.d.ts +2 -0
  67. package/dist/esm/init/hasProvenance.js +3 -0
  68. package/dist/esm/init/initCommand.d.ts +3 -6
  69. package/dist/esm/init/initCommand.js +10 -10
  70. package/dist/esm/init/parseJsonRecord.d.ts +1 -4
  71. package/dist/esm/init/scaffold.d.ts +1 -1
  72. package/dist/esm/init/templates.d.ts +9 -11
  73. package/dist/esm/init/templates.js +12 -11
  74. package/dist/esm/init/updateTemplatesCommand.d.ts +13 -0
  75. package/dist/esm/init/updateTemplatesCommand.js +64 -0
  76. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +12 -17
  77. package/dist/esm/injectSection.d.ts +3 -3
  78. package/dist/esm/loadConfig.d.ts +16 -14
  79. package/dist/esm/loadConfig.js +2 -2
  80. package/dist/esm/loadValidatedConfig.d.ts +16 -19
  81. package/dist/esm/loadValidatedConfig.js +6 -1
  82. package/dist/esm/packsPath.d.ts +9 -9
  83. package/dist/esm/parseChangeRecordBlock.d.ts +2 -2
  84. package/dist/esm/parseCommitMessage.d.ts +17 -32
  85. package/dist/esm/parseCommitMessage.js +28 -37
  86. package/dist/esm/parseRequestedTags.d.ts +3 -3
  87. package/dist/esm/planReleaseNotesPreviews.d.ts +6 -6
  88. package/dist/esm/planVersionBump.d.ts +5 -3
  89. package/dist/esm/planVersionBump.js +3 -0
  90. package/dist/esm/prepareCommand.d.ts +4 -7
  91. package/dist/esm/prepareCommand.js +13 -6
  92. package/dist/esm/previewTagPrefixes.d.ts +8 -10
  93. package/dist/esm/propagateBumps.d.ts +6 -7
  94. package/dist/esm/publish.d.ts +1 -1
  95. package/dist/esm/publishCommand.d.ts +4 -3
  96. package/dist/esm/publishCommand.js +8 -8
  97. package/dist/esm/pushCommand.d.ts +4 -3
  98. package/dist/esm/pushCommand.js +8 -2
  99. package/dist/esm/pushRelease.d.ts +2 -2
  100. package/dist/esm/readChangelogSections.d.ts +33 -0
  101. package/dist/esm/readChangelogSections.js +80 -0
  102. package/dist/esm/releaseFiles.d.ts +5 -7
  103. package/dist/esm/releasePlan.d.ts +11 -13
  104. package/dist/esm/releasePrepare.d.ts +23 -22
  105. package/dist/esm/releasePrepare.js +31 -11
  106. package/dist/esm/releasePrepareMono.d.ts +9 -7
  107. package/dist/esm/releasePrepareMono.js +85 -33
  108. package/dist/esm/releasePrepareProject.d.ts +19 -29
  109. package/dist/esm/releasePrepareProject.js +31 -12
  110. package/dist/esm/renderChangelogMarkdown.d.ts +10 -4
  111. package/dist/esm/renderChangelogMarkdown.js +27 -2
  112. package/dist/esm/renderReleaseNotes.d.ts +3 -11
  113. package/dist/esm/renderReleaseNotes.js +2 -6
  114. package/dist/esm/reportPrepare.d.ts +4 -5
  115. package/dist/esm/reportPrepare.js +33 -19
  116. package/dist/esm/resolveCommandTags.d.ts +15 -11
  117. package/dist/esm/resolveCommandTags.js +35 -5
  118. package/dist/esm/resolveConfigFlag.d.ts +7 -0
  119. package/dist/esm/resolveConfigFlag.js +4 -0
  120. package/dist/esm/resolveReleaseTags.d.ts +6 -8
  121. package/dist/esm/resolveReleaseTags.js +1 -1
  122. package/dist/esm/resolveStylesOrExit.d.ts +2 -2
  123. package/dist/esm/showTagPrefixesCommand.d.ts +7 -10
  124. package/dist/esm/showTagPrefixesCommand.js +4 -4
  125. package/dist/esm/stripEmojiPrefix.d.ts +1 -0
  126. package/dist/esm/stripScope.d.ts +2 -2
  127. package/dist/esm/stripScope.js +3 -3
  128. package/dist/esm/sync-labels/generateCommand.d.ts +11 -10
  129. package/dist/esm/sync-labels/generateCommand.js +11 -8
  130. package/dist/esm/sync-labels/initCommand.d.ts +6 -7
  131. package/dist/esm/sync-labels/initCommand.js +2 -2
  132. package/dist/esm/sync-labels/presets.d.ts +5 -4
  133. package/dist/esm/sync-labels/presets.js +9 -9
  134. package/dist/esm/sync-labels/resolveLabels.d.ts +7 -7
  135. package/dist/esm/sync-labels/resolveLabels.js +1 -1
  136. package/dist/esm/sync-labels/retiredConfig.d.ts +4 -5
  137. package/dist/esm/sync-labels/syncCommand.d.ts +1 -4
  138. package/dist/esm/sync-labels/templates.d.ts +3 -3
  139. package/dist/esm/sync-labels/templates.js +3 -3
  140. package/dist/esm/sync-labels/typeLabels.d.ts +14 -0
  141. package/dist/esm/sync-labels/typeLabels.js +38 -0
  142. package/dist/esm/sync-labels/types.d.ts +1 -1
  143. package/dist/esm/tagCommand.d.ts +1 -1
  144. package/dist/esm/typeGuards.d.ts +2 -2
  145. package/dist/esm/types.d.ts +116 -91
  146. package/dist/esm/types.js +1 -1
  147. package/dist/esm/validateConfig.d.ts +2 -11
  148. package/dist/esm/validateConfig.js +2 -2
  149. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -12
  150. package/dist/esm/validateOverridesCommand.d.ts +17 -20
  151. package/dist/esm/validateOverridesCommand.js +1 -1
  152. package/docs/api.md +6 -6
  153. package/docs/changelogs.md +63 -40
  154. package/docs/configuration.md +29 -27
  155. package/docs/editorial-overrides.md +23 -23
  156. package/docs/labels.md +21 -19
  157. package/docs/project-releases.md +13 -13
  158. package/docs/publishing.md +5 -5
  159. package/docs/readiness-checks.md +3 -3
  160. package/docs/releasing.md +50 -25
  161. package/docs/work-types.md +41 -57
  162. package/package.json +7 -6
  163. package/presets/labels/common.yaml +1 -41
  164. package/dist/esm/resolveReleaseNotesConfig.d.ts +0 -26
  165. package/dist/esm/resolveReleaseNotesConfig.js +0 -29
  166. package/dist/esm/workTypesData.d.ts +0 -50
  167. package/dist/esm/workTypesData.js +0 -145
  168. package/schemas/label-map.json +0 -24
@@ -1,10 +1,10 @@
1
1
  import { type RawCommit } from './enumerateReleaseWindows.js';
2
2
  import type { GenerateChangelogOptions } from './generateChangelogs.js';
3
- import type { ChangelogEntry, ChangelogSection, MalformedChangeRecordBlock, PolicyViolation, ReleaseConfig, ReleaseType, UndeclaredEntryType } from './types.js';
3
+ import type { ChangelogEntry, ChangelogSection, MalformedChangeRecordBlock, PolicyViolation, ReleaseConfig, ReleaseType, UndeclaredEntryType, UnroutedEntryScope } from './types.js';
4
4
  /**
5
5
  * Strips a section title down to its bare name.
6
6
  *
7
- * A default title carries a leading emoji (e.g. `"🐛 Bug fixes"`), while a consumer's
7
+ * A default title has a leading emoji (e.g. `"🐛 Bug fixes"`), while a consumer's
8
8
  * `devOnlySections` or `workTypes` override may be written as a bare name. Comparing both sides
9
9
  * through this helper lets the two forms match.
10
10
  */
@@ -12,8 +12,7 @@ export declare function stripGroupDecorations(group: string): string;
12
12
  /**
13
13
  * Builds structured changelog entries from the release windows of git history, naming the unreleased window `tag`.
14
14
  *
15
- * Composes `readReleaseHistory` and `toChangelogEntries` for a caller that knows the tag before it reads. Performs no
16
- * `changelog.json` I/O; callers persist the entries via `renderChangelogJson`.
15
+ * Composes `readReleaseHistory` and `toChangelogEntries` for a caller that knows the tag before it reads.
17
16
  */
18
17
  export declare function buildChangelogEntries(config: ReleaseHistoryConfig, tag: string, options: GenerateChangelogOptions): {
19
18
  entries: ChangelogEntry[];
@@ -25,30 +24,35 @@ export interface ChangelogDiagnostics {
25
24
  /** Breaking-policy violations of the window's titles and change-record entries. */
26
25
  policyViolations: PolicyViolation[];
27
26
  undeclaredEntryTypes: UndeclaredEntryType[];
27
+ /**
28
+ * Entry scopes that don't route to any workspace. A read leaves it empty: Only a pass over every workspace can fill
29
+ * it.
30
+ */
31
+ unroutedEntryScopes: UnroutedEntryScope[];
28
32
  }
29
33
  /**
30
34
  * Reads a scope's release windows once and returns the changelog items of every window, with what the unreleased
31
35
  * window decides: its commits, its bump, and its diagnostics.
32
36
  *
33
- * A commit whose last `change-record` block records entries yields one item per entry; any other commit is
34
- * classified by its title through `classifyChangelogCommit`. A `release:` or merge subject yields nothing either
35
- * way. Only the unreleased window records diagnostics, since the released windows were reported when they were
36
- * prepared.
37
+ * A commit whose last `change-record` block records entries yields one item per entry routed to
38
+ * `options.workspaceDir`; any other commit is classified by its title through `classifyChangelogCommit`. A `release:`
39
+ * or merge subject yields nothing either way. The read records diagnostics only for the unreleased window, since the
40
+ * released windows were reported when they were prepared.
37
41
  */
38
42
  export declare function readReleaseHistory(config: ReleaseHistoryConfig, options: GenerateChangelogOptions): ReleaseHistory;
39
43
  /** The configuration that `readReleaseHistory` reads. */
40
- export type ReleaseHistoryConfig = Pick<ReleaseConfig, 'breakingPolicies' | 'changelogJson' | 'versionPatterns' | 'workTypes'>;
44
+ export type ReleaseHistoryConfig = Pick<ReleaseConfig, 'breakingPolicies' | 'changelogJson' | 'scopeAliases' | 'versionPatterns' | 'workTypes'>;
41
45
  /** One read of a scope's release windows. */
42
46
  export interface ReleaseHistory {
43
47
  /** The newest matching tag that HEAD reaches; undefined when none does. */
44
48
  previousTag: string | undefined;
45
- /** Entries of the released windows, newest first; a window that yields no item contributes none. */
49
+ /** Entries of the released windows, newest first; a window that doesn't yield any item contributes none. */
46
50
  releasedEntries: ChangelogEntry[];
47
51
  unreleased: UnreleasedWindowReading;
48
52
  }
49
53
  /** The unreleased window: its changelog sections and commits, and the bump and diagnostics that they determine. */
50
54
  export interface UnreleasedWindowReading {
51
- /** The highest bump that the window's items call for; undefined when the window yields no item. */
55
+ /** The highest bump that the window's items call for; undefined when the window doesn't yield any item. */
52
56
  bump: ReleaseType | undefined;
53
57
  /** The window's commits, newest first, without release commits. */
54
58
  commits: RawCommit[];
@@ -59,10 +63,16 @@ export interface UnreleasedWindowReading {
59
63
  parsedCommitCount: number;
60
64
  sections: ChangelogSection[];
61
65
  /**
62
- * Commits that yield no item and that neither an exclusion nor a diagnostic accounts for, newest first; undefined
63
- * when none.
66
+ * Commits that don't yield any item and that neither an exclusion nor a diagnostic accounts for, newest first;
67
+ * undefined when none.
64
68
  */
65
69
  unparseableCommits: RawCommit[] | undefined;
66
70
  }
67
71
  /** Labels the unreleased window of `history` with `tag` and returns the entries of every window, newest first. */
68
72
  export declare function toChangelogEntries(history: ReleaseHistory, tag: string): ChangelogEntry[];
73
+ /**
74
+ * Returns the entries that a direct release tagged `tag` records: those of `toChangelogEntries`, or, when the
75
+ * unreleased window doesn't yield any item, the synthetic "Forced version bump." entry dated `today` ahead of the
76
+ * released entries.
77
+ */
78
+ export declare function toReleaseEntries(history: ReleaseHistory, tag: string, today: string): ChangelogEntry[];
@@ -1,4 +1,5 @@
1
1
  import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
2
+ import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
2
3
  import { extractVersion } from "./changelogJsonUtils.js";
3
4
  import { classifyChangelogCommit, isNonChangeSubject, isReleaseSubject } from "./classifyChangelogCommit.js";
4
5
  import { DEFAULT_BREAKING_POLICIES, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
@@ -6,7 +7,7 @@ import { determineBumpType } from "./determineBumpType.js";
6
7
  import { enumerateReleaseWindows } from "./enumerateReleaseWindows.js";
7
8
  import { extractMigration } from "./extractMigration.js";
8
9
  import { parseChangeRecordBlock, stripChangeRecordBlocks } from "./parseChangeRecordBlock.js";
9
- import { COMMIT_PREPROCESSOR_PATTERNS, evaluateBreakingPolicy, PIPE_SCOPE_SOURCE, resolveType, } from "./parseCommitMessage.js";
10
+ import { evaluateBreakingPolicy, parseSubject, resolveType } from "./parseCommitMessage.js";
10
11
  import { stripEmojiPrefix } from "./stripEmojiPrefix.js";
11
12
  const UNRELEASED_TAG = 'unreleased';
12
13
  const CANONICAL_SECTION_ORDER = new Map(Object.values(DEFAULT_WORK_TYPES).map((config, index) => [stripGroupDecorations(config.header), index]));
@@ -31,6 +32,9 @@ export function readReleaseHistory(config, options) {
31
32
  const context = {
32
33
  breakingPolicies: config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES,
33
34
  devOnlySections: new Set(config.changelogJson.devOnlySections.map(stripGroupDecorations)),
35
+ ...(options.workspaceDir !== undefined && {
36
+ routing: { workspaceDir: options.workspaceDir, scopeAliases: config.scopeAliases ?? {} },
37
+ }),
34
38
  versionPatterns: config.versionPatterns ?? DEFAULT_VERSION_PATTERNS,
35
39
  workTypes: config.workTypes ?? DEFAULT_WORK_TYPES,
36
40
  };
@@ -50,9 +54,20 @@ export function toChangelogEntries(history, tag) {
50
54
  ...history.releasedEntries,
51
55
  ];
52
56
  }
57
+ export function toReleaseEntries(history, tag, today) {
58
+ if (history.unreleased.sections.length === 0) {
59
+ return [buildEmptyReleaseEntry(extractVersion(tag), today), ...history.releasedEntries];
60
+ }
61
+ return toChangelogEntries(history, tag);
62
+ }
53
63
  function transformReleases(windows, context) {
54
64
  const [unreleasedWindow, baselineWindow] = windows;
55
- const diagnostics = { malformedBlocks: [], policyViolations: [], undeclaredEntryTypes: [] };
65
+ const diagnostics = {
66
+ malformedBlocks: [],
67
+ policyViolations: [],
68
+ undeclaredEntryTypes: [],
69
+ unroutedEntryScopes: [],
70
+ };
56
71
  const signals = [];
57
72
  const unparseable = [];
58
73
  let parsedCommitCount = 0;
@@ -130,7 +145,7 @@ function readCommit(commit, context, diagnostics) {
130
145
  if (classification.kind !== 'header') {
131
146
  return { items: [], isUnparseable: classification.kind === 'unparseable' && reading.kind !== 'malformed' };
132
147
  }
133
- const item = buildTitleItem(commit, classification.breaking);
148
+ const item = buildTitleItem(commit, classification.breaking, context.workTypes);
134
149
  return {
135
150
  items: [
136
151
  { header: classification.header, item, signal: { type: classification.type, breaking: item.breaking === true } },
@@ -149,6 +164,15 @@ function buildSections(sectionMap, devOnlySections) {
149
164
  }
150
165
  return sections.toSorted((a, b) => canonicalSectionPriority(a.title) - canonicalSectionPriority(b.title));
151
166
  }
167
+ function isRoutedTo(scopes, routing) {
168
+ if (scopes.length === 0) {
169
+ return true;
170
+ }
171
+ return scopes.some((scope) => {
172
+ const resolved = routing.scopeAliases[scope] ?? scope;
173
+ return resolved === '*' || resolved === routing.workspaceDir;
174
+ });
175
+ }
152
176
  function formatDate(timestamp) {
153
177
  return new Date(timestamp * 1_000).toISOString().slice(0, 10);
154
178
  }
@@ -161,7 +185,10 @@ function appendItem(sectionMap, header, item) {
161
185
  items.push(item);
162
186
  }
163
187
  function buildEntryItem(commit, entry, position, prNumber, context, diagnostics) {
164
- const { breakingPolicies, workTypes } = context;
188
+ const { breakingPolicies, routing, workTypes } = context;
189
+ if (routing !== undefined && !isRoutedTo(entry.scopes, routing)) {
190
+ return undefined;
191
+ }
165
192
  const type = resolveType(entry.type, workTypes);
166
193
  if (type === undefined) {
167
194
  diagnostics?.undeclaredEntryTypes.push({
@@ -208,13 +235,13 @@ function buildEntryItem(commit, entry, position, prNumber, context, diagnostics)
208
235
  item.entry = position;
209
236
  return { header: config.header, item, signal: { type, breaking } };
210
237
  }
211
- function buildTitleItem(commit, isParsedBreaking) {
238
+ function buildTitleItem(commit, isParsedBreaking, workTypes) {
212
239
  const body = extractBody(stripChangeRecordBlocks(commit.message));
213
240
  const item = { description: extractDescription(commit.message) };
214
241
  if (body !== undefined) {
215
242
  item.body = body;
216
243
  }
217
- if (isParsedBreaking && subjectHasBreakingMarker(commit.message)) {
244
+ if (isParsedBreaking && subjectHasBreakingMarker(commit.message, workTypes)) {
218
245
  item.breaking = true;
219
246
  }
220
247
  const migration = extractMigration(body);
@@ -224,21 +251,13 @@ function buildTitleItem(commit, isParsedBreaking) {
224
251
  item.hash = commit.hash;
225
252
  return item;
226
253
  }
227
- const SUBJECT_BREAKING_MARKER_PATTERN = new RegExp(String.raw `^(?:${PIPE_SCOPE_SOURCE}\|)?\w+(?:\([^)]+\))?!:`);
228
- function subjectHasBreakingMarker(message) {
229
- let subject = message.split('\n', 1)[0] ?? '';
230
- for (const pattern of COMMIT_PREPROCESSOR_PATTERNS) {
231
- subject = subject.replace(pattern, '');
232
- }
233
- return SUBJECT_BREAKING_MARKER_PATTERN.test(subject);
254
+ function subjectHasBreakingMarker(message, workTypes) {
255
+ return parseSubject(message.split('\n', 1)[0] ?? '', workTypes)?.breaking === true;
234
256
  }
235
257
  function extractDescription(message) {
236
258
  const firstLine = message.split('\n', 1)[0] ?? message;
237
259
  const afterColon = firstLine.split(': ').slice(1).join(': ');
238
- if (afterColon.length > 0) {
239
- return afterColon.charAt(0).toUpperCase() + afterColon.slice(1);
240
- }
241
- return firstLine;
260
+ return afterColon.charAt(0).toUpperCase() + afterColon.slice(1);
242
261
  }
243
262
  const TRAILER_PATTERNS = [
244
263
  /^Change:/i,
@@ -1,17 +1,17 @@
1
1
  import type { WorkspaceConfig } from './types.js';
2
2
  /** Identities, current versions, and `workspace:` dependency edges of the workspaces' packages. */
3
3
  export interface DependencyGraph {
4
- /** Resolve a package name to its workspace `dir`. */
4
+ /** Maps a package name to its workspace `dir`. */
5
5
  packageNameToDir: Map<string, string>;
6
- /** Resolve a workspace `dir` to its package name (inverse of `packageNameToDir`). */
6
+ /** Maps a workspace `dir` to its package name (inverse of `packageNameToDir`). */
7
7
  dirToPackageName: Map<string, string>;
8
- /** Resolve a workspace `dir` to its current version; absent when its `package.json` has no string `version`. */
8
+ /** Maps a workspace `dir` to its current version; absent when its `package.json` doesn't have a string `version`. */
9
9
  dirToVersion: Map<string, string>;
10
- /** Map a package name to the workspaces that depend on it. */
10
+ /** Maps a package name to the workspaces that depend on it. */
11
11
  dependentsOf: Map<string, WorkspaceConfig[]>;
12
12
  /**
13
- * Forward adjacency: map a workspace `dir` to the set of `workspace:`-protocol package
14
- * names it declares in `dependencies` or `peerDependencies`. Complement of `dependentsOf`.
13
+ * Maps a workspace `dir` to the `workspace:`-protocol package names that it declares in `dependencies` or
14
+ * `peerDependencies` (the forward counterpart of `dependentsOf`).
15
15
  */
16
16
  dependenciesOf: Map<string, Set<string>>;
17
17
  }
@@ -1,10 +1,3 @@
1
1
  import type { ChangelogEntry } from './types.js';
2
- /**
3
- * Build a synthetic changelog entry for a forced empty-range release.
4
- *
5
- * Produces a single `ChangelogEntry` with one `'Notes'` section (audience `'dev'`)
6
- * containing a single `'Forced version bump.'` item. Used when `release-kit prepare`
7
- * proceeds via `--force`, `--bump=X`, or `--set-version` against a unit with zero
8
- * qualifying commits since its last tag. Pure function: no I/O.
9
- */
2
+ /** Builds the changelog entry of a forced release whose unreleased window doesn't yield any changelog item. */
10
3
  export declare function buildEmptyReleaseEntry(version: string, date: string): ChangelogEntry;
@@ -1,12 +1,6 @@
1
1
  import type { PrepareResult } from './types.js';
2
2
  /**
3
- * Build a release commit body from a prepare result.
4
- *
5
- * Each released workspace with commits gets a section headed by its tag,
6
- * followed by scope-stripped commit messages as bullet points. Sections
7
- * are separated by blank lines. The project release (when present) is
8
- * appended as a final section using the same format. Returns an empty
9
- * string when no released workspaces have commits and there is no project
10
- * release.
3
+ * Builds a release commit body from a prepare result: one section per released workspace or project that has commits,
4
+ * the project last, each headed by its tag and listing its scope-stripped commit subjects.
11
5
  */
12
6
  export declare function buildReleaseSummary(result: Pick<PrepareResult, 'project' | 'workspaces'>): string;
@@ -1,11 +1,9 @@
1
1
  import type { ChangelogEntry } from './types.js';
2
2
  /**
3
- * Build a synthetic changelog entry for a propagation-only bump.
4
- *
5
- * Produces a single `ChangelogEntry` with one `'Dependency updates'` section (audience `'dev'`)
6
- * containing one item per propagated-from dependency. Pure function: no I/O.
3
+ * Builds the changelog entry of a propagation-only bump, with one item per dependency whose bump propagated, under
4
+ * `sectionTitle`, the resolved `deps` work type's header.
7
5
  */
8
6
  export declare function buildSyntheticChangelogEntry(propagatedFrom: ReadonlyArray<{
9
7
  packageName: string;
10
8
  newVersion: string;
11
- }>, version: string, date: string): ChangelogEntry;
9
+ }>, version: string, date: string, sectionTitle: string): ChangelogEntry;
@@ -1,10 +1,10 @@
1
- export function buildSyntheticChangelogEntry(propagatedFrom, version, date) {
1
+ export function buildSyntheticChangelogEntry(propagatedFrom, version, date, sectionTitle) {
2
2
  const items = propagatedFrom.map((dep) => ({
3
3
  description: `Bumped \`${dep.packageName}\` to ${dep.newVersion}`,
4
4
  }));
5
5
  return {
6
6
  version,
7
7
  date,
8
- sections: [{ title: 'Dependency updates', audience: 'dev', items }],
8
+ sections: [{ title: sectionTitle, audience: 'dev', items }],
9
9
  };
10
10
  }
@@ -1,10 +1,3 @@
1
1
  import type { ReleaseType } from './types.js';
2
- /**
3
- * Bumps a semver version string by the given release type.
4
- *
5
- * @param version - A semver version string (e.g., '1.2.3').
6
- * @param releaseType - The type of release bump to apply.
7
- * @returns The bumped version string.
8
- * @throws If the version string is not a valid semver format.
9
- */
2
+ /** Bumps a `major.minor.patch` version by the release type; throws on a version of any other form. */
10
3
  export declare function bumpVersion(version: string, releaseType: ReleaseType): string;
@@ -1,15 +1,13 @@
1
1
  import type { ChangelogEntry, ReleaseConfig } from './types.js';
2
- /** Resolve the absolute output path for the `changelog.json` file under a workspace's changelog directory. */
2
+ /** Resolves the path of the `changelog.json` file under a workspace's changelog directory. */
3
3
  export declare function resolveChangelogJsonPath(config: Pick<ReleaseConfig, 'changelogJson'>, changelogPath: string): string;
4
4
  /** Renders changelog entries as a `changelog.json` file's complete content, sorted newest-first. */
5
5
  export declare function renderChangelogJson(entries: ChangelogEntry[]): string;
6
6
  /**
7
- * Merges `entries` with the on-disk entries at `filePath` and returns the merged set in
8
- * newest-first order, without writing.
7
+ * Merges `entries` with the on-disk entries at `filePath` and returns the merged set in newest-first order, without
8
+ * writing.
9
9
  *
10
- * Preserves entries that exist on disk but are absent from `entries` — load-bearing for
11
- * synthetic-entry preservation across propagation runs. Reads the file when present; treats a
12
- * missing or malformed file as an empty existing set, so a malformed file does not abort the
13
- * release.
10
+ * Keeps an on-disk entry that `entries` lacks, which preserves synthetic entries across propagation runs. A missing or
11
+ * malformed file counts as empty, so that a malformed file does not abort the release.
14
12
  */
15
13
  export declare function mergeChangelogEntriesWithDisk(filePath: string, entries: ChangelogEntry[]): ChangelogEntry[];
@@ -2,8 +2,8 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { describeError } from '@williamthorsen/toolbelt.errors';
4
4
  import stringify from 'json-stringify-pretty-compact';
5
- import semver from 'semver';
6
5
  import { isChangelogEntry } from "./changelogJsonUtils.js";
6
+ import { compareVersionsDescending } from "./compareVersions.js";
7
7
  import { isUnknownArray } from "./typeGuards.js";
8
8
  export function resolveChangelogJsonPath(config, changelogPath) {
9
9
  return join(changelogPath, config.changelogJson.outputPath);
@@ -16,7 +16,7 @@ export function mergeChangelogEntriesWithDisk(filePath, entries) {
16
16
  return mergeEntries(entries, existing);
17
17
  }
18
18
  function sortNewestFirst(entries) {
19
- return [...entries].sort((a, b) => compareVersionsDescending(a.version, b.version));
19
+ return [...entries].toSorted((a, b) => compareVersionsDescending(a.version, b.version));
20
20
  }
21
21
  function readExistingEntries(filePath) {
22
22
  if (!existsSync(filePath)) {
@@ -45,18 +45,3 @@ function mergeEntries(newEntries, existingEntries) {
45
45
  }
46
46
  return sortNewestFirst(versionMap.values());
47
47
  }
48
- function compareVersionsDescending(a, b) {
49
- const aValid = semver.valid(a);
50
- const bValid = semver.valid(b);
51
- if (aValid && bValid)
52
- return semver.rcompare(aValid, bValid);
53
- if (aValid)
54
- return -1;
55
- if (bValid)
56
- return 1;
57
- if (a > b)
58
- return -1;
59
- if (a < b)
60
- return 1;
61
- return 0;
62
- }
@@ -1,19 +1,16 @@
1
1
  import type { ChangelogEntry } from './types.js';
2
2
  /**
3
- * Type guard for `ChangelogEntry` values parsed from JSON.
3
+ * Reports whether a value parsed from JSON is a `ChangelogEntry`.
4
4
  *
5
- * Checks the entry's top level only: `sections` is verified to be an array, and its contents pass
6
- * through unvalidated. `.meta/changelog.json` is written by release-kit and read tolerantly; strict
7
- * validation applies to the human-authored `.meta/changelog-overrides.json`, in `changelogOverrides.ts`.
5
+ * Checks the entry's top level only: `sections` must be an array, and its contents pass through unvalidated.
6
+ * `.meta/changelog.json` is written by release-kit and read tolerantly; strict validation applies to the
7
+ * human-authored `.meta/changelog-overrides.json`, in `changelogOverrides.ts`.
8
8
  */
9
9
  export declare function isChangelogEntry(value: unknown): value is ChangelogEntry;
10
- /** Extract the version from a tag by stripping the prefix up to the first digit. */
10
+ /** Extracts the version from a tag: the text from its first `N.N.N` onward, or the whole tag when it contains none. */
11
11
  export declare function extractVersion(tag: string): string;
12
12
  /**
13
- * Read and parse a changelog JSON file, returning validated entries.
14
- *
15
- * Returns `undefined` if the file cannot be read or does not contain a valid array.
16
- * Silent on parse error — for the warn-and-treat-as-empty variant used by upsert callers,
17
- * see `readExistingEntries` in `./changelogJsonFile.ts`.
13
+ * Reads the changelog entries in a JSON file; undefined when the file is missing or unreadable, or doesn't contain an
14
+ * array.
18
15
  */
19
16
  export declare function readChangelogEntries(filePath: string): ChangelogEntry[] | undefined;