@williamthorsen/release-kit 12.0.0 → 13.1.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/.meta/changelog.json +229 -0
- package/.readyup/kits/default.js +18 -17
- package/.readyup/kits/npm-auto-publish.js +24 -23
- package/.readyup/manifest.json +16 -11
- package/CHANGELOG.md +64 -0
- package/README.md +27 -51
- package/bin/release-kit.js +1 -1
- package/dist/esm/assertCleanWorkingTree.d.ts +1 -5
- package/dist/esm/assertTaggedBaseline.d.ts +29 -0
- package/dist/esm/assertTaggedBaseline.js +41 -0
- package/dist/esm/attachChangelogDiagnostics.d.ts +1 -1
- package/dist/esm/attachChangelogDiagnostics.js +3 -0
- package/dist/esm/bin/release-kit.js +85 -22
- package/dist/esm/buildChangelogEntries.d.ts +23 -13
- package/dist/esm/buildChangelogEntries.js +36 -17
- package/dist/esm/buildDependencyGraph.d.ts +6 -6
- package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -8
- package/dist/esm/buildReleaseSummary.d.ts +2 -8
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +3 -5
- package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
- package/dist/esm/bumpVersion.d.ts +1 -8
- package/dist/esm/changelogJsonFile.d.ts +5 -7
- package/dist/esm/changelogJsonFile.js +2 -17
- package/dist/esm/changelogJsonUtils.d.ts +7 -10
- package/dist/esm/changelogOverrides.d.ts +59 -98
- package/dist/esm/classifyChangelogCommit.d.ts +7 -9
- package/dist/esm/classifyChangelogCommit.js +3 -2
- package/dist/esm/commitCommand.d.ts +2 -4
- package/dist/esm/compareVersions.d.ts +10 -4
- package/dist/esm/compareVersions.js +16 -0
- package/dist/esm/configFlagSchema.d.ts +1 -1
- package/dist/esm/createGithubRelease.d.ts +7 -19
- package/dist/esm/createGithubReleaseCommand.d.ts +5 -5
- package/dist/esm/createGithubReleaseCommand.js +9 -9
- package/dist/esm/createTags.d.ts +2 -3
- package/dist/esm/decideRelease.d.ts +2 -6
- package/dist/esm/defaults.d.ts +5 -24
- package/dist/esm/defaults.js +4 -5
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/deleteFileIfExists.d.ts +1 -1
- package/dist/esm/deriveReleaseNotesConfig.d.ts +20 -0
- package/dist/esm/deriveReleaseNotesConfig.js +21 -0
- package/dist/esm/deriveWorkspaceConfig.d.ts +3 -5
- package/dist/esm/detectPackageManager.d.ts +2 -4
- package/dist/esm/detectUndeclaredTagPrefixes.d.ts +4 -6
- package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
- package/dist/esm/determineBumpType.d.ts +1 -1
- package/dist/esm/discoverWorkspaces.d.ts +14 -15
- package/dist/esm/discoverWorkspaces.js +12 -12
- package/dist/esm/enterRepoRoot.d.ts +12 -0
- package/dist/esm/enterRepoRoot.js +16 -0
- package/dist/esm/enumerateReleaseWindows.d.ts +8 -8
- package/dist/esm/extractMigration.d.ts +6 -8
- package/dist/esm/findUnroutedEntryScopes.d.ts +16 -0
- package/dist/esm/findUnroutedEntryScopes.js +57 -0
- package/dist/esm/formatExcludedSkip.d.ts +3 -0
- package/dist/esm/formatExcludedSkip.js +3 -0
- package/dist/esm/formatPrivateSkip.d.ts +1 -6
- package/dist/esm/generateChangelogs.d.ts +10 -5
- package/dist/esm/glyphs.d.ts +1 -1
- package/dist/esm/help/prepareHelp.d.ts +3 -4
- package/dist/esm/help/prepareHelp.js +4 -3
- package/dist/esm/init/checks.d.ts +3 -3
- package/dist/esm/init/checks.js +4 -1
- package/dist/esm/init/detectRepoType.d.ts +2 -3
- package/dist/esm/init/hasProvenance.d.ts +2 -0
- package/dist/esm/init/hasProvenance.js +3 -0
- package/dist/esm/init/initCommand.d.ts +3 -6
- package/dist/esm/init/initCommand.js +10 -10
- package/dist/esm/init/parseJsonRecord.d.ts +1 -4
- package/dist/esm/init/scaffold.d.ts +1 -1
- package/dist/esm/init/templates.d.ts +9 -11
- package/dist/esm/init/templates.js +12 -11
- package/dist/esm/init/updateTemplatesCommand.d.ts +13 -0
- package/dist/esm/init/updateTemplatesCommand.js +64 -0
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +12 -17
- package/dist/esm/injectSection.d.ts +3 -3
- package/dist/esm/loadConfig.d.ts +16 -14
- package/dist/esm/loadConfig.js +2 -2
- package/dist/esm/loadValidatedConfig.d.ts +16 -19
- package/dist/esm/loadValidatedConfig.js +6 -1
- package/dist/esm/packsPath.d.ts +9 -9
- package/dist/esm/parseChangeRecordBlock.d.ts +2 -2
- package/dist/esm/parseCommitMessage.d.ts +17 -32
- package/dist/esm/parseCommitMessage.js +28 -37
- package/dist/esm/parseRequestedTags.d.ts +3 -3
- package/dist/esm/planReleaseNotesPreviews.d.ts +6 -6
- package/dist/esm/planVersionBump.d.ts +5 -3
- package/dist/esm/planVersionBump.js +3 -0
- package/dist/esm/prepareCommand.d.ts +4 -7
- package/dist/esm/prepareCommand.js +13 -6
- package/dist/esm/previewTagPrefixes.d.ts +8 -10
- package/dist/esm/propagateBumps.d.ts +6 -7
- package/dist/esm/publish.d.ts +1 -1
- package/dist/esm/publishCommand.d.ts +4 -3
- package/dist/esm/publishCommand.js +8 -8
- package/dist/esm/pushCommand.d.ts +4 -3
- package/dist/esm/pushCommand.js +8 -2
- package/dist/esm/pushRelease.d.ts +2 -2
- package/dist/esm/readChangelogSections.d.ts +33 -0
- package/dist/esm/readChangelogSections.js +80 -0
- package/dist/esm/releaseFiles.d.ts +5 -7
- package/dist/esm/releasePlan.d.ts +11 -13
- package/dist/esm/releasePrepare.d.ts +23 -22
- package/dist/esm/releasePrepare.js +31 -11
- package/dist/esm/releasePrepareMono.d.ts +9 -7
- package/dist/esm/releasePrepareMono.js +85 -33
- package/dist/esm/releasePrepareProject.d.ts +19 -29
- package/dist/esm/releasePrepareProject.js +31 -12
- package/dist/esm/renderChangelogMarkdown.d.ts +10 -4
- package/dist/esm/renderChangelogMarkdown.js +27 -2
- package/dist/esm/renderReleaseNotes.d.ts +3 -11
- package/dist/esm/renderReleaseNotes.js +2 -6
- package/dist/esm/reportPrepare.d.ts +4 -5
- package/dist/esm/reportPrepare.js +33 -19
- package/dist/esm/resolveCommandTags.d.ts +15 -11
- package/dist/esm/resolveCommandTags.js +35 -5
- package/dist/esm/resolveConfigFlag.d.ts +7 -0
- package/dist/esm/resolveConfigFlag.js +4 -0
- package/dist/esm/resolveReleaseTags.d.ts +6 -8
- package/dist/esm/resolveReleaseTags.js +1 -1
- package/dist/esm/resolveStylesOrExit.d.ts +2 -2
- package/dist/esm/showTagPrefixesCommand.d.ts +7 -10
- package/dist/esm/showTagPrefixesCommand.js +4 -4
- package/dist/esm/stripEmojiPrefix.d.ts +1 -0
- package/dist/esm/stripScope.d.ts +2 -2
- package/dist/esm/stripScope.js +3 -3
- package/dist/esm/sync-labels/generateCommand.d.ts +11 -10
- package/dist/esm/sync-labels/generateCommand.js +17 -9
- package/dist/esm/sync-labels/initCommand.d.ts +6 -7
- package/dist/esm/sync-labels/initCommand.js +2 -2
- package/dist/esm/sync-labels/presets.d.ts +5 -4
- package/dist/esm/sync-labels/presets.js +9 -9
- package/dist/esm/sync-labels/resolveLabels.d.ts +7 -7
- package/dist/esm/sync-labels/resolveLabels.js +2 -1
- package/dist/esm/sync-labels/retiredConfig.d.ts +4 -5
- package/dist/esm/sync-labels/syncCommand.d.ts +1 -4
- package/dist/esm/sync-labels/templates.d.ts +3 -3
- package/dist/esm/sync-labels/templates.js +3 -3
- package/dist/esm/sync-labels/typeLabels.d.ts +14 -0
- package/dist/esm/sync-labels/typeLabels.js +38 -0
- package/dist/esm/sync-labels/types.d.ts +4 -2
- package/dist/esm/tagCommand.d.ts +1 -1
- package/dist/esm/typeGuards.d.ts +2 -2
- package/dist/esm/types.d.ts +122 -93
- package/dist/esm/types.js +2 -1
- package/dist/esm/validateConfig.d.ts +2 -11
- package/dist/esm/validateConfig.js +2 -2
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -12
- package/dist/esm/validateOverridesCommand.d.ts +17 -20
- package/dist/esm/validateOverridesCommand.js +1 -1
- package/docs/api.md +6 -6
- package/docs/changelogs.md +63 -40
- package/docs/configuration.md +29 -27
- package/docs/editorial-overrides.md +23 -23
- package/docs/labels.md +37 -19
- package/docs/project-releases.md +13 -13
- package/docs/publishing.md +5 -5
- package/docs/readiness-checks.md +3 -3
- package/docs/releasing.md +50 -25
- package/docs/work-types.md +41 -57
- package/package.json +7 -6
- package/presets/labels/common.yaml +1 -41
- package/dist/esm/resolveReleaseNotesConfig.d.ts +0 -26
- package/dist/esm/resolveReleaseNotesConfig.js +0 -29
- package/dist/esm/workTypesData.d.ts +0 -50
- package/dist/esm/workTypesData.js +0 -145
- package/schemas/label-map.json +0 -24
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { join as joinPath } from 'node:path';
|
|
2
|
+
import { assertTaggedBaseline, findUntaggedBaseline } from "./assertTaggedBaseline.js";
|
|
2
3
|
import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
|
|
3
|
-
import { readReleaseHistory,
|
|
4
|
-
import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
|
|
4
|
+
import { readReleaseHistory, toReleaseEntries } from "./buildChangelogEntries.js";
|
|
5
5
|
import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
|
|
6
6
|
import { applyChangelogOverrides } from "./changelogOverrides.js";
|
|
7
7
|
import { decideRelease } from "./decideRelease.js";
|
|
8
8
|
import { DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
9
|
+
import { deriveSectionOrder } from "./deriveReleaseNotesConfig.js";
|
|
9
10
|
import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
|
|
10
11
|
import { planVersionBump } from "./planVersionBump.js";
|
|
12
|
+
import { planPreservedSections } from "./readChangelogSections.js";
|
|
11
13
|
import { renderChangelogMarkdown } from "./renderChangelogMarkdown.js";
|
|
12
|
-
import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
|
|
13
14
|
const ROOT_PACKAGE_FILE = './package.json';
|
|
14
15
|
const ROOT_CHANGELOG_PATH = '.';
|
|
15
16
|
export function releasePrepareProject(args) {
|
|
@@ -22,6 +23,15 @@ export function releasePrepareProject(args) {
|
|
|
22
23
|
}
|
|
23
24
|
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
24
25
|
const history = readReleaseHistory(config, { tagPrefixes: [project.tagPrefix], paths: project.paths });
|
|
26
|
+
assertTaggedBaseline([
|
|
27
|
+
findUntaggedBaseline({
|
|
28
|
+
label: 'project',
|
|
29
|
+
packageFiles: [ROOT_PACKAGE_FILE],
|
|
30
|
+
changelogPaths: [ROOT_CHANGELOG_PATH],
|
|
31
|
+
tagPrefixes: [project.tagPrefix],
|
|
32
|
+
previousTag: history.previousTag,
|
|
33
|
+
}, config),
|
|
34
|
+
]);
|
|
25
35
|
const { commits, diagnostics, parsedCommitCount, unparseableCommits } = history.unreleased;
|
|
26
36
|
const tag = history.previousTag;
|
|
27
37
|
const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
|
|
@@ -59,7 +69,6 @@ export function releasePrepareProject(args) {
|
|
|
59
69
|
config,
|
|
60
70
|
history,
|
|
61
71
|
newTag,
|
|
62
|
-
newVersion: bump.newVersion,
|
|
63
72
|
rootOverrides,
|
|
64
73
|
overrideWarnings,
|
|
65
74
|
globalMatchedRootKeys,
|
|
@@ -95,6 +104,9 @@ export function releasePrepareProject(args) {
|
|
|
95
104
|
if (previewFiles.length > 0) {
|
|
96
105
|
result.previewFiles = previewFiles;
|
|
97
106
|
}
|
|
107
|
+
if (changelogs.changelogPreservation.length > 0) {
|
|
108
|
+
result.changelogPreservation = changelogs.changelogPreservation;
|
|
109
|
+
}
|
|
98
110
|
if (tag !== undefined) {
|
|
99
111
|
result.previousTag = tag;
|
|
100
112
|
}
|
|
@@ -116,10 +128,9 @@ function resolveOptionalOverrideArgs(args) {
|
|
|
116
128
|
};
|
|
117
129
|
}
|
|
118
130
|
function planProjectChangelogs(args) {
|
|
119
|
-
const { config, history, newTag,
|
|
120
|
-
const isEmptyRange = history.unreleased.commits.length === 0;
|
|
131
|
+
const { config, history, newTag, rootOverrides, overrideWarnings, globalMatchedRootKeys } = args;
|
|
121
132
|
const today = new Date().toISOString().slice(0, 10);
|
|
122
|
-
const builtEntries =
|
|
133
|
+
const builtEntries = toReleaseEntries(history, newTag, today);
|
|
123
134
|
const applied = applyChangelogOverrides(builtEntries, rootOverrides);
|
|
124
135
|
if (applied.errors.length > 0) {
|
|
125
136
|
throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
|
|
@@ -130,9 +141,7 @@ function planProjectChangelogs(args) {
|
|
|
130
141
|
}
|
|
131
142
|
const changelogJsonPath = resolveChangelogJsonPath(config, ROOT_CHANGELOG_PATH);
|
|
132
143
|
const sectionOrder = deriveSectionOrder(config.workTypes ?? { ...DEFAULT_WORK_TYPES });
|
|
133
|
-
const renderEntries =
|
|
134
|
-
? mergeChangelogEntriesWithDisk(changelogJsonPath, applied.entries)
|
|
135
|
-
: applied.entries;
|
|
144
|
+
const renderEntries = mergeChangelogEntriesWithDisk(changelogJsonPath, applied.entries);
|
|
136
145
|
const writes = [];
|
|
137
146
|
const changelogJsonFiles = [];
|
|
138
147
|
if (config.changelogJson.enabled) {
|
|
@@ -140,6 +149,16 @@ function planProjectChangelogs(args) {
|
|
|
140
149
|
changelogJsonFiles.push(changelogJsonPath);
|
|
141
150
|
}
|
|
142
151
|
const changelogFile = joinPath(ROOT_CHANGELOG_PATH, 'CHANGELOG.md');
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
const preserved = planPreservedSections(changelogFile, renderEntries);
|
|
153
|
+
writes.push({
|
|
154
|
+
path: changelogFile,
|
|
155
|
+
content: renderChangelogMarkdown(renderEntries, { sectionOrder, preservedSections: preserved.sections }),
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
changelogFiles: [changelogFile],
|
|
159
|
+
changelogJsonFiles,
|
|
160
|
+
changelogPreservation: preserved.preservation === undefined ? [] : [preserved.preservation],
|
|
161
|
+
entries: renderEntries,
|
|
162
|
+
writes,
|
|
163
|
+
};
|
|
145
164
|
}
|
|
@@ -1,22 +1,28 @@
|
|
|
1
|
+
import type { VersionedMarkdownSection } from './readChangelogSections.js';
|
|
1
2
|
import type { ChangelogEntry } from './types.js';
|
|
2
3
|
/** Options for `renderChangelogMarkdown`. */
|
|
3
4
|
export interface RenderChangelogMarkdownOptions {
|
|
4
|
-
/** Section titles in canonical order; passed through to `
|
|
5
|
+
/** Section titles in canonical order; passed through to `renderReleaseNotesSingle`. */
|
|
5
6
|
sectionOrder?: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Sections of the existing `CHANGELOG.md` to keep verbatim, interleaved by version among the rendered entries. A
|
|
9
|
+
* section's version must not also be an entry's version.
|
|
10
|
+
*/
|
|
11
|
+
preservedSections?: readonly VersionedMarkdownSection[];
|
|
6
12
|
}
|
|
7
13
|
/**
|
|
8
|
-
*
|
|
14
|
+
* Renders `ChangelogEntry[]` into a complete `CHANGELOG.md`-shaped string.
|
|
9
15
|
*
|
|
10
16
|
* Format:
|
|
11
17
|
* `# Changelog`
|
|
12
18
|
* blank line
|
|
13
19
|
* `All notable changes …`
|
|
14
20
|
* blank line
|
|
15
|
-
* per-entry sections (delegated to `
|
|
21
|
+
* per-entry sections (delegated to `renderReleaseNotesSingle`), with preserved sections interleaved by version
|
|
16
22
|
* blank line
|
|
17
23
|
* footer comment
|
|
18
24
|
*
|
|
19
25
|
* Entries are rendered in the order provided; the caller sorts (typically newest-first via
|
|
20
|
-
* `
|
|
26
|
+
* `compareVersionsDescending`).
|
|
21
27
|
*/
|
|
22
28
|
export declare function renderChangelogMarkdown(entries: ChangelogEntry[], options?: RenderChangelogMarkdownOptions): string;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { compareVersionsDescending } from "./compareVersions.js";
|
|
2
|
+
import { renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
2
3
|
const CHANGELOG_HEADER = `# Changelog
|
|
3
4
|
|
|
4
5
|
All notable changes to this project will be documented in this file.`;
|
|
5
6
|
const CHANGELOG_FOOTER = `<!-- Generated by release-kit. Do not edit this file. Use .meta/changelog-overrides.json to override entries. -->`;
|
|
6
7
|
export function renderChangelogMarkdown(entries, options) {
|
|
7
|
-
const
|
|
8
|
+
const sections = interleavePreservedSections(entries.map((entry) => ({ version: entry.version, text: renderReleaseNotesSingle(entry, options) })), options?.preservedSections ?? []);
|
|
9
|
+
const body = sections
|
|
10
|
+
.map((part) => part.text)
|
|
11
|
+
.filter((text) => text.length > 0)
|
|
12
|
+
.join('\n');
|
|
8
13
|
const trimmedBody = body.trimEnd();
|
|
9
14
|
const parts = [CHANGELOG_HEADER];
|
|
10
15
|
if (trimmedBody.length > 0) {
|
|
@@ -13,3 +18,23 @@ export function renderChangelogMarkdown(entries, options) {
|
|
|
13
18
|
parts.push(CHANGELOG_FOOTER);
|
|
14
19
|
return parts.join('\n\n') + '\n';
|
|
15
20
|
}
|
|
21
|
+
function interleavePreservedSections(rendered, preserved) {
|
|
22
|
+
const sorted = preserved.toSorted((a, b) => compareVersionsDescending(a.version, b.version));
|
|
23
|
+
const pending = sorted.map(withTrailingNewline);
|
|
24
|
+
const result = [];
|
|
25
|
+
let index = 0;
|
|
26
|
+
for (const part of rendered) {
|
|
27
|
+
let section = pending[index];
|
|
28
|
+
while (section !== undefined && compareVersionsDescending(section.version, part.version) < 0) {
|
|
29
|
+
result.push(section);
|
|
30
|
+
index++;
|
|
31
|
+
section = pending[index];
|
|
32
|
+
}
|
|
33
|
+
result.push(part);
|
|
34
|
+
}
|
|
35
|
+
result.push(...pending.slice(index));
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
function withTrailingNewline(section) {
|
|
39
|
+
return { version: section.version, text: `${section.text}\n` };
|
|
40
|
+
}
|
|
@@ -13,22 +13,14 @@ export interface RenderOptions {
|
|
|
13
13
|
sectionOrder?: string[];
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Creates a predicate that matches sections visible to the given audience.
|
|
17
17
|
*
|
|
18
18
|
* `"all"` matches only sections with `audience: "all"` (public-facing).
|
|
19
19
|
* `"dev"` matches all sections (developers see everything).
|
|
20
20
|
*/
|
|
21
21
|
export declare function matchesAudience(audience: ChangelogAudience): (section: ChangelogSection) => boolean;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Output format mirrors the existing CHANGELOG.md style: an H2 version heading followed by
|
|
26
|
-
* H3 section headings with bulleted items.
|
|
23
|
+
* Renders a single `ChangelogEntry` to markdown: an H2 version heading followed by H3 section
|
|
24
|
+
* headings with bulleted items.
|
|
27
25
|
*/
|
|
28
26
|
export declare function renderReleaseNotesSingle(entry: ChangelogEntry, options?: RenderOptions): string;
|
|
29
|
-
/**
|
|
30
|
-
* Render multiple `ChangelogEntry` values to a single markdown document.
|
|
31
|
-
*
|
|
32
|
-
* Entries are rendered in the order provided; the caller is responsible for sorting.
|
|
33
|
-
*/
|
|
34
|
-
export declare function renderReleaseNotesMulti(entries: ChangelogEntry[], options?: RenderOptions): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CANONICAL_TAXONOMY } from '@williamthorsen/change-grammar';
|
|
2
2
|
function getBreakingPrefix() {
|
|
3
|
-
const { emoji, label } =
|
|
3
|
+
const { emoji, label } = CANONICAL_TAXONOMY.markers.breaking;
|
|
4
4
|
return `${emoji} **${label}:** `;
|
|
5
5
|
}
|
|
6
6
|
function allSections() {
|
|
@@ -62,7 +62,3 @@ function sortSectionsByOrder(sections, order) {
|
|
|
62
62
|
function indentBodyLines(body) {
|
|
63
63
|
return body.split('\n').map((line) => (line.length === 0 ? '' : ` ${line}`));
|
|
64
64
|
}
|
|
65
|
-
export function renderReleaseNotesMulti(entries, options) {
|
|
66
|
-
const parts = entries.map((entry) => renderReleaseNotesSingle(entry, options)).filter((part) => part.length > 0);
|
|
67
|
-
return parts.join('\n');
|
|
68
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type OutputStyle } from '@williamthorsen/nmr-core';
|
|
2
2
|
import type { PrepareResult } from './types.js';
|
|
3
|
-
/** How the plan was carried out, which decides the
|
|
3
|
+
/** How the plan was carried out, which decides the report's tense. */
|
|
4
4
|
export interface ReportPrepareOptions {
|
|
5
5
|
/** True when the plan was written to disk; false for a dry run. */
|
|
6
6
|
applied: boolean;
|
|
@@ -9,14 +9,13 @@ export interface ReportPrepareOptions {
|
|
|
9
9
|
style: OutputStyle;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* Formats a release plan into styled terminal output, without writing it.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* Single-workspace mode (no `name` field) renders flat output; multi-workspace mode
|
|
14
|
+
* Single-workspace mode (without a `name` field) renders flat output; multi-workspace mode
|
|
16
15
|
* renders section headers per workspace, an optional project section, and a tag summary
|
|
17
16
|
* at the end.
|
|
18
17
|
*
|
|
19
|
-
* Whether the plan was applied is a property of the render, not of the plan:
|
|
18
|
+
* Whether the plan was applied is a property of the render, not of the plan: The same plan
|
|
20
19
|
* produces the dry-run report and the applied report.
|
|
21
20
|
*/
|
|
22
21
|
export declare function reportPrepare(plan: PrepareResult, options: ReportPrepareOptions): string;
|
|
@@ -51,7 +51,7 @@ function formatSingleWorkspace(result, options) {
|
|
|
51
51
|
formatChangeRecordWarnings(lines, workspace, style);
|
|
52
52
|
formatBumpFiles(lines, workspace, dryRun);
|
|
53
53
|
lines.push(dim('Generating changelogs...'));
|
|
54
|
-
formatChangelogFiles(lines, workspace, dryRun);
|
|
54
|
+
formatChangelogFiles(lines, workspace, dryRun, style);
|
|
55
55
|
formatPreviewFiles(lines, workspace, dryRun);
|
|
56
56
|
formatFormatCommand(lines, result, options);
|
|
57
57
|
formatWarnings(lines, result, style);
|
|
@@ -77,7 +77,7 @@ function formatMultiWorkspace(result, options) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
|
-
lines.push(`\n${formatStatusLine(style, 'skipped', '
|
|
80
|
+
lines.push(`\n${formatStatusLine(style, 'skipped', 'The run did not find release-worthy changes in any workspace.')}`);
|
|
81
81
|
}
|
|
82
82
|
return lines.join('\n');
|
|
83
83
|
}
|
|
@@ -111,14 +111,7 @@ function formatProjectSection(lines, project, dryRun, style) {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
lines.push(dim(' Generating changelogs...'));
|
|
114
|
-
|
|
115
|
-
if (dryRun) {
|
|
116
|
-
lines.push(dim(` [dry-run] Would generate changelog: ${file}`));
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
lines.push(dim(` Generating changelog: ${file}`));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
114
|
+
formatChangelogFiles(lines, project, dryRun, style, ' ');
|
|
122
115
|
formatPreviewFiles(lines, project, dryRun, ' ');
|
|
123
116
|
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(tag))}`);
|
|
124
117
|
}
|
|
@@ -136,7 +129,7 @@ function formatWorkspaceSection(lines, workspace, dryRun, style) {
|
|
|
136
129
|
return;
|
|
137
130
|
}
|
|
138
131
|
const { propagatedFrom } = workspace;
|
|
139
|
-
const isPropagatedOnly =
|
|
132
|
+
const isPropagatedOnly = workspace.propagatedOnly === true;
|
|
140
133
|
formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
141
134
|
formatUnparseableWarning(lines, workspace, style, ' ');
|
|
142
135
|
formatPolicyViolations(lines, workspace.policyViolations, style, ' ');
|
|
@@ -145,14 +138,14 @@ function formatWorkspaceSection(lines, workspace, dryRun, style) {
|
|
|
145
138
|
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly, style);
|
|
146
139
|
formatBumpFiles(lines, workspace, dryRun, ' ');
|
|
147
140
|
lines.push(dim(' Generating changelogs...'));
|
|
148
|
-
formatChangelogFiles(lines, workspace, dryRun, ' ');
|
|
141
|
+
formatChangelogFiles(lines, workspace, dryRun, style, ' ');
|
|
149
142
|
formatPreviewFiles(lines, workspace, dryRun, ' ');
|
|
150
143
|
lines.push(` ${formatGlyphLine(RELEASE_GLYPHS, style, 'tag', bold(workspace.tag))}`);
|
|
151
144
|
}
|
|
152
145
|
function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
153
146
|
if (isPropagatedOnly && propagatedFrom !== undefined) {
|
|
154
147
|
const depNames = propagatedFrom.map((p) => p.packageName).join(', ');
|
|
155
|
-
lines.push(dim(`
|
|
148
|
+
lines.push(dim(` Bumped via dependency: ${depNames}`));
|
|
156
149
|
}
|
|
157
150
|
else if (workspace.parsedCommitCount !== undefined && workspace.parsedCommitCount > 0) {
|
|
158
151
|
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
@@ -192,14 +185,26 @@ function formatBumpFiles(lines, workspace, dryRun, indent = '') {
|
|
|
192
185
|
}
|
|
193
186
|
}
|
|
194
187
|
}
|
|
195
|
-
function formatChangelogFiles(lines,
|
|
196
|
-
for (const file of
|
|
188
|
+
function formatChangelogFiles(lines, release, dryRun, style, indent = '') {
|
|
189
|
+
for (const file of release.changelogFiles) {
|
|
197
190
|
if (dryRun) {
|
|
198
191
|
lines.push(dim(`${indent} [dry-run] Would generate changelog: ${file}`));
|
|
199
192
|
}
|
|
200
193
|
else {
|
|
201
194
|
lines.push(dim(`${indent} Generating changelog: ${file}`));
|
|
202
195
|
}
|
|
196
|
+
const preservation = release.changelogPreservation?.find((candidate) => candidate.file === file);
|
|
197
|
+
if (preservation === undefined) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (preservation.preservedVersions.length > 0) {
|
|
201
|
+
lines.push(dim(`${indent} Kept from the existing file: ${preservation.preservedVersions.join(', ')}`));
|
|
202
|
+
}
|
|
203
|
+
const dropped = preservation.droppedUnversionedHeadings;
|
|
204
|
+
if (dropped.length > 0) {
|
|
205
|
+
const message = `Dropped ${dropped.length} section${dropped.length === 1 ? '' : 's'} without a version: ${dropped.join(', ')}`;
|
|
206
|
+
lines.push(`${indent} ${formatStatusLine(style, 'warning', message)}`);
|
|
207
|
+
}
|
|
203
208
|
}
|
|
204
209
|
}
|
|
205
210
|
function formatPreviewFiles(lines, release, dryRun, indent = '') {
|
|
@@ -237,17 +242,17 @@ function formatPolicyViolations(lines, violations, style, indent = '') {
|
|
|
237
242
|
const subject = violation.commitSubject;
|
|
238
243
|
const truncatedSubject = truncateSubject(subject);
|
|
239
244
|
const location = violation.surface === 'entry' ? `entry ${violation.entryPosition ?? '?'}` : `${violation.surface} surface`;
|
|
240
|
-
lines.push(`${indent} · ${shortHash} '${truncatedSubject}'
|
|
245
|
+
lines.push(`${indent} · ${shortHash} '${truncatedSubject}': type '${violation.type}' at ${location}`);
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
function formatChangeRecordWarnings(lines, release, style, indent = '') {
|
|
244
|
-
const { malformedBlocks = [], undeclaredEntryTypes = [] } = release;
|
|
249
|
+
const { malformedBlocks = [], undeclaredEntryTypes = [], unroutedEntryScopes = [] } = release;
|
|
245
250
|
if (malformedBlocks.length > 0) {
|
|
246
251
|
const count = malformedBlocks.length;
|
|
247
252
|
const header = `${count} change-record block${count === 1 ? '' : 's'} could not be read (item taken from the title)`;
|
|
248
253
|
lines.push(`${indent} ${formatStatusLine(style, 'warning', `${header}:`)}`);
|
|
249
254
|
for (const block of malformedBlocks) {
|
|
250
|
-
lines.push(`${indent} · ${block.commitHash.slice(0, 7)} '${truncateSubject(block.commitSubject)}'
|
|
255
|
+
lines.push(`${indent} · ${block.commitHash.slice(0, 7)} '${truncateSubject(block.commitSubject)}': ${block.reason}`);
|
|
251
256
|
}
|
|
252
257
|
}
|
|
253
258
|
if (undeclaredEntryTypes.length > 0) {
|
|
@@ -256,7 +261,16 @@ function formatChangeRecordWarnings(lines, release, style, indent = '') {
|
|
|
256
261
|
lines.push(`${indent} ${formatStatusLine(style, 'warning', `${header}:`)}`);
|
|
257
262
|
for (const entry of undeclaredEntryTypes) {
|
|
258
263
|
const shortHash = entry.commitHash.slice(0, 7);
|
|
259
|
-
lines.push(`${indent} · ${shortHash} '${truncateSubject(entry.commitSubject)}'
|
|
264
|
+
lines.push(`${indent} · ${shortHash} '${truncateSubject(entry.commitSubject)}': type '${entry.type}' at entry ${entry.entryPosition}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (unroutedEntryScopes.length > 0) {
|
|
268
|
+
const count = unroutedEntryScopes.length;
|
|
269
|
+
const header = `${count} change-record entry scope${count === 1 ? ' does not match' : 's do not match'} any workspace in the commit's window (no item there)`;
|
|
270
|
+
lines.push(`${indent} ${formatStatusLine(style, 'warning', `${header}:`)}`);
|
|
271
|
+
for (const finding of unroutedEntryScopes) {
|
|
272
|
+
const shortHash = finding.commitHash.slice(0, 7);
|
|
273
|
+
lines.push(`${indent} · ${shortHash} '${truncateSubject(finding.commitSubject)}': scope '${finding.scope}' at entry ${finding.entryPosition}`);
|
|
260
274
|
}
|
|
261
275
|
}
|
|
262
276
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { type ResolvedTag } from './resolveReleaseTags.js';
|
|
2
|
+
import type { ReleaseKitConfig } from './types.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Discovers workspaces, resolves release tags from HEAD, validates `--tags` names against the full resolved tag
|
|
5
|
+
* names (e.g., `nmr-core-v1.3.0`), and returns the filtered tag list. Exits with an error message on any validation
|
|
6
|
+
* failure, including a workspace whose `package.json` does not have a `name` field, a config that
|
|
7
|
+
* `mergeMonorepoConfig` rejects, and a workspace that does not resolve to any package, which is not single-package
|
|
8
|
+
* mode.
|
|
8
9
|
*
|
|
9
|
-
* In
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* In monorepo mode the workspace list is `userConfig` merged over the discovered workspaces, as `prepare` builds it.
|
|
11
|
+
* A tag on HEAD whose workspace the config excludes still counts as a tag on HEAD, so `--tags` may name it and a
|
|
12
|
+
* HEAD whose tags are all excluded returns an empty list rather than failing. Each excluded tag that the run
|
|
13
|
+
* selects, every one without `--tags` or the named ones with it, is dropped with a warning.
|
|
14
|
+
* Single-package mode ignores `userConfig`.
|
|
15
|
+
*
|
|
16
|
+
* In both modes the workspace configs come from `deriveWorkspaceConfig`, so that `WorkspaceConfig.isPublishable`
|
|
17
|
+
* (read from `package.json#private`) propagates onto each `ResolvedTag`.
|
|
14
18
|
*/
|
|
15
|
-
export declare function resolveCommandTags(tags: string[] | undefined): ResolvedTag[];
|
|
19
|
+
export declare function resolveCommandTags(tags: string[] | undefined, userConfig: ReleaseKitConfig | undefined): ResolvedTag[];
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
1
2
|
import { reportError } from '@williamthorsen/nmr-core';
|
|
2
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
4
|
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
4
5
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
6
|
+
import { formatExcludedSkip } from "./formatExcludedSkip.js";
|
|
7
|
+
import { mergeMonorepoConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
5
8
|
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
6
|
-
export function resolveCommandTags(tags) {
|
|
9
|
+
export function resolveCommandTags(tags, userConfig) {
|
|
7
10
|
let workspace;
|
|
8
11
|
try {
|
|
9
12
|
workspace = discoverWorkspaces();
|
|
@@ -18,12 +21,13 @@ export function resolveCommandTags(tags) {
|
|
|
18
21
|
}
|
|
19
22
|
let workspaces;
|
|
20
23
|
let singleWorkspace;
|
|
24
|
+
const excludedDirs = workspace.kind === 'packages' ? collectExcludedDirs(userConfig) : new Set();
|
|
21
25
|
try {
|
|
22
26
|
if (workspace.kind === 'single-package') {
|
|
23
27
|
singleWorkspace = deriveWorkspaceConfig('.');
|
|
24
28
|
}
|
|
25
29
|
else {
|
|
26
|
-
workspaces = workspace.packageDirs
|
|
30
|
+
workspaces = buildMonorepoWorkspaces(workspace.packageDirs, userConfig, excludedDirs);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
catch (error) {
|
|
@@ -38,10 +42,10 @@ export function resolveCommandTags(tags) {
|
|
|
38
42
|
resolvedTags = resolveReleaseTags({ singleWorkspace });
|
|
39
43
|
}
|
|
40
44
|
else {
|
|
41
|
-
throw new Error('resolveCommandTags: invariant violated
|
|
45
|
+
throw new Error('resolveCommandTags: invariant violated; neither workspaces nor singleWorkspace was derived');
|
|
42
46
|
}
|
|
43
47
|
if (resolvedTags.length === 0) {
|
|
44
|
-
reportError('
|
|
48
|
+
reportError('HEAD does not have any release tags. Create tags with `release-kit tag` first.');
|
|
45
49
|
process.exit(1);
|
|
46
50
|
}
|
|
47
51
|
if (tags !== undefined) {
|
|
@@ -55,5 +59,31 @@ export function resolveCommandTags(tags) {
|
|
|
55
59
|
}
|
|
56
60
|
resolvedTags = resolvedTags.filter((t) => tags.includes(t.tag));
|
|
57
61
|
}
|
|
58
|
-
return resolvedTags;
|
|
62
|
+
return dropExcludedTags(resolvedTags, excludedDirs);
|
|
63
|
+
}
|
|
64
|
+
function buildMonorepoWorkspaces(packageDirs, userConfig, excludedDirs) {
|
|
65
|
+
const { workspaces } = mergeMonorepoConfig(packageDirs, userConfig, readRootPackageVersion());
|
|
66
|
+
if (excludedDirs.size === 0) {
|
|
67
|
+
return workspaces;
|
|
68
|
+
}
|
|
69
|
+
const excluded = packageDirs
|
|
70
|
+
.filter((packageDir) => excludedDirs.has(basename(packageDir)))
|
|
71
|
+
.map((packageDir) => deriveWorkspaceConfig(packageDir));
|
|
72
|
+
return [...workspaces, ...excluded];
|
|
73
|
+
}
|
|
74
|
+
function collectExcludedDirs(userConfig) {
|
|
75
|
+
const overrides = userConfig?.workspaces ?? [];
|
|
76
|
+
return new Set(overrides.filter((override) => override.shouldExclude === true).map((override) => override.dir));
|
|
77
|
+
}
|
|
78
|
+
function dropExcludedTags(resolvedTags, excludedDirs) {
|
|
79
|
+
const retained = [];
|
|
80
|
+
for (const resolvedTag of resolvedTags) {
|
|
81
|
+
if (excludedDirs.has(resolvedTag.dir)) {
|
|
82
|
+
console.warn(formatExcludedSkip(resolvedTag));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
retained.push(resolvedTag);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return retained;
|
|
59
89
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a `--config` value against the directory from which `release-kit` was invoked, which differs from the
|
|
3
|
+
* working directory once the process has moved to the repo root.
|
|
4
|
+
*
|
|
5
|
+
* Returns `undefined` when the flag is absent, so that `loadConfig` falls back to the default path under the root.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveConfigFlag(config: string | undefined, invocationDir: string): string | undefined;
|
|
@@ -4,10 +4,8 @@ export interface ResolvedTag {
|
|
|
4
4
|
dir: string;
|
|
5
5
|
workspacePath: string;
|
|
6
6
|
/**
|
|
7
|
-
* Whether the workspace this tag belongs
|
|
8
|
-
*
|
|
9
|
-
* workspace config (single-package mode). Consumed by `release-kit publish` to filter the
|
|
10
|
-
* tag set; other commands ignore it.
|
|
7
|
+
* Whether the workspace to which this tag belongs can be published to a registry, copied from the matched
|
|
8
|
+
* workspace's `WorkspaceConfig.isPublishable`.
|
|
11
9
|
*/
|
|
12
10
|
isPublishable: boolean;
|
|
13
11
|
}
|
|
@@ -18,15 +16,15 @@ type ResolveReleaseTagsArgs = {
|
|
|
18
16
|
singleWorkspace: WorkspaceConfig;
|
|
19
17
|
};
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
19
|
+
* Resolves release tags pointing at HEAD into publishable package descriptors.
|
|
22
20
|
*
|
|
23
|
-
* Pass `{ workspaces }` for monorepo mode:
|
|
21
|
+
* Pass `{ workspaces }` for monorepo mode: Each tag is matched against the workspace
|
|
24
22
|
* whose `tagPrefix` it starts with, and that workspace's `isPublishable` propagates onto
|
|
25
23
|
* the `ResolvedTag`. Because `tagPrefix` is derived from `deriveWorkspaceConfig()` (the
|
|
26
24
|
* same source that produced the tag), encoding and decoding stay colocated.
|
|
27
25
|
*
|
|
28
|
-
* Pass `{ singleWorkspace }` for single-package mode:
|
|
29
|
-
* each
|
|
26
|
+
* Pass `{ singleWorkspace }` for single-package mode: Tags like `v1.2.3` are matched and
|
|
27
|
+
* each has the workspace's `isPublishable` bit derived from `./package.json#private`.
|
|
30
28
|
*
|
|
31
29
|
* The no-arg form is a test-isolation convenience for unit tests that exercise tag
|
|
32
30
|
* matching without needing `isPublishable` propagation; resolved tags default to
|
|
@@ -30,7 +30,7 @@ function resolveSinglePackageTags(tags, singleWorkspace) {
|
|
|
30
30
|
return matched.map((tag) => ({ tag, dir: '.', workspacePath: '.', isPublishable }));
|
|
31
31
|
}
|
|
32
32
|
function resolveMonorepoTags(tags, workspaces) {
|
|
33
|
-
const sortedWorkspaces =
|
|
33
|
+
const sortedWorkspaces = workspaces.toSorted((a, b) => b.tagPrefix.length - a.tagPrefix.length);
|
|
34
34
|
const resolved = [];
|
|
35
35
|
for (const tag of tags) {
|
|
36
36
|
const match = findMatchingWorkspace(tag, sortedWorkspaces);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
-
/** The environment variable
|
|
2
|
+
/** The environment variable that sets the caller's standing style preference: `auto`, `plain`, or `rich`. */
|
|
3
3
|
export declare const OUTPUT_STYLE_ENV_VAR = "RELEASE_KIT_OUTPUT_STYLE";
|
|
4
4
|
export interface ResolveStylesOptions {
|
|
5
5
|
readonly env: Readonly<Record<string, string | undefined>>;
|
|
6
6
|
readonly stderrIsTty: boolean;
|
|
7
7
|
readonly stdoutIsTty: boolean;
|
|
8
8
|
}
|
|
9
|
-
/** Resolves the style of each output stream, exiting with a usage error when the variable
|
|
9
|
+
/** Resolves the style of each output stream, exiting with a usage error when the variable does not name a setting. */
|
|
10
10
|
export declare function resolveStylesOrExit(options: ResolveStylesOptions): StreamStyles;
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Runs the CLI `show-tag-prefixes` command and returns its exit code.
|
|
4
4
|
*
|
|
5
|
-
* Prints a per-workspace table of derived prefixes, tag counts, and declared legacy
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* understate the declared legacy prefixes. An absent default config previews against derived defaults.
|
|
12
|
-
*
|
|
13
|
-
* @returns The exit code the caller should use.
|
|
5
|
+
* Prints a per-workspace table of derived prefixes, tag counts, and declared legacy entries, followed by an
|
|
6
|
+
* "Undeclared tag prefixes" section when candidate-shaped tags exist outside the known set. Returns `1` on any
|
|
7
|
+
* derivation failure or collision and `0` otherwise; undeclared candidates do not affect the exit code. When a config
|
|
8
|
+
* exists and either fails to load or fails validation, the command reports it to stderr and returns `1` in both repo
|
|
9
|
+
* modes, because a default-config preview would understate the declared legacy prefixes. When the default config is
|
|
10
|
+
* absent, the command previews against derived defaults.
|
|
14
11
|
*/
|
|
15
12
|
export declare function showTagPrefixesCommand(styles: StreamStyles, configPath?: string): Promise<number>;
|
|
@@ -41,7 +41,7 @@ function renderMonorepo(preview, style) {
|
|
|
41
41
|
lines.push('', ...preview.collisions.map((collision) => formatStatusLine(style, 'failed', `tag prefix collision: '${collision.tagPrefix}' used by ${collision.workspacePaths.join(', ')}`)));
|
|
42
42
|
}
|
|
43
43
|
if (preview.undeclaredCandidates.length > 0) {
|
|
44
|
-
lines.push('', 'Undeclared tag prefixes:', '', ...preview.undeclaredCandidates.map((candidate) => ` '${candidate.prefix}'
|
|
44
|
+
lines.push('', 'Undeclared tag prefixes:', '', ...preview.undeclaredCandidates.map((candidate) => ` '${candidate.prefix}': ${candidate.tagCount} tags (e.g., ${candidate.exampleTags.join(', ')})`), '', 'Suggested config snippet (adjust `dir` to match the workspace if the guess is wrong, and replace the `name` placeholder with the legacy npm name):', '', renderSuggestedSnippet(preview.undeclaredCandidates), '', "If the suggested `dir` does not match the workspace, adjust it before pasting. Each legacy identity requires a `name`: Replace the `TODO-fill-in-legacy-npm-name` placeholder with the package's prior npm name.");
|
|
45
45
|
}
|
|
46
46
|
lines.push('');
|
|
47
47
|
return lines.join('\n');
|
|
@@ -50,20 +50,20 @@ function renderWorkspaceRow(row, style) {
|
|
|
50
50
|
const lines = [];
|
|
51
51
|
if (row.derivedPrefix === null) {
|
|
52
52
|
const failure = formatStatusLine(style, 'failed', `derivation failed: ${row.derivationError ?? 'unknown error'}`);
|
|
53
|
-
lines.push(` ${row.workspacePath}
|
|
53
|
+
lines.push(` ${row.workspacePath}: ${failure}`);
|
|
54
54
|
return lines;
|
|
55
55
|
}
|
|
56
56
|
const statusMarker = row.derivedTagCount > 0
|
|
57
57
|
? formatStatusLine(style, 'passed', `${row.derivedTagCount} tags`)
|
|
58
58
|
: formatStatusLine(style, 'warning', 'no existing tags');
|
|
59
|
-
lines.push(` ${row.workspacePath}
|
|
59
|
+
lines.push(` ${row.workspacePath}: derived prefix '${row.derivedPrefix}', ${statusMarker}`);
|
|
60
60
|
for (const entry of row.legacyEntries) {
|
|
61
61
|
if (entry.tagCount > 0) {
|
|
62
62
|
const recognized = `${entry.tagCount} legacy tags with '${entry.prefix}' prefix (recognized)`;
|
|
63
63
|
lines.push(` ${formatStatusLine(style, 'passed', recognized)}`);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
|
-
const tagless = `recorded legacy prefix '${entry.prefix}'
|
|
66
|
+
const tagless = `recorded legacy prefix '${entry.prefix}' does not have any tags`;
|
|
67
67
|
lines.push(` ${formatStatusLine(style, 'warning', tagless)}`);
|
|
68
68
|
}
|
|
69
69
|
}
|
package/dist/esm/stripScope.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Strips scope indicators from a raw commit message.
|
|
3
3
|
*
|
|
4
4
|
* Handles `scope|type: desc`, `type(scope): desc`, and messages with
|
|
5
5
|
* ticket prefixes (e.g., `#72 release-kit|fix: ...`). Returns the
|
|
6
|
-
* message unchanged when
|
|
6
|
+
* message unchanged when it does not detect a scope.
|
|
7
7
|
*/
|
|
8
8
|
export declare function stripScope(message: string): string;
|