@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.
- package/CHANGELOG.md +8 -0
- package/README.md +3 -3
- package/dist/esm/.cache +1 -1
- package/dist/esm/assertCleanWorkingTree.js +9 -14
- package/dist/esm/bin/release-kit.js +200 -199
- package/dist/esm/buildChangelogEntries.d.ts +2 -2
- package/dist/esm/buildChangelogEntries.js +142 -149
- package/dist/esm/buildDependencyGraph.d.ts +1 -1
- package/dist/esm/buildDependencyGraph.js +60 -59
- package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -1
- package/dist/esm/buildEmptyReleaseEntry.js +12 -15
- package/dist/esm/buildReleaseSummary.d.ts +1 -1
- package/dist/esm/buildReleaseSummary.js +23 -26
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +1 -1
- package/dist/esm/buildSyntheticChangelogEntry.js +9 -12
- package/dist/esm/bumpAllVersions.d.ts +1 -1
- package/dist/esm/bumpAllVersions.js +51 -52
- package/dist/esm/bumpVersion.d.ts +1 -1
- package/dist/esm/bumpVersion.js +25 -28
- package/dist/esm/changelogJsonFile.d.ts +1 -1
- package/dist/esm/changelogJsonFile.js +59 -62
- package/dist/esm/changelogJsonUtils.d.ts +1 -1
- package/dist/esm/changelogJsonUtils.js +35 -32
- package/dist/esm/changelogOverrides.d.ts +1 -1
- package/dist/esm/changelogOverrides.js +374 -377
- package/dist/esm/checkWorkTypesDrift.js +104 -102
- package/dist/esm/collectPolicyViolations.d.ts +2 -2
- package/dist/esm/collectPolicyViolations.js +11 -14
- package/dist/esm/commitCommand.js +48 -46
- package/dist/esm/compareVersions.js +20 -23
- package/dist/esm/createGithubRelease.js +56 -59
- package/dist/esm/createGithubReleaseCommand.js +21 -23
- package/dist/esm/createTags.js +57 -56
- package/dist/esm/decideRelease.d.ts +2 -2
- package/dist/esm/decideRelease.js +27 -41
- package/dist/esm/defaults.d.ts +3 -3
- package/dist/esm/defaults.js +36 -48
- package/dist/esm/deleteFileIfExists.js +10 -12
- package/dist/esm/deriveWorkspaceConfig.d.ts +1 -1
- package/dist/esm/deriveWorkspaceConfig.js +34 -36
- package/dist/esm/detectPackageManager.js +35 -37
- package/dist/esm/detectUndeclaredTagPrefixes.js +41 -40
- package/dist/esm/determineBumpFromCommits.d.ts +2 -2
- package/dist/esm/determineBumpFromCommits.js +25 -29
- package/dist/esm/determineBumpType.d.ts +1 -1
- package/dist/esm/determineBumpType.js +31 -32
- package/dist/esm/discoverWorkspaces.js +38 -40
- package/dist/esm/format.js +6 -11
- package/dist/esm/generateChangelogs.d.ts +1 -1
- package/dist/esm/generateChangelogs.js +13 -17
- package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
- package/dist/esm/getCommitsSinceTarget.js +54 -55
- package/dist/esm/hasPrettierConfig.js +33 -35
- package/dist/esm/help/prepareHelp.js +4 -8
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +2 -8
- package/dist/esm/init/checks.js +28 -32
- package/dist/esm/init/detectRepoType.js +12 -15
- package/dist/esm/init/initCommand.js +60 -54
- package/dist/esm/init/parseJsonRecord.js +9 -11
- package/dist/esm/init/scaffold.d.ts +1 -1
- package/dist/esm/init/scaffold.js +32 -38
- package/dist/esm/init/templates.d.ts +1 -1
- package/dist/esm/init/templates.js +16 -22
- package/dist/esm/injectReleaseNotesIntoReadme.js +42 -49
- package/dist/esm/injectSection.js +17 -27
- package/dist/esm/loadConfig.d.ts +1 -1
- package/dist/esm/loadConfig.js +206 -225
- package/dist/esm/parseCommitMessage.d.ts +1 -1
- package/dist/esm/parseCommitMessage.js +72 -76
- package/dist/esm/parseRequestedTags.js +6 -9
- package/dist/esm/prepareCommand.d.ts +1 -1
- package/dist/esm/prepareCommand.js +225 -235
- package/dist/esm/previewTagPrefixes.d.ts +1 -1
- package/dist/esm/previewTagPrefixes.js +101 -96
- package/dist/esm/propagateBumps.d.ts +2 -2
- package/dist/esm/propagateBumps.js +48 -50
- package/dist/esm/publish.d.ts +2 -2
- package/dist/esm/publish.js +22 -28
- package/dist/esm/publishCommand.js +75 -80
- package/dist/esm/pushCommand.js +33 -34
- package/dist/esm/pushRelease.d.ts +1 -1
- package/dist/esm/pushRelease.js +21 -24
- package/dist/esm/readCurrentVersion.js +13 -17
- package/dist/esm/releasePrepare.d.ts +1 -1
- package/dist/esm/releasePrepare.js +213 -231
- package/dist/esm/releasePrepareMono.d.ts +2 -2
- package/dist/esm/releasePrepareMono.js +434 -494
- package/dist/esm/releasePrepareProject.d.ts +2 -2
- package/dist/esm/releasePrepareProject.js +143 -153
- package/dist/esm/renderChangelogMarkdown.d.ts +1 -1
- package/dist/esm/renderChangelogMarkdown.js +20 -24
- package/dist/esm/renderReleaseNotes.d.ts +1 -1
- package/dist/esm/renderReleaseNotes.js +50 -55
- package/dist/esm/reportPrepare.d.ts +1 -1
- package/dist/esm/reportPrepare.js +230 -231
- package/dist/esm/resolveCliffConfigPath.js +18 -23
- package/dist/esm/resolveCommandTags.d.ts +1 -1
- package/dist/esm/resolveCommandTags.js +47 -45
- package/dist/esm/resolveReleaseNotesConfig.d.ts +1 -1
- package/dist/esm/resolveReleaseNotesConfig.js +39 -43
- package/dist/esm/resolveReleaseTags.d.ts +1 -1
- package/dist/esm/resolveReleaseTags.js +39 -40
- package/dist/esm/runGitCliff.js +27 -30
- package/dist/esm/showTagPrefixesCommand.js +48 -70
- package/dist/esm/stripEmojiPrefix.js +2 -5
- package/dist/esm/stripScope.js +18 -21
- package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
- package/dist/esm/sync-labels/generateCommand.js +51 -53
- package/dist/esm/sync-labels/initCommand.js +46 -48
- package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +1 -1
- package/dist/esm/sync-labels/loadSyncLabelsConfig.js +46 -52
- package/dist/esm/sync-labels/presets.d.ts +1 -1
- package/dist/esm/sync-labels/presets.js +40 -43
- package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
- package/dist/esm/sync-labels/resolveLabels.js +28 -35
- package/dist/esm/sync-labels/syncCommand.js +30 -31
- package/dist/esm/sync-labels/templates.d.ts +1 -1
- package/dist/esm/sync-labels/templates.js +23 -26
- package/dist/esm/sync-labels/types.js +1 -0
- package/dist/esm/syncWorkTypes.js +83 -84
- package/dist/esm/tagCommand.js +12 -14
- package/dist/esm/typeGuards.js +4 -8
- package/dist/esm/types.js +66 -60
- package/dist/esm/validateConfig.d.ts +1 -1
- package/dist/esm/validateConfig.js +109 -115
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +2 -2
- package/dist/esm/validateOnlyExcludesStrandedDependents.js +97 -91
- package/dist/esm/validateOverridesCommand.d.ts +2 -2
- package/dist/esm/validateOverridesCommand.js +110 -111
- package/dist/esm/workTypesData.js +57 -60
- package/dist/esm/workTypesUtils.js +16 -21
- package/dist/esm/writeReleaseNotesPreviews.js +55 -57
- package/package.json +2 -2
package/dist/esm/loadConfig.js
CHANGED
|
@@ -1,256 +1,237 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from
|
|
2
|
-
import path from
|
|
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 =
|
|
13
|
-
function readRootPackageVersion() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return { exists: true, version:
|
|
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 =
|
|
40
|
-
async function loadConfig() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
workspaces,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
124
|
+
export function resolveWorkTypes(userWorkTypes) {
|
|
125
|
+
return userWorkTypes === undefined ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userWorkTypes };
|
|
134
126
|
}
|
|
135
127
|
function mergeChangelogJsonConfig(partial) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
211
|
+
return a === b || a.startsWith(b) || b.startsWith(a);
|
|
223
212
|
}
|
|
224
213
|
function assertUniqueTagPrefixes(workspaces) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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.
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
if (policy === 'required') {
|
|
53
|
+
if (!hasPrefixBreaking) {
|
|
54
|
+
onPolicyViolation?.(commit, resolvedType, 'prefix');
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
56
58
|
}
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
return hasPrefixBreaking || hasFooterBreaking;
|
|
59
|
+
return hasPrefixBreaking || hasFooterBreaking;
|
|
60
60
|
}
|
|
61
61
|
function resolveType(rawType, workTypes) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
};
|