@ckeditor/ckeditor5-dev-changelog 55.6.0-alpha.0 → 55.6.1
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/dist/index.d.ts +6 -6
- package/dist/index.js +1217 -1384
- package/dist/tasks/generatechangelogformonorepository.d.ts +3 -7
- package/dist/tasks/generatechangelogforsinglerepository.d.ts +2 -6
- package/dist/template.d.ts +5 -9
- package/dist/template.js +96 -106
- package/dist/types.d.ts +97 -99
- package/dist/utils/asyncarray.d.ts +31 -31
- package/dist/utils/commitchanges.d.ts +2 -6
- package/dist/utils/composechangelog.d.ts +16 -20
- package/dist/utils/composereleasesummary.d.ts +17 -17
- package/dist/utils/constants.d.ts +34 -38
- package/dist/utils/detectreleasechannel.d.ts +3 -7
- package/dist/utils/determinenextversion.d.ts +14 -18
- package/dist/utils/displaychanges.d.ts +13 -17
- package/dist/utils/filtervisiblesections.d.ts +7 -11
- package/dist/utils/findchangelogentrypaths.d.ts +8 -12
- package/dist/utils/findpackages.d.ts +7 -11
- package/dist/utils/generatechangelog.d.ts +9 -13
- package/dist/utils/getdateformatted.d.ts +2 -6
- package/dist/utils/getreleasetype.d.ts +4 -4
- package/dist/utils/groupentriesbysection.d.ts +7 -11
- package/dist/utils/internalerror.d.ts +6 -7
- package/dist/utils/linktogithubuser.d.ts +8 -8
- package/dist/utils/loginfo.d.ts +8 -8
- package/dist/utils/modifychangelog.d.ts +5 -9
- package/dist/utils/movechangelogentryfiles.d.ts +5 -9
- package/dist/utils/normalizeentry.d.ts +4 -4
- package/dist/utils/parsechangelogentries.d.ts +3 -7
- package/dist/utils/promptreleasetype.d.ts +3 -7
- package/dist/utils/providenewversion.d.ts +15 -19
- package/dist/utils/removechangelogentryfiles.d.ts +4 -8
- package/dist/utils/sortentriesbyscopeanddate.d.ts +9 -9
- package/dist/utils/truncatechangelog.d.ts +2 -6
- package/dist/utils/useraborterror.d.ts +6 -7
- package/dist/utils/validateentry.d.ts +13 -13
- package/dist/utils/validateinputversion.d.ts +6 -10
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,1530 +1,1363 @@
|
|
|
1
|
-
import { styleText } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import upath from
|
|
5
|
-
import fs
|
|
6
|
-
import fs from
|
|
7
|
-
import semver from
|
|
8
|
-
import inquirer from
|
|
9
|
-
import { glob } from
|
|
10
|
-
import matter from
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const CHANGELOG_FILE =
|
|
17
|
-
const CHANGELOG_HEADER =
|
|
18
|
-
const NPM_URL =
|
|
19
|
-
const VERSIONING_POLICY_URL =
|
|
20
|
-
const CHANGESET_DIRECTORY =
|
|
21
|
-
const PRE_RELEASE_DIRECTORY =
|
|
22
|
-
upath.join(import.meta.dirname,
|
|
1
|
+
import { styleText } from "node:util";
|
|
2
|
+
import { format, isValid, parse } from "date-fns";
|
|
3
|
+
import { npm, tools, workspaces } from "@ckeditor/ckeditor5-dev-utils";
|
|
4
|
+
import upath from "upath";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import fs$1 from "node:fs";
|
|
7
|
+
import semver from "semver";
|
|
8
|
+
import inquirer from "inquirer";
|
|
9
|
+
import { glob } from "glob";
|
|
10
|
+
import matter from "gray-matter";
|
|
11
|
+
//#region src/utils/constants.ts
|
|
12
|
+
/**
|
|
13
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
14
|
+
* For licensing, see LICENSE.md.
|
|
15
|
+
*/
|
|
16
|
+
const CHANGELOG_FILE = "CHANGELOG.md";
|
|
17
|
+
const CHANGELOG_HEADER = "Changelog\n=========";
|
|
18
|
+
const NPM_URL = "https://www.npmjs.com/package";
|
|
19
|
+
const VERSIONING_POLICY_URL = "https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html";
|
|
20
|
+
const CHANGESET_DIRECTORY = ".changelog";
|
|
21
|
+
const PRE_RELEASE_DIRECTORY = "pre-release";
|
|
22
|
+
upath.join(import.meta.dirname, "../template/template.md");
|
|
23
23
|
const SECTIONS = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
major: {
|
|
25
|
+
title: `MAJOR BREAKING CHANGES [ℹ️](${VERSIONING_POLICY_URL}#major-and-minor-breaking-changes)`,
|
|
26
|
+
titleInLogs: "MAJOR BREAKING CHANGES"
|
|
27
|
+
},
|
|
28
|
+
minor: {
|
|
29
|
+
title: `MINOR BREAKING CHANGES [ℹ️](${VERSIONING_POLICY_URL}#major-and-minor-breaking-changes)`,
|
|
30
|
+
titleInLogs: "MINOR BREAKING CHANGES"
|
|
31
|
+
},
|
|
32
|
+
breaking: { title: "BREAKING CHANGES" },
|
|
33
|
+
feature: { title: "Features" },
|
|
34
|
+
fix: { title: "Bug fixes" },
|
|
35
|
+
other: { title: "Other changes" },
|
|
36
|
+
warning: {
|
|
37
|
+
title: "Incorrect values",
|
|
38
|
+
excludeInChangelog: true
|
|
39
|
+
},
|
|
40
|
+
invalid: {
|
|
41
|
+
title: "Invalid files",
|
|
42
|
+
excludeInChangelog: true
|
|
43
|
+
}
|
|
44
44
|
};
|
|
45
45
|
const ISSUE_SLUG_PATTERN = /^(?<owner>[a-z0-9.-]+)\/(?<repository>[a-z0-9.-]+)#(?<number>\d+)$/;
|
|
46
46
|
const ISSUE_PATTERN = /^\d+$/;
|
|
47
47
|
const ISSUE_URL_PATTERN = /^(?<base>https:\/\/github\.com)\/(?<owner>[a-z0-9.-]+)\/(?<repository>[a-z0-9.-]+)\/issues\/(?<number>\d+)$/;
|
|
48
48
|
const NICK_NAME_PATTERN = /^@[a-z0-9-_]+$/i;
|
|
49
49
|
const TYPES = [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
{ name: "Feature" },
|
|
51
|
+
{ name: "Other" },
|
|
52
|
+
{ name: "Fix" },
|
|
53
|
+
{ name: "Major breaking change" },
|
|
54
|
+
{ name: "Minor breaking change" },
|
|
55
|
+
{ name: "Breaking change" }
|
|
56
56
|
];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/utils/linktogithubuser.ts
|
|
59
|
+
/**
|
|
60
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
61
|
+
* For licensing, see LICENSE.md.
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* This function enhances changelog entries by linking contributor usernames to their GitHub profiles.
|
|
65
|
+
*
|
|
66
|
+
* It searches for occurrences of GitHub-style mentions (e.g., @username) in the given comment string
|
|
67
|
+
* and transforms them into Markdown links pointing to the corresponding GitHub user page.
|
|
68
|
+
*/
|
|
68
69
|
function linkToGitHubUser(comment) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
70
|
+
return comment.replace(/(^|[\s(])@([\w-]+)(?![/\w-])/gi, (_, charBefore, nickName) => {
|
|
71
|
+
return `${charBefore}[@${nickName}](https://github.com/${nickName})`;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/utils/validateentry.ts
|
|
76
|
+
/**
|
|
77
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
78
|
+
* For licensing, see LICENSE.md.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Validates a changelog entry against expected types, scopes, and issue references.
|
|
82
|
+
*
|
|
83
|
+
* It checks if the type is valid and consistent with single or multi-package modes,
|
|
84
|
+
* verifies scopes against known package names, and ensures issue references are correctly formatted.
|
|
85
|
+
*
|
|
86
|
+
* Returns whether the entry is valid along with a validated version including any validation messages.
|
|
87
|
+
*/
|
|
86
88
|
function validateEntry(entry, packagesNames, singlePackage) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
data.see = seeValidated;
|
|
139
|
-
const closesValidated = [];
|
|
140
|
-
for (const closes of data.closes) {
|
|
141
|
-
if (!(closes.match(ISSUE_PATTERN) || closes.match(ISSUE_SLUG_PATTERN) || closes.match(ISSUE_URL_PATTERN))) {
|
|
142
|
-
validations.push([
|
|
143
|
-
`Closes "${closes}" is not a valid issue reference. Provide either:`,
|
|
144
|
-
'issue number, repository-slug#id or full issue link URL.'
|
|
145
|
-
].join(' '));
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
closesValidated.push(closes);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
data.closes = closesValidated;
|
|
152
|
-
const communityCreditsValidated = [];
|
|
153
|
-
for (const nickName of data.communityCredits) {
|
|
154
|
-
if (!nickName.match(NICK_NAME_PATTERN)) {
|
|
155
|
-
validations.push(`Community username "${nickName}" is not valid GitHub username.`);
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
communityCreditsValidated.push(nickName);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
data.communityCredits = communityCreditsValidated;
|
|
162
|
-
const validatedEntry = {
|
|
163
|
-
...entry,
|
|
164
|
-
data: {
|
|
165
|
-
...data,
|
|
166
|
-
validations,
|
|
167
|
-
type: data.type
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
return { isValid, validatedEntry };
|
|
89
|
+
const noScopePackagesNames = packagesNames.map((packageName) => packageName.replace(/@.*\//, ""));
|
|
90
|
+
const data = entry.data;
|
|
91
|
+
const validations = [];
|
|
92
|
+
let isValid = true;
|
|
93
|
+
const allowedTypesArray = TYPES.map(({ name }) => name);
|
|
94
|
+
const allowedTypesList = getAllowedTypesList();
|
|
95
|
+
if (typeof data.type === "undefined") {
|
|
96
|
+
validations.push(`Provide a type with one of the values: ${allowedTypesList} (case insensitive).`);
|
|
97
|
+
isValid = false;
|
|
98
|
+
} else if (!allowedTypesArray.includes(data.type)) {
|
|
99
|
+
validations.push(`Type is required and should be one of: ${allowedTypesList} (case insensitive).`);
|
|
100
|
+
isValid = false;
|
|
101
|
+
}
|
|
102
|
+
if (singlePackage && ["Major breaking change", "Minor breaking change"].includes(data.type)) {
|
|
103
|
+
validations.push(`Breaking change "${data.type}" should be generic: "Breaking change", for a single package mode (case insensitive).`);
|
|
104
|
+
isValid = false;
|
|
105
|
+
}
|
|
106
|
+
if (!singlePackage && data.type === "Breaking change") {
|
|
107
|
+
validations.push(`Breaking change "${data.type}" should be one of: "Minor breaking change", "Major breaking change" for a monorepo (case insensitive).`);
|
|
108
|
+
isValid = false;
|
|
109
|
+
}
|
|
110
|
+
const scopeValidated = [];
|
|
111
|
+
if (singlePackage) scopeValidated.push(...data.scope);
|
|
112
|
+
else for (const scopeName of data.scope) if (!noScopePackagesNames.includes(scopeName)) validations.push(`Scope "${scopeName}" is not recognized as a valid package in the repository.`);
|
|
113
|
+
else scopeValidated.push(scopeName);
|
|
114
|
+
data.scope = scopeValidated;
|
|
115
|
+
const seeValidated = [];
|
|
116
|
+
for (const see of data.see) if (!(see.match(ISSUE_PATTERN) || see.match(ISSUE_SLUG_PATTERN) || see.match(ISSUE_URL_PATTERN))) validations.push([`See "${see}" is not a valid issue reference. Provide either:`, "issue number, repository-slug#id or full issue link URL."].join(" "));
|
|
117
|
+
else seeValidated.push(see);
|
|
118
|
+
data.see = seeValidated;
|
|
119
|
+
const closesValidated = [];
|
|
120
|
+
for (const closes of data.closes) if (!(closes.match(ISSUE_PATTERN) || closes.match(ISSUE_SLUG_PATTERN) || closes.match(ISSUE_URL_PATTERN))) validations.push([`Closes "${closes}" is not a valid issue reference. Provide either:`, "issue number, repository-slug#id or full issue link URL."].join(" "));
|
|
121
|
+
else closesValidated.push(closes);
|
|
122
|
+
data.closes = closesValidated;
|
|
123
|
+
const communityCreditsValidated = [];
|
|
124
|
+
for (const nickName of data.communityCredits) if (!nickName.match(NICK_NAME_PATTERN)) validations.push(`Community username "${nickName}" is not valid GitHub username.`);
|
|
125
|
+
else communityCreditsValidated.push(nickName);
|
|
126
|
+
data.communityCredits = communityCreditsValidated;
|
|
127
|
+
const validatedEntry = {
|
|
128
|
+
...entry,
|
|
129
|
+
data: {
|
|
130
|
+
...data,
|
|
131
|
+
validations,
|
|
132
|
+
type: data.type
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
isValid,
|
|
137
|
+
validatedEntry
|
|
138
|
+
};
|
|
171
139
|
}
|
|
172
140
|
function getAllowedTypesList() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
|
|
184
|
-
|
|
141
|
+
const formatter = new Intl.ListFormat("en-US", {
|
|
142
|
+
style: "long",
|
|
143
|
+
type: "disjunction"
|
|
144
|
+
});
|
|
145
|
+
const items = TYPES.map((type) => `"${type.name}"`);
|
|
146
|
+
return formatter.format(items);
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region src/utils/groupentriesbysection.ts
|
|
150
|
+
/**
|
|
151
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
152
|
+
* For licensing, see LICENSE.md.
|
|
153
|
+
*/
|
|
154
|
+
/**
|
|
155
|
+
* This function categorizes changelog entries based on their types and packages.
|
|
156
|
+
*/
|
|
185
157
|
function groupEntriesBySection(options) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
158
|
+
const { files, packagesMetadata, transformScope, isSinglePackage } = options;
|
|
159
|
+
const packageNames = [...packagesMetadata.keys()];
|
|
160
|
+
return files.reduce((sections, entry) => {
|
|
161
|
+
const { validatedEntry, isValid } = validateEntry(entry, packageNames, isSinglePackage);
|
|
162
|
+
const validatedData = validatedEntry.data;
|
|
163
|
+
const closesLinks = filterLinks(validatedData.closes, validatedEntry);
|
|
164
|
+
const closes = getIssuesLinks(closesLinks, "Closes");
|
|
165
|
+
const seeLinks = filterLinks(validatedData.see, validatedEntry);
|
|
166
|
+
const see = getIssuesLinks(seeLinks, "See");
|
|
167
|
+
const scope = isSinglePackage ? null : getScopesLinks(validatedData.scope, transformScope);
|
|
168
|
+
const section = getSection({
|
|
169
|
+
entry: validatedEntry,
|
|
170
|
+
isSinglePackage,
|
|
171
|
+
isValid
|
|
172
|
+
});
|
|
173
|
+
const [mainContent, ...restContent] = formatContent(linkToGitHubUser(getContentWithCommunityCredits(validatedEntry.content, validatedData.communityCredits)));
|
|
174
|
+
const newEntry = {
|
|
175
|
+
message: getChangeMessage({
|
|
176
|
+
restContent,
|
|
177
|
+
scope,
|
|
178
|
+
mainContent,
|
|
179
|
+
see,
|
|
180
|
+
closes
|
|
181
|
+
}),
|
|
182
|
+
data: {
|
|
183
|
+
mainContent,
|
|
184
|
+
restContent,
|
|
185
|
+
type: validatedData.type,
|
|
186
|
+
scope: validatedData.scope,
|
|
187
|
+
see: seeLinks,
|
|
188
|
+
closes: closesLinks,
|
|
189
|
+
validations: validatedData.validations,
|
|
190
|
+
communityCredits: validatedData.communityCredits
|
|
191
|
+
},
|
|
192
|
+
changesetPath: validatedEntry.changesetPath
|
|
193
|
+
};
|
|
194
|
+
sections[section].entries.push(newEntry);
|
|
195
|
+
if (isValid && newEntry.data.validations?.length) sections.warning.entries.push(newEntry);
|
|
196
|
+
return sections;
|
|
197
|
+
}, getInitialSectionsWithEntries());
|
|
221
198
|
}
|
|
222
199
|
function filterLinks(links, entry) {
|
|
223
|
-
|
|
224
|
-
.map(link => getIssueLinkObject(link, entry.gitHubUrl))
|
|
225
|
-
.filter(({ link }) => entry.linkFilter(link));
|
|
200
|
+
return links.map((link) => getIssueLinkObject(link, entry.gitHubUrl)).filter(({ link }) => entry.linkFilter(link));
|
|
226
201
|
}
|
|
227
202
|
function getChangeMessage({ restContent, scope, mainContent, see, closes }) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return ` ${line}`;
|
|
241
|
-
}
|
|
242
|
-
return line;
|
|
243
|
-
}).join('\n')}`;
|
|
203
|
+
const messageFirstLine = [
|
|
204
|
+
"*",
|
|
205
|
+
scope ? `**${scope}**:` : null,
|
|
206
|
+
mainContent,
|
|
207
|
+
see.length ? see : null,
|
|
208
|
+
closes.length ? closes : null
|
|
209
|
+
].filter(Boolean).join(" ");
|
|
210
|
+
if (!restContent || !restContent.length) return messageFirstLine;
|
|
211
|
+
return `${messageFirstLine}\n\n${restContent.map((line) => {
|
|
212
|
+
if (line.length) return ` ${line}`;
|
|
213
|
+
return line;
|
|
214
|
+
}).join("\n")}`;
|
|
244
215
|
}
|
|
245
216
|
function formatContent(content) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (line.trim() === '') {
|
|
258
|
-
// Only add empty line if the last item is not already an empty line
|
|
259
|
-
if (acc.length > 0 && acc.at(-1) !== '') {
|
|
260
|
-
acc.push('');
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
acc.push(line);
|
|
265
|
-
}
|
|
266
|
-
return acc;
|
|
267
|
-
}, []);
|
|
268
|
-
return [mainContent, ...cleanedRestContent];
|
|
217
|
+
const lines = content.trim().split("\n").map((line) => line.trimEnd()).map((line) => normalizeListMarker(line));
|
|
218
|
+
const mainIndex = lines.findIndex((line) => line.trim() !== "");
|
|
219
|
+
const mainContent = lines.at(mainIndex);
|
|
220
|
+
let restContent = lines.slice(mainIndex + 1);
|
|
221
|
+
if (restContent.at(0)?.trim() === "") restContent = restContent.slice(1);
|
|
222
|
+
return [mainContent, ...restContent.reduce((acc, line) => {
|
|
223
|
+
if (line.trim() === "") {
|
|
224
|
+
if (acc.length > 0 && acc.at(-1) !== "") acc.push("");
|
|
225
|
+
} else acc.push(line);
|
|
226
|
+
return acc;
|
|
227
|
+
}, [])];
|
|
269
228
|
}
|
|
270
229
|
function getContentWithCommunityCredits(content, communityCredits) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
return content.concat(`\n\nThanks to ${communityCredits?.join(', ')}.`);
|
|
230
|
+
if (!communityCredits?.length) return content;
|
|
231
|
+
return content.concat(`\n\nThanks to ${communityCredits?.join(", ")}.`);
|
|
275
232
|
}
|
|
276
233
|
function getScopesLinks(scope, transformScope) {
|
|
277
|
-
|
|
278
|
-
.map(scope => transformScope(scope))
|
|
279
|
-
.map(({ displayName, npmUrl }) => `[${displayName}](${npmUrl})`)
|
|
280
|
-
.join(', ');
|
|
234
|
+
return scope.map((scope) => transformScope(scope)).map(({ displayName, npmUrl }) => `[${displayName}](${npmUrl})`).join(", ");
|
|
281
235
|
}
|
|
282
236
|
function getIssueLinkObject(issue, gitHubUrl) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
237
|
+
if (issue.match(ISSUE_PATTERN)) return {
|
|
238
|
+
displayName: `#${issue}`,
|
|
239
|
+
link: `${gitHubUrl}/issues/${issue}`
|
|
240
|
+
};
|
|
241
|
+
const differentRepoMatch = issue.match(ISSUE_SLUG_PATTERN);
|
|
242
|
+
if (differentRepoMatch) {
|
|
243
|
+
const { owner, repository, number } = differentRepoMatch.groups;
|
|
244
|
+
return {
|
|
245
|
+
displayName: issue,
|
|
246
|
+
link: `https://github.com/${owner}/${repository}/issues/${number}`
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const repoUrlMatch = issue.match(ISSUE_URL_PATTERN);
|
|
250
|
+
if (repoUrlMatch) {
|
|
251
|
+
const { owner, repository, number } = repoUrlMatch.groups;
|
|
252
|
+
if (issue.startsWith(gitHubUrl)) return {
|
|
253
|
+
displayName: `#${number}`,
|
|
254
|
+
link: issue
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
displayName: `${owner}/${repository}#${number}`,
|
|
258
|
+
link: issue
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
displayName: "",
|
|
263
|
+
link: ""
|
|
264
|
+
};
|
|
300
265
|
}
|
|
301
266
|
function getIssuesLinks(issues, prefix) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
const links = issues.map(issue => `[${issue.displayName}](${issue.link})`);
|
|
306
|
-
return `${prefix} ${links.join(', ')}.`;
|
|
267
|
+
if (!issues.length) return "";
|
|
268
|
+
return `${prefix} ${issues.map((issue) => `[${issue.displayName}](${issue.link})`).join(", ")}.`;
|
|
307
269
|
}
|
|
308
270
|
function getSection(options) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return 'minor';
|
|
323
|
-
}
|
|
324
|
-
if (entry.data.type === 'Major breaking change') {
|
|
325
|
-
return 'major';
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return entry.data.type.toLowerCase();
|
|
271
|
+
const { entry, isSinglePackage, isValid } = options;
|
|
272
|
+
if (!isValid) return "invalid";
|
|
273
|
+
if (isSinglePackage) {
|
|
274
|
+
if ([
|
|
275
|
+
"Minor breaking change",
|
|
276
|
+
"Major breaking change",
|
|
277
|
+
"Breaking change"
|
|
278
|
+
].includes(entry.data.type)) return "breaking";
|
|
279
|
+
} else {
|
|
280
|
+
if (entry.data.type === "Minor breaking change") return "minor";
|
|
281
|
+
if (entry.data.type === "Major breaking change") return "major";
|
|
282
|
+
}
|
|
283
|
+
return entry.data.type.toLowerCase();
|
|
329
284
|
}
|
|
330
285
|
function getInitialSectionsWithEntries() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
return sections;
|
|
286
|
+
const sections = structuredClone(SECTIONS);
|
|
287
|
+
for (const key in sections) sections[key].entries = [];
|
|
288
|
+
return sections;
|
|
336
289
|
}
|
|
337
290
|
function normalizeListMarker(line) {
|
|
338
|
-
|
|
339
|
-
return line.replace(listMarkerRegexp, '$1*$2');
|
|
291
|
+
return line.replace(/^(\s*)[-+](\s*)/, "$1*$2");
|
|
340
292
|
}
|
|
341
|
-
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/utils/loginfo.ts
|
|
342
295
|
/**
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
296
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
297
|
+
* For licensing, see LICENSE.md.
|
|
298
|
+
*/
|
|
346
299
|
/**
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
300
|
+
* This function provides a consistent logging format for the changelog generation process.
|
|
301
|
+
*
|
|
302
|
+
* It logs the given text to the console, optionally indenting it by a specified number of levels.
|
|
303
|
+
*/
|
|
351
304
|
function logInfo(text, { indent } = { indent: 0 }) {
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
305
|
+
console.log(" ".repeat(indent * 3) + text);
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/utils/displaychanges.ts
|
|
309
|
+
/**
|
|
310
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
311
|
+
* For licensing, see LICENSE.md.
|
|
312
|
+
*/
|
|
313
|
+
/**
|
|
314
|
+
* Displays a formatted summary of all changelog entries grouped by sections (e.g., Features, Fixes, Breaking changes).
|
|
315
|
+
*
|
|
316
|
+
* This function:
|
|
317
|
+
* * Lists all non-empty changelog sections with appropriate formatting.
|
|
318
|
+
* * Differentiates between valid and invalid entries using visual indicators (`+` for valid, `x` for invalid).
|
|
319
|
+
* * Supports both mono-repository and single package repositories through the `isSinglePackage` flag.
|
|
320
|
+
* * Applies a transformation to scope names using the `transformScope` callback for a mono-repository setup.
|
|
321
|
+
* * Outputs a helpful legend for interpreting the entry indicators.
|
|
322
|
+
*/
|
|
369
323
|
function displayChanges(options) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
logInfo('1. An issue number (e.g., 1000)', { indent: 4 });
|
|
393
|
-
logInfo('2. Repository-slug#id (e.g., org/repo#1000)', { indent: 4 });
|
|
394
|
-
logInfo('3. A full issue link URL', { indent: 4 });
|
|
395
|
-
logInfo('* A scope field consists of existing packages.', { indent: 3 });
|
|
396
|
-
logInfo('');
|
|
397
|
-
logInfo(`Found ${numberOfEntries} entries to parse.`, { indent: 1 });
|
|
398
|
-
logInfo('');
|
|
324
|
+
const { sections, isSinglePackage, transformScope } = options;
|
|
325
|
+
let numberOfEntries = 0;
|
|
326
|
+
logInfo(`○ ${styleText("cyan", "Listing the changes...")}`);
|
|
327
|
+
const nonEmptySections = Object.entries(sections).filter(([, section]) => section.entries.length);
|
|
328
|
+
for (const [sectionName, section] of nonEmptySections) {
|
|
329
|
+
logInfo("◌ " + getTitleColor(sectionName)(styleText("underline", `${section.titleInLogs || section.title}:`)), { indent: 1 });
|
|
330
|
+
const displayCallback = sectionName === "invalid" || sectionName === "warning" ? displayWarningEntry : displayValidEntry;
|
|
331
|
+
if (sectionName !== "warning") numberOfEntries += section.entries.length;
|
|
332
|
+
section.entries.forEach((entry) => displayCallback(entry, sectionName, isSinglePackage, transformScope));
|
|
333
|
+
logInfo("");
|
|
334
|
+
}
|
|
335
|
+
logInfo("◌ " + styleText("underline", "Legend:"), { indent: 1 });
|
|
336
|
+
logInfo(`- Entries marked with ${styleText("green", "+")} symbol are included in the changelog.`, { indent: 2 });
|
|
337
|
+
logInfo("- Entries marked with " + styleText("yellow", "x") + " symbol include invalid references (see and/or closes) or scope definitions. Please ensure that:", { indent: 2 });
|
|
338
|
+
logInfo("* Reference entries match one of the following formats:", { indent: 3 });
|
|
339
|
+
logInfo("1. An issue number (e.g., 1000)", { indent: 4 });
|
|
340
|
+
logInfo("2. Repository-slug#id (e.g., org/repo#1000)", { indent: 4 });
|
|
341
|
+
logInfo("3. A full issue link URL", { indent: 4 });
|
|
342
|
+
logInfo("* A scope field consists of existing packages.", { indent: 3 });
|
|
343
|
+
logInfo("");
|
|
344
|
+
logInfo(`Found ${numberOfEntries} entries to parse.`, { indent: 1 });
|
|
345
|
+
logInfo("");
|
|
399
346
|
}
|
|
400
347
|
function getTitleColor(sectionName) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
let defaultColor = (text) => styleText('blue', text);
|
|
408
|
-
if (isBreakingChangeSection(sectionName)) {
|
|
409
|
-
// To avoid tricks in tests, let's simplify the implementation.
|
|
410
|
-
defaultColor = (value) => styleText(['bold', 'blue'], value);
|
|
411
|
-
}
|
|
412
|
-
return defaultColor;
|
|
348
|
+
if (sectionName === "warning") return (text) => styleText("yellow", text);
|
|
349
|
+
if (sectionName === "invalid") return (text) => styleText("red", text);
|
|
350
|
+
let defaultColor = (text) => styleText("blue", text);
|
|
351
|
+
if (isBreakingChangeSection(sectionName)) defaultColor = (value) => styleText(["bold", "blue"], value);
|
|
352
|
+
return defaultColor;
|
|
413
353
|
}
|
|
414
354
|
function isBreakingChangeSection(sectionName) {
|
|
415
|
-
|
|
355
|
+
return sectionName === "breaking" || sectionName === "major" || sectionName === "minor";
|
|
416
356
|
}
|
|
417
357
|
function displayWarningEntry(entry) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
logInfo(`- ${validationMessage}`, { indent: 3 });
|
|
421
|
-
}
|
|
358
|
+
logInfo(`» file://${entry.changesetPath}`, { indent: 2 });
|
|
359
|
+
for (const validationMessage of entry.data.validations || []) logInfo(`- ${validationMessage}`, { indent: 3 });
|
|
422
360
|
}
|
|
423
361
|
function displayValidEntry(entry, sectionName, isSinglePackage, transformScope) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
453
|
-
* For licensing, see LICENSE.md.
|
|
454
|
-
*/
|
|
455
|
-
/**
|
|
456
|
-
* This function limits the size of the changelog by removing older entries.
|
|
457
|
-
*/
|
|
362
|
+
const isEntryFullyValid = !entry.data.validations?.length;
|
|
363
|
+
const scopeFormatted = transformScope ? entry.data.scope.map((scope) => transformScope(scope).displayName) : entry.data.scope;
|
|
364
|
+
const scope = entry.data.scope.length ? styleText("grey", scopeFormatted?.join(", ")) : `${styleText(["italic", "gray"], "(no scope)")}`;
|
|
365
|
+
const validationIndicator = isEntryFullyValid ? styleText("green", "+") : styleText("yellow", "x");
|
|
366
|
+
const trimmedMessageContent = String(entry.data.mainContent).length > 100 ? entry.data.mainContent?.slice(0, 100) + "..." : entry.data.mainContent;
|
|
367
|
+
if (isSinglePackage) logInfo(`${validationIndicator} ${trimmedMessageContent}`, { indent: 2 });
|
|
368
|
+
else logInfo(`${validationIndicator} ${scope}: ${trimmedMessageContent}`, { indent: 2 });
|
|
369
|
+
if (!isBreakingChangeSection(sectionName)) return;
|
|
370
|
+
entry.data.see.forEach(({ link }) => {
|
|
371
|
+
logInfo(`- See: ${link}`, { indent: 3 });
|
|
372
|
+
});
|
|
373
|
+
entry.data.closes.forEach(({ link }) => {
|
|
374
|
+
logInfo(`- Closes: ${link}`, { indent: 3 });
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
//#endregion
|
|
378
|
+
//#region src/utils/truncatechangelog.ts
|
|
379
|
+
/**
|
|
380
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
381
|
+
* For licensing, see LICENSE.md.
|
|
382
|
+
*/
|
|
383
|
+
/**
|
|
384
|
+
* This function limits the size of the changelog by removing older entries.
|
|
385
|
+
*/
|
|
458
386
|
function truncateChangelog(length, cwd) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
const truncatedEntries = entries.slice(0, length);
|
|
472
|
-
const repositoryUrl = workspaces.getRepositoryUrl(cwd);
|
|
473
|
-
const changelogFooter = entries.length > truncatedEntries.length ?
|
|
474
|
-
`\n\n---\n\nTo see all releases, visit the [release page](${repositoryUrl}/releases).\n` :
|
|
475
|
-
'\n';
|
|
476
|
-
const truncatedChangelog = CHANGELOG_HEADER + '\n\n' + truncatedEntries.join('\n').trim() + changelogFooter;
|
|
477
|
-
saveChangelog(truncatedChangelog, cwd);
|
|
387
|
+
const changelog = getChangelog(cwd);
|
|
388
|
+
if (!changelog) return;
|
|
389
|
+
const entryHeader = "## [\\s\\S]+?";
|
|
390
|
+
const entryHeaderRegexp = new RegExp(`\\n(${entryHeader})(?=\\n${entryHeader}|$)`, "g");
|
|
391
|
+
const entries = [...changelog.matchAll(entryHeaderRegexp)].filter((match) => match && match[1]).map((match) => match[1]);
|
|
392
|
+
if (!entries.length) return;
|
|
393
|
+
const truncatedEntries = entries.slice(0, length);
|
|
394
|
+
const repositoryUrl = workspaces.getRepositoryUrl(cwd);
|
|
395
|
+
const changelogFooter = entries.length > truncatedEntries.length ? `\n\n---\n\nTo see all releases, visit the [release page](${repositoryUrl}/releases).\n` : "\n";
|
|
396
|
+
saveChangelog(CHANGELOG_HEADER + "\n\n" + truncatedEntries.join("\n").trim() + changelogFooter, cwd);
|
|
478
397
|
}
|
|
479
398
|
function getChangelog(cwd) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
return fs.readFileSync(changelogFile, 'utf-8');
|
|
399
|
+
const changelogFile = upath.join(cwd, CHANGELOG_FILE);
|
|
400
|
+
if (!fs$1.existsSync(changelogFile)) return null;
|
|
401
|
+
return fs$1.readFileSync(changelogFile, "utf-8");
|
|
485
402
|
}
|
|
486
403
|
function saveChangelog(content, cwd) {
|
|
487
|
-
|
|
488
|
-
|
|
404
|
+
const changelogFile = upath.join(cwd, CHANGELOG_FILE);
|
|
405
|
+
fs$1.writeFileSync(changelogFile, content, "utf-8");
|
|
489
406
|
}
|
|
490
|
-
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/utils/modifychangelog.ts
|
|
491
409
|
/**
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
410
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
411
|
+
* For licensing, see LICENSE.md.
|
|
412
|
+
*/
|
|
495
413
|
/**
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
414
|
+
* This function writes the generated changelog content to the repository's changelog file.
|
|
415
|
+
*
|
|
416
|
+
* It reads the existing changelog (if any), inserts the new changelog content after the defined header,
|
|
417
|
+
* writes the updated content back to the changelog file, and truncates the changelog to keep a manageable length.
|
|
418
|
+
*/
|
|
501
419
|
async function modifyChangelog(newChangelog, cwd) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
await truncateChangelog(5, cwd);
|
|
420
|
+
const changelogPath = upath.join(cwd, CHANGELOG_FILE);
|
|
421
|
+
const updatedChangelog = prepareChangelogContent(await readExistingChangelog(changelogPath), newChangelog);
|
|
422
|
+
logInfo(`○ ${styleText("cyan", "Appending changes to the existing changelog...")}`);
|
|
423
|
+
await fs.writeFile(changelogPath, updatedChangelog, "utf-8");
|
|
424
|
+
await truncateChangelog(5, cwd);
|
|
508
425
|
}
|
|
509
426
|
/**
|
|
510
|
-
|
|
511
|
-
|
|
427
|
+
* Reads the existing changelog file or returns an empty string if the file doesn't exist.
|
|
428
|
+
*/
|
|
512
429
|
async function readExistingChangelog(changelogPath) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
430
|
+
try {
|
|
431
|
+
return await fs.readFile(changelogPath, "utf-8");
|
|
432
|
+
} catch {
|
|
433
|
+
logInfo(`○ ${styleText("yellow", "CHANGELOG.md not found.")}`);
|
|
434
|
+
return "";
|
|
435
|
+
}
|
|
520
436
|
}
|
|
521
437
|
/**
|
|
522
|
-
|
|
523
|
-
|
|
438
|
+
* Prepares the new changelog content by inserting it after the header or at the beginning if header is missing.
|
|
439
|
+
*/
|
|
524
440
|
function prepareChangelogContent(existingChangelog, newChangelog) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
*/
|
|
441
|
+
const headerIndex = existingChangelog.indexOf(CHANGELOG_HEADER);
|
|
442
|
+
if (headerIndex === -1) return `${CHANGELOG_HEADER}\n\n${newChangelog}${existingChangelog}`;
|
|
443
|
+
const insertPosition = headerIndex + 19;
|
|
444
|
+
return existingChangelog.slice(0, insertPosition) + "\n\n" + newChangelog + existingChangelog.slice(insertPosition);
|
|
445
|
+
}
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region src/utils/useraborterror.ts
|
|
448
|
+
/**
|
|
449
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
450
|
+
* For licensing, see LICENSE.md.
|
|
451
|
+
*/
|
|
452
|
+
/**
|
|
453
|
+
* Custom error class for handling a case when a user aborts the process (at any stage).
|
|
454
|
+
*/
|
|
455
|
+
var UserAbortError = class extends Error {};
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/utils/validateinputversion.ts
|
|
458
|
+
/**
|
|
459
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
460
|
+
* For licensing, see LICENSE.md.
|
|
461
|
+
*/
|
|
547
462
|
async function validateInputVersion(options) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
// Promoting a pre‑release: new version ≥ suggested version.
|
|
570
|
-
if (isPrereleasePromote && !semver.gte(newVersion, suggestedVersion)) {
|
|
571
|
-
return `Provided version must be higher or equal to "${suggestedVersion}".`;
|
|
572
|
-
}
|
|
573
|
-
// Continuing a pre‑release stream: channel cannot change.
|
|
574
|
-
if (isPrerelease &&
|
|
575
|
-
currentChannel !== 'latest' &&
|
|
576
|
-
currentChannel !== newChannel) {
|
|
577
|
-
return `Provided channel must be the same existing channel ${currentChannel}.`;
|
|
578
|
-
}
|
|
579
|
-
// Latest release must not carry a channel suffix.
|
|
580
|
-
if (isLatest && newChannel !== 'latest') {
|
|
581
|
-
return 'You chose the "latest" release type. Please provide a version without a channel suffix.';
|
|
582
|
-
}
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
588
|
-
* For licensing, see LICENSE.md.
|
|
589
|
-
*/
|
|
463
|
+
const { newVersion, version, releaseType, packageName, suggestedVersion } = options;
|
|
464
|
+
const [newChannel] = semver.prerelease(newVersion) || ["latest"];
|
|
465
|
+
const [currentChannel] = semver.prerelease(version) || ["latest"];
|
|
466
|
+
if (!semver.valid(newVersion)) return "Please provide a valid version.";
|
|
467
|
+
if (!semver.gt(newVersion, version)) return `Provided version must be higher than "${version}".`;
|
|
468
|
+
if (!await npm.checkVersionAvailability(newVersion, packageName)) return "Given version is already taken.";
|
|
469
|
+
const isPrerelease = releaseType === "prerelease";
|
|
470
|
+
const isPrereleasePromote = releaseType === "prerelease-promote";
|
|
471
|
+
const isLatest = releaseType === "latest";
|
|
472
|
+
if ((isPrerelease || isPrereleasePromote) && newChannel === "latest") return "You chose the \"pre-release\" release type. Please provide a version with a channel suffix.";
|
|
473
|
+
if (isPrereleasePromote && !semver.gte(newVersion, suggestedVersion)) return `Provided version must be higher or equal to "${suggestedVersion}".`;
|
|
474
|
+
if (isPrerelease && currentChannel !== "latest" && currentChannel !== newChannel) return `Provided channel must be the same existing channel ${currentChannel}.`;
|
|
475
|
+
if (isLatest && newChannel !== "latest") return "You chose the \"latest\" release type. Please provide a version without a channel suffix.";
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/utils/providenewversion.ts
|
|
480
|
+
/**
|
|
481
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
482
|
+
* For licensing, see LICENSE.md.
|
|
483
|
+
*/
|
|
590
484
|
const CLI_INDENT_SIZE = 3;
|
|
591
485
|
/**
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
486
|
+
* Prompts the user to provide a new version for a package.
|
|
487
|
+
*
|
|
488
|
+
* Validates the input (version format, version higher than current, availability).
|
|
489
|
+
*
|
|
490
|
+
* Optionally shows warnings for invalid changes and allows user to abort.
|
|
491
|
+
*/
|
|
598
492
|
async function provideNewVersion(options) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
const question = createVersionQuestion(options);
|
|
609
|
-
const { customVersion, version } = await inquirer.prompt(question);
|
|
610
|
-
return customVersion || version;
|
|
493
|
+
if (options.displayValidationWarning) {
|
|
494
|
+
displayInvalidChangesWarning();
|
|
495
|
+
if (!await askContinueConfirmation(options.indentLevel)) throw new UserAbortError("Aborted while detecting invalid changes.");
|
|
496
|
+
}
|
|
497
|
+
const question = createVersionQuestion(options);
|
|
498
|
+
const { customVersion, version } = await inquirer.prompt(question);
|
|
499
|
+
return customVersion || version;
|
|
611
500
|
}
|
|
612
501
|
/**
|
|
613
|
-
|
|
614
|
-
|
|
502
|
+
* Displays a warning message about invalid changes in a visible color.
|
|
503
|
+
*/
|
|
615
504
|
function displayInvalidChangesWarning() {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
505
|
+
logInfo("");
|
|
506
|
+
logInfo(styleText(["yellow", "bold"], `⚠️ ${styleText("underline", "WARNING: Invalid changes detected!")}`));
|
|
507
|
+
logInfo("");
|
|
508
|
+
logInfo(styleText("yellow", "You can cancel the process, fix the invalid files, and run the tool again."));
|
|
509
|
+
logInfo(styleText("yellow", "Alternatively, you can continue - but invalid values will be lost."));
|
|
510
|
+
logInfo("");
|
|
622
511
|
}
|
|
623
512
|
/**
|
|
624
|
-
|
|
625
|
-
|
|
513
|
+
* Asks the user if they want to continue with the version bump process.
|
|
514
|
+
*/
|
|
626
515
|
async function askContinueConfirmation(indentLevel = 0) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
*/
|
|
516
|
+
const question = {
|
|
517
|
+
type: "confirm",
|
|
518
|
+
name: "continue",
|
|
519
|
+
message: "Should continue?",
|
|
520
|
+
default: false,
|
|
521
|
+
prefix: " ".repeat(indentLevel * CLI_INDENT_SIZE) + styleText("cyan", "?")
|
|
522
|
+
};
|
|
523
|
+
return (await inquirer.prompt(question)).continue;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Creates a prompt question for version input with validation.
|
|
527
|
+
*/
|
|
640
528
|
function createVersionQuestion(options) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
529
|
+
const { version, packageName, bumpType, releaseChannel, releaseType, indentLevel = 0 } = options;
|
|
530
|
+
const suggestedVersion = getSuggestedVersion(bumpType, version, releaseChannel) || version;
|
|
531
|
+
return [{
|
|
532
|
+
type: "list",
|
|
533
|
+
name: "version",
|
|
534
|
+
default: suggestedVersion,
|
|
535
|
+
message: [
|
|
536
|
+
"Select the new version.",
|
|
537
|
+
`Current version: ${styleText("cyan", version)}.`,
|
|
538
|
+
`Suggested version: ${styleText("cyan", suggestedVersion)}.`
|
|
539
|
+
].join(" "),
|
|
540
|
+
prefix: " ".repeat(indentLevel * CLI_INDENT_SIZE) + styleText("cyan", "?"),
|
|
541
|
+
choices: getChoices$1({
|
|
542
|
+
version,
|
|
543
|
+
bumpType,
|
|
544
|
+
releaseChannel,
|
|
545
|
+
releaseType
|
|
546
|
+
})
|
|
547
|
+
}, {
|
|
548
|
+
type: "input",
|
|
549
|
+
name: "customVersion",
|
|
550
|
+
message: "Enter your custom version:",
|
|
551
|
+
when: ({ version }) => version === "custom",
|
|
552
|
+
filter: (newVersion) => newVersion.trim(),
|
|
553
|
+
validate: (newVersion) => validateInputVersion({
|
|
554
|
+
newVersion,
|
|
555
|
+
version,
|
|
556
|
+
releaseType,
|
|
557
|
+
packageName,
|
|
558
|
+
suggestedVersion
|
|
559
|
+
}),
|
|
560
|
+
prefix: " ".repeat(indentLevel * CLI_INDENT_SIZE) + styleText("cyan", "?")
|
|
561
|
+
}];
|
|
664
562
|
}
|
|
665
563
|
function getSuggestedVersion(bumpType, version, releaseChannel) {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
else if (bumpType === 'prerelease' && releaseChannel === 'latest') {
|
|
670
|
-
// Using 'premajor` and `alpha` channel for a case, when introducing a prerelease for the next major.
|
|
671
|
-
// E.g. 1.0.0 -> 2.0.0-alpha.0.
|
|
672
|
-
return semver.inc(version, 'premajor', 'alpha');
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
return semver.inc(version, bumpType);
|
|
676
|
-
}
|
|
564
|
+
if (bumpType === "prerelease" && releaseChannel !== "latest") return semver.inc(version, bumpType, releaseChannel);
|
|
565
|
+
if (bumpType === "premajor" || bumpType === "preminor" || bumpType === "prepatch") return semver.inc(version, bumpType, "alpha");
|
|
566
|
+
return semver.inc(version, bumpType);
|
|
677
567
|
}
|
|
678
568
|
function getChoices$1({ version, bumpType, releaseChannel, releaseType }) {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
* Detects the release channel from a version string.
|
|
714
|
-
*/
|
|
569
|
+
const proposedVersions = [];
|
|
570
|
+
const preReleaseChannels = [
|
|
571
|
+
"alpha",
|
|
572
|
+
"beta",
|
|
573
|
+
"rc"
|
|
574
|
+
];
|
|
575
|
+
const preInitialBumpTypes = [
|
|
576
|
+
"premajor",
|
|
577
|
+
"preminor",
|
|
578
|
+
"prepatch"
|
|
579
|
+
];
|
|
580
|
+
const validPromotionChannels = preReleaseChannels.filter((value, index, array) => index >= array.indexOf(releaseChannel));
|
|
581
|
+
if (releaseType === "latest" && releaseChannel === "latest") proposedVersions.push(semver.inc(version, "major"), semver.inc(version, "minor"), semver.inc(version, "patch"));
|
|
582
|
+
if (preInitialBumpTypes.includes(bumpType) && releaseType === "prerelease" && releaseChannel === "latest") proposedVersions.push(semver.inc(version, "premajor", "alpha"), semver.inc(version, "preminor", "alpha"), semver.inc(version, "prepatch", "alpha"));
|
|
583
|
+
if (releaseType === "latest" && preReleaseChannels.includes(releaseChannel)) proposedVersions.push(semver.inc(version, "release"));
|
|
584
|
+
if (bumpType === "prerelease" && releaseType === "prerelease" && preReleaseChannels.includes(releaseChannel)) proposedVersions.push(semver.inc(version, "prerelease", releaseChannel));
|
|
585
|
+
if (bumpType === "prerelease" && releaseType === "prerelease-promote" && preReleaseChannels.includes(releaseChannel)) proposedVersions.push(...validPromotionChannels.map((channel) => semver.inc(version, "prerelease", channel)));
|
|
586
|
+
return [...proposedVersions.map((proposedVersion) => ({
|
|
587
|
+
name: proposedVersion,
|
|
588
|
+
value: proposedVersion
|
|
589
|
+
})), {
|
|
590
|
+
name: "Custom...",
|
|
591
|
+
value: "custom"
|
|
592
|
+
}];
|
|
593
|
+
}
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/utils/detectreleasechannel.ts
|
|
596
|
+
/**
|
|
597
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
598
|
+
* For licensing, see LICENSE.md.
|
|
599
|
+
*/
|
|
600
|
+
/**
|
|
601
|
+
* Detects the release channel from a version string.
|
|
602
|
+
*/
|
|
715
603
|
function detectReleaseChannel(version, promotePrerelease = false) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
|
|
752
|
-
* The function handles:
|
|
753
|
-
* * Automatic version bump calculation from categorized changelog sections (major, minor, patch).
|
|
754
|
-
* * Version bump for prerelease channels.
|
|
755
|
-
* * User prompts for version input when no explicit version is provided.
|
|
756
|
-
*/
|
|
604
|
+
const prerelease = semver.prerelease(version);
|
|
605
|
+
if (!prerelease) return "latest";
|
|
606
|
+
const currentChannel = prerelease[0];
|
|
607
|
+
if (promotePrerelease) {
|
|
608
|
+
if (currentChannel === "alpha") return "beta";
|
|
609
|
+
if (currentChannel === "beta") return "rc";
|
|
610
|
+
logInfo(styleText("yellow", `Warning! Unknown release channel to promote from ${currentChannel}.`));
|
|
611
|
+
return "alpha";
|
|
612
|
+
}
|
|
613
|
+
return currentChannel;
|
|
614
|
+
}
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/utils/internalerror.ts
|
|
617
|
+
/**
|
|
618
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
619
|
+
* For licensing, see LICENSE.md.
|
|
620
|
+
*/
|
|
621
|
+
/**
|
|
622
|
+
* Custom error class for handling validation errors in the changelog generation process.
|
|
623
|
+
*/
|
|
624
|
+
var InternalError = class extends Error {};
|
|
625
|
+
//#endregion
|
|
626
|
+
//#region src/utils/determinenextversion.ts
|
|
627
|
+
/**
|
|
628
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
629
|
+
* For licensing, see LICENSE.md.
|
|
630
|
+
*/
|
|
631
|
+
/**
|
|
632
|
+
* Determines the next version for a single package or a mono-repository setup based on
|
|
633
|
+
* the change sections, user input, and semantic versioning rules.
|
|
634
|
+
*
|
|
635
|
+
* The function handles:
|
|
636
|
+
* * Automatic version bump calculation from categorized changelog sections (major, minor, patch).
|
|
637
|
+
* * Version bump for prerelease channels.
|
|
638
|
+
* * User prompts for version input when no explicit version is provided.
|
|
639
|
+
*/
|
|
757
640
|
async function determineNextVersion(options) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const newPromise = this.promise.then(async (arr) => {
|
|
842
|
-
const mapped = await Promise.all(arr.map(fn));
|
|
843
|
-
return mapped.flat();
|
|
844
|
-
});
|
|
845
|
-
return new AsyncArray(newPromise);
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* Allows chaining or awaiting the result of the internal promise.
|
|
849
|
-
*/
|
|
850
|
-
then(onfulfilled) {
|
|
851
|
-
return this.promise.then(onfulfilled);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
641
|
+
const { sections, currentVersion, packageName, nextVersion, releaseType } = options;
|
|
642
|
+
if (nextVersion) {
|
|
643
|
+
logInfo(`○ ${styleText("cyan", `Determined the next version to be ${nextVersion}.`)}`);
|
|
644
|
+
if (nextVersion.startsWith("0.0.0-")) return nextVersion;
|
|
645
|
+
const validationResult = await validateInputVersion({
|
|
646
|
+
newVersion: nextVersion,
|
|
647
|
+
suggestedVersion: nextVersion,
|
|
648
|
+
version: currentVersion,
|
|
649
|
+
releaseType,
|
|
650
|
+
packageName
|
|
651
|
+
});
|
|
652
|
+
if (typeof validationResult === "string") throw new InternalError(validationResult);
|
|
653
|
+
return nextVersion;
|
|
654
|
+
}
|
|
655
|
+
logInfo(`○ ${styleText("cyan", "Determining the new version...")}`);
|
|
656
|
+
let bumpType = "patch";
|
|
657
|
+
if (sections.major.entries.length || sections.breaking.entries.length) bumpType = "major";
|
|
658
|
+
else if (sections.minor.entries.length || sections.feature.entries.length) bumpType = "minor";
|
|
659
|
+
if (releaseType === "prerelease" || releaseType === "prerelease-promote") bumpType = semver.prerelease(currentVersion) ? "prerelease" : `pre${bumpType}`;
|
|
660
|
+
const areErrorsPresent = !!sections.invalid.entries.length;
|
|
661
|
+
const areWarningsPresent = Object.values(sections).some((section) => section.entries.some((entry) => entry.data.validations && entry.data.validations.length > 0));
|
|
662
|
+
return await provideNewVersion({
|
|
663
|
+
packageName,
|
|
664
|
+
bumpType,
|
|
665
|
+
releaseType,
|
|
666
|
+
version: currentVersion,
|
|
667
|
+
releaseChannel: detectReleaseChannel(currentVersion, releaseType === "prerelease-promote"),
|
|
668
|
+
displayValidationWarning: areErrorsPresent || areWarningsPresent
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
//#endregion
|
|
672
|
+
//#region src/utils/asyncarray.ts
|
|
673
|
+
/**
|
|
674
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
675
|
+
* For licensing, see LICENSE.md.
|
|
676
|
+
*/
|
|
677
|
+
/**
|
|
678
|
+
* A utility class that wraps a Promise of an array and provides async array-like operations.
|
|
679
|
+
*/
|
|
680
|
+
var AsyncArray = class AsyncArray {
|
|
681
|
+
promise;
|
|
682
|
+
/**
|
|
683
|
+
* Creates a new `AsyncArray` instance.
|
|
684
|
+
*/
|
|
685
|
+
constructor(promise) {
|
|
686
|
+
this.promise = promise;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Creates an `AsyncArray` from a given promise.
|
|
690
|
+
*/
|
|
691
|
+
static from(promise) {
|
|
692
|
+
return new AsyncArray(promise);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Asynchronously maps each item in the array using the provided callback.
|
|
696
|
+
*/
|
|
697
|
+
map(fn) {
|
|
698
|
+
return new AsyncArray(this.promise.then((arr) => Promise.all(arr.map(fn))));
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Flattens one level of nesting in an array of arrays.
|
|
702
|
+
*/
|
|
703
|
+
flat() {
|
|
704
|
+
return new AsyncArray(this.promise.then((arr) => arr.flat()));
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Maps each item using a callback that returns an array (or promise of an array),
|
|
708
|
+
* then flattens the result by one level.
|
|
709
|
+
*/
|
|
710
|
+
flatMap(fn) {
|
|
711
|
+
return new AsyncArray(this.promise.then(async (arr) => {
|
|
712
|
+
return (await Promise.all(arr.map(fn))).flat();
|
|
713
|
+
}));
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Allows chaining or awaiting the result of the internal promise.
|
|
717
|
+
*/
|
|
718
|
+
then(onfulfilled) {
|
|
719
|
+
return this.promise.then(onfulfilled);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
//#endregion
|
|
723
|
+
//#region src/utils/findpackages.ts
|
|
855
724
|
/**
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
725
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
726
|
+
* For licensing, see LICENSE.md.
|
|
727
|
+
*/
|
|
859
728
|
/**
|
|
860
|
-
|
|
861
|
-
|
|
729
|
+
* Retrieves the names and versions of packages found in both the main repository and any external repositories.
|
|
730
|
+
*/
|
|
862
731
|
async function findPackages(options) {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
/**
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
*
|
|
885
|
-
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
|
|
889
|
-
* * Group packages by release type based on the changelog sections: major, minor, and features.
|
|
890
|
-
* * Exclude packages already accounted for in higher-priority release categories from lower ones.
|
|
891
|
-
* * Provide a fallback category for "other releases" that don't fall into the above groups.
|
|
892
|
-
*/
|
|
732
|
+
const { cwd, packagesDirectory, externalRepositories, shouldIgnoreRootPackage = false } = options;
|
|
733
|
+
const externalPackagesPromises = externalRepositories.map((externalRepository) => {
|
|
734
|
+
return workspaces.findPathsToPackages(externalRepository.cwd, externalRepository.packagesDirectory, { includePackageJson: true });
|
|
735
|
+
});
|
|
736
|
+
const promise = Promise.all([workspaces.findPathsToPackages(cwd, packagesDirectory, {
|
|
737
|
+
includeCwd: !shouldIgnoreRootPackage,
|
|
738
|
+
includePackageJson: true
|
|
739
|
+
}), ...externalPackagesPromises]);
|
|
740
|
+
return AsyncArray.from(promise).flat().map((packagePath) => fs$1.readFileSync(packagePath, "utf-8")).map((packageJson) => JSON.parse(packageJson)).map(({ name, version }) => [name, version]).then((entries) => new Map(entries.sort(([a], [b]) => a.localeCompare(b))));
|
|
741
|
+
}
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/utils/composereleasesummary.ts
|
|
744
|
+
/**
|
|
745
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
746
|
+
* For licensing, see LICENSE.md.
|
|
747
|
+
*/
|
|
748
|
+
/**
|
|
749
|
+
* Generates a categorized summary of packages released in the new version,
|
|
750
|
+
* including new packages, major, minor, feature, and other releases.
|
|
751
|
+
*
|
|
752
|
+
* This function analyzes changelog sections and package metadata to:
|
|
753
|
+
* * Identify new packages introduced with version '0.0.1'.
|
|
754
|
+
* * Group packages by release type based on the changelog sections: major, minor, and features.
|
|
755
|
+
* * Exclude packages already accounted for in higher-priority release categories from lower ones.
|
|
756
|
+
* * Provide a fallback category for "other releases" that don't fall into the above groups.
|
|
757
|
+
*/
|
|
893
758
|
async function composeReleaseSummary(options) {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
759
|
+
const { sections, currentVersion, newVersion, packagesMetadata } = options;
|
|
760
|
+
const versionUpgradeText = `v${currentVersion} => v${newVersion}`;
|
|
761
|
+
const packageNames = [...packagesMetadata.keys()];
|
|
762
|
+
const newVersionReleases = getNewVersionReleases(packagesMetadata);
|
|
763
|
+
const majorReleases = getScopeWithOrgNamespace(sections.major.entries, {
|
|
764
|
+
packagesToRemove: newVersionReleases,
|
|
765
|
+
packageNames
|
|
766
|
+
});
|
|
767
|
+
const minorReleases = getScopeWithOrgNamespace(sections.minor.entries, {
|
|
768
|
+
packagesToRemove: [...majorReleases, ...newVersionReleases],
|
|
769
|
+
packageNames
|
|
770
|
+
});
|
|
771
|
+
const newFeaturesReleases = getScopeWithOrgNamespace(sections.feature.entries, {
|
|
772
|
+
packagesToRemove: [
|
|
773
|
+
...minorReleases,
|
|
774
|
+
...majorReleases,
|
|
775
|
+
...newVersionReleases
|
|
776
|
+
],
|
|
777
|
+
packageNames
|
|
778
|
+
});
|
|
779
|
+
const packagesToRemoveFromOtherReleases = [
|
|
780
|
+
majorReleases,
|
|
781
|
+
minorReleases,
|
|
782
|
+
newFeaturesReleases,
|
|
783
|
+
newVersionReleases
|
|
784
|
+
].flat();
|
|
785
|
+
const otherReleases = packageNames.filter((packageName) => !packagesToRemoveFromOtherReleases.includes(packageName)).sort();
|
|
786
|
+
return [
|
|
787
|
+
{
|
|
788
|
+
title: "New packages:",
|
|
789
|
+
version: `v${newVersion}`,
|
|
790
|
+
packages: newVersionReleases
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
title: "Major releases (contain major breaking changes):",
|
|
794
|
+
version: versionUpgradeText,
|
|
795
|
+
packages: majorReleases
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
title: "Minor releases (contain minor breaking changes):",
|
|
799
|
+
version: versionUpgradeText,
|
|
800
|
+
packages: minorReleases
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
title: "Releases containing new features:",
|
|
804
|
+
version: versionUpgradeText,
|
|
805
|
+
packages: newFeaturesReleases
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
title: "Other releases:",
|
|
809
|
+
version: versionUpgradeText,
|
|
810
|
+
packages: otherReleases
|
|
811
|
+
}
|
|
812
|
+
].filter((release) => release.packages?.length > 0);
|
|
918
813
|
}
|
|
919
814
|
function getNewVersionReleases(packages) {
|
|
920
|
-
|
|
921
|
-
.filter(([, version]) => version === '0.0.1')
|
|
922
|
-
.map(([packageName]) => packageName)
|
|
923
|
-
.sort();
|
|
815
|
+
return [...packages].filter(([, version]) => version === "0.0.1").map(([packageName]) => packageName).sort();
|
|
924
816
|
}
|
|
925
817
|
function getScopeWithOrgNamespace(entries = [], { packagesToRemove, packageNames }) {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
.filter((item, index, array) => array.indexOf(item) === index);
|
|
930
|
-
const packagesFullNames = uniqueScopes.map(scope => {
|
|
931
|
-
return packageNames.find(packageName => getPackageName(packageName) === scope);
|
|
932
|
-
});
|
|
933
|
-
return packagesFullNames.filter(packageName => !packagesToRemove.includes(packageName));
|
|
818
|
+
return entries.flatMap((entry) => entry.data.scope).filter(Boolean).filter((item, index, array) => array.indexOf(item) === index).map((scope) => {
|
|
819
|
+
return packageNames.find((packageName) => getPackageName(packageName) === scope);
|
|
820
|
+
}).filter((packageName) => !packagesToRemove.includes(packageName));
|
|
934
821
|
}
|
|
935
822
|
function getPackageName(value) {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
return value;
|
|
823
|
+
if (value.includes("/")) return value.split("/").at(1);
|
|
824
|
+
return value;
|
|
940
825
|
}
|
|
941
|
-
|
|
826
|
+
//#endregion
|
|
827
|
+
//#region src/utils/findchangelogentrypaths.ts
|
|
942
828
|
/**
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
829
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
830
|
+
* For licensing, see LICENSE.md.
|
|
831
|
+
*/
|
|
946
832
|
/**
|
|
947
|
-
|
|
948
|
-
|
|
833
|
+
* Gathers changelog entry file paths (Markdown files) from the main repository and any configured external repositories.
|
|
834
|
+
*/
|
|
949
835
|
async function findChangelogEntryPaths(options) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
linkFilter: getLinkFilter(options),
|
|
976
|
-
cwd,
|
|
977
|
-
isRoot: true
|
|
978
|
-
};
|
|
979
|
-
return [mainEntry, ...externalResults];
|
|
980
|
-
});
|
|
836
|
+
const { cwd, externalRepositories, includeSubdirectories = true } = options;
|
|
837
|
+
const globPattern = includeSubdirectories ? "**/*.md" : "*.md";
|
|
838
|
+
return AsyncArray.from(Promise.resolve(externalRepositories)).map(async (repo) => {
|
|
839
|
+
return {
|
|
840
|
+
filePaths: (await glob(globPattern, {
|
|
841
|
+
cwd: upath.join(repo.cwd, CHANGESET_DIRECTORY),
|
|
842
|
+
absolute: true
|
|
843
|
+
})).map((p) => upath.normalize(p)),
|
|
844
|
+
gitHubUrl: await workspaces.getRepositoryUrl(repo.cwd, { async: true }),
|
|
845
|
+
linkFilter: getLinkFilter(repo),
|
|
846
|
+
cwd: repo.cwd,
|
|
847
|
+
isRoot: false
|
|
848
|
+
};
|
|
849
|
+
}).then(async (externalResults) => {
|
|
850
|
+
return [{
|
|
851
|
+
filePaths: (await glob(globPattern, {
|
|
852
|
+
cwd: upath.join(cwd, CHANGESET_DIRECTORY),
|
|
853
|
+
absolute: true
|
|
854
|
+
})).map((p) => upath.normalize(p)),
|
|
855
|
+
gitHubUrl: await workspaces.getRepositoryUrl(cwd, { async: true }),
|
|
856
|
+
linkFilter: getLinkFilter(options),
|
|
857
|
+
cwd,
|
|
858
|
+
isRoot: true
|
|
859
|
+
}, ...externalResults];
|
|
860
|
+
});
|
|
981
861
|
}
|
|
982
862
|
function getLinkFilter(options) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
986
|
-
return options.linkFilter || (() => true);
|
|
863
|
+
if (typeof options.shouldSkipLinks === "boolean") return () => !options.shouldSkipLinks;
|
|
864
|
+
return options.linkFilter || (() => true);
|
|
987
865
|
}
|
|
988
|
-
|
|
866
|
+
//#endregion
|
|
867
|
+
//#region src/utils/sortentriesbyscopeanddate.ts
|
|
989
868
|
/**
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
869
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
870
|
+
* For licensing, see LICENSE.md.
|
|
871
|
+
*/
|
|
993
872
|
/**
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
873
|
+
* Sorts parsed files according to the rules:
|
|
874
|
+
* 1. Entries with more scopes at the top.
|
|
875
|
+
* 2. Entries with single scope grouped by scope and sorted by date within a group.
|
|
876
|
+
* 3. Entries with no scope at the bottom.
|
|
877
|
+
*/
|
|
999
878
|
function sortEntriesByScopeAndDate(entries) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
/**
|
|
1023
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1024
|
-
* For licensing, see LICENSE.md.
|
|
1025
|
-
*/
|
|
879
|
+
return entries.sort((itemBefore, itemAfter) => {
|
|
880
|
+
const beforeScopeCount = itemBefore.data.scope.length;
|
|
881
|
+
const afterScopeCount = itemAfter.data.scope.length;
|
|
882
|
+
if (beforeScopeCount === 1 && afterScopeCount === 1) {
|
|
883
|
+
const firstScope = itemBefore.data.scope.at(0);
|
|
884
|
+
const secondScope = itemAfter.data.scope.at(0);
|
|
885
|
+
if (firstScope !== secondScope) return firstScope.localeCompare(secondScope);
|
|
886
|
+
return itemBefore.createdAt.getTime() - itemAfter.createdAt.getTime();
|
|
887
|
+
}
|
|
888
|
+
if (beforeScopeCount === afterScopeCount) return itemBefore.createdAt.getTime() - itemAfter.createdAt.getTime();
|
|
889
|
+
return afterScopeCount - beforeScopeCount;
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
//#endregion
|
|
893
|
+
//#region src/utils/normalizeentry.ts
|
|
894
|
+
/**
|
|
895
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
896
|
+
* For licensing, see LICENSE.md.
|
|
897
|
+
*/
|
|
1026
898
|
function normalizeEntry(entry, isSinglePackage) {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
.filter(see => see)
|
|
1044
|
-
.map(credits => ensureAt(String(credits)));
|
|
1045
|
-
return {
|
|
1046
|
-
...entry,
|
|
1047
|
-
data: {
|
|
1048
|
-
type: typeNormalized,
|
|
1049
|
-
scope: deduplicate(scopeNormalized),
|
|
1050
|
-
closes: deduplicate(closesNormalized),
|
|
1051
|
-
see: deduplicate(seeNormalized),
|
|
1052
|
-
communityCredits: deduplicate(communityCreditsNormalized),
|
|
1053
|
-
validations: []
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
899
|
+
const typeNormalized = getTypeNormalized(entry.data.type, isSinglePackage);
|
|
900
|
+
const scopeNormalized = toArray(entry.data.scope).filter((scope) => scope).map((scopeEntry) => String(scopeEntry).toLowerCase()).sort();
|
|
901
|
+
const closesNormalized = toArray(entry.data.closes).filter((closes) => closes).map((closes) => String(closes));
|
|
902
|
+
const seeNormalized = toArray(entry.data.see).filter((see) => see).map((see) => String(see));
|
|
903
|
+
const communityCreditsNormalized = toArray(entry.data.communityCredits).filter((see) => see).map((credits) => ensureAt(String(credits)));
|
|
904
|
+
return {
|
|
905
|
+
...entry,
|
|
906
|
+
data: {
|
|
907
|
+
type: typeNormalized,
|
|
908
|
+
scope: deduplicate(scopeNormalized),
|
|
909
|
+
closes: deduplicate(closesNormalized),
|
|
910
|
+
see: deduplicate(seeNormalized),
|
|
911
|
+
communityCredits: deduplicate(communityCreditsNormalized),
|
|
912
|
+
validations: []
|
|
913
|
+
}
|
|
914
|
+
};
|
|
1056
915
|
}
|
|
1057
916
|
function getTypeNormalized(type, isSinglePackage) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
return 'Breaking change';
|
|
1062
|
-
}
|
|
1063
|
-
return typeCapitalized;
|
|
917
|
+
const typeCapitalized = capitalize(type);
|
|
918
|
+
if (isSinglePackage && ["Major breaking change", "Minor breaking change"].includes(typeCapitalized)) return "Breaking change";
|
|
919
|
+
return typeCapitalized;
|
|
1064
920
|
}
|
|
1065
921
|
function capitalize(value) {
|
|
1066
|
-
|
|
1067
|
-
|
|
922
|
+
const valueStr = String(value);
|
|
923
|
+
return valueStr.charAt(0).toUpperCase() + valueStr.slice(1).toLowerCase();
|
|
1068
924
|
}
|
|
1069
925
|
function ensureAt(str) {
|
|
1070
|
-
|
|
926
|
+
return str.startsWith("@") ? str : "@" + str;
|
|
1071
927
|
}
|
|
1072
928
|
function deduplicate(packageNames) {
|
|
1073
|
-
|
|
929
|
+
return [...new Set(packageNames)];
|
|
1074
930
|
}
|
|
1075
931
|
function toArray(input) {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1079
|
-
return Array.isArray(input) ? input : [input];
|
|
932
|
+
if (!input) return [];
|
|
933
|
+
return Array.isArray(input) ? input : [input];
|
|
1080
934
|
}
|
|
1081
|
-
|
|
935
|
+
//#endregion
|
|
936
|
+
//#region src/utils/parsechangelogentries.ts
|
|
1082
937
|
/**
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
938
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
939
|
+
* For licensing, see LICENSE.md.
|
|
940
|
+
*/
|
|
1086
941
|
/**
|
|
1087
|
-
|
|
1088
|
-
|
|
942
|
+
* Reads and processes input files to extract changelog entries.
|
|
943
|
+
*/
|
|
1089
944
|
function parseChangelogEntries(entryPaths, isSinglePackage) {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
* Defaults to the current date if the filename does not match the expected format.
|
|
1112
|
-
*/
|
|
945
|
+
const fileEntries = entryPaths.reduce((acc, { filePaths, gitHubUrl, linkFilter }) => {
|
|
946
|
+
for (const changesetPath of filePaths) acc.push({
|
|
947
|
+
changesetPath,
|
|
948
|
+
gitHubUrl,
|
|
949
|
+
linkFilter
|
|
950
|
+
});
|
|
951
|
+
return acc;
|
|
952
|
+
}, []);
|
|
953
|
+
return AsyncArray.from(Promise.resolve(fileEntries)).map(async ({ changesetPath, gitHubUrl, linkFilter }) => ({
|
|
954
|
+
...matter(await fs.readFile(changesetPath, "utf-8")),
|
|
955
|
+
gitHubUrl,
|
|
956
|
+
changesetPath,
|
|
957
|
+
linkFilter,
|
|
958
|
+
createdAt: extractDateFromFilename(changesetPath)
|
|
959
|
+
})).map((entry) => normalizeEntry(entry, isSinglePackage)).then((entries) => sortEntriesByScopeAndDate(entries));
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Extracts date from an entry filename (`YYYYMMDDHHMMSS_*.md`).
|
|
963
|
+
*
|
|
964
|
+
* Defaults to the current date if the filename does not match the expected format.
|
|
965
|
+
*/
|
|
1113
966
|
function extractDateFromFilename(changesetPath) {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
*
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
* Filters and returns only those changelog sections that:
|
|
1135
|
-
* * Have at least one entry.
|
|
1136
|
-
* * Are not explicitly marked to be excluded from the final changelog.
|
|
1137
|
-
*
|
|
1138
|
-
* This is used to determine which sections should be displayed or processed for changelog generation.
|
|
1139
|
-
*/
|
|
967
|
+
const now = /* @__PURE__ */ new Date();
|
|
968
|
+
const dateMatch = (changesetPath.split("/").pop() || "").match(/^(\d{14})_/);
|
|
969
|
+
if (!dateMatch || !dateMatch[1]) return now;
|
|
970
|
+
const parsedDate = parse(dateMatch[1], "yyyyMMddHHmmss", now);
|
|
971
|
+
if (!isValid(parsedDate)) return now;
|
|
972
|
+
return parsedDate;
|
|
973
|
+
}
|
|
974
|
+
//#endregion
|
|
975
|
+
//#region src/utils/filtervisiblesections.ts
|
|
976
|
+
/**
|
|
977
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
978
|
+
* For licensing, see LICENSE.md.
|
|
979
|
+
*/
|
|
980
|
+
/**
|
|
981
|
+
* Filters and returns only those changelog sections that:
|
|
982
|
+
* * Have at least one entry.
|
|
983
|
+
* * Are not explicitly marked to be excluded from the final changelog.
|
|
984
|
+
*
|
|
985
|
+
* This is used to determine which sections should be displayed or processed for changelog generation.
|
|
986
|
+
*/
|
|
1140
987
|
function filterVisibleSections(sectionsWithEntries) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (!sectionsToDisplay.length) {
|
|
1147
|
-
const message = 'No valid entries were found. Please ensure that:\n' +
|
|
1148
|
-
'1) Input files exist in the `.changelog/` directory.\n' +
|
|
1149
|
-
'2) The `cwd` parameter points to the root of your project.\n' +
|
|
1150
|
-
'3) The `packagesDirectory` parameter correctly specifies the packages folder.\n' +
|
|
1151
|
-
'If no errors appear in the console but inputs are present, your project configuration may be incorrect.\n' +
|
|
1152
|
-
'If validation errors are shown, please resolve them according to the details provided.\n';
|
|
1153
|
-
throw new InternalError(message);
|
|
1154
|
-
}
|
|
1155
|
-
return sectionsToDisplay;
|
|
988
|
+
const sectionsToDisplay = Object.entries(sectionsWithEntries).filter(([, { entries, excludeInChangelog }]) => {
|
|
989
|
+
return entries?.length && !excludeInChangelog;
|
|
990
|
+
}).map(([, section]) => section);
|
|
991
|
+
if (!sectionsToDisplay.length) throw new InternalError("No valid entries were found. Please ensure that:\n1) Input files exist in the `.changelog/` directory.\n2) The `cwd` parameter points to the root of your project.\n3) The `packagesDirectory` parameter correctly specifies the packages folder.\nIf no errors appear in the console but inputs are present, your project configuration may be incorrect.\nIf validation errors are shown, please resolve them according to the details provided.\n");
|
|
992
|
+
return sectionsToDisplay;
|
|
1156
993
|
}
|
|
1157
|
-
|
|
994
|
+
//#endregion
|
|
995
|
+
//#region src/utils/getdateformatted.ts
|
|
1158
996
|
/**
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
997
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
998
|
+
* For licensing, see LICENSE.md.
|
|
999
|
+
*/
|
|
1162
1000
|
/**
|
|
1163
|
-
|
|
1164
|
-
|
|
1001
|
+
* Formats a date string `YYYY-MM-DD` into a human-readable format for the changelog.
|
|
1002
|
+
*/
|
|
1165
1003
|
function getDateFormatted(date) {
|
|
1166
|
-
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1004
|
+
return format(parse(date, "yyyy-MM-dd", /* @__PURE__ */ new Date()), "LLLL d, yyyy");
|
|
1005
|
+
}
|
|
1006
|
+
//#endregion
|
|
1007
|
+
//#region src/utils/composechangelog.ts
|
|
1008
|
+
/**
|
|
1009
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1010
|
+
* For licensing, see LICENSE.md.
|
|
1011
|
+
*/
|
|
1012
|
+
/**
|
|
1013
|
+
* Generates a formatted changelog string for a new version release.
|
|
1014
|
+
*
|
|
1015
|
+
* This function constructs the changelog content including
|
|
1016
|
+
* * A version header with a link to the GitHub comparison view (except for an initial version).
|
|
1017
|
+
* * Sections with grouped changelog entries and their messages.
|
|
1018
|
+
* * A collapsible summary of released packages and their version bumps for a mono-repository setup.
|
|
1019
|
+
* * Special handling for single-package repositories.
|
|
1020
|
+
*/
|
|
1182
1021
|
async function composeChangelog(options) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
* Cleans up the input files that have been incorporated into the changelog by deleting them
|
|
1218
|
-
* and removing any resulting empty directories both in the current repository and in any external repositories.
|
|
1219
|
-
*/
|
|
1022
|
+
const { cwd, date, currentVersion, newVersion, sections, releasedPackagesInfo, isSinglePackage } = options;
|
|
1023
|
+
const gitHubUrl = await workspaces.getRepositoryUrl(cwd, { async: true });
|
|
1024
|
+
const dateFormatted = getDateFormatted(date);
|
|
1025
|
+
const header = currentVersion === "0.0.1" ? `## ${newVersion} (${dateFormatted})` : `## [${newVersion}](${gitHubUrl}/compare/v${currentVersion}...v${newVersion}) (${dateFormatted})`;
|
|
1026
|
+
const sectionsAsString = sections.map(({ title, entries }) => [
|
|
1027
|
+
`### ${title}`,
|
|
1028
|
+
"",
|
|
1029
|
+
...entries.map((entry) => entry.message),
|
|
1030
|
+
""
|
|
1031
|
+
]).flat().join("\n");
|
|
1032
|
+
const packagesVersionBumps = releasedPackagesInfo.map(({ title, version, packages }) => [
|
|
1033
|
+
"",
|
|
1034
|
+
title,
|
|
1035
|
+
"",
|
|
1036
|
+
...packages.map((packageName) => `* [${packageName}](${NPM_URL}/${packageName}/v/${newVersion}): ${version}`)
|
|
1037
|
+
]).flat().join("\n");
|
|
1038
|
+
const changelog = [
|
|
1039
|
+
header,
|
|
1040
|
+
"",
|
|
1041
|
+
sectionsAsString
|
|
1042
|
+
];
|
|
1043
|
+
if (!isSinglePackage) changelog.push("### Released packages", "", `Check out the [Versioning policy](${VERSIONING_POLICY_URL}) guide for more information.`, "", "<details>", "<summary>Released packages (summary)</summary>", packagesVersionBumps, "</details>", "");
|
|
1044
|
+
return changelog.join("\n");
|
|
1045
|
+
}
|
|
1046
|
+
//#endregion
|
|
1047
|
+
//#region src/utils/removechangelogentryfiles.ts
|
|
1048
|
+
/**
|
|
1049
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1050
|
+
* For licensing, see LICENSE.md.
|
|
1051
|
+
*/
|
|
1052
|
+
/**
|
|
1053
|
+
* Cleans up the input files that have been incorporated into the changelog by deleting them
|
|
1054
|
+
* and removing any resulting empty directories both in the current repository and in any external repositories.
|
|
1055
|
+
*/
|
|
1220
1056
|
async function removeChangelogEntryFiles(entryPaths) {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
.flatMap(repo => repo.filePaths)
|
|
1224
|
-
.map(file => fs$1.unlink(file)));
|
|
1057
|
+
logInfo(`○ ${styleText("cyan", "Removing the changeset files...")}`);
|
|
1058
|
+
await Promise.all(entryPaths.flatMap((repo) => repo.filePaths).map((file) => fs.unlink(file)));
|
|
1225
1059
|
}
|
|
1226
|
-
|
|
1060
|
+
//#endregion
|
|
1061
|
+
//#region src/utils/movechangelogentryfiles.ts
|
|
1227
1062
|
/**
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1063
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1064
|
+
* For licensing, see LICENSE.md.
|
|
1065
|
+
*/
|
|
1231
1066
|
/**
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1067
|
+
* Moves changelog entry files to cycle-specific directories instead of deleting them.
|
|
1068
|
+
* This preserves the history of changes across prerelease cycles.
|
|
1069
|
+
* Returns an array of entry paths that were modified by the move operation
|
|
1070
|
+
*/
|
|
1236
1071
|
async function moveChangelogEntryFiles(entryPaths) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1072
|
+
const targetDir = PRE_RELEASE_DIRECTORY;
|
|
1073
|
+
const modifiedEntryPaths = [];
|
|
1074
|
+
logInfo(`○ ${styleText("cyan", `Moving changelog entries to ${targetDir}/ directory...`)}`);
|
|
1075
|
+
for (const repo of entryPaths) {
|
|
1076
|
+
const { cwd, filePaths } = repo;
|
|
1077
|
+
const changelogDir = upath.join(cwd, CHANGESET_DIRECTORY);
|
|
1078
|
+
const targetPath = upath.join(changelogDir, targetDir);
|
|
1079
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
1080
|
+
const modifiedFilePaths = [];
|
|
1081
|
+
for (const filePath of filePaths) {
|
|
1082
|
+
const fileName = upath.basename(filePath);
|
|
1083
|
+
const targetFilePath = upath.join(targetPath, fileName);
|
|
1084
|
+
await fs.rename(filePath, targetFilePath);
|
|
1085
|
+
modifiedFilePaths.push(targetFilePath);
|
|
1086
|
+
modifiedFilePaths.push(filePath);
|
|
1087
|
+
}
|
|
1088
|
+
modifiedEntryPaths.push({
|
|
1089
|
+
...repo,
|
|
1090
|
+
filePaths: modifiedFilePaths
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
return modifiedEntryPaths;
|
|
1094
|
+
}
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region src/utils/commitchanges.ts
|
|
1097
|
+
/**
|
|
1098
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1099
|
+
* For licensing, see LICENSE.md.
|
|
1100
|
+
*/
|
|
1265
1101
|
async function commitChanges(version, repositories) {
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1102
|
+
const message = `Changelog for v${version}. [skip ci]`;
|
|
1103
|
+
logInfo(`○ ${styleText("cyan", "Committing changes...")}`);
|
|
1104
|
+
for (const { cwd, isRoot, filePaths } of repositories) {
|
|
1105
|
+
const files = filePaths.slice(0);
|
|
1106
|
+
if (isRoot) files.unshift(upath.join(cwd, CHANGELOG_FILE));
|
|
1107
|
+
logInfo(`◌ Processing "${cwd}".`, { indent: 1 });
|
|
1108
|
+
await tools.commit({
|
|
1109
|
+
cwd,
|
|
1110
|
+
message,
|
|
1111
|
+
files
|
|
1112
|
+
}).catch((error) => {
|
|
1113
|
+
logInfo("An error occurred while committing changes.", { indent: 2 });
|
|
1114
|
+
logInfo(styleText("red", error.message), { indent: 2 });
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
//#endregion
|
|
1119
|
+
//#region src/utils/promptreleasetype.ts
|
|
1120
|
+
/**
|
|
1121
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1122
|
+
* For licensing, see LICENSE.md.
|
|
1123
|
+
*/
|
|
1124
|
+
/**
|
|
1125
|
+
* Prompts the user to choose between latest or prerelease
|
|
1126
|
+
*/
|
|
1290
1127
|
async function promptReleaseType(currentVersion) {
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
]);
|
|
1299
|
-
return releaseType;
|
|
1128
|
+
const { releaseType } = await inquirer.prompt([{
|
|
1129
|
+
type: "list",
|
|
1130
|
+
name: "releaseType",
|
|
1131
|
+
message: `Select the release type. Current version: ${styleText("cyan", currentVersion)}.`,
|
|
1132
|
+
choices: getChoices(currentVersion)
|
|
1133
|
+
}]);
|
|
1134
|
+
return releaseType;
|
|
1300
1135
|
}
|
|
1301
1136
|
function getChoices(currentVersion) {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1137
|
+
const currentVersionPrerelease = semver.prerelease(currentVersion);
|
|
1138
|
+
if (!currentVersionPrerelease) {
|
|
1139
|
+
const possibleStableVersions = [
|
|
1140
|
+
semver.inc(currentVersion, "major"),
|
|
1141
|
+
semver.inc(currentVersion, "minor"),
|
|
1142
|
+
semver.inc(currentVersion, "patch")
|
|
1143
|
+
].join(" | ");
|
|
1144
|
+
const possiblePrereleaseVersions = [
|
|
1145
|
+
semver.inc(currentVersion, "premajor", "alpha"),
|
|
1146
|
+
semver.inc(currentVersion, "preminor", "alpha"),
|
|
1147
|
+
semver.inc(currentVersion, "prepatch", "alpha")
|
|
1148
|
+
].join(" | ");
|
|
1149
|
+
return [{
|
|
1150
|
+
name: `Latest (stable) release (${possibleStableVersions})`,
|
|
1151
|
+
value: "latest"
|
|
1152
|
+
}, {
|
|
1153
|
+
name: `Pre-release (${possiblePrereleaseVersions})`,
|
|
1154
|
+
value: "prerelease"
|
|
1155
|
+
}];
|
|
1156
|
+
}
|
|
1157
|
+
const currentPreReleaseChannel = currentVersionPrerelease[0];
|
|
1158
|
+
const preReleaseChannels = [
|
|
1159
|
+
"alpha",
|
|
1160
|
+
"beta",
|
|
1161
|
+
"rc"
|
|
1162
|
+
];
|
|
1163
|
+
while (preReleaseChannels.includes(currentPreReleaseChannel)) preReleaseChannels.shift();
|
|
1164
|
+
const stableVersion = semver.inc(currentVersion, "release");
|
|
1165
|
+
const continuationVersion = semver.inc(currentVersion, "prerelease", currentPreReleaseChannel);
|
|
1166
|
+
const choices = [{
|
|
1167
|
+
name: `Latest (stable) release (${stableVersion})`,
|
|
1168
|
+
value: "latest"
|
|
1169
|
+
}, {
|
|
1170
|
+
name: `Pre-release continuation (${continuationVersion})`,
|
|
1171
|
+
value: "prerelease"
|
|
1172
|
+
}];
|
|
1173
|
+
if (preReleaseChannels.length) {
|
|
1174
|
+
const availablePromotions = preReleaseChannels.map((channel) => semver.inc(currentVersion, "prerelease", channel)).join(" | ");
|
|
1175
|
+
choices.push({
|
|
1176
|
+
name: `Pre-release promotion (${availablePromotions})`,
|
|
1177
|
+
value: "prerelease-promote"
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
return choices;
|
|
1181
|
+
}
|
|
1182
|
+
//#endregion
|
|
1183
|
+
//#region src/utils/getreleasetype.ts
|
|
1184
|
+
/**
|
|
1185
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1186
|
+
* For licensing, see LICENSE.md.
|
|
1187
|
+
*/
|
|
1343
1188
|
function getReleaseType(currentVersion, nextVersion) {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
*
|
|
1361
|
-
*
|
|
1362
|
-
*
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
* If `disableFilesystemOperations` is enabled, file operations (writing/committing) will be skipped,
|
|
1374
|
-
* and the assembled changelog object will be returned instead.
|
|
1375
|
-
*/
|
|
1189
|
+
const [currentChannel] = semver.prerelease(currentVersion) || ["latest"];
|
|
1190
|
+
const [nextChannel] = semver.prerelease(nextVersion) || ["latest"];
|
|
1191
|
+
if (nextChannel === "latest") return "latest";
|
|
1192
|
+
if (nextChannel === currentChannel) return "prerelease";
|
|
1193
|
+
return "prerelease-promote";
|
|
1194
|
+
}
|
|
1195
|
+
//#endregion
|
|
1196
|
+
//#region src/utils/generatechangelog.ts
|
|
1197
|
+
/**
|
|
1198
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1199
|
+
* For licensing, see LICENSE.md.
|
|
1200
|
+
*/
|
|
1201
|
+
/**
|
|
1202
|
+
* Orchestrates the full changelog generation workflow.
|
|
1203
|
+
*
|
|
1204
|
+
* This function:
|
|
1205
|
+
* * Reads the current package version and metadata.
|
|
1206
|
+
* * Locates all changelog entry files from the main and external repositories.
|
|
1207
|
+
* * Parses, validates, and groups entries by their section.
|
|
1208
|
+
* * Optionally displays the changes for manual inspection.
|
|
1209
|
+
* * Prompts for the next version if not provided via `options.nextVersion`.
|
|
1210
|
+
* * Computes the released package information based on version changes.
|
|
1211
|
+
* * Assembles a new changelog based on the visible entries.
|
|
1212
|
+
* * Optionally writes the new changelog to disk and removes the processed entry files.
|
|
1213
|
+
* * Commits the changes (changelog and removed files) to the Git repository.
|
|
1214
|
+
*
|
|
1215
|
+
* If `disableFilesystemOperations` is enabled, file operations (writing/committing) will be skipped,
|
|
1216
|
+
* and the assembled changelog object will be returned instead.
|
|
1217
|
+
*/
|
|
1376
1218
|
const main = async (options) => {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
if (releaseType === 'latest') {
|
|
1443
|
-
await removeChangelogEntryFiles(entryPaths);
|
|
1444
|
-
}
|
|
1445
|
-
else {
|
|
1446
|
-
pathsToCommit = await moveChangelogEntryFiles(entryPaths);
|
|
1447
|
-
}
|
|
1448
|
-
await modifyChangelog(newChangelog, cwd);
|
|
1449
|
-
await commitChanges(newVersion, pathsToCommit.map(({ cwd, isRoot, filePaths }) => ({ cwd, isRoot, filePaths })));
|
|
1450
|
-
logInfo('○ ' + styleText('green', 'Done!'));
|
|
1219
|
+
const { nextVersion, packagesDirectory, isSinglePackage, transformScope, npmPackageToCheck, linkFilter, shouldSkipLinks, cwd = process.cwd(), externalRepositories = [], date = format(/* @__PURE__ */ new Date(), "yyyy-MM-dd"), shouldIgnoreRootPackage = false, disableFilesystemOperations = false } = options;
|
|
1220
|
+
const { version: currentVersion, name: rootPackageName } = await workspaces.getPackageJson(cwd, { async: true });
|
|
1221
|
+
const packagesMetadata = await findPackages({
|
|
1222
|
+
cwd,
|
|
1223
|
+
packagesDirectory,
|
|
1224
|
+
shouldIgnoreRootPackage,
|
|
1225
|
+
externalRepositories
|
|
1226
|
+
});
|
|
1227
|
+
const releaseType = nextVersion ? getReleaseType(currentVersion, nextVersion) : await promptReleaseType(currentVersion);
|
|
1228
|
+
const entryPaths = await findChangelogEntryPaths({
|
|
1229
|
+
cwd,
|
|
1230
|
+
externalRepositories,
|
|
1231
|
+
linkFilter,
|
|
1232
|
+
shouldSkipLinks,
|
|
1233
|
+
includeSubdirectories: releaseType === "latest" || releaseType === "prerelease-promote"
|
|
1234
|
+
});
|
|
1235
|
+
const parsedChangesetFiles = await parseChangelogEntries(entryPaths, isSinglePackage);
|
|
1236
|
+
const sectionsWithEntries = groupEntriesBySection({
|
|
1237
|
+
packagesMetadata,
|
|
1238
|
+
transformScope,
|
|
1239
|
+
isSinglePackage,
|
|
1240
|
+
files: parsedChangesetFiles
|
|
1241
|
+
});
|
|
1242
|
+
if (!parsedChangesetFiles.length) {
|
|
1243
|
+
logInfo("");
|
|
1244
|
+
logInfo(styleText("bold", "ℹ️ No changelog entries found, so there is nothing to prepare a changelog from."));
|
|
1245
|
+
return disableFilesystemOperations ? "" : void 0;
|
|
1246
|
+
}
|
|
1247
|
+
if (!nextVersion) displayChanges({
|
|
1248
|
+
isSinglePackage,
|
|
1249
|
+
transformScope,
|
|
1250
|
+
sections: sectionsWithEntries
|
|
1251
|
+
});
|
|
1252
|
+
const newVersion = await determineNextVersion({
|
|
1253
|
+
currentVersion,
|
|
1254
|
+
nextVersion,
|
|
1255
|
+
releaseType,
|
|
1256
|
+
sections: sectionsWithEntries,
|
|
1257
|
+
packageName: shouldIgnoreRootPackage ? npmPackageToCheck : rootPackageName
|
|
1258
|
+
});
|
|
1259
|
+
const newChangelog = await composeChangelog({
|
|
1260
|
+
currentVersion,
|
|
1261
|
+
cwd,
|
|
1262
|
+
date,
|
|
1263
|
+
newVersion,
|
|
1264
|
+
isSinglePackage,
|
|
1265
|
+
releasedPackagesInfo: await composeReleaseSummary({
|
|
1266
|
+
currentVersion,
|
|
1267
|
+
newVersion,
|
|
1268
|
+
packagesMetadata,
|
|
1269
|
+
sections: sectionsWithEntries
|
|
1270
|
+
}),
|
|
1271
|
+
sections: filterVisibleSections(sectionsWithEntries)
|
|
1272
|
+
});
|
|
1273
|
+
if (disableFilesystemOperations) return newChangelog;
|
|
1274
|
+
let pathsToCommit = entryPaths;
|
|
1275
|
+
if (releaseType === "latest") await removeChangelogEntryFiles(entryPaths);
|
|
1276
|
+
else pathsToCommit = await moveChangelogEntryFiles(entryPaths);
|
|
1277
|
+
await modifyChangelog(newChangelog, cwd);
|
|
1278
|
+
await commitChanges(newVersion, pathsToCommit.map(({ cwd, isRoot, filePaths }) => ({
|
|
1279
|
+
cwd,
|
|
1280
|
+
isRoot,
|
|
1281
|
+
filePaths
|
|
1282
|
+
})));
|
|
1283
|
+
logInfo("○ " + styleText("green", "Done!"));
|
|
1451
1284
|
};
|
|
1452
1285
|
/**
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1286
|
+
* Entry point for generating a changelog with error handling.
|
|
1287
|
+
*
|
|
1288
|
+
* This wrapper ensures that:
|
|
1289
|
+
* * Interruptions from the user (e.g., Ctrl+C or intentional aborts) exit silently with code 0.
|
|
1290
|
+
* * Expected and unexpected internal errors are logged to the console and exit with code 1.
|
|
1291
|
+
* * Other unexpected errors are re-thrown for higher-level handling.
|
|
1292
|
+
*/
|
|
1460
1293
|
const generateChangelog = async (options) => {
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
else {
|
|
1470
|
-
console.error(styleText('red', 'Error: ' + error.message));
|
|
1471
|
-
process.exit(1);
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1294
|
+
return main(options).catch((error) => {
|
|
1295
|
+
if (isExpectedError(error)) process.exit(0);
|
|
1296
|
+
else if (!(error instanceof InternalError)) throw error;
|
|
1297
|
+
else {
|
|
1298
|
+
console.error(styleText("red", "Error: " + error.message));
|
|
1299
|
+
process.exit(1);
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1474
1302
|
};
|
|
1475
1303
|
function isError(error) {
|
|
1476
|
-
|
|
1304
|
+
return typeof error === "object" && error !== null && "message" in error;
|
|
1477
1305
|
}
|
|
1478
1306
|
function isExpectedError(error) {
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
if (error instanceof UserAbortError) {
|
|
1483
|
-
return true;
|
|
1484
|
-
}
|
|
1485
|
-
return false;
|
|
1307
|
+
if (isError(error) && error.message.includes("User force closed the prompt with SIGINT")) return true;
|
|
1308
|
+
if (error instanceof UserAbortError) return true;
|
|
1309
|
+
return false;
|
|
1486
1310
|
}
|
|
1487
|
-
|
|
1311
|
+
//#endregion
|
|
1312
|
+
//#region src/tasks/generatechangelogformonorepository.ts
|
|
1488
1313
|
/**
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1314
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1315
|
+
* For licensing, see LICENSE.md.
|
|
1316
|
+
*/
|
|
1492
1317
|
const generateChangelogForMonoRepository = async (options) => {
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1318
|
+
const { date, cwd, externalRepositories, nextVersion, disableFilesystemOperations, npmPackageToCheck, packagesDirectory, linkFilter, shouldSkipLinks, shouldIgnoreRootPackage, transformScope } = options;
|
|
1319
|
+
return generateChangelog({
|
|
1320
|
+
nextVersion,
|
|
1321
|
+
cwd,
|
|
1322
|
+
packagesDirectory,
|
|
1323
|
+
externalRepositories,
|
|
1324
|
+
transformScope,
|
|
1325
|
+
date,
|
|
1326
|
+
linkFilter,
|
|
1327
|
+
shouldSkipLinks,
|
|
1328
|
+
disableFilesystemOperations,
|
|
1329
|
+
...shouldIgnoreRootPackage && npmPackageToCheck ? {
|
|
1330
|
+
shouldIgnoreRootPackage: true,
|
|
1331
|
+
npmPackageToCheck
|
|
1332
|
+
} : { shouldIgnoreRootPackage: false },
|
|
1333
|
+
isSinglePackage: false
|
|
1334
|
+
});
|
|
1509
1335
|
};
|
|
1510
|
-
|
|
1336
|
+
//#endregion
|
|
1337
|
+
//#region src/tasks/generatechangelogforsinglerepository.ts
|
|
1511
1338
|
/**
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1339
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1340
|
+
* For licensing, see LICENSE.md.
|
|
1341
|
+
*/
|
|
1515
1342
|
const generateChangelogForSingleRepository = async (options) => {
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1343
|
+
const { cwd, date, externalRepositories, nextVersion, disableFilesystemOperations, linkFilter, shouldSkipLinks } = options;
|
|
1344
|
+
return generateChangelog({
|
|
1345
|
+
nextVersion,
|
|
1346
|
+
cwd,
|
|
1347
|
+
externalRepositories,
|
|
1348
|
+
date,
|
|
1349
|
+
linkFilter,
|
|
1350
|
+
shouldSkipLinks,
|
|
1351
|
+
disableFilesystemOperations,
|
|
1352
|
+
isSinglePackage: true,
|
|
1353
|
+
packagesDirectory: null
|
|
1354
|
+
});
|
|
1528
1355
|
};
|
|
1529
|
-
|
|
1356
|
+
//#endregion
|
|
1357
|
+
//#region src/index.ts
|
|
1358
|
+
/**
|
|
1359
|
+
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
1360
|
+
* For licensing, see LICENSE.md.
|
|
1361
|
+
*/
|
|
1362
|
+
//#endregion
|
|
1530
1363
|
export { generateChangelogForMonoRepository, generateChangelogForSingleRepository };
|