@williamthorsen/release-kit 6.0.0 → 8.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 (134) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +5 -3
  3. package/dist/esm/assertCleanWorkingTree.js +9 -14
  4. package/dist/esm/bin/release-kit.js +200 -199
  5. package/dist/esm/buildChangelogEntries.d.ts +2 -2
  6. package/dist/esm/buildChangelogEntries.js +142 -149
  7. package/dist/esm/buildDependencyGraph.d.ts +1 -1
  8. package/dist/esm/buildDependencyGraph.js +60 -59
  9. package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -1
  10. package/dist/esm/buildEmptyReleaseEntry.js +12 -15
  11. package/dist/esm/buildReleaseSummary.d.ts +1 -1
  12. package/dist/esm/buildReleaseSummary.js +23 -26
  13. package/dist/esm/buildSyntheticChangelogEntry.d.ts +1 -1
  14. package/dist/esm/buildSyntheticChangelogEntry.js +9 -12
  15. package/dist/esm/bumpAllVersions.d.ts +1 -1
  16. package/dist/esm/bumpAllVersions.js +51 -52
  17. package/dist/esm/bumpVersion.d.ts +1 -1
  18. package/dist/esm/bumpVersion.js +25 -28
  19. package/dist/esm/changelogJsonFile.d.ts +1 -1
  20. package/dist/esm/changelogJsonFile.js +59 -62
  21. package/dist/esm/changelogJsonUtils.d.ts +1 -1
  22. package/dist/esm/changelogJsonUtils.js +35 -32
  23. package/dist/esm/changelogOverrides.d.ts +1 -1
  24. package/dist/esm/changelogOverrides.js +374 -377
  25. package/dist/esm/checkWorkTypesDrift.js +104 -102
  26. package/dist/esm/collectPolicyViolations.d.ts +2 -2
  27. package/dist/esm/collectPolicyViolations.js +11 -14
  28. package/dist/esm/commitCommand.js +48 -46
  29. package/dist/esm/compareVersions.js +20 -23
  30. package/dist/esm/createGithubRelease.js +56 -59
  31. package/dist/esm/createGithubReleaseCommand.js +21 -23
  32. package/dist/esm/createTags.js +57 -56
  33. package/dist/esm/decideRelease.d.ts +2 -2
  34. package/dist/esm/decideRelease.js +27 -41
  35. package/dist/esm/defaults.d.ts +3 -3
  36. package/dist/esm/defaults.js +36 -48
  37. package/dist/esm/deleteFileIfExists.js +10 -12
  38. package/dist/esm/deriveWorkspaceConfig.d.ts +1 -1
  39. package/dist/esm/deriveWorkspaceConfig.js +34 -36
  40. package/dist/esm/detectPackageManager.js +35 -37
  41. package/dist/esm/detectUndeclaredTagPrefixes.js +41 -40
  42. package/dist/esm/determineBumpFromCommits.d.ts +2 -2
  43. package/dist/esm/determineBumpFromCommits.js +25 -29
  44. package/dist/esm/determineBumpType.d.ts +1 -1
  45. package/dist/esm/determineBumpType.js +31 -32
  46. package/dist/esm/discoverWorkspaces.js +38 -40
  47. package/dist/esm/format.js +6 -11
  48. package/dist/esm/generateChangelogs.d.ts +1 -1
  49. package/dist/esm/generateChangelogs.js +13 -17
  50. package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
  51. package/dist/esm/getCommitsSinceTarget.js +54 -55
  52. package/dist/esm/hasPrettierConfig.js +33 -35
  53. package/dist/esm/help/prepareHelp.js +4 -8
  54. package/dist/esm/index.d.ts +4 -4
  55. package/dist/esm/index.js +2 -8
  56. package/dist/esm/init/checks.js +28 -32
  57. package/dist/esm/init/detectRepoType.js +12 -15
  58. package/dist/esm/init/initCommand.js +60 -54
  59. package/dist/esm/init/parseJsonRecord.js +9 -11
  60. package/dist/esm/init/scaffold.d.ts +1 -1
  61. package/dist/esm/init/scaffold.js +32 -38
  62. package/dist/esm/init/templates.d.ts +1 -1
  63. package/dist/esm/init/templates.js +16 -22
  64. package/dist/esm/injectReleaseNotesIntoReadme.js +42 -49
  65. package/dist/esm/injectSection.js +17 -27
  66. package/dist/esm/loadConfig.d.ts +1 -1
  67. package/dist/esm/loadConfig.js +206 -225
  68. package/dist/esm/parseCommitMessage.d.ts +1 -1
  69. package/dist/esm/parseCommitMessage.js +72 -76
  70. package/dist/esm/parseRequestedTags.js +6 -9
  71. package/dist/esm/prepareCommand.d.ts +1 -1
  72. package/dist/esm/prepareCommand.js +225 -235
  73. package/dist/esm/previewTagPrefixes.d.ts +1 -1
  74. package/dist/esm/previewTagPrefixes.js +101 -96
  75. package/dist/esm/propagateBumps.d.ts +2 -2
  76. package/dist/esm/propagateBumps.js +48 -50
  77. package/dist/esm/publish.d.ts +2 -2
  78. package/dist/esm/publish.js +22 -28
  79. package/dist/esm/publishCommand.js +75 -80
  80. package/dist/esm/pushCommand.js +33 -34
  81. package/dist/esm/pushRelease.d.ts +1 -1
  82. package/dist/esm/pushRelease.js +21 -24
  83. package/dist/esm/readCurrentVersion.js +13 -17
  84. package/dist/esm/releasePrepare.d.ts +1 -1
  85. package/dist/esm/releasePrepare.js +213 -231
  86. package/dist/esm/releasePrepareMono.d.ts +2 -2
  87. package/dist/esm/releasePrepareMono.js +434 -494
  88. package/dist/esm/releasePrepareProject.d.ts +2 -2
  89. package/dist/esm/releasePrepareProject.js +143 -153
  90. package/dist/esm/renderChangelogMarkdown.d.ts +1 -1
  91. package/dist/esm/renderChangelogMarkdown.js +20 -24
  92. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  93. package/dist/esm/renderReleaseNotes.js +50 -55
  94. package/dist/esm/reportPrepare.d.ts +1 -1
  95. package/dist/esm/reportPrepare.js +230 -231
  96. package/dist/esm/resolveCliffConfigPath.js +18 -23
  97. package/dist/esm/resolveCommandTags.d.ts +1 -1
  98. package/dist/esm/resolveCommandTags.js +47 -45
  99. package/dist/esm/resolveReleaseNotesConfig.d.ts +1 -1
  100. package/dist/esm/resolveReleaseNotesConfig.js +39 -43
  101. package/dist/esm/resolveReleaseTags.d.ts +1 -1
  102. package/dist/esm/resolveReleaseTags.js +39 -40
  103. package/dist/esm/runGitCliff.js +27 -30
  104. package/dist/esm/showTagPrefixesCommand.js +48 -70
  105. package/dist/esm/stripEmojiPrefix.js +2 -5
  106. package/dist/esm/stripScope.js +18 -21
  107. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  108. package/dist/esm/sync-labels/generateCommand.js +51 -53
  109. package/dist/esm/sync-labels/initCommand.js +46 -48
  110. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +1 -1
  111. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +46 -52
  112. package/dist/esm/sync-labels/presets.d.ts +1 -1
  113. package/dist/esm/sync-labels/presets.js +40 -43
  114. package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
  115. package/dist/esm/sync-labels/resolveLabels.js +28 -35
  116. package/dist/esm/sync-labels/syncCommand.js +30 -31
  117. package/dist/esm/sync-labels/templates.d.ts +1 -1
  118. package/dist/esm/sync-labels/templates.js +23 -26
  119. package/dist/esm/sync-labels/types.js +1 -0
  120. package/dist/esm/syncWorkTypes.js +83 -84
  121. package/dist/esm/tagCommand.js +12 -14
  122. package/dist/esm/typeGuards.js +4 -8
  123. package/dist/esm/types.js +66 -60
  124. package/dist/esm/validateConfig.d.ts +1 -1
  125. package/dist/esm/validateConfig.js +109 -115
  126. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +2 -2
  127. package/dist/esm/validateOnlyExcludesStrandedDependents.js +97 -91
  128. package/dist/esm/validateOverridesCommand.d.ts +2 -2
  129. package/dist/esm/validateOverridesCommand.js +110 -111
  130. package/dist/esm/workTypesData.js +57 -60
  131. package/dist/esm/workTypesUtils.js +16 -21
  132. package/dist/esm/writeReleaseNotesPreviews.js +55 -57
  133. package/package.json +3 -2
  134. package/dist/esm/.cache +0 -1
@@ -1,19 +1,11 @@
1
- import { execSync } from "node:child_process";
1
+ import { execSync } from 'node:child_process';
2
2
  import { buildChangelogEntries } from "./buildChangelogEntries.js";
3
3
  import { buildDependencyGraph } from "./buildDependencyGraph.js";
4
4
  import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
5
5
  import { buildSyntheticChangelogEntry } from "./buildSyntheticChangelogEntry.js";
6
6
  import { bumpAllVersions, setAllVersions } from "./bumpAllVersions.js";
7
- import {
8
- mergeChangelogEntriesWithDisk,
9
- resolveChangelogJsonPath,
10
- upsertChangelogJsonAndReturn
11
- } from "./changelogJsonFile.js";
12
- import {
13
- applyWorkspaceOverrides,
14
- createOverrideContext,
15
- formatStaleOverrideKeyWarning
16
- } from "./changelogOverrides.js";
7
+ import { mergeChangelogEntriesWithDisk, resolveChangelogJsonPath, upsertChangelogJsonAndReturn, } from "./changelogJsonFile.js";
8
+ import { applyWorkspaceOverrides, createOverrideContext, formatStaleOverrideKeyWarning, } from "./changelogOverrides.js";
17
9
  import { createPolicyViolationCollector } from "./collectPolicyViolations.js";
18
10
  import { isForwardVersion } from "./compareVersions.js";
19
11
  import { decideRelease } from "./decideRelease.js";
@@ -30,527 +22,475 @@ import { writeChangelogMarkdown } from "./renderChangelogMarkdown.js";
30
22
  import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
31
23
  import { refreshGitCliffCache } from "./runGitCliff.js";
32
24
  import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
33
- function releasePrepareMono(config, options) {
34
- const { dryRun, withReleaseNotes } = options;
35
- if (withReleaseNotes === true && !config.changelogJson.enabled) {
36
- console.warn("Warning: --with-release-notes requires changelogJson.enabled; skipping preview generation");
37
- }
38
- const sectionOrder = deriveSectionOrder(resolveWorkTypes(config.workTypes));
39
- const overrideContext = createOverrideContext(config.workspaces);
40
- const { directBumps, directResults, skippedResults, currentVersions } = determineDirectBumps(config, options);
41
- const previousTags = /* @__PURE__ */ new Map();
42
- for (const result of directResults.values()) {
43
- previousTags.set(result.workspace.dir, result.tag);
44
- }
45
- for (const skipped of skippedResults) {
46
- previousTags.set(skipped.workspace.dir, skipped.tag);
47
- }
48
- const graph = buildDependencyGraph(config.workspaces);
49
- const fullReleaseSet = propagateBumps(directBumps, graph, currentVersions);
50
- const { sorted: sortedDirs, cyclicDirs } = topologicalSort(fullReleaseSet, graph);
51
- const warnings = [];
52
- if (cyclicDirs.length > 0) {
53
- warnings.push(
54
- `Circular workspace dependencies detected among: ${cyclicDirs.join(", ")}. Propagation metadata may be incomplete for these workspaces.`
55
- );
56
- }
57
- const workspaces = collectSkippedWorkspaces(skippedResults, fullReleaseSet);
58
- const previewOptions = {
59
- enabled: withReleaseNotes === true && config.changelogJson.enabled,
60
- sectionOrder
61
- };
62
- if (fullReleaseSet.size > 0) {
63
- refreshGitCliffCache();
64
- }
65
- const { tags, modifiedFiles } = executeReleaseSet({
66
- sortedDirs,
67
- fullReleaseSet,
68
- config,
69
- directResults,
70
- previousTags,
71
- dryRun,
72
- workspaces,
73
- previewOptions,
74
- overrideContext,
75
- sectionOrder
76
- });
77
- const configOrder = new Map(config.workspaces.map((w, i) => [w.dir, i]));
78
- workspaces.sort((a, b) => {
79
- const orderA = configOrder.get(a.name ?? "") ?? 0;
80
- const orderB = configOrder.get(b.name ?? "") ?? 0;
81
- return orderA - orderB;
82
- });
83
- let project;
84
- if (config.project !== void 0) {
85
- project = tryStage(
86
- "project release stage",
87
- () => releasePrepareProject({
25
+ export function releasePrepareMono(config, options) {
26
+ const { dryRun, withReleaseNotes } = options;
27
+ if (withReleaseNotes === true && !config.changelogJson.enabled) {
28
+ console.warn('Warning: --with-release-notes requires changelogJson.enabled; skipping preview generation');
29
+ }
30
+ const sectionOrder = deriveSectionOrder(resolveWorkTypes(config.workTypes));
31
+ const overrideContext = createOverrideContext(config.workspaces);
32
+ const { directBumps, directResults, skippedResults, currentVersions } = determineDirectBumps(config, options);
33
+ const previousTags = new Map();
34
+ for (const result of directResults.values()) {
35
+ previousTags.set(result.workspace.dir, result.tag);
36
+ }
37
+ for (const skipped of skippedResults) {
38
+ previousTags.set(skipped.workspace.dir, skipped.tag);
39
+ }
40
+ const graph = buildDependencyGraph(config.workspaces);
41
+ const fullReleaseSet = propagateBumps(directBumps, graph, currentVersions);
42
+ const { sorted: sortedDirs, cyclicDirs } = topologicalSort(fullReleaseSet, graph);
43
+ const warnings = [];
44
+ if (cyclicDirs.length > 0) {
45
+ warnings.push(`Circular workspace dependencies detected among: ${cyclicDirs.join(', ')}. ` +
46
+ 'Propagation metadata may be incomplete for these workspaces.');
47
+ }
48
+ const workspaces = collectSkippedWorkspaces(skippedResults, fullReleaseSet);
49
+ const previewOptions = {
50
+ enabled: withReleaseNotes === true && config.changelogJson.enabled,
51
+ sectionOrder,
52
+ };
53
+ if (fullReleaseSet.size > 0) {
54
+ refreshGitCliffCache();
55
+ }
56
+ const { tags, modifiedFiles } = executeReleaseSet({
57
+ sortedDirs,
58
+ fullReleaseSet,
88
59
  config,
89
- options,
90
- modifiedFiles,
60
+ directResults,
61
+ previousTags,
62
+ dryRun,
63
+ workspaces,
64
+ previewOptions,
65
+ overrideContext,
66
+ sectionOrder,
67
+ });
68
+ const configOrder = new Map(config.workspaces.map((w, i) => [w.dir, i]));
69
+ workspaces.sort((a, b) => {
70
+ const orderA = configOrder.get(a.name ?? '') ?? 0;
71
+ const orderB = configOrder.get(b.name ?? '') ?? 0;
72
+ return orderA - orderB;
73
+ });
74
+ let project;
75
+ if (config.project !== undefined) {
76
+ project = tryStage('project release stage', () => releasePrepareProject({
77
+ config,
78
+ options,
79
+ modifiedFiles,
80
+ tags,
81
+ rootOverrides: overrideContext.project,
82
+ overrideWarnings: overrideContext.overrideWarnings,
83
+ globalMatchedRootKeys: overrideContext.globalMatchedRootKeys,
84
+ }));
85
+ }
86
+ const formatCommand = runFormatCommand(config, tags, modifiedFiles, dryRun);
87
+ for (const overrideKey of overrideContext.project.keys()) {
88
+ if (!overrideContext.globalMatchedRootKeys.has(overrideKey)) {
89
+ overrideContext.overrideWarnings.push(formatStaleOverrideKeyWarning(overrideKey));
90
+ }
91
+ }
92
+ const allWarnings = [...warnings, ...overrideContext.overrideWarnings];
93
+ return {
94
+ workspaces,
91
95
  tags,
92
- rootOverrides: overrideContext.project,
93
- overrideWarnings: overrideContext.overrideWarnings,
94
- globalMatchedRootKeys: overrideContext.globalMatchedRootKeys
95
- })
96
- );
97
- }
98
- const formatCommand = runFormatCommand(config, tags, modifiedFiles, dryRun);
99
- for (const overrideKey of overrideContext.project.keys()) {
100
- if (!overrideContext.globalMatchedRootKeys.has(overrideKey)) {
101
- overrideContext.overrideWarnings.push(formatStaleOverrideKeyWarning(overrideKey));
102
- }
103
- }
104
- const allWarnings = [...warnings, ...overrideContext.overrideWarnings];
105
- return {
106
- workspaces,
107
- tags,
108
- formatCommand,
109
- dryRun,
110
- ...allWarnings.length > 0 ? { warnings: allWarnings } : {},
111
- ...project === void 0 ? {} : { project }
112
- };
96
+ formatCommand,
97
+ dryRun,
98
+ ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}),
99
+ ...(project === undefined ? {} : { project }),
100
+ };
113
101
  }
114
102
  function determineDirectBumps(config, options) {
115
- const { force, bumpOverride, setVersion } = options;
116
- if (setVersion !== void 0 && config.workspaces.length !== 1) {
117
- throw new Error(`--set-version requires exactly one workspace; received ${config.workspaces.length}`);
118
- }
119
- const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
120
- const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
121
- const breakingPolicies = config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES;
122
- const directBumps = /* @__PURE__ */ new Map();
123
- const directResults = /* @__PURE__ */ new Map();
124
- const skippedResults = [];
125
- const currentVersions = /* @__PURE__ */ new Map();
126
- const hintState = { emitted: false };
127
- const knownPrefixes = config.workspaces.flatMap(getAllTagPrefixes);
128
- for (const workspace of config.workspaces) {
129
- const name = workspace.dir;
130
- const stageLabel = workspaceStageLabel(workspace.dir);
131
- const { tag, commits } = tryStage(
132
- stageLabel,
133
- () => getCommitsSinceTarget(getAllTagPrefixes(workspace), workspace.paths)
134
- );
135
- const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
136
- if (tag === void 0) {
137
- maybeEmitBaselineHint(workspace, knownPrefixes, hintState);
138
- }
139
- const primaryPackageFile = workspace.packageFiles[0];
140
- if (primaryPackageFile !== void 0) {
141
- const currentVersion = tryStage(stageLabel, () => readCurrentVersion(primaryPackageFile));
142
- if (currentVersion !== void 0) {
143
- currentVersions.set(workspace.dir, currentVersion);
144
- }
145
- }
146
- if (setVersion !== void 0) {
147
- const currentVersion = currentVersions.get(workspace.dir);
148
- if (currentVersion === void 0) {
149
- throw new Error(
150
- `Cannot validate --set-version: failed to read current version from ${primaryPackageFile ?? "(no package file)"}`
151
- );
152
- }
153
- if (!isForwardVersion(currentVersion, setVersion)) {
154
- throw new Error(`--set-version ${setVersion} is not greater than current version ${currentVersion}`);
155
- }
156
- directBumps.set(workspace.dir, { releaseType: "patch", newVersionOverride: setVersion });
157
- directResults.set(workspace.dir, {
158
- workspace,
159
- tag,
160
- commits,
161
- releaseType: void 0,
162
- parsedCommitCount: void 0,
163
- unparseableCommits: void 0,
164
- policyViolations: void 0,
165
- bumpOverride: void 0,
166
- setVersion
167
- });
168
- continue;
169
- }
170
- const collector = createPolicyViolationCollector();
171
- const decision = tryStage(
172
- stageLabel,
173
- () => decideRelease({
174
- commits,
175
- force,
176
- bumpOverride,
177
- workTypes,
178
- versionPatterns,
179
- scopeAliases: config.scopeAliases,
180
- breakingPolicies,
181
- onPolicyViolation: collector.onPolicyViolation,
182
- skipReasons: {
183
- noCommits: `No commits for ${name} ${since}. Pass --force to release at patch. Skipping.`,
184
- noBumpWorthy: `No bump-worthy commits for ${name} ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`
103
+ const { force, bumpOverride, setVersion } = options;
104
+ if (setVersion !== undefined && config.workspaces.length !== 1) {
105
+ throw new Error(`--set-version requires exactly one workspace; received ${config.workspaces.length}`);
106
+ }
107
+ const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
108
+ const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
109
+ const breakingPolicies = config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES;
110
+ const directBumps = new Map();
111
+ const directResults = new Map();
112
+ const skippedResults = [];
113
+ const currentVersions = new Map();
114
+ const hintState = { emitted: false };
115
+ const knownPrefixes = config.workspaces.flatMap(getAllTagPrefixes);
116
+ for (const workspace of config.workspaces) {
117
+ const name = workspace.dir;
118
+ const stageLabel = workspaceStageLabel(workspace.dir);
119
+ const { tag, commits } = tryStage(stageLabel, () => getCommitsSinceTarget(getAllTagPrefixes(workspace), workspace.paths));
120
+ const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
121
+ if (tag === undefined) {
122
+ maybeEmitBaselineHint(workspace, knownPrefixes, hintState);
185
123
  }
186
- })
187
- );
188
- const policyViolations = collector.violations.length > 0 ? collector.violations : void 0;
189
- if (decision.outcome === "skip") {
190
- skippedResults.push({
191
- workspace,
192
- tag,
193
- commitCount: commits.length,
194
- parsedCommitCount: decision.parsedCommitCount,
195
- unparseableCommits: decision.unparseableCommits,
196
- policyViolations,
197
- skipReason: decision.skipReason
198
- });
199
- continue;
200
- }
201
- directBumps.set(workspace.dir, { releaseType: decision.releaseType });
202
- directResults.set(workspace.dir, {
203
- workspace,
204
- tag,
205
- commits,
206
- releaseType: decision.releaseType,
207
- parsedCommitCount: decision.parsedCommitCount,
208
- unparseableCommits: decision.unparseableCommits,
209
- policyViolations,
210
- bumpOverride
211
- });
212
- }
213
- return { directBumps, directResults, skippedResults, currentVersions };
124
+ const primaryPackageFile = workspace.packageFiles[0];
125
+ if (primaryPackageFile !== undefined) {
126
+ const currentVersion = tryStage(stageLabel, () => readCurrentVersion(primaryPackageFile));
127
+ if (currentVersion !== undefined) {
128
+ currentVersions.set(workspace.dir, currentVersion);
129
+ }
130
+ }
131
+ if (setVersion !== undefined) {
132
+ const currentVersion = currentVersions.get(workspace.dir);
133
+ if (currentVersion === undefined) {
134
+ throw new Error(`Cannot validate --set-version: failed to read current version from ${primaryPackageFile ?? '(no package file)'}`);
135
+ }
136
+ if (!isForwardVersion(currentVersion, setVersion)) {
137
+ throw new Error(`--set-version ${setVersion} is not greater than current version ${currentVersion}`);
138
+ }
139
+ directBumps.set(workspace.dir, { releaseType: 'patch', newVersionOverride: setVersion });
140
+ directResults.set(workspace.dir, {
141
+ workspace,
142
+ tag,
143
+ commits,
144
+ releaseType: undefined,
145
+ parsedCommitCount: undefined,
146
+ unparseableCommits: undefined,
147
+ policyViolations: undefined,
148
+ bumpOverride: undefined,
149
+ setVersion,
150
+ });
151
+ continue;
152
+ }
153
+ const collector = createPolicyViolationCollector();
154
+ const decision = tryStage(stageLabel, () => decideRelease({
155
+ commits,
156
+ force,
157
+ bumpOverride,
158
+ workTypes,
159
+ versionPatterns,
160
+ scopeAliases: config.scopeAliases,
161
+ breakingPolicies,
162
+ onPolicyViolation: collector.onPolicyViolation,
163
+ skipReasons: {
164
+ noCommits: `No commits for ${name} ${since}. Pass --force to release at patch. Skipping.`,
165
+ noBumpWorthy: `No bump-worthy commits for ${name} ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
166
+ },
167
+ }));
168
+ const policyViolations = collector.violations.length > 0 ? collector.violations : undefined;
169
+ if (decision.outcome === 'skip') {
170
+ skippedResults.push({
171
+ workspace,
172
+ tag,
173
+ commitCount: commits.length,
174
+ parsedCommitCount: decision.parsedCommitCount,
175
+ unparseableCommits: decision.unparseableCommits,
176
+ policyViolations,
177
+ skipReason: decision.skipReason,
178
+ });
179
+ continue;
180
+ }
181
+ directBumps.set(workspace.dir, { releaseType: decision.releaseType });
182
+ directResults.set(workspace.dir, {
183
+ workspace,
184
+ tag,
185
+ commits,
186
+ releaseType: decision.releaseType,
187
+ parsedCommitCount: decision.parsedCommitCount,
188
+ unparseableCommits: decision.unparseableCommits,
189
+ policyViolations,
190
+ bumpOverride,
191
+ });
192
+ }
193
+ return { directBumps, directResults, skippedResults, currentVersions };
214
194
  }
215
195
  function collectSkippedWorkspaces(skippedResults, fullReleaseSet) {
216
- const workspaces = [];
217
- for (const skipped of skippedResults) {
218
- if (fullReleaseSet.has(skipped.workspace.dir)) {
219
- continue;
220
- }
221
- const result = {
222
- name: skipped.workspace.dir,
223
- status: "skipped",
224
- commitCount: skipped.commitCount,
225
- skipReason: skipped.skipReason
226
- };
227
- if (skipped.tag !== void 0) {
228
- result.previousTag = skipped.tag;
229
- }
230
- if (skipped.parsedCommitCount !== void 0) {
231
- result.parsedCommitCount = skipped.parsedCommitCount;
232
- }
233
- if (skipped.unparseableCommits !== void 0) {
234
- result.unparseableCommits = skipped.unparseableCommits;
235
- }
236
- if (skipped.policyViolations !== void 0) {
237
- result.policyViolations = skipped.policyViolations;
196
+ const workspaces = [];
197
+ for (const skipped of skippedResults) {
198
+ if (fullReleaseSet.has(skipped.workspace.dir)) {
199
+ continue;
200
+ }
201
+ const result = {
202
+ name: skipped.workspace.dir,
203
+ status: 'skipped',
204
+ commitCount: skipped.commitCount,
205
+ skipReason: skipped.skipReason,
206
+ };
207
+ if (skipped.tag !== undefined) {
208
+ result.previousTag = skipped.tag;
209
+ }
210
+ if (skipped.parsedCommitCount !== undefined) {
211
+ result.parsedCommitCount = skipped.parsedCommitCount;
212
+ }
213
+ if (skipped.unparseableCommits !== undefined) {
214
+ result.unparseableCommits = skipped.unparseableCommits;
215
+ }
216
+ if (skipped.policyViolations !== undefined) {
217
+ result.policyViolations = skipped.policyViolations;
218
+ }
219
+ workspaces.push(result);
238
220
  }
239
- workspaces.push(result);
240
- }
241
- return workspaces;
221
+ return workspaces;
242
222
  }
243
223
  function executeReleaseSet(args) {
244
- const {
245
- sortedDirs,
246
- fullReleaseSet,
247
- config,
248
- directResults,
249
- previousTags,
250
- dryRun,
251
- workspaces,
252
- previewOptions,
253
- overrideContext,
254
- sectionOrder
255
- } = args;
256
- const tags = [];
257
- const modifiedFiles = [];
258
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
259
- for (const dir of sortedDirs) {
260
- const releaseEntry = fullReleaseSet.get(dir);
261
- if (releaseEntry === void 0) {
262
- continue;
263
- }
264
- const workspace = findWorkspace(config.workspaces, dir);
265
- if (workspace === void 0) {
266
- continue;
267
- }
268
- tryStage(
269
- workspaceStageLabel(dir),
270
- () => executeWorkspaceRelease({
271
- dir,
224
+ const { sortedDirs, fullReleaseSet, config, directResults, previousTags, dryRun, workspaces, previewOptions, overrideContext, sectionOrder, } = args;
225
+ const tags = [];
226
+ const modifiedFiles = [];
227
+ const today = new Date().toISOString().slice(0, 10);
228
+ for (const dir of sortedDirs) {
229
+ const releaseEntry = fullReleaseSet.get(dir);
230
+ if (releaseEntry === undefined) {
231
+ continue;
232
+ }
233
+ const workspace = findWorkspace(config.workspaces, dir);
234
+ if (workspace === undefined) {
235
+ continue;
236
+ }
237
+ tryStage(workspaceStageLabel(dir), () => executeWorkspaceRelease({
238
+ dir,
239
+ workspace,
240
+ releaseEntry,
241
+ directResult: directResults.get(dir),
242
+ previousTags,
243
+ config,
244
+ dryRun,
245
+ today,
246
+ tags,
247
+ modifiedFiles,
248
+ workspaces,
249
+ previewOptions,
250
+ overrideContext,
251
+ sectionOrder,
252
+ }));
253
+ }
254
+ return { tags, modifiedFiles };
255
+ }
256
+ function executeWorkspaceRelease(args) {
257
+ const { dir, workspace, releaseEntry, directResult, previousTags, config, dryRun, today, tags, modifiedFiles, workspaces, previewOptions, overrideContext, sectionOrder, } = args;
258
+ const setVersionTarget = directResult?.setVersion;
259
+ const bump = setVersionTarget === undefined
260
+ ? bumpAllVersions(workspace.packageFiles, releaseEntry.releaseType, dryRun)
261
+ : setAllVersions(workspace.packageFiles, setVersionTarget, dryRun);
262
+ const newTag = `${workspace.tagPrefix}${bump.newVersion}`;
263
+ tags.push(newTag);
264
+ modifiedFiles.push(...workspace.packageFiles, ...workspace.changelogPaths.map((p) => `${p}/CHANGELOG.md`));
265
+ const isPropagationOnly = directResult === undefined;
266
+ const isEmptyRange = directResult !== undefined && directResult.commits.length === 0;
267
+ const changelogFiles = generateWorkspaceChangelogs({
272
268
  workspace,
273
269
  releaseEntry,
274
- directResult: directResults.get(dir),
275
- previousTags,
270
+ newTag,
271
+ newVersion: bump.newVersion,
272
+ isPropagationOnly,
273
+ isEmptyRange,
276
274
  config,
277
275
  dryRun,
278
276
  today,
279
- tags,
280
277
  modifiedFiles,
281
- workspaces,
282
278
  previewOptions,
283
279
  overrideContext,
284
- sectionOrder
285
- })
286
- );
287
- }
288
- return { tags, modifiedFiles };
289
- }
290
- function executeWorkspaceRelease(args) {
291
- const {
292
- dir,
293
- workspace,
294
- releaseEntry,
295
- directResult,
296
- previousTags,
297
- config,
298
- dryRun,
299
- today,
300
- tags,
301
- modifiedFiles,
302
- workspaces,
303
- previewOptions,
304
- overrideContext,
305
- sectionOrder
306
- } = args;
307
- const setVersionTarget = directResult?.setVersion;
308
- const bump = setVersionTarget === void 0 ? bumpAllVersions(workspace.packageFiles, releaseEntry.releaseType, dryRun) : setAllVersions(workspace.packageFiles, setVersionTarget, dryRun);
309
- const newTag = `${workspace.tagPrefix}${bump.newVersion}`;
310
- tags.push(newTag);
311
- modifiedFiles.push(...workspace.packageFiles, ...workspace.changelogPaths.map((p) => `${p}/CHANGELOG.md`));
312
- const isPropagationOnly = directResult === void 0;
313
- const isEmptyRange = directResult !== void 0 && directResult.commits.length === 0;
314
- const changelogFiles = generateWorkspaceChangelogs({
315
- workspace,
316
- releaseEntry,
317
- newTag,
318
- newVersion: bump.newVersion,
319
- isPropagationOnly,
320
- isEmptyRange,
321
- config,
322
- dryRun,
323
- today,
324
- modifiedFiles,
325
- previewOptions,
326
- overrideContext,
327
- sectionOrder
328
- });
329
- const released = {
330
- name: dir,
331
- status: "released",
332
- commitCount: directResult?.commits.length ?? 0,
333
- currentVersion: bump.currentVersion,
334
- newVersion: bump.newVersion,
335
- tag: newTag,
336
- bumpedFiles: bump.files,
337
- changelogFiles
338
- };
339
- attachReleasedWorkspaceOptionals(released, {
340
- previousTag: directResult?.tag ?? previousTags.get(dir),
341
- directResult,
342
- releaseEntry,
343
- setVersionTarget
344
- });
345
- workspaces.push(released);
280
+ sectionOrder,
281
+ });
282
+ const released = {
283
+ name: dir,
284
+ status: 'released',
285
+ commitCount: directResult?.commits.length ?? 0,
286
+ currentVersion: bump.currentVersion,
287
+ newVersion: bump.newVersion,
288
+ tag: newTag,
289
+ bumpedFiles: bump.files,
290
+ changelogFiles,
291
+ };
292
+ attachReleasedWorkspaceOptionals(released, {
293
+ previousTag: directResult?.tag ?? previousTags.get(dir),
294
+ directResult,
295
+ releaseEntry,
296
+ setVersionTarget,
297
+ });
298
+ workspaces.push(released);
346
299
  }
347
300
  function attachReleasedWorkspaceOptionals(released, args) {
348
- const { previousTag, directResult, releaseEntry, setVersionTarget } = args;
349
- if (previousTag !== void 0) {
350
- released.previousTag = previousTag;
351
- }
352
- if (directResult?.parsedCommitCount !== void 0) {
353
- released.parsedCommitCount = directResult.parsedCommitCount;
354
- }
355
- if (setVersionTarget === void 0) {
356
- released.releaseType = releaseEntry.releaseType;
357
- }
358
- if (directResult?.commits !== void 0) {
359
- released.commits = directResult.commits;
360
- }
361
- if (directResult?.unparseableCommits !== void 0) {
362
- released.unparseableCommits = directResult.unparseableCommits;
363
- }
364
- if (directResult?.policyViolations !== void 0) {
365
- released.policyViolations = directResult.policyViolations;
366
- }
367
- if (releaseEntry.propagatedFrom !== void 0) {
368
- released.propagatedFrom = releaseEntry.propagatedFrom;
369
- }
370
- if (directResult?.bumpOverride !== void 0) {
371
- released.bumpOverride = directResult.bumpOverride;
372
- }
373
- if (setVersionTarget !== void 0) {
374
- released.setVersion = setVersionTarget;
375
- }
301
+ const { previousTag, directResult, releaseEntry, setVersionTarget } = args;
302
+ if (previousTag !== undefined) {
303
+ released.previousTag = previousTag;
304
+ }
305
+ if (directResult?.parsedCommitCount !== undefined) {
306
+ released.parsedCommitCount = directResult.parsedCommitCount;
307
+ }
308
+ if (setVersionTarget === undefined) {
309
+ released.releaseType = releaseEntry.releaseType;
310
+ }
311
+ if (directResult?.commits !== undefined) {
312
+ released.commits = directResult.commits;
313
+ }
314
+ if (directResult?.unparseableCommits !== undefined) {
315
+ released.unparseableCommits = directResult.unparseableCommits;
316
+ }
317
+ if (directResult?.policyViolations !== undefined) {
318
+ released.policyViolations = directResult.policyViolations;
319
+ }
320
+ if (releaseEntry.propagatedFrom !== undefined) {
321
+ released.propagatedFrom = releaseEntry.propagatedFrom;
322
+ }
323
+ if (directResult?.bumpOverride !== undefined) {
324
+ released.bumpOverride = directResult.bumpOverride;
325
+ }
326
+ if (setVersionTarget !== undefined) {
327
+ released.setVersion = setVersionTarget;
328
+ }
376
329
  }
377
330
  function generateWorkspaceChangelogs(args) {
378
- const {
379
- workspace,
380
- releaseEntry,
381
- newTag,
382
- newVersion,
383
- isPropagationOnly,
384
- isEmptyRange,
385
- config,
386
- dryRun,
387
- today,
388
- modifiedFiles,
389
- previewOptions,
390
- overrideContext,
391
- sectionOrder
392
- } = args;
393
- const newEntries = buildWorkspaceEntries({
394
- workspace,
395
- releaseEntry,
396
- newTag,
397
- newVersion,
398
- isPropagationOnly,
399
- isEmptyRange,
400
- config,
401
- today
402
- });
403
- const applied = applyWorkspaceOverrides(newEntries, workspace.workspacePath, overrideContext);
404
- const changelogFiles = [];
405
- let firstChangelogJsonPath;
406
- for (const changelogPath of workspace.changelogPaths) {
407
- const jsonPath = resolveChangelogJsonPath(config, changelogPath);
408
- const shouldWriteJson = config.changelogJson.enabled && !dryRun;
409
- const mergedEntries = shouldWriteJson ? upsertChangelogJsonAndReturn(jsonPath, applied.entries) : mergeChangelogEntriesWithDisk(jsonPath, applied.entries);
410
- if (config.changelogJson.enabled) {
411
- modifiedFiles.push(jsonPath);
412
- firstChangelogJsonPath ??= jsonPath;
413
- }
414
- changelogFiles.push(
415
- writeChangelogMarkdown({
416
- changelogPath,
417
- entries: mergedEntries,
418
- sectionOrder,
419
- dryRun
420
- })
421
- );
422
- }
423
- maybeWritePreviews(workspace, newTag, firstChangelogJsonPath, previewOptions, dryRun);
424
- return changelogFiles;
331
+ const { workspace, releaseEntry, newTag, newVersion, isPropagationOnly, isEmptyRange, config, dryRun, today, modifiedFiles, previewOptions, overrideContext, sectionOrder, } = args;
332
+ const newEntries = buildWorkspaceEntries({
333
+ workspace,
334
+ releaseEntry,
335
+ newTag,
336
+ newVersion,
337
+ isPropagationOnly,
338
+ isEmptyRange,
339
+ config,
340
+ today,
341
+ });
342
+ const applied = applyWorkspaceOverrides(newEntries, workspace.workspacePath, overrideContext);
343
+ const changelogFiles = [];
344
+ let firstChangelogJsonPath;
345
+ for (const changelogPath of workspace.changelogPaths) {
346
+ const jsonPath = resolveChangelogJsonPath(config, changelogPath);
347
+ const shouldWriteJson = config.changelogJson.enabled && !dryRun;
348
+ const mergedEntries = shouldWriteJson
349
+ ? upsertChangelogJsonAndReturn(jsonPath, applied.entries)
350
+ : mergeChangelogEntriesWithDisk(jsonPath, applied.entries);
351
+ if (config.changelogJson.enabled) {
352
+ modifiedFiles.push(jsonPath);
353
+ firstChangelogJsonPath ??= jsonPath;
354
+ }
355
+ changelogFiles.push(writeChangelogMarkdown({
356
+ changelogPath,
357
+ entries: mergedEntries,
358
+ sectionOrder,
359
+ dryRun,
360
+ }));
361
+ }
362
+ maybeWritePreviews(workspace, newTag, firstChangelogJsonPath, previewOptions, dryRun);
363
+ return changelogFiles;
425
364
  }
426
365
  function buildWorkspaceEntries(args) {
427
- const { workspace, releaseEntry, newTag, newVersion, isPropagationOnly, isEmptyRange, config, today } = args;
428
- if (isPropagationOnly && releaseEntry.propagatedFrom !== void 0) {
429
- return [buildSyntheticChangelogEntry(releaseEntry.propagatedFrom, newVersion, today)];
430
- }
431
- if (isEmptyRange) {
432
- return [buildEmptyReleaseEntry(newVersion, today)];
433
- }
434
- const tagPattern = buildTagPattern(getAllTagPrefixes(workspace));
435
- return buildChangelogEntries(config, newTag, { tagPattern, includePaths: workspace.paths });
366
+ const { workspace, releaseEntry, newTag, newVersion, isPropagationOnly, isEmptyRange, config, today } = args;
367
+ if (isPropagationOnly && releaseEntry.propagatedFrom !== undefined) {
368
+ return [buildSyntheticChangelogEntry(releaseEntry.propagatedFrom, newVersion, today)];
369
+ }
370
+ if (isEmptyRange) {
371
+ return [buildEmptyReleaseEntry(newVersion, today)];
372
+ }
373
+ const tagPattern = buildTagPattern(getAllTagPrefixes(workspace));
374
+ return buildChangelogEntries(config, newTag, { tagPattern, includePaths: workspace.paths });
436
375
  }
437
376
  function maybeWritePreviews(workspace, newTag, changelogJsonPath, previewOptions, dryRun) {
438
- if (!previewOptions.enabled || changelogJsonPath === void 0) {
439
- return;
440
- }
441
- writeReleaseNotesPreviews({
442
- workspacePath: workspace.workspacePath,
443
- tag: newTag,
444
- changelogJsonPath,
445
- sectionOrder: previewOptions.sectionOrder,
446
- dryRun
447
- });
377
+ if (!previewOptions.enabled || changelogJsonPath === undefined) {
378
+ return;
379
+ }
380
+ writeReleaseNotesPreviews({
381
+ workspacePath: workspace.workspacePath,
382
+ tag: newTag,
383
+ changelogJsonPath,
384
+ sectionOrder: previewOptions.sectionOrder,
385
+ dryRun,
386
+ });
448
387
  }
449
388
  function runFormatCommand(config, tags, modifiedFiles, dryRun) {
450
- const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
451
- if (tags.length === 0 || formatCommandStr === void 0) {
452
- return void 0;
453
- }
454
- const fullCommand = `${formatCommandStr} ${modifiedFiles.join(" ")}`;
455
- if (dryRun) {
456
- return { command: fullCommand, executed: false, files: modifiedFiles };
457
- }
458
- try {
459
- execSync(fullCommand, { stdio: "inherit" });
460
- } catch (error) {
461
- const baseMessage = error instanceof Error ? error.message : String(error);
462
- throw new Error(`format stage: ${baseMessage} (command: '${fullCommand}')`, { cause: error });
463
- }
464
- return { command: fullCommand, executed: true, files: modifiedFiles };
389
+ const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? 'npx prettier --write' : undefined);
390
+ if (tags.length === 0 || formatCommandStr === undefined) {
391
+ return undefined;
392
+ }
393
+ const fullCommand = `${formatCommandStr} ${modifiedFiles.join(' ')}`;
394
+ if (dryRun) {
395
+ return { command: fullCommand, executed: false, files: modifiedFiles };
396
+ }
397
+ try {
398
+ execSync(fullCommand, { stdio: 'inherit' });
399
+ }
400
+ catch (error) {
401
+ const baseMessage = error instanceof Error ? error.message : String(error);
402
+ throw new Error(`format stage: ${baseMessage} (command: '${fullCommand}')`, { cause: error });
403
+ }
404
+ return { command: fullCommand, executed: true, files: modifiedFiles };
465
405
  }
466
406
  function findWorkspace(workspaces, dir) {
467
- return workspaces.find((w) => w.dir === dir);
407
+ return workspaces.find((w) => w.dir === dir);
468
408
  }
469
409
  function wrapStageError(stageLabel, error) {
470
- const message = error instanceof Error ? error.message : String(error);
471
- return new Error(`${stageLabel}: ${message}`, { cause: error });
410
+ const message = error instanceof Error ? error.message : String(error);
411
+ return new Error(`${stageLabel}: ${message}`, { cause: error });
472
412
  }
473
413
  function tryStage(stageLabel, fn) {
474
- try {
475
- return fn();
476
- } catch (error) {
477
- throw wrapStageError(stageLabel, error);
478
- }
414
+ try {
415
+ return fn();
416
+ }
417
+ catch (error) {
418
+ throw wrapStageError(stageLabel, error);
419
+ }
479
420
  }
480
421
  function workspaceStageLabel(dir) {
481
- return `workspace '${dir}' release stage`;
422
+ return `workspace '${dir}' release stage`;
482
423
  }
483
424
  function maybeEmitBaselineHint(workspace, knownPrefixes, state) {
484
- if (state.emitted) return;
485
- if ((workspace.legacyIdentities?.length ?? 0) > 0) return;
486
- const candidates = detectUndeclaredTagPrefixes(knownPrefixes);
487
- if (candidates.length === 0) return;
488
- const totalTags = candidates.reduce((sum, candidate) => sum + candidate.tagCount, 0);
489
- const example = candidates[0]?.exampleTags[0] ?? `${candidates[0]?.prefix ?? ""}?`;
490
- process.stderr.write(
491
- `Hint: no baseline tag found for ${workspace.dir} under '${workspace.tagPrefix}', but ${totalTags} candidate-shaped tags exist (e.g., ${example}). Run 'release-kit show-tag-prefixes' to check for undeclared legacy prefixes.
492
- `
493
- );
494
- state.emitted = true;
425
+ if (state.emitted)
426
+ return;
427
+ if ((workspace.legacyIdentities?.length ?? 0) > 0)
428
+ return;
429
+ const candidates = detectUndeclaredTagPrefixes(knownPrefixes);
430
+ if (candidates.length === 0)
431
+ return;
432
+ const totalTags = candidates.reduce((sum, candidate) => sum + candidate.tagCount, 0);
433
+ const example = candidates[0]?.exampleTags[0] ?? `${candidates[0]?.prefix ?? ''}?`;
434
+ process.stderr.write(`Hint: no baseline tag found for ${workspace.dir} under '${workspace.tagPrefix}', but ` +
435
+ `${totalTags} candidate-shaped tags exist (e.g., ${example}). ` +
436
+ "Run 'release-kit show-tag-prefixes' to check for undeclared legacy prefixes.\n");
437
+ state.emitted = true;
495
438
  }
496
439
  function topologicalSort(releaseSet, graph) {
497
- const releaseDirs = new Set(releaseSet.keys());
498
- if (releaseDirs.size === 0) {
499
- return { sorted: [], cyclicDirs: [] };
500
- }
501
- const inDegree = /* @__PURE__ */ new Map();
502
- const forwardEdges = /* @__PURE__ */ new Map();
503
- for (const dir of releaseDirs) {
504
- inDegree.set(dir, 0);
505
- forwardEdges.set(dir, []);
506
- }
507
- for (const [packageName, dependents] of graph.dependentsOf) {
508
- const depDir = graph.packageNameToDir.get(packageName);
509
- if (depDir === void 0 || !releaseDirs.has(depDir)) {
510
- continue;
511
- }
512
- for (const dependent of dependents) {
513
- if (!releaseDirs.has(dependent.dir)) {
514
- continue;
515
- }
516
- const edges = forwardEdges.get(depDir);
517
- if (edges !== void 0) {
518
- edges.push(dependent.dir);
519
- }
520
- inDegree.set(dependent.dir, (inDegree.get(dependent.dir) ?? 0) + 1);
521
- }
522
- }
523
- const queue = [];
524
- for (const [dir, degree] of inDegree) {
525
- if (degree === 0) {
526
- queue.push(dir);
527
- }
528
- }
529
- const sorted = [];
530
- while (queue.length > 0) {
531
- const dir = queue.shift();
532
- if (dir === void 0) {
533
- break;
534
- }
535
- sorted.push(dir);
536
- for (const dependent of forwardEdges.get(dir) ?? []) {
537
- const newDegree = (inDegree.get(dependent) ?? 1) - 1;
538
- inDegree.set(dependent, newDegree);
539
- if (newDegree === 0) {
540
- queue.push(dependent);
541
- }
542
- }
543
- }
544
- const sortedSet = new Set(sorted);
545
- const cyclicDirs = [];
546
- for (const dir of releaseDirs) {
547
- if (!sortedSet.has(dir)) {
548
- sorted.push(dir);
549
- cyclicDirs.push(dir);
550
- }
551
- }
552
- return { sorted, cyclicDirs };
440
+ const releaseDirs = new Set(releaseSet.keys());
441
+ if (releaseDirs.size === 0) {
442
+ return { sorted: [], cyclicDirs: [] };
443
+ }
444
+ const inDegree = new Map();
445
+ const forwardEdges = new Map();
446
+ for (const dir of releaseDirs) {
447
+ inDegree.set(dir, 0);
448
+ forwardEdges.set(dir, []);
449
+ }
450
+ for (const [packageName, dependents] of graph.dependentsOf) {
451
+ const depDir = graph.packageNameToDir.get(packageName);
452
+ if (depDir === undefined || !releaseDirs.has(depDir)) {
453
+ continue;
454
+ }
455
+ for (const dependent of dependents) {
456
+ if (!releaseDirs.has(dependent.dir)) {
457
+ continue;
458
+ }
459
+ const edges = forwardEdges.get(depDir);
460
+ if (edges !== undefined) {
461
+ edges.push(dependent.dir);
462
+ }
463
+ inDegree.set(dependent.dir, (inDegree.get(dependent.dir) ?? 0) + 1);
464
+ }
465
+ }
466
+ const queue = [];
467
+ for (const [dir, degree] of inDegree) {
468
+ if (degree === 0) {
469
+ queue.push(dir);
470
+ }
471
+ }
472
+ const sorted = [];
473
+ while (queue.length > 0) {
474
+ const dir = queue.shift();
475
+ if (dir === undefined) {
476
+ break;
477
+ }
478
+ sorted.push(dir);
479
+ for (const dependent of forwardEdges.get(dir) ?? []) {
480
+ const newDegree = (inDegree.get(dependent) ?? 1) - 1;
481
+ inDegree.set(dependent, newDegree);
482
+ if (newDegree === 0) {
483
+ queue.push(dependent);
484
+ }
485
+ }
486
+ }
487
+ const sortedSet = new Set(sorted);
488
+ const cyclicDirs = [];
489
+ for (const dir of releaseDirs) {
490
+ if (!sortedSet.has(dir)) {
491
+ sorted.push(dir);
492
+ cyclicDirs.push(dir);
493
+ }
494
+ }
495
+ return { sorted, cyclicDirs };
553
496
  }
554
- export {
555
- releasePrepareMono
556
- };