@ckeditor/ckeditor5-dev-changelog 54.0.0 → 54.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +109 -54
  2. package/package.json +4 -6
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import chalk from 'chalk';
1
+ import { styleText } from 'util';
2
2
  import { parse, isValid, format } from 'date-fns';
3
3
  import { workspaces, npm, tools } from '@ckeditor/ckeditor5-dev-utils';
4
4
  import upath from 'upath';
5
- import fs$1 from 'fs-extra';
5
+ import fs$1 from 'fs/promises';
6
6
  import fs from 'fs';
7
7
  import semver from 'semver';
8
8
  import inquirer from 'inquirer';
@@ -369,12 +369,12 @@ function logInfo(text, { indent } = { indent: 0 }) {
369
369
  function displayChanges(options) {
370
370
  const { sections, isSinglePackage, transformScope } = options;
371
371
  let numberOfEntries = 0;
372
- logInfo(`○ ${chalk.cyan('Listing the changes...')}`);
372
+ logInfo(`○ ${styleText('cyan', 'Listing the changes...')}`);
373
373
  const nonEmptySections = Object.entries(sections)
374
374
  .filter(([, section]) => section.entries.length);
375
375
  for (const [sectionName, section] of nonEmptySections) {
376
376
  const color = getTitleColor(sectionName);
377
- logInfo('◌ ' + color(chalk.underline(`${section.titleInLogs || section.title}:`)), { indent: 1 });
377
+ logInfo('◌ ' + color(styleText('underline', `${section.titleInLogs || section.title}:`)), { indent: 1 });
378
378
  const displayCallback = sectionName === 'invalid' || sectionName === 'warning' ?
379
379
  displayWarningEntry :
380
380
  displayValidEntry;
@@ -384,9 +384,9 @@ function displayChanges(options) {
384
384
  section.entries.forEach(entry => displayCallback(entry, sectionName, isSinglePackage, transformScope));
385
385
  logInfo('');
386
386
  }
387
- logInfo('◌ ' + chalk.underline('Legend:'), { indent: 1 });
388
- logInfo(`- Entries marked with ${chalk.green('+')} symbol are included in the changelog.`, { indent: 2 });
389
- logInfo('- Entries marked with ' + chalk.yellow('x') + ' symbol include invalid references (see and/or closes) ' +
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
390
  'or scope definitions. Please ensure that:', { indent: 2 });
391
391
  logInfo('* Reference entries match one of the following formats:', { indent: 3 });
392
392
  logInfo('1. An issue number (e.g., 1000)', { indent: 4 });
@@ -399,17 +399,15 @@ function displayChanges(options) {
399
399
  }
400
400
  function getTitleColor(sectionName) {
401
401
  if (sectionName === 'warning') {
402
- return chalk.yellow;
402
+ return (text) => styleText('yellow', text);
403
403
  }
404
404
  if (sectionName === 'invalid') {
405
- return chalk.red;
405
+ return (text) => styleText('red', text);
406
406
  }
407
- let defaultColor = chalk.blue;
407
+ let defaultColor = (text) => styleText('blue', text);
408
408
  if (isBreakingChangeSection(sectionName)) {
409
409
  // To avoid tricks in tests, let's simplify the implementation.
410
- defaultColor = (value) => {
411
- return chalk.bold(chalk.blue(value));
412
- };
410
+ defaultColor = (value) => styleText(['bold', 'blue'], value);
413
411
  }
414
412
  return defaultColor;
415
413
  }
@@ -428,9 +426,9 @@ function displayValidEntry(entry, sectionName, isSinglePackage, transformScope)
428
426
  entry.data.scope.map(scope => transformScope(scope).displayName) :
429
427
  entry.data.scope;
430
428
  const scope = entry.data.scope.length ?
431
- chalk.grey(scopeFormatted?.join(', ')) :
432
- `${chalk.italic(chalk.grey('(no scope)'))}`;
433
- const validationIndicator = isEntryFullyValid ? chalk.green('+') : chalk.yellow('x');
429
+ styleText('grey', scopeFormatted?.join(', ')) :
430
+ `${styleText(['italic', 'gray'], '(no scope)')}`;
431
+ const validationIndicator = isEntryFullyValid ? styleText('green', '+') : styleText('yellow', 'x');
434
432
  const shouldTrimMessage = String(entry.data.mainContent).length > 100;
435
433
  const trimmedMessageContent = shouldTrimMessage ? entry.data.mainContent?.slice(0, 100) + '...' : entry.data.mainContent;
436
434
  if (isSinglePackage) {
@@ -504,7 +502,7 @@ async function modifyChangelog(newChangelog, cwd) {
504
502
  const changelogPath = upath.join(cwd, CHANGELOG_FILE);
505
503
  const existingChangelog = await readExistingChangelog(changelogPath);
506
504
  const updatedChangelog = prepareChangelogContent(existingChangelog, newChangelog);
507
- logInfo(`○ ${chalk.cyan('Appending changes to the existing changelog...')}`);
505
+ logInfo(`○ ${styleText('cyan', 'Appending changes to the existing changelog...')}`);
508
506
  await fs$1.writeFile(changelogPath, updatedChangelog, 'utf-8');
509
507
  await truncateChangelog(5, cwd);
510
508
  }
@@ -516,7 +514,7 @@ async function readExistingChangelog(changelogPath) {
516
514
  return await fs$1.readFile(changelogPath, 'utf-8');
517
515
  }
518
516
  catch {
519
- logInfo(`○ ${chalk.yellow('CHANGELOG.md not found.')}`);
517
+ logInfo(`○ ${styleText('yellow', 'CHANGELOG.md not found.')}`);
520
518
  return '';
521
519
  }
522
520
  }
@@ -608,18 +606,18 @@ async function provideNewVersion(options) {
608
606
  }
609
607
  }
610
608
  const question = createVersionQuestion(options);
611
- const answers = await inquirer.prompt(question);
612
- return answers.version;
609
+ const { customVersion, version } = await inquirer.prompt(question);
610
+ return customVersion || version;
613
611
  }
614
612
  /**
615
613
  * Displays a warning message about invalid changes in a visible color.
616
614
  */
617
615
  function displayInvalidChangesWarning() {
618
616
  logInfo('');
619
- logInfo(chalk.yellow(chalk.bold(`⚠️ ${chalk.underline('WARNING: Invalid changes detected!')}`)));
617
+ logInfo(styleText(['yellow', 'bold'], `⚠️ ${styleText('underline', 'WARNING: Invalid changes detected!')}`));
620
618
  logInfo('');
621
- logInfo(chalk.yellow('You can cancel the process, fix the invalid files, and run the tool again.'));
622
- logInfo(chalk.yellow('Alternatively, you can continue - but invalid values will be lost.'));
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.'));
623
621
  logInfo('');
624
622
  }
625
623
  /**
@@ -631,7 +629,7 @@ async function askContinueConfirmation(indentLevel = 0) {
631
629
  name: 'continue',
632
630
  message: 'Should continue?',
633
631
  default: false,
634
- prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + chalk.cyan('?')
632
+ prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + styleText('cyan', '?')
635
633
  };
636
634
  const answers = await inquirer.prompt(question);
637
635
  return answers.continue;
@@ -642,16 +640,26 @@ async function askContinueConfirmation(indentLevel = 0) {
642
640
  function createVersionQuestion(options) {
643
641
  const { version, packageName, bumpType, releaseChannel, releaseType, indentLevel = 0 } = options;
644
642
  const suggestedVersion = getSuggestedVersion(bumpType, version, releaseChannel) || version;
645
- const message = 'Type the new version ' +
646
- `(current: "${version}", suggested: "${suggestedVersion}":`;
643
+ const message = [
644
+ 'Select the new version.',
645
+ `Current version: ${styleText('cyan', version)}.`,
646
+ `Suggested version: ${styleText('cyan', suggestedVersion)}.`
647
+ ].join(' ');
647
648
  return [{
648
- type: 'input',
649
+ type: 'list',
649
650
  name: 'version',
650
651
  default: suggestedVersion,
651
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',
652
660
  filter: (newVersion) => newVersion.trim(),
653
661
  validate: (newVersion) => validateInputVersion({ newVersion, version, releaseType, packageName, suggestedVersion }),
654
- prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + chalk.cyan('?')
662
+ prefix: ' '.repeat(indentLevel * CLI_INDENT_SIZE) + styleText('cyan', '?')
655
663
  }];
656
664
  }
657
665
  function getSuggestedVersion(bumpType, version, releaseChannel) {
@@ -667,6 +675,35 @@ function getSuggestedVersion(bumpType, version, releaseChannel) {
667
675
  return semver.inc(version, bumpType);
668
676
  }
669
677
  }
678
+ 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
+ }
670
707
 
671
708
  /**
672
709
  * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
@@ -688,7 +725,7 @@ function detectReleaseChannel(version, promotePrerelease = false) {
688
725
  if (currentChannel === 'beta') {
689
726
  return 'rc';
690
727
  }
691
- logInfo(chalk.yellow(`Warning! Unknown release channel to promote from ${currentChannel}.`));
728
+ logInfo(styleText('yellow', `Warning! Unknown release channel to promote from ${currentChannel}.`));
692
729
  return 'alpha';
693
730
  }
694
731
  return currentChannel;
@@ -720,7 +757,7 @@ class InternalError extends Error {
720
757
  async function determineNextVersion(options) {
721
758
  const { sections, currentVersion, packageName, nextVersion, releaseType } = options;
722
759
  if (nextVersion) {
723
- logInfo(`○ ${chalk.cyan(`Determined the next version to be ${nextVersion}.`)}`);
760
+ logInfo(`○ ${styleText('cyan', `Determined the next version to be ${nextVersion}.`)}`);
724
761
  const isNightlyVersion = nextVersion.startsWith('0.0.0-');
725
762
  if (isNightlyVersion) {
726
763
  return nextVersion;
@@ -737,7 +774,7 @@ async function determineNextVersion(options) {
737
774
  }
738
775
  return nextVersion;
739
776
  }
740
- logInfo(`○ ${chalk.cyan('Determining the new version...')}`);
777
+ logInfo(`○ ${styleText('cyan', 'Determining the new version...')}`);
741
778
  let bumpType = 'patch';
742
779
  if (releaseType === 'prerelease' || releaseType === 'prerelease-promote') {
743
780
  bumpType = 'prerelease';
@@ -833,7 +870,8 @@ async function findPackages(options) {
833
870
  ]);
834
871
  return AsyncArray.from(promise)
835
872
  .flat()
836
- .map(packagePath => fs$1.readJson(packagePath))
873
+ .map(packagePath => fs.readFileSync(packagePath, 'utf-8'))
874
+ .map(packageJson => JSON.parse(packageJson))
837
875
  .map(({ name, version }) => [name, version])
838
876
  .then(entries => new Map(entries.sort(([a], [b]) => a.localeCompare(b))));
839
877
  }
@@ -1183,7 +1221,7 @@ async function composeChangelog(options) {
1183
1221
  * and removing any resulting empty directories both in the current repository and in any external repositories.
1184
1222
  */
1185
1223
  async function removeChangelogEntryFiles(entryPaths) {
1186
- logInfo(`○ ${chalk.cyan('Removing the changeset files...')}`);
1224
+ logInfo(`○ ${styleText('cyan', 'Removing the changeset files...')}`);
1187
1225
  await Promise.all(entryPaths
1188
1226
  .flatMap(repo => repo.filePaths)
1189
1227
  .map(file => fs$1.unlink(file)));
@@ -1201,12 +1239,12 @@ async function removeChangelogEntryFiles(entryPaths) {
1201
1239
  async function moveChangelogEntryFiles(entryPaths) {
1202
1240
  const targetDir = PRE_RELEASE_DIRECTORY;
1203
1241
  const modifiedEntryPaths = [];
1204
- logInfo(`○ ${chalk.cyan(`Moving changelog entries to ${targetDir}/ directory...`)}`);
1242
+ logInfo(`○ ${styleText('cyan', `Moving changelog entries to ${targetDir}/ directory...`)}`);
1205
1243
  for (const repo of entryPaths) {
1206
1244
  const { cwd, filePaths } = repo;
1207
1245
  const changelogDir = upath.join(cwd, CHANGESET_DIRECTORY);
1208
1246
  const targetPath = upath.join(changelogDir, targetDir);
1209
- await fs$1.ensureDir(targetPath);
1247
+ await fs$1.mkdir(targetPath, { recursive: true });
1210
1248
  const modifiedFilePaths = [];
1211
1249
  for (const filePath of filePaths) {
1212
1250
  const fileName = upath.basename(filePath);
@@ -1229,7 +1267,7 @@ async function moveChangelogEntryFiles(entryPaths) {
1229
1267
  */
1230
1268
  async function commitChanges(version, repositories) {
1231
1269
  const message = `Changelog for v${version}. [skip ci]`;
1232
- logInfo(`○ ${chalk.cyan('Committing changes...')}`);
1270
+ logInfo(`○ ${styleText('cyan', 'Committing changes...')}`);
1233
1271
  for (const { cwd, isRoot, filePaths } of repositories) {
1234
1272
  // Copy to avoid modifying the original array.
1235
1273
  const files = filePaths.slice(0);
@@ -1240,7 +1278,7 @@ async function commitChanges(version, repositories) {
1240
1278
  await tools.commit({ cwd, message, files })
1241
1279
  .catch(error => {
1242
1280
  logInfo('An error occurred while committing changes.', { indent: 2 });
1243
- logInfo(chalk.red(error.message), { indent: 2 });
1281
+ logInfo(styleText('red', error.message), { indent: 2 });
1244
1282
  });
1245
1283
  }
1246
1284
  }
@@ -1257,31 +1295,48 @@ async function promptReleaseType(currentVersion) {
1257
1295
  {
1258
1296
  type: 'list',
1259
1297
  name: 'releaseType',
1260
- message: 'Please select the release type.',
1261
- choices: getQuestions(currentVersion)
1298
+ message: `Select the release type. Current version: ${styleText('cyan', currentVersion)}.`,
1299
+ choices: getChoices(currentVersion)
1262
1300
  }
1263
1301
  ]);
1264
1302
  return releaseType;
1265
1303
  }
1266
- function getQuestions(currentVersion) {
1304
+ function getChoices(currentVersion) {
1267
1305
  const currentVersionPrerelease = semver.prerelease(currentVersion);
1268
1306
  if (!currentVersionPrerelease) {
1307
+ const possibleStableVersions = [
1308
+ semver.inc(currentVersion, 'major'),
1309
+ semver.inc(currentVersion, 'minor'),
1310
+ semver.inc(currentVersion, 'patch')
1311
+ ].join(' | ');
1312
+ const possiblePrereleaseVersions = [
1313
+ semver.inc(currentVersion, 'premajor', 'alpha'),
1314
+ semver.inc(currentVersion, 'preminor', 'alpha'),
1315
+ semver.inc(currentVersion, 'prepatch', 'alpha')
1316
+ ].join(' | ');
1269
1317
  return [
1270
- { name: 'Latest (stable) release (e.g. 1.0.0 -> 2.0.0)', value: 'latest' },
1271
- { name: 'Pre-release (e.g. 1.0.0 -> 2.0.0-alpha.0)', value: 'prerelease' }
1318
+ { name: `Latest (stable) release (${possibleStableVersions})`, value: 'latest' },
1319
+ { name: `Pre-release (${possiblePrereleaseVersions})`, value: 'prerelease' }
1272
1320
  ];
1273
1321
  }
1274
- if (currentVersionPrerelease[0] === 'rc') {
1275
- return [
1276
- { name: 'Latest (stable) release (e.g. 1.0.0-beta.2 -> 1.0.0)', value: 'latest' },
1277
- { name: 'Pre-release continuation (e.g. 1.0.0-alpha.0 -> 1.0.0-alpha.1)', value: 'prerelease' }
1278
- ];
1322
+ const currentPreReleaseChannel = currentVersionPrerelease[0];
1323
+ const preReleaseChannels = ['alpha', 'beta', 'rc'];
1324
+ while (preReleaseChannels.includes(currentPreReleaseChannel)) {
1325
+ preReleaseChannels.shift();
1279
1326
  }
1280
- return [
1281
- { name: 'Latest (stable) release (e.g. 1.0.0-beta.2 -> 1.0.0)', value: 'latest' },
1282
- { name: 'Pre-release continuation (e.g. 1.0.0-alpha.0 -> 1.0.0-alpha.1)', value: 'prerelease' },
1283
- { name: 'Pre-release promotion (e.g. 1.0.0-alpha.1 -> 1.0.0-beta.0)', value: 'prerelease-promote' }
1327
+ const stableVersion = semver.inc(currentVersion, 'release');
1328
+ const continuationVersion = semver.inc(currentVersion, 'prerelease', currentPreReleaseChannel);
1329
+ const choices = [
1330
+ { name: `Latest (stable) release (${stableVersion})`, value: 'latest' },
1331
+ { name: `Pre-release continuation (${continuationVersion})`, value: 'prerelease' }
1284
1332
  ];
1333
+ if (preReleaseChannels.length) {
1334
+ const availablePromotions = preReleaseChannels
1335
+ .map(channel => semver.inc(currentVersion, 'prerelease', channel))
1336
+ .join(' | ');
1337
+ choices.push({ name: `Pre-release promotion (${availablePromotions})`, value: 'prerelease-promote' });
1338
+ }
1339
+ return choices;
1285
1340
  }
1286
1341
 
1287
1342
  /**
@@ -1348,7 +1403,7 @@ const main = async (options) => {
1348
1403
  // Exit when no changelog entries exist.
1349
1404
  if (!parsedChangesetFiles.length) {
1350
1405
  logInfo('');
1351
- logInfo(chalk.bold('ℹ️ No changelog entries found, so there is nothing to prepare a changelog from.'));
1406
+ logInfo(styleText('bold', 'ℹ️ No changelog entries found, so there is nothing to prepare a changelog from.'));
1352
1407
  return disableFilesystemOperations ? '' : undefined;
1353
1408
  }
1354
1409
  // Log changes in the console only when `nextVersion` is not provided.
@@ -1395,7 +1450,7 @@ const main = async (options) => {
1395
1450
  }
1396
1451
  await modifyChangelog(newChangelog, cwd);
1397
1452
  await commitChanges(newVersion, pathsToCommit.map(({ cwd, isRoot, filePaths }) => ({ cwd, isRoot, filePaths })));
1398
- logInfo('○ ' + chalk.green('Done!'));
1453
+ logInfo('○ ' + styleText('green', 'Done!'));
1399
1454
  };
1400
1455
  /**
1401
1456
  * Entry point for generating a changelog with error handling.
@@ -1415,7 +1470,7 @@ const generateChangelog = async (options) => {
1415
1470
  throw error;
1416
1471
  }
1417
1472
  else {
1418
- console.error(chalk.red('Error: ' + error.message));
1473
+ console.error(styleText('red', 'Error: ' + error.message));
1419
1474
  process.exit(1);
1420
1475
  }
1421
1476
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-changelog",
3
- "version": "54.0.0",
3
+ "version": "54.2.0",
4
4
  "description": "A CKEditor 5 development tool for handling changelogs.",
5
5
  "keywords": [],
6
6
  "author": "CKSource (http://cksource.com/)",
@@ -28,14 +28,12 @@
28
28
  "ckeditor5-dev-changelog-create-entry": "bin/generate-template.js"
29
29
  },
30
30
  "dependencies": {
31
- "@ckeditor/ckeditor5-dev-utils": "^54.0.0",
32
- "chalk": "^5.0.0",
31
+ "@ckeditor/ckeditor5-dev-utils": "^54.2.0",
33
32
  "date-fns": "^4.0.0",
34
- "fs-extra": "^11.0.0",
35
- "glob": "^11.0.2",
33
+ "glob": "^13.0.0",
36
34
  "gray-matter": "^4.0.3",
37
35
  "inquirer": "^12.5.2",
38
36
  "semver": "^7.6.3",
39
37
  "upath": "^2.0.1"
40
38
  }
41
- }
39
+ }