@ckeditor/ckeditor5-dev-release-tools 51.1.0 → 53.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,771 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md.
4
- */
5
-
6
- import fs from 'fs';
7
- import path from 'path';
8
- import { tools, logger, workspaces } from '@ckeditor/ckeditor5-dev-utils';
9
- import compareFunc from 'compare-func';
10
- import chalk from 'chalk';
11
- import semver from 'semver';
12
- import displayCommits from '../utils/displaycommits.js';
13
- import displaySkippedPackages from '../utils/displayskippedpackages.js';
14
- import generateChangelog from '../utils/generatechangelog.js';
15
- import getPackagesPaths from '../utils/getpackagespaths.js';
16
- import getCommits from '../utils/getcommits.js';
17
- import getNewVersionType from '../utils/getnewversiontype.js';
18
- import getWriterOptions from '../utils/getwriteroptions.js';
19
- import getFormattedDate from '../utils/getformatteddate.js';
20
- import getChangelog from '../utils/getchangelog.js';
21
- import saveChangelog from '../utils/savechangelog.js';
22
- import truncateChangelog from '../utils/truncatechangelog.js';
23
- import transformCommitFactory from '../utils/transformcommitfactory.js';
24
- import provideNewVersionForMonoRepository from '../utils/providenewversionformonorepository.js';
25
- import { CHANGELOG_FILE, CHANGELOG_HEADER, CLI_INDENT_SIZE } from '../utils/constants.js';
26
-
27
- const VERSIONING_POLICY_URL = 'https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/versioning-policy.html';
28
- const noteInfo = `[ℹ️](${ VERSIONING_POLICY_URL }#major-and-minor-breaking-changes)`;
29
-
30
- /**
31
- * Generates the single changelog for the mono repository. It means that changes which have been done in all packages
32
- * will be described in the changelog file located in the `options.cwd` directory.
33
- *
34
- * The typed version will be the same for all packages. See: https://github.com/ckeditor/ckeditor5/issues/7323.
35
- *
36
- * @param {object} options
37
- *
38
- * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved.
39
- *
40
- * @param {string} options.packages Where to look for packages.
41
- *
42
- * @param {function} options.transformScope A function that returns a URL to a package from a scope of a commit.
43
- *
44
- * @param {string} [options.scope] Package names have to match to specified glob pattern in order to be processed.
45
- *
46
- * @param {Array.<string>} [options.skipPackages=[]] Name of packages which won't be touched.
47
- *
48
- * @param {boolean} [options.skipLinks=false] If set on true, links to release or commits will be omitted.
49
- *
50
- * @param {string} [options.from] A commit or tag name that will be the first param of the range of commits to collect.
51
- *
52
- * @param {string} [options.releaseBranch='master'] A name of the branch that should be used for releasing packages.
53
- *
54
- * @param {string} [options.mainBranch='master'] A name of the main branch in the repository.
55
- *
56
- * @param {Array.<ExternalRepository>} [options.externalRepositories=[]] An array of object with additional repositories
57
- * that the function takes into consideration while gathering commits. It assumes that those directories are also mono repositories.
58
- *
59
- * @param {boolean} [options.skipFileSave=false] Whether to resolve the changes instead of saving it to a file.
60
- *
61
- * @param {string|null} [options.nextVersion=null] Next version to use. If not provided, a user needs to provide via CLI.
62
- *
63
- * @param {FormatDateCallback} [options.formatDate] A callback allowing defining a custom format of the date inserted into the changelog.
64
- * If not specified, the default date matches the `YYYY-MM-DD` pattern.
65
- *
66
- * @returns {Promise.<undefined|string>}
67
- */
68
- export default async function generateChangelogForMonoRepository( options ) {
69
- const log = logger();
70
- const cwd = process.cwd();
71
- const rootPkgJson = workspaces.getPackageJson( options.cwd );
72
- const skipFileSave = options.skipFileSave || false;
73
-
74
- logProcess( 'Collecting paths to packages...' );
75
-
76
- const pathsCollection = gatherAllPackagesPaths( {
77
- cwd: options.cwd,
78
- packages: options.packages,
79
- scope: options.scope || null,
80
- skipPackages: options.skipPackages || [],
81
- externalRepositories: options.externalRepositories || []
82
- } );
83
-
84
- const commitOptions = {
85
- cwd: options.cwd,
86
- from: options.from ? options.from : 'v' + rootPkgJson.version,
87
- releaseBranch: options.releaseBranch || 'master',
88
- mainBranch: options.mainBranch || 'master',
89
- externalRepositories: options.externalRepositories || []
90
- };
91
-
92
- logProcess( 'Collecting all commits since the last release...' );
93
-
94
- // Collection of all entries (real commits + additional "fake" commits extracted from descriptions).
95
- const allCommits = await gatherAllCommits( commitOptions );
96
-
97
- // Collection of public entries that will be inserted in the changelog.
98
- let publicCommits;
99
-
100
- // The next version for the upcoming release. If not specified, a user has to provide via CLI.
101
- let nextVersion = options.nextVersion || null;
102
-
103
- // A map contains packages and their new versions.
104
- const packagesVersion = new Map();
105
-
106
- // A map contains packages and their current versions.
107
- const currentPackagesVersion = new Map();
108
-
109
- // A map contains packages and their paths (where they are located)
110
- const packagesPaths = new Map();
111
-
112
- logInfo( `Found ${ allCommits.length } entries to parse.`, { indentLevel: 1, startWithNewLine: true } );
113
-
114
- // If the next version is specified by the integrator, setup internal values used when preparing
115
- // the "Releases packages" section.
116
- if ( nextVersion ) {
117
- packagesVersion.set( rootPkgJson.name, nextVersion );
118
-
119
- for ( const packagePath of pathsCollection.matched ) {
120
- const packageJson = workspaces.getPackageJson( packagePath );
121
-
122
- currentPackagesVersion.set( packageJson.name, packageJson.version );
123
- packagesPaths.set( packageJson.name, packagePath );
124
- packagesVersion.set( packageJson.name, nextVersion );
125
- }
126
- } else {
127
- await typeNewVersionForAllPackages();
128
- }
129
-
130
- if ( !skipFileSave ) {
131
- await saveChangelogToFile();
132
-
133
- // Make a commit from the repository where we started.
134
- process.chdir( options.cwd );
135
- tools.shExec( `git add ${ CHANGELOG_FILE }`, { verbosity: 'error' } );
136
- tools.shExec( 'git commit -m "Docs: Changelog. [skip ci]"', { verbosity: 'error' } );
137
- logInfo( 'Committed.', { indentLevel: 1 } );
138
- }
139
-
140
- process.chdir( cwd );
141
-
142
- logProcess( 'Summary' );
143
-
144
- displaySkippedPackages( new Set( [
145
- ...pathsCollection.skipped
146
- ].sort() ) );
147
-
148
- logInfo(
149
- `Changelog for "${ chalk.underline( rootPkgJson.name ) }" (v${ packagesVersion.get( rootPkgJson.name ) }) has been generated.`,
150
- { indentLevel: 1 }
151
- );
152
-
153
- if ( skipFileSave ) {
154
- return getChangelogForNextVersion();
155
- }
156
-
157
- return Promise.resolve();
158
-
159
- /**
160
- * Returns collections with packages found in the `options.cwd` directory and the external repositories.
161
- *
162
- * @param {object} options
163
- * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved.
164
- * @param {string} options.packages Where to look for packages.
165
- * @param {string} options.scope Package names have to match to specified glob pattern in order to be processed.
166
- * @param {Array.<string>} options.skipPackages Name of packages which won't be touched.
167
- * @param {Array.<ExternalRepository>} options.externalRepositories An array of object with additional repositories
168
- * that the function takes into consideration while gathering packages.
169
- * @returns {PathsCollection}
170
- */
171
- function gatherAllPackagesPaths( options ) {
172
- logInfo( `Processing "${ options.cwd }"...`, { indentLevel: 1 } );
173
-
174
- const pathsCollection = getPackagesPaths( {
175
- cwd: options.cwd,
176
- packages: options.packages,
177
- scope: options.scope,
178
- skipPackages: options.skipPackages,
179
- skipMainRepository: true
180
- } );
181
-
182
- for ( const externalRepository of options.externalRepositories ) {
183
- logInfo( `Processing "${ externalRepository.cwd }"...`, { indentLevel: 1 } );
184
-
185
- const externalPackages = getPackagesPaths( {
186
- cwd: externalRepository.cwd,
187
- packages: externalRepository.packages,
188
- scope: externalRepository.scope || null,
189
- skipPackages: externalRepository.skipPackages || [],
190
- skipMainRepository: true
191
- } );
192
-
193
- // The main package in an external repository is a private package.
194
- externalPackages.skipped.delete( externalRepository.cwd );
195
-
196
- // Merge results with the object that will be returned.
197
- [ ...externalPackages.matched ].forEach( item => pathsCollection.matched.add( item ) );
198
- [ ...externalPackages.skipped ].forEach( item => pathsCollection.skipped.add( item ) );
199
- }
200
-
201
- // The main repository should be at the end of the list.
202
- pathsCollection.skipped.delete( options.cwd );
203
- pathsCollection.matched.add( options.cwd );
204
-
205
- return pathsCollection;
206
- }
207
-
208
- /**
209
- * Returns a promise that resolves an array of commits since the last tag specified as `options.from`.
210
- *
211
- * @param {object} options
212
- * @param {string} options.cwd Current working directory (packages) from which all paths will be resolved.
213
- * @param {string} options.from A commit or tag name that will be the first param of the range of commits to collect.
214
- * @param {string} options.releaseBranch A name of the branch that should be used for releasing packages.
215
- * @param {Array.<ExternalRepository>} options.externalRepositories An array of object with additional repositories
216
- * that the function takes into consideration while gathering commits.
217
- * @returns {Promise.<Array.<Commit>>}
218
- */
219
- function gatherAllCommits( options ) {
220
- process.chdir( options.cwd );
221
- logInfo( `Processing "${ options.cwd }"...`, { indentLevel: 1 } );
222
-
223
- const transformCommit = transformCommitFactory( {
224
- useExplicitBreakingChangeGroups: true
225
- } );
226
-
227
- const commitOptions = {
228
- from: options.from,
229
- releaseBranch: options.releaseBranch,
230
- mainBranch: options.mainBranch
231
- };
232
-
233
- let promise = getCommits( transformCommit, commitOptions )
234
- .then( commits => {
235
- logInfo( `Found ${ commits.length } entries in "${ options.cwd }".`, { indentLevel: 1 } );
236
-
237
- return commits;
238
- } );
239
-
240
- for ( const externalRepository of options.externalRepositories ) {
241
- promise = promise.then( commits => {
242
- logInfo( `Processing "${ externalRepository.cwd }"...`, { indentLevel: 1, startWithNewLine: true } );
243
- process.chdir( externalRepository.cwd );
244
-
245
- const commitOptions = {
246
- from: externalRepository.from || options.from,
247
- releaseBranch: externalRepository.releaseBranch || options.releaseBranch
248
- };
249
-
250
- return getCommits( transformCommit, commitOptions )
251
- .then( newCommits => {
252
- logInfo( `Found ${ newCommits.length } entries in "${ externalRepository.cwd }".`, { indentLevel: 1 } );
253
-
254
- for ( const singleCommit of newCommits ) {
255
- singleCommit.skipCommitsLink = externalRepository.skipLinks || false;
256
- }
257
-
258
- // Merge arrays with the commits.
259
- return [].concat( commits, newCommits );
260
- } );
261
- } );
262
- }
263
-
264
- return promise.then( commits => {
265
- process.chdir( options.cwd );
266
-
267
- return commits;
268
- } );
269
- }
270
-
271
- /**
272
- * Asks the user about the new version for all packages for the upcoming release.
273
- *
274
- * @returns {Promise}
275
- */
276
- function typeNewVersionForAllPackages() {
277
- logProcess( 'Determining the new version...' );
278
-
279
- displayAllChanges();
280
-
281
- // Find the highest version in all packages.
282
- const [ packageHighestVersion, highestVersion ] = [ ...pathsCollection.matched ]
283
- .reduce( ( currentHighest, repositoryPath ) => {
284
- const packageJson = workspaces.getPackageJson( repositoryPath );
285
-
286
- currentPackagesVersion.set( packageJson.name, packageJson.version );
287
-
288
- if ( semver.gt( packageJson.version, currentHighest[ 1 ] ) ) {
289
- return [ packageJson.name, packageJson.version ];
290
- }
291
-
292
- return currentHighest;
293
- }, [ null, '0.0.0' ] );
294
-
295
- let bumpType = getNewVersionType( allCommits );
296
-
297
- // When made commits are not public, bump the `patch` version.
298
- if ( bumpType === 'internal' ) {
299
- bumpType = 'patch';
300
- }
301
-
302
- const provideVersionOptions = {
303
- packageName: packageHighestVersion,
304
- version: highestVersion,
305
- bumpType,
306
- indentLevel: 1
307
- };
308
-
309
- return provideNewVersionForMonoRepository( provideVersionOptions )
310
- .then( version => {
311
- nextVersion = version;
312
-
313
- let promise = Promise.resolve();
314
-
315
- // Update the version for all packages.
316
- for ( const packagePath of pathsCollection.matched ) {
317
- promise = promise.then( () => {
318
- const pkgJson = workspaces.getPackageJson( packagePath );
319
-
320
- packagesPaths.set( pkgJson.name, packagePath );
321
- packagesVersion.set( pkgJson.name, nextVersion );
322
- } );
323
- }
324
-
325
- return promise;
326
- } );
327
- }
328
-
329
- /**
330
- * Displays breaking changes and commits.
331
- */
332
- function displayAllChanges() {
333
- const majorBreakingChangesCommits = filterCommitsByNoteTitle( allCommits, 'MAJOR BREAKING CHANGES' );
334
- const infoOptions = { indentLevel: 1, startWithNewLine: true };
335
-
336
- if ( majorBreakingChangesCommits.length > 0 ) {
337
- logInfo( `🔸 Found ${ chalk.bold( 'MAJOR BREAKING CHANGES' ) }:`, infoOptions );
338
- displayCommits( majorBreakingChangesCommits, { attachLinkToCommit: true, indentLevel: 2 } );
339
- } else {
340
- logInfo( `🔸 ${ chalk.bold( 'MAJOR BREAKING CHANGES' ) } commits have not been found.`, infoOptions );
341
- }
342
-
343
- const minorBreakingChangesCommits = filterCommitsByNoteTitle( allCommits, 'MINOR BREAKING CHANGES' );
344
-
345
- if ( minorBreakingChangesCommits.length > 0 ) {
346
- logInfo( `🔸 Found ${ chalk.bold( 'MINOR BREAKING CHANGES' ) }:`, infoOptions );
347
- displayCommits( minorBreakingChangesCommits, { attachLinkToCommit: true, indentLevel: 2 } );
348
- } else {
349
- logInfo( `🔸 ${ chalk.bold( 'MINOR BREAKING CHANGES' ) } commits have not been found.`, infoOptions );
350
- }
351
-
352
- logInfo( '🔸 Commits since the last release:', infoOptions );
353
-
354
- const commits = allCommits.sort( sortFunctionFactory( 'scope' ) );
355
-
356
- displayCommits( commits, { indentLevel: 2 } );
357
-
358
- logInfo( '💡 Review commits listed above and propose the new version for all packages in the upcoming release.', infoOptions );
359
- }
360
-
361
- /**
362
- * Finds commits that contain a note which matches to `titleNote`.
363
- *
364
- * @returns {Array.<Commit>}
365
- */
366
- function filterCommitsByNoteTitle( commits, titleNote ) {
367
- return commits.filter( commit => {
368
- if ( !commit.isPublicCommit ) {
369
- return false;
370
- }
371
-
372
- for ( const note of commit.notes ) {
373
- if ( note.title.startsWith( titleNote ) ) {
374
- return true;
375
- }
376
- }
377
-
378
- return false;
379
- } );
380
- }
381
-
382
- /**
383
- * Finds commits that touched the package under `packagePath` directory.
384
- *
385
- * @param {Array.<Commit>} commits
386
- * @param {string} packagePath
387
- * @returns {Array.<Commit>}
388
- */
389
- function filterCommitsByPath( commits, packagePath ) {
390
- const shortPackagePath = packagePath.replace( options.cwd, '' )
391
- .replace( new RegExp( `^\\${ path.sep }` ), '' );
392
-
393
- return commits.filter( commit => {
394
- return commit.files.some( file => {
395
- // The main repository.
396
- if ( shortPackagePath === '' ) {
397
- return !file.startsWith( 'packages' );
398
- }
399
-
400
- return file.startsWith( shortPackagePath );
401
- } );
402
- } );
403
- }
404
-
405
- /**
406
- * Generates a list of changes based on the commits in the main repository.
407
- *
408
- * @returns {Promise.<string>}
409
- */
410
- function generateChangelogFromCommits() {
411
- logProcess( 'Generating the changelog...' );
412
-
413
- const version = packagesVersion.get( rootPkgJson.name );
414
-
415
- const writerContext = {
416
- version,
417
- commit: 'commit',
418
- repoUrl: workspaces.getRepositoryUrl( options.cwd ),
419
- currentTag: 'v' + version,
420
- previousTag: options.from ? options.from : 'v' + rootPkgJson.version,
421
- isPatch: semver.diff( version, rootPkgJson.version ) === 'patch',
422
- skipCommitsLink: Boolean( options.skipLinks ),
423
- skipCompareLink: Boolean( options.skipLinks ),
424
- date: options.formatDate ? options.formatDate( new Date() ) : getFormattedDate()
425
- };
426
-
427
- const writerOptions = getWriterOptions( commit => {
428
- // We do not allow modifying the commit hash value by the generator itself.
429
- return commit;
430
- } );
431
-
432
- writerOptions.commitsSort = sortFunctionFactory( 'rawScope' );
433
- writerOptions.notesSort = sortFunctionFactory( 'rawScope' );
434
-
435
- publicCommits = [ ...allCommits ]
436
- .filter( commit => commit.isPublicCommit )
437
- .map( commit => {
438
- commit.rawScope = commit.scope;
439
-
440
- // Transforms a scope to markdown link.
441
- if ( Array.isArray( commit.scope ) ) {
442
- commit.scope = commit.scope.map( scopeToLink );
443
- }
444
-
445
- // Attaches an icon to notes.
446
- commit.notes = commit.notes.map( note => {
447
- note.title += ' ' + noteInfo;
448
- note.rawScope = note.scope;
449
-
450
- // Transforms a scope to markdown link.
451
- if ( Array.isArray( note.scope ) ) {
452
- note.scope = note.scope.map( scopeToLink );
453
- }
454
-
455
- return note;
456
- } );
457
-
458
- return commit;
459
- } );
460
-
461
- return generateChangelog( publicCommits, writerContext, writerOptions )
462
- .then( changes => {
463
- logInfo( 'Changes based on commits have been generated.', { indentLevel: 1 } );
464
-
465
- return Promise.resolve( changes );
466
- } );
467
-
468
- function scopeToLink( name ) {
469
- return `[${ name }](${ options.transformScope( name ) })`;
470
- }
471
- }
472
-
473
- async function getChangelogForNextVersion() {
474
- const changesFromCommits = await generateChangelogFromCommits();
475
- const dependenciesSummary = generateSummaryOfChangesInPackages();
476
-
477
- return [
478
- CHANGELOG_HEADER,
479
- changesFromCommits.trim(),
480
- '\n\n',
481
- dependenciesSummary
482
- ].join( '' );
483
- }
484
-
485
- /**
486
- * Combines the generated changes based on commits and summary of version changes in packages.
487
- * Appends those changes at the beginning of the changelog file.
488
- */
489
- async function saveChangelogToFile() {
490
- logProcess( 'Saving changelog...' );
491
-
492
- if ( !fs.existsSync( CHANGELOG_FILE ) ) {
493
- logInfo( 'Changelog file does not exist. Creating...', { isWarning: true, indentLevel: 1 } );
494
-
495
- saveChangelog( CHANGELOG_FILE );
496
- }
497
-
498
- logInfo( 'Preparing a summary of version changes in packages.', { indentLevel: 1 } );
499
-
500
- let currentChangelog = getChangelog();
501
-
502
- const nextVersionChangelog = await getChangelogForNextVersion();
503
-
504
- // Remove header from current changelog.
505
- currentChangelog = currentChangelog.replace( CHANGELOG_HEADER, '' ).trim();
506
-
507
- // Concat header, new entries and old changelog to single string.
508
- let newChangelog = nextVersionChangelog + '\n\n\n' + currentChangelog;
509
-
510
- newChangelog = newChangelog.trim() + '\n';
511
-
512
- // Save the changelog.
513
- saveChangelog( newChangelog );
514
-
515
- // Truncate the changelog to keep the latest five release entries.
516
- truncateChangelog( 5 );
517
-
518
- logInfo( 'Saved.', { indentLevel: 1 } );
519
- }
520
-
521
- /**
522
- * Prepares a summary that describes what has changed in all dependencies.
523
- *
524
- * @returns {string}
525
- */
526
- function generateSummaryOfChangesInPackages() {
527
- const dependencies = new Map();
528
-
529
- for ( const [ packageName, nextVersion ] of packagesVersion ) {
530
- // Skip the package hosted in the main repository.
531
- if ( packageName === rootPkgJson.name ) {
532
- continue;
533
- }
534
-
535
- dependencies.set( packageName, {
536
- next: nextVersion,
537
- current: currentPackagesVersion.get( packageName )
538
- } );
539
- }
540
-
541
- // When the `--from` flag is specified, we want to use its value as the "current version".
542
- const currentVersion = options.from ? options.from.replace( /^v/, '' ) : null;
543
- const newPackages = getNewPackages( dependencies );
544
- const majorBreakingChangesPackages = getPackagesMatchedToScopesFromNotes( dependencies, 'MAJOR BREAKING CHANGES' );
545
- const minorBreakingChangesPackages = getPackagesMatchedToScopesFromNotes( dependencies, 'MINOR BREAKING CHANGES' );
546
- const newFeaturesPackages = getPackagesWithNewFeatures( dependencies );
547
-
548
- const entries = [
549
- '### Released packages\n',
550
- `Check out the [Versioning policy](${ VERSIONING_POLICY_URL }) guide for more information.\n`,
551
- '<details>',
552
- '<summary>Released packages (summary)</summary>'
553
- ];
554
-
555
- if ( newPackages.size ) {
556
- entries.push( '\nNew packages:\n' );
557
-
558
- for ( const [ packageName, version ] of [ ...newPackages ].sort( sortByPackageName ) ) {
559
- entries.push( formatChangelogEntry( packageName, version.next ) );
560
- }
561
- }
562
-
563
- if ( majorBreakingChangesPackages.size ) {
564
- entries.push( '\nMajor releases (contain major breaking changes):\n' );
565
-
566
- for ( const [ packageName, version ] of [ ...majorBreakingChangesPackages ].sort( sortByPackageName ) ) {
567
- entries.push( formatChangelogEntry( packageName, version.next, currentVersion || version.current ) );
568
- }
569
- }
570
-
571
- if ( minorBreakingChangesPackages.size ) {
572
- entries.push( '\nMinor releases (contain minor breaking changes):\n' );
573
-
574
- for ( const [ packageName, version ] of [ ...minorBreakingChangesPackages ].sort( sortByPackageName ) ) {
575
- entries.push( formatChangelogEntry( packageName, version.next, currentVersion || version.current ) );
576
- }
577
- }
578
-
579
- if ( newFeaturesPackages.size ) {
580
- entries.push( '\nReleases containing new features:\n' );
581
-
582
- for ( const [ packageName, version ] of [ ...newFeaturesPackages ].sort( sortByPackageName ) ) {
583
- entries.push( formatChangelogEntry( packageName, version.next, currentVersion || version.current ) );
584
- }
585
- }
586
-
587
- if ( dependencies.size ) {
588
- entries.push( '\nOther releases:\n' );
589
-
590
- for ( const [ packageName, version ] of [ ...dependencies ].sort( sortByPackageName ) ) {
591
- entries.push( formatChangelogEntry( packageName, version.next, currentVersion || version.current ) );
592
- }
593
- }
594
-
595
- entries.push( '</details>' );
596
-
597
- return entries.join( '\n' ).trim();
598
-
599
- function sortByPackageName( a, b ) {
600
- return a[ 0 ] > b[ 0 ] ? 1 : -1;
601
- }
602
- }
603
-
604
- /**
605
- * @param {Map.<string, Version>} dependencies
606
- * @returns {Map.<string, Version>}
607
- */
608
- function getNewPackages( dependencies ) {
609
- const packages = new Map();
610
-
611
- for ( const [ packageName, version ] of dependencies ) {
612
- if ( semver.eq( version.current, '0.0.1' ) ) {
613
- packages.set( packageName, version );
614
- dependencies.delete( packageName );
615
- }
616
- }
617
-
618
- return packages;
619
- }
620
-
621
- /**
622
- * Returns packages where scope of changes described in the commits' notes match to packages' names.
623
- *
624
- * @param {Map.<string, Version>} dependencies
625
- * @param {string} noteTitle
626
- * @returns {Map.<string, Version>}
627
- */
628
- function getPackagesMatchedToScopesFromNotes( dependencies, noteTitle ) {
629
- const packages = new Map();
630
- const scopes = new Set();
631
-
632
- for ( const commit of filterCommitsByNoteTitle( publicCommits, noteTitle ) ) {
633
- for ( const note of commit.notes ) {
634
- if ( Array.isArray( note.rawScope ) ) {
635
- scopes.add( note.rawScope[ 0 ] );
636
- }
637
- }
638
- }
639
-
640
- for ( const [ packageName, version ] of dependencies ) {
641
- const packageScope = packageName.replace( /^@ckeditor\/ckeditor5?-/, '' );
642
-
643
- for ( const singleScope of scopes ) {
644
- if ( packageScope === singleScope ) {
645
- packages.set( packageName, version );
646
- dependencies.delete( packageName );
647
- }
648
- }
649
- }
650
-
651
- return packages;
652
- }
653
-
654
- /**
655
- * Returns packages that contain new features.
656
- *
657
- * @param {Map.<string, Version>} dependencies
658
- * @returns {Map.<string, Version>}
659
- */
660
- function getPackagesWithNewFeatures( dependencies ) {
661
- const packages = new Map();
662
-
663
- for ( const [ packageName, version ] of dependencies ) {
664
- const packagePath = packagesPaths.get( packageName );
665
- const commits = filterCommitsByPath( publicCommits, packagePath );
666
- const hasFeatures = commits.some( commit => commit.rawType === 'Feature' );
667
-
668
- if ( hasFeatures ) {
669
- packages.set( packageName, version );
670
- dependencies.delete( packageName );
671
- }
672
- }
673
-
674
- return packages;
675
- }
676
-
677
- /**
678
- * Returns a formatted entry (string) for the changelog.
679
- *
680
- * @param {string} packageName
681
- * @param {string} nextVersion
682
- * @param {string} currentVersion
683
- * @returns {string}
684
- */
685
- function formatChangelogEntry( packageName, nextVersion, currentVersion = null ) {
686
- const npmUrl = `https://www.npmjs.com/package/${ packageName }/v/${ nextVersion }`;
687
-
688
- if ( currentVersion ) {
689
- return `* [${ packageName }](${ npmUrl }): v${ currentVersion } => v${ nextVersion }`;
690
- }
691
-
692
- return `* [${ packageName }](${ npmUrl }): v${ nextVersion }`;
693
- }
694
-
695
- /**
696
- * Returns a function that is being used when sorting commits.
697
- *
698
- * @param {string} scopeField A name of the field that saves the commit's scope.
699
- * @returns {Function}
700
- */
701
- function sortFunctionFactory( scopeField ) {
702
- return compareFunc( item => {
703
- if ( Array.isArray( item[ scopeField ] ) ) {
704
- // A hack that allows moving all scoped commits from the main repository/package at the beginning of the list.
705
- if ( item[ scopeField ][ 0 ] === rootPkgJson.name ) {
706
- return 'a'.repeat( 15 );
707
- }
708
-
709
- return item[ scopeField ][ 0 ];
710
- }
711
-
712
- // A hack that allows moving all non-scoped commits or breaking changes notes at the end of the list.
713
- return 'z'.repeat( 15 );
714
- } );
715
- }
716
-
717
- function logProcess( message ) {
718
- log.info( '\n📍 ' + chalk.cyan( message ) );
719
- }
720
-
721
- /**
722
- * @param {string} message
723
- * @param {object} [options={}]
724
- * @param {number} [options.indentLevel=0]
725
- * @param {boolean} [options.startWithNewLine=false] Whether to append a new line before the message.
726
- * @param {boolean} [options.isWarning=false] Whether to use `warning` method instead of `log`.
727
- */
728
- function logInfo( message, options = {} ) {
729
- const indentLevel = options.indentLevel || 0;
730
- const startWithNewLine = options.startWithNewLine || false;
731
- const method = options.isWarning ? 'warning' : 'info';
732
-
733
- log[ method ]( `${ startWithNewLine ? '\n' : '' }${ ' '.repeat( indentLevel * CLI_INDENT_SIZE ) }` + message );
734
- }
735
- }
736
-
737
- /**
738
- * @typedef {object} Version
739
- *
740
- * @param {boolean} current The current version defined in the `package.json` file.
741
- *
742
- * @param {boolean} next The next version defined during generating the changelog file.
743
- */
744
-
745
- /**
746
- * @typedef {object} ExternalRepository
747
- *
748
- * @param {string} cwd An absolute path to the repository.
749
- *
750
- * @param {string} packages Subdirectory in a given `cwd` that should searched for packages. E.g. `'packages'`.
751
- *
752
- * @param {string} [scope] Glob pattern for package names to be processed.
753
- *
754
- * @param {Array.<string>} [skipPackages] Name of packages which won't be touched.
755
- *
756
- * @param {boolean} [skipLinks] If set on `true`, a URL to commit (hash) will be omitted.
757
- *
758
- * @param {string} [from] A commit or tag name that will be the first param of the range of commits to collect. If not specified,
759
- * the option will inherit its value from the function's `options` object.
760
- *
761
- * @param {string} [releaseBranch] A name of the branch that should be used for releasing packages. If not specified, the branch
762
- * used for the main repository will be used.
763
- */
764
-
765
- /**
766
- * @callback FormatDateCallback
767
- *
768
- * @param {Date} now The current date.
769
- *
770
- * @returns {string} The formatted date inserted into the changelog.
771
- */