@williamthorsen/release-kit 5.0.0 → 5.1.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 (42) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +137 -43
  3. package/dist/esm/.cache +1 -1
  4. package/dist/esm/assertCleanWorkingTree.js +1 -1
  5. package/dist/esm/bin/release-kit.js +1 -1
  6. package/dist/esm/buildChangelogEntries.d.ts +3 -0
  7. package/dist/esm/{generateChangelogJson.js → buildChangelogEntries.js} +7 -80
  8. package/dist/esm/buildDependencyGraph.d.ts +1 -0
  9. package/dist/esm/buildDependencyGraph.js +8 -1
  10. package/dist/esm/buildReleaseSummary.js +9 -1
  11. package/dist/esm/buildSyntheticChangelogEntry.d.ts +5 -0
  12. package/dist/esm/buildSyntheticChangelogEntry.js +13 -0
  13. package/dist/esm/changelogJsonFile.d.ts +4 -0
  14. package/dist/esm/changelogJsonFile.js +68 -0
  15. package/dist/esm/decideRelease.d.ts +25 -0
  16. package/dist/esm/decideRelease.js +28 -0
  17. package/dist/esm/defaults.d.ts +1 -0
  18. package/dist/esm/defaults.js +2 -0
  19. package/dist/esm/index.d.ts +2 -43
  20. package/dist/esm/index.js +0 -82
  21. package/dist/esm/init/templates.js +2 -2
  22. package/dist/esm/loadConfig.d.ts +10 -1
  23. package/dist/esm/loadConfig.js +96 -2
  24. package/dist/esm/prepareCommand.js +51 -9
  25. package/dist/esm/publish.d.ts +0 -1
  26. package/dist/esm/publish.js +3 -3
  27. package/dist/esm/publishCommand.js +10 -1
  28. package/dist/esm/releasePrepare.js +83 -39
  29. package/dist/esm/releasePrepareMono.js +133 -87
  30. package/dist/esm/releasePrepareProject.d.ts +9 -0
  31. package/dist/esm/releasePrepareProject.js +109 -0
  32. package/dist/esm/reportPrepare.js +70 -24
  33. package/dist/esm/types.d.ts +57 -14
  34. package/dist/esm/validateConfig.js +26 -0
  35. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -0
  36. package/dist/esm/validateOnlyExcludesStrandedDependents.js +109 -0
  37. package/dist/esm/version.d.ts +1 -1
  38. package/dist/esm/version.js +1 -1
  39. package/package.json +4 -1
  40. package/presets/labels/common.yaml +9 -6
  41. package/schemas/label-map.json +24 -0
  42. package/dist/esm/generateChangelogJson.d.ts +0 -7
@@ -1,9 +1,10 @@
1
1
  import { execSync } from "node:child_process";
2
+ import { buildChangelogEntries } from "./buildChangelogEntries.js";
2
3
  import { bumpAllVersions, setAllVersions } from "./bumpAllVersions.js";
4
+ import { resolveChangelogJsonPath, upsertChangelogJson } from "./changelogJsonFile.js";
3
5
  import { isForwardVersion } from "./compareVersions.js";
4
6
  import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
5
7
  import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
6
- import { generateChangelogJson } from "./generateChangelogJson.js";
7
8
  import { generateChangelogs } from "./generateChangelogs.js";
8
9
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
9
10
  import { hasPrettierConfig } from "./hasPrettierConfig.js";
@@ -43,19 +44,22 @@ function releasePrepare(config, options) {
43
44
  releaseType = bumpOverride;
44
45
  }
45
46
  if (releaseType === void 0) {
47
+ const skipped = {
48
+ status: "skipped",
49
+ commitCount: commits.length,
50
+ skipReason: "No release-worthy changes found. Skipping."
51
+ };
52
+ if (tag !== void 0) {
53
+ skipped.previousTag = tag;
54
+ }
55
+ if (parsedCommitCount !== void 0) {
56
+ skipped.parsedCommitCount = parsedCommitCount;
57
+ }
58
+ if (unparseableCommits !== void 0) {
59
+ skipped.unparseableCommits = unparseableCommits;
60
+ }
46
61
  return {
47
- workspaces: [
48
- {
49
- status: "skipped",
50
- previousTag: tag,
51
- commitCount: commits.length,
52
- parsedCommitCount,
53
- unparseableCommits,
54
- bumpedFiles: [],
55
- changelogFiles: [],
56
- skipReason: "No release-worthy changes found. Skipping."
57
- }
58
- ],
62
+ workspaces: [skipped],
59
63
  tags: [],
60
64
  dryRun
61
65
  };
@@ -64,12 +68,7 @@ function releasePrepare(config, options) {
64
68
  }
65
69
  const newTag = `${config.tagPrefix}${bump.newVersion}`;
66
70
  const changelogFiles = generateChangelogs(config, newTag, dryRun);
67
- const changelogJsonFiles = [];
68
- if (config.changelogJson.enabled) {
69
- for (const changelogPath of config.changelogPaths) {
70
- changelogJsonFiles.push(...generateChangelogJson(config, changelogPath, newTag, dryRun));
71
- }
72
- }
71
+ const changelogJsonFiles = config.changelogJson.enabled ? buildAndPersistChangelogJson(config, newTag, dryRun) : [];
73
72
  maybeWriteSinglePackagePreviews(withReleaseNotes === true, config, newTag, changelogJsonFiles[0], dryRun);
74
73
  const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
75
74
  let formatCommand;
@@ -86,36 +85,81 @@ function releasePrepare(config, options) {
86
85
  try {
87
86
  execSync(fullCommand, { stdio: "inherit" });
88
87
  } catch (error) {
89
- throw new Error(
90
- `Format command failed ('${fullCommand}'): ${error instanceof Error ? error.message : String(error)}`
91
- );
88
+ const baseMessage = error instanceof Error ? error.message : String(error);
89
+ throw new Error(`format stage: ${baseMessage} (command: '${fullCommand}')`, { cause: error });
92
90
  }
93
91
  formatCommand = { command: fullCommand, executed: true, files: modifiedFiles };
94
92
  }
95
93
  }
94
+ const released = buildReleasedSinglePackage({
95
+ commits,
96
+ bump,
97
+ newTag,
98
+ changelogFiles,
99
+ previousTag: tag,
100
+ parsedCommitCount,
101
+ releaseType,
102
+ unparseableCommits,
103
+ setVersion
104
+ });
96
105
  return {
97
- workspaces: [
98
- {
99
- status: "released",
100
- previousTag: tag,
101
- commitCount: commits.length,
102
- parsedCommitCount,
103
- releaseType,
104
- currentVersion: bump.currentVersion,
105
- newVersion: bump.newVersion,
106
- tag: newTag,
107
- bumpedFiles: bump.files,
108
- changelogFiles,
109
- commits,
110
- unparseableCommits,
111
- ...setVersion === void 0 ? {} : { setVersion }
112
- }
113
- ],
106
+ workspaces: [released],
114
107
  tags: [newTag],
115
108
  formatCommand,
116
109
  dryRun
117
110
  };
118
111
  }
112
+ function buildReleasedSinglePackage(args) {
113
+ const {
114
+ commits,
115
+ bump,
116
+ newTag,
117
+ changelogFiles,
118
+ previousTag,
119
+ parsedCommitCount,
120
+ releaseType,
121
+ unparseableCommits,
122
+ setVersion
123
+ } = args;
124
+ const released = {
125
+ status: "released",
126
+ commitCount: commits.length,
127
+ currentVersion: bump.currentVersion,
128
+ newVersion: bump.newVersion,
129
+ tag: newTag,
130
+ bumpedFiles: bump.files,
131
+ changelogFiles,
132
+ commits
133
+ };
134
+ if (previousTag !== void 0) {
135
+ released.previousTag = previousTag;
136
+ }
137
+ if (parsedCommitCount !== void 0) {
138
+ released.parsedCommitCount = parsedCommitCount;
139
+ }
140
+ if (releaseType !== void 0) {
141
+ released.releaseType = releaseType;
142
+ }
143
+ if (unparseableCommits !== void 0) {
144
+ released.unparseableCommits = unparseableCommits;
145
+ }
146
+ if (setVersion !== void 0) {
147
+ released.setVersion = setVersion;
148
+ }
149
+ return released;
150
+ }
151
+ function buildAndPersistChangelogJson(config, newTag, dryRun) {
152
+ const changelogJsonFiles = [];
153
+ const entries = buildChangelogEntries(config, newTag);
154
+ for (const changelogPath of config.changelogPaths) {
155
+ const jsonPath = resolveChangelogJsonPath(config, changelogPath);
156
+ if (!dryRun) {
157
+ upsertChangelogJson(jsonPath, entries);
158
+ }
159
+ changelogJsonFiles.push(jsonPath);
160
+ }
161
+ return changelogJsonFiles;
162
+ }
119
163
  function maybeWriteSinglePackagePreviews(withReleaseNotes, config, newTag, changelogJsonPath, dryRun) {
120
164
  if (!withReleaseNotes) {
121
165
  return;
@@ -1,17 +1,20 @@
1
1
  import { execSync } from "node:child_process";
2
+ import { buildChangelogEntries } from "./buildChangelogEntries.js";
2
3
  import { buildDependencyGraph } from "./buildDependencyGraph.js";
4
+ import { buildSyntheticChangelogEntry } from "./buildSyntheticChangelogEntry.js";
3
5
  import { bumpAllVersions, setAllVersions } from "./bumpAllVersions.js";
6
+ import { resolveChangelogJsonPath, upsertChangelogJson } from "./changelogJsonFile.js";
4
7
  import { isForwardVersion } from "./compareVersions.js";
8
+ import { decideRelease } from "./decideRelease.js";
5
9
  import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
6
10
  import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
7
- import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
8
- import { generateChangelogJson, generateSyntheticChangelogJson } from "./generateChangelogJson.js";
9
11
  import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
10
12
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
11
13
  import { hasPrettierConfig } from "./hasPrettierConfig.js";
12
14
  import { resolveWorkTypes } from "./loadConfig.js";
13
15
  import { propagateBumps } from "./propagateBumps.js";
14
16
  import { readCurrentVersion } from "./readCurrentVersion.js";
17
+ import { releasePrepareProject } from "./releasePrepareProject.js";
15
18
  import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
16
19
  import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
17
20
  import { writeSyntheticChangelog } from "./writeSyntheticChangelog.js";
@@ -59,13 +62,18 @@ function releasePrepareMono(config, options) {
59
62
  const orderB = configOrder.get(b.name ?? "") ?? 0;
60
63
  return orderA - orderB;
61
64
  });
65
+ let project;
66
+ if (config.project !== void 0) {
67
+ project = tryStage("project release stage", () => releasePrepareProject({ config, options, modifiedFiles, tags }));
68
+ }
62
69
  const formatCommand = runFormatCommand(config, tags, modifiedFiles, dryRun);
63
70
  return {
64
71
  workspaces,
65
72
  tags,
66
73
  formatCommand,
67
74
  dryRun,
68
- ...warnings.length > 0 ? { warnings } : {}
75
+ ...warnings.length > 0 ? { warnings } : {},
76
+ ...project === void 0 ? {} : { project }
69
77
  };
70
78
  }
71
79
  function determineDirectBumps(config, options) {
@@ -83,14 +91,18 @@ function determineDirectBumps(config, options) {
83
91
  const knownPrefixes = config.workspaces.flatMap(getAllTagPrefixes);
84
92
  for (const workspace of config.workspaces) {
85
93
  const name = workspace.dir;
86
- const { tag, commits } = getCommitsSinceTarget(getAllTagPrefixes(workspace), workspace.paths);
94
+ const stageLabel = workspaceStageLabel(workspace.dir);
95
+ const { tag, commits } = tryStage(
96
+ stageLabel,
97
+ () => getCommitsSinceTarget(getAllTagPrefixes(workspace), workspace.paths)
98
+ );
87
99
  const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
88
100
  if (tag === void 0) {
89
101
  maybeEmitBaselineHint(workspace, knownPrefixes, hintState);
90
102
  }
91
103
  const primaryPackageFile = workspace.packageFiles[0];
92
104
  if (primaryPackageFile !== void 0) {
93
- const currentVersion = readCurrentVersion(primaryPackageFile);
105
+ const currentVersion = tryStage(stageLabel, () => readCurrentVersion(primaryPackageFile));
94
106
  if (currentVersion !== void 0) {
95
107
  currentVersions.set(workspace.dir, currentVersion);
96
108
  }
@@ -113,51 +125,46 @@ function determineDirectBumps(config, options) {
113
125
  releaseType: void 0,
114
126
  parsedCommitCount: void 0,
115
127
  unparseableCommits: void 0,
128
+ bumpOverride: void 0,
116
129
  setVersion
117
130
  });
118
131
  continue;
119
132
  }
120
- if (commits.length === 0 && !force) {
121
- skippedResults.push({
122
- workspace,
123
- tag,
124
- commitCount: 0,
125
- parsedCommitCount: void 0,
126
- unparseableCommits: void 0,
127
- skipReason: `No changes for ${name} ${since}. Skipping.`
128
- });
129
- continue;
130
- }
131
- let releaseType;
132
- let parsedCommitCount;
133
- let unparseableCommits;
134
- if (bumpOverride === void 0) {
135
- const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.scopeAliases);
136
- parsedCommitCount = determination.parsedCommitCount;
137
- unparseableCommits = determination.unparseableCommits;
138
- releaseType = determination.releaseType;
139
- } else {
140
- releaseType = bumpOverride;
141
- }
142
- if (releaseType === void 0) {
133
+ const decision = tryStage(
134
+ stageLabel,
135
+ () => decideRelease({
136
+ commits,
137
+ force,
138
+ bumpOverride,
139
+ workTypes,
140
+ versionPatterns,
141
+ scopeAliases: config.scopeAliases,
142
+ skipReasons: {
143
+ noCommits: `No commits for ${name} ${since}. Pass --force to release at patch. Skipping.`,
144
+ noBumpWorthy: `No bump-worthy commits for ${name} ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`
145
+ }
146
+ })
147
+ );
148
+ if (decision.outcome === "skip") {
143
149
  skippedResults.push({
144
150
  workspace,
145
151
  tag,
146
152
  commitCount: commits.length,
147
- parsedCommitCount,
148
- unparseableCommits,
149
- skipReason: `No release-worthy changes for ${name} ${since}. Skipping.`
153
+ parsedCommitCount: decision.parsedCommitCount,
154
+ unparseableCommits: decision.unparseableCommits,
155
+ skipReason: decision.skipReason
150
156
  });
151
157
  continue;
152
158
  }
153
- directBumps.set(workspace.dir, { releaseType });
159
+ directBumps.set(workspace.dir, { releaseType: decision.releaseType });
154
160
  directResults.set(workspace.dir, {
155
161
  workspace,
156
162
  tag,
157
163
  commits,
158
- releaseType,
159
- parsedCommitCount,
160
- unparseableCommits
164
+ releaseType: decision.releaseType,
165
+ parsedCommitCount: decision.parsedCommitCount,
166
+ unparseableCommits: decision.unparseableCommits,
167
+ bumpOverride
161
168
  });
162
169
  }
163
170
  return { directBumps, directResults, skippedResults, currentVersions };
@@ -168,17 +175,22 @@ function collectSkippedWorkspaces(skippedResults, fullReleaseSet) {
168
175
  if (fullReleaseSet.has(skipped.workspace.dir)) {
169
176
  continue;
170
177
  }
171
- workspaces.push({
178
+ const result = {
172
179
  name: skipped.workspace.dir,
173
180
  status: "skipped",
174
- previousTag: skipped.tag,
175
181
  commitCount: skipped.commitCount,
176
- parsedCommitCount: skipped.parsedCommitCount,
177
- unparseableCommits: skipped.unparseableCommits,
178
- bumpedFiles: [],
179
- changelogFiles: [],
180
182
  skipReason: skipped.skipReason
181
- });
183
+ };
184
+ if (skipped.tag !== void 0) {
185
+ result.previousTag = skipped.tag;
186
+ }
187
+ if (skipped.parsedCommitCount !== void 0) {
188
+ result.parsedCommitCount = skipped.parsedCommitCount;
189
+ }
190
+ if (skipped.unparseableCommits !== void 0) {
191
+ result.unparseableCommits = skipped.unparseableCommits;
192
+ }
193
+ workspaces.push(result);
182
194
  }
183
195
  return workspaces;
184
196
  }
@@ -195,20 +207,23 @@ function executeReleaseSet(sortedDirs, fullReleaseSet, config, directResults, pr
195
207
  if (workspace === void 0) {
196
208
  continue;
197
209
  }
198
- executeWorkspaceRelease({
199
- dir,
200
- workspace,
201
- releaseEntry,
202
- directResult: directResults.get(dir),
203
- previousTags,
204
- config,
205
- dryRun,
206
- today,
207
- tags,
208
- modifiedFiles,
209
- workspaces,
210
- previewOptions
211
- });
210
+ tryStage(
211
+ workspaceStageLabel(dir),
212
+ () => executeWorkspaceRelease({
213
+ dir,
214
+ workspace,
215
+ releaseEntry,
216
+ directResult: directResults.get(dir),
217
+ previousTags,
218
+ config,
219
+ dryRun,
220
+ today,
221
+ tags,
222
+ modifiedFiles,
223
+ workspaces,
224
+ previewOptions
225
+ })
226
+ );
212
227
  }
213
228
  return { tags, modifiedFiles };
214
229
  }
@@ -245,25 +260,42 @@ function executeWorkspaceRelease(args) {
245
260
  modifiedFiles,
246
261
  previewOptions
247
262
  });
248
- workspaces.push({
263
+ const released = {
249
264
  name: dir,
250
265
  status: "released",
251
- previousTag: directResult?.tag ?? previousTags.get(dir),
252
266
  commitCount: directResult?.commits.length ?? 0,
253
- parsedCommitCount: directResult?.parsedCommitCount,
254
- // For --set-version workspaces releaseType is left undefined so reporting can branch
255
- // on the override case without conflating it with a bump type.
256
- releaseType: setVersionTarget === void 0 ? releaseEntry.releaseType : void 0,
257
267
  currentVersion: bump.currentVersion,
258
268
  newVersion: bump.newVersion,
259
269
  tag: newTag,
260
270
  bumpedFiles: bump.files,
261
- changelogFiles,
262
- commits: directResult?.commits,
263
- unparseableCommits: directResult?.unparseableCommits,
264
- propagatedFrom: releaseEntry.propagatedFrom,
265
- ...setVersionTarget === void 0 ? {} : { setVersion: setVersionTarget }
266
- });
271
+ changelogFiles
272
+ };
273
+ const previousTag = directResult?.tag ?? previousTags.get(dir);
274
+ if (previousTag !== void 0) {
275
+ released.previousTag = previousTag;
276
+ }
277
+ if (directResult?.parsedCommitCount !== void 0) {
278
+ released.parsedCommitCount = directResult.parsedCommitCount;
279
+ }
280
+ if (setVersionTarget === void 0) {
281
+ released.releaseType = releaseEntry.releaseType;
282
+ }
283
+ if (directResult?.commits !== void 0) {
284
+ released.commits = directResult.commits;
285
+ }
286
+ if (directResult?.unparseableCommits !== void 0) {
287
+ released.unparseableCommits = directResult.unparseableCommits;
288
+ }
289
+ if (releaseEntry.propagatedFrom !== void 0) {
290
+ released.propagatedFrom = releaseEntry.propagatedFrom;
291
+ }
292
+ if (directResult?.bumpOverride !== void 0) {
293
+ released.bumpOverride = directResult.bumpOverride;
294
+ }
295
+ if (setVersionTarget !== void 0) {
296
+ released.setVersion = setVersionTarget;
297
+ }
298
+ workspaces.push(released);
267
299
  }
268
300
  function generateWorkspaceChangelogs(args) {
269
301
  const {
@@ -293,17 +325,14 @@ function generateWorkspaceChangelogs(args) {
293
325
  );
294
326
  }
295
327
  if (config.changelogJson.enabled) {
328
+ const syntheticEntry = buildSyntheticChangelogEntry(releaseEntry.propagatedFrom, newVersion, today);
296
329
  for (const changelogPath of workspace.changelogPaths) {
297
- const jsonFiles = generateSyntheticChangelogJson(
298
- config,
299
- changelogPath,
300
- newVersion,
301
- today,
302
- releaseEntry.propagatedFrom,
303
- dryRun
304
- );
305
- modifiedFiles.push(...jsonFiles);
306
- firstChangelogJsonPath ??= jsonFiles[0];
330
+ const jsonPath = resolveChangelogJsonPath(config, changelogPath);
331
+ if (!dryRun) {
332
+ upsertChangelogJson(jsonPath, [syntheticEntry]);
333
+ }
334
+ modifiedFiles.push(jsonPath);
335
+ firstChangelogJsonPath ??= jsonPath;
307
336
  }
308
337
  }
309
338
  maybeWritePreviews(workspace, newTag, firstChangelogJsonPath, previewOptions, dryRun);
@@ -319,13 +348,17 @@ function generateWorkspaceChangelogs(args) {
319
348
  );
320
349
  }
321
350
  if (config.changelogJson.enabled) {
351
+ const entries = buildChangelogEntries(config, newTag, {
352
+ tagPattern,
353
+ includePaths: workspace.paths
354
+ });
322
355
  for (const changelogPath of workspace.changelogPaths) {
323
- const jsonFiles = generateChangelogJson(config, changelogPath, newTag, dryRun, {
324
- tagPattern,
325
- includePaths: workspace.paths
326
- });
327
- modifiedFiles.push(...jsonFiles);
328
- firstChangelogJsonPath ??= jsonFiles[0];
356
+ const jsonPath = resolveChangelogJsonPath(config, changelogPath);
357
+ if (!dryRun) {
358
+ upsertChangelogJson(jsonPath, entries);
359
+ }
360
+ modifiedFiles.push(jsonPath);
361
+ firstChangelogJsonPath ??= jsonPath;
329
362
  }
330
363
  }
331
364
  maybeWritePreviews(workspace, newTag, firstChangelogJsonPath, previewOptions, dryRun);
@@ -355,15 +388,28 @@ function runFormatCommand(config, tags, modifiedFiles, dryRun) {
355
388
  try {
356
389
  execSync(fullCommand, { stdio: "inherit" });
357
390
  } catch (error) {
358
- throw new Error(
359
- `Format command failed ('${fullCommand}'): ${error instanceof Error ? error.message : String(error)}`
360
- );
391
+ const baseMessage = error instanceof Error ? error.message : String(error);
392
+ throw new Error(`format stage: ${baseMessage} (command: '${fullCommand}')`, { cause: error });
361
393
  }
362
394
  return { command: fullCommand, executed: true, files: modifiedFiles };
363
395
  }
364
396
  function findWorkspace(workspaces, dir) {
365
397
  return workspaces.find((w) => w.dir === dir);
366
398
  }
399
+ function wrapStageError(stageLabel, error) {
400
+ const message = error instanceof Error ? error.message : String(error);
401
+ return new Error(`${stageLabel}: ${message}`, { cause: error });
402
+ }
403
+ function tryStage(stageLabel, fn) {
404
+ try {
405
+ return fn();
406
+ } catch (error) {
407
+ throw wrapStageError(stageLabel, error);
408
+ }
409
+ }
410
+ function workspaceStageLabel(dir) {
411
+ return `workspace '${dir}' release stage`;
412
+ }
367
413
  function maybeEmitBaselineHint(workspace, knownPrefixes, state) {
368
414
  if (state.emitted) return;
369
415
  if ((workspace.legacyIdentities?.length ?? 0) > 0) return;
@@ -0,0 +1,9 @@
1
+ import type { ReleasePrepareOptions } from './releasePrepare.ts';
2
+ import type { MonorepoReleaseConfig, ProjectPrepareResult } from './types.ts';
3
+ export interface ReleasePrepareProjectArgs {
4
+ config: MonorepoReleaseConfig;
5
+ options: ReleasePrepareOptions;
6
+ modifiedFiles: string[];
7
+ tags: string[];
8
+ }
9
+ export declare function releasePrepareProject(args: ReleasePrepareProjectArgs): ProjectPrepareResult;
@@ -0,0 +1,109 @@
1
+ import { buildChangelogEntries } from "./buildChangelogEntries.js";
2
+ import { bumpAllVersions } from "./bumpAllVersions.js";
3
+ import { resolveChangelogJsonPath, writeChangelogJson } from "./changelogJsonFile.js";
4
+ import { decideRelease } from "./decideRelease.js";
5
+ import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
6
+ import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
7
+ import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
8
+ import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
9
+ import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
10
+ const ROOT_PACKAGE_FILE = "./package.json";
11
+ const ROOT_CHANGELOG_PATH = ".";
12
+ function releasePrepareProject(args) {
13
+ const { config, options, modifiedFiles, tags } = args;
14
+ const { dryRun, bumpOverride, withReleaseNotes, force } = options;
15
+ const project = config.project;
16
+ if (project === void 0) {
17
+ throw new Error("releasePrepareProject called without a configured project block");
18
+ }
19
+ const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
20
+ const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
21
+ const contributingPaths = config.workspaces.flatMap((workspace) => workspace.paths);
22
+ const { tag, commits } = getCommitsSinceTarget([project.tagPrefix], contributingPaths);
23
+ const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
24
+ const decision = decideRelease({
25
+ commits,
26
+ force,
27
+ bumpOverride,
28
+ workTypes,
29
+ versionPatterns,
30
+ scopeAliases: config.scopeAliases,
31
+ skipReasons: {
32
+ noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
33
+ noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`
34
+ }
35
+ });
36
+ if (decision.outcome === "skip") {
37
+ const skipped = {
38
+ status: "skipped",
39
+ commitCount: commits.length,
40
+ parsedCommitCount: decision.parsedCommitCount,
41
+ skipReason: decision.skipReason
42
+ };
43
+ if (tag !== void 0) {
44
+ skipped.previousTag = tag;
45
+ }
46
+ if (decision.unparseableCommits !== void 0) {
47
+ skipped.unparseableCommits = decision.unparseableCommits;
48
+ }
49
+ return skipped;
50
+ }
51
+ const { releaseType, parsedCommitCount, unparseableCommits } = decision;
52
+ const bump = bumpAllVersions([ROOT_PACKAGE_FILE], releaseType, dryRun);
53
+ const newTag = `${project.tagPrefix}${bump.newVersion}`;
54
+ const tagPattern = buildTagPattern([project.tagPrefix]);
55
+ const changelogFiles = generateChangelog(config, ROOT_CHANGELOG_PATH, newTag, dryRun, {
56
+ tagPattern,
57
+ includePaths: contributingPaths
58
+ });
59
+ const changelogJsonFiles = [];
60
+ if (config.changelogJson.enabled) {
61
+ const changelogJsonPath = resolveChangelogJsonPath(config, ROOT_CHANGELOG_PATH);
62
+ const entries = buildChangelogEntries(config, newTag, {
63
+ tagPattern,
64
+ includePaths: contributingPaths
65
+ });
66
+ if (!dryRun) {
67
+ writeChangelogJson(changelogJsonPath, entries);
68
+ }
69
+ changelogJsonFiles.push(changelogJsonPath);
70
+ }
71
+ const firstChangelogJsonPath = changelogJsonFiles[0];
72
+ if (withReleaseNotes === true && config.changelogJson.enabled && firstChangelogJsonPath !== void 0) {
73
+ const sectionOrder = deriveSectionOrder(workTypes);
74
+ writeReleaseNotesPreviews({
75
+ workspacePath: ROOT_CHANGELOG_PATH,
76
+ tag: newTag,
77
+ changelogJsonPath: firstChangelogJsonPath,
78
+ sectionOrder,
79
+ dryRun
80
+ });
81
+ }
82
+ tags.push(newTag);
83
+ modifiedFiles.push(ROOT_PACKAGE_FILE, ...changelogFiles, ...changelogJsonFiles);
84
+ const result = {
85
+ status: "released",
86
+ commitCount: commits.length,
87
+ parsedCommitCount,
88
+ releaseType,
89
+ currentVersion: bump.currentVersion,
90
+ newVersion: bump.newVersion,
91
+ tag: newTag,
92
+ bumpedFiles: bump.files,
93
+ changelogFiles,
94
+ commits
95
+ };
96
+ if (tag !== void 0) {
97
+ result.previousTag = tag;
98
+ }
99
+ if (unparseableCommits !== void 0) {
100
+ result.unparseableCommits = unparseableCommits;
101
+ }
102
+ if (bumpOverride !== void 0) {
103
+ result.bumpOverride = bumpOverride;
104
+ }
105
+ return result;
106
+ }
107
+ export {
108
+ releasePrepareProject
109
+ };