@williamthorsen/release-kit 5.4.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +12 -12
  3. package/dist/esm/.cache +1 -1
  4. package/dist/esm/assertCleanWorkingTree.js +9 -14
  5. package/dist/esm/bin/release-kit.js +202 -229
  6. package/dist/esm/buildChangelogEntries.d.ts +2 -2
  7. package/dist/esm/buildChangelogEntries.js +142 -149
  8. package/dist/esm/buildDependencyGraph.d.ts +1 -1
  9. package/dist/esm/buildDependencyGraph.js +60 -59
  10. package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -1
  11. package/dist/esm/buildEmptyReleaseEntry.js +12 -15
  12. package/dist/esm/buildReleaseSummary.d.ts +1 -1
  13. package/dist/esm/buildReleaseSummary.js +23 -26
  14. package/dist/esm/buildSyntheticChangelogEntry.d.ts +1 -1
  15. package/dist/esm/buildSyntheticChangelogEntry.js +9 -12
  16. package/dist/esm/bumpAllVersions.d.ts +1 -1
  17. package/dist/esm/bumpAllVersions.js +51 -52
  18. package/dist/esm/bumpVersion.d.ts +1 -1
  19. package/dist/esm/bumpVersion.js +25 -28
  20. package/dist/esm/changelogJsonFile.d.ts +1 -1
  21. package/dist/esm/changelogJsonFile.js +59 -62
  22. package/dist/esm/changelogJsonUtils.d.ts +1 -1
  23. package/dist/esm/changelogJsonUtils.js +35 -32
  24. package/dist/esm/changelogOverrides.d.ts +1 -1
  25. package/dist/esm/changelogOverrides.js +374 -377
  26. package/dist/esm/checkWorkTypesDrift.js +104 -99
  27. package/dist/esm/collectPolicyViolations.d.ts +2 -2
  28. package/dist/esm/collectPolicyViolations.js +11 -14
  29. package/dist/esm/commitCommand.js +48 -53
  30. package/dist/esm/compareVersions.js +20 -23
  31. package/dist/esm/createGithubRelease.js +56 -59
  32. package/dist/esm/createGithubReleaseCommand.js +21 -29
  33. package/dist/esm/createTags.js +57 -56
  34. package/dist/esm/decideRelease.d.ts +2 -2
  35. package/dist/esm/decideRelease.js +27 -41
  36. package/dist/esm/defaults.d.ts +3 -3
  37. package/dist/esm/defaults.js +36 -48
  38. package/dist/esm/deleteFileIfExists.js +10 -12
  39. package/dist/esm/deriveWorkspaceConfig.d.ts +1 -1
  40. package/dist/esm/deriveWorkspaceConfig.js +34 -36
  41. package/dist/esm/detectPackageManager.js +35 -37
  42. package/dist/esm/detectUndeclaredTagPrefixes.js +41 -40
  43. package/dist/esm/determineBumpFromCommits.d.ts +2 -2
  44. package/dist/esm/determineBumpFromCommits.js +25 -29
  45. package/dist/esm/determineBumpType.d.ts +1 -1
  46. package/dist/esm/determineBumpType.js +31 -32
  47. package/dist/esm/discoverWorkspaces.js +38 -40
  48. package/dist/esm/format.js +6 -11
  49. package/dist/esm/generateChangelogs.d.ts +1 -1
  50. package/dist/esm/generateChangelogs.js +13 -17
  51. package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
  52. package/dist/esm/getCommitsSinceTarget.js +54 -55
  53. package/dist/esm/hasPrettierConfig.js +33 -35
  54. package/dist/esm/help/prepareHelp.d.ts +2 -0
  55. package/dist/esm/help/prepareHelp.js +26 -0
  56. package/dist/esm/index.d.ts +4 -4
  57. package/dist/esm/index.js +2 -8
  58. package/dist/esm/init/checks.js +28 -32
  59. package/dist/esm/init/detectRepoType.js +12 -15
  60. package/dist/esm/init/initCommand.js +60 -54
  61. package/dist/esm/init/parseJsonRecord.js +9 -11
  62. package/dist/esm/init/scaffold.d.ts +1 -1
  63. package/dist/esm/init/scaffold.js +32 -38
  64. package/dist/esm/init/templates.d.ts +1 -1
  65. package/dist/esm/init/templates.js +16 -22
  66. package/dist/esm/injectReleaseNotesIntoReadme.js +42 -49
  67. package/dist/esm/injectSection.js +17 -27
  68. package/dist/esm/loadConfig.d.ts +1 -1
  69. package/dist/esm/loadConfig.js +206 -225
  70. package/dist/esm/parseCommitMessage.d.ts +1 -1
  71. package/dist/esm/parseCommitMessage.js +72 -76
  72. package/dist/esm/parseRequestedTags.js +6 -9
  73. package/dist/esm/prepareCommand.d.ts +32 -1
  74. package/dist/esm/prepareCommand.js +225 -271
  75. package/dist/esm/previewTagPrefixes.d.ts +1 -1
  76. package/dist/esm/previewTagPrefixes.js +101 -96
  77. package/dist/esm/propagateBumps.d.ts +2 -2
  78. package/dist/esm/propagateBumps.js +48 -50
  79. package/dist/esm/publish.d.ts +2 -2
  80. package/dist/esm/publish.js +22 -28
  81. package/dist/esm/publishCommand.js +75 -86
  82. package/dist/esm/pushCommand.js +33 -40
  83. package/dist/esm/pushRelease.d.ts +1 -1
  84. package/dist/esm/pushRelease.js +21 -24
  85. package/dist/esm/readCurrentVersion.js +13 -17
  86. package/dist/esm/releasePrepare.d.ts +1 -1
  87. package/dist/esm/releasePrepare.js +213 -231
  88. package/dist/esm/releasePrepareMono.d.ts +2 -2
  89. package/dist/esm/releasePrepareMono.js +434 -493
  90. package/dist/esm/releasePrepareProject.d.ts +2 -2
  91. package/dist/esm/releasePrepareProject.js +143 -153
  92. package/dist/esm/renderChangelogMarkdown.d.ts +1 -1
  93. package/dist/esm/renderChangelogMarkdown.js +20 -24
  94. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  95. package/dist/esm/renderReleaseNotes.js +50 -55
  96. package/dist/esm/reportPrepare.d.ts +1 -1
  97. package/dist/esm/reportPrepare.js +230 -231
  98. package/dist/esm/resolveCliffConfigPath.js +18 -23
  99. package/dist/esm/resolveCommandTags.d.ts +1 -1
  100. package/dist/esm/resolveCommandTags.js +47 -44
  101. package/dist/esm/resolveReleaseNotesConfig.d.ts +1 -1
  102. package/dist/esm/resolveReleaseNotesConfig.js +39 -41
  103. package/dist/esm/resolveReleaseTags.d.ts +1 -1
  104. package/dist/esm/resolveReleaseTags.js +39 -40
  105. package/dist/esm/runGitCliff.js +27 -30
  106. package/dist/esm/showTagPrefixesCommand.js +48 -70
  107. package/dist/esm/stripEmojiPrefix.js +2 -5
  108. package/dist/esm/stripScope.js +18 -21
  109. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  110. package/dist/esm/sync-labels/generateCommand.js +51 -52
  111. package/dist/esm/sync-labels/initCommand.js +46 -47
  112. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +1 -1
  113. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +46 -52
  114. package/dist/esm/sync-labels/presets.d.ts +1 -1
  115. package/dist/esm/sync-labels/presets.js +40 -43
  116. package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
  117. package/dist/esm/sync-labels/resolveLabels.js +28 -35
  118. package/dist/esm/sync-labels/syncCommand.js +30 -30
  119. package/dist/esm/sync-labels/templates.d.ts +1 -1
  120. package/dist/esm/sync-labels/templates.js +23 -26
  121. package/dist/esm/sync-labels/types.js +1 -0
  122. package/dist/esm/syncWorkTypes.js +83 -81
  123. package/dist/esm/tagCommand.js +12 -21
  124. package/dist/esm/typeGuards.js +4 -8
  125. package/dist/esm/types.js +66 -60
  126. package/dist/esm/validateConfig.d.ts +1 -1
  127. package/dist/esm/validateConfig.js +109 -115
  128. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +2 -2
  129. package/dist/esm/validateOnlyExcludesStrandedDependents.js +97 -91
  130. package/dist/esm/validateOverridesCommand.d.ts +2 -2
  131. package/dist/esm/validateOverridesCommand.js +111 -111
  132. package/dist/esm/workTypesData.js +57 -60
  133. package/dist/esm/workTypesUtils.js +16 -21
  134. package/dist/esm/writeReleaseNotesPreviews.js +55 -57
  135. package/package.json +2 -6
@@ -1,10 +1,7 @@
1
- function parseRequestedTags(flagValue) {
2
- if (flagValue === void 0) {
3
- return void 0;
4
- }
5
- const segments = flagValue.split(",").filter(Boolean);
6
- return segments.length === 0 ? void 0 : segments;
1
+ export function parseRequestedTags(flagValue) {
2
+ if (flagValue === undefined) {
3
+ return undefined;
4
+ }
5
+ const segments = flagValue.split(',').filter(Boolean);
6
+ return segments.length === 0 ? undefined : segments;
7
7
  }
8
- export {
9
- parseRequestedTags
10
- };
@@ -1,7 +1,38 @@
1
1
  import type { WriteResult } from '@williamthorsen/nmr-core';
2
- import type { ReleaseType } from './types.ts';
2
+ import type { ReleaseType } from './types.js';
3
3
  export declare const RELEASE_TAGS_FILE = "tmp/.release-tags";
4
4
  export declare const RELEASE_SUMMARY_FILE = "tmp/.release-summary";
5
+ export declare const prepareFlagSchema: {
6
+ dryRun: {
7
+ long: string;
8
+ type: "boolean";
9
+ };
10
+ force: {
11
+ long: string;
12
+ type: "boolean";
13
+ };
14
+ noGitChecks: {
15
+ long: string;
16
+ type: "boolean";
17
+ short: string;
18
+ };
19
+ bump: {
20
+ long: string;
21
+ type: "string";
22
+ };
23
+ setVersion: {
24
+ long: string;
25
+ type: "string";
26
+ };
27
+ only: {
28
+ long: string;
29
+ type: "string";
30
+ };
31
+ withReleaseNotes: {
32
+ long: string;
33
+ type: "boolean";
34
+ };
35
+ };
5
36
  export declare function parseArgs(argv: string[]): {
6
37
  dryRun: boolean;
7
38
  force: boolean;
@@ -1,4 +1,4 @@
1
- import { parseArgs as coreParseArgs, translateParseError, writeFileWithCheck } from "@williamthorsen/nmr-core";
1
+ import { parseArgsOrExit, reportError, writeFileWithCheck } from '@williamthorsen/nmr-core';
2
2
  import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
3
3
  import { buildDependencyGraph } from "./buildDependencyGraph.js";
4
4
  import { buildReleaseSummary } from "./buildReleaseSummary.js";
@@ -11,301 +11,255 @@ import { releasePrepareMono } from "./releasePrepareMono.js";
11
11
  import { reportPrepare } from "./reportPrepare.js";
12
12
  import { validateConfig } from "./validateConfig.js";
13
13
  import { validateOnlyExcludesStrandedDependents } from "./validateOnlyExcludesStrandedDependents.js";
14
- const RELEASE_TAGS_FILE = "tmp/.release-tags";
15
- const RELEASE_SUMMARY_FILE = "tmp/.release-summary";
16
- const VALID_BUMP_TYPES = ["major", "minor", "patch"];
14
+ export const RELEASE_TAGS_FILE = 'tmp/.release-tags';
15
+ export const RELEASE_SUMMARY_FILE = 'tmp/.release-summary';
16
+ const VALID_BUMP_TYPES = ['major', 'minor', 'patch'];
17
17
  const CANONICAL_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
18
18
  function isReleaseType(value) {
19
- return VALID_BUMP_TYPES.includes(value);
19
+ return VALID_BUMP_TYPES.includes(value);
20
20
  }
21
- function showHelp() {
22
- console.info(`
23
- Usage: npx @williamthorsen/release-kit prepare [options]
24
-
25
- Options:
26
- --dry-run Run without modifying any files
27
- --bump=major|minor|patch Override the bump type for all workspaces
28
- --set-version=X.Y.Z Set an explicit version; bypasses commit-derived bumps.
29
- Requires --only in monorepo mode (rejected when a 'project' block is configured).
30
- --force Release even when no commits or no bump-worthy commits exist
31
- since the last tag. Defaults to patch when --bump is not given;
32
- use --bump=X to release at a different level.
33
- --no-git-checks, -n Skip the clean-working-tree check
34
- --only=name1,name2 Only process the named workspaces (comma-separated, monorepo only;
35
- rejected when a 'project' block is configured)
36
- --with-release-notes Also write per-workspace release-notes previews under {workspacePath}/docs/
37
- (docs/README.v{version}.md and docs/RELEASE_NOTES.v{version}.md).
38
- Recommended .gitignore entry: packages/*/docs/*.v*.md (or docs/*.v*.md).
39
- --help Show this help message
40
- `);
41
- }
42
- const prepareFlagSchema = {
43
- dryRun: { long: "--dry-run", type: "boolean" },
44
- force: { long: "--force", type: "boolean" },
45
- noGitChecks: {
46
- long: "--no-git-checks",
47
- type: "boolean",
48
- short: "-n"
49
- },
50
- bump: { long: "--bump", type: "string" },
51
- setVersion: { long: "--set-version", type: "string" },
52
- only: { long: "--only", type: "string" },
53
- withReleaseNotes: { long: "--with-release-notes", type: "boolean" },
54
- help: { long: "--help", type: "boolean", short: "-h" }
21
+ export const prepareFlagSchema = {
22
+ dryRun: { long: '--dry-run', type: 'boolean' },
23
+ force: { long: '--force', type: 'boolean' },
24
+ noGitChecks: {
25
+ long: '--no-git-checks',
26
+ type: 'boolean',
27
+ short: '-n',
28
+ },
29
+ bump: { long: '--bump', type: 'string' },
30
+ setVersion: { long: '--set-version', type: 'string' },
31
+ only: { long: '--only', type: 'string' },
32
+ withReleaseNotes: { long: '--with-release-notes', type: 'boolean' },
55
33
  };
56
- function parseArgs(argv) {
57
- let parsed;
58
- try {
59
- parsed = coreParseArgs(argv, prepareFlagSchema);
60
- } catch (error) {
61
- throw new Error(translateParseError(error));
62
- }
63
- const { flags } = parsed;
64
- if (flags.help) {
65
- showHelp();
66
- process.exit(0);
67
- }
68
- let bumpOverride;
69
- if (flags.bump !== void 0) {
70
- if (!isReleaseType(flags.bump)) {
71
- throw new Error(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
34
+ export function parseArgs(argv) {
35
+ const { flags } = parseArgsOrExit(argv, prepareFlagSchema);
36
+ let bumpOverride;
37
+ if (flags.bump !== undefined) {
38
+ if (!isReleaseType(flags.bump)) {
39
+ reportError(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(', ')}`);
40
+ process.exit(1);
41
+ }
42
+ bumpOverride = flags.bump;
43
+ }
44
+ let setVersion;
45
+ if (flags.setVersion !== undefined) {
46
+ if (!CANONICAL_SEMVER_PATTERN.test(flags.setVersion)) {
47
+ reportError(`Invalid --set-version value "${flags.setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`);
48
+ process.exit(1);
49
+ }
50
+ setVersion = flags.setVersion;
51
+ }
52
+ let only;
53
+ if (flags.only !== undefined) {
54
+ only = flags.only.split(',');
72
55
  }
73
- bumpOverride = flags.bump;
74
- }
75
- let setVersion;
76
- if (flags.setVersion !== void 0) {
77
- if (!CANONICAL_SEMVER_PATTERN.test(flags.setVersion)) {
78
- throw new Error(
79
- `Invalid --set-version value "${flags.setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`
80
- );
56
+ if (setVersion !== undefined && bumpOverride !== undefined) {
57
+ reportError('--set-version cannot be combined with --bump');
58
+ process.exit(1);
59
+ }
60
+ if (setVersion !== undefined && flags.force) {
61
+ reportError('--set-version cannot be combined with --force');
62
+ process.exit(1);
81
63
  }
82
- setVersion = flags.setVersion;
83
- }
84
- let only;
85
- if (flags.only !== void 0) {
86
- only = flags.only.split(",");
87
- }
88
- if (setVersion !== void 0 && bumpOverride !== void 0) {
89
- throw new Error("--set-version cannot be combined with --bump");
90
- }
91
- if (setVersion !== void 0 && flags.force) {
92
- throw new Error("--set-version cannot be combined with --force");
93
- }
94
- return {
95
- dryRun: flags.dryRun,
96
- force: flags.force,
97
- noGitChecks: flags.noGitChecks,
98
- bumpOverride,
99
- only,
100
- setVersion,
101
- withReleaseNotes: flags.withReleaseNotes
102
- };
64
+ return {
65
+ dryRun: flags.dryRun,
66
+ force: flags.force,
67
+ noGitChecks: flags.noGitChecks,
68
+ bumpOverride,
69
+ only,
70
+ setVersion,
71
+ withReleaseNotes: flags.withReleaseNotes,
72
+ };
103
73
  }
104
- function writeReleaseTags(tags, dryRun) {
105
- if (tags.length === 0) {
106
- return void 0;
107
- }
108
- return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), {
109
- dryRun,
110
- overwrite: true
111
- });
74
+ export function writeReleaseTags(tags, dryRun) {
75
+ if (tags.length === 0) {
76
+ return undefined;
77
+ }
78
+ return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join('\n'), {
79
+ dryRun,
80
+ overwrite: true,
81
+ });
112
82
  }
113
- async function prepareCommand(argv) {
114
- let dryRun;
115
- let force;
116
- let noGitChecks;
117
- let bumpOverride;
118
- let only;
119
- let setVersion;
120
- let withReleaseNotes;
121
- try {
122
- ({ dryRun, force, noGitChecks, bumpOverride, only, setVersion, withReleaseNotes } = parseArgs(argv));
123
- } catch (error) {
124
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
125
- process.exit(1);
126
- }
127
- const options = {
128
- dryRun,
129
- force,
130
- ...bumpOverride === void 0 ? {} : { bumpOverride },
131
- ...setVersion === void 0 ? {} : { setVersion },
132
- ...withReleaseNotes ? { withReleaseNotes: true } : {}
133
- };
134
- if (dryRun) {
135
- console.info("\n\u{1F50D} DRY RUN \u2014 no files will be modified\n");
136
- }
137
- if (!dryRun && !noGitChecks) {
83
+ export async function prepareCommand(argv) {
84
+ const { dryRun, force, noGitChecks, bumpOverride, only, setVersion, withReleaseNotes } = parseArgs(argv);
85
+ const options = {
86
+ dryRun,
87
+ force,
88
+ ...(bumpOverride === undefined ? {} : { bumpOverride }),
89
+ ...(setVersion === undefined ? {} : { setVersion }),
90
+ ...(withReleaseNotes ? { withReleaseNotes: true } : {}),
91
+ };
92
+ if (dryRun) {
93
+ console.info('\n🔍 DRY RUN — no files will be modified\n');
94
+ }
95
+ if (!dryRun && !noGitChecks) {
96
+ try {
97
+ assertCleanWorkingTree();
98
+ }
99
+ catch (error) {
100
+ reportError(error instanceof Error ? error.message : String(error));
101
+ process.exit(1);
102
+ }
103
+ }
104
+ const userConfig = await loadAndValidateConfig();
105
+ let discoveredPaths;
138
106
  try {
139
- assertCleanWorkingTree();
140
- } catch (error) {
141
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
142
- process.exit(1);
107
+ discoveredPaths = await discoverWorkspaces();
108
+ }
109
+ catch (error) {
110
+ reportError(`Failed to discover workspaces: ${error instanceof Error ? error.message : String(error)}`);
111
+ process.exit(1);
112
+ }
113
+ if (discoveredPaths === undefined) {
114
+ runSinglePackageMode(userConfig, options, only, dryRun);
115
+ }
116
+ else {
117
+ runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun);
143
118
  }
144
- }
145
- const userConfig = await loadAndValidateConfig();
146
- let discoveredPaths;
147
- try {
148
- discoveredPaths = await discoverWorkspaces();
149
- } catch (error) {
150
- console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
151
- process.exit(1);
152
- }
153
- if (discoveredPaths === void 0) {
154
- runSinglePackageMode(userConfig, options, only, dryRun);
155
- } else {
156
- runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun);
157
- }
158
119
  }
159
120
  function runSinglePackageMode(userConfig, options, only, dryRun) {
160
- if (only !== void 0) {
161
- console.error("Error: --only is only supported for monorepo configurations");
162
- process.exit(1);
163
- }
164
- if (options.force && options.bumpOverride === void 0) {
165
- console.error(
166
- "Error: --force without --bump is only supported for monorepo configurations. Use --bump=major|minor|patch to set the level for a single-package release."
167
- );
168
- process.exit(1);
169
- }
170
- const config = mergeSinglePackageConfig(userConfig);
171
- runAndReport(() => releasePrepare(config, options), dryRun);
121
+ if (only !== undefined) {
122
+ reportError('--only is only supported for monorepo configurations');
123
+ process.exit(1);
124
+ }
125
+ if (options.force && options.bumpOverride === undefined) {
126
+ reportError('--force without --bump is only supported for monorepo configurations. ' +
127
+ 'Use --bump=major|minor|patch to set the level for a single-package release.');
128
+ process.exit(1);
129
+ }
130
+ const config = mergeSinglePackageConfig(userConfig);
131
+ runAndReport(() => releasePrepare(config, options), dryRun);
172
132
  }
173
133
  function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun) {
174
- let config;
175
- try {
176
- const rootPackage = readRootPackageVersion();
177
- config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
178
- } catch (error) {
179
- console.error(`Error resolving workspaces: ${error instanceof Error ? error.message : String(error)}`);
180
- process.exit(1);
181
- }
182
- if (setVersion !== void 0 && config.project !== void 0) {
183
- console.error(
184
- "Error: --set-version cannot be combined with a project release. --set-version operates on a single workspace; a project release rolls up every contributing workspace. To use --set-version, run on a config without a `project` block."
185
- );
186
- process.exit(1);
187
- }
188
- if (only !== void 0 && config.project !== void 0) {
189
- console.error(
190
- "Error: --only cannot be combined with a project release. To release a single workspace, use a config without a `project` block, or run a full `prepare` (no --only) to include the project release."
191
- );
192
- process.exit(1);
193
- }
194
- if (only !== void 0) {
195
- const knownNames = config.workspaces.map((w) => w.dir);
196
- for (const name of only) {
197
- if (!knownNames.includes(name)) {
198
- console.error(`Error: Unknown workspace "${name}". Known workspaces: ${knownNames.join(", ")}`);
134
+ let config;
135
+ try {
136
+ const rootPackage = readRootPackageVersion();
137
+ config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
138
+ }
139
+ catch (error) {
140
+ reportError(`Failed to resolve workspaces: ${error instanceof Error ? error.message : String(error)}`);
199
141
  process.exit(1);
200
- }
201
142
  }
202
- const graph = buildDependencyGraph(config.workspaces);
203
- const violations = validateOnlyExcludesStrandedDependents(config.workspaces, only, graph, (workspace) => {
204
- const tagPrefixes = [
205
- workspace.tagPrefix,
206
- ...workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []
207
- ];
208
- const result = getCommitsSinceTarget(tagPrefixes, workspace.paths);
209
- return { has: result.commits.length > 0, tag: result.tag };
210
- });
211
- if (violations !== void 0) {
212
- console.error("Error: --only excludes packages with changes that would be stranded by the release.");
213
- console.error("The following packages must be added to --only or have their dependencies removed:");
214
- for (const violation of violations) {
215
- const since = violation.tag ?? "the beginning";
216
- console.error(` - ${violation.dir} (downstream of ${violation.downstreamOf}; has commits since ${since})`);
217
- }
218
- console.error("Alternatively, run `release-kit prepare` without --only to release everything.");
219
- process.exit(1);
143
+ if (setVersion !== undefined && config.project !== undefined) {
144
+ reportError('--set-version cannot be combined with a project release. ' +
145
+ '--set-version operates on a single workspace; a project release rolls up every ' +
146
+ 'contributing workspace. To use --set-version, run on a config without a `project` block.');
147
+ process.exit(1);
148
+ }
149
+ if (only !== undefined && config.project !== undefined) {
150
+ reportError('--only cannot be combined with a project release. ' +
151
+ 'To release a single workspace, use a config without a `project` block, ' +
152
+ 'or run a full `prepare` (no --only) to include the project release.');
153
+ process.exit(1);
220
154
  }
221
- config.workspaces = config.workspaces.filter((w) => only.includes(w.dir));
222
- }
223
- if (setVersion !== void 0) {
224
- if (only === void 0) {
225
- console.error("Error: --set-version requires --only in monorepo mode");
226
- process.exit(1);
155
+ if (only !== undefined) {
156
+ const knownNames = config.workspaces.map((w) => w.dir);
157
+ for (const name of only) {
158
+ if (!knownNames.includes(name)) {
159
+ reportError(`Unknown workspace "${name}". Known workspaces: ${knownNames.join(', ')}`);
160
+ process.exit(1);
161
+ }
162
+ }
163
+ const graph = buildDependencyGraph(config.workspaces);
164
+ const violations = validateOnlyExcludesStrandedDependents(config.workspaces, only, graph, (workspace) => {
165
+ const tagPrefixes = [
166
+ workspace.tagPrefix,
167
+ ...(workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []),
168
+ ];
169
+ const result = getCommitsSinceTarget(tagPrefixes, workspace.paths);
170
+ return { has: result.commits.length > 0, tag: result.tag };
171
+ });
172
+ if (violations !== undefined) {
173
+ reportError('--only excludes packages with changes that would be stranded by the release.');
174
+ process.stderr.write('The following packages must be added to --only or have their dependencies removed:\n');
175
+ for (const violation of violations) {
176
+ const since = violation.tag ?? 'the beginning';
177
+ process.stderr.write(` - ${violation.dir} (downstream of ${violation.downstreamOf}; has commits since ${since})\n`);
178
+ }
179
+ process.stderr.write('Alternatively, run `release-kit prepare` without --only to release everything.\n');
180
+ process.exit(1);
181
+ }
182
+ config.workspaces = config.workspaces.filter((w) => only.includes(w.dir));
227
183
  }
228
- if (config.workspaces.length !== 1) {
229
- console.error(
230
- `Error: --set-version requires --only to match exactly one workspace; matched ${config.workspaces.length}`
231
- );
232
- process.exit(1);
184
+ if (setVersion !== undefined) {
185
+ if (only === undefined) {
186
+ reportError('--set-version requires --only in monorepo mode');
187
+ process.exit(1);
188
+ }
189
+ if (config.workspaces.length !== 1) {
190
+ reportError(`--set-version requires --only to match exactly one workspace; matched ${config.workspaces.length}`);
191
+ process.exit(1);
192
+ }
233
193
  }
234
- }
235
- runAndReport(() => releasePrepareMono(config, options), dryRun);
194
+ runAndReport(() => releasePrepareMono(config, options), dryRun);
236
195
  }
237
196
  async function loadAndValidateConfig() {
238
- let rawConfig;
239
- try {
240
- rawConfig = await loadConfig();
241
- } catch (error) {
242
- console.error(`Error loading config: ${error instanceof Error ? error.message : String(error)}`);
243
- process.exit(1);
244
- }
245
- if (rawConfig === void 0) {
246
- return void 0;
247
- }
248
- const { config, errors, warnings } = validateConfig(rawConfig);
249
- if (errors.length > 0) {
250
- console.error("Invalid config:");
251
- for (const err of errors) {
252
- console.error(` \u274C ${err}`);
197
+ let rawConfig;
198
+ try {
199
+ rawConfig = await loadConfig();
200
+ }
201
+ catch (error) {
202
+ reportError(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`);
203
+ process.exit(1);
204
+ }
205
+ if (rawConfig === undefined) {
206
+ return undefined;
207
+ }
208
+ const { config, errors, warnings } = validateConfig(rawConfig);
209
+ if (errors.length > 0) {
210
+ process.stderr.write('Invalid config:\n');
211
+ for (const err of errors) {
212
+ process.stderr.write(` ❌ ${err}\n`);
213
+ }
214
+ process.exit(1);
215
+ }
216
+ for (const warning of warnings) {
217
+ console.warn(` ⚠️ ${warning}`);
253
218
  }
254
- process.exit(1);
255
- }
256
- for (const warning of warnings) {
257
- console.warn(` \u26A0\uFE0F ${warning}`);
258
- }
259
- return config;
219
+ return config;
260
220
  }
261
221
  function runAndReport(execute, dryRun) {
262
- let result;
263
- try {
264
- result = execute();
265
- } catch (error) {
266
- console.error(error instanceof Error ? error.message : String(error));
267
- process.exit(1);
268
- }
269
- process.stdout.write(reportPrepare(result) + "\n");
270
- const writeResult = writeReleaseTags(result.tags, dryRun);
271
- if (writeResult?.outcome === "failed") {
272
- console.error(`Error writing release tags: ${writeResult.error ?? "unknown error"}`);
273
- process.exit(1);
274
- }
275
- if (writeResult) {
276
- if (dryRun) {
277
- console.info(dim(` [dry-run] Would write ${RELEASE_TAGS_FILE}: ${result.tags.join(" ")}`));
278
- } else {
279
- console.info(dim(` Wrote ${RELEASE_TAGS_FILE}: ${result.tags.join(" ")}`));
280
- console.info(dim(`
281
- Release tags file: ${RELEASE_TAGS_FILE}`));
222
+ let result;
223
+ try {
224
+ result = execute();
282
225
  }
283
- }
284
- const summary = buildReleaseSummary(result);
285
- if (summary.length > 0) {
286
- const summaryResult = writeFileWithCheck(RELEASE_SUMMARY_FILE, summary, {
287
- dryRun,
288
- overwrite: true
289
- });
290
- if (summaryResult.outcome === "failed") {
291
- console.error(`Error writing release summary: ${summaryResult.error ?? "unknown error"}`);
292
- process.exit(1);
226
+ catch (error) {
227
+ reportError(error instanceof Error ? error.message : String(error));
228
+ process.exit(1);
293
229
  }
294
- if (dryRun) {
295
- console.info(dim(` [dry-run] Would write ${RELEASE_SUMMARY_FILE}`));
296
- } else {
297
- console.info(dim(` Wrote ${RELEASE_SUMMARY_FILE}`));
230
+ process.stdout.write(reportPrepare(result) + '\n');
231
+ const writeResult = writeReleaseTags(result.tags, dryRun);
232
+ if (writeResult?.outcome === 'failed') {
233
+ reportError(`Failed to write release tags: ${writeResult.error ?? 'unknown error'}`);
234
+ process.exit(1);
235
+ }
236
+ if (writeResult) {
237
+ if (dryRun) {
238
+ console.info(dim(` [dry-run] Would write ${RELEASE_TAGS_FILE}: ${result.tags.join(' ')}`));
239
+ }
240
+ else {
241
+ console.info(dim(` Wrote ${RELEASE_TAGS_FILE}: ${result.tags.join(' ')}`));
242
+ console.info(dim(`\n Release tags file: ${RELEASE_TAGS_FILE}`));
243
+ }
244
+ }
245
+ const summary = buildReleaseSummary(result);
246
+ if (summary.length > 0) {
247
+ const summaryResult = writeFileWithCheck(RELEASE_SUMMARY_FILE, summary, {
248
+ dryRun,
249
+ overwrite: true,
250
+ });
251
+ if (summaryResult.outcome === 'failed') {
252
+ reportError(`Failed to write release summary: ${summaryResult.error ?? 'unknown error'}`);
253
+ process.exit(1);
254
+ }
255
+ if (dryRun) {
256
+ console.info(dim(` [dry-run] Would write ${RELEASE_SUMMARY_FILE}`));
257
+ }
258
+ else {
259
+ console.info(dim(` Wrote ${RELEASE_SUMMARY_FILE}`));
260
+ }
261
+ }
262
+ if (writeResult && !dryRun) {
263
+ process.stderr.write(`\nRun 'release-kit commit' to create the release commit.\n`);
298
264
  }
299
- }
300
- if (writeResult && !dryRun) {
301
- console.error(`
302
- Run 'release-kit commit' to create the release commit.`);
303
- }
304
265
  }
305
- export {
306
- RELEASE_SUMMARY_FILE,
307
- RELEASE_TAGS_FILE,
308
- parseArgs,
309
- prepareCommand,
310
- writeReleaseTags
311
- };
@@ -1,4 +1,4 @@
1
- import type { UndeclaredTagPrefix } from './detectUndeclaredTagPrefixes.ts';
1
+ import type { UndeclaredTagPrefix } from './detectUndeclaredTagPrefixes.js';
2
2
  export interface TagPrefixPreviewRow {
3
3
  workspacePath: string;
4
4
  dir: string;