@williamthorsen/release-kit 6.0.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 (134) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +3 -3
  3. package/dist/esm/.cache +1 -1
  4. package/dist/esm/assertCleanWorkingTree.js +9 -14
  5. package/dist/esm/bin/release-kit.js +200 -199
  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 -102
  27. package/dist/esm/collectPolicyViolations.d.ts +2 -2
  28. package/dist/esm/collectPolicyViolations.js +11 -14
  29. package/dist/esm/commitCommand.js +48 -46
  30. package/dist/esm/compareVersions.js +20 -23
  31. package/dist/esm/createGithubRelease.js +56 -59
  32. package/dist/esm/createGithubReleaseCommand.js +21 -23
  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.js +4 -8
  55. package/dist/esm/index.d.ts +4 -4
  56. package/dist/esm/index.js +2 -8
  57. package/dist/esm/init/checks.js +28 -32
  58. package/dist/esm/init/detectRepoType.js +12 -15
  59. package/dist/esm/init/initCommand.js +60 -54
  60. package/dist/esm/init/parseJsonRecord.js +9 -11
  61. package/dist/esm/init/scaffold.d.ts +1 -1
  62. package/dist/esm/init/scaffold.js +32 -38
  63. package/dist/esm/init/templates.d.ts +1 -1
  64. package/dist/esm/init/templates.js +16 -22
  65. package/dist/esm/injectReleaseNotesIntoReadme.js +42 -49
  66. package/dist/esm/injectSection.js +17 -27
  67. package/dist/esm/loadConfig.d.ts +1 -1
  68. package/dist/esm/loadConfig.js +206 -225
  69. package/dist/esm/parseCommitMessage.d.ts +1 -1
  70. package/dist/esm/parseCommitMessage.js +72 -76
  71. package/dist/esm/parseRequestedTags.js +6 -9
  72. package/dist/esm/prepareCommand.d.ts +1 -1
  73. package/dist/esm/prepareCommand.js +225 -235
  74. package/dist/esm/previewTagPrefixes.d.ts +1 -1
  75. package/dist/esm/previewTagPrefixes.js +101 -96
  76. package/dist/esm/propagateBumps.d.ts +2 -2
  77. package/dist/esm/propagateBumps.js +48 -50
  78. package/dist/esm/publish.d.ts +2 -2
  79. package/dist/esm/publish.js +22 -28
  80. package/dist/esm/publishCommand.js +75 -80
  81. package/dist/esm/pushCommand.js +33 -34
  82. package/dist/esm/pushRelease.d.ts +1 -1
  83. package/dist/esm/pushRelease.js +21 -24
  84. package/dist/esm/readCurrentVersion.js +13 -17
  85. package/dist/esm/releasePrepare.d.ts +1 -1
  86. package/dist/esm/releasePrepare.js +213 -231
  87. package/dist/esm/releasePrepareMono.d.ts +2 -2
  88. package/dist/esm/releasePrepareMono.js +434 -494
  89. package/dist/esm/releasePrepareProject.d.ts +2 -2
  90. package/dist/esm/releasePrepareProject.js +143 -153
  91. package/dist/esm/renderChangelogMarkdown.d.ts +1 -1
  92. package/dist/esm/renderChangelogMarkdown.js +20 -24
  93. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  94. package/dist/esm/renderReleaseNotes.js +50 -55
  95. package/dist/esm/reportPrepare.d.ts +1 -1
  96. package/dist/esm/reportPrepare.js +230 -231
  97. package/dist/esm/resolveCliffConfigPath.js +18 -23
  98. package/dist/esm/resolveCommandTags.d.ts +1 -1
  99. package/dist/esm/resolveCommandTags.js +47 -45
  100. package/dist/esm/resolveReleaseNotesConfig.d.ts +1 -1
  101. package/dist/esm/resolveReleaseNotesConfig.js +39 -43
  102. package/dist/esm/resolveReleaseTags.d.ts +1 -1
  103. package/dist/esm/resolveReleaseTags.js +39 -40
  104. package/dist/esm/runGitCliff.js +27 -30
  105. package/dist/esm/showTagPrefixesCommand.js +48 -70
  106. package/dist/esm/stripEmojiPrefix.js +2 -5
  107. package/dist/esm/stripScope.js +18 -21
  108. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  109. package/dist/esm/sync-labels/generateCommand.js +51 -53
  110. package/dist/esm/sync-labels/initCommand.js +46 -48
  111. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +1 -1
  112. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +46 -52
  113. package/dist/esm/sync-labels/presets.d.ts +1 -1
  114. package/dist/esm/sync-labels/presets.js +40 -43
  115. package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
  116. package/dist/esm/sync-labels/resolveLabels.js +28 -35
  117. package/dist/esm/sync-labels/syncCommand.js +30 -31
  118. package/dist/esm/sync-labels/templates.d.ts +1 -1
  119. package/dist/esm/sync-labels/templates.js +23 -26
  120. package/dist/esm/sync-labels/types.js +1 -0
  121. package/dist/esm/syncWorkTypes.js +83 -84
  122. package/dist/esm/tagCommand.js +12 -14
  123. package/dist/esm/typeGuards.js +4 -8
  124. package/dist/esm/types.js +66 -60
  125. package/dist/esm/validateConfig.d.ts +1 -1
  126. package/dist/esm/validateConfig.js +109 -115
  127. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +2 -2
  128. package/dist/esm/validateOnlyExcludesStrandedDependents.js +97 -91
  129. package/dist/esm/validateOverridesCommand.d.ts +2 -2
  130. package/dist/esm/validateOverridesCommand.js +110 -111
  131. package/dist/esm/workTypesData.js +57 -60
  132. package/dist/esm/workTypesUtils.js +16 -21
  133. package/dist/esm/writeReleaseNotesPreviews.js +55 -57
  134. package/package.json +2 -2
@@ -1,256 +1,237 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import {
4
- DEFAULT_CHANGELOG_JSON_CONFIG,
5
- DEFAULT_PROJECT_TAG_PREFIX,
6
- DEFAULT_RELEASE_NOTES_CONFIG,
7
- DEFAULT_VERSION_PATTERNS,
8
- DEFAULT_WORK_TYPES
9
- } from "./defaults.js";
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_PROJECT_TAG_PREFIX, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from "./defaults.js";
10
4
  import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
11
5
  import { isRecord } from "./typeGuards.js";
12
- const ROOT_PACKAGE_JSON_PATH = "package.json";
13
- function readRootPackageVersion() {
14
- const absolutePath = path.resolve(process.cwd(), ROOT_PACKAGE_JSON_PATH);
15
- if (!existsSync(absolutePath)) {
16
- return { exists: false, version: void 0 };
17
- }
18
- let contents;
19
- try {
20
- contents = readFileSync(absolutePath, "utf8");
21
- } catch (error) {
22
- throw new Error(
23
- `Failed to read root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`
24
- );
25
- }
26
- let parsed;
27
- try {
28
- parsed = JSON.parse(contents);
29
- } catch (error) {
30
- throw new Error(
31
- `Failed to parse root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`
32
- );
33
- }
34
- if (!isRecord(parsed)) {
35
- return { exists: true, version: void 0 };
36
- }
37
- return { exists: true, version: typeof parsed.version === "string" ? parsed.version : void 0 };
6
+ export const ROOT_PACKAGE_JSON_PATH = 'package.json';
7
+ export function readRootPackageVersion() {
8
+ const absolutePath = path.resolve(process.cwd(), ROOT_PACKAGE_JSON_PATH);
9
+ if (!existsSync(absolutePath)) {
10
+ return { exists: false, version: undefined };
11
+ }
12
+ let contents;
13
+ try {
14
+ contents = readFileSync(absolutePath, 'utf8');
15
+ }
16
+ catch (error) {
17
+ throw new Error(`Failed to read root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`);
18
+ }
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(contents);
22
+ }
23
+ catch (error) {
24
+ throw new Error(`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`);
25
+ }
26
+ if (!isRecord(parsed)) {
27
+ return { exists: true, version: undefined };
28
+ }
29
+ return { exists: true, version: typeof parsed.version === 'string' ? parsed.version : undefined };
38
30
  }
39
- const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
40
- async function loadConfig() {
41
- const absoluteConfigPath = path.resolve(process.cwd(), CONFIG_FILE_PATH);
42
- if (!existsSync(absoluteConfigPath)) {
43
- return void 0;
44
- }
45
- const { createJiti } = await import("jiti");
46
- const jiti = createJiti(import.meta.url);
47
- const imported = await jiti.import(absoluteConfigPath);
48
- if (!isRecord(imported)) {
49
- throw new Error(`Config file must export an object, got ${Array.isArray(imported) ? "array" : typeof imported}`);
50
- }
51
- const resolved = imported.default ?? imported.config;
52
- if (resolved === void 0) {
53
- throw new Error(
54
- "Config file must have a default export or a named `config` export (e.g., `export default { ... }` or `export const config = { ... }`)"
55
- );
56
- }
57
- return resolved;
31
+ export const CONFIG_FILE_PATH = '.config/release-kit.config.ts';
32
+ export async function loadConfig() {
33
+ const absoluteConfigPath = path.resolve(process.cwd(), CONFIG_FILE_PATH);
34
+ if (!existsSync(absoluteConfigPath)) {
35
+ return undefined;
36
+ }
37
+ const { createJiti } = await import('jiti');
38
+ const jiti = createJiti(import.meta.url);
39
+ const imported = await jiti.import(absoluteConfigPath);
40
+ if (!isRecord(imported)) {
41
+ throw new Error(`Config file must export an object, got ${Array.isArray(imported) ? 'array' : typeof imported}`);
42
+ }
43
+ const resolved = imported.default ?? imported.config;
44
+ if (resolved === undefined) {
45
+ throw new Error('Config file must have a default export or a named `config` export (e.g., `export default { ... }` or `export const config = { ... }`)');
46
+ }
47
+ return resolved;
58
48
  }
59
- function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
60
- let workspaces = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
61
- assertUniqueTagPrefixes(workspaces);
62
- if (userConfig?.workspaces !== void 0) {
63
- const overrides = new Map(userConfig.workspaces.map((w) => [w.dir, w]));
64
- workspaces = workspaces.filter((w) => {
65
- const override = overrides.get(w.dir);
66
- return override?.shouldExclude !== true;
67
- }).map((w) => {
68
- const override = overrides.get(w.dir);
69
- if (override?.legacyIdentities === void 0) {
70
- return w;
71
- }
72
- assertLegacyIdentityDoesNotMatchCurrent(w.dir, w.name, w.tagPrefix, override.legacyIdentities);
73
- return { ...w, legacyIdentities: override.legacyIdentities.map((identity) => ({ ...identity })) };
74
- });
75
- }
76
- if (userConfig?.retiredPackages !== void 0) {
77
- assertRetiredPackagesDoNotCollideWithActive(workspaces, userConfig.retiredPackages);
78
- }
79
- const project = resolveProjectConfig(userConfig?.project, rootPackage);
80
- const workTypes = resolveWorkTypes(userConfig?.workTypes);
81
- const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
82
- const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
83
- const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
84
- assertNoTagPrefixCollisions(workspaces, userConfig?.retiredPackages, project);
85
- const result = {
86
- workspaces,
87
- workTypes,
88
- versionPatterns,
89
- changelogJson,
90
- releaseNotes
91
- };
92
- if (project !== void 0) {
93
- result.project = project;
94
- }
95
- applyOptionalPassthroughFields(result, userConfig);
96
- return result;
49
+ export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
50
+ let workspaces = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
51
+ assertUniqueTagPrefixes(workspaces);
52
+ if (userConfig?.workspaces !== undefined) {
53
+ const overrides = new Map(userConfig.workspaces.map((w) => [w.dir, w]));
54
+ workspaces = workspaces
55
+ .filter((w) => {
56
+ const override = overrides.get(w.dir);
57
+ return override?.shouldExclude !== true;
58
+ })
59
+ .map((w) => {
60
+ const override = overrides.get(w.dir);
61
+ if (override?.legacyIdentities === undefined) {
62
+ return w;
63
+ }
64
+ assertLegacyIdentityDoesNotMatchCurrent(w.dir, w.name, w.tagPrefix, override.legacyIdentities);
65
+ return { ...w, legacyIdentities: override.legacyIdentities.map((identity) => ({ ...identity })) };
66
+ });
67
+ }
68
+ if (userConfig?.retiredPackages !== undefined) {
69
+ assertRetiredPackagesDoNotCollideWithActive(workspaces, userConfig.retiredPackages);
70
+ }
71
+ const project = resolveProjectConfig(userConfig?.project, rootPackage);
72
+ const workTypes = resolveWorkTypes(userConfig?.workTypes);
73
+ const versionPatterns = userConfig?.versionPatterns === undefined ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
74
+ const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
75
+ const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
76
+ assertNoTagPrefixCollisions(workspaces, userConfig?.retiredPackages, project);
77
+ const result = {
78
+ workspaces,
79
+ workTypes,
80
+ versionPatterns,
81
+ changelogJson,
82
+ releaseNotes,
83
+ };
84
+ if (project !== undefined) {
85
+ result.project = project;
86
+ }
87
+ applyOptionalPassthroughFields(result, userConfig);
88
+ return result;
97
89
  }
98
90
  function applyOptionalPassthroughFields(result, userConfig) {
99
- if (userConfig?.formatCommand !== void 0) {
100
- result.formatCommand = userConfig.formatCommand;
101
- }
102
- if (userConfig?.cliffConfigPath !== void 0) {
103
- result.cliffConfigPath = userConfig.cliffConfigPath;
104
- }
105
- if (userConfig?.scopeAliases !== void 0) {
106
- result.scopeAliases = userConfig.scopeAliases;
107
- }
108
- if (userConfig?.breakingPolicies !== void 0) {
109
- result.breakingPolicies = userConfig.breakingPolicies;
110
- }
91
+ if (userConfig?.formatCommand !== undefined) {
92
+ result.formatCommand = userConfig.formatCommand;
93
+ }
94
+ if (userConfig?.cliffConfigPath !== undefined) {
95
+ result.cliffConfigPath = userConfig.cliffConfigPath;
96
+ }
97
+ if (userConfig?.scopeAliases !== undefined) {
98
+ result.scopeAliases = userConfig.scopeAliases;
99
+ }
100
+ if (userConfig?.breakingPolicies !== undefined) {
101
+ result.breakingPolicies = userConfig.breakingPolicies;
102
+ }
111
103
  }
112
- function mergeSinglePackageConfig(userConfig) {
113
- if (userConfig?.project !== void 0) {
114
- throw new Error("project block is not supported in single-package mode");
115
- }
116
- const workTypes = resolveWorkTypes(userConfig?.workTypes);
117
- const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
118
- const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
119
- const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
120
- const result = {
121
- tagPrefix: "v",
122
- packageFiles: ["package.json"],
123
- changelogPaths: ["."],
124
- workTypes,
125
- versionPatterns,
126
- changelogJson,
127
- releaseNotes
128
- };
129
- applyOptionalPassthroughFields(result, userConfig);
130
- return result;
104
+ export function mergeSinglePackageConfig(userConfig) {
105
+ if (userConfig?.project !== undefined) {
106
+ throw new Error('project block is not supported in single-package mode');
107
+ }
108
+ const workTypes = resolveWorkTypes(userConfig?.workTypes);
109
+ const versionPatterns = userConfig?.versionPatterns === undefined ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
110
+ const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
111
+ const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
112
+ const result = {
113
+ tagPrefix: 'v',
114
+ packageFiles: ['package.json'],
115
+ changelogPaths: ['.'],
116
+ workTypes,
117
+ versionPatterns,
118
+ changelogJson,
119
+ releaseNotes,
120
+ };
121
+ applyOptionalPassthroughFields(result, userConfig);
122
+ return result;
131
123
  }
132
- function resolveWorkTypes(userWorkTypes) {
133
- return userWorkTypes === void 0 ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userWorkTypes };
124
+ export function resolveWorkTypes(userWorkTypes) {
125
+ return userWorkTypes === undefined ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userWorkTypes };
134
126
  }
135
127
  function mergeChangelogJsonConfig(partial) {
136
- if (partial === void 0) {
137
- return { ...DEFAULT_CHANGELOG_JSON_CONFIG };
138
- }
139
- return {
140
- enabled: partial.enabled ?? DEFAULT_CHANGELOG_JSON_CONFIG.enabled,
141
- outputPath: partial.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
142
- devOnlySections: partial.devOnlySections ?? [...DEFAULT_CHANGELOG_JSON_CONFIG.devOnlySections]
143
- };
128
+ if (partial === undefined) {
129
+ return { ...DEFAULT_CHANGELOG_JSON_CONFIG };
130
+ }
131
+ return {
132
+ enabled: partial.enabled ?? DEFAULT_CHANGELOG_JSON_CONFIG.enabled,
133
+ outputPath: partial.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
134
+ devOnlySections: partial.devOnlySections ?? [...DEFAULT_CHANGELOG_JSON_CONFIG.devOnlySections],
135
+ };
144
136
  }
145
137
  function assertLegacyIdentityDoesNotMatchCurrent(dir, currentName, currentTagPrefix, legacyIdentities) {
146
- for (const identity of legacyIdentities) {
147
- if (identity.name === currentName && identity.tagPrefix === currentTagPrefix) {
148
- throw new Error(
149
- `Workspace '${dir}': legacyIdentities must not match the current identity (name='${currentName}', tagPrefix='${currentTagPrefix}'). The current identity is always searched; listing it again is a no-op.`
150
- );
138
+ for (const identity of legacyIdentities) {
139
+ if (identity.name === currentName && identity.tagPrefix === currentTagPrefix) {
140
+ throw new Error(`Workspace '${dir}': legacyIdentities must not match the current identity ` +
141
+ `(name='${currentName}', tagPrefix='${currentTagPrefix}'). ` +
142
+ 'The current identity is always searched; listing it again is a no-op.');
143
+ }
151
144
  }
152
- }
153
145
  }
154
146
  function assertRetiredPackagesDoNotCollideWithActive(workspaces, retiredPackages) {
155
- const workspaceByDerivedPrefix = /* @__PURE__ */ new Map();
156
- for (const workspace of workspaces) {
157
- workspaceByDerivedPrefix.set(workspace.tagPrefix, workspace);
158
- }
159
- for (const retired of retiredPackages) {
160
- const active = workspaceByDerivedPrefix.get(retired.tagPrefix);
161
- if (active !== void 0) {
162
- throw new Error(
163
- `retiredPackages: tagPrefix '${retired.tagPrefix}' collides with active workspace '${active.dir}' (derived prefix '${active.tagPrefix}'). A retired package's tagPrefix cannot belong to an active workspace.`
164
- );
147
+ const workspaceByDerivedPrefix = new Map();
148
+ for (const workspace of workspaces) {
149
+ workspaceByDerivedPrefix.set(workspace.tagPrefix, workspace);
150
+ }
151
+ for (const retired of retiredPackages) {
152
+ const active = workspaceByDerivedPrefix.get(retired.tagPrefix);
153
+ if (active !== undefined) {
154
+ throw new Error(`retiredPackages: tagPrefix '${retired.tagPrefix}' collides with active workspace '${active.dir}' ` +
155
+ `(derived prefix '${active.tagPrefix}'). A retired package's tagPrefix cannot belong to an active workspace.`);
156
+ }
165
157
  }
166
- }
167
158
  }
168
159
  function resolveProjectConfig(userProject, rootPackage) {
169
- if (userProject === void 0) {
170
- return void 0;
171
- }
172
- if (rootPackage === void 0 || !rootPackage.exists) {
173
- throw new Error(
174
- `project block requires a root ${ROOT_PACKAGE_JSON_PATH}; create one with a 'version' field at the repo root`
175
- );
176
- }
177
- if (rootPackage.version === void 0) {
178
- throw new Error(
179
- `project block requires root ${ROOT_PACKAGE_JSON_PATH} to have a 'version' field; add a 'version' field to your root package.json`
180
- );
181
- }
182
- return { tagPrefix: userProject.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX };
160
+ if (userProject === undefined) {
161
+ return undefined;
162
+ }
163
+ if (rootPackage === undefined || !rootPackage.exists) {
164
+ throw new Error(`project block requires a root ${ROOT_PACKAGE_JSON_PATH}; create one with a 'version' field at the repo root`);
165
+ }
166
+ if (rootPackage.version === undefined) {
167
+ throw new Error(`project block requires root ${ROOT_PACKAGE_JSON_PATH} to have a 'version' field; add a 'version' field to your root package.json`);
168
+ }
169
+ return { tagPrefix: userProject.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX };
183
170
  }
184
171
  function assertNoTagPrefixCollisions(workspaces, retiredPackages, project) {
185
- const sources = [];
186
- for (const workspace of workspaces) {
187
- const owner = `ws:${workspace.dir}`;
188
- sources.push({ prefix: workspace.tagPrefix, label: `workspace '${workspace.dir}'`, owner });
189
- for (const identity of workspace.legacyIdentities ?? []) {
190
- sources.push({
191
- prefix: identity.tagPrefix,
192
- label: `workspace '${workspace.dir}' legacyIdentities entry (name='${identity.name}')`,
193
- owner
194
- });
172
+ const sources = [];
173
+ for (const workspace of workspaces) {
174
+ const owner = `ws:${workspace.dir}`;
175
+ sources.push({ prefix: workspace.tagPrefix, label: `workspace '${workspace.dir}'`, owner });
176
+ for (const identity of workspace.legacyIdentities ?? []) {
177
+ sources.push({
178
+ prefix: identity.tagPrefix,
179
+ label: `workspace '${workspace.dir}' legacyIdentities entry (name='${identity.name}')`,
180
+ owner,
181
+ });
182
+ }
195
183
  }
196
- }
197
- for (const [index, retired] of (retiredPackages ?? []).entries()) {
198
- sources.push({
199
- prefix: retired.tagPrefix,
200
- label: `retiredPackages entry (name='${retired.name}')`,
201
- owner: `retired:${index}`
202
- });
203
- }
204
- if (project !== void 0) {
205
- sources.push({ prefix: project.tagPrefix, label: "project", owner: "project" });
206
- }
207
- for (let i = 0; i < sources.length; i++) {
208
- for (let j = i + 1; j < sources.length; j++) {
209
- const a = sources[i];
210
- const b = sources[j];
211
- if (a === void 0 || b === void 0) continue;
212
- if (a.owner === b.owner) continue;
213
- if (isPrefixCollision(a.prefix, b.prefix)) {
214
- throw new Error(
215
- `Tag prefix collision: '${a.prefix}' (${a.label}) and '${b.prefix}' (${b.label}). One prefix is identical to or a strict prefix of the other; this would cause \`git describe --match=<prefix>*\` to return cross-matches.`
216
- );
217
- }
184
+ for (const [index, retired] of (retiredPackages ?? []).entries()) {
185
+ sources.push({
186
+ prefix: retired.tagPrefix,
187
+ label: `retiredPackages entry (name='${retired.name}')`,
188
+ owner: `retired:${index}`,
189
+ });
190
+ }
191
+ if (project !== undefined) {
192
+ sources.push({ prefix: project.tagPrefix, label: 'project', owner: 'project' });
193
+ }
194
+ for (let i = 0; i < sources.length; i++) {
195
+ for (let j = i + 1; j < sources.length; j++) {
196
+ const a = sources[i];
197
+ const b = sources[j];
198
+ if (a === undefined || b === undefined)
199
+ continue;
200
+ if (a.owner === b.owner)
201
+ continue;
202
+ if (isPrefixCollision(a.prefix, b.prefix)) {
203
+ throw new Error(`Tag prefix collision: '${a.prefix}' (${a.label}) and '${b.prefix}' (${b.label}). ` +
204
+ 'One prefix is identical to or a strict prefix of the other; ' +
205
+ 'this would cause `git describe --match=<prefix>*` to return cross-matches.');
206
+ }
207
+ }
218
208
  }
219
- }
220
209
  }
221
210
  function isPrefixCollision(a, b) {
222
- return a === b || a.startsWith(b) || b.startsWith(a);
211
+ return a === b || a.startsWith(b) || b.startsWith(a);
223
212
  }
224
213
  function assertUniqueTagPrefixes(workspaces) {
225
- const pathsByPrefix = /* @__PURE__ */ new Map();
226
- for (const workspace of workspaces) {
227
- const existing = pathsByPrefix.get(workspace.tagPrefix);
228
- if (existing === void 0) {
229
- pathsByPrefix.set(workspace.tagPrefix, [workspace.workspacePath]);
230
- } else {
231
- existing.push(workspace.workspacePath);
214
+ const pathsByPrefix = new Map();
215
+ for (const workspace of workspaces) {
216
+ const existing = pathsByPrefix.get(workspace.tagPrefix);
217
+ if (existing === undefined) {
218
+ pathsByPrefix.set(workspace.tagPrefix, [workspace.workspacePath]);
219
+ }
220
+ else {
221
+ existing.push(workspace.workspacePath);
222
+ }
232
223
  }
233
- }
234
- for (const [prefix, paths] of pathsByPrefix) {
235
- if (paths.length > 1) {
236
- throw new Error(`Duplicate tag prefix '${prefix}' for workspaces: ${paths.join(", ")}`);
224
+ for (const [prefix, paths] of pathsByPrefix) {
225
+ if (paths.length > 1) {
226
+ throw new Error(`Duplicate tag prefix '${prefix}' for workspaces: ${paths.join(', ')}`);
227
+ }
237
228
  }
238
- }
239
229
  }
240
230
  function mergeReleaseNotesConfig(partial) {
241
- if (partial === void 0) {
242
- return { ...DEFAULT_RELEASE_NOTES_CONFIG };
243
- }
244
- return {
245
- shouldInjectIntoReadme: partial.shouldInjectIntoReadme ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldInjectIntoReadme
246
- };
231
+ if (partial === undefined) {
232
+ return { ...DEFAULT_RELEASE_NOTES_CONFIG };
233
+ }
234
+ return {
235
+ shouldInjectIntoReadme: partial.shouldInjectIntoReadme ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldInjectIntoReadme,
236
+ };
247
237
  }
248
- export {
249
- CONFIG_FILE_PATH,
250
- ROOT_PACKAGE_JSON_PATH,
251
- loadConfig,
252
- mergeMonorepoConfig,
253
- mergeSinglePackageConfig,
254
- readRootPackageVersion,
255
- resolveWorkTypes
256
- };
@@ -1,4 +1,4 @@
1
- import type { Commit, ParsedCommit, WorkTypeConfig } from './types.ts';
1
+ import type { Commit, ParsedCommit, WorkTypeConfig } from './types.js';
2
2
  export declare const COMMIT_PREPROCESSOR_PATTERNS: readonly RegExp[];
3
3
  export type PolicyViolationSurface = 'prefix' | 'body';
4
4
  export type PolicyViolationHandler = (commit: Commit, type: string, surface: PolicyViolationSurface) => void;
@@ -1,87 +1,83 @@
1
- const COMMIT_PREPROCESSOR_PATTERNS = [/^##\s+/, /^#\d+([.-]\d+)?\s+/, /^[A-Z]+-\d+\s+/];
2
- function parseCommitMessage(message, hash, workTypes, scopeAliases, options) {
3
- const firstLine = message.split("\n", 1)[0] ?? "";
4
- const stripped = stripTicketPrefix(firstLine);
5
- const match = stripped.match(/^(?:([^|]+)\|)?(\w+)(?:\(([^)]+)\))?(!)?:\s*(.*)$/);
6
- if (!match) {
7
- return void 0;
8
- }
9
- const pipeScope = match[1];
10
- const rawType = match[2];
11
- const parenthesizedScope = match[3];
12
- const breakingMarker = match[4];
13
- const description = match[5];
14
- if (rawType === void 0 || description === void 0) {
15
- return void 0;
16
- }
17
- const resolvedType = resolveType(rawType, workTypes);
18
- if (resolvedType === void 0) {
19
- return void 0;
20
- }
21
- const commit = { message, hash };
22
- const breaking = evaluateBreakingPolicy({
23
- commit,
24
- resolvedType,
25
- hasPrefixBreaking: breakingMarker === "!",
26
- hasFooterBreaking: message.includes("BREAKING CHANGE:"),
27
- policy: options?.breakingPolicies?.[resolvedType] ?? "optional",
28
- onPolicyViolation: options?.onPolicyViolation
29
- });
30
- const rawScope = pipeScope ?? parenthesizedScope;
31
- const resolvedScope = rawScope !== void 0 && scopeAliases !== void 0 ? scopeAliases[rawScope] ?? rawScope : rawScope;
32
- return {
33
- message,
34
- hash,
35
- type: resolvedType,
36
- description,
37
- breaking,
38
- ...resolvedScope !== void 0 && { scope: resolvedScope }
39
- };
1
+ export const COMMIT_PREPROCESSOR_PATTERNS = [/^##\s+/, /^#\d+([.-]\d+)?\s+/, /^[A-Z]+-\d+\s+/];
2
+ export function parseCommitMessage(message, hash, workTypes, scopeAliases, options) {
3
+ const firstLine = message.split('\n', 1)[0] ?? '';
4
+ const stripped = stripTicketPrefix(firstLine);
5
+ const match = stripped.match(/^(?:([^|]+)\|)?(\w+)(?:\(([^)]+)\))?(!)?:\s*(.*)$/);
6
+ if (!match) {
7
+ return undefined;
8
+ }
9
+ const pipeScope = match[1];
10
+ const rawType = match[2];
11
+ const parenthesizedScope = match[3];
12
+ const breakingMarker = match[4];
13
+ const description = match[5];
14
+ if (rawType === undefined || description === undefined) {
15
+ return undefined;
16
+ }
17
+ const resolvedType = resolveType(rawType, workTypes);
18
+ if (resolvedType === undefined) {
19
+ return undefined;
20
+ }
21
+ const commit = { message, hash };
22
+ const breaking = evaluateBreakingPolicy({
23
+ commit,
24
+ resolvedType,
25
+ hasPrefixBreaking: breakingMarker === '!',
26
+ hasFooterBreaking: message.includes('BREAKING CHANGE:'),
27
+ policy: options?.breakingPolicies?.[resolvedType] ?? 'optional',
28
+ onPolicyViolation: options?.onPolicyViolation,
29
+ });
30
+ const rawScope = pipeScope ?? parenthesizedScope;
31
+ const resolvedScope = rawScope !== undefined && scopeAliases !== undefined ? (scopeAliases[rawScope] ?? rawScope) : rawScope;
32
+ return {
33
+ message,
34
+ hash,
35
+ type: resolvedType,
36
+ description,
37
+ breaking,
38
+ ...(resolvedScope !== undefined && { scope: resolvedScope }),
39
+ };
40
40
  }
41
41
  function evaluateBreakingPolicy(inputs) {
42
- const { commit, resolvedType, hasPrefixBreaking, hasFooterBreaking, policy, onPolicyViolation } = inputs;
43
- if (policy === "forbidden") {
44
- if (hasPrefixBreaking) {
45
- onPolicyViolation?.(commit, resolvedType, "prefix");
46
- }
47
- if (hasFooterBreaking) {
48
- onPolicyViolation?.(commit, resolvedType, "body");
42
+ const { commit, resolvedType, hasPrefixBreaking, hasFooterBreaking, policy, onPolicyViolation } = inputs;
43
+ if (policy === 'forbidden') {
44
+ if (hasPrefixBreaking) {
45
+ onPolicyViolation?.(commit, resolvedType, 'prefix');
46
+ }
47
+ if (hasFooterBreaking) {
48
+ onPolicyViolation?.(commit, resolvedType, 'body');
49
+ }
50
+ return false;
49
51
  }
50
- return false;
51
- }
52
- if (policy === "required") {
53
- if (!hasPrefixBreaking) {
54
- onPolicyViolation?.(commit, resolvedType, "prefix");
55
- return false;
52
+ if (policy === 'required') {
53
+ if (!hasPrefixBreaking) {
54
+ onPolicyViolation?.(commit, resolvedType, 'prefix');
55
+ return false;
56
+ }
57
+ return true;
56
58
  }
57
- return true;
58
- }
59
- return hasPrefixBreaking || hasFooterBreaking;
59
+ return hasPrefixBreaking || hasFooterBreaking;
60
60
  }
61
61
  function resolveType(rawType, workTypes) {
62
- const lowered = rawType.toLowerCase();
63
- for (const [key, config] of Object.entries(workTypes)) {
64
- if (key === lowered) {
65
- return key;
66
- }
67
- if (config.aliases !== void 0) {
68
- for (const alias of config.aliases) {
69
- if (alias === lowered) {
70
- return key;
62
+ const lowered = rawType.toLowerCase();
63
+ for (const [key, config] of Object.entries(workTypes)) {
64
+ if (key === lowered) {
65
+ return key;
66
+ }
67
+ if (config.aliases !== undefined) {
68
+ for (const alias of config.aliases) {
69
+ if (alias === lowered) {
70
+ return key;
71
+ }
72
+ }
71
73
  }
72
- }
73
74
  }
74
- }
75
- return void 0;
75
+ return undefined;
76
76
  }
77
77
  function stripTicketPrefix(message) {
78
- let result = message;
79
- for (const pattern of COMMIT_PREPROCESSOR_PATTERNS) {
80
- result = result.replace(pattern, "");
81
- }
82
- return result;
78
+ let result = message;
79
+ for (const pattern of COMMIT_PREPROCESSOR_PATTERNS) {
80
+ result = result.replace(pattern, '');
81
+ }
82
+ return result;
83
83
  }
84
- export {
85
- COMMIT_PREPROCESSOR_PATTERNS,
86
- parseCommitMessage
87
- };