@williamthorsen/release-kit 15.0.3 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.meta/changelog.json +66 -0
  2. package/.readyup/kits/default.js +13 -13
  3. package/.readyup/kits/npm-auto-publish.js +4 -4
  4. package/.readyup/manifest.json +12 -12
  5. package/CHANGELOG.md +19 -0
  6. package/README.md +7 -1
  7. package/dist/esm/applyChangelogOverrides.js +6 -6
  8. package/dist/esm/assertReleasesOnNpm.js +11 -11
  9. package/dist/esm/assertTaggedBaseline.js +5 -5
  10. package/dist/esm/bin/release-kit.js +16 -16
  11. package/dist/esm/buildChangelogEntries.d.ts +19 -19
  12. package/dist/esm/buildChangelogEntries.js +40 -36
  13. package/dist/esm/buildChangelogItem.js +24 -24
  14. package/dist/esm/buildDependencyGraph.js +23 -23
  15. package/dist/esm/buildReleasedResult.js +21 -19
  16. package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
  17. package/dist/esm/bumpVersion.js +8 -8
  18. package/dist/esm/changelogArchive.d.ts +1 -1
  19. package/dist/esm/changelogArchive.js +2 -2
  20. package/dist/esm/changelogJsonFile.js +5 -5
  21. package/dist/esm/changelogJsonUtils.js +3 -3
  22. package/dist/esm/changelogOverrides.d.ts +5 -5
  23. package/dist/esm/changelogOverrides.js +23 -23
  24. package/dist/esm/classifyChangelogCommit.js +4 -4
  25. package/dist/esm/commitCommand.js +3 -3
  26. package/dist/esm/compareVersions.js +13 -13
  27. package/dist/esm/configSchema.d.ts +2 -2
  28. package/dist/esm/configSchema.js +1 -1
  29. package/dist/esm/createGithubRelease.d.ts +4 -4
  30. package/dist/esm/createGithubRelease.js +10 -10
  31. package/dist/esm/createGithubReleaseCommand.js +9 -9
  32. package/dist/esm/createTags.d.ts +2 -2
  33. package/dist/esm/createTags.js +8 -8
  34. package/dist/esm/decideDirectReleases.d.ts +5 -5
  35. package/dist/esm/decideDirectReleases.js +25 -25
  36. package/dist/esm/decideRelease.d.ts +1 -1
  37. package/dist/esm/decideRelease.js +2 -2
  38. package/dist/esm/deriveDirectoryConfig.d.ts +1 -1
  39. package/dist/esm/deriveDirectoryConfig.js +18 -18
  40. package/dist/esm/deriveWorkspaceConfig.js +4 -4
  41. package/dist/esm/detectPackageManager.js +5 -5
  42. package/dist/esm/detectUndeclaredTagPrefixes.js +6 -6
  43. package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
  44. package/dist/esm/enumerateReleaseWindows.js +7 -7
  45. package/dist/esm/findTransitiveDependents.js +5 -5
  46. package/dist/esm/findUnroutedEntryScopes.js +11 -11
  47. package/dist/esm/format.d.ts +1 -1
  48. package/dist/esm/format.js +1 -1
  49. package/dist/esm/formatPrepareFiles.d.ts +3 -3
  50. package/dist/esm/formatPrepareFiles.js +10 -10
  51. package/dist/esm/formatPrepareWarnings.js +6 -6
  52. package/dist/esm/hasPrettierConfig.js +2 -2
  53. package/dist/esm/init/checks.js +5 -3
  54. package/dist/esm/init/detectRepoType.js +3 -3
  55. package/dist/esm/init/initCommand.d.ts +4 -4
  56. package/dist/esm/init/initCommand.js +7 -7
  57. package/dist/esm/init/parseJsonRecord.d.ts +1 -1
  58. package/dist/esm/init/parseJsonRecord.js +4 -4
  59. package/dist/esm/init/scaffold.d.ts +4 -4
  60. package/dist/esm/init/scaffold.js +15 -15
  61. package/dist/esm/init/templates.d.ts +10 -10
  62. package/dist/esm/init/templates.js +5 -5
  63. package/dist/esm/init/updateTemplatesCommand.d.ts +2 -2
  64. package/dist/esm/init/updateTemplatesCommand.js +14 -12
  65. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +1 -1
  66. package/dist/esm/injectReleaseNotesIntoReadme.js +6 -6
  67. package/dist/esm/injectSection.js +8 -8
  68. package/dist/esm/loadChangelogOverrides.d.ts +1 -1
  69. package/dist/esm/loadChangelogOverrides.js +9 -9
  70. package/dist/esm/loadConfig.d.ts +3 -3
  71. package/dist/esm/loadConfig.js +10 -10
  72. package/dist/esm/loadValidatedConfig.js +4 -4
  73. package/dist/esm/mergeConfig.d.ts +1 -1
  74. package/dist/esm/mergeConfig.js +27 -27
  75. package/dist/esm/monorepoConfigAssertions.js +15 -15
  76. package/dist/esm/npmRegistry.js +4 -4
  77. package/dist/esm/packsPath.js +3 -3
  78. package/dist/esm/parseChangeRecordBlock.js +33 -33
  79. package/dist/esm/parseCommitMessage.js +5 -5
  80. package/dist/esm/planChangelogFiles.js +8 -8
  81. package/dist/esm/planReleaseNotesPreviews.js +10 -10
  82. package/dist/esm/planVersionBump.js +24 -16
  83. package/dist/esm/prepareArgs.d.ts +9 -9
  84. package/dist/esm/prepareArgs.js +11 -11
  85. package/dist/esm/prepareCommand.js +23 -23
  86. package/dist/esm/prepareComposite.d.ts +4 -4
  87. package/dist/esm/prepareComposite.js +16 -16
  88. package/dist/esm/{releasePrepareTargets.d.ts → prepareReleaseTargets.d.ts} +9 -9
  89. package/dist/esm/{releasePrepareTargets.js → prepareReleaseTargets.js} +40 -40
  90. package/dist/esm/prepareRunContext.d.ts +1 -1
  91. package/dist/esm/prepareRunContext.js +5 -5
  92. package/dist/esm/prepareTarget.d.ts +3 -3
  93. package/dist/esm/prepareTarget.js +41 -39
  94. package/dist/esm/previewTagPrefixes.js +19 -19
  95. package/dist/esm/propagateBumps.js +8 -8
  96. package/dist/esm/publish.d.ts +2 -2
  97. package/dist/esm/publish.js +5 -5
  98. package/dist/esm/publishCommand.js +20 -20
  99. package/dist/esm/pushCommand.js +8 -8
  100. package/dist/esm/pushRelease.d.ts +1 -1
  101. package/dist/esm/pushRelease.js +2 -2
  102. package/dist/esm/readChangelogSections.d.ts +1 -1
  103. package/dist/esm/readChangelogSections.js +16 -16
  104. package/dist/esm/releasePlan.js +25 -25
  105. package/dist/esm/renderChangelogMarkdown.d.ts +4 -4
  106. package/dist/esm/renderChangelogMarkdown.js +18 -15
  107. package/dist/esm/renderReleaseNotes.d.ts +1 -1
  108. package/dist/esm/renderReleaseNotes.js +16 -16
  109. package/dist/esm/reportPrepare.js +34 -34
  110. package/dist/esm/resolveCommandTags.js +9 -9
  111. package/dist/esm/resolveReleaseDeclaration.d.ts +2 -2
  112. package/dist/esm/resolveReleaseDeclaration.js +22 -22
  113. package/dist/esm/resolveReleaseTags.js +17 -15
  114. package/dist/esm/resolveReleaseTargets.js +2 -2
  115. package/dist/esm/showTagPrefixesCommand.js +4 -4
  116. package/dist/esm/sync-labels/generateCommand.d.ts +3 -3
  117. package/dist/esm/sync-labels/generateCommand.js +10 -10
  118. package/dist/esm/sync-labels/initCommand.d.ts +3 -3
  119. package/dist/esm/sync-labels/initCommand.js +15 -15
  120. package/dist/esm/sync-labels/presets.js +3 -3
  121. package/dist/esm/sync-labels/resolveLabels.js +13 -13
  122. package/dist/esm/sync-labels/templates.d.ts +2 -2
  123. package/dist/esm/sync-labels/templates.js +1 -1
  124. package/dist/esm/tagCommand.js +4 -4
  125. package/dist/esm/topologicalSort.d.ts +2 -2
  126. package/dist/esm/topologicalSort.js +7 -7
  127. package/dist/esm/types.d.ts +3 -3
  128. package/dist/esm/validateAllChangelogOverrides.js +9 -9
  129. package/dist/esm/validateConfig.d.ts +1 -1
  130. package/dist/esm/validateConfig.js +60 -50
  131. package/dist/esm/validateOverridesCommand.js +2 -2
  132. package/docs/changelogs.md +5 -5
  133. package/docs/configuration.md +1 -1
  134. package/docs/work-types.md +1 -1
  135. package/package.json +1 -1
@@ -4,17 +4,17 @@ import { deleteFileIfExists } from "./deleteFileIfExists.js";
4
4
  import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
5
5
  const HANDOFF_FILES = new Set([RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE]);
6
6
  export function applyReleasePlan(plan) {
7
- const ordered = orderPlannedSteps(plan);
8
- const applied = [];
9
- for (const [index, step] of ordered.entries()) {
7
+ const orderedSteps = orderPlannedSteps(plan);
8
+ const appliedWrites = [];
9
+ for (const [index, step] of orderedSteps.entries()) {
10
10
  const outcome = applyStep(step);
11
11
  if (outcome.outcome === 'failed') {
12
- const unwritten = ordered.slice(index).map((pending) => pending.path);
13
- throw new Error(describeApplyFailure({ failedStep: step, error: outcome.error, applied, unwritten }));
12
+ const unwrittenPaths = orderedSteps.slice(index).map((pending) => pending.path);
13
+ throw new Error(describeApplyFailure({ failedStep: step, error: outcome.error, appliedWrites, unwrittenPaths }));
14
14
  }
15
- applied.push({ path: step.path, created: outcome.outcome === 'created' });
15
+ appliedWrites.push({ path: step.path, created: outcome.outcome === 'created' });
16
16
  }
17
- return applied.map((write) => write.path);
17
+ return appliedWrites.map((write) => write.path);
18
18
  }
19
19
  function applyStep(step) {
20
20
  if (step.kind === 'remove') {
@@ -33,43 +33,43 @@ function applyStep(step) {
33
33
  return { outcome: result.outcome === 'created' ? 'created' : 'applied' };
34
34
  }
35
35
  function orderPlannedSteps(plan) {
36
- const ordered = [
36
+ const orderedSteps = [
37
37
  ...plan.writes.map((write) => ({ kind: 'write', ...write })),
38
38
  ...plan.removals.map((path) => ({ kind: 'remove', path })),
39
39
  ];
40
40
  if (plan.summary.length > 0) {
41
- ordered.push({ kind: 'write', path: RELEASE_SUMMARY_FILE, content: plan.summary });
41
+ orderedSteps.push({ kind: 'write', path: RELEASE_SUMMARY_FILE, content: plan.summary });
42
42
  }
43
43
  if (plan.tags.length > 0) {
44
- ordered.push({ kind: 'write', path: RELEASE_TAGS_FILE, content: plan.tags.join('\n') });
44
+ orderedSteps.push({ kind: 'write', path: RELEASE_TAGS_FILE, content: plan.tags.join('\n') });
45
45
  }
46
- return ordered;
46
+ return orderedSteps;
47
47
  }
48
48
  function describeApplyFailure(args) {
49
- const { failedStep, error, applied, unwritten } = args;
50
- const total = applied.length + unwritten.length;
49
+ const { failedStep, error, appliedWrites, unwrittenPaths } = args;
50
+ const totalCount = appliedWrites.length + unwrittenPaths.length;
51
51
  const verb = failedStep.kind === 'remove' ? 'remove' : 'write';
52
52
  const lines = [`Failed to ${verb} ${failedStep.path}: ${error ?? 'unknown error'}`, ''];
53
- if (applied.length === 0) {
54
- lines.push(`The release was not applied. None of its ${total} files were written.`);
53
+ if (appliedWrites.length === 0) {
54
+ lines.push(`The release was not applied. None of its ${totalCount} files were written.`);
55
55
  return lines.join('\n');
56
56
  }
57
- lines.push(`The release was partially applied. ${applied.length} of ${total} files were written:`, ...applied.map((write) => ` ${write.path}`), '', 'Not written:', ...unwritten.map((path) => ` ${path}`), ...describeRecovery(applied));
57
+ lines.push(`The release was partially applied. ${appliedWrites.length} of ${totalCount} files were written:`, ...appliedWrites.map((write) => ` ${write.path}`), '', 'Not written:', ...unwrittenPaths.map((path) => ` ${path}`), ...describeRecovery(appliedWrites));
58
58
  return lines.join('\n');
59
59
  }
60
- function describeRecovery(applied) {
61
- const releaseWrites = applied.filter((write) => !HANDOFF_FILES.has(write.path));
62
- const replaced = releaseWrites.filter((write) => !write.created).map((write) => write.path);
63
- const created = releaseWrites.filter((write) => write.created).map((write) => write.path);
64
- if (replaced.length === 0 && created.length === 0) {
60
+ function describeRecovery(appliedWrites) {
61
+ const releaseWrites = appliedWrites.filter((write) => !HANDOFF_FILES.has(write.path));
62
+ const replacedPaths = releaseWrites.filter((write) => !write.created).map((write) => write.path);
63
+ const createdPaths = releaseWrites.filter((write) => write.created).map((write) => write.path);
64
+ if (replacedPaths.length === 0 && createdPaths.length === 0) {
65
65
  return [];
66
66
  }
67
67
  const lines = ['', 'Undo the written release files before re-running `release-kit prepare`:'];
68
- if (replaced.length > 0) {
69
- lines.push(` git restore ${replaced.join(' ')}`);
68
+ if (replacedPaths.length > 0) {
69
+ lines.push(` git restore ${replacedPaths.join(' ')}`);
70
70
  }
71
- if (created.length > 0) {
72
- lines.push(` rm ${created.join(' ')}`);
71
+ if (createdPaths.length > 0) {
72
+ lines.push(` rm ${createdPaths.join(' ')}`);
73
73
  }
74
74
  return lines;
75
75
  }
@@ -13,7 +13,7 @@ export interface RenderChangelogMarkdownOptions {
13
13
  /** Options for `renderChangelogFiles`. */
14
14
  export interface RenderChangelogFilesOptions extends RenderChangelogMarkdownOptions {
15
15
  /** How many of the newest sections the main file keeps; every section when omitted. Must be at least 1. */
16
- keepVersions?: number;
16
+ keptVersionCount?: number;
17
17
  }
18
18
  /** A rendered archive file: the sections of one major version that the main file does not keep. */
19
19
  export interface RenderedChangelogArchive {
@@ -28,9 +28,9 @@ export interface RenderedChangelogFiles {
28
28
  /**
29
29
  * Renders entries and preserved sections into `CHANGELOG.md` and its per-major archive files.
30
30
  *
31
- * The main file keeps the newest `keepVersions` sections, counting preserved sections, and links to each archive file
32
- * from a paragraph below its header. Each older section goes to the archive file of its major version. Every file
33
- * has the same header and footer.
31
+ * The main file keeps as many of the newest sections as `keptVersionCount` names, counting preserved sections, and
32
+ * links to each archive file from a paragraph below its header. Each older section goes to the archive file of its
33
+ * major version. Every file has the same header and footer.
34
34
  */
35
35
  export declare function renderChangelogFiles(entries: ChangelogEntry[], options?: RenderChangelogFilesOptions): RenderedChangelogFiles;
36
36
  /**
@@ -8,19 +8,25 @@ All notable changes to this project will be documented in this file.`;
8
8
  const CHANGELOG_FOOTER = `<!-- Generated by release-kit. Do not edit this file. Use .meta/changelog-overrides.json to override entries. -->`;
9
9
  export function renderChangelogFiles(entries, options) {
10
10
  const sections = interleavePreservedSections(entries.map((entry) => ({ version: entry.version, text: renderReleaseNotesSingle(entry, options) })), options?.preservedSections ?? []).filter((section) => section.text.length > 0);
11
- const keepVersions = options?.keepVersions;
12
- if (keepVersions === undefined || sections.length <= keepVersions) {
11
+ const keptVersionCount = options?.keptVersionCount;
12
+ if (keptVersionCount === undefined || sections.length <= keptVersionCount) {
13
13
  return { main: renderFile(sections), archives: [] };
14
14
  }
15
- const archived = groupByMajor(sections.slice(keepVersions));
15
+ const archivedGroups = groupByMajor(sections.slice(keptVersionCount));
16
16
  return {
17
- main: renderFile(sections.slice(0, keepVersions), describeArchivePointer(archived.map(({ major }) => major))),
18
- archives: archived.map(({ major, sections: majorSections }) => ({ major, content: renderFile(majorSections) })),
17
+ main: renderFile(sections.slice(0, keptVersionCount), describeArchivePointer(archivedGroups.map(({ major }) => major))),
18
+ archives: archivedGroups.map(({ major, sections: majorSections }) => ({
19
+ major,
20
+ content: renderFile(majorSections),
21
+ })),
19
22
  };
20
23
  }
21
24
  export function renderChangelogMarkdown(entries, options) {
22
25
  return renderChangelogFiles(entries, options).main;
23
26
  }
27
+ function appendTrailingNewline(section) {
28
+ return { version: section.version, text: `${section.text}\n` };
29
+ }
24
30
  function describeArchivePointer(majors) {
25
31
  const links = majors.map((major) => `[v${major}](${describeArchivePath(major)})`);
26
32
  return `Earlier versions: ${links.join(', ')}.`;
@@ -39,21 +45,21 @@ function groupByMajor(sections) {
39
45
  }
40
46
  return Array.from(groups, ([major, majorSections]) => ({ major, sections: majorSections })).toSorted((a, b) => b.major - a.major);
41
47
  }
42
- function interleavePreservedSections(rendered, preserved) {
43
- const sorted = preserved.toSorted((a, b) => compareVersionsDescending(a.version, b.version));
44
- const pending = sorted.map(withTrailingNewline);
48
+ function interleavePreservedSections(renderedSections, preservedSections) {
49
+ const sortedSections = preservedSections.toSorted((a, b) => compareVersionsDescending(a.version, b.version));
50
+ const pendingSections = sortedSections.map(appendTrailingNewline);
45
51
  const result = [];
46
52
  let index = 0;
47
- for (const part of rendered) {
48
- let section = pending[index];
53
+ for (const part of renderedSections) {
54
+ let section = pendingSections[index];
49
55
  while (section !== undefined && compareVersionsDescending(section.version, part.version) < 0) {
50
56
  result.push(section);
51
57
  index++;
52
- section = pending[index];
58
+ section = pendingSections[index];
53
59
  }
54
60
  result.push(part);
55
61
  }
56
- result.push(...pending.slice(index));
62
+ result.push(...pendingSections.slice(index));
57
63
  return result;
58
64
  }
59
65
  function renderFile(sections, paragraph) {
@@ -71,6 +77,3 @@ function renderFile(sections, paragraph) {
71
77
  parts.push(CHANGELOG_FOOTER);
72
78
  return parts.join('\n\n') + '\n';
73
79
  }
74
- function withTrailingNewline(section) {
75
- return { version: section.version, text: `${section.text}\n` };
76
- }
@@ -4,7 +4,7 @@ export interface RenderOptions {
4
4
  /** Predicate to filter sections. When absent, all sections are included. */
5
5
  filter?: (section: ChangelogSection) => boolean;
6
6
  /** Whether to include the version/date heading. Defaults to `true`. */
7
- includeHeading?: boolean;
7
+ shouldIncludeHeading?: boolean;
8
8
  /**
9
9
  * Section titles in desired priority order. Known titles are emitted first in this order;
10
10
  * titles not listed preserve their relative position after known ones. When absent, section
@@ -3,26 +3,26 @@ function getBreakingPrefix() {
3
3
  const { emoji, label } = CANONICAL_TAXONOMY.markers.breaking;
4
4
  return `${emoji} **${label}:** `;
5
5
  }
6
- function allSections() {
6
+ function matchAllSections() {
7
7
  return true;
8
8
  }
9
- function publicSections(section) {
9
+ function matchPublicSections(section) {
10
10
  return section.audience === 'all';
11
11
  }
12
12
  export function matchesAudience(audience) {
13
- return audience === 'dev' ? allSections : publicSections;
13
+ return audience === 'dev' ? matchAllSections : matchPublicSections;
14
14
  }
15
15
  export function renderReleaseNotesSingle(entry, options) {
16
16
  const filter = options?.filter;
17
- const includeHeading = options?.includeHeading ?? true;
17
+ const shouldIncludeHeading = options?.shouldIncludeHeading ?? true;
18
18
  const sectionOrder = options?.sectionOrder;
19
- const filtered = filter !== undefined ? entry.sections.filter(filter) : entry.sections;
20
- const sections = sectionOrder !== undefined ? sortSectionsByOrder(filtered, sectionOrder) : filtered;
19
+ const filteredSections = filter !== undefined ? entry.sections.filter(filter) : entry.sections;
20
+ const sections = sectionOrder !== undefined ? sortSectionsByOrder(filteredSections, sectionOrder) : filteredSections;
21
21
  if (sections.length === 0) {
22
22
  return '';
23
23
  }
24
24
  const lines = [];
25
- if (includeHeading) {
25
+ if (shouldIncludeHeading) {
26
26
  lines.push(`## ${entry.version} — ${entry.date}`);
27
27
  }
28
28
  for (const section of sections) {
@@ -45,20 +45,20 @@ export function renderReleaseNotesSingle(entry, options) {
45
45
  return lines.join('\n') + '\n';
46
46
  }
47
47
  function sortSectionsByOrder(sections, order) {
48
- const priority = new Map();
48
+ const priorityByTitle = new Map();
49
49
  for (const [index, title] of order.entries()) {
50
- priority.set(title, index);
50
+ priorityByTitle.set(title, index);
51
51
  }
52
- const indexed = sections.map((section, index) => ({ section, index }));
53
- indexed.sort((a, b) => {
54
- const priorityA = priority.get(a.section.title) ?? Infinity;
55
- const priorityB = priority.get(b.section.title) ?? Infinity;
56
- if (priorityA !== priorityB) {
57
- return priorityA - priorityB;
52
+ const indexedSections = sections.map((section, index) => ({ section, index }));
53
+ indexedSections.sort((a, b) => {
54
+ const aPriority = priorityByTitle.get(a.section.title) ?? Infinity;
55
+ const bPriority = priorityByTitle.get(b.section.title) ?? Infinity;
56
+ if (aPriority !== bPriority) {
57
+ return aPriority - bPriority;
58
58
  }
59
59
  return a.index - b.index;
60
60
  });
61
- return indexed.map(({ section }) => section);
61
+ return indexedSections.map(({ section }) => section);
62
62
  }
63
63
  function indentBodyLines(body) {
64
64
  return body.split('\n').map((line) => (line.length === 0 ? '' : ` ${line}`));
@@ -1,5 +1,5 @@
1
1
  import { formatGlyphLine, formatStatusLine, measureGlyphColumn, STATUS_GLYPHS, } from '@williamthorsen/nmr-core';
2
- import { bold, dim, sectionHeader } from "./format.js";
2
+ import { bold, dim, renderSectionHeader } from "./format.js";
3
3
  import { formatBumpFiles, formatChangelogFiles, formatPreviewFiles } from "./formatPrepareFiles.js";
4
4
  import { formatChangeRecordWarnings, formatPolicyViolations, formatUnparseableWarning, formatWarnings, } from "./formatPrepareWarnings.js";
5
5
  import { RELEASE_GLYPHS } from "./glyphs.js";
@@ -12,15 +12,15 @@ export function reportPrepare(plan, options) {
12
12
  return formatSingleWorkspace(plan, options);
13
13
  }
14
14
  function formatSingleWorkspace(result, options) {
15
- const dryRun = !options.applied;
15
+ const isDryRun = !options.applied;
16
16
  const { style } = options;
17
17
  const lines = [];
18
18
  const workspace = result.targets[0];
19
19
  if (workspace === undefined || workspace.kind === 'composite') {
20
20
  return '';
21
21
  }
22
- const since = workspace.previousTag === undefined ? 'the beginning' : workspace.previousTag;
23
- lines.push(dim(`Found ${workspace.commitCount} commits since ${since}`));
22
+ const sinceLabel = workspace.previousTag === undefined ? 'the beginning' : workspace.previousTag;
23
+ lines.push(dim(`Found ${workspace.commitCount} commits since ${sinceLabel}`));
24
24
  if (workspace.parsedCommitCount !== undefined) {
25
25
  lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
26
26
  }
@@ -32,8 +32,8 @@ function formatSingleWorkspace(result, options) {
32
32
  formatWarnings(lines, result, style);
33
33
  return lines.join('\n');
34
34
  }
35
- if (workspace.setVersion !== undefined) {
36
- lines.push(` Using version override: ${workspace.setVersion}`);
35
+ if (workspace.versionOverride !== undefined) {
36
+ lines.push(` Using version override: ${workspace.versionOverride}`);
37
37
  }
38
38
  else if (workspace.bumpOverride !== undefined) {
39
39
  lines.push(` Using bump override: ${workspace.bumpOverride}`);
@@ -41,35 +41,35 @@ function formatSingleWorkspace(result, options) {
41
41
  if (workspace.releaseType !== undefined) {
42
42
  lines.push(dim(`Bumping versions (${workspace.releaseType})...`));
43
43
  }
44
- else if (workspace.setVersion !== undefined) {
44
+ else if (workspace.versionOverride !== undefined) {
45
45
  lines.push(dim(`Bumping versions (version override)...`));
46
46
  }
47
- if (workspace.setVersion !== undefined) {
47
+ if (workspace.versionOverride !== undefined) {
48
48
  lines.push(formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${workspace.currentVersion} → ${bold(workspace.newVersion)} (version override)`));
49
49
  }
50
50
  else if (workspace.releaseType !== undefined) {
51
51
  lines.push(formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${workspace.currentVersion} → ${bold(workspace.newVersion)} (${workspace.releaseType})`));
52
52
  }
53
53
  formatChangeRecordWarnings(lines, workspace, style);
54
- formatBumpFiles(lines, workspace, dryRun);
54
+ formatBumpFiles(lines, workspace, isDryRun);
55
55
  lines.push(dim('Generating changelogs...'));
56
- formatChangelogFiles(lines, workspace, dryRun, style);
57
- formatPreviewFiles(lines, workspace, dryRun);
56
+ formatChangelogFiles(lines, workspace, isDryRun, style);
57
+ formatPreviewFiles(lines, workspace, isDryRun);
58
58
  formatFormatCommand(lines, result, options);
59
59
  formatWarnings(lines, result, style);
60
60
  lines.push(formatStatusLine(style, 'passed', 'Release preparation complete.'), ` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(workspace.tag))}`);
61
61
  return lines.join('\n');
62
62
  }
63
63
  function formatMultiWorkspace(result, options) {
64
- const dryRun = !options.applied;
64
+ const isDryRun = !options.applied;
65
65
  const { style } = options;
66
66
  const lines = [];
67
67
  for (const target of result.targets) {
68
68
  if (target.kind === 'composite') {
69
- formatProjectSection(lines, target, dryRun, style);
69
+ formatProjectSection(lines, target, isDryRun, style);
70
70
  }
71
71
  else {
72
- formatWorkspaceSection(lines, target, dryRun, style);
72
+ formatWorkspaceSection(lines, target, isDryRun, style);
73
73
  }
74
74
  }
75
75
  formatFormatCommand(lines, result, options);
@@ -85,10 +85,10 @@ function formatMultiWorkspace(result, options) {
85
85
  }
86
86
  return lines.join('\n');
87
87
  }
88
- function formatProjectSection(lines, project, dryRun, style) {
89
- lines.push(`\n${sectionHeader(project.targetId === PROJECT_TARGET_ID ? 'project' : project.targetId)}`);
90
- const since = project.previousTag === undefined ? '(no previous release found)' : `since ${project.previousTag}`;
91
- lines.push(dim(` Found ${project.commitCount} commits ${since}`));
88
+ function formatProjectSection(lines, project, isDryRun, style) {
89
+ lines.push(`\n${renderSectionHeader(project.targetId === PROJECT_TARGET_ID ? 'project' : project.targetId)}`);
90
+ const sinceLabel = project.previousTag === undefined ? '(no previous release found)' : `since ${project.previousTag}`;
91
+ lines.push(dim(` Found ${project.commitCount} commits ${sinceLabel}`));
92
92
  formatPolicyViolations(lines, project.policyViolations, style, ' ');
93
93
  if (project.status === 'skipped') {
94
94
  formatUnparseableWarning(lines, project, style, ' ');
@@ -107,7 +107,7 @@ function formatProjectSection(lines, project, dryRun, style) {
107
107
  formatChangeRecordWarnings(lines, project, style, ' ');
108
108
  lines.push(dim(` Bumping versions (${releaseType})...`), ` ${formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${currentVersion} → ${bold(newVersion)} (${releaseType})`)}`);
109
109
  for (const file of project.bumpedFiles) {
110
- if (dryRun) {
110
+ if (isDryRun) {
111
111
  lines.push(dim(` [dry-run] Would bump ${file}`));
112
112
  }
113
113
  else {
@@ -115,16 +115,16 @@ function formatProjectSection(lines, project, dryRun, style) {
115
115
  }
116
116
  }
117
117
  lines.push(dim(' Generating changelogs...'));
118
- formatChangelogFiles(lines, project, dryRun, style, ' ');
119
- formatPreviewFiles(lines, project, dryRun, ' ');
118
+ formatChangelogFiles(lines, project, isDryRun, style, ' ');
119
+ formatPreviewFiles(lines, project, isDryRun, ' ');
120
120
  lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(tag))}`);
121
121
  }
122
- function formatWorkspaceSection(lines, workspace, dryRun, style) {
122
+ function formatWorkspaceSection(lines, workspace, isDryRun, style) {
123
123
  if (workspace.name !== undefined) {
124
- lines.push(`\n${sectionHeader(workspace.name)}`);
124
+ lines.push(`\n${renderSectionHeader(workspace.name)}`);
125
125
  }
126
- const since = workspace.previousTag === undefined ? '(no previous release found)' : `since ${workspace.previousTag}`;
127
- lines.push(dim(` Found ${workspace.commitCount} commits ${since}`));
126
+ const sinceLabel = workspace.previousTag === undefined ? '(no previous release found)' : `since ${workspace.previousTag}`;
127
+ lines.push(dim(` Found ${workspace.commitCount} commits ${sinceLabel}`));
128
128
  if (workspace.status === 'skipped') {
129
129
  formatUnparseableWarning(lines, workspace, style, ' ');
130
130
  formatPolicyViolations(lines, workspace.policyViolations, style, ' ');
@@ -140,24 +140,24 @@ function formatWorkspaceSection(lines, workspace, dryRun, style) {
140
140
  formatChangeRecordWarnings(lines, workspace, style, ' ');
141
141
  formatBumpLabels(lines, workspace, isPropagatedOnly);
142
142
  formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly, style);
143
- formatBumpFiles(lines, workspace, dryRun, ' ');
143
+ formatBumpFiles(lines, workspace, isDryRun, ' ');
144
144
  lines.push(dim(' Generating changelogs...'));
145
- formatChangelogFiles(lines, workspace, dryRun, style, ' ');
146
- formatPreviewFiles(lines, workspace, dryRun, ' ');
145
+ formatChangelogFiles(lines, workspace, isDryRun, style, ' ');
146
+ formatPreviewFiles(lines, workspace, isDryRun, ' ');
147
147
  lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(workspace.tag))}`);
148
148
  }
149
149
  function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
150
150
  if (isPropagatedOnly && propagatedFrom !== undefined) {
151
- const depNames = propagatedFrom.map((p) => p.packageName).join(', ');
152
- lines.push(dim(` Bumped via dependency: ${depNames}`));
151
+ const dependencyNames = propagatedFrom.map((p) => p.packageName).join(', ');
152
+ lines.push(dim(` Bumped via dependency: ${dependencyNames}`));
153
153
  }
154
154
  else if (workspace.parsedCommitCount !== undefined && workspace.parsedCommitCount > 0) {
155
155
  lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
156
156
  }
157
157
  }
158
158
  function formatBumpLabels(lines, workspace, isPropagatedOnly) {
159
- if (workspace.setVersion !== undefined) {
160
- lines.push(` Using version override: ${workspace.setVersion}`);
159
+ if (workspace.versionOverride !== undefined) {
160
+ lines.push(` Using version override: ${workspace.versionOverride}`);
161
161
  }
162
162
  else if (workspace.bumpOverride !== undefined && !isPropagatedOnly) {
163
163
  lines.push(` Using bump override: ${workspace.bumpOverride}`);
@@ -165,13 +165,13 @@ function formatBumpLabels(lines, workspace, isPropagatedOnly) {
165
165
  if (workspace.releaseType !== undefined) {
166
166
  lines.push(dim(` Bumping versions (${workspace.releaseType})...`));
167
167
  }
168
- else if (workspace.setVersion !== undefined) {
168
+ else if (workspace.versionOverride !== undefined) {
169
169
  lines.push(dim(` Bumping versions (version override)...`));
170
170
  }
171
171
  }
172
172
  function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly, style) {
173
173
  const versions = `${workspace.currentVersion} → ${bold(workspace.newVersion)}`;
174
- if (workspace.setVersion !== undefined) {
174
+ if (workspace.versionOverride !== undefined) {
175
175
  lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'bump', `${versions} (version override)`)}`);
176
176
  }
177
177
  else if (workspace.releaseType !== undefined) {
@@ -29,12 +29,12 @@ export function resolveCommandTags(tags, userConfig) {
29
29
  singleWorkspace = deriveWorkspaceConfig('.');
30
30
  }
31
31
  else {
32
- const merged = mergeMonorepoConfig(workspace.packageDirs, userConfig, readRootPackageVersion());
33
- const excluded = merged.excludedWorkspaces ?? [];
34
- workspaces = [...merged.workspaces, ...excluded];
35
- directories = merged.directories ?? [];
36
- project = merged.project;
37
- excludedPaths = new Set(excluded.map((excludedWorkspace) => excludedWorkspace.workspacePath));
32
+ const mergedConfig = mergeMonorepoConfig(workspace.packageDirs, userConfig, readRootPackageVersion());
33
+ const excludedWorkspaces = mergedConfig.excludedWorkspaces ?? [];
34
+ workspaces = [...mergedConfig.workspaces, ...excludedWorkspaces];
35
+ directories = mergedConfig.directories ?? [];
36
+ project = mergedConfig.project;
37
+ excludedPaths = new Set(excludedWorkspaces.map((excludedWorkspace) => excludedWorkspace.workspacePath));
38
38
  }
39
39
  }
40
40
  catch (error) {
@@ -69,15 +69,15 @@ export function resolveCommandTags(tags, userConfig) {
69
69
  return dropExcludedTags(resolvedTags, excludedPaths, userConfig?.releases === undefined ? 'legacy' : 'releases');
70
70
  }
71
71
  function dropExcludedTags(resolvedTags, excludedPaths, surface) {
72
- const retained = [];
72
+ const retainedTags = [];
73
73
  for (const resolvedTag of resolvedTags) {
74
74
  if (resolvedTag.kind === 'package' && excludedPaths.has(resolvedTag.workspacePath)) {
75
75
  const setting = surface === 'releases' ? `releases.${resolvedTag.dir}: false` : undefined;
76
76
  console.warn(formatExcludedSkip(resolvedTag, setting));
77
77
  }
78
78
  else {
79
- retained.push(resolvedTag);
79
+ retainedTags.push(resolvedTag);
80
80
  }
81
81
  }
82
- return retained;
82
+ return retainedTags;
83
83
  }
@@ -24,9 +24,9 @@ export interface DeclaredDirectory {
24
24
  /** The release targets that a config declares over the discovered workspaces. */
25
25
  export interface ReleaseDeclaration {
26
26
  /** Retained workspaces in discovery order, each with its target name and legacy identities applied. */
27
- retained: WorkspaceConfig[];
27
+ retainedWorkspaces: WorkspaceConfig[];
28
28
  /** Discovered workspaces that the config excludes, in discovery order. */
29
- excluded: WorkspaceConfig[];
29
+ excludedWorkspaces: WorkspaceConfig[];
30
30
  /** Directories that `releases` entries declare as targets, in config order. */
31
31
  directories: DeclaredDirectory[];
32
32
  composite?: DeclaredComposite;
@@ -3,8 +3,8 @@ export function getTargetName(workspace) {
3
3
  return workspace.targetName ?? workspace.dir;
4
4
  }
5
5
  export function normalizeWorkspacePath(configuredPath) {
6
- const normalized = path.posix.normalize(configuredPath.replaceAll('\\', '/'));
7
- return normalized.replace(/^\.\//, '').replace(/\/+$/, '');
6
+ const normalizedPath = path.posix.normalize(configuredPath.replaceAll('\\', '/'));
7
+ return normalizedPath.replace(/^\.\//, '').replace(/\/+$/, '');
8
8
  }
9
9
  export function resolveReleaseDeclaration(workspaces, userConfig) {
10
10
  if (userConfig?.releases !== undefined) {
@@ -19,9 +19,9 @@ function applyLegacyIdentities(workspace, legacyIdentities) {
19
19
  return { ...workspace, legacyIdentities: legacyIdentities.map((identity) => ({ ...identity })) };
20
20
  }
21
21
  function claimPath(entryByPath, name, claimedPath, kind) {
22
- const claimedBy = entryByPath.get(claimedPath);
23
- if (claimedBy !== undefined) {
24
- throw new Error(`releases.${name}: ${kind} '${claimedPath}' is already declared by releases.${claimedBy}; declare each ${kind} once`);
22
+ const claimingEntryName = entryByPath.get(claimedPath);
23
+ if (claimingEntryName !== undefined) {
24
+ throw new Error(`releases.${name}: ${kind} '${claimedPath}' is already declared by releases.${claimingEntryName}; declare each ${kind} once`);
25
25
  }
26
26
  entryByPath.set(claimedPath, name);
27
27
  }
@@ -40,9 +40,9 @@ function declareDirectory(name, entry, normalizedPath) {
40
40
  }),
41
41
  };
42
42
  }
43
- function resolveCompositeMembers(composite, retained, excluded, directories) {
43
+ function resolveCompositeMembers(composite, retainedWorkspaces, excludedWorkspaces, directories) {
44
44
  const membersByName = new Map([
45
- ...retained.map((workspace) => [getTargetName(workspace), { name: getTargetName(workspace), paths: workspace.paths }]),
45
+ ...retainedWorkspaces.map((workspace) => [getTargetName(workspace), { name: getTargetName(workspace), paths: workspace.paths }]),
46
46
  ...directories.map((directory) => [directory.name, { name: directory.name, paths: directory.paths }]),
47
47
  ]);
48
48
  return composite.includes.map((memberName) => {
@@ -54,7 +54,7 @@ function resolveCompositeMembers(composite, retained, excluded, directories) {
54
54
  if (memberName === composite.name) {
55
55
  throw new Error(`${label} is the composite itself; a composite includes package and directory targets only`);
56
56
  }
57
- if (excluded.some((workspace) => workspace.dir === memberName)) {
57
+ if (excludedWorkspaces.some((workspace) => workspace.dir === memberName)) {
58
58
  throw new Error(`${label} is excluded, so the composite cannot include it`);
59
59
  }
60
60
  throw new Error(`${label} is not a release target. Known targets: ${membersByName.keys().toArray().join(', ')}`);
@@ -62,18 +62,18 @@ function resolveCompositeMembers(composite, retained, excluded, directories) {
62
62
  }
63
63
  function resolveLegacyBlocks(workspaces, overrides, project) {
64
64
  const overridesByDir = new Map(overrides.map((override) => [override.dir, override]));
65
- const retained = [];
66
- const excluded = [];
65
+ const retainedWorkspaces = [];
66
+ const excludedWorkspaces = [];
67
67
  for (const workspace of workspaces) {
68
68
  const override = overridesByDir.get(workspace.dir);
69
69
  if (override?.shouldExclude === true) {
70
- excluded.push(workspace);
70
+ excludedWorkspaces.push(workspace);
71
71
  }
72
72
  else {
73
- retained.push(applyLegacyIdentities(workspace, override?.legacyIdentities));
73
+ retainedWorkspaces.push(applyLegacyIdentities(workspace, override?.legacyIdentities));
74
74
  }
75
75
  }
76
- const declaration = { retained, excluded, directories: [] };
76
+ const declaration = { retainedWorkspaces, excludedWorkspaces, directories: [] };
77
77
  if (project !== undefined) {
78
78
  declaration.composite = {
79
79
  ...(project.paths !== undefined && { paths: [...project.paths] }),
@@ -87,7 +87,7 @@ function resolveReleasesBlock(workspaces, releases) {
87
87
  const workspaceByDir = new Map(workspaces.map((workspace) => [workspace.dir, workspace]));
88
88
  const entryByPath = new Map();
89
89
  const excludedPaths = new Set();
90
- const renamed = new Map();
90
+ const renamedWorkspaces = new Map();
91
91
  const directories = [];
92
92
  let composite;
93
93
  for (const [name, entry] of Object.entries(releases)) {
@@ -126,22 +126,22 @@ function resolveReleasesBlock(workspaces, releases) {
126
126
  "whose tag prefix derives from its package name; declare the workspace's earlier prefixes in legacyIdentities");
127
127
  }
128
128
  claimPath(entryByPath, name, workspace.workspacePath, 'workspace');
129
- const named = applyLegacyIdentities(workspace, entry.legacyIdentities);
130
- renamed.set(workspace.workspacePath, name === workspace.dir ? named : { ...named, targetName: name });
129
+ const identifiedWorkspace = applyLegacyIdentities(workspace, entry.legacyIdentities);
130
+ renamedWorkspaces.set(workspace.workspacePath, name === workspace.dir ? identifiedWorkspace : { ...identifiedWorkspace, targetName: name });
131
131
  }
132
- const retained = [];
133
- const excluded = [];
132
+ const retainedWorkspaces = [];
133
+ const excludedWorkspaces = [];
134
134
  for (const workspace of workspaces) {
135
135
  if (excludedPaths.has(workspace.workspacePath)) {
136
- excluded.push(workspace);
136
+ excludedWorkspaces.push(workspace);
137
137
  }
138
138
  else {
139
- retained.push(renamed.get(workspace.workspacePath) ?? workspace);
139
+ retainedWorkspaces.push(renamedWorkspaces.get(workspace.workspacePath) ?? workspace);
140
140
  }
141
141
  }
142
- const declaration = { retained, excluded, directories };
142
+ const declaration = { retainedWorkspaces, excludedWorkspaces, directories };
143
143
  if (composite !== undefined) {
144
- const members = resolveCompositeMembers(composite, retained, excluded, directories);
144
+ const members = resolveCompositeMembers(composite, retainedWorkspaces, excludedWorkspaces, directories);
145
145
  declaration.composite = {
146
146
  name: composite.name,
147
147
  members: members.map((member) => member.name),