@ckeditor/ckeditor5-dev-changelog 55.6.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.
Files changed (38) hide show
  1. package/dist/index.d.ts +6 -6
  2. package/dist/index.js +1217 -1384
  3. package/dist/tasks/generatechangelogformonorepository.d.ts +3 -7
  4. package/dist/tasks/generatechangelogforsinglerepository.d.ts +2 -6
  5. package/dist/template.d.ts +5 -9
  6. package/dist/template.js +96 -106
  7. package/dist/types.d.ts +97 -99
  8. package/dist/utils/asyncarray.d.ts +31 -31
  9. package/dist/utils/commitchanges.d.ts +2 -6
  10. package/dist/utils/composechangelog.d.ts +16 -20
  11. package/dist/utils/composereleasesummary.d.ts +17 -17
  12. package/dist/utils/constants.d.ts +34 -38
  13. package/dist/utils/detectreleasechannel.d.ts +3 -7
  14. package/dist/utils/determinenextversion.d.ts +14 -18
  15. package/dist/utils/displaychanges.d.ts +13 -17
  16. package/dist/utils/filtervisiblesections.d.ts +7 -11
  17. package/dist/utils/findchangelogentrypaths.d.ts +8 -12
  18. package/dist/utils/findpackages.d.ts +7 -11
  19. package/dist/utils/generatechangelog.d.ts +9 -13
  20. package/dist/utils/getdateformatted.d.ts +2 -6
  21. package/dist/utils/getreleasetype.d.ts +4 -4
  22. package/dist/utils/groupentriesbysection.d.ts +7 -11
  23. package/dist/utils/internalerror.d.ts +6 -7
  24. package/dist/utils/linktogithubuser.d.ts +8 -8
  25. package/dist/utils/loginfo.d.ts +8 -8
  26. package/dist/utils/modifychangelog.d.ts +5 -9
  27. package/dist/utils/movechangelogentryfiles.d.ts +5 -9
  28. package/dist/utils/normalizeentry.d.ts +4 -4
  29. package/dist/utils/parsechangelogentries.d.ts +3 -7
  30. package/dist/utils/promptreleasetype.d.ts +3 -7
  31. package/dist/utils/providenewversion.d.ts +15 -19
  32. package/dist/utils/removechangelogentryfiles.d.ts +4 -8
  33. package/dist/utils/sortentriesbyscopeanddate.d.ts +9 -9
  34. package/dist/utils/truncatechangelog.d.ts +2 -6
  35. package/dist/utils/useraborterror.d.ts +6 -7
  36. package/dist/utils/validateentry.d.ts +13 -13
  37. package/dist/utils/validateinputversion.d.ts +6 -10
  38. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,1530 +1,1363 @@
1
- import { styleText } from 'node:util';
2
- import { parse, isValid, format } from 'date-fns';
3
- import { workspaces, npm, tools } from '@ckeditor/ckeditor5-dev-utils';
4
- import upath from 'upath';
5
- import fs$1 from 'node:fs/promises';
6
- import fs 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
-
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');
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
- 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
- }
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
- { name: 'Feature' },
51
- { name: 'Other' },
52
- { name: 'Fix' },
53
- { name: 'Major breaking change' },
54
- { name: 'Minor breaking change' },
55
- { name: 'Breaking change' }
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
60
- * For licensing, see LICENSE.md.
61
- */
62
- /**
63
- * This function enhances changelog entries by linking contributor usernames to their GitHub profiles.
64
- *
65
- * It searches for occurrences of GitHub-style mentions (e.g., @username) in the given comment string
66
- * and transforms them into Markdown links pointing to the corresponding GitHub user page.
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
- return comment.replace(/(^|[\s(])@([\w-]+)(?![/\w-])/ig, (_, charBefore, nickName) => {
70
- return `${charBefore}[@${nickName}](https://github.com/${nickName})`;
71
- });
72
- }
73
-
74
- /**
75
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
76
- * For licensing, see LICENSE.md.
77
- */
78
- /**
79
- * Validates a changelog entry against expected types, scopes, and issue references.
80
- *
81
- * It checks if the type is valid and consistent with single or multi-package modes,
82
- * verifies scopes against known package names, and ensures issue references are correctly formatted.
83
- *
84
- * Returns whether the entry is valid along with a validated version including any validation messages.
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
- const noScopePackagesNames = packagesNames.map(packageName => packageName.replace(/@.*\//, ''));
88
- const data = entry.data;
89
- const validations = [];
90
- let isValid = true;
91
- const allowedTypesArray = TYPES.map(({ name }) => name);
92
- const allowedTypesList = getAllowedTypesList();
93
- if (typeof data.type === 'undefined') {
94
- validations.push(`Provide a type with one of the values: ${allowedTypesList} (case insensitive).`);
95
- isValid = false;
96
- }
97
- else if (!allowedTypesArray.includes(data.type)) {
98
- validations.push(`Type is required and should be one of: ${allowedTypesList} (case insensitive).`);
99
- isValid = false;
100
- }
101
- if (singlePackage && ['Major breaking change', 'Minor breaking change'].includes(data.type)) {
102
- validations.push(`Breaking change "${data.type}" should be generic: "Breaking change", for a single package mode (case insensitive).`);
103
- isValid = false;
104
- }
105
- if (!singlePackage && data.type === 'Breaking change') {
106
- validations.push(`Breaking change "${data.type}" should be one of: "Minor breaking change", "Major breaking change" ` +
107
- 'for a monorepo (case insensitive).');
108
- isValid = false;
109
- }
110
- const scopeValidated = [];
111
- if (singlePackage) {
112
- // Skip scope validation for single package mode
113
- scopeValidated.push(...data.scope);
114
- }
115
- else {
116
- for (const scopeName of data.scope) {
117
- if (!noScopePackagesNames.includes(scopeName)) {
118
- validations.push(`Scope "${scopeName}" is not recognized as a valid package in the repository.`);
119
- }
120
- else {
121
- scopeValidated.push(scopeName);
122
- }
123
- }
124
- }
125
- data.scope = scopeValidated;
126
- const seeValidated = [];
127
- for (const see of data.see) {
128
- if (!(see.match(ISSUE_PATTERN) || see.match(ISSUE_SLUG_PATTERN) || see.match(ISSUE_URL_PATTERN))) {
129
- validations.push([
130
- `See "${see}" is not a valid issue reference. Provide either:`,
131
- 'issue number, repository-slug#id or full issue link URL.'
132
- ].join(' '));
133
- }
134
- else {
135
- seeValidated.push(see);
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
- const formatter = new Intl.ListFormat('en-US', { style: 'long', type: 'disjunction' });
174
- const items = TYPES.map(type => `"${type.name}"`);
175
- return formatter.format(items);
176
- }
177
-
178
- /**
179
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
180
- * For licensing, see LICENSE.md.
181
- */
182
- /**
183
- * This function categorizes changelog entries based on their types and packages.
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
- const { files, packagesMetadata, transformScope, isSinglePackage } = options;
187
- const packageNames = [...packagesMetadata.keys()];
188
- return files.reduce((sections, entry) => {
189
- const { validatedEntry, isValid } = validateEntry(entry, packageNames, isSinglePackage);
190
- const validatedData = validatedEntry.data;
191
- const closesLinks = filterLinks(validatedData.closes, validatedEntry);
192
- const closes = getIssuesLinks(closesLinks, 'Closes');
193
- const seeLinks = filterLinks(validatedData.see, validatedEntry);
194
- const see = getIssuesLinks(seeLinks, 'See');
195
- const scope = isSinglePackage ? null : getScopesLinks(validatedData.scope, transformScope);
196
- const section = getSection({ entry: validatedEntry, isSinglePackage, isValid });
197
- const contentWithCommunityCredits = getContentWithCommunityCredits(validatedEntry.content, validatedData.communityCredits);
198
- const content = linkToGitHubUser(contentWithCommunityCredits);
199
- const [mainContent, ...restContent] = formatContent(content);
200
- const changeMessage = getChangeMessage({ restContent, scope, mainContent, see, closes });
201
- const newEntry = {
202
- message: changeMessage,
203
- data: {
204
- mainContent,
205
- restContent,
206
- type: validatedData.type,
207
- scope: validatedData.scope,
208
- see: seeLinks,
209
- closes: closesLinks,
210
- validations: validatedData.validations,
211
- communityCredits: validatedData.communityCredits
212
- },
213
- changesetPath: validatedEntry.changesetPath
214
- };
215
- sections[section].entries.push(newEntry);
216
- if (isValid && newEntry.data.validations?.length) {
217
- sections.warning.entries.push(newEntry);
218
- }
219
- return sections;
220
- }, getInitialSectionsWithEntries());
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
- return links
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
- const messageFirstLine = [
229
- '*',
230
- scope ? `**${scope}**:` : null,
231
- mainContent,
232
- see.length ? see : null,
233
- closes.length ? closes : null
234
- ].filter(Boolean).join(' ');
235
- if (!restContent || !restContent.length) {
236
- return messageFirstLine;
237
- }
238
- return `${messageFirstLine}\n\n${restContent.map(line => {
239
- if (line.length) {
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
- const lines = content.trim()
247
- .split('\n')
248
- .map(line => line.trimEnd())
249
- .map(line => normalizeListMarker(line));
250
- const mainIndex = lines.findIndex(line => line.trim() !== '');
251
- const mainContent = lines.at(mainIndex);
252
- let restContent = lines.slice(mainIndex + 1);
253
- if (restContent.at(0)?.trim() === '') {
254
- restContent = restContent.slice(1);
255
- }
256
- const cleanedRestContent = restContent.reduce((acc, line) => {
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
- if (!communityCredits?.length) {
272
- return content;
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
- return scope
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
- if (issue.match(ISSUE_PATTERN)) {
284
- return { displayName: `#${issue}`, link: `${gitHubUrl}/issues/${issue}` };
285
- }
286
- const differentRepoMatch = issue.match(ISSUE_SLUG_PATTERN);
287
- if (differentRepoMatch) {
288
- const { owner, repository, number } = differentRepoMatch.groups;
289
- return { displayName: issue, link: `https://github.com/${owner}/${repository}/issues/${number}` };
290
- }
291
- const repoUrlMatch = issue.match(ISSUE_URL_PATTERN);
292
- if (repoUrlMatch) {
293
- const { owner, repository, number } = repoUrlMatch.groups;
294
- if (issue.startsWith(gitHubUrl)) {
295
- return { displayName: `#${number}`, link: issue };
296
- }
297
- return { displayName: `${owner}/${repository}#${number}`, link: issue };
298
- }
299
- return { displayName: '', link: '' };
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
- if (!issues.length) {
303
- return '';
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
- const { entry, isSinglePackage, isValid } = options;
310
- if (!isValid) {
311
- return 'invalid';
312
- }
313
- // If someone tries to use minor/major breaking change in a single package, we simply cast it to a generic breaking change.
314
- if (isSinglePackage) {
315
- const breakingChangeTypes = ['Minor breaking change', 'Major breaking change', 'Breaking change'];
316
- if (breakingChangeTypes.includes(entry.data.type)) {
317
- return 'breaking';
318
- }
319
- }
320
- else {
321
- if (entry.data.type === 'Minor breaking change') {
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
- const sections = structuredClone(SECTIONS);
332
- for (const key in sections) {
333
- sections[key].entries = [];
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
- const listMarkerRegexp = /^(\s*)[-+](\s*)/;
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
344
- * For licensing, see LICENSE.md.
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
- * This function provides a consistent logging format for the changelog generation process.
348
- *
349
- * It logs the given text to the console, optionally indenting it by a specified number of levels.
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
- console.log(' '.repeat(indent * 3) + text);
353
- }
354
-
355
- /**
356
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
357
- * For licensing, see LICENSE.md.
358
- */
359
- /**
360
- * Displays a formatted summary of all changelog entries grouped by sections (e.g., Features, Fixes, Breaking changes).
361
- *
362
- * This function:
363
- * * Lists all non-empty changelog sections with appropriate formatting.
364
- * * Differentiates between valid and invalid entries using visual indicators (`+` for valid, `x` for invalid).
365
- * * Supports both mono-repository and single package repositories through the `isSinglePackage` flag.
366
- * * Applies a transformation to scope names using the `transformScope` callback for a mono-repository setup.
367
- * * Outputs a helpful legend for interpreting the entry indicators.
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
- const { sections, isSinglePackage, transformScope } = options;
371
- let numberOfEntries = 0;
372
- logInfo(`○ ${styleText('cyan', 'Listing the changes...')}`);
373
- const nonEmptySections = Object.entries(sections)
374
- .filter(([, section]) => section.entries.length);
375
- for (const [sectionName, section] of nonEmptySections) {
376
- const color = getTitleColor(sectionName);
377
- logInfo('◌ ' + color(styleText('underline', `${section.titleInLogs || section.title}:`)), { indent: 1 });
378
- const displayCallback = sectionName === 'invalid' || sectionName === 'warning' ?
379
- displayWarningEntry :
380
- displayValidEntry;
381
- if (sectionName !== 'warning') {
382
- numberOfEntries += section.entries.length;
383
- }
384
- section.entries.forEach(entry => displayCallback(entry, sectionName, isSinglePackage, transformScope));
385
- logInfo('');
386
- }
387
- logInfo('◌ ' + styleText('underline', 'Legend:'), { indent: 1 });
388
- logInfo(`- Entries marked with ${styleText('green', '+')} symbol are included in the changelog.`, { indent: 2 });
389
- logInfo('- Entries marked with ' + styleText('yellow', 'x') + ' symbol include invalid references (see and/or closes) ' +
390
- 'or scope definitions. Please ensure that:', { indent: 2 });
391
- logInfo('* Reference entries match one of the following formats:', { indent: 3 });
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
- if (sectionName === 'warning') {
402
- return (text) => styleText('yellow', text);
403
- }
404
- if (sectionName === 'invalid') {
405
- return (text) => styleText('red', text);
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
- return sectionName === 'breaking' || sectionName === 'major' || sectionName === 'minor';
355
+ return sectionName === "breaking" || sectionName === "major" || sectionName === "minor";
416
356
  }
417
357
  function displayWarningEntry(entry) {
418
- logInfo(`» file://${entry.changesetPath}`, { indent: 2 });
419
- for (const validationMessage of (entry.data.validations || [])) {
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
- const isEntryFullyValid = !entry.data.validations?.length;
425
- const scopeFormatted = transformScope ?
426
- entry.data.scope.map(scope => transformScope(scope).displayName) :
427
- entry.data.scope;
428
- const scope = entry.data.scope.length ?
429
- styleText('grey', scopeFormatted?.join(', ')) :
430
- `${styleText(['italic', 'gray'], '(no scope)')}`;
431
- const validationIndicator = isEntryFullyValid ? styleText('green', '+') : styleText('yellow', 'x');
432
- const shouldTrimMessage = String(entry.data.mainContent).length > 100;
433
- const trimmedMessageContent = shouldTrimMessage ? entry.data.mainContent?.slice(0, 100) + '...' : entry.data.mainContent;
434
- if (isSinglePackage) {
435
- logInfo(`${validationIndicator} ${trimmedMessageContent}`, { indent: 2 });
436
- }
437
- else {
438
- logInfo(`${validationIndicator} ${scope}: ${trimmedMessageContent}`, { indent: 2 });
439
- }
440
- if (!isBreakingChangeSection(sectionName)) {
441
- return;
442
- }
443
- entry.data.see.forEach(({ link }) => {
444
- logInfo(`- See: ${link}`, { indent: 3 });
445
- });
446
- entry.data.closes.forEach(({ link }) => {
447
- logInfo(`- Closes: ${link}`, { indent: 3 });
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
- const changelog = getChangelog(cwd);
460
- if (!changelog) {
461
- return;
462
- }
463
- const entryHeader = '## [\\s\\S]+?';
464
- const entryHeaderRegexp = new RegExp(`\\n(${entryHeader})(?=\\n${entryHeader}|$)`, 'g');
465
- const entries = [...changelog.matchAll(entryHeaderRegexp)]
466
- .filter(match => match && match[1])
467
- .map(match => match[1]);
468
- if (!entries.length) {
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
- const changelogFile = upath.join(cwd, CHANGELOG_FILE);
481
- if (!fs.existsSync(changelogFile)) {
482
- return null;
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
- const changelogFile = upath.join(cwd, CHANGELOG_FILE);
488
- fs.writeFileSync(changelogFile, content, 'utf-8');
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
493
- * For licensing, see LICENSE.md.
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
- * This function writes the generated changelog content to the repository's changelog file.
497
- *
498
- * It reads the existing changelog (if any), inserts the new changelog content after the defined header,
499
- * writes the updated content back to the changelog file, and truncates the changelog to keep a manageable length.
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
- const changelogPath = upath.join(cwd, CHANGELOG_FILE);
503
- const existingChangelog = await readExistingChangelog(changelogPath);
504
- const updatedChangelog = prepareChangelogContent(existingChangelog, newChangelog);
505
- logInfo(`○ ${styleText('cyan', 'Appending changes to the existing changelog...')}`);
506
- await fs$1.writeFile(changelogPath, updatedChangelog, 'utf-8');
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
- * Reads the existing changelog file or returns an empty string if the file doesn't exist.
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
- try {
514
- return await fs$1.readFile(changelogPath, 'utf-8');
515
- }
516
- catch {
517
- logInfo(`○ ${styleText('yellow', 'CHANGELOG.md not found.')}`);
518
- return '';
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
- * Prepares the new changelog content by inserting it after the header or at the beginning if header is missing.
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
- const headerIndex = existingChangelog.indexOf(CHANGELOG_HEADER);
526
- if (headerIndex === -1) {
527
- return `${CHANGELOG_HEADER}\n\n${newChangelog}${existingChangelog}`;
528
- }
529
- const insertPosition = headerIndex + CHANGELOG_HEADER.length;
530
- return existingChangelog.slice(0, insertPosition) + '\n\n' + newChangelog + existingChangelog.slice(insertPosition);
531
- }
532
-
533
- /**
534
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
535
- * For licensing, see LICENSE.md.
536
- */
537
- /**
538
- * Custom error class for handling a case when a user aborts the process (at any stage).
539
- */
540
- class UserAbortError extends Error {
541
- }
542
-
543
- /**
544
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
545
- * For licensing, see LICENSE.md.
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
- const { newVersion, version, releaseType, packageName, suggestedVersion } = options;
549
- const [newChannel] = semver.prerelease(newVersion) || ['latest'];
550
- const [currentChannel] = semver.prerelease(version) || ['latest'];
551
- // Generic semantic‑version checks.
552
- if (!semver.valid(newVersion)) {
553
- return 'Please provide a valid version.';
554
- }
555
- if (!semver.gt(newVersion, version)) {
556
- return `Provided version must be higher than "${version}".`;
557
- }
558
- if (!(await npm.checkVersionAvailability(newVersion, packageName))) {
559
- return 'Given version is already taken.';
560
- }
561
- // Rules that depend on release type.
562
- const isPrerelease = releaseType === 'prerelease';
563
- const isPrereleasePromote = releaseType === 'prerelease-promote';
564
- const isLatest = releaseType === 'latest';
565
- // Pre‑release types must always include a channel suffix.
566
- if ((isPrerelease || isPrereleasePromote) && newChannel === 'latest') {
567
- return 'You chose the "pre-release" release type. Please provide a version with a channel suffix.';
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
- * Prompts the user to provide a new version for a package.
593
- *
594
- * Validates the input (version format, version higher than current, availability).
595
- *
596
- * Optionally shows warnings for invalid changes and allows user to abort.
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
- if (options.displayValidationWarning) {
600
- // Display warning about invalid changes
601
- displayInvalidChangesWarning();
602
- // Ask for confirmation to continue
603
- const shouldContinue = await askContinueConfirmation(options.indentLevel);
604
- if (!shouldContinue) {
605
- throw new UserAbortError('Aborted while detecting invalid changes.');
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
- * Displays a warning message about invalid changes in a visible color.
614
- */
502
+ * Displays a warning message about invalid changes in a visible color.
503
+ */
615
504
  function displayInvalidChangesWarning() {
616
- logInfo('');
617
- logInfo(styleText(['yellow', 'bold'], `⚠️ ${styleText('underline', 'WARNING: Invalid changes detected!')}`));
618
- logInfo('');
619
- logInfo(styleText('yellow', 'You can cancel the process, fix the invalid files, and run the tool again.'));
620
- logInfo(styleText('yellow', 'Alternatively, you can continue - but invalid values will be lost.'));
621
- logInfo('');
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
- * Asks the user if they want to continue with the version bump process.
625
- */
513
+ * Asks the user if they want to continue with the version bump process.
514
+ */
626
515
  async function askContinueConfirmation(indentLevel = 0) {
627
- const question = {
628
- type: 'confirm',
629
- name: 'continue',
630
- message: 'Should continue?',
631
- default: false,
632
- prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + styleText('cyan', '?')
633
- };
634
- const answers = await inquirer.prompt(question);
635
- return answers.continue;
636
- }
637
- /**
638
- * Creates a prompt question for version input with validation.
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
- const { version, packageName, bumpType, releaseChannel, releaseType, indentLevel = 0 } = options;
642
- const suggestedVersion = getSuggestedVersion(bumpType, version, releaseChannel) || version;
643
- const message = [
644
- 'Select the new version.',
645
- `Current version: ${styleText('cyan', version)}.`,
646
- `Suggested version: ${styleText('cyan', suggestedVersion)}.`
647
- ].join(' ');
648
- return [{
649
- type: 'list',
650
- name: 'version',
651
- default: suggestedVersion,
652
- message,
653
- prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + styleText('cyan', '?'),
654
- choices: getChoices$1({ version, bumpType, releaseChannel, releaseType })
655
- }, {
656
- type: 'input',
657
- name: 'customVersion',
658
- message: 'Enter your custom version:',
659
- when: ({ version }) => version === 'custom',
660
- filter: (newVersion) => newVersion.trim(),
661
- validate: (newVersion) => validateInputVersion({ newVersion, version, releaseType, packageName, suggestedVersion }),
662
- prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + styleText('cyan', '?')
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
- if (bumpType === 'prerelease' && releaseChannel !== 'latest') {
667
- return semver.inc(version, bumpType, releaseChannel);
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
- const proposedVersions = [];
680
- const preReleaseChannels = ['alpha', 'beta', 'rc'];
681
- const validPromotionChannels = preReleaseChannels.filter((value, index, array) => index >= array.indexOf(releaseChannel));
682
- // 6.0.0 => Latest (stable) release (7.0.0 | 6.1.0 | 6.0.1)
683
- if (bumpType !== 'prerelease' && releaseType === 'latest' && releaseChannel === 'latest') {
684
- proposedVersions.push(semver.inc(version, 'major'), semver.inc(version, 'minor'), semver.inc(version, 'patch'));
685
- }
686
- // 6.0.0 => Pre-release (7.0.0-alpha.0 | 6.1.0-alpha.0 | 6.0.1-alpha.0)
687
- if (bumpType === 'prerelease' && releaseType === 'prerelease' && releaseChannel === 'latest') {
688
- proposedVersions.push(semver.inc(version, 'premajor', 'alpha'), semver.inc(version, 'preminor', 'alpha'), semver.inc(version, 'prepatch', 'alpha'));
689
- }
690
- // 6.0.0-alpha.0 => Latest (stable) release (6.0.0)
691
- if (bumpType !== 'prerelease' && releaseType === 'latest' && preReleaseChannels.includes(releaseChannel)) {
692
- proposedVersions.push(semver.inc(version, 'release'));
693
- }
694
- // 6.0.0-alpha.0 => Pre-release continuation (6.0.0-alpha.1)
695
- if (bumpType === 'prerelease' && releaseType === 'prerelease' && preReleaseChannels.includes(releaseChannel)) {
696
- proposedVersions.push(semver.inc(version, 'prerelease', releaseChannel));
697
- }
698
- // 6.0.0-alpha.0 => Pre-release promotion (6.0.0-beta.0 | 6.0.0-rc.0)
699
- if (bumpType === 'prerelease' && releaseType === 'prerelease-promote' && preReleaseChannels.includes(releaseChannel)) {
700
- proposedVersions.push(...validPromotionChannels.map(channel => semver.inc(version, 'prerelease', channel)));
701
- }
702
- return [
703
- ...proposedVersions.map(proposedVersion => ({ name: proposedVersion, value: proposedVersion })),
704
- { name: 'Custom...', value: 'custom' }
705
- ];
706
- }
707
-
708
- /**
709
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
710
- * For licensing, see LICENSE.md.
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
- const prerelease = semver.prerelease(version);
717
- if (!prerelease) {
718
- return 'latest';
719
- }
720
- const currentChannel = prerelease[0];
721
- if (promotePrerelease) {
722
- if (currentChannel === 'alpha') {
723
- return 'beta';
724
- }
725
- if (currentChannel === 'beta') {
726
- return 'rc';
727
- }
728
- logInfo(styleText('yellow', `Warning! Unknown release channel to promote from ${currentChannel}.`));
729
- return 'alpha';
730
- }
731
- return currentChannel;
732
- }
733
-
734
- /**
735
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
736
- * For licensing, see LICENSE.md.
737
- */
738
- /**
739
- * Custom error class for handling validation errors in the changelog generation process.
740
- */
741
- class InternalError extends Error {
742
- }
743
-
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
- * Determines the next version for a single package or a mono-repository setup based on
750
- * the change sections, user input, and semantic versioning rules.
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
- const { sections, currentVersion, packageName, nextVersion, releaseType } = options;
759
- if (nextVersion) {
760
- logInfo(`○ ${styleText('cyan', `Determined the next version to be ${nextVersion}.`)}`);
761
- const isNightlyVersion = nextVersion.startsWith('0.0.0-');
762
- if (isNightlyVersion) {
763
- return nextVersion;
764
- }
765
- const validationResult = await validateInputVersion({
766
- newVersion: nextVersion,
767
- suggestedVersion: nextVersion,
768
- version: currentVersion,
769
- releaseType,
770
- packageName
771
- });
772
- if (typeof validationResult === 'string') {
773
- throw new InternalError(validationResult);
774
- }
775
- return nextVersion;
776
- }
777
- logInfo(`○ ${styleText('cyan', 'Determining the new version...')}`);
778
- let bumpType = 'patch';
779
- if (releaseType === 'prerelease' || releaseType === 'prerelease-promote') {
780
- bumpType = 'prerelease';
781
- }
782
- else if (sections.major.entries.length || sections.breaking.entries.length) {
783
- bumpType = 'major';
784
- }
785
- else if (sections.minor.entries.length || sections.feature.entries.length) {
786
- bumpType = 'minor';
787
- }
788
- const areErrorsPresent = !!sections.invalid.entries.length;
789
- const areWarningsPresent = Object.values(sections).some(section => section.entries.some(entry => entry.data.validations && entry.data.validations.length > 0));
790
- const userProvidedVersion = await provideNewVersion({
791
- packageName,
792
- bumpType,
793
- releaseType,
794
- version: currentVersion,
795
- releaseChannel: detectReleaseChannel(currentVersion, releaseType === 'prerelease-promote'),
796
- displayValidationWarning: areErrorsPresent || areWarningsPresent
797
- });
798
- return userProvidedVersion;
799
- }
800
-
801
- /**
802
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
803
- * For licensing, see LICENSE.md.
804
- */
805
- /**
806
- * A utility class that wraps a Promise of an array and provides async array-like operations.
807
- */
808
- class AsyncArray {
809
- promise;
810
- /**
811
- * Creates a new `AsyncArray` instance.
812
- */
813
- constructor(promise) {
814
- this.promise = promise;
815
- }
816
- /**
817
- * Creates an `AsyncArray` from a given promise.
818
- */
819
- static from(promise) {
820
- return new AsyncArray(promise);
821
- }
822
- /**
823
- * Asynchronously maps each item in the array using the provided callback.
824
- */
825
- map(fn) {
826
- const newPromise = this.promise.then(arr => Promise.all(arr.map(fn)));
827
- return new AsyncArray(newPromise);
828
- }
829
- /**
830
- * Flattens one level of nesting in an array of arrays.
831
- */
832
- flat() {
833
- const newPromise = this.promise.then(arr => arr.flat());
834
- return new AsyncArray(newPromise);
835
- }
836
- /**
837
- * Maps each item using a callback that returns an array (or promise of an array),
838
- * then flattens the result by one level.
839
- */
840
- flatMap(fn) {
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
857
- * For licensing, see LICENSE.md.
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
- * Retrieves the names and versions of packages found in both the main repository and any external repositories.
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
- const { cwd, packagesDirectory, externalRepositories, shouldIgnoreRootPackage = false } = options;
864
- const externalPackagesPromises = externalRepositories.map(externalRepository => {
865
- return workspaces.findPathsToPackages(externalRepository.cwd, externalRepository.packagesDirectory, { includePackageJson: true });
866
- });
867
- const promise = Promise.all([
868
- workspaces.findPathsToPackages(cwd, packagesDirectory, { includeCwd: !shouldIgnoreRootPackage, includePackageJson: true }),
869
- ...externalPackagesPromises
870
- ]);
871
- return AsyncArray.from(promise)
872
- .flat()
873
- .map(packagePath => fs.readFileSync(packagePath, 'utf-8'))
874
- .map(packageJson => JSON.parse(packageJson))
875
- .map(({ name, version }) => [name, version])
876
- .then(entries => new Map(entries.sort(([a], [b]) => a.localeCompare(b))));
877
- }
878
-
879
- /**
880
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
881
- * For licensing, see LICENSE.md.
882
- */
883
- /**
884
- * Generates a categorized summary of packages released in the new version,
885
- * including new packages, major, minor, feature, and other releases.
886
- *
887
- * This function analyzes changelog sections and package metadata to:
888
- * * Identify new packages introduced with version '0.0.1'.
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
- const { sections, currentVersion, newVersion, packagesMetadata } = options;
895
- const versionUpgradeText = `v${currentVersion} => v${newVersion}`;
896
- const packageNames = [...packagesMetadata.keys()];
897
- const newVersionReleases = getNewVersionReleases(packagesMetadata);
898
- const majorReleases = getScopeWithOrgNamespace(sections.major.entries, { packagesToRemove: newVersionReleases, packageNames });
899
- const minorReleases = getScopeWithOrgNamespace(sections.minor.entries, {
900
- packagesToRemove: [...majorReleases, ...newVersionReleases],
901
- packageNames
902
- });
903
- const newFeaturesReleases = getScopeWithOrgNamespace(sections.feature.entries, {
904
- packagesToRemove: [...minorReleases, ...majorReleases, ...newVersionReleases],
905
- packageNames
906
- });
907
- const packagesToRemoveFromOtherReleases = [majorReleases, minorReleases, newFeaturesReleases, newVersionReleases].flat();
908
- const otherReleases = packageNames
909
- .filter(packageName => !packagesToRemoveFromOtherReleases.includes(packageName))
910
- .sort();
911
- return [
912
- { title: 'New packages:', version: `v${newVersion}`, packages: newVersionReleases },
913
- { title: 'Major releases (contain major breaking changes):', version: versionUpgradeText, packages: majorReleases },
914
- { title: 'Minor releases (contain minor breaking changes):', version: versionUpgradeText, packages: minorReleases },
915
- { title: 'Releases containing new features:', version: versionUpgradeText, packages: newFeaturesReleases },
916
- { title: 'Other releases:', version: versionUpgradeText, packages: otherReleases }
917
- ].filter(release => release.packages?.length > 0);
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
- return [...packages]
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
- const uniqueScopes = entries
927
- .flatMap(entry => entry.data.scope)
928
- .filter(Boolean)
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
- if (value.includes('/')) {
937
- return value.split('/').at(1);
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
944
- * For licensing, see LICENSE.md.
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
- * Gathers changelog entry file paths (Markdown files) from the main repository and any configured external repositories.
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
- const { cwd, externalRepositories, includeSubdirectories = true } = options;
951
- const globPattern = includeSubdirectories ? '**/*.md' : '*.md';
952
- return AsyncArray
953
- .from(Promise.resolve(externalRepositories))
954
- .map(async (repo) => {
955
- const changesetGlob = await glob(globPattern, {
956
- cwd: upath.join(repo.cwd, CHANGESET_DIRECTORY),
957
- absolute: true
958
- });
959
- return {
960
- filePaths: changesetGlob.map(p => upath.normalize(p)),
961
- gitHubUrl: await workspaces.getRepositoryUrl(repo.cwd, { async: true }),
962
- linkFilter: getLinkFilter(repo),
963
- cwd: repo.cwd,
964
- isRoot: false
965
- };
966
- })
967
- .then(async (externalResults) => {
968
- const mainChangesetGlob = await glob(globPattern, {
969
- cwd: upath.join(cwd, CHANGESET_DIRECTORY),
970
- absolute: true
971
- });
972
- const mainEntry = {
973
- filePaths: mainChangesetGlob.map(p => upath.normalize(p)),
974
- gitHubUrl: await workspaces.getRepositoryUrl(cwd, { async: true }),
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
- if (typeof options.shouldSkipLinks === 'boolean') {
984
- return () => !options.shouldSkipLinks;
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
991
- * For licensing, see LICENSE.md.
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
- * Sorts parsed files according to the rules:
995
- * 1. Entries with more scopes at the top.
996
- * 2. Entries with single scope grouped by scope and sorted by date within a group.
997
- * 3. Entries with no scope at the bottom.
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
- return entries.sort((itemBefore, itemAfter) => {
1001
- const beforeScopeCount = itemBefore.data.scope.length;
1002
- const afterScopeCount = itemAfter.data.scope.length;
1003
- // Both have single scope - group by scope name first.
1004
- if (beforeScopeCount === 1 && afterScopeCount === 1) {
1005
- const firstScope = itemBefore.data.scope.at(0);
1006
- const secondScope = itemAfter.data.scope.at(0);
1007
- if (firstScope !== secondScope) {
1008
- return firstScope.localeCompare(secondScope); // Alphabetical scope order.
1009
- }
1010
- // Same scope - sort by date (older first).
1011
- return itemBefore.createdAt.getTime() - itemAfter.createdAt.getTime();
1012
- }
1013
- // Both have the same number of scopes - sort by date (older first).
1014
- if (beforeScopeCount === afterScopeCount) {
1015
- return itemBefore.createdAt.getTime() - itemAfter.createdAt.getTime();
1016
- }
1017
- // Sort by number of scopes.
1018
- return afterScopeCount - beforeScopeCount;
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
- // Normalize type.
1028
- const typeNormalized = getTypeNormalized(entry.data.type, isSinglePackage);
1029
- // Normalize scope.
1030
- const scopeNormalized = toArray(entry.data.scope)
1031
- .filter(scope => scope)
1032
- .map(scopeEntry => String(scopeEntry).toLowerCase())
1033
- .sort();
1034
- // Normalize closes.
1035
- const closesNormalized = toArray(entry.data.closes)
1036
- .filter(closes => closes)
1037
- .map(closes => String(closes));
1038
- // Normalize see.
1039
- const seeNormalized = toArray(entry.data.see)
1040
- .filter(see => see).map(see => String(see));
1041
- // Normalize community credits.
1042
- const communityCreditsNormalized = toArray(entry.data.communityCredits)
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
- const typeCapitalized = capitalize(type);
1059
- const expectedBreakingChangeTypes = ['Major breaking change', 'Minor breaking change'];
1060
- if (isSinglePackage && expectedBreakingChangeTypes.includes(typeCapitalized)) {
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
- const valueStr = String(value);
1067
- return valueStr.charAt(0).toUpperCase() + valueStr.slice(1).toLowerCase();
922
+ const valueStr = String(value);
923
+ return valueStr.charAt(0).toUpperCase() + valueStr.slice(1).toLowerCase();
1068
924
  }
1069
925
  function ensureAt(str) {
1070
- return str.startsWith('@') ? str : '@' + str;
926
+ return str.startsWith("@") ? str : "@" + str;
1071
927
  }
1072
928
  function deduplicate(packageNames) {
1073
- return [...new Set(packageNames)];
929
+ return [...new Set(packageNames)];
1074
930
  }
1075
931
  function toArray(input) {
1076
- if (!input) {
1077
- return [];
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1084
- * For licensing, see LICENSE.md.
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
- * Reads and processes input files to extract changelog entries.
1088
- */
942
+ * Reads and processes input files to extract changelog entries.
943
+ */
1089
944
  function parseChangelogEntries(entryPaths, isSinglePackage) {
1090
- const fileEntries = entryPaths.reduce((acc, { filePaths, gitHubUrl, linkFilter }) => {
1091
- for (const changesetPath of filePaths) {
1092
- acc.push({ changesetPath, gitHubUrl, linkFilter });
1093
- }
1094
- return acc;
1095
- }, []);
1096
- return AsyncArray
1097
- .from(Promise.resolve(fileEntries))
1098
- .map(async ({ changesetPath, gitHubUrl, linkFilter }) => ({
1099
- ...matter(await fs$1.readFile(changesetPath, 'utf-8')),
1100
- gitHubUrl,
1101
- changesetPath,
1102
- linkFilter,
1103
- createdAt: extractDateFromFilename(changesetPath)
1104
- }))
1105
- .map(entry => normalizeEntry(entry, isSinglePackage))
1106
- .then(entries => sortEntriesByScopeAndDate(entries));
1107
- }
1108
- /**
1109
- * Extracts date from an entry filename (`YYYYMMDDHHMMSS_*.md`).
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
- const now = new Date();
1115
- const filename = changesetPath.split('/').pop() || '';
1116
- const dateMatch = filename.match(/^(\d{14})_/);
1117
- if (!dateMatch || !dateMatch[1]) {
1118
- // Fallback to the current date if no date pattern found.
1119
- return now;
1120
- }
1121
- const parsedDate = parse(dateMatch[1], 'yyyyMMddHHmmss', now);
1122
- // Validate the parsed date and fallback to the current date when failed.
1123
- if (!isValid(parsedDate)) {
1124
- return now;
1125
- }
1126
- return parsedDate;
1127
- }
1128
-
1129
- /**
1130
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1131
- * For licensing, see LICENSE.md.
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
- const sectionsToDisplay = Object.entries(sectionsWithEntries)
1142
- .filter(([, { entries, excludeInChangelog }]) => {
1143
- return entries?.length && !excludeInChangelog;
1144
- })
1145
- .map(([, section]) => section);
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1160
- * For licensing, see LICENSE.md.
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
- * Formats a date string `YYYY-MM-DD` into a human-readable format for the changelog.
1164
- */
1001
+ * Formats a date string `YYYY-MM-DD` into a human-readable format for the changelog.
1002
+ */
1165
1003
  function getDateFormatted(date) {
1166
- return format(parse(date, 'yyyy-MM-dd', new Date()), 'LLLL d, yyyy');
1167
- }
1168
-
1169
- /**
1170
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1171
- * For licensing, see LICENSE.md.
1172
- */
1173
- /**
1174
- * Generates a formatted changelog string for a new version release.
1175
- *
1176
- * This function constructs the changelog content including
1177
- * * A version header with a link to the GitHub comparison view (except for an initial version).
1178
- * * Sections with grouped changelog entries and their messages.
1179
- * * A collapsible summary of released packages and their version bumps for a mono-repository setup.
1180
- * * Special handling for single-package repositories.
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
- const { cwd, date, currentVersion, newVersion, sections, releasedPackagesInfo, isSinglePackage } = options;
1184
- const gitHubUrl = await workspaces.getRepositoryUrl(cwd, { async: true });
1185
- const dateFormatted = getDateFormatted(date);
1186
- const header = currentVersion === '0.0.1' ?
1187
- `## ${newVersion} (${dateFormatted})` :
1188
- `## [${newVersion}](${gitHubUrl}/compare/v${currentVersion}...v${newVersion}) (${dateFormatted})`;
1189
- const sectionsAsString = sections.map(({ title, entries }) => ([
1190
- `### ${title}`,
1191
- '',
1192
- ...entries.map(entry => entry.message),
1193
- ''
1194
- ])).flat().join('\n');
1195
- const packagesVersionBumps = releasedPackagesInfo.map(({ title, version, packages }) => ([
1196
- '',
1197
- title,
1198
- '',
1199
- ...packages.map(packageName => `* [${packageName}](${NPM_URL}/${packageName}/v/${newVersion}): ${version}`)
1200
- ])).flat().join('\n');
1201
- const changelog = [
1202
- header,
1203
- '',
1204
- sectionsAsString
1205
- ];
1206
- if (!isSinglePackage) {
1207
- changelog.push('### Released packages', '', `Check out the [Versioning policy](${VERSIONING_POLICY_URL}) guide for more information.`, '', '<details>', '<summary>Released packages (summary)</summary>', packagesVersionBumps, '</details>', '');
1208
- }
1209
- return changelog.join('\n');
1210
- }
1211
-
1212
- /**
1213
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1214
- * For licensing, see LICENSE.md.
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
- logInfo(`○ ${styleText('cyan', 'Removing the changeset files...')}`);
1222
- await Promise.all(entryPaths
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1229
- * For licensing, see LICENSE.md.
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
- * Moves changelog entry files to cycle-specific directories instead of deleting them.
1233
- * This preserves the history of changes across prerelease cycles.
1234
- * Returns an array of entry paths that were modified by the move operation
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
- const targetDir = PRE_RELEASE_DIRECTORY;
1238
- const modifiedEntryPaths = [];
1239
- logInfo(`○ ${styleText('cyan', `Moving changelog entries to ${targetDir}/ directory...`)}`);
1240
- for (const repo of entryPaths) {
1241
- const { cwd, filePaths } = repo;
1242
- const changelogDir = upath.join(cwd, CHANGESET_DIRECTORY);
1243
- const targetPath = upath.join(changelogDir, targetDir);
1244
- await fs$1.mkdir(targetPath, { recursive: true });
1245
- const modifiedFilePaths = [];
1246
- for (const filePath of filePaths) {
1247
- const fileName = upath.basename(filePath);
1248
- const targetFilePath = upath.join(targetPath, fileName);
1249
- await fs$1.rename(filePath, targetFilePath);
1250
- modifiedFilePaths.push(targetFilePath);
1251
- modifiedFilePaths.push(filePath);
1252
- }
1253
- modifiedEntryPaths.push({
1254
- ...repo,
1255
- filePaths: modifiedFilePaths
1256
- });
1257
- }
1258
- return modifiedEntryPaths;
1259
- }
1260
-
1261
- /**
1262
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1263
- * For licensing, see LICENSE.md.
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
- const message = `Changelog for v${version}. [skip ci]`;
1267
- logInfo(`○ ${styleText('cyan', 'Committing changes...')}`);
1268
- for (const { cwd, isRoot, filePaths } of repositories) {
1269
- // Copy to avoid modifying the original array.
1270
- const files = filePaths.slice(0);
1271
- if (isRoot) {
1272
- files.unshift(upath.join(cwd, CHANGELOG_FILE));
1273
- }
1274
- logInfo(`◌ Processing "${cwd}".`, { indent: 1 });
1275
- await tools.commit({ cwd, message, files })
1276
- .catch(error => {
1277
- logInfo('An error occurred while committing changes.', { indent: 2 });
1278
- logInfo(styleText('red', error.message), { indent: 2 });
1279
- });
1280
- }
1281
- }
1282
-
1283
- /**
1284
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1285
- * For licensing, see LICENSE.md.
1286
- */
1287
- /**
1288
- * Prompts the user to choose between latest or prerelease
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
- const { releaseType } = await inquirer.prompt([
1292
- {
1293
- type: 'list',
1294
- name: 'releaseType',
1295
- message: `Select the release type. Current version: ${styleText('cyan', currentVersion)}.`,
1296
- choices: getChoices(currentVersion)
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
- const currentVersionPrerelease = semver.prerelease(currentVersion);
1303
- if (!currentVersionPrerelease) {
1304
- const possibleStableVersions = [
1305
- semver.inc(currentVersion, 'major'),
1306
- semver.inc(currentVersion, 'minor'),
1307
- semver.inc(currentVersion, 'patch')
1308
- ].join(' | ');
1309
- const possiblePrereleaseVersions = [
1310
- semver.inc(currentVersion, 'premajor', 'alpha'),
1311
- semver.inc(currentVersion, 'preminor', 'alpha'),
1312
- semver.inc(currentVersion, 'prepatch', 'alpha')
1313
- ].join(' | ');
1314
- return [
1315
- { name: `Latest (stable) release (${possibleStableVersions})`, value: 'latest' },
1316
- { name: `Pre-release (${possiblePrereleaseVersions})`, value: 'prerelease' }
1317
- ];
1318
- }
1319
- const currentPreReleaseChannel = currentVersionPrerelease[0];
1320
- const preReleaseChannels = ['alpha', 'beta', 'rc'];
1321
- while (preReleaseChannels.includes(currentPreReleaseChannel)) {
1322
- preReleaseChannels.shift();
1323
- }
1324
- const stableVersion = semver.inc(currentVersion, 'release');
1325
- const continuationVersion = semver.inc(currentVersion, 'prerelease', currentPreReleaseChannel);
1326
- const choices = [
1327
- { name: `Latest (stable) release (${stableVersion})`, value: 'latest' },
1328
- { name: `Pre-release continuation (${continuationVersion})`, value: 'prerelease' }
1329
- ];
1330
- if (preReleaseChannels.length) {
1331
- const availablePromotions = preReleaseChannels
1332
- .map(channel => semver.inc(currentVersion, 'prerelease', channel))
1333
- .join(' | ');
1334
- choices.push({ name: `Pre-release promotion (${availablePromotions})`, value: 'prerelease-promote' });
1335
- }
1336
- return choices;
1337
- }
1338
-
1339
- /**
1340
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1341
- * For licensing, see LICENSE.md.
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
- const [currentChannel] = semver.prerelease(currentVersion) || ['latest'];
1345
- const [nextChannel] = semver.prerelease(nextVersion) || ['latest'];
1346
- if (nextChannel === 'latest') {
1347
- return 'latest';
1348
- }
1349
- if (nextChannel === currentChannel) {
1350
- return 'prerelease';
1351
- }
1352
- return 'prerelease-promote';
1353
- }
1354
-
1355
- /**
1356
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1357
- * For licensing, see LICENSE.md.
1358
- */
1359
- /**
1360
- * Orchestrates the full changelog generation workflow.
1361
- *
1362
- * This function:
1363
- * * Reads the current package version and metadata.
1364
- * * Locates all changelog entry files from the main and external repositories.
1365
- * * Parses, validates, and groups entries by their section.
1366
- * * Optionally displays the changes for manual inspection.
1367
- * * Prompts for the next version if not provided via `options.nextVersion`.
1368
- * * Computes the released package information based on version changes.
1369
- * * Assembles a new changelog based on the visible entries.
1370
- * * Optionally writes the new changelog to disk and removes the processed entry files.
1371
- * * Commits the changes (changelog and removed files) to the Git repository.
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
- const { nextVersion, packagesDirectory, isSinglePackage, transformScope, npmPackageToCheck, linkFilter, shouldSkipLinks, cwd = process.cwd(), externalRepositories = [], date = format(new Date(), 'yyyy-MM-dd'), shouldIgnoreRootPackage = false, disableFilesystemOperations = false } = options;
1378
- const { version: currentVersion, name: rootPackageName } = await workspaces.getPackageJson(cwd, { async: true });
1379
- const packagesMetadata = await findPackages({
1380
- cwd,
1381
- packagesDirectory,
1382
- shouldIgnoreRootPackage,
1383
- externalRepositories
1384
- });
1385
- const releaseType = nextVersion ? getReleaseType(currentVersion, nextVersion) : await promptReleaseType(currentVersion);
1386
- const entryPaths = await findChangelogEntryPaths({
1387
- cwd,
1388
- externalRepositories,
1389
- linkFilter,
1390
- shouldSkipLinks,
1391
- includeSubdirectories: releaseType === 'latest' || releaseType === 'prerelease-promote'
1392
- });
1393
- const parsedChangesetFiles = await parseChangelogEntries(entryPaths, isSinglePackage);
1394
- const sectionsWithEntries = groupEntriesBySection({
1395
- packagesMetadata,
1396
- transformScope,
1397
- isSinglePackage,
1398
- files: parsedChangesetFiles
1399
- });
1400
- // Exit when no changelog entries exist.
1401
- if (!parsedChangesetFiles.length) {
1402
- logInfo('');
1403
- logInfo(styleText('bold', 'ℹ️ No changelog entries found, so there is nothing to prepare a changelog from.'));
1404
- return disableFilesystemOperations ? '' : undefined;
1405
- }
1406
- // Log changes in the console only when `nextVersion` is not provided.
1407
- if (!nextVersion) {
1408
- displayChanges({
1409
- isSinglePackage,
1410
- transformScope,
1411
- sections: sectionsWithEntries
1412
- });
1413
- }
1414
- // Display a prompt to provide a new version in the console.
1415
- const newVersion = await determineNextVersion({
1416
- currentVersion,
1417
- nextVersion,
1418
- releaseType,
1419
- sections: sectionsWithEntries,
1420
- packageName: shouldIgnoreRootPackage ? npmPackageToCheck : rootPackageName
1421
- });
1422
- const releasedPackagesInfo = await composeReleaseSummary({
1423
- currentVersion,
1424
- newVersion,
1425
- packagesMetadata,
1426
- sections: sectionsWithEntries
1427
- });
1428
- const newChangelog = await composeChangelog({
1429
- currentVersion,
1430
- cwd,
1431
- date,
1432
- newVersion,
1433
- isSinglePackage,
1434
- releasedPackagesInfo,
1435
- sections: filterVisibleSections(sectionsWithEntries)
1436
- });
1437
- if (disableFilesystemOperations) {
1438
- return newChangelog;
1439
- }
1440
- let pathsToCommit = entryPaths;
1441
- // Handle changelog entry files based on release type.
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
- * Entry point for generating a changelog with error handling.
1454
- *
1455
- * This wrapper ensures that:
1456
- * * Interruptions from the user (e.g., Ctrl+C or intentional aborts) exit silently with code 0.
1457
- * * Expected and unexpected internal errors are logged to the console and exit with code 1.
1458
- * * Other unexpected errors are re-thrown for higher-level handling.
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
- return main(options)
1462
- .catch(error => {
1463
- if (isExpectedError(error)) {
1464
- process.exit(0);
1465
- }
1466
- else if (!(error instanceof InternalError)) {
1467
- throw error;
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
- return typeof error === 'object' && error !== null && 'message' in error;
1304
+ return typeof error === "object" && error !== null && "message" in error;
1477
1305
  }
1478
1306
  function isExpectedError(error) {
1479
- if (isError(error) && error.message.includes('User force closed the prompt with SIGINT')) {
1480
- return true;
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1490
- * For licensing, see LICENSE.md.
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
- const { date, cwd, externalRepositories, nextVersion, disableFilesystemOperations, npmPackageToCheck, packagesDirectory, linkFilter, shouldSkipLinks, shouldIgnoreRootPackage, transformScope } = options;
1494
- return generateChangelog({
1495
- nextVersion,
1496
- cwd,
1497
- packagesDirectory,
1498
- externalRepositories,
1499
- transformScope,
1500
- date,
1501
- linkFilter,
1502
- shouldSkipLinks,
1503
- disableFilesystemOperations,
1504
- ...(shouldIgnoreRootPackage && npmPackageToCheck ?
1505
- { shouldIgnoreRootPackage: true, npmPackageToCheck } :
1506
- { shouldIgnoreRootPackage: false }),
1507
- isSinglePackage: false
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
- * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
1513
- * For licensing, see LICENSE.md.
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
- const { cwd, date, externalRepositories, nextVersion, disableFilesystemOperations, linkFilter, shouldSkipLinks } = options;
1517
- return generateChangelog({
1518
- nextVersion,
1519
- cwd,
1520
- externalRepositories,
1521
- date,
1522
- linkFilter,
1523
- shouldSkipLinks,
1524
- disableFilesystemOperations,
1525
- isSinglePackage: true,
1526
- packagesDirectory: null
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 };