@williamthorsen/release-kit 6.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +5 -3
- 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 +3 -2
- package/dist/esm/.cache +0 -1
|
@@ -1,424 +1,421 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from
|
|
2
|
-
import path from
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { isRecord } from "./typeGuards.js";
|
|
4
|
-
const OVERRIDES_FILENAME =
|
|
5
|
-
const VALID_AUDIENCE_VALUES =
|
|
6
|
-
const V1_SUPPORTED_AUDIENCE_VALUES =
|
|
7
|
-
const KNOWN_OVERRIDE_FIELDS =
|
|
8
|
-
function loadChangelogOverrides(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
4
|
+
const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
|
|
5
|
+
const VALID_AUDIENCE_VALUES = new Set(['all', 'dev', 'skip']);
|
|
6
|
+
const V1_SUPPORTED_AUDIENCE_VALUES = new Set(['skip']);
|
|
7
|
+
const KNOWN_OVERRIDE_FIELDS = new Set(['audience', 'description', 'body', 'breaking']);
|
|
8
|
+
export function loadChangelogOverrides(path) {
|
|
9
|
+
if (!existsSync(path)) {
|
|
10
|
+
return { overrides: new Map() };
|
|
11
|
+
}
|
|
12
|
+
let content;
|
|
13
|
+
try {
|
|
14
|
+
content = readFileSync(path, 'utf8');
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return {
|
|
18
|
+
errors: [`Failed to read override file ${path}: ${error instanceof Error ? error.message : String(error)}`],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(content);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
errors: [`Failed to parse override file ${path}: ${error instanceof Error ? error.message : String(error)}`],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const result = validateChangelogOverrides(parsed);
|
|
31
|
+
if (result.errors.length > 0) {
|
|
32
|
+
return { errors: result.errors };
|
|
33
|
+
}
|
|
34
|
+
return { overrides: result.overrides };
|
|
33
35
|
}
|
|
34
|
-
function validateChangelogOverrides(raw) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
export function validateChangelogOverrides(raw) {
|
|
37
|
+
const overrides = new Map();
|
|
38
|
+
const errors = [];
|
|
39
|
+
if (!isRecord(raw)) {
|
|
40
|
+
errors.push('Override file: top-level value must be an object keyed by commit hash');
|
|
41
|
+
return { overrides, errors };
|
|
42
|
+
}
|
|
43
|
+
for (const [key, rawEntry] of Object.entries(raw)) {
|
|
44
|
+
if (key === '') {
|
|
45
|
+
errors.push('Override file: empty-string key is not a valid commit hash');
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const validated = validateSingleOverride(key, rawEntry, errors);
|
|
49
|
+
if (validated !== undefined) {
|
|
50
|
+
overrides.set(key, validated);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
39
53
|
return { overrides, errors };
|
|
40
|
-
}
|
|
41
|
-
for (const [key, rawEntry] of Object.entries(raw)) {
|
|
42
|
-
if (key === "") {
|
|
43
|
-
errors.push("Override file: empty-string key is not a valid commit hash");
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
const validated = validateSingleOverride(key, rawEntry, errors);
|
|
47
|
-
if (validated !== void 0) {
|
|
48
|
-
overrides.set(key, validated);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return { overrides, errors };
|
|
52
54
|
}
|
|
53
55
|
function validateSingleOverride(key, rawEntry, errors) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
56
|
+
if (!isRecord(rawEntry)) {
|
|
57
|
+
errors.push(`overrides['${key}']: must be an object`);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
let entryValid = true;
|
|
61
|
+
for (const fieldName of Object.keys(rawEntry)) {
|
|
62
|
+
if (!KNOWN_OVERRIDE_FIELDS.has(fieldName)) {
|
|
63
|
+
errors.push(`overrides['${key}']: unknown field '${fieldName}'`);
|
|
64
|
+
entryValid = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const result = {};
|
|
68
|
+
if (rawEntry.audience !== undefined) {
|
|
69
|
+
const audienceResult = validateAudience(key, rawEntry.audience, errors);
|
|
70
|
+
if (audienceResult === undefined) {
|
|
71
|
+
entryValid = false;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
result.audience = audienceResult;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (rawEntry.description !== undefined) {
|
|
78
|
+
if (typeof rawEntry.description !== 'string') {
|
|
79
|
+
errors.push(`overrides['${key}']: 'description' must be a string`);
|
|
80
|
+
entryValid = false;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
result.description = rawEntry.description;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (rawEntry.body !== undefined) {
|
|
87
|
+
if (typeof rawEntry.body !== 'string') {
|
|
88
|
+
errors.push(`overrides['${key}']: 'body' must be a string`);
|
|
89
|
+
entryValid = false;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
result.body = rawEntry.body;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (rawEntry.breaking !== undefined) {
|
|
96
|
+
if (typeof rawEntry.breaking !== 'boolean') {
|
|
97
|
+
errors.push(`overrides['${key}']: 'breaking' must be a boolean`);
|
|
98
|
+
entryValid = false;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
result.breaking = rawEntry.breaking;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (Object.keys(result).length === 0 && entryValid) {
|
|
105
|
+
errors.push(`overrides['${key}']: at least one override field must be set`);
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (!entryValid) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
106
112
|
}
|
|
107
113
|
function validateAudience(key, value, errors) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
if (typeof value !== 'string' || !VALID_AUDIENCE_VALUES.has(value)) {
|
|
115
|
+
errors.push(`overrides['${key}']: 'audience' must be one of 'all' | 'dev' | 'skip'`);
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (!V1_SUPPORTED_AUDIENCE_VALUES.has(value)) {
|
|
119
|
+
errors.push(`overrides['${key}']: audience '${value}' is not yet supported; only 'skip' is currently accepted`);
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return 'skip';
|
|
117
123
|
}
|
|
118
|
-
function formatStaleOverrideKeyWarning(key) {
|
|
119
|
-
|
|
124
|
+
export function formatStaleOverrideKeyWarning(key) {
|
|
125
|
+
return `Override key '${key}' did not match any commit hash in the changelog (likely a stale reference)`;
|
|
120
126
|
}
|
|
121
|
-
function applyChangelogOverrides(entries, overrides) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
export function applyChangelogOverrides(entries, overrides) {
|
|
128
|
+
const warnings = [];
|
|
129
|
+
const errors = [];
|
|
130
|
+
const matchedKeys = [];
|
|
131
|
+
if (overrides.size === 0) {
|
|
132
|
+
return { entries: entries.map(cloneEntry), warnings, errors, matchedKeys };
|
|
133
|
+
}
|
|
134
|
+
const allHashes = [];
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
for (const section of entry.sections) {
|
|
137
|
+
for (const item of section.items) {
|
|
138
|
+
if (item.hash !== undefined) {
|
|
139
|
+
allHashes.push(item.hash);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const keyToMatchedHashes = new Map();
|
|
145
|
+
for (const overrideKey of overrides.keys()) {
|
|
146
|
+
const matches = allHashes.filter((hash) => hash.startsWith(overrideKey));
|
|
147
|
+
if (matches.length === 0) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (matches.length > 1) {
|
|
151
|
+
errors.push(`Override key '${overrideKey}' is ambiguous: matches multiple commits (${matches.join(', ')}). ` +
|
|
152
|
+
'Use a longer prefix or the full commit hash.');
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
keyToMatchedHashes.set(overrideKey, matches);
|
|
156
|
+
matchedKeys.push(overrideKey);
|
|
157
|
+
}
|
|
158
|
+
const hashToOverride = new Map();
|
|
159
|
+
for (const [overrideKey, matchedHashes] of keyToMatchedHashes) {
|
|
160
|
+
const override = overrides.get(overrideKey);
|
|
161
|
+
if (override === undefined)
|
|
162
|
+
continue;
|
|
163
|
+
for (const hash of matchedHashes) {
|
|
164
|
+
hashToOverride.set(hash, override);
|
|
134
165
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
keyToMatchedHashes.set(overrideKey, matches);
|
|
151
|
-
matchedKeys.push(overrideKey);
|
|
152
|
-
}
|
|
153
|
-
const hashToOverride = /* @__PURE__ */ new Map();
|
|
154
|
-
for (const [overrideKey, matchedHashes] of keyToMatchedHashes) {
|
|
155
|
-
const override = overrides.get(overrideKey);
|
|
156
|
-
if (override === void 0) continue;
|
|
157
|
-
for (const hash of matchedHashes) {
|
|
158
|
-
hashToOverride.set(hash, override);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const transformedEntries = [];
|
|
162
|
-
for (const entry of entries) {
|
|
163
|
-
const transformedSections = [];
|
|
164
|
-
for (const section of entry.sections) {
|
|
165
|
-
const transformedItems = applyOverridesToItems(section.items, hashToOverride);
|
|
166
|
-
if (transformedItems.length === 0) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
transformedSections.push({ ...section, items: transformedItems });
|
|
170
|
-
}
|
|
171
|
-
transformedEntries.push({ ...entry, sections: transformedSections });
|
|
172
|
-
}
|
|
173
|
-
return { entries: transformedEntries, warnings, errors, matchedKeys };
|
|
166
|
+
}
|
|
167
|
+
const transformedEntries = [];
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const transformedSections = [];
|
|
170
|
+
for (const section of entry.sections) {
|
|
171
|
+
const transformedItems = applyOverridesToItems(section.items, hashToOverride);
|
|
172
|
+
if (transformedItems.length === 0) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
transformedSections.push({ ...section, items: transformedItems });
|
|
176
|
+
}
|
|
177
|
+
transformedEntries.push({ ...entry, sections: transformedSections });
|
|
178
|
+
}
|
|
179
|
+
return { entries: transformedEntries, warnings, errors, matchedKeys };
|
|
174
180
|
}
|
|
175
181
|
function applyOverridesToItems(items, hashToOverride) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
182
|
+
const result = [];
|
|
183
|
+
for (const item of items) {
|
|
184
|
+
if (item.hash === undefined) {
|
|
185
|
+
result.push(cloneItem(item));
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const override = hashToOverride.get(item.hash);
|
|
189
|
+
if (override === undefined) {
|
|
190
|
+
result.push(cloneItem(item));
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (override.audience === 'skip') {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
result.push(applyOverrideToItem(item, override));
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
193
199
|
}
|
|
194
200
|
function applyOverrideToItem(item, override) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
const result = { ...item };
|
|
202
|
+
if (override.description !== undefined) {
|
|
203
|
+
result.description = override.description;
|
|
204
|
+
}
|
|
205
|
+
if (override.body !== undefined) {
|
|
206
|
+
result.body = override.body;
|
|
207
|
+
}
|
|
208
|
+
if (override.breaking !== undefined) {
|
|
209
|
+
result.breaking = override.breaking;
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
206
212
|
}
|
|
207
213
|
function cloneItem(item) {
|
|
208
|
-
|
|
214
|
+
return { ...item };
|
|
209
215
|
}
|
|
210
216
|
function cloneEntry(entry) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
function resolveOverridePath(scopeRoot) {
|
|
217
|
-
return path.posix.join(scopeRoot, OVERRIDES_FILENAME);
|
|
217
|
+
return {
|
|
218
|
+
...entry,
|
|
219
|
+
sections: entry.sections.map((section) => ({ ...section, items: section.items.map(cloneItem) })),
|
|
220
|
+
};
|
|
218
221
|
}
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
let project = /* @__PURE__ */ new Map();
|
|
222
|
-
const perWorkspace = /* @__PURE__ */ new Map();
|
|
223
|
-
if (scopes.project !== void 0) {
|
|
224
|
-
const result = loadChangelogOverrides(resolveOverridePath(scopes.project));
|
|
225
|
-
if ("errors" in result) {
|
|
226
|
-
errors.push(...result.errors);
|
|
227
|
-
} else {
|
|
228
|
-
project = result.overrides;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
for (const workspacePath of scopes.workspaces ?? []) {
|
|
232
|
-
const result = loadChangelogOverrides(resolveOverridePath(workspacePath));
|
|
233
|
-
if ("errors" in result) {
|
|
234
|
-
errors.push(...result.errors);
|
|
235
|
-
} else if (result.overrides.size > 0) {
|
|
236
|
-
perWorkspace.set(workspacePath, result.overrides);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return { project, perWorkspace, errors };
|
|
222
|
+
export function resolveOverridePath(scopeRoot) {
|
|
223
|
+
return path.posix.join(scopeRoot, OVERRIDES_FILENAME);
|
|
240
224
|
}
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
225
|
+
export function loadOverridesForScopes(scopes) {
|
|
226
|
+
const errors = [];
|
|
227
|
+
let project = new Map();
|
|
228
|
+
const perWorkspace = new Map();
|
|
229
|
+
if (scopes.project !== undefined) {
|
|
230
|
+
const result = loadChangelogOverrides(resolveOverridePath(scopes.project));
|
|
231
|
+
if ('errors' in result) {
|
|
232
|
+
errors.push(...result.errors);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
project = result.overrides;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const workspacePath of scopes.workspaces ?? []) {
|
|
239
|
+
const result = loadChangelogOverrides(resolveOverridePath(workspacePath));
|
|
240
|
+
if ('errors' in result) {
|
|
241
|
+
errors.push(...result.errors);
|
|
242
|
+
}
|
|
243
|
+
else if (result.overrides.size > 0) {
|
|
244
|
+
perWorkspace.set(workspacePath, result.overrides);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return { project, perWorkspace, errors };
|
|
249
248
|
}
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
return {
|
|
260
|
-
project: result.project,
|
|
261
|
-
perWorkspace: result.perWorkspace,
|
|
262
|
-
overrideWarnings: [],
|
|
263
|
-
globalMatchedRootKeys: /* @__PURE__ */ new Set()
|
|
264
|
-
};
|
|
249
|
+
export function composeOverrides(rootEntries, workspaceEntries) {
|
|
250
|
+
const composed = new Map(rootEntries);
|
|
251
|
+
if (workspaceEntries !== undefined) {
|
|
252
|
+
for (const [key, value] of workspaceEntries) {
|
|
253
|
+
composed.set(key, value);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return composed;
|
|
265
257
|
}
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const projectMap = loadScopeMap(projectFilePath, errors);
|
|
271
|
-
const workspaceMaps = (inputs.workspaces ?? []).map((scope) => ({
|
|
272
|
-
filePath: scope.filePath,
|
|
273
|
-
hashes: scope.hashes,
|
|
274
|
-
map: loadScopeMap(scope.filePath, errors)
|
|
275
|
-
}));
|
|
276
|
-
const globalMatchedRootKeys = /* @__PURE__ */ new Set();
|
|
277
|
-
for (const workspace of workspaceMaps) {
|
|
278
|
-
processWorkspaceScope({
|
|
279
|
-
workspace,
|
|
280
|
-
projectFilePath,
|
|
281
|
-
projectMap,
|
|
282
|
-
errors,
|
|
283
|
-
warnings,
|
|
284
|
-
globalMatchedRootKeys
|
|
258
|
+
export function createOverrideContext(workspaces) {
|
|
259
|
+
const result = loadOverridesForScopes({
|
|
260
|
+
project: '.',
|
|
261
|
+
workspaces: workspaces.map((workspace) => workspace.workspacePath),
|
|
285
262
|
});
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
263
|
+
if (result.errors.length > 0) {
|
|
264
|
+
throw new Error(`Failed to load changelog overrides:\n - ${result.errors.join('\n - ')}`);
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
project: result.project,
|
|
268
|
+
perWorkspace: result.perWorkspace,
|
|
269
|
+
overrideWarnings: [],
|
|
270
|
+
globalMatchedRootKeys: new Set(),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
export function validateAllChangelogOverrides(inputs) {
|
|
274
|
+
const errors = [];
|
|
275
|
+
const warnings = [];
|
|
276
|
+
const projectFilePath = inputs.project?.filePath;
|
|
277
|
+
const projectMap = loadScopeMap(projectFilePath, errors);
|
|
278
|
+
const workspaceMaps = (inputs.workspaces ?? []).map((scope) => ({
|
|
279
|
+
filePath: scope.filePath,
|
|
280
|
+
hashes: scope.hashes,
|
|
281
|
+
map: loadScopeMap(scope.filePath, errors),
|
|
282
|
+
}));
|
|
283
|
+
const globalMatchedRootKeys = new Set();
|
|
284
|
+
for (const workspace of workspaceMaps) {
|
|
285
|
+
processWorkspaceScope({
|
|
286
|
+
workspace,
|
|
287
|
+
projectFilePath,
|
|
288
|
+
projectMap,
|
|
289
|
+
errors,
|
|
290
|
+
warnings,
|
|
291
|
+
globalMatchedRootKeys,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
const projectHashes = inputs.project?.hashes;
|
|
295
|
+
if (projectFilePath !== undefined && projectHashes !== undefined) {
|
|
296
|
+
processProjectScope({ projectFilePath, projectMap, projectHashes, errors, globalMatchedRootKeys });
|
|
297
|
+
}
|
|
298
|
+
if (projectFilePath !== undefined) {
|
|
299
|
+
collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings);
|
|
300
|
+
}
|
|
301
|
+
return { errors, warnings };
|
|
295
302
|
}
|
|
296
303
|
function processWorkspaceScope(args) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
304
|
+
const { workspace, projectFilePath, projectMap, errors, warnings, globalMatchedRootKeys } = args;
|
|
305
|
+
const { filePath, hashes, map } = workspace;
|
|
306
|
+
const workspaceApplied = applyChangelogOverrides(makeValidationEntries(hashes), map);
|
|
307
|
+
for (const message of workspaceApplied.errors) {
|
|
308
|
+
errors.push(prefixWithFilePath(filePath, message));
|
|
309
|
+
}
|
|
310
|
+
if (projectFilePath !== undefined && projectMap.size > 0) {
|
|
311
|
+
const projectMinusShadowed = filterShadowedKeys(projectMap, map);
|
|
312
|
+
const projectApplied = applyChangelogOverrides(makeValidationEntries(hashes), projectMinusShadowed);
|
|
313
|
+
for (const message of projectApplied.errors) {
|
|
314
|
+
errors.push(prefixWithFilePath(projectFilePath, message));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const key of map.keys()) {
|
|
318
|
+
if (!hasAnyMatch(key, hashes)) {
|
|
319
|
+
warnings.push(formatWorkspaceStaleWarning(filePath, key));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const key of projectMap.keys()) {
|
|
323
|
+
if (map.has(key))
|
|
324
|
+
continue;
|
|
325
|
+
if (hasAnyMatch(key, hashes)) {
|
|
326
|
+
globalMatchedRootKeys.add(key);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
321
329
|
}
|
|
322
330
|
function processProjectScope(args) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
const { projectFilePath, projectMap, projectHashes, errors, globalMatchedRootKeys } = args;
|
|
332
|
+
const applied = applyChangelogOverrides(makeValidationEntries(projectHashes), projectMap);
|
|
333
|
+
for (const message of applied.errors) {
|
|
334
|
+
errors.push(prefixWithFilePath(projectFilePath, message));
|
|
335
|
+
}
|
|
336
|
+
for (const key of projectMap.keys()) {
|
|
337
|
+
if (hasAnyMatch(key, projectHashes)) {
|
|
338
|
+
globalMatchedRootKeys.add(key);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
333
341
|
}
|
|
334
342
|
function collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
343
|
+
for (const key of projectMap.keys()) {
|
|
344
|
+
if (!globalMatchedRootKeys.has(key)) {
|
|
345
|
+
warnings.push(formatRootStaleWarning(projectFilePath, key));
|
|
346
|
+
}
|
|
338
347
|
}
|
|
339
|
-
}
|
|
340
348
|
}
|
|
341
349
|
function hasAnyMatch(key, hashes) {
|
|
342
|
-
|
|
350
|
+
return hashes.some((hash) => hash.startsWith(key));
|
|
343
351
|
}
|
|
344
352
|
function filterShadowedKeys(projectMap, workspaceMap) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
353
|
+
const result = new Map();
|
|
354
|
+
for (const [key, value] of projectMap) {
|
|
355
|
+
if (workspaceMap.has(key))
|
|
356
|
+
continue;
|
|
357
|
+
result.set(key, value);
|
|
358
|
+
}
|
|
359
|
+
return result;
|
|
351
360
|
}
|
|
352
361
|
function loadScopeMap(filePath, errors) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
362
|
+
if (filePath === undefined) {
|
|
363
|
+
return new Map();
|
|
364
|
+
}
|
|
365
|
+
const result = loadChangelogOverrides(filePath);
|
|
366
|
+
if ('errors' in result) {
|
|
367
|
+
for (const message of result.errors) {
|
|
368
|
+
errors.push(prefixWithFilePath(filePath, message));
|
|
369
|
+
}
|
|
370
|
+
return new Map();
|
|
371
|
+
}
|
|
372
|
+
return result.overrides;
|
|
364
373
|
}
|
|
365
374
|
function makeValidationEntries(hashes) {
|
|
366
|
-
|
|
367
|
-
{
|
|
368
|
-
version: "0.0.0",
|
|
369
|
-
date: "0000-00-00",
|
|
370
|
-
sections: [
|
|
375
|
+
return [
|
|
371
376
|
{
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
version: '0.0.0',
|
|
378
|
+
date: '0000-00-00',
|
|
379
|
+
sections: [
|
|
380
|
+
{
|
|
381
|
+
title: 'Validation',
|
|
382
|
+
audience: 'all',
|
|
383
|
+
items: hashes.map((hash) => ({ description: '', hash })),
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
},
|
|
387
|
+
];
|
|
379
388
|
}
|
|
380
389
|
function prefixWithFilePath(filePath, message) {
|
|
381
|
-
|
|
390
|
+
return `${filePath}: ${message}`;
|
|
382
391
|
}
|
|
383
392
|
function formatWorkspaceStaleWarning(filePath, key) {
|
|
384
|
-
|
|
393
|
+
return `${filePath}: Override key '${key}' did not match any commit in this workspace's history (likely a stale reference)`;
|
|
385
394
|
}
|
|
386
395
|
function formatRootStaleWarning(filePath, key) {
|
|
387
|
-
|
|
396
|
+
return `${filePath}: Override key '${key}' did not match any commit in any scope (likely a stale reference)`;
|
|
388
397
|
}
|
|
389
|
-
function applyWorkspaceOverrides(newEntries, workspacePath, overrideContext) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
398
|
+
export function applyWorkspaceOverrides(newEntries, workspacePath, overrideContext) {
|
|
399
|
+
const { project, perWorkspace, overrideWarnings, globalMatchedRootKeys } = overrideContext;
|
|
400
|
+
const workspaceOverrides = perWorkspace.get(workspacePath);
|
|
401
|
+
const composed = composeOverrides(project, workspaceOverrides);
|
|
402
|
+
const applied = applyChangelogOverrides(newEntries, composed);
|
|
403
|
+
if (applied.errors.length > 0) {
|
|
404
|
+
throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
|
|
405
|
+
}
|
|
406
|
+
overrideWarnings.push(...applied.warnings);
|
|
407
|
+
const matchedSet = new Set(applied.matchedKeys);
|
|
408
|
+
if (workspaceOverrides !== undefined) {
|
|
409
|
+
for (const key of workspaceOverrides.keys()) {
|
|
410
|
+
if (!matchedSet.has(key)) {
|
|
411
|
+
overrideWarnings.push(formatStaleOverrideKeyWarning(key));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const key of applied.matchedKeys) {
|
|
416
|
+
if (workspaceOverrides?.has(key))
|
|
417
|
+
continue;
|
|
418
|
+
globalMatchedRootKeys.add(key);
|
|
419
|
+
}
|
|
420
|
+
return applied;
|
|
412
421
|
}
|
|
413
|
-
export {
|
|
414
|
-
applyChangelogOverrides,
|
|
415
|
-
applyWorkspaceOverrides,
|
|
416
|
-
composeOverrides,
|
|
417
|
-
createOverrideContext,
|
|
418
|
-
formatStaleOverrideKeyWarning,
|
|
419
|
-
loadChangelogOverrides,
|
|
420
|
-
loadOverridesForScopes,
|
|
421
|
-
resolveOverridePath,
|
|
422
|
-
validateAllChangelogOverrides,
|
|
423
|
-
validateChangelogOverrides
|
|
424
|
-
};
|