@williamthorsen/release-kit 14.2.2 → 14.2.3

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 (84) hide show
  1. package/.meta/changelog.json +50 -0
  2. package/.readyup/kits/default.js +135 -145
  3. package/.readyup/kits/npm-auto-publish.js +179 -180
  4. package/.readyup/manifest.json +31 -17
  5. package/CHANGELOG.md +16 -0
  6. package/README.md +7 -1
  7. package/dist/esm/applyChangelogOverrides.d.ts +50 -0
  8. package/dist/esm/applyChangelogOverrides.js +162 -0
  9. package/dist/esm/attachChangelogDiagnostics.d.ts +1 -1
  10. package/dist/esm/bin/release-kit.js +6 -238
  11. package/dist/esm/buildChangelogEntries.d.ts +20 -31
  12. package/dist/esm/buildChangelogEntries.js +71 -188
  13. package/dist/esm/buildChangelogItem.d.ts +56 -0
  14. package/dist/esm/buildChangelogItem.js +119 -0
  15. package/dist/esm/buildReleasedResult.d.ts +20 -0
  16. package/dist/esm/buildReleasedResult.js +78 -0
  17. package/dist/esm/changelogJsonUtils.d.ts +1 -1
  18. package/dist/esm/changelogOverrides.d.ts +29 -143
  19. package/dist/esm/changelogOverrides.js +35 -467
  20. package/dist/esm/configSchema.d.ts +225 -0
  21. package/dist/esm/configSchema.js +103 -0
  22. package/dist/esm/decideDirectReleases.d.ts +30 -0
  23. package/dist/esm/decideDirectReleases.js +66 -0
  24. package/dist/esm/defineConfig.d.ts +2 -2
  25. package/dist/esm/deriveReleaseNotesConfig.d.ts +2 -1
  26. package/dist/esm/deriveReleaseNotesConfig.js +1 -1
  27. package/dist/esm/formatPrepareFiles.d.ts +11 -0
  28. package/dist/esm/formatPrepareFiles.js +55 -0
  29. package/dist/esm/formatPrepareWarnings.d.ts +26 -0
  30. package/dist/esm/formatPrepareWarnings.js +72 -0
  31. package/dist/esm/help/initHelp.d.ts +4 -0
  32. package/dist/esm/help/initHelp.js +31 -0
  33. package/dist/esm/help/overridesHelp.d.ts +4 -0
  34. package/dist/esm/help/overridesHelp.js +32 -0
  35. package/dist/esm/help/releaseStepsHelp.d.ts +12 -0
  36. package/dist/esm/help/releaseStepsHelp.js +91 -0
  37. package/dist/esm/help/syncLabelsHelp.d.ts +8 -0
  38. package/dist/esm/help/syncLabelsHelp.js +55 -0
  39. package/dist/esm/help/usage.d.ts +2 -0
  40. package/dist/esm/help/usage.js +29 -0
  41. package/dist/esm/index.d.ts +1 -1
  42. package/dist/esm/index.js +1 -1
  43. package/dist/esm/loadChangelogOverrides.d.ts +31 -0
  44. package/dist/esm/loadChangelogOverrides.js +138 -0
  45. package/dist/esm/loadConfig.d.ts +0 -40
  46. package/dist/esm/loadConfig.js +0 -240
  47. package/dist/esm/loadValidatedConfig.d.ts +1 -1
  48. package/dist/esm/mergeConfig.d.ts +41 -0
  49. package/dist/esm/mergeConfig.js +118 -0
  50. package/dist/esm/monorepoConfigAssertions.d.ts +49 -0
  51. package/dist/esm/monorepoConfigAssertions.js +126 -0
  52. package/dist/esm/planChangelogFiles.d.ts +16 -0
  53. package/dist/esm/planChangelogFiles.js +41 -0
  54. package/dist/esm/prepareArgs.d.ts +55 -0
  55. package/dist/esm/prepareArgs.js +69 -0
  56. package/dist/esm/prepareCommand.d.ts +0 -48
  57. package/dist/esm/prepareCommand.js +6 -73
  58. package/dist/esm/prepareComposite.d.ts +16 -0
  59. package/dist/esm/prepareComposite.js +39 -0
  60. package/dist/esm/prepareRunContext.d.ts +38 -0
  61. package/dist/esm/prepareRunContext.js +39 -0
  62. package/dist/esm/prepareTarget.d.ts +2 -7
  63. package/dist/esm/prepareTarget.js +2 -118
  64. package/dist/esm/previewTagPrefixes.d.ts +1 -1
  65. package/dist/esm/releasePrepareTargets.js +13 -205
  66. package/dist/esm/reportPrepare.js +3 -125
  67. package/dist/esm/resolveCommandTags.d.ts +1 -1
  68. package/dist/esm/resolveCommandTags.js +2 -1
  69. package/dist/esm/resolveReleaseDeclaration.d.ts +2 -1
  70. package/dist/esm/resolveReleaseTargets.d.ts +2 -1
  71. package/dist/esm/resolveReleaseTargets.js +2 -1
  72. package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
  73. package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
  74. package/dist/esm/topologicalSort.d.ts +12 -0
  75. package/dist/esm/topologicalSort.js +60 -0
  76. package/dist/esm/types.d.ts +1 -225
  77. package/dist/esm/types.js +1 -103
  78. package/dist/esm/validateAllChangelogOverrides.d.ts +56 -0
  79. package/dist/esm/validateAllChangelogOverrides.js +138 -0
  80. package/dist/esm/validateConfig.d.ts +1 -1
  81. package/dist/esm/validateConfig.js +7 -9
  82. package/dist/esm/validateOverridesCommand.d.ts +1 -1
  83. package/dist/esm/validateOverridesCommand.js +2 -1
  84. package/package.json +2 -2
@@ -1,307 +1,52 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
1
  import path from 'node:path';
3
- import { describeError } from '@williamthorsen/toolbelt.errors';
4
- import { extractMigration } from "./extractMigration.js";
5
- import { isRecord } from "./typeGuards.js";
2
+ import { applyChangelogOverrides, formatStaleOverrideKeyWarning } from "./applyChangelogOverrides.js";
3
+ import { loadChangelogOverrides } from "./loadChangelogOverrides.js";
6
4
  const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
7
- const VALID_AUDIENCE_VALUES = new Set(['all', 'dev', 'skip']);
8
- const V1_SUPPORTED_AUDIENCE_VALUES = new Set(['skip']);
9
- export const KNOWN_OVERRIDE_FIELDS = new Set(['audience', 'description', 'body', 'breaking']);
10
- export const OVERRIDE_KEY_PATTERN = /^[0-9a-f]+(:[1-9][0-9]*)?$/;
11
- const SCHEMA_KEY = '$schema';
12
- export function loadChangelogOverrides(path) {
13
- if (!existsSync(path)) {
14
- return { overrides: new Map() };
15
- }
16
- let content;
17
- try {
18
- content = readFileSync(path, 'utf8');
19
- }
20
- catch (error) {
21
- return {
22
- errors: [`Failed to read override file ${path}: ${describeError(error)}`],
23
- };
24
- }
25
- let parsed;
26
- try {
27
- parsed = JSON.parse(content);
28
- }
29
- catch (error) {
30
- return {
31
- errors: [`Failed to parse override file ${path}: ${describeError(error)}`],
32
- };
33
- }
34
- const result = validateChangelogOverrides(parsed);
35
- if (result.errors.length > 0) {
36
- return { errors: result.errors };
37
- }
38
- return { overrides: result.overrides };
39
- }
40
- export function validateChangelogOverrides(raw) {
41
- const overrides = new Map();
42
- const errors = [];
43
- if (!isRecord(raw)) {
44
- errors.push('Override file: top-level value must be an object keyed by commit hash');
45
- return { overrides, errors };
46
- }
47
- for (const [key, rawEntry] of Object.entries(raw)) {
48
- if (key === SCHEMA_KEY) {
49
- if (typeof rawEntry !== 'string') {
50
- errors.push(`Override file: '${SCHEMA_KEY}' must be a string`);
51
- }
52
- continue;
53
- }
54
- if (!OVERRIDE_KEY_PATTERN.test(key)) {
55
- errors.push(`overrides['${key}']: key must be a lowercase hex commit hash or prefix, optionally followed by ':<n>'`);
56
- continue;
57
- }
58
- const validated = validateSingleOverride(key, rawEntry, errors);
59
- if (validated !== undefined) {
60
- overrides.set(key, validated);
61
- }
62
- }
63
- return { overrides, errors };
64
- }
65
- export function parseOverrideKey(key) {
66
- const separatorIndex = key.indexOf(':');
67
- if (separatorIndex === -1) {
68
- return { hashPrefix: key };
69
- }
70
- return { hashPrefix: key.slice(0, separatorIndex), entry: Number(key.slice(separatorIndex + 1)) };
71
- }
72
- function validateSingleOverride(key, rawEntry, errors) {
73
- if (!isRecord(rawEntry)) {
74
- errors.push(`overrides['${key}']: must be an object`);
75
- return undefined;
76
- }
77
- let entryValid = true;
78
- for (const fieldName of Object.keys(rawEntry)) {
79
- if (KNOWN_OVERRIDE_FIELDS.has(fieldName)) {
80
- continue;
81
- }
82
- errors.push(`overrides['${key}']: unknown field '${fieldName}'`);
83
- entryValid = false;
84
- }
85
- const result = {};
86
- if (rawEntry['audience'] !== undefined) {
87
- const audienceResult = validateAudience(key, rawEntry['audience'], errors);
88
- if (audienceResult === undefined) {
89
- entryValid = false;
90
- }
91
- else {
92
- result.audience = audienceResult;
93
- }
94
- }
95
- if (rawEntry['description'] !== undefined) {
96
- if (typeof rawEntry['description'] !== 'string') {
97
- errors.push(`overrides['${key}']: 'description' must be a string`);
98
- entryValid = false;
99
- }
100
- else {
101
- result.description = rawEntry['description'];
102
- }
103
- }
104
- if (rawEntry['body'] !== undefined) {
105
- if (typeof rawEntry['body'] !== 'string') {
106
- errors.push(`overrides['${key}']: 'body' must be a string`);
107
- entryValid = false;
108
- }
109
- else {
110
- result.body = rawEntry['body'];
111
- }
112
- }
113
- if (rawEntry['breaking'] !== undefined) {
114
- if (typeof rawEntry['breaking'] !== 'boolean') {
115
- errors.push(`overrides['${key}']: 'breaking' must be a boolean`);
116
- entryValid = false;
117
- }
118
- else {
119
- result.breaking = rawEntry['breaking'];
120
- }
121
- }
122
- if (Object.keys(result).length === 0 && entryValid) {
123
- errors.push(`overrides['${key}']: at least one override field must be set`);
124
- return undefined;
125
- }
126
- if (!entryValid) {
127
- return undefined;
128
- }
129
- return result;
130
- }
131
- function validateAudience(key, value, errors) {
132
- if (typeof value !== 'string' || !VALID_AUDIENCE_VALUES.has(value)) {
133
- errors.push(`overrides['${key}']: 'audience' must be one of 'all' | 'dev' | 'skip'`);
134
- return undefined;
135
- }
136
- if (!V1_SUPPORTED_AUDIENCE_VALUES.has(value)) {
137
- errors.push(`overrides['${key}']: audience '${value}' is not yet supported; only 'skip' is currently accepted`);
138
- return undefined;
139
- }
140
- return 'skip';
141
- }
142
- export function formatStaleOverrideKeyWarning(key) {
143
- return `Override key '${key}' did not match any item in the changelog (likely a stale reference)`;
144
- }
145
- export function applyChangelogOverrides(entries, overrides) {
146
- const warnings = [];
147
- if (overrides.size === 0) {
148
- return { entries: entries.map(cloneEntry), warnings, errors: [], matchedKeys: [] };
149
- }
150
- const { errors, itemIdToKey } = resolveOverrideKeys(indexEntryPositions(entries), overrides);
151
- const itemIdToOverride = new Map();
152
- for (const [itemId, overrideKey] of itemIdToKey) {
153
- const override = overrides.get(overrideKey);
154
- if (override !== undefined) {
155
- itemIdToOverride.set(itemId, override);
156
- }
157
- }
158
- const matchedKeys = [...new Set(itemIdToKey.values())];
159
- const transformedEntries = [];
160
- for (const entry of entries) {
161
- const transformedSections = [];
162
- for (const section of entry.sections) {
163
- const transformedItems = applyOverridesToItems(section.items, itemIdToOverride);
164
- if (transformedItems.length === 0) {
165
- continue;
166
- }
167
- transformedSections.push({ ...section, items: transformedItems });
168
- }
169
- transformedEntries.push({ ...entry, sections: transformedSections });
5
+ export function applyWorkspaceOverrides(newEntries, workspacePath, overrideContext) {
6
+ const { project, perWorkspace, overrideWarnings, globalMatchedRootKeys } = overrideContext;
7
+ const workspaceOverrides = workspacePath === undefined ? undefined : perWorkspace.get(workspacePath);
8
+ const composed = composeOverrides(project, workspaceOverrides);
9
+ const applied = applyChangelogOverrides(newEntries, composed);
10
+ if (applied.errors.length > 0) {
11
+ throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
170
12
  }
171
- return { entries: transformedEntries, warnings, errors, matchedKeys };
172
- }
173
- function resolveOverrideKeys(positionsByHash, overrides) {
174
- const errors = [];
175
- const failedKeys = new Set();
176
- const itemIdToKey = new Map();
177
- for (const [overrideKey, override] of overrides) {
178
- const resolution = resolveOverrideKey(overrideKey, positionsByHash);
179
- if (resolution.kind === 'ambiguous') {
180
- errors.push(`Override key '${overrideKey}' is ambiguous: matches multiple commits (${resolution.hashes.join(', ')}). ` +
181
- 'Use a longer prefix or the full commit hash.');
182
- continue;
183
- }
184
- if (resolution.kind === 'none') {
185
- continue;
186
- }
187
- const { hash, positions } = resolution;
188
- if (parseOverrideKey(overrideKey).entry === undefined && positions.length > 1) {
189
- const fieldsSet = ['description', 'body'].filter((field) => override[field] !== undefined);
190
- if (fieldsSet.length > 0) {
191
- const ordinalKeys = positions.map((position) => `${overrideKey}:${position}`).join(', ');
192
- errors.push(`Override key '${overrideKey}' sets ${fieldsSet.join(' and ')} on a commit with several items; use ${ordinalKeys}`);
193
- continue;
194
- }
195
- }
196
- for (const position of positions) {
197
- const itemId = formatItemId(hash, position);
198
- const claimingKey = itemIdToKey.get(itemId);
199
- if (claimingKey === undefined) {
200
- itemIdToKey.set(itemId, overrideKey);
201
- continue;
13
+ overrideWarnings.push(...applied.warnings);
14
+ const matchedSet = new Set(applied.matchedKeys);
15
+ if (workspaceOverrides !== undefined) {
16
+ for (const key of workspaceOverrides.keys()) {
17
+ if (!matchedSet.has(key)) {
18
+ overrideWarnings.push(formatStaleOverrideKeyWarning(key));
202
19
  }
203
- const target = position === undefined ? `commit ${hash}` : `the item at ${hash}:${position}`;
204
- errors.push(`Override keys '${claimingKey}' and '${overrideKey}' both match ${target}; keep one`);
205
- failedKeys.add(claimingKey);
206
- failedKeys.add(overrideKey);
207
20
  }
208
21
  }
209
- for (const [itemId, overrideKey] of itemIdToKey) {
210
- if (failedKeys.has(overrideKey)) {
211
- itemIdToKey.delete(itemId);
212
- }
213
- }
214
- return { errors, itemIdToKey };
215
- }
216
- function resolveOverrideKey(overrideKey, positionsByHash) {
217
- const { hashPrefix, entry } = parseOverrideKey(overrideKey);
218
- const hashes = positionsByHash
219
- .keys()
220
- .filter((hash) => hash.startsWith(hashPrefix))
221
- .toArray();
222
- const [hash] = hashes;
223
- if (hash === undefined) {
224
- return { kind: 'none' };
225
- }
226
- if (hashes.length > 1) {
227
- return { kind: 'ambiguous', hashes };
228
- }
229
- const available = [...(positionsByHash.get(hash) ?? [])];
230
- const positions = entry === undefined
231
- ? available.toSorted((left, right) => (left ?? 0) - (right ?? 0))
232
- : available.filter((position) => position === entry);
233
- return positions.length === 0 ? { kind: 'none' } : { kind: 'items', hash, positions };
234
- }
235
- function indexEntryPositions(entries) {
236
- return indexItemPositions(entries.flatMap((entry) => entry.sections.flatMap((section) => section.items)));
237
- }
238
- function indexItemPositions(items) {
239
- const positionsByHash = new Map();
240
- for (const item of items) {
241
- if (item.hash === undefined)
22
+ for (const key of applied.matchedKeys) {
23
+ if (workspaceOverrides?.has(key))
242
24
  continue;
243
- const positions = positionsByHash.get(item.hash) ?? new Set();
244
- positions.add(item.entry);
245
- positionsByHash.set(item.hash, positions);
25
+ globalMatchedRootKeys.add(key);
246
26
  }
247
- return positionsByHash;
248
- }
249
- function formatItemId(hash, entry) {
250
- return entry === undefined ? hash : `${hash}:${entry}`;
27
+ return applied;
251
28
  }
252
- function applyOverridesToItems(items, itemIdToOverride) {
253
- const result = [];
254
- for (const item of items) {
255
- if (item.hash === undefined) {
256
- result.push(cloneItem(item));
257
- continue;
258
- }
259
- const override = itemIdToOverride.get(formatItemId(item.hash, item.entry));
260
- if (override === undefined) {
261
- result.push(cloneItem(item));
262
- continue;
263
- }
264
- if (override.audience === 'skip') {
265
- continue;
29
+ export function composeOverrides(rootEntries, workspaceEntries) {
30
+ const composed = new Map(rootEntries);
31
+ if (workspaceEntries !== undefined) {
32
+ for (const [key, value] of workspaceEntries) {
33
+ composed.set(key, value);
266
34
  }
267
- result.push(applyOverrideToItem(item, override));
268
35
  }
269
- return result;
36
+ return composed;
270
37
  }
271
- function applyOverrideToItem(item, override) {
272
- const result = { ...item };
273
- if (override.description !== undefined) {
274
- result.description = override.description;
275
- }
276
- if (override.body !== undefined) {
277
- result.body = override.body;
278
- }
279
- if (override.body !== undefined && item.entry === undefined) {
280
- const migration = extractMigration(override.body);
281
- if (migration === undefined) {
282
- delete result.migration;
283
- }
284
- else {
285
- result.migration = migration;
286
- }
287
- }
288
- if (override.breaking !== undefined) {
289
- result.breaking = override.breaking;
38
+ export function createOverrideContext(workspacePaths) {
39
+ const result = loadOverridesForScopes({ project: '.', workspaces: [...workspacePaths] });
40
+ if (result.errors.length > 0) {
41
+ throw new Error(`Failed to load changelog overrides:\n - ${result.errors.join('\n - ')}`);
290
42
  }
291
- return result;
292
- }
293
- function cloneItem(item) {
294
- return { ...item };
295
- }
296
- function cloneEntry(entry) {
297
43
  return {
298
- ...entry,
299
- sections: entry.sections.map((section) => ({ ...section, items: section.items.map(cloneItem) })),
44
+ project: result.project,
45
+ perWorkspace: result.perWorkspace,
46
+ overrideWarnings: [],
47
+ globalMatchedRootKeys: new Set(),
300
48
  };
301
49
  }
302
- export function resolveOverridePath(scopeRoot) {
303
- return path.posix.join(scopeRoot, OVERRIDES_FILENAME);
304
- }
305
50
  export function loadOverridesForScopes(scopes) {
306
51
  const errors = [];
307
52
  let project = new Map();
@@ -327,183 +72,6 @@ export function loadOverridesForScopes(scopes) {
327
72
  }
328
73
  return { project, perWorkspace, errors };
329
74
  }
330
- export function composeOverrides(rootEntries, workspaceEntries) {
331
- const composed = new Map(rootEntries);
332
- if (workspaceEntries !== undefined) {
333
- for (const [key, value] of workspaceEntries) {
334
- composed.set(key, value);
335
- }
336
- }
337
- return composed;
338
- }
339
- export function createOverrideContext(workspacePaths) {
340
- const result = loadOverridesForScopes({ project: '.', workspaces: [...workspacePaths] });
341
- if (result.errors.length > 0) {
342
- throw new Error(`Failed to load changelog overrides:\n - ${result.errors.join('\n - ')}`);
343
- }
344
- return {
345
- project: result.project,
346
- perWorkspace: result.perWorkspace,
347
- overrideWarnings: [],
348
- globalMatchedRootKeys: new Set(),
349
- };
350
- }
351
- export function validateAllChangelogOverrides(inputs) {
352
- const errors = [];
353
- const warnings = [];
354
- const projectFilePath = inputs.project?.filePath;
355
- const projectMap = loadScopeMap(projectFilePath, errors);
356
- const workspaceMaps = (inputs.workspaces ?? []).map((scope) => ({
357
- filePath: scope.filePath,
358
- items: scope.items,
359
- map: loadScopeMap(scope.filePath, errors),
360
- }));
361
- const globalMatchedRootKeys = new Set();
362
- for (const workspace of workspaceMaps) {
363
- processWorkspaceScope({
364
- workspace,
365
- projectFilePath,
366
- projectMap,
367
- errors,
368
- warnings,
369
- globalMatchedRootKeys,
370
- });
371
- }
372
- const projectItems = inputs.project?.items;
373
- if (projectFilePath !== undefined && projectItems !== undefined) {
374
- processProjectScope({ projectFilePath, projectMap, projectItems, errors, globalMatchedRootKeys });
375
- }
376
- if (projectFilePath !== undefined) {
377
- collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings);
378
- }
379
- return { errors, warnings };
380
- }
381
- function processWorkspaceScope(args) {
382
- const { workspace, projectFilePath, projectMap, errors, warnings, globalMatchedRootKeys } = args;
383
- const { filePath, items, map } = workspace;
384
- const validationEntries = makeValidationEntries(items);
385
- const positionsByHash = indexItemPositions(items);
386
- const workspaceApplied = applyChangelogOverrides(validationEntries, map);
387
- for (const message of workspaceApplied.errors) {
388
- errors.push(prefixWithFilePath(filePath, message));
389
- }
390
- if (projectFilePath !== undefined && projectMap.size > 0) {
391
- const projectMinusShadowed = filterShadowedKeys(projectMap, map);
392
- const projectApplied = applyChangelogOverrides(validationEntries, projectMinusShadowed);
393
- for (const message of projectApplied.errors) {
394
- errors.push(prefixWithFilePath(projectFilePath, message));
395
- }
396
- const perFileErrors = new Set([...workspaceApplied.errors, ...projectApplied.errors]);
397
- const composedApplied = applyChangelogOverrides(validationEntries, composeOverrides(projectMap, map));
398
- for (const message of composedApplied.errors) {
399
- if (!perFileErrors.has(message)) {
400
- errors.push(prefixWithFilePath(filePath, message));
401
- }
402
- }
403
- }
404
- for (const key of map.keys()) {
405
- if (!hasAnyMatch(key, positionsByHash)) {
406
- warnings.push(formatWorkspaceStaleWarning(filePath, key));
407
- }
408
- }
409
- for (const key of projectMap.keys()) {
410
- if (map.has(key))
411
- continue;
412
- if (hasAnyMatch(key, positionsByHash)) {
413
- globalMatchedRootKeys.add(key);
414
- }
415
- }
416
- }
417
- function processProjectScope(args) {
418
- const { projectFilePath, projectMap, projectItems, errors, globalMatchedRootKeys } = args;
419
- const applied = applyChangelogOverrides(makeValidationEntries(projectItems), projectMap);
420
- for (const message of applied.errors) {
421
- errors.push(prefixWithFilePath(projectFilePath, message));
422
- }
423
- const positionsByHash = indexItemPositions(projectItems);
424
- for (const key of projectMap.keys()) {
425
- if (hasAnyMatch(key, positionsByHash)) {
426
- globalMatchedRootKeys.add(key);
427
- }
428
- }
429
- }
430
- function collectRootStaleWarnings(projectFilePath, projectMap, globalMatchedRootKeys, warnings) {
431
- for (const key of projectMap.keys()) {
432
- if (!globalMatchedRootKeys.has(key)) {
433
- warnings.push(formatRootStaleWarning(projectFilePath, key));
434
- }
435
- }
436
- }
437
- function hasAnyMatch(key, positionsByHash) {
438
- return resolveOverrideKey(key, positionsByHash).kind !== 'none';
439
- }
440
- function filterShadowedKeys(projectMap, workspaceMap) {
441
- const result = new Map();
442
- for (const [key, value] of projectMap) {
443
- if (workspaceMap.has(key))
444
- continue;
445
- result.set(key, value);
446
- }
447
- return result;
448
- }
449
- function loadScopeMap(filePath, errors) {
450
- if (filePath === undefined) {
451
- return new Map();
452
- }
453
- const result = loadChangelogOverrides(filePath);
454
- if ('errors' in result) {
455
- for (const message of result.errors) {
456
- errors.push(prefixWithFilePath(filePath, message));
457
- }
458
- return new Map();
459
- }
460
- return result.overrides;
461
- }
462
- function makeValidationEntries(items) {
463
- return [
464
- {
465
- version: '0.0.0',
466
- date: '0000-00-00',
467
- sections: [
468
- {
469
- title: 'Validation',
470
- audience: 'all',
471
- items: items.map((item) => ({ ...item, description: '' })),
472
- },
473
- ],
474
- },
475
- ];
476
- }
477
- function prefixWithFilePath(filePath, message) {
478
- return `${filePath}: ${message}`;
479
- }
480
- function formatWorkspaceStaleWarning(filePath, key) {
481
- return `${filePath}: Override key '${key}' did not match any item in this workspace's history (likely a stale reference)`;
482
- }
483
- function formatRootStaleWarning(filePath, key) {
484
- return `${filePath}: Override key '${key}' did not match any item in any scope (likely a stale reference)`;
485
- }
486
- export function applyWorkspaceOverrides(newEntries, workspacePath, overrideContext) {
487
- const { project, perWorkspace, overrideWarnings, globalMatchedRootKeys } = overrideContext;
488
- const workspaceOverrides = workspacePath === undefined ? undefined : perWorkspace.get(workspacePath);
489
- const composed = composeOverrides(project, workspaceOverrides);
490
- const applied = applyChangelogOverrides(newEntries, composed);
491
- if (applied.errors.length > 0) {
492
- throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
493
- }
494
- overrideWarnings.push(...applied.warnings);
495
- const matchedSet = new Set(applied.matchedKeys);
496
- if (workspaceOverrides !== undefined) {
497
- for (const key of workspaceOverrides.keys()) {
498
- if (!matchedSet.has(key)) {
499
- overrideWarnings.push(formatStaleOverrideKeyWarning(key));
500
- }
501
- }
502
- }
503
- for (const key of applied.matchedKeys) {
504
- if (workspaceOverrides?.has(key))
505
- continue;
506
- globalMatchedRootKeys.add(key);
507
- }
508
- return applied;
75
+ export function resolveOverridePath(scopeRoot) {
76
+ return path.posix.join(scopeRoot, OVERRIDES_FILENAME);
509
77
  }