@williamthorsen/release-kit 14.0.0 → 14.2.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 (110) hide show
  1. package/.meta/changelog.json +173 -0
  2. package/.readyup/kits/default.js +64 -7
  3. package/.readyup/kits/npm-auto-publish.js +127 -75
  4. package/.readyup/manifest.json +22 -7
  5. package/CHANGELOG.md +47 -0
  6. package/README.md +18 -4
  7. package/dist/esm/assertReleasesOnNpm.d.ts +7 -0
  8. package/dist/esm/assertReleasesOnNpm.js +56 -0
  9. package/dist/esm/assertTaggedBaseline.d.ts +2 -2
  10. package/dist/esm/attachChangelogDiagnostics.d.ts +2 -2
  11. package/dist/esm/bin/release-kit.js +10 -6
  12. package/dist/esm/buildChangelogEntries.d.ts +3 -3
  13. package/dist/esm/buildChangelogEntries.js +3 -3
  14. package/dist/esm/buildDependencyGraph.d.ts +11 -11
  15. package/dist/esm/buildDependencyGraph.js +13 -13
  16. package/dist/esm/buildReleaseSummary.d.ts +3 -3
  17. package/dist/esm/buildReleaseSummary.js +4 -12
  18. package/dist/esm/changelogArchive.d.ts +14 -0
  19. package/dist/esm/changelogArchive.js +21 -0
  20. package/dist/esm/changelogJsonFile.d.ts +2 -2
  21. package/dist/esm/changelogOverrides.d.ts +9 -8
  22. package/dist/esm/changelogOverrides.js +3 -6
  23. package/dist/esm/createGithubReleaseCommand.d.ts +2 -1
  24. package/dist/esm/createGithubReleaseCommand.js +7 -4
  25. package/dist/esm/deriveDirectoryConfig.d.ts +13 -0
  26. package/dist/esm/deriveDirectoryConfig.js +59 -0
  27. package/dist/esm/deriveWorkspaceConfig.js +2 -3
  28. package/dist/esm/discoverWorkspaces.d.ts +3 -3
  29. package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
  30. package/dist/esm/enumerateReleaseWindows.js +1 -3
  31. package/dist/esm/findTransitiveDependents.js +3 -3
  32. package/dist/esm/findUnroutedEntryScopes.d.ts +2 -2
  33. package/dist/esm/formatDirectoryPublishSkip.d.ts +3 -0
  34. package/dist/esm/formatDirectoryPublishSkip.js +3 -0
  35. package/dist/esm/formatExcludedSkip.d.ts +5 -2
  36. package/dist/esm/formatExcludedSkip.js +2 -2
  37. package/dist/esm/formatProjectPublishSkip.d.ts +3 -0
  38. package/dist/esm/formatProjectPublishSkip.js +3 -0
  39. package/dist/esm/generateChangelogs.d.ts +7 -7
  40. package/dist/esm/generateChangelogs.js +2 -2
  41. package/dist/esm/help/prepareHelp.d.ts +1 -1
  42. package/dist/esm/help/prepareHelp.js +7 -5
  43. package/dist/esm/init/hasPublishablePackage.d.ts +10 -0
  44. package/dist/esm/init/hasPublishablePackage.js +28 -0
  45. package/dist/esm/init/initCommand.js +23 -13
  46. package/dist/esm/init/scaffold.d.ts +6 -1
  47. package/dist/esm/init/scaffold.js +10 -7
  48. package/dist/esm/init/templates.js +1 -1
  49. package/dist/esm/init/updateTemplatesCommand.d.ts +2 -1
  50. package/dist/esm/init/updateTemplatesCommand.js +12 -6
  51. package/dist/esm/isPublishableManifest.d.ts +2 -0
  52. package/dist/esm/isPublishableManifest.js +5 -0
  53. package/dist/esm/loadConfig.d.ts +12 -12
  54. package/dist/esm/loadConfig.js +79 -30
  55. package/dist/esm/matchesTagPrefix.d.ts +2 -0
  56. package/dist/esm/matchesTagPrefix.js +4 -0
  57. package/dist/esm/npmRegistry.d.ts +35 -0
  58. package/dist/esm/npmRegistry.js +58 -0
  59. package/dist/esm/prepareCommand.d.ts +1 -1
  60. package/dist/esm/prepareCommand.js +50 -36
  61. package/dist/esm/prepareTarget.d.ts +103 -0
  62. package/dist/esm/prepareTarget.js +274 -0
  63. package/dist/esm/previewTagPrefixes.d.ts +5 -3
  64. package/dist/esm/previewTagPrefixes.js +60 -14
  65. package/dist/esm/propagateBumps.js +1 -1
  66. package/dist/esm/publishCommand.js +13 -1
  67. package/dist/esm/readChangelogSections.d.ts +6 -4
  68. package/dist/esm/readChangelogSections.js +18 -4
  69. package/dist/esm/releasePlan.d.ts +4 -2
  70. package/dist/esm/releasePlan.js +34 -12
  71. package/dist/esm/releasePrepareTargets.d.ts +47 -0
  72. package/dist/esm/releasePrepareTargets.js +335 -0
  73. package/dist/esm/renderChangelogMarkdown.d.ts +23 -0
  74. package/dist/esm/renderChangelogMarkdown.js +47 -11
  75. package/dist/esm/reportPrepare.d.ts +2 -3
  76. package/dist/esm/reportPrepare.js +24 -12
  77. package/dist/esm/resolveCommandTags.d.ts +3 -1
  78. package/dist/esm/resolveCommandTags.js +15 -22
  79. package/dist/esm/resolveReleaseDeclaration.d.ts +45 -0
  80. package/dist/esm/resolveReleaseDeclaration.js +153 -0
  81. package/dist/esm/resolveReleaseTags.d.ts +14 -3
  82. package/dist/esm/resolveReleaseTags.js +36 -15
  83. package/dist/esm/resolveReleaseTargets.d.ts +29 -0
  84. package/dist/esm/resolveReleaseTargets.js +131 -0
  85. package/dist/esm/sync-labels/generateCommand.js +2 -1
  86. package/dist/esm/sync-labels/resolveLabels.d.ts +4 -3
  87. package/dist/esm/sync-labels/resolveLabels.js +30 -1
  88. package/dist/esm/sync-labels/templates.js +1 -1
  89. package/dist/esm/sync-labels/types.d.ts +2 -0
  90. package/dist/esm/types.d.ts +251 -167
  91. package/dist/esm/types.js +22 -0
  92. package/dist/esm/validateConfig.js +80 -21
  93. package/dist/esm/validateOverridesCommand.d.ts +1 -1
  94. package/dist/esm/validateOverridesCommand.js +17 -28
  95. package/docs/api.md +25 -58
  96. package/docs/changelogs.md +26 -6
  97. package/docs/configuration.md +80 -17
  98. package/docs/labels.md +30 -5
  99. package/docs/project-releases.md +42 -2
  100. package/docs/publishing.md +3 -3
  101. package/docs/releasing.md +12 -4
  102. package/docs/work-types.md +1 -1
  103. package/package.json +6 -6
  104. package/presets/labels/common.yaml +11 -0
  105. package/dist/esm/releasePrepare.d.ts +0 -36
  106. package/dist/esm/releasePrepare.js +0 -230
  107. package/dist/esm/releasePrepareMono.d.ts +0 -29
  108. package/dist/esm/releasePrepareMono.js +0 -503
  109. package/dist/esm/releasePrepareProject.d.ts +0 -47
  110. package/dist/esm/releasePrepareProject.js +0 -164
@@ -1,503 +0,0 @@
1
- import { join as joinPath } from 'node:path';
2
- import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
3
- import { assertTaggedBaseline, findUntaggedBaseline } from "./assertTaggedBaseline.js";
4
- import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
5
- import { readReleaseHistory, toReleaseEntries } from "./buildChangelogEntries.js";
6
- import { buildDependencyGraph } from "./buildDependencyGraph.js";
7
- import { buildReleaseSummary } from "./buildReleaseSummary.js";
8
- import { buildSyntheticChangelogEntry } from "./buildSyntheticChangelogEntry.js";
9
- import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
10
- import { applyWorkspaceOverrides, createOverrideContext, formatStaleOverrideKeyWarning, } from "./changelogOverrides.js";
11
- import { decideRelease } from "./decideRelease.js";
12
- import { deriveDependenciesHeader, deriveSectionOrder } from "./deriveReleaseNotesConfig.js";
13
- import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
14
- import { findTransitiveDependents } from "./findTransitiveDependents.js";
15
- import { findUnroutedEntryScopes } from "./findUnroutedEntryScopes.js";
16
- import { getAllTagPrefixes } from "./generateChangelogs.js";
17
- import { hasPrettierConfig } from "./hasPrettierConfig.js";
18
- import { resolveWorkTypes } from "./loadConfig.js";
19
- import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
20
- import { planVersionBump, planVersionSet } from "./planVersionBump.js";
21
- import { propagateBumps } from "./propagateBumps.js";
22
- import { planPreservedSections } from "./readChangelogSections.js";
23
- import { releasePrepareProject } from "./releasePrepareProject.js";
24
- import { renderChangelogMarkdown } from "./renderChangelogMarkdown.js";
25
- export function releasePrepareMono(fullConfig, options) {
26
- const { only, withReleaseNotes } = options;
27
- const writes = [];
28
- const warnings = [];
29
- const graph = buildDependencyGraph(fullConfig.workspaces);
30
- const startDirs = selectStartDirs(fullConfig.workspaces, options);
31
- const plannedDirs = findTransitiveDependents(graph, startDirs);
32
- const config = {
33
- ...fullConfig,
34
- workspaces: fullConfig.workspaces.filter((workspace) => plannedDirs.has(workspace.dir)),
35
- };
36
- if (withReleaseNotes === true && !config.changelogJson.enabled) {
37
- warnings.push('--with-release-notes requires changelogJson.enabled; skipping preview generation');
38
- }
39
- const workTypes = resolveWorkTypes(config.workTypes);
40
- const sectionOrder = deriveSectionOrder(workTypes);
41
- const dependenciesHeader = deriveDependenciesHeader(workTypes);
42
- const overrideContext = createOverrideContext(config.workspaces);
43
- const { directBumps, directResults, skippedResults, untaggedBaselines } = determineDirectBumps(config, options, startDirs);
44
- reportUnroutedEntryScopes([...directResults.values(), ...skippedResults], config, fullConfig.workspaces.map((workspace) => workspace.dir));
45
- const skippedHistories = new Map(skippedResults.map((skipped) => [skipped.workspace.dir, skipped.history]));
46
- const fullReleaseSet = propagateBumps(directBumps, graph);
47
- assertTaggedBaseline([...untaggedBaselines]
48
- .filter(([dir]) => directBumps.has(dir) || !fullReleaseSet.has(dir))
49
- .map(([, untagged]) => untagged));
50
- const { sorted: sortedDirs, cyclicDirs } = topologicalSort(fullReleaseSet, graph);
51
- if (cyclicDirs.length > 0) {
52
- warnings.push(`Circular workspace dependencies detected among: ${cyclicDirs.join(', ')}. ` +
53
- 'Propagation metadata may be incomplete for these workspaces.');
54
- }
55
- const workspaces = collectSkippedWorkspaces(skippedResults, fullReleaseSet);
56
- const previewOptions = {
57
- enabled: withReleaseNotes === true && config.changelogJson.enabled,
58
- sectionOrder,
59
- };
60
- const { tags, modifiedFiles } = executeReleaseSet({
61
- sortedDirs,
62
- fullReleaseSet,
63
- config,
64
- directResults,
65
- skippedHistories,
66
- writes,
67
- warnings,
68
- workspaces,
69
- previewOptions,
70
- overrideContext,
71
- sectionOrder,
72
- dependenciesHeader,
73
- });
74
- const configOrder = new Map(config.workspaces.map((w, i) => [w.dir, i]));
75
- workspaces.sort((a, b) => {
76
- const orderA = configOrder.get(a.name ?? '') ?? 0;
77
- const orderB = configOrder.get(b.name ?? '') ?? 0;
78
- return orderA - orderB;
79
- });
80
- let project;
81
- if (config.project !== undefined) {
82
- if (only === undefined) {
83
- project = tryStage('project release stage', () => releasePrepareProject({
84
- config,
85
- options,
86
- modifiedFiles,
87
- writes,
88
- tags,
89
- warnings,
90
- rootOverrides: overrideContext.project,
91
- overrideWarnings: overrideContext.overrideWarnings,
92
- globalMatchedRootKeys: overrideContext.globalMatchedRootKeys,
93
- }));
94
- }
95
- else {
96
- warnings.push(`Project release skipped: --only narrows this run to ${only.join(', ')}. ` +
97
- 'Run `release-kit prepare` without --only to include the project release.');
98
- }
99
- }
100
- const formatCommand = planFormatCommand(config, tags, modifiedFiles);
101
- for (const overrideKey of overrideContext.project.keys()) {
102
- if (!overrideContext.globalMatchedRootKeys.has(overrideKey)) {
103
- overrideContext.overrideWarnings.push(formatStaleOverrideKeyWarning(overrideKey));
104
- }
105
- }
106
- const allWarnings = [...warnings, ...overrideContext.overrideWarnings];
107
- return {
108
- workspaces,
109
- tags,
110
- writes,
111
- summary: buildReleaseSummary({ workspaces, ...(project !== undefined && { project }) }),
112
- formatCommand,
113
- ...(allWarnings.length > 0 && { warnings: allWarnings }),
114
- ...(project !== undefined && { project }),
115
- };
116
- }
117
- function selectStartDirs(workspaces, options) {
118
- const { only, setVersions } = options;
119
- if (only === undefined) {
120
- return new Set(workspaces.map((workspace) => workspace.dir));
121
- }
122
- return new Set([...only, ...(setVersions?.keys() ?? [])]);
123
- }
124
- function determineDirectBumps(config, options, startDirs) {
125
- const { force, bumpOverride, setVersions } = options;
126
- const directBumps = new Map();
127
- const directResults = new Map();
128
- const skippedResults = [];
129
- const untaggedBaselines = new Map();
130
- const hintState = { emitted: false };
131
- const knownPrefixes = config.workspaces.flatMap(getAllTagPrefixes);
132
- for (const workspace of config.workspaces) {
133
- const name = workspace.dir;
134
- const stageLabel = workspaceStageLabel(workspace.dir);
135
- const tagPrefixes = getAllTagPrefixes(workspace);
136
- const history = tryStage(stageLabel, () => readReleaseHistory(config, { tagPrefixes, paths: workspace.paths, workspaceDir: workspace.dir }));
137
- const untagged = tryStage(stageLabel, () => findUntaggedBaseline({
138
- label: `workspace '${workspace.dir}'`,
139
- packageFiles: workspace.packageFiles,
140
- changelogPaths: workspace.changelogPaths,
141
- tagPrefixes,
142
- previousTag: history.previousTag,
143
- }, config));
144
- if (untagged !== undefined) {
145
- untaggedBaselines.set(workspace.dir, untagged);
146
- }
147
- const tag = history.previousTag;
148
- const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
149
- if (tag === undefined) {
150
- maybeEmitBaselineHint(workspace, knownPrefixes, hintState);
151
- }
152
- const setVersion = setVersions?.get(workspace.dir);
153
- if (setVersion !== undefined) {
154
- directBumps.set(workspace.dir, { releaseType: 'patch', newVersionOverride: setVersion });
155
- directResults.set(workspace.dir, {
156
- workspace,
157
- history,
158
- releaseType: undefined,
159
- bumpOverride: undefined,
160
- setVersion,
161
- });
162
- continue;
163
- }
164
- const decision = decideRelease({
165
- naturalBump: history.unreleased.bump,
166
- commitCount: history.unreleased.commits.length,
167
- force: force === true && startDirs.has(workspace.dir),
168
- bumpOverride,
169
- skipReasons: {
170
- noCommits: `No commits for ${name} ${since}. Pass --force to release at patch. Skipping.`,
171
- noBumpWorthy: `No bump-worthy commits for ${name} ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
172
- },
173
- });
174
- if (decision.outcome === 'skip') {
175
- skippedResults.push({ workspace, history, skipReason: decision.skipReason });
176
- continue;
177
- }
178
- directBumps.set(workspace.dir, { releaseType: decision.releaseType });
179
- directResults.set(workspace.dir, {
180
- workspace,
181
- history,
182
- releaseType: decision.releaseType,
183
- bumpOverride,
184
- });
185
- }
186
- return { directBumps, directResults, skippedResults, untaggedBaselines };
187
- }
188
- function reportUnroutedEntryScopes(results, config, configuredWorkspaceDirs) {
189
- const findings = findUnroutedEntryScopes(results.map(({ workspace, history }) => ({ dir: workspace.dir, commits: history.unreleased.commits })), configuredWorkspaceDirs, config.scopeAliases ?? {});
190
- for (const { workspace, history } of results) {
191
- history.unreleased.diagnostics.unroutedEntryScopes = findings.get(workspace.dir) ?? [];
192
- }
193
- }
194
- function collectSkippedWorkspaces(skippedResults, fullReleaseSet) {
195
- const workspaces = [];
196
- for (const skipped of skippedResults) {
197
- if (fullReleaseSet.has(skipped.workspace.dir)) {
198
- continue;
199
- }
200
- const { previousTag, unreleased } = skipped.history;
201
- const result = {
202
- name: skipped.workspace.dir,
203
- status: 'skipped',
204
- commitCount: unreleased.commits.length,
205
- parsedCommitCount: unreleased.parsedCommitCount,
206
- skipReason: skipped.skipReason,
207
- };
208
- if (previousTag !== undefined) {
209
- result.previousTag = previousTag;
210
- }
211
- if (unreleased.unparseableCommits !== undefined) {
212
- result.unparseableCommits = unreleased.unparseableCommits;
213
- }
214
- attachChangelogDiagnostics(result, unreleased.diagnostics);
215
- workspaces.push(result);
216
- }
217
- return workspaces;
218
- }
219
- function executeReleaseSet(args) {
220
- const { sortedDirs, fullReleaseSet, config, directResults, skippedHistories, writes, warnings, workspaces, previewOptions, overrideContext, sectionOrder, dependenciesHeader, } = args;
221
- const tags = [];
222
- const modifiedFiles = [];
223
- const today = new Date().toISOString().slice(0, 10);
224
- for (const dir of sortedDirs) {
225
- const releaseEntry = fullReleaseSet.get(dir);
226
- if (releaseEntry === undefined) {
227
- continue;
228
- }
229
- const workspace = findWorkspace(config.workspaces, dir);
230
- if (workspace === undefined) {
231
- continue;
232
- }
233
- tryStage(workspaceStageLabel(dir), () => executeWorkspaceRelease({
234
- dir,
235
- workspace,
236
- releaseEntry,
237
- directResult: directResults.get(dir),
238
- skippedHistory: skippedHistories.get(dir),
239
- config,
240
- today,
241
- tags,
242
- modifiedFiles,
243
- writes,
244
- warnings,
245
- workspaces,
246
- previewOptions,
247
- overrideContext,
248
- sectionOrder,
249
- dependenciesHeader,
250
- }));
251
- }
252
- return { tags, modifiedFiles };
253
- }
254
- function executeWorkspaceRelease(args) {
255
- const { dir, workspace, releaseEntry, directResult, skippedHistory, config, today, tags, modifiedFiles, writes, warnings, workspaces, previewOptions, overrideContext, sectionOrder, dependenciesHeader, } = args;
256
- const setVersionTarget = directResult?.setVersion;
257
- const bump = setVersionTarget === undefined
258
- ? planVersionBump(workspace.packageFiles, releaseEntry.releaseType)
259
- : planVersionSet(workspace.packageFiles, setVersionTarget);
260
- const newTag = `${workspace.tagPrefix}${bump.newVersion}`;
261
- tags.push(newTag);
262
- writes.push(...bump.writes);
263
- modifiedFiles.push(...workspace.packageFiles, ...workspace.changelogPaths.map((changelogPath) => joinPath(changelogPath, 'CHANGELOG.md')));
264
- const phase1History = directResult?.history ?? skippedHistory;
265
- const { changelogFiles, changelogPreservation, previewFiles } = generateWorkspaceChangelogs({
266
- workspace,
267
- releaseEntry,
268
- newTag,
269
- newVersion: bump.newVersion,
270
- history: directResult?.history,
271
- config,
272
- today,
273
- modifiedFiles,
274
- writes,
275
- warnings,
276
- previewOptions,
277
- overrideContext,
278
- sectionOrder,
279
- dependenciesHeader,
280
- });
281
- const released = {
282
- name: dir,
283
- status: 'released',
284
- commitCount: phase1History?.unreleased.commits.length ?? 0,
285
- currentVersion: bump.currentVersion,
286
- newVersion: bump.newVersion,
287
- tag: newTag,
288
- bumpedFiles: bump.writes.map((write) => write.path),
289
- changelogFiles,
290
- ...(previewFiles.length > 0 && { previewFiles }),
291
- ...(changelogPreservation.length > 0 && { changelogPreservation }),
292
- };
293
- attachReleasedWorkspaceOptionals(released, {
294
- previousTag: phase1History?.previousTag,
295
- directResult,
296
- skippedHistory,
297
- releaseEntry,
298
- setVersionTarget,
299
- });
300
- workspaces.push(released);
301
- }
302
- function attachReleasedWorkspaceOptionals(released, args) {
303
- const { previousTag, directResult, skippedHistory, releaseEntry, setVersionTarget } = args;
304
- if (previousTag !== undefined) {
305
- released.previousTag = previousTag;
306
- }
307
- if (setVersionTarget === undefined) {
308
- released.releaseType = releaseEntry.releaseType;
309
- }
310
- if (directResult !== undefined) {
311
- attachDirectHistory(released, directResult.history.unreleased, setVersionTarget === undefined);
312
- }
313
- else {
314
- attachPropagatedHistory(released, skippedHistory?.unreleased);
315
- }
316
- if (releaseEntry.propagatedFrom !== undefined) {
317
- released.propagatedFrom = releaseEntry.propagatedFrom;
318
- }
319
- if (directResult?.bumpOverride !== undefined) {
320
- released.bumpOverride = directResult.bumpOverride;
321
- }
322
- if (setVersionTarget !== undefined) {
323
- released.setVersion = setVersionTarget;
324
- }
325
- }
326
- function attachDirectHistory(released, unreleased, hasDecidedBump) {
327
- released.commits = unreleased.commits;
328
- if (hasDecidedBump) {
329
- released.parsedCommitCount = unreleased.parsedCommitCount;
330
- if (unreleased.unparseableCommits !== undefined) {
331
- released.unparseableCommits = unreleased.unparseableCommits;
332
- }
333
- }
334
- attachChangelogDiagnostics(released, unreleased.diagnostics);
335
- }
336
- function attachPropagatedHistory(released, unreleased) {
337
- released.propagatedOnly = true;
338
- if (unreleased === undefined) {
339
- return;
340
- }
341
- released.commits = unreleased.commits;
342
- if (unreleased.unparseableCommits !== undefined) {
343
- released.unparseableCommits = unreleased.unparseableCommits;
344
- }
345
- attachChangelogDiagnostics(released, unreleased.diagnostics);
346
- }
347
- function generateWorkspaceChangelogs(args) {
348
- const { workspace, releaseEntry, newTag, newVersion, history, config, today, modifiedFiles, writes, warnings, previewOptions, overrideContext, sectionOrder, dependenciesHeader, } = args;
349
- const entries = buildWorkspaceEntries({
350
- workspace,
351
- releaseEntry,
352
- newTag,
353
- newVersion,
354
- history,
355
- today,
356
- dependenciesHeader,
357
- });
358
- const applied = applyWorkspaceOverrides(entries, workspace.workspacePath, overrideContext);
359
- const changelogFiles = [];
360
- const changelogPreservation = [];
361
- let firstMergedEntries;
362
- for (const changelogPath of workspace.changelogPaths) {
363
- const jsonPath = resolveChangelogJsonPath(config, changelogPath);
364
- const mergedEntries = mergeChangelogEntriesWithDisk(jsonPath, applied.entries);
365
- if (config.changelogJson.enabled) {
366
- writes.push({ path: jsonPath, content: renderChangelogJson(mergedEntries) });
367
- modifiedFiles.push(jsonPath);
368
- firstMergedEntries ??= mergedEntries;
369
- }
370
- const changelogFile = joinPath(changelogPath, 'CHANGELOG.md');
371
- const preserved = planPreservedSections(changelogFile, mergedEntries);
372
- writes.push({
373
- path: changelogFile,
374
- content: renderChangelogMarkdown(mergedEntries, { sectionOrder, preservedSections: preserved.sections }),
375
- });
376
- changelogFiles.push(changelogFile);
377
- if (preserved.preservation !== undefined) {
378
- changelogPreservation.push(preserved.preservation);
379
- }
380
- }
381
- const previews = planPreviews(workspace, newTag, firstMergedEntries, previewOptions, warnings);
382
- writes.push(...previews);
383
- return { changelogFiles, changelogPreservation, previewFiles: previews.map((write) => write.path) };
384
- }
385
- function buildWorkspaceEntries(args) {
386
- const { workspace, releaseEntry, newTag, newVersion, history, today, dependenciesHeader } = args;
387
- if (history === undefined) {
388
- if (releaseEntry.propagatedFrom === undefined) {
389
- throw new Error(`Workspace '${workspace.dir}' has neither a direct release nor a propagation source`);
390
- }
391
- return [buildSyntheticChangelogEntry(releaseEntry.propagatedFrom, newVersion, today, dependenciesHeader)];
392
- }
393
- return toReleaseEntries(history, newTag, today);
394
- }
395
- function planPreviews(workspace, newTag, entries, previewOptions, warnings) {
396
- if (!previewOptions.enabled || entries === undefined) {
397
- return [];
398
- }
399
- const previews = planReleaseNotesPreviews({
400
- workspacePath: workspace.workspacePath,
401
- tag: newTag,
402
- entries,
403
- sectionOrder: previewOptions.sectionOrder,
404
- });
405
- warnings.push(...previews.warnings);
406
- return previews.writes;
407
- }
408
- function planFormatCommand(config, tags, modifiedFiles) {
409
- const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? 'npx prettier --write' : undefined);
410
- if (tags.length === 0 || formatCommandStr === undefined) {
411
- return undefined;
412
- }
413
- return { command: `${formatCommandStr} ${modifiedFiles.join(' ')}`, files: modifiedFiles };
414
- }
415
- function findWorkspace(workspaces, dir) {
416
- return workspaces.find((w) => w.dir === dir);
417
- }
418
- function tryStage(stageLabel, fn) {
419
- try {
420
- return fn();
421
- }
422
- catch (error) {
423
- throw chainError(stageLabel, error);
424
- }
425
- }
426
- function workspaceStageLabel(dir) {
427
- return `workspace '${dir}' release stage`;
428
- }
429
- function maybeEmitBaselineHint(workspace, knownPrefixes, state) {
430
- if (state.emitted)
431
- return;
432
- if ((workspace.legacyIdentities?.length ?? 0) > 0)
433
- return;
434
- const candidates = detectUndeclaredTagPrefixes(knownPrefixes);
435
- if (candidates.length === 0)
436
- return;
437
- const totalTags = candidates.reduce((sum, candidate) => sum + candidate.tagCount, 0);
438
- const example = candidates[0]?.exampleTags[0] ?? `${candidates[0]?.prefix ?? ''}?`;
439
- process.stderr.write(`Hint: no baseline tag found for ${workspace.dir} under '${workspace.tagPrefix}', but ` +
440
- `${totalTags} candidate-shaped tags exist (e.g., ${example}). ` +
441
- "Run 'release-kit show-tag-prefixes' to check for undeclared legacy prefixes.\n");
442
- state.emitted = true;
443
- }
444
- function topologicalSort(releaseSet, graph) {
445
- const releaseDirs = new Set(releaseSet.keys());
446
- if (releaseDirs.size === 0) {
447
- return { sorted: [], cyclicDirs: [] };
448
- }
449
- const inDegree = new Map();
450
- const forwardEdges = new Map();
451
- for (const dir of releaseDirs) {
452
- inDegree.set(dir, 0);
453
- forwardEdges.set(dir, []);
454
- }
455
- for (const [packageName, dependents] of graph.dependentsOf) {
456
- const depDir = graph.packageNameToDir.get(packageName);
457
- if (depDir === undefined || !releaseDirs.has(depDir)) {
458
- continue;
459
- }
460
- for (const dependent of dependents) {
461
- if (!releaseDirs.has(dependent.dir)) {
462
- continue;
463
- }
464
- const edges = forwardEdges.get(depDir);
465
- if (edges !== undefined) {
466
- edges.push(dependent.dir);
467
- }
468
- inDegree.set(dependent.dir, (inDegree.get(dependent.dir) ?? 0) + 1);
469
- }
470
- }
471
- const queue = [];
472
- for (const [dir, degree] of inDegree) {
473
- if (degree === 0) {
474
- queue.push(dir);
475
- }
476
- }
477
- const sorted = [];
478
- while (queue.length > 0) {
479
- const dir = queue.shift();
480
- if (dir === undefined) {
481
- break;
482
- }
483
- sorted.push(dir);
484
- const dependents = forwardEdges.get(dir) ?? [];
485
- for (const dependent of dependents) {
486
- const newDegree = (inDegree.get(dependent) ?? 1) - 1;
487
- inDegree.set(dependent, newDegree);
488
- if (newDegree === 0) {
489
- queue.push(dependent);
490
- }
491
- }
492
- }
493
- const sortedSet = new Set(sorted);
494
- const cyclicDirs = [];
495
- for (const dir of releaseDirs) {
496
- if (sortedSet.has(dir)) {
497
- continue;
498
- }
499
- sorted.push(dir);
500
- cyclicDirs.push(dir);
501
- }
502
- return { sorted, cyclicDirs };
503
- }
@@ -1,47 +0,0 @@
1
- import type { PlannedWrite } from './releasePlan.js';
2
- import type { ReleasePrepareOptions } from './releasePrepare.js';
3
- import type { ChangelogOverride, MonorepoPrepareConfig, ProjectPrepareResult } from './types.js';
4
- /** Inputs to the project-release stage. */
5
- export interface ReleasePrepareProjectArgs {
6
- /** Resolved monorepo config; `config.project` must be defined when this function is called. */
7
- config: MonorepoPrepareConfig;
8
- options: ReleasePrepareOptions;
9
- /** Mutated in-place to append project-level files (root package.json, root CHANGELOG.md, root changelog.json). */
10
- modifiedFiles: string[];
11
- /** Mutated in-place to append every file that this stage intends to write. */
12
- writes: PlannedWrite[];
13
- /** Mutated in-place to append the project tag. */
14
- tags: string[];
15
- /**
16
- * Mutated in-place to receive the warnings that this stage raises and that are not override-specific.
17
- * Defaults to a discardable sink.
18
- */
19
- warnings?: string[];
20
- /**
21
- * Root-tier editorial overrides. Defaults to an empty map. The project changelog applies only the
22
- * root-tier file: Per-workspace files describe per-workspace editorial intent and are
23
- * meaningless at the aggregated project tier.
24
- */
25
- rootOverrides?: Map<string, ChangelogOverride>;
26
- /**
27
- * Mutated in-place to receive override-application warnings. Stale-key warnings are not among them: Those
28
- * depend on the matches of every batch, which `globalMatchedRootKeys` collects. Defaults to a discardable sink.
29
- */
30
- overrideWarnings?: string[];
31
- /**
32
- * Mutated in-place to receive every root-tier override key that this stage matches, so that stale-key
33
- * warnings can cover the whole run. Defaults to a discardable sink.
34
- */
35
- globalMatchedRootKeys?: Set<string>;
36
- }
37
- /**
38
- * Runs the project-level release stage: reads the history, decides the bump, and plans the root `package.json`
39
- * bump, the root `CHANGELOG.md`, and optionally `changelog.json` and release-notes previews. Contributing paths
40
- * come from the resolved `project.paths`, which defaults to the union of every (already-filtered) workspace's
41
- * `paths`.
42
- *
43
- * Returns a `skipped` result when neither commits nor `--force` provide a release signal, and throws when
44
- * `config.project` is undefined. A run narrowed by `--only` does not reach this stage, so it doesn't
45
- * read any narrowing flag.
46
- */
47
- export declare function releasePrepareProject(args: ReleasePrepareProjectArgs): ProjectPrepareResult;