From f2c106bd633112e28ea547db7deafbeb7d00422d Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 16 Jan 2024 08:40:11 -0500 Subject: [PATCH 01/46] build: changelog generation --- .../@stdlib/_tools/changelog/README.md | 49 +++ .../_tools/changelog/generate/README.md | 86 +++++ .../changelog/generate/examples/index.js | 31 ++ .../generate/lib/excluded_contributors.json | 3 + .../generate/lib/format_closed_issues.js | 71 ++++ .../generate/lib/format_commit_factory.js | 109 +++++++ .../changelog/generate/lib/format_commits.js | 48 +++ .../generate/lib/format_contributors.js | 99 ++++++ .../_tools/changelog/generate/lib/heading.js | 58 ++++ .../_tools/changelog/generate/lib/index.js | 45 +++ .../_tools/changelog/generate/lib/main.js | 303 ++++++++++++++++++ .../changelog/generate/lib/npm_releases.js | 72 +++++ .../changelog/generate/lib/release_summary.js | 220 +++++++++++++ .../_tools/changelog/generate/package.json | 63 ++++ .../_tools/changelog/generate/test/test.js | 95 ++++++ .../_tools/changelog/parse-commits/README.md | 94 ++++++ .../changelog/parse-commits/examples/index.js | 45 +++ .../changelog/parse-commits/lib/commits.js | 110 +++++++ .../lib/conventional_changelog.js | 274 ++++++++++++++++ .../changelog/parse-commits/lib/index.js | 40 +++ .../changelog/parse-commits/lib/main.js | 87 +++++ .../changelog/parse-commits/package.json | 62 ++++ .../changelog/parse-commits/test/test.js | 95 ++++++ 23 files changed, 2159 insertions(+) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/README.md create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/README.md create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/package.json create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js diff --git a/lib/node_modules/@stdlib/_tools/changelog/README.md b/lib/node_modules/@stdlib/_tools/changelog/README.md new file mode 100644 index 00000000000..472511b4896 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/README.md @@ -0,0 +1,49 @@ + + +# Changelog + +> Changelog tooling. + + + +
+ +This directory contains utilities for managing and maintaining changelogs of project packages. + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md new file mode 100644 index 00000000000..88913847e0e --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -0,0 +1,86 @@ + + +# Generate Changelog + +> Generate a changelog for a specified package. + +
+ +## Usage + +```javascript +var generate = require( '@stdlib/_tools/changelog/generate' ); +``` + +#### generate( pkg ) + +Generates a Markdown formatted changelog for a specified package. + +```javascript +var changelog = generate( 'assert/contains' ); +// returns '...' +``` + +
+ + + +
+ +
+ + + +
+ +## Examples + +```javascript +var generate = require( '@stdlib/_tools/changelog/generate' ); + +// Generate a changelog for a non-namespace package: +var changelog = generate( 'random/array/levy' ); +// returns '...' + +// Generate a changelog for a namespace package: +changelog = generate( 'string/base' ); +// returns '...' +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js new file mode 100644 index 00000000000..cab7c69cedc --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js @@ -0,0 +1,31 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var generate = require( './../lib' ); + +// Generate a changelog for a non-namespace package: +var changelog = generate( 'utils/curry' ); +console.log( changelog ); +// => '...' + +// Generate a changelog for a namespace package: +changelog = generate( 'string/base' ); +console.log( changelog ); +// => '...' diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json new file mode 100644 index 00000000000..293596b822a --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/excluded_contributors.json @@ -0,0 +1,3 @@ +[ + "stdlib-bot " +] diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js new file mode 100644 index 00000000000..8016e1ac0b0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js @@ -0,0 +1,71 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var map = require( '@stdlib/utils/map' ); + + +// FUNCTIONS // + +/** +* Formats an issue. +* +* @private +* @param {Object} mention - mention object +* @returns {string} formatted issue +* +* @example +* var out = formatIssue({ +* 'ref': '123', +* 'url': 'https://github.com/stdlib-js/stdlib/issues/123' +* }); +* // returns '[#123](https://github.com/stdlib-js/stdlib/issues/123)' +*/ +function formatIssue( mention ) { + return '[#' + mention.ref + '](' + mention.url + ')'; +} + + +// MAIN // + +/** +* Formats a list of closed issues. +* +* @private +* @param {ObjectArray} closedIssues - issue objects +* @returns {string} formatted issues list +*/ +function formatClosedIssues( closedIssues ) { + var out = '### Closed Issues\n\n'; + if ( closedIssues.length === 1 ) { + out += 'This release closes the following issue:\n\n'; + } else { + out += 'A total of ' + closedIssues.length + ' issues were closed in this release:\n\n'; + } + out += map( closedIssues, formatIssue ).join( ', ' ); + out += '\n\n'; + return out; +} + + +// EXPORTS // + +module.exports = formatClosedIssues; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js new file mode 100644 index 00000000000..30824618977 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js @@ -0,0 +1,109 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var replace = require( '@stdlib/string/replace' ); +var trim = require( '@stdlib/string/trim' ); + + +// VARIABLES // + +var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit'; +var RE_EMAIL = /\s*<[^>]+>\s*/; + + +// MAIN // + +/** +* Returns a function which formats a commit message for inclusion in a changelog. +* +* @private +* @param {boolean} type - whether to include the commit type +* @param {boolean} author - whether to include the commit author +* @returns {Function} formatting function +* +* @example +* var f = formatCommitFactory( true, true ); +* // returns +*/ +function formatCommitFactory( type, author ) { + return formatCommit; + + /** + * Formats a commit message for inclusion in a changelog. + * + * @private + * @param {Object} commit - commit object + * @returns {string} changelog entry + */ + function formatCommit( commit ) { + var hash = trim( commit.hash ); + var out = '- [`'; + out += hash.substring( 0, 7 ); + out += '`]('; + out += STDLIB_GITHUB_URL; + out += '/'; + out += hash; + out += ') - '; + if ( type ) { + out += '**'; + out += commit.type; + if ( commit.scope ) { + out += '('; + out += commit.scope; + out += ')'; + } + out += ':** '; + } + else if ( commit.scope ) { + out += '**'; + out += commit.scope; + out += ':** '; + } + out += commit.subject; + if ( commit.mentions && commit.mentions.length > 0 ) { + commit.mentions.forEach( onMention ); + } + if ( author ) { + out += ' _(by '; + out += replace( commit.author, RE_EMAIL, '' ); + out += ')_'; + } + return out; + + /** + * Callback invoked upon encountering a mention. + * + * @private + * @param {Object} mention - mention object + */ + function onMention( mention ) { + if ( mention.action === 'PR-URL' ) { + out += ' [(#' + mention.ref + ')](' + mention.url + ')'; + } + } + } +} + + +// EXPORTS // + +module.exports = formatCommitFactory; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js new file mode 100644 index 00000000000..537dbe79e66 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js @@ -0,0 +1,48 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var map = require( '@stdlib/utils/map' ); +var formatCommitFactory = require( './format_commit_factory.js' ); +var heading = require( './heading.js' ); + + +// MAIN // + +/** +* Formats a list of commits as a Markdown string. +* +* @private +* @param {ObjectArray} commits - commits +* @returns {string} Markdown string +*/ +function formatCommits( commits ) { + var out = heading( 'Commits', 3 ); + out += '
\n\n'; + out += map( commits, formatCommitFactory( true, true ) ).join( '\n' ); + out += '\n\n
\n\n'; + return out; +} + + +// EXPORTS // + +module.exports = formatCommits; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js new file mode 100644 index 00000000000..53e267b1538 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -0,0 +1,99 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var contains = require( '@stdlib/assert/contains' ); +var replace = require( '@stdlib/string/replace' ); +var map = require( '@stdlib/utils/map' ); +var heading = require( './heading.js' ); +var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' ); + + +// FUNCTIONS // + +/** +* Extracts a list of contributors from a list of commits. +* +* @private +* @param {ObjectArray} commits - commit objects +* @returns {StringArray} sorted list of contributors +*/ +function extractContributors( commits ) { + var out; + var i; + + out = []; + for ( i = 0; i < commits.length; i++ ) { + if ( + !contains( out, commits[ i ].author ) && + !contains( EXCLUDED_CONTRIBUTORS, commits[ i ].author ) + ) { + out.push( commits[ i ].author ); + } + } + return out.sort(); +} + +/** +* Formats a contributor. +* +* @private +* @param {string} contributor - contributor +* @returns {string} formatted contributor +* +* @example +* var out = formatContributor( 'Jane Doe ' ); +* // returns '- Jane Doe' +*/ +function formatContributor( contributor ) { + var out = '- '; + out += replace( contributor, /\s*<[^>]+>\s*/, '' ); + return out; +} + + +// MAIN // + +/** +* Formats a list of contributors. +* +* @private +* @param {ObjectArray} commits - commit objects +* @returns {string} formatted contributors list +*/ +function formatContributors( commits ) { + var contributors = extractContributors( commits ); + var out = heading( 'Contributors', 3 ); + if ( contributors.length === 1 ) { + out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n'; + } + else { + out += 'A total of '+contributors.length+' people contributed to this release. Thank you to the following contributors:\n\n'; + } + out += map( contributors, formatContributor ).join( '\n' ); + out += '\n\n'; + return out; +} + + +// EXPORTS // + +module.exports = formatContributors; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js new file mode 100644 index 00000000000..0102d24f03c --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js @@ -0,0 +1,58 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var repeat = require( '@stdlib/string/repeat' ); + + +// MAIN // + +/** +* Returns a Markdown heading. +* +* @private +* @param {string} text - heading +* @param {number} level - heading level +* @returns {string} Markdown heading +* +* @example +* var out = heading( 'beep', 1 ); +* // returns '\n\n# beep\n\n' +* +* @example +* var out = heading( 'boop', 3 ); +* // returns '\n\n### boop\n\n' +*/ +function heading( text, level ) { + var str; + str = '\n'; + str += '\n'; + str += repeat( '#', level ); + str += ' ' + text; + str += '\n'; + str += '\n'; + return str; +} + + +// EXPORTS // + +module.exports = heading; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js new file mode 100644 index 00000000000..1444eb0f92c --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js @@ -0,0 +1,45 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Generate a changelog for a specified package. +* +* @module @stdlib/_tools/changelog/generate +* +* @example +* var generateChangelog = require( '@stdlib/_tools/changelog/generate' ); +* +* // Standard packages: +* var changelog = generateChangelog( 'utils/curry' ); +* // returns '...' +* +* // Namespace packages: +* changelog = generateChangelog( 'stats/base/dists' ); +* // returns '...' +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js new file mode 100644 index 00000000000..fda3db81dc5 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -0,0 +1,303 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var dirname = require( 'path' ).dirname; +var substringAfter = require( '@stdlib/string/substring-after' ); +var objectEntries = require( '@stdlib/utils/entries' ); +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +var objectKeys = require( '@stdlib/utils/keys' ); +var namespaces = require( '@stdlib/_tools/pkgs/namespaces' ).sync; +var contains = require( '@stdlib/assert/contains' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var replace = require( '@stdlib/string/replace' ); +var groupBy = require( '@stdlib/utils/group-by' ); +var filter = require( '@stdlib/array/base/filter' ); +var format = require( '@stdlib/string/format' ); +var formatContributors = require( './format_contributors.js' ); +var releaseSummary = require( './release_summary.js' ); +var formatCommits = require( './format_commits.js' ); +var npmReleases = require( './npm_releases.js' ); +var heading = require( './heading.js' ); + + +// VARIABLES // + +var STDLIB_NAMESPACE_PKGS = namespaces(); +var STDLIB_REPO_NODE_PATH = 'https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules'; +var RE_PACKAGE_SUBDIRS = /\/?(benchmark|bin|data|docs|docs|etc|examples|include|lib|scripts|src|test)\/?[\s\S]*$/; +var RE_MARKDOWN_HEADER = /(^#+)/gm; +var RE_EXTRANEOUS_NEWLINES = /\n{3,}/g; + + +// FUNCTIONS // + +/** +* Changes all Markdown headers to be one level lower. +* +* @private +* @param {string} str - string to modify +* @returns {string} modified string +* +* @example +* var str = '## Foo\n\n### Bar\n'; +* var out = decrementHeaders( str ); +* // returns '### Foo\n\n#### Bar\n' +*/ +function decrementHeaders( str ) { + return replace( str, RE_MARKDOWN_HEADER, '$1#' ); +} + +/** +* Groups commits by subpackages. +* +* @private +* @param {ObjectArray} commits - commits +* @param {string} pkg - package name +* @returns {Object} grouped commits +*/ +function groupBySubPackage( commits, pkg ) { + var commit; + var group; + var out; + var i; + var j; + + out = {}; + for ( i = 0; i < commits.length; i++ ) { + commit = commits[ i ]; + for ( j = 0; j < commit.files.length; j++ ) { + if ( commit.files[ j ].indexOf( 'lib/node_modules/@stdlib/' ) !== -1 ) { + group = dirname( commit.files[ j ] ); + group = substringAfter( group, ( pkg ) ? pkg + '/' : '@stdlib/' ); + group = replace( group, RE_PACKAGE_SUBDIRS, '' ); + if ( contains( group, '/_tools/' ) ) { + continue; + } + if ( !out[ group ] ) { + out[ group ] = []; + } + if ( !contains( out[ group ], commit ) ) { + out[ group ].push( commit ); + } + } + } + } + return out; +} + + +// MAIN // + +/** +* Generates a Markdown formatted changelog for a specified package. +* +* @param {string} pkg - package name +* @throws {TypeError} must provide a string +* @throws {Error} must provide a valid package name +* @returns {string} changelog contents +* +* @example +* var changelog = generate( 'utils/curry' ); +* // returns '...' +* +* @example +* var changelog = generate( 'stats/base/dists' ); +* // returns '...' +* +* @example +* var changelog = generate( 'proxy' ); +* // returns '...' +*/ +function generate( pkg ) { + var isNamespacePkg; + var releaseCommits; + var bySubpackage; + var standalone; + var unreleased; + var releases; + var pkgNames; + var commits; + var summary; + var date; + var href; + var name; + var out; + var i; + var j; + + if ( !isString( pkg ) ) { + throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) ); + } + if ( pkg === '' ) { + // Case: root package + isNamespacePkg = true; + standalone = '@stdlib/stdlib'; + releases = objectEntries( npmReleases( standalone ) ); + } else { + // Case: all other packages + isNamespacePkg = contains( STDLIB_NAMESPACE_PKGS, '@stdlib/' + pkg ); + standalone = '@stdlib/' + replace( pkg, '/', '-' ); + releases = objectEntries( npmReleases( standalone ) ); + } + + out = ''; + out += '# CHANGELOG'; + out += '\n'; + out += '\n'; + out += '> Package changelog.'; + + commits = parseCommits( pkg ); + if ( commits.length === 0 ) { + throw new Error( 'invalid argument. Unable to parse commits for package: `'+pkg+'`.' ); + } + + // Filter out all commits which only affect internal tooling: + commits = filter( commits, isNotInternalTooling ); + + // Group commits by release: + commits = groupBy( commits, indicator ); + if ( isNamespacePkg ) { + out += heading( 'Unreleased', 2 ); + bySubpackage = groupBySubPackage( commits.unreleased, pkg ); + pkgNames = objectKeys( bySubpackage ).sort(); + out += heading( 'Packages', 3 ); + for ( i = 0; i < pkgNames.length; i++ ) { + name = pkgNames[ i ]; + unreleased = releaseSummary( bySubpackage[ name ], true, true ); + if ( unreleased ) { + out += '
\n\n'; + name = ( ( pkg ) ? '/' + pkg : pkg ) + ( ( name ) ? '/' + name : '' ); + href = STDLIB_REPO_NODE_PATH + '/%40stdlib' + name; + out += ''; + out += '@stdlib' + name + ''; + out += '\n\n'; + out += decrementHeaders( unreleased ); + out += '
\n\n'; + } + } + out += formatContributors( commits.unreleased ); + out += formatCommits( commits.unreleased ); + } else { + unreleased = releaseSummary( commits.unreleased ); + if ( unreleased ) { + out += heading( 'Unreleased', 2 ); + out += unreleased; + } + } + if ( isNamespacePkg ) { + for ( i = releases.length-1; i >= 0; i-- ) { + releaseCommits = commits[ releases[ i ][ 0 ] ]; + if ( !releaseCommits ) { + continue; + } + date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); + out += '## ' + releases[ i ][ 0 ] + ' (' + date + ')'; + out += '\n'; + out += '\n'; + bySubpackage = groupBySubPackage( releaseCommits, pkg ); + pkgNames = objectKeys( bySubpackage ).sort(); + out += heading( 'Packages', 3 ); + for ( j = 0; j < pkgNames.length; j++ ) { + name = pkgNames[ j ]; + summary = releaseSummary( bySubpackage[ name ], true, true ); + if ( !summary ) { + continue; + } + name = pkg + ( ( name ) ? '/' + name : '' ); + href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; + out += '
\n\n'; + out += ''; + out += '@stdlib/' + name + ''; + out += '\n\n'; + out += decrementHeaders( summary ); + out += '
'; + } + out += formatContributors( releaseCommits ); + out += formatCommits( releaseCommits ); + } + } else { + for ( i = releases.length-1; i >= 0; i-- ) { + summary = releaseSummary( commits[ releases[ i ][ 0 ] ] ); + if ( !summary ) { + continue; + } + date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); + out += '## ' + releases[ i ][ 0 ] + ' (' + date + ')'; + out += '\n'; + out += '\n'; + out += summary; + } + } + out = replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); + return out; + + /** + * Indicator function indicating which release a commit belongs to. + * + * @private + * @param {Object} commit - commit object + * @returns {string} release indicator + */ + function indicator( commit ) { + var version; + var date; + var i; + + // Walk the releases in reverse chronological order: + for ( i = releases.length-1; i >= 0; i-- ) { + version = releases[ i ][ 0 ]; + date = releases[ i ][ 1 ]; + if ( new Date( commit.date ) <= new Date( date ) ) { + return version; + } + } + return 'unreleased'; + } + + /** + * Tests whether a commit only affects internal tooling, i.e., files located in the `lib/node_modules/@stdlib/_tools/` directory. + * + * @private + * @param {Object} commit - commit object + * @returns {boolean} boolean indicating whether a commit is not only touching internal tooling + */ + function isNotInternalTooling( commit ) { + var files; + var bool; + var i; + + files = commit.files; + bool = false; + for ( i = 0; i < files.length; i++ ) { + if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) { + bool = true; + break; + } + } + return bool; + } +} + + +// EXPORTS // + +module.exports = generate; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js new file mode 100644 index 00000000000..11ec674cbd0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js @@ -0,0 +1,72 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var shell = require( 'child_process' ).execSync; // eslint-disable-line node/no-sync +var logger = require( 'debug' ); +var omit = require( '@stdlib/utils/omit' ); +var rootDir = require( '@stdlib/_tools/utils/root-dir' ); + + +// VARIABLES // + +var debug = logger( 'changelog:generate' ); +var OMITTED_KEYS = [ 'created', 'modified', 'unpublished' ]; + + +// MAIN // + +/** +* Returns a list of published package versions and their release dates. +* +* @private +* @param {string} pkg - package name +* @returns {Object} object mapping versions to release dates +* +* @example +* var releases = npmReleases( '@stdlib/utils-omit' ); +* // returns {...} +*/ +function npmReleases( pkg ) { + var command; + var opts; + var out; + + command = 'npm view ' + pkg + ' time --json'; + opts = { + 'cwd': rootDir() + }; + try { + out = shell( command, opts ).toString(); + out = JSON.parse( out ); + out = omit( out, OMITTED_KEYS ); + } + catch ( err ) { + debug( 'Encountered an error when attempting to retrieve package release dates: %s', err.message ); + out = {}; + } + return out; +} + + +// EXPORTS // + +module.exports = npmReleases; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js new file mode 100644 index 00000000000..480fedbab29 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -0,0 +1,220 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var logger = require( 'debug' ); +var groupBy = require( '@stdlib/utils/group-by' ); +var filter = require( '@stdlib/array/base/filter' ); +var trim = require( '@stdlib/string/trim' ); +var map = require( '@stdlib/utils/map' ); +var formatCommitFactory = require( './format_commit_factory.js' ); +var formatClosedIssues = require( './format_closed_issues.js' ); +var formatContributors = require( './format_contributors.js' ); +var formatCommits = require( './format_commits.js' ); +var heading = require( './heading.js' ); + + +// VARIABLES // + +var debug = logger( 'changelog:generate:release-summary' ); +var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit'; + + +// FUNCTIONS // + +/** +* Collects all field elements from a list of commits. +* +* @private +* @param {ObjectArray} commits - commit objects +* @param {string} field - field to collect +* @returns {ObjectArray} field elements +*/ +function collectField( commits, field ) { + var out; + var i; + var j; + + out = []; + for ( i = 0; i < commits.length; i++ ) { + for ( j = 0; j < commits[ i ][ field ].length; j++ ) { + out.push( commits[ i ][ field ][ j ] ); + } + } + return out; +} + +/** +* Formats a breaking change. +* +* @private +* @param {Object} note - note object +* @returns {string} changelog entry +* +* @example +* var note = { +* 'title': 'BREAKING CHANGE', +* 'text': 'beep', +* 'hash': 'abcdef1234567890' +* }; +* var out = formatBreakingChange( note ); +* // returns '- [`abcdef1`](https://github.com/stdlib-js/stdlib/commit/abcdef1234567890): beep' +*/ +function formatBreakingChange( note ) { + var parts = note.text.split( '\n' ); + var hash = trim( note.hash ); + var out = '- [`'; + out += hash.substring( 0, 7 ); + out += '`]('; + out += STDLIB_GITHUB_URL; + out += '/'; + out += hash; + out += '): '; + out += parts[ 0 ]; + if ( parts.length > 1 ) { + out +=' \n\n'; + out += ' - '; + out += parts.slice( 1 ).join( '\n ' ); + out += '\n'; + } + return out; +} + + +// MAIN // + +/** +* Generates a release summary. +* +* @private +* @param {ObjectArray} commits - commit objects +* @param {boolean} excludeCommits - boolean indicating whether to exclude commit details +* @param {boolean} excludeContributors - boolean indicating whether to exclude a list of contributors +* @returns {string} summary +*/ +function releaseSummary( commits, excludeCommits, excludeContributors ) { + var breakingChanges; + var groupedFormat; + var closedIssues; + var mentions; + var grouped; + var notes; + var out; + + if ( !commits || !commits.length ) { + return ''; + } + grouped = groupBy( commits, groupByType ); + notes = collectField( commits, 'notes' ); + breakingChanges = filter( notes, isBreakingChange ); + out = ''; + + groupedFormat = formatCommitFactory( false, false ); + if ( grouped.feat ) { + out += heading( 'Features', 3 ); + out += map( grouped.feat, groupedFormat ).join( '\n' ); + } + if ( grouped.fix ) { + out += heading( 'Bug Fixes', 3 ); + out += map( grouped.fix, groupedFormat ).join( '\n' ); + } + if ( grouped.perf ) { + out += heading( 'Performance', 3 ); + out += map( grouped.perf, groupedFormat ).join( '\n' ); + } + if ( grouped.revert ) { + out += heading( 'Reverts', 3 ); + out += map( grouped.revert, groupedFormat ).join( '\n' ); + } + if ( grouped.deprecate ) { + out += heading( 'Deprecations', 3 ); + out += map( grouped.deprecate, groupedFormat ).join( '\n' ); + } + if ( breakingChanges && breakingChanges.length > 0 ) { + debug( 'Found %d breaking changes...', breakingChanges.length ); + out += heading( 'BREAKING CHANGES', 3 ); + out += map( breakingChanges, formatBreakingChange ).join( '\n' ); + } + + mentions = collectField( commits, 'mentions' ); + closedIssues = filter( mentions, isClosingIssue ); + closedIssues.sort( sortByIssueNumber ); + if ( closedIssues && closedIssues.length > 0 ) { + out += formatClosedIssues( closedIssues ); + } + if ( !excludeCommits ) { + out += formatCommits( commits ); + } + if ( !excludeContributors ) { + out += formatContributors( commits ); + } + return out; + + /** + * Groups commits by type. + * + * @private + * @param {Object} commit - commit object + * @returns {(string|null)} group key + */ + function groupByType( commit ) { + return commit.type; + } + + /** + * Tests whether a note is a breaking change. + * + * @private + * @param {Object} note - note object + * @returns {boolean} boolean indicating whether a note is a breaking change + */ + function isBreakingChange( note ) { + return note.title === 'BREAKING CHANGE'; + } + + /** + * Tests whether a mention references closing an issue. + * + * @private + * @param {Object} mention - mention object + * @returns {boolean} boolean indicating whether a mention references closing an issue + */ + function isClosingIssue( mention ) { + return mention.action === 'Closes' || mention.action === 'Fixes' || mention.action === 'Resolves'; + } + + /** + * Sorts issue mentions by issue number. + * + * @private + * @param {Object} a - first mention + * @param {Object} b - second mention + * @returns {number} difference in issue numbers + */ + function sortByIssueNumber( a, b ) { + return a.ref - b.ref; + } +} + + +// EXPORTS // + +module.exports = releaseSummary; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json new file mode 100644 index 00000000000..817037e098d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json @@ -0,0 +1,63 @@ +{ + "name": "@stdlib/_tools/changelog/generate", + "version": "0.0.0", + "description": "Generate a changelog for a specified package.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "bin": {}, + "main": "./lib", + "directories": { + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "tools", + "tool", + "changelog", + "generate", + "generator", + "commit", + "commits", + "conventional", + "markdown", + "md" + ], + "__stdlib__": {} +} diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js new file mode 100644 index 00000000000..472ad5084cd --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -0,0 +1,95 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var generate = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.equal( typeof generate, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if not provided a string', function test( t ) { + var values; + var i; + + values = [ + 5, + null, + true, + void 0, + NaN, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + generate( value ); + }; + } +}); + +tape( 'the function throws an error if provided an unrecognized package name', function test( t ) { + var values; + var i; + + values = [ + 'beep', + 'beep/boop', + 'foo/bar/baz' + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), Error, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + generate( value ); + }; + } +}); + +tape( 'the function returns a string', function test( t ) { + var out = generate( 'utils/curry' ); + t.equal( typeof out, 'string', 'returns a string' ); + + out = generate( 'stats/base/dists' ); + t.equal( typeof out, 'string', 'returns a string' ); + + out = generate( 'proxy' ); + t.equal( typeof out, 'string', 'returns a string' ); + + t.end(); +}); diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md new file mode 100644 index 00000000000..2d941180672 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md @@ -0,0 +1,94 @@ + + +# Parse Commits + +> Parse package commit messages into conventional changelog objects. + +
+ +## Usage + +```javascript +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +``` + +#### parseCommits( pkg ) + +Parses package commit messages into conventional changelog objects. + +```javascript +var commits = parseCommits( 'assert/contains' ); +// returns [...] +``` + +
+ + + +
+ +
+ + + +
+ +## Examples + +```javascript +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); + +// Commits for a non-namespace package: +var commits = parseCommits( 'assert/contains' ); +// returns [...] + +// Commits for a namespace package: +commits = parseCommits( 'math/base/special' ); +// returns [...] + +// Commits for entire project: +commits = parseCommits( '' ); +// returns [...] + +// Commits for non-existent package: +commits = parseCommits( 'beep/boop' ); +// returns [] +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js new file mode 100644 index 00000000000..4cfb6aa8bf4 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js @@ -0,0 +1,45 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var parseCommits = require( './../lib' ); + +// Commits for a non-namespace package: +var commits = parseCommits( 'assert/contains' ); +console.log( 'Commits for `assert/contains`:' ); +console.log( commits ); +// => [...] + +// Commits for a namespace package: +commits = parseCommits( 'math/base/special' ); +console.log( 'Commits for `math/base/special`:' ); +console.log( commits ); +// => [...] + +// Commits for entire project: +commits = parseCommits( '' ); +console.log( 'Commits for entire project:' ); +console.log( commits ); +// => [...] + +// Commits for non-existent package: +commits = parseCommits( 'beep/boop' ); +console.log( 'Commits for `beep/boop`:' ); +console.log( commits ); +// => [] diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js new file mode 100644 index 00000000000..ffb129de2b7 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -0,0 +1,110 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var shell = require( 'child_process' ).execSync; // eslint-disable-line node/no-sync +var replace = require( '@stdlib/string/replace' ); +var rootDir = require( '@stdlib/_tools/utils/root-dir' ); +var trim = require( '@stdlib/string/trim' ); + + +// VARIABLES // + +var GIT_COMMIT_SEP = '$---$'; + + +// FUNCTIONS // + +/** +* Escapes double quotes in a string. +* +* @private +* @param {string} str - input string +* @returns {string} escaped string +* +* @example +* var str = escapeDoubleQuotes( 'beep "boop"' ); +* // returns 'beep \"boop\"' +*/ +function escapeDoubleQuotes( str ) { + return replace( str, '"', '\\"' ); +} + + +// MAIN // + +/** +* Extracts commit details for a specified package. +* +* @private +* @param {string} pkg - package name +* @returns {Array} array of commit details +* +* @example +* var commits = extractCommits( 'utils/curry' ); +* // returns [...] +*/ +function extractCommits( pkg ) { + var commits; + var lines; + var parts; + var opts; + var cmd; + var out; + var i; + + opts = { + 'cwd': rootDir(), + 'maxBuffer': 1024*1024*60 // 60 MB + }; + cmd = 'if [ -d "lib/node_modules/@stdlib/'+pkg+'" ]; then\n'; + cmd = ' git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|"'; + cmd += ' '; + cmd += 'lib/node_modules/@stdlib/'+pkg; + + // Insert a separator between commits: + cmd += ' | '; + cmd += 'awk \'/^$/{p=1;next} /^[0-9a-f]{40}\\|/{if (p==1) print "'+GIT_COMMIT_SEP+'"; p=0} {print}\''; + cmd += 'fi\n'; + + commits = []; + out = trim( shell( cmd, opts ).toString() ); + lines = out.split( GIT_COMMIT_SEP ); + for ( i = 0; i < lines.length; i++ ) { + if ( !lines[ i ] ) { + continue; + } + parts = lines[ i ].split( '|' ); + commits.push({ + 'hash': trim( parts[ 0 ] ), + 'date': parts[ 1 ], + 'author': parts[ 2 ], + 'message': trim( escapeDoubleQuotes( parts[ 3 ] ) ), + 'files': trim( parts[ 4 ] ).split( '\n' ) + }); + } + return commits; +} + + +// EXPORTS // + +module.exports = extractCommits; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js new file mode 100644 index 00000000000..76a351f6cb7 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -0,0 +1,274 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var visitWithAncestors = require( 'unist-util-visit-parents' ); +var visit = require( 'unist-util-visit' ); + + +// VARIABLES // + +var STDLIB_ISSUE_URL = 'https://github.com/stdlib-js/stdlib/issues/'; +var STDLIB_PR_URL = 'https://github.com/stdlib-js/stdlib/pull/'; +var RE_DIGITS = /^[0-9]+$/; + + +// MAIN // + +/** +* Converts a commit message AST to the conventional changelog format. +* +* @private +* @param {Object} ast - unist commit message AST +* @returns {Object} conventional changelog format +*/ +function toConventionalChangelog( ast ) { + var out = { + 'type': '', + 'scope': null, + 'header': '', + 'subject': '', + 'body': '', + 'notes': [], + 'references': [], + 'mentions': [], + 'footer': null + }; + + visit( ast, 'body', onBody ); + visit( ast, 'summary', onSummary ); + visitWithAncestors( ast, [ 'breaking-change' ], processBreakingChanges ); + visit( ast, 'footer', processFooter ); + + return out; + + /** + * Callback invoked upon visiting a breaking change node. + * + * @private + * @param {Object} _ - AST node + * @param {ObjectArray} ancestors - node ancestors + */ + function processBreakingChanges( _, ancestors ) { + var startCollecting = false; + var breaking = { + 'title': 'BREAKING CHANGE', + 'text': '' + }; + var parent = ancestors.pop(); + switch ( parent.type ) { + case 'summary': + breaking.text = out.subject; + break; + case 'body': + breaking.text = ''; + visit( parent, [ 'text', 'breaking-change' ], visitBodyNode ); + break; + case 'token': + parent = ancestors.pop(); + visit( parent, 'text', visitTextNode ); + break; + default: + break; + } + if ( breaking.text !== '' ) { + out.notes.push( breaking ); + } + + /** + * Callback invoked upon visiting a body node. + * + * @private + * @param {Object} node - AST node + */ + function visitBodyNode( node ) { + if ( startCollecting && node.type === 'text' ) { + if ( breaking.text !== '' ) { + breaking.text += '\n'; + } + breaking.text += node.value; + } else if ( node.type === 'breaking-change' ) { + startCollecting = true; + } + } + + /** + * Callback invoked upon visiting a text node. + * + * @private + * @param {Object} node - AST node + */ + function visitTextNode( node ) { + breaking.text = node.value; + } + } + + /** + * Callback invoked upon visiting the body node. + * + * @private + * @param {Object} body - AST node + */ + function onBody( body ) { + if ( body ) { + visit( body, 'text', extractBody ); + } + + /** + * Callback invoked upon visiting a text node. + * + * @private + * @param {Object} node - AST node + */ + function extractBody( node ) { + if ( out.body !== '' ) { + out.body += '\n'; + } + out.body += node.value; + } + } + + /** + * Callback invoked upon visiting the summary node. + * + * @private + * @param {Object} summary - AST node + */ + function onSummary( summary ) { + visit( summary, visitSummary ); + + /** + * Processes a summary node and extracts relevant information. + * + * @private + * @param {Object} node - AST node + */ + function visitSummary( node ) { + switch ( node.type ) { + case 'type': + out.type = node.value; + out.header += node.value; + break; + case 'scope': + out.scope = node.value; + out.header += '(' + node.value + ')'; + break; + case 'breaking-change': + out.header += '!'; + break; + case 'text': + out.subject = node.value; + out.header += ': ' + node.value; + break; + default: + break; + } + } + } + + /** + * Processes a footer node and extracts relevant information. + * + * @private + * @param {Object} node - AST node + */ + function processFooter( node ) { + var hasRefSepartor = false; + var closesIssue = false; + var reference = {}; + var hasPRURL = false; + visit( node, [ 'type', 'separator', 'text' ], processFooterNode ); + if ( hasRefSepartor && reference.ref.match( RE_DIGITS ) ) { + reference.prefix = '#'; + out.references.push( reference ); + } + if ( hasPRURL || closesIssue ) { + out.mentions.push( reference ); + } + + /** + * Processes a footer node and extracts relevant information. + * + * @private + * @param {Object} node - AST node + */ + function processFooterNode( node ) { + switch ( node.type ) { + case 'type': + if ( node.value === 'PR-URL' ) { + hasPRURL = true; + } + else if ( + node.value === 'Closes' || + node.value === 'Fixes' || + node.value === 'Resolves' + ) { + closesIssue = true; + } + reference.action = node.value; + break; + case 'separator': + if ( node.value.includes( '#' ) ) { + hasRefSepartor = true; + } + break; + case 'text': + if ( node.value.includes( STDLIB_ISSUE_URL ) ) { + reference.url = node.value; + reference.ref = node.value.split( '/' ).pop(); + } + else if ( node.value.includes( STDLIB_PR_URL ) ) { + hasPRURL = true; + reference.url = node.value; + reference.ref = node.value.split('/').pop(); + } + else if ( node.value.charAt( 0 ) === '#' ) { + hasRefSepartor = true; + reference.ref = node.value.substring( 1 ); + if ( !reference.url ) { + if ( closesIssue ) { + reference.url = STDLIB_ISSUE_URL + reference.ref; + } else if ( hasPRURL ) { + reference.url = STDLIB_PR_URL + reference.ref; + } + } + } else { + reference.ref = node.value; + if ( !reference.url ) { + if ( closesIssue ) { + reference.url = STDLIB_ISSUE_URL + reference.ref; + } else if ( hasPRURL ) { + reference.url = STDLIB_PR_URL + reference.ref; + } + } + } + break; + default: + break; + } + } + } +} + + +// EXPORTS // + +module.exports = toConventionalChangelog; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js new file mode 100644 index 00000000000..f2504721a77 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js @@ -0,0 +1,40 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Parse package commit messages into conventional changelog objects. +* +* @module @stdlib/_tools/changelog/parse-commits +* +* @example +* var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +* +* var out = parseCommits( 'utils/curry' ); +* // returns [...] +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js new file mode 100644 index 00000000000..1c2ff92221d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -0,0 +1,87 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var logger = require( 'debug' ); +var parser = require( '@conventional-commits/parser' ).parser; +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var format = require( '@stdlib/string/format' ); +var merge = require( '@stdlib/utils/merge' ); +var toConventionalChangelog = require( './conventional_changelog.js' ); +var extractCommits = require( './commits.js' ); + + +// VARIABLES // + +var debug = logger( 'changelog:parse-commits' ); + + +// MAIN // + +/** +* Parses package commit messages into conventional changelog objects. +* +* @param {string} pkg - package name +* @throws {TypeError} must provide a string +* @returns {Array} array of conventional changelog formatted commit message objects +* +* @example +* var out = parseCommits( 'utils/curry' ); +* // returns [...] +*/ +function parseCommits( pkg ) { + var commits; + var merged; + var msg; + var ast; + var out; + var i; + var j; + + if ( !isString( pkg ) ) { + throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) ); + } + commits = extractCommits( pkg ); + out = []; + i = 0; + + for ( i = 0; i < commits.length; i++ ) { + try { + msg = commits[ i ].message; + ast = parser( msg ); + merged = merge( commits[ i ], toConventionalChangelog( ast ) ); + for ( j = 0; j < merged.notes.length; j++ ) { + merged.notes[ j ].hash = commits[ i ].hash; + } + out.push( merged ); + } + catch ( err ) { + debug( 'Encountered an error when parsing a commit message: %s. Message: %s', err.message, commits[ i ] ); + continue; + } + } + return out; +} + + +// EXPORTS // + +module.exports = parseCommits; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json new file mode 100644 index 00000000000..d49763beca0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json @@ -0,0 +1,62 @@ +{ + "name": "@stdlib/_tools/changelog/parse-commits", + "version": "0.0.0", + "description": "Parse package commit messages into conventional changelog objects.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "bin": {}, + "main": "./lib", + "directories": { + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "tools", + "tool", + "changelog", + "parse", + "parser", + "commits", + "conventional", + "metadata", + "git" + ], + "__stdlib__": {} +} diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js new file mode 100644 index 00000000000..bc06a230ce6 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -0,0 +1,95 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var isPlainObject = require( '@stdlib/assert/is-plain-object' ); +var isArray = require( '@stdlib/assert/is-array' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var parseCommits = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.equal( typeof parseCommits, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function throws an error if not provided a string', function test( t ) { + var values; + var i; + + values = [ + 5, + null, + true, + void 0, + NaN, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + parseCommits( value ); + }; + } +}); + +tape( 'the function returns an empty array if provided a string not matching a package directory', function test( t ) { + t.deepEqual( parseCommits( 'beep/boop' ), [], 'returns empty array' ); + t.deepEqual( parseCommits( 'foo/bar/baz' ), [], 'returns empty array' ); + t.end(); +}); + +tape( 'the function returns an array of conventional changelog formatted commit message objects', function test( t ) { + var commits; + var commit; + var i; + + commits = parseCommits( 'utils/curry' ); + t.strictEqual( isArray( commits ), true, 'returns an array' ); + t.strictEqual( commits.length > 0, true, 'has length greater than 0' ); + for ( i = 0; i < commits.length; i++ ) { + commit = commits[ i ]; + t.strictEqual( isPlainObject( commit ), true, 'element is an object' ); + t.strictEqual( hasOwnProp( commit, 'hash' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'date' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'author' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'message' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'type' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'scope' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'subject' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'body' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'footer' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'notes' ), true, 'has property' ); + t.strictEqual( hasOwnProp( commit, 'references' ), true, 'has property' ); + } + t.end(); +}); From a2b4544b64398bc2648cd6db2c20d1e07f43bebe Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 28 Jan 2024 18:25:50 -0500 Subject: [PATCH 02/46] build: add new dev dependencies --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 7ea5feb1315..185d0630f6e 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "0x": "^4.10.2", "@commitlint/cli": "^17.4.4", "@commitlint/cz-commitlint": "^17.4.4", + "@conventional-commits/parser": "^0.4.1", "@kaciras/deasync": "^1.0.1", "@types/node": "^13.9.0", "@typescript-eslint/parser": "^6.9.1", @@ -245,6 +246,7 @@ "uglifyify": "^5.0.0", "unified-lint-rule": "^1.0.1", "unist-util-visit": "^2.0.0", + "unist-util-visit-parents": "^3.1.1", "yaml": "^1.0.0" }, "engines": { From 0f25e8b42cf12af404e52b3649f6986b657a2ab3 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 30 Jan 2024 16:22:21 -0500 Subject: [PATCH 03/46] build: add section tags --- .../generate/lib/format_closed_issues.js | 6 +++- .../changelog/generate/lib/format_commits.js | 5 ++- .../generate/lib/format_contributors.js | 5 ++- .../_tools/changelog/generate/lib/main.js | 31 ++++++++++++++++--- .../changelog/generate/lib/release_summary.js | 18 +++++++++++ 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js index 8016e1ac0b0..9c2b56784fe 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js @@ -21,6 +21,7 @@ // MODULES // var map = require( '@stdlib/utils/map' ); +var heading = require( './heading.js' ); // FUNCTIONS // @@ -54,7 +55,8 @@ function formatIssue( mention ) { * @returns {string} formatted issues list */ function formatClosedIssues( closedIssues ) { - var out = '### Closed Issues\n\n'; + var out = '
\n\n'; + out += heading( 'Closed Issues', 3 ); if ( closedIssues.length === 1 ) { out += 'This release closes the following issue:\n\n'; } else { @@ -62,6 +64,8 @@ function formatClosedIssues( closedIssues ) { } out += map( closedIssues, formatIssue ).join( ', ' ); out += '\n\n'; + out += '
\n\n'; + out += '\n\n'; return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js index 537dbe79e66..ccea5341cd9 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js @@ -35,10 +35,13 @@ var heading = require( './heading.js' ); * @returns {string} Markdown string */ function formatCommits( commits ) { - var out = heading( 'Commits', 3 ); + var out = '\n\n
'; + out += heading( 'Commits', 3 ); out += '
\n\n'; out += map( commits, formatCommitFactory( true, true ) ).join( '\n' ); out += '\n\n
\n\n'; + out += '
\n\n'; + out += '\n\n'; return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index 53e267b1538..61882d7366a 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -81,7 +81,8 @@ function formatContributor( contributor ) { */ function formatContributors( commits ) { var contributors = extractContributors( commits ); - var out = heading( 'Contributors', 3 ); + var out = '\n\n
'; + out += heading( 'Contributors', 3 ); if ( contributors.length === 1 ) { out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n'; } @@ -90,6 +91,8 @@ function formatContributors( commits ) { } out += map( contributors, formatContributor ).join( '\n' ); out += '\n\n'; + out += '
\n\n'; + out += '\n\n'; return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index fda3db81dc5..5c289aae1df 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -164,6 +164,8 @@ function generate( pkg ) { out += '\n'; out += '\n'; out += '> Package changelog.'; + out += '\n'; + out += '\n'; commits = parseCommits( pkg ); if ( commits.length === 0 ) { @@ -176,31 +178,43 @@ function generate( pkg ) { // Group commits by release: commits = groupBy( commits, indicator ); if ( isNamespacePkg ) { + out += '
\n\n'; out += heading( 'Unreleased', 2 ); bySubpackage = groupBySubPackage( commits.unreleased, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); + out += '
\n\n'; out += heading( 'Packages', 3 ); for ( i = 0; i < pkgNames.length; i++ ) { name = pkgNames[ i ]; unreleased = releaseSummary( bySubpackage[ name ], true, true ); if ( unreleased ) { + name = pkg + ( ( name ) ? '/' + name : '' ); + out += '
\n\n'; out += '
\n\n'; - name = ( ( pkg ) ? '/' + pkg : pkg ) + ( ( name ) ? '/' + name : '' ); - href = STDLIB_REPO_NODE_PATH + '/%40stdlib' + name; + href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; out += ''; - out += '@stdlib' + name + ''; + out += '@stdlib/' + name + ''; out += '\n\n'; out += decrementHeaders( unreleased ); out += '
\n\n'; + out += '
\n\n'; + out += '\n\n'; } } + out += '
\n\n'; + out += '\n\n'; out += formatContributors( commits.unreleased ); out += formatCommits( commits.unreleased ); + out += '
\n\n'; + out += '\n\n'; } else { unreleased = releaseSummary( commits.unreleased ); if ( unreleased ) { + out += '
\n\n'; out += heading( 'Unreleased', 2 ); out += unreleased; + out += '
\n\n'; + out += '\n\n'; } } if ( isNamespacePkg ) { @@ -215,6 +229,7 @@ function generate( pkg ) { out += '\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); + out += '
\n\n'; out += heading( 'Packages', 3 ); for ( j = 0; j < pkgNames.length; j++ ) { name = pkgNames[ j ]; @@ -224,13 +239,18 @@ function generate( pkg ) { } name = pkg + ( ( name ) ? '/' + name : '' ); href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; + out += '
\n\n'; out += '
\n\n'; out += ''; out += '@stdlib/' + name + ''; out += '\n\n'; out += decrementHeaders( summary ); - out += '
'; + out += '\n\n'; + out += '
\n\n'; + out += '\n\n'; } + out += '
\n\n'; + out += '\n\n'; out += formatContributors( releaseCommits ); out += formatCommits( releaseCommits ); } @@ -241,10 +261,13 @@ function generate( pkg ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); + out += '
\n\n'; out += '## ' + releases[ i ][ 0 ] + ' (' + date + ')'; out += '\n'; out += '\n'; out += summary; + out += '
\n\n'; + out += '\n\n'; } } out = replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js index 480fedbab29..ea56a0d0446 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -129,29 +129,47 @@ function releaseSummary( commits, excludeCommits, excludeContributors ) { groupedFormat = formatCommitFactory( false, false ); if ( grouped.feat ) { + out += '\n\n
\n\n'; out += heading( 'Features', 3 ); out += map( grouped.feat, groupedFormat ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } if ( grouped.fix ) { + out += '\n\n
\n\n'; out += heading( 'Bug Fixes', 3 ); out += map( grouped.fix, groupedFormat ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } if ( grouped.perf ) { + out += '\n\n
\n\n'; out += heading( 'Performance', 3 ); out += map( grouped.perf, groupedFormat ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } if ( grouped.revert ) { + out += '\n\n
\n\n'; out += heading( 'Reverts', 3 ); out += map( grouped.revert, groupedFormat ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } if ( grouped.deprecate ) { + out += '\n\n
\n\n'; out += heading( 'Deprecations', 3 ); out += map( grouped.deprecate, groupedFormat ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } if ( breakingChanges && breakingChanges.length > 0 ) { debug( 'Found %d breaking changes...', breakingChanges.length ); + out += '\n\n
\n\n'; out += heading( 'BREAKING CHANGES', 3 ); out += map( breakingChanges, formatBreakingChange ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; } mentions = collectField( commits, 'mentions' ); From c73ed53ed7f2e6efa3f7695f7c66a2e471bbab89 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 30 Jan 2024 21:32:30 -0500 Subject: [PATCH 04/46] build: add version suffix to ids and use kebab case consistently --- .../generate/lib/format_commit_factory.js | 12 +- .../_tools/changelog/generate/lib/heading.js | 10 +- .../_tools/changelog/generate/lib/main.js | 115 +++++++++--------- 3 files changed, 65 insertions(+), 72 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js index 30824618977..15333574823 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js @@ -67,25 +67,19 @@ function formatCommitFactory( type, author ) { out += '**'; out += commit.type; if ( commit.scope ) { - out += '('; - out += commit.scope; - out += ')'; + out += '(' + commit.scope + ')'; } out += ':** '; } else if ( commit.scope ) { - out += '**'; - out += commit.scope; - out += ':** '; + out += '**' + commit.scope + ':** '; } out += commit.subject; if ( commit.mentions && commit.mentions.length > 0 ) { commit.mentions.forEach( onMention ); } if ( author ) { - out += ' _(by '; - out += replace( commit.author, RE_EMAIL, '' ); - out += ')_'; + out += ' _(by ' + replace( commit.author, RE_EMAIL, '' ) + ')_'; } return out; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js index 0102d24f03c..04394a1ebdb 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js @@ -42,13 +42,9 @@ var repeat = require( '@stdlib/string/repeat' ); * // returns '\n\n### boop\n\n' */ function heading( text, level ) { - var str; - str = '\n'; - str += '\n'; - str += repeat( '#', level ); - str += ' ' + text; - str += '\n'; - str += '\n'; + var str = '\n\n'; + str += repeat( '#', level ) + ' ' + text; + str += '\n\n'; return str; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 5c289aae1df..84634a6decf 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -43,7 +43,7 @@ var heading = require( './heading.js' ); var STDLIB_NAMESPACE_PKGS = namespaces(); var STDLIB_REPO_NODE_PATH = 'https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules'; -var RE_PACKAGE_SUBDIRS = /\/?(benchmark|bin|data|docs|docs|etc|examples|include|lib|scripts|src|test)\/?[\s\S]*$/; +var RE_PACKAGE_SUBDIRS = /\/?(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)\/?[\s\S]*$/; var RE_MARKDOWN_HEADER = /(^#+)/gm; var RE_EXTRANEOUS_NEWLINES = /\n{3,}/g; @@ -104,6 +104,29 @@ function groupBySubPackage( commits, pkg ) { return out; } +/** +* Tests whether a commit only affects internal tooling, i.e., files located in the `lib/node_modules/@stdlib/_tools/` directory. +* +* @private +* @param {Object} commit - commit object +* @returns {boolean} boolean indicating whether a commit is not only touching internal tooling +*/ +function isNotInternalTooling( commit ) { + var files; + var bool; + var i; + + files = commit.files; + bool = false; + for ( i = 0; i < files.length; i++ ) { + if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) { + bool = true; + break; + } + } + return bool; +} + // MAIN // @@ -136,9 +159,9 @@ function generate( pkg ) { var releases; var pkgNames; var commits; + var version; var summary; var date; - var href; var name; var out; var i; @@ -159,13 +182,8 @@ function generate( pkg ) { releases = objectEntries( npmReleases( standalone ) ); } - out = ''; - out += '# CHANGELOG'; - out += '\n'; - out += '\n'; - out += '> Package changelog.'; - out += '\n'; - out += '\n'; + out = '# CHANGELOG\n\n'; + out += '> Package changelog.\n\n'; commits = parseCommits( pkg ); if ( commits.length === 0 ) { @@ -188,17 +206,7 @@ function generate( pkg ) { name = pkgNames[ i ]; unreleased = releaseSummary( bySubpackage[ name ], true, true ); if ( unreleased ) { - name = pkg + ( ( name ) ? '/' + name : '' ); - out += '
\n\n'; - out += '
\n\n'; - href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; - out += ''; - out += '@stdlib/' + name + ''; - out += '\n\n'; - out += decrementHeaders( unreleased ); - out += '
\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += packageSummaryWrapper( pkg, 'unreleased', name, unreleased ); } } out += '\n\n'; @@ -219,14 +227,13 @@ function generate( pkg ) { } if ( isNamespacePkg ) { for ( i = releases.length-1; i >= 0; i-- ) { - releaseCommits = commits[ releases[ i ][ 0 ] ]; + version = releases[ i ][ 0 ]; + releaseCommits = commits[ version ]; if ( !releaseCommits ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '## ' + releases[ i ][ 0 ] + ' (' + date + ')'; - out += '\n'; - out += '\n'; + out += '## ' + version + ' (' + date + ')\n\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); out += '
\n\n'; @@ -237,17 +244,7 @@ function generate( pkg ) { if ( !summary ) { continue; } - name = pkg + ( ( name ) ? '/' + name : '' ); - href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; - out += '
\n\n'; - out += '
\n\n'; - out += ''; - out += '@stdlib/' + name + ''; - out += '\n\n'; - out += decrementHeaders( summary ); - out += '
\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += packageSummaryWrapper( pkg, version, name, summary ); } out += '
\n\n'; out += '\n\n'; @@ -256,15 +253,14 @@ function generate( pkg ) { } } else { for ( i = releases.length-1; i >= 0; i-- ) { - summary = releaseSummary( commits[ releases[ i ][ 0 ] ] ); + version = releases[ i ][ 0 ]; + summary = releaseSummary( commits[ version ] ); if ( !summary ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '
\n\n'; - out += '## ' + releases[ i ][ 0 ] + ' (' + date + ')'; - out += '\n'; - out += '\n'; + out += '
\n\n'; + out += '## ' + version + ' (' + date + ')\n\n'; out += summary; out += '
\n\n'; out += '\n\n'; @@ -297,26 +293,33 @@ function generate( pkg ) { } /** - * Tests whether a commit only affects internal tooling, i.e., files located in the `lib/node_modules/@stdlib/_tools/` directory. + * Wraps a package summary for inclusion in a namespace changelog. * * @private - * @param {Object} commit - commit object - * @returns {boolean} boolean indicating whether a commit is not only touching internal tooling + * @param {string} pkg - namespace package + * @param {string} version - namespace version + * @param {string} name - subpackage name + * @param {string} summary - package summary + * @returns {string} wrapped package summary */ - function isNotInternalTooling( commit ) { - var files; - var bool; - var i; + function packageSummaryWrapper( pkg, version, name, summary ) { + var href; + var out; + var id; - files = commit.files; - bool = false; - for ( i = 0; i < files.length; i++ ) { - if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) { - bool = true; - break; - } - } - return bool; + name = pkg + ( ( name ) ? '/' + name : '' ); + href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; + id = replace( name, '/', '-' ) + '-' + version; + out = '
\n\n'; + out += '
\n\n'; + out += ''; + out += '@stdlib/' + name + ''; + out += '\n\n'; + out += decrementHeaders( summary ); + out += '
\n\n'; + out += '
\n\n'; + out += '\n\n'; + return out; } } From 674736d1d15e6bac7b30e274c136c64783a7e7f2 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 31 Jan 2024 22:00:34 -0500 Subject: [PATCH 05/46] build: add parameter to generate changelog for new release --- .../changelog/generate/examples/index.js | 5 + .../_tools/changelog/generate/lib/main.js | 103 +++++++++++------- .../changelog/generate/lib/npm_releases.js | 3 +- .../_tools/changelog/generate/test/test.js | 39 ++++++- .../changelog/parse-commits/lib/commits.js | 3 +- 5 files changed, 113 insertions(+), 40 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js index cab7c69cedc..aaf0a752bf6 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js @@ -25,6 +25,11 @@ var changelog = generate( 'utils/curry' ); console.log( changelog ); // => '...' +// Generate a changelog for a new release: +changelog = generate( 'utils/curry', 'patch' ); +console.log( changelog ); +// => '...' + // Generate a changelog for a namespace package: changelog = generate( 'string/base' ); console.log( changelog ); diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 84634a6decf..1cb6f98e086 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -21,17 +21,20 @@ // MODULES // var dirname = require( 'path' ).dirname; +var semver = require( 'semver' ); var substringAfter = require( '@stdlib/string/substring-after' ); var objectEntries = require( '@stdlib/utils/entries' ); var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); var objectKeys = require( '@stdlib/utils/keys' ); var namespaces = require( '@stdlib/_tools/pkgs/namespaces' ).sync; +var lowercase = require( '@stdlib/string/lowercase' ); var contains = require( '@stdlib/assert/contains' ); var isString = require( '@stdlib/assert/is-string' ).isPrimitive; var replace = require( '@stdlib/string/replace' ); var groupBy = require( '@stdlib/utils/group-by' ); var filter = require( '@stdlib/array/base/filter' ); var format = require( '@stdlib/string/format' ); +var isNull = require( '@stdlib/assert/is-null' ); var formatContributors = require( './format_contributors.js' ); var releaseSummary = require( './release_summary.js' ); var formatCommits = require( './format_commits.js' ); @@ -66,6 +69,17 @@ function decrementHeaders( str ) { return replace( str, RE_MARKDOWN_HEADER, '$1#' ); } +/** +* Formats a given date as a string in the format YYYY-MM-DD. +* +* @private +* @param {(string|integer)} [date] - date to format (default: current date) +* @returns {string} formatted date string in YYYY-MM-DD format +*/ +function formatDate( date ) { + return new Date( date || Date.now() ).toISOString().slice( 0, 10 ); +} + /** * Groups commits by subpackages. * @@ -127,6 +141,36 @@ function isNotInternalTooling( commit ) { return bool; } +/** +* Wraps a package summary for inclusion in a namespace changelog. +* +* @private +* @param {string} pkg - namespace package +* @param {string} version - namespace version +* @param {string} name - subpackage name +* @param {string} summary - package summary +* @returns {string} wrapped package summary +*/ +function packageSummaryWrapper( pkg, version, name, summary ) { + var href; + var out; + var id; + + name = pkg + ( ( name ) ? '/' + name : '' ); + href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; + id = replace( name, '/', '-' ) + '-v' + version; + out = '
\n\n'; + out += '
\n\n'; + out += ''; + out += '@stdlib/' + name + ''; + out += '\n\n'; + out += decrementHeaders( summary ); + out += '
\n\n'; + out += '
\n\n'; + out += '\n\n'; + return out; +} + // MAIN // @@ -134,8 +178,10 @@ function isNotInternalTooling( commit ) { * Generates a Markdown formatted changelog for a specified package. * * @param {string} pkg - package name +* @param {string} [releaseType] - release type (e.g., `patch`, `minor`, `major`) * @throws {TypeError} must provide a string * @throws {Error} must provide a valid package name +* @throws {TypeError} must provide a recognized release type * @returns {string} changelog contents * * @example @@ -150,10 +196,12 @@ function isNotInternalTooling( commit ) { * var changelog = generate( 'proxy' ); * // returns '...' */ -function generate( pkg ) { +function generate( pkg, releaseType ) { var isNamespacePkg; var releaseCommits; + var newestRelease; var bySubpackage; + var nextVersion; var standalone; var unreleased; var releases; @@ -195,9 +243,20 @@ function generate( pkg ) { // Group commits by release: commits = groupBy( commits, indicator ); + + if ( releaseType ) { + newestRelease = releases[ releases.length-1 ][ 0 ]; + nextVersion = semver.inc( newestRelease, releaseType ); + if ( isNull( nextVersion ) ) { + throw new TypeError( 'invalid argument. Unrecognized release type: `'+releaseType+'`.' ); + } + } else { + nextVersion = 'Unreleased'; + } + if ( isNamespacePkg ) { out += '
\n\n'; - out += heading( 'Unreleased', 2 ); + out += '## ' + nextVersion + ' (' + formatDate() + ')\n\n'; bySubpackage = groupBySubPackage( commits.unreleased, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); out += '
\n\n'; @@ -218,8 +277,8 @@ function generate( pkg ) { } else { unreleased = releaseSummary( commits.unreleased ); if ( unreleased ) { - out += '
\n\n'; - out += heading( 'Unreleased', 2 ); + out += '
\n\n'; + out += '## ' + nextVersion + ' (' + formatDate() + ')\n\n'; out += unreleased; out += '
\n\n'; out += '\n\n'; @@ -233,7 +292,7 @@ function generate( pkg ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '## ' + version + ' (' + date + ')\n\n'; + out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); out += '
\n\n'; @@ -259,8 +318,8 @@ function generate( pkg ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '
\n\n'; - out += '## ' + version + ' (' + date + ')\n\n'; + out += '
\n\n'; + out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; out += summary; out += '
\n\n'; out += '\n\n'; @@ -291,36 +350,6 @@ function generate( pkg ) { } return 'unreleased'; } - - /** - * Wraps a package summary for inclusion in a namespace changelog. - * - * @private - * @param {string} pkg - namespace package - * @param {string} version - namespace version - * @param {string} name - subpackage name - * @param {string} summary - package summary - * @returns {string} wrapped package summary - */ - function packageSummaryWrapper( pkg, version, name, summary ) { - var href; - var out; - var id; - - name = pkg + ( ( name ) ? '/' + name : '' ); - href = STDLIB_REPO_NODE_PATH + '/%40stdlib/' + name; - id = replace( name, '/', '-' ) + '-' + version; - out = '
\n\n'; - out += '
\n\n'; - out += ''; - out += '@stdlib/' + name + ''; - out += '\n\n'; - out += decrementHeaders( summary ); - out += '
\n\n'; - out += '
\n\n'; - out += '\n\n'; - return out; - } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js index 11ec674cbd0..7d74e33c2b6 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js @@ -52,7 +52,8 @@ function npmReleases( pkg ) { command = 'npm view ' + pkg + ' time --json'; opts = { - 'cwd': rootDir() + 'cwd': rootDir(), + 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr }; try { out = shell( command, opts ).toString(); diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js index 472ad5084cd..04efc2cbdb3 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -81,7 +81,31 @@ tape( 'the function throws an error if provided an unrecognized package name', f } }); -tape( 'the function returns a string', function test( t ) { +tape( 'the function throws an error if provided an invalid release type', function test( t ) { + var values; + var i; + + values = [ + 'massive', + 'big', + 'beep', + 'boop', + 'patching' + ]; + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[i] ), Error, 'throws an error when provided '+values[i] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + generate( 'utils/curry', value ); + }; + } +}); + +tape( 'the function returns a changelog', function test( t ) { var out = generate( 'utils/curry' ); t.equal( typeof out, 'string', 'returns a string' ); @@ -93,3 +117,16 @@ tape( 'the function returns a string', function test( t ) { t.end(); }); + +tape( 'the function returns a changelog for a new release', function test( t ) { + var out = generate( 'utils/noop', 'patch' ); + t.equal( typeof out, 'string', 'returns a string' ); + + out = generate( 'utils/noop', 'minor' ); + t.equal( typeof out, 'string', 'returns a string' ); + + out = generate( 'utils/noop', 'major' ); + t.equal( typeof out, 'string', 'returns a string' ); + + t.end(); +}); diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js index ffb129de2b7..c164d4f4123 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -73,7 +73,8 @@ function extractCommits( pkg ) { opts = { 'cwd': rootDir(), - 'maxBuffer': 1024*1024*60 // 60 MB + 'maxBuffer': 1024*1024*60, // 60 MB + 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr }; cmd = 'if [ -d "lib/node_modules/@stdlib/'+pkg+'" ]; then\n'; cmd = ' git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|"'; From 3695e2d73c3e02f378c6e7ba23277970584a592e Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 31 Jan 2024 22:18:51 -0500 Subject: [PATCH 06/46] build: generate changelog in publish script --- .../@stdlib/_tools/scripts/publish_packages.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js index 3ffabe2f10b..fb58d4c02c5 100644 --- a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js +++ b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js @@ -57,6 +57,7 @@ var currentYear = require( '@stdlib/time/current-year' ); var namespaceDeps = require( '@stdlib/_tools/pkgs/namespace-deps' ); var depList = require( '@stdlib/_tools/pkgs/dep-list' ); var name2standalone = require( '@stdlib/_tools/pkgs/name2standalone' ); +var generateChangelog = require( '@stdlib/_tools/changelog/generate' ); var toposort = require( '@stdlib/_tools/pkgs/toposort' ).sync; var ENV = require( '@stdlib/process/env' ); @@ -204,13 +205,6 @@ var FUNDING = { 'type': 'opencollective', 'url': 'https://opencollective.com/stdlib' }; -var CHANGELOG = [ - '# CHANGELOG', - '', - '> Package changelog.', - '', - 'See [GitHub Releases](https://github.com/stdlib-js//releases) for the changelog.' -].join( '\n' ); var BRANCHES = [ '\n\n'; @@ -318,7 +334,7 @@ function generate( pkg, releaseType ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '
\n\n'; + out += releaseSection( version ); out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; out += summary; out += '
\n\n'; From 7e5af81801d704a8ce06b666b19635cab0e250b6 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Wed, 31 Jan 2024 22:42:28 -0500 Subject: [PATCH 08/46] build: rewrite changelog in standalone repo publish action --- .../@stdlib/_tools/scripts/templates/workflow_publish.yml.txt | 4 ++++ .../scripts/templates/workflow_publish_toplevel.yml.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish.yml.txt b/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish.yml.txt index 13450246859..e92495edeee 100644 --- a/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish.yml.txt +++ b/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish.yml.txt @@ -94,6 +94,10 @@ jobs: # Replace branch in README.md link definitions for badges with the new version: find . -type f -name '*.md' -print0 | xargs -0 sed -Ei "s/branch([=:])[^ ]+/branch\1v${NEW_VERSION}/g" + # Rewrite CHANGELOG.md to replace "Unreleased" with the new version: + sed -Ei "s/Unreleased/${NEW_VERSION}/g" CHANGELOG.md + sed -Ei "s/unreleased/v${NEW_VERSION}/g" CHANGELOG.md + # Create a new commit and tag: git add package.json README.md git commit -m "Release v${NEW_VERSION}" diff --git a/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish_toplevel.yml.txt b/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish_toplevel.yml.txt index 4a7093fee4f..1a6efce9862 100644 --- a/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish_toplevel.yml.txt +++ b/lib/node_modules/@stdlib/_tools/scripts/templates/workflow_publish_toplevel.yml.txt @@ -94,6 +94,10 @@ jobs: # Replace branch in README.md link definitions for badges with the new version: find . -type f -name '*.md' -print0 | xargs -0 sed -Ei "s/branch([=:])[^ ]+/branch\1v${NEW_VERSION}/g" + # Replace `Unreleased` in the CHANGELOG.md file with the new version: + sed -Ei "s/Unreleased/${NEW_VERSION}/g" CHANGELOG.md + sed -Ei "s/unreleased/v${NEW_VERSION}/g" CHANGELOG.md + # Create a new commit and tag: git add package.json README.md git commit -m "Release v${NEW_VERSION}" From 1caa4dde659486af05ba2451511d8c352e1e7494 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 2 Feb 2024 10:29:10 -0500 Subject: [PATCH 09/46] build: refactor to also include breaking changes and closed issues at top-level for namespace packages --- .../_tools/changelog/generate/README.md | 21 ++- .../generate/lib/breaking_changes.js | 109 ++++++++++++++ ...rmat_closed_issues.js => closed_issues.js} | 55 ++++++- .../changelog/generate/lib/collect_field.js | 49 +++++++ .../generate/lib/format_commit_factory.js | 6 +- .../generate/lib/format_contributors.js | 6 +- .../_tools/changelog/generate/lib/main.js | 25 +++- .../changelog/generate/lib/release_summary.js | 134 +----------------- 8 files changed, 258 insertions(+), 147 deletions(-) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js rename lib/node_modules/@stdlib/_tools/changelog/generate/lib/{format_closed_issues.js => closed_issues.js} (57%) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index 88913847e0e..3b465f0b62e 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -30,7 +30,7 @@ limitations under the License. var generate = require( '@stdlib/_tools/changelog/generate' ); ``` -#### generate( pkg ) +#### generate( pkg\[, releaseType] ) Generates a Markdown formatted changelog for a specified package. @@ -39,6 +39,25 @@ var changelog = generate( 'assert/contains' ); // returns '...' ``` +To generate a changelog for an upcoming release, provide a valid release type as the second argument. + +```javascript +var changelog = generate( 'assert/contains', 'patch' ); +// returns '...' + +changelog = generate( 'assert/contains', 'minor' ); +``` + +The following release types are supported: + +- `patch`: a patch release (e.g., bug fixes). +- `minor`: a minor release (e.g., new features which do not break backward compatibility). +- `major`: a major release (e.g., breaking changes). +- `prerelease`: a prerelease (e.g., a release candidate). +- `prepatch`: a prepatch release. +- `preminor`: a preminor release. +- `premajor`: a premajor release. +
diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js new file mode 100644 index 00000000000..45c5032384e --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js @@ -0,0 +1,109 @@ + +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var filter = require( '@stdlib/array/base/filter' ); +var trim = require( '@stdlib/string/trim' ); +var map = require( '@stdlib/utils/map' ); +var collectField = require( './collect_field.js' ); +var heading = require( './heading.js' ); + + +// VARIABLES // + +var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit'; + + +// FUNCTIONS // + +/** +* Formats a breaking change. +* +* @private +* @param {Object} note - note object +* @returns {string} changelog entry +* +* @example +* var note = { +* 'title': 'BREAKING CHANGE', +* 'text': 'beep', +* 'hash': 'abcdef1234567890' +* }; +* var out = formatBreakingChange( note ); +* // returns '- [`abcdef1`](https://github.com/stdlib-js/stdlib/commit/abcdef1234567890): beep' +*/ +function formatBreakingChange( note ) { + var parts = note.text.split( '\n' ); + var hash = trim( note.hash ); + var out = '- [`'+hash.substring( 0, 7 )+'`]('+STDLIB_GITHUB_URL+'/'+hash+'): '+parts[ 0 ]; + if ( parts.length > 1 ) { + out +=' \n\n'; + out += ' - '; + out += parts.slice( 1 ).join( '\n ' ); + out += '\n'; + } + return out; +} + + +// MAIN // + +/** +* Extracts a list of breaking changes from a list of commits and formats the list as a Markdown list. +* +* @private +* @param {ObjectArray} commits - commit objects +* @returns {string} list of breaking changes +*/ +function breakingChanges( commits ) { + var breakingChanges; + var notes; + var out; + + notes = collectField( commits, 'notes' ); + breakingChanges = filter( notes, isBreakingChange ); + if ( !breakingChanges || breakingChanges.length === 0 ) { + return ''; + } + out = '\n\n
\n\n'; + out += heading( 'BREAKING CHANGES', 3 ); + out += map( breakingChanges, formatBreakingChange ).join( '\n' ); + out += '\n\n
\n\n'; + out += '\n\n'; + return out; + + /** + * Tests whether a note is a breaking change. + * + * @private + * @param {Object} note - note object + * @returns {boolean} boolean indicating whether a note is a breaking change + */ + function isBreakingChange( note ) { + return note.title === 'BREAKING CHANGE'; + } +} + + +// EXPORTS // + +module.exports = breakingChanges; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js similarity index 57% rename from lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js rename to lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js index 9c2b56784fe..2073dfc5ac1 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_closed_issues.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js @@ -20,12 +20,37 @@ // MODULES // +var filter = require( '@stdlib/array/base/filter' ); var map = require( '@stdlib/utils/map' ); +var collectField = require( './collect_field.js' ); var heading = require( './heading.js' ); // FUNCTIONS // +/** +* Tests whether a mention references closing an issue. +* +* @private +* @param {Object} mention - mention object +* @returns {boolean} boolean indicating whether a mention references closing an issue +*/ +function isClosingIssue( mention ) { + return mention.action === 'Closes' || mention.action === 'Fixes' || mention.action === 'Resolves'; +} + +/** +* Sorts issue mentions by issue number. +* +* @private +* @param {Object} a - first mention +* @param {Object} b - second mention +* @returns {number} difference in issue numbers +*/ +function sortByIssueNumber( a, b ) { + return a.ref - b.ref; +} + /** * Formats an issue. * @@ -44,11 +69,8 @@ function formatIssue( mention ) { return '[#' + mention.ref + '](' + mention.url + ')'; } - -// MAIN // - /** -* Formats a list of closed issues. +* Formats a list of closed issues as a comma-separated list. * * @private * @param {ObjectArray} closedIssues - issue objects @@ -70,6 +92,29 @@ function formatClosedIssues( closedIssues ) { } +// MAIN // + +/** +* Extracts a list of closed issues from a list of commits and formats it as Markdown. +* +* @private +* @param {ObjectArray} commits - commit objects +* @returns {string} Markdown list of closed issues +*/ +function closedIssues( commits ) { + var closedIssues; + var mentions; + + mentions = collectField( commits, 'mentions' ); + closedIssues = filter( mentions, isClosingIssue ); + closedIssues.sort( sortByIssueNumber ); + if ( !closedIssues || closedIssues.length === 0 ) { + return ''; + } + return formatClosedIssues( closedIssues ); +} + + // EXPORTS // -module.exports = formatClosedIssues; +module.exports = closedIssues; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js new file mode 100644 index 00000000000..b324e969b70 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/collect_field.js @@ -0,0 +1,49 @@ + +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Collects all field elements from a list of commits. +* +* @private +* @param {ObjectArray} commits - commit objects +* @param {string} field - field to collect +* @returns {ObjectArray} field elements +*/ +function collectField( commits, field ) { + var out; + var i; + var j; + + out = []; + for ( i = 0; i < commits.length; i++ ) { + for ( j = 0; j < commits[ i ][ field ].length; j++ ) { + out.push( commits[ i ][ field ][ j ] ); + } + } + return out; +} + + +// EXPORTS // + +module.exports = collectField; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js index 15333574823..db0e22ed234 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js @@ -36,8 +36,8 @@ var RE_EMAIL = /\s*<[^>]+>\s*/; * Returns a function which formats a commit message for inclusion in a changelog. * * @private -* @param {boolean} type - whether to include the commit type -* @param {boolean} author - whether to include the commit author +* @param {boolean} type - boolean indicating whether to include the commit type +* @param {boolean} author - boolean indicating whether to include the commit author * @returns {Function} formatting function * * @example @@ -91,7 +91,7 @@ function formatCommitFactory( type, author ) { */ function onMention( mention ) { if ( mention.action === 'PR-URL' ) { - out += ' [(#' + mention.ref + ')](' + mention.url + ')'; + out += ' [(#' + trim( mention.ref ) + ')](' + mention.url + ')'; } } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index 61882d7366a..bf25e68277c 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -64,9 +64,7 @@ function extractContributors( commits ) { * // returns '- Jane Doe' */ function formatContributor( contributor ) { - var out = '- '; - out += replace( contributor, /\s*<[^>]+>\s*/, '' ); - return out; + return '- ' + replace( contributor, /\s*<[^>]+>\s*/, '' ); } @@ -87,7 +85,7 @@ function formatContributors( commits ) { out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n'; } else { - out += 'A total of '+contributors.length+' people contributed to this release. Thank you to the following contributors:\n\n'; + out += 'A total of ' + contributors.length + ' people contributed to this release. Thank you to the following contributors:\n\n'; } out += map( contributors, formatContributor ).join( '\n' ); out += '\n\n'; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 58fb92ec2fa..1ef8f615731 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -35,6 +35,8 @@ var filter = require( '@stdlib/array/base/filter' ); var format = require( '@stdlib/string/format' ); var isNull = require( '@stdlib/assert/is-null' ); var formatContributors = require( './format_contributors.js' ); +var breakingChanges = require( './breaking_changes.js' ); +var closedIssues = require( './closed_issues.js' ); var releaseSummary = require( './release_summary.js' ); var formatCommits = require( './format_commits.js' ); var npmReleases = require( './npm_releases.js' ); @@ -196,7 +198,7 @@ function packageSummaryWrapper( pkg, version, name, summary ) { * Generates a Markdown formatted changelog for a specified package. * * @param {string} pkg - package name -* @param {string} [releaseType] - release type (e.g., `patch`, `minor`, `major`) +* @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, or `premajor`) * @throws {TypeError} must provide a string * @throws {Error} must provide a valid package name * @throws {TypeError} must provide a recognized release type @@ -213,6 +215,14 @@ function packageSummaryWrapper( pkg, version, name, summary ) { * @example * var changelog = generate( 'proxy' ); * // returns '...' +* +* @example +* var changelog = generate( 'utils/curry', 'patch' ); +* // returns '...' +* +* @example +* var changelog = generate( 'utils/curry', 'major' ); +* // returns '...' */ function generate( pkg, releaseType ) { var isNamespacePkg; @@ -252,8 +262,8 @@ function generate( pkg, releaseType ) { out += '> Package changelog.\n\n'; commits = parseCommits( pkg ); - if ( commits.length === 0 ) { - throw new Error( 'invalid argument. Unable to parse commits for package: `'+pkg+'`.' ); + if ( !commits || commits.length === 0 ) { + throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) ); } // Filter out all commits which only affect internal tooling: @@ -266,7 +276,7 @@ function generate( pkg, releaseType ) { newestRelease = releases[ releases.length-1 ][ 0 ]; nextVersion = semver.inc( newestRelease, releaseType ); if ( isNull( nextVersion ) ) { - throw new TypeError( 'invalid argument. Unrecognized release type: `'+releaseType+'`.' ); + throw new TypeError( format( 'invalid argument. Unrecognized release type: `%s`.', releaseType ) ); } } @@ -286,6 +296,8 @@ function generate( pkg, releaseType ) { } out += '
\n\n'; out += '\n\n'; + out += breakingChanges( commits.unreleased ); + out += closedIssues( commits.unreleased ); out += formatContributors( commits.unreleased ); out += formatCommits( commits.unreleased ); out += '
\n\n'; @@ -323,6 +335,8 @@ function generate( pkg, releaseType ) { } out += '
\n\n'; out += '\n\n'; + out += breakingChanges( releaseCommits ); + out += closedIssues( releaseCommits ); out += formatContributors( releaseCommits ); out += formatCommits( releaseCommits ); } @@ -341,8 +355,7 @@ function generate( pkg, releaseType ) { out += '\n\n'; } } - out = replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); - return out; + return replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); /** * Indicator function indicating which release a commit belongs to. diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js index ea56a0d0446..8440b44e79a 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -20,85 +20,16 @@ // MODULES // -var logger = require( 'debug' ); var groupBy = require( '@stdlib/utils/group-by' ); -var filter = require( '@stdlib/array/base/filter' ); -var trim = require( '@stdlib/string/trim' ); var map = require( '@stdlib/utils/map' ); var formatCommitFactory = require( './format_commit_factory.js' ); -var formatClosedIssues = require( './format_closed_issues.js' ); var formatContributors = require( './format_contributors.js' ); var formatCommits = require( './format_commits.js' ); +var breakingChanges = require( './breaking_changes.js' ); +var closedIssues = require( './closed_issues.js' ); var heading = require( './heading.js' ); -// VARIABLES // - -var debug = logger( 'changelog:generate:release-summary' ); -var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit'; - - -// FUNCTIONS // - -/** -* Collects all field elements from a list of commits. -* -* @private -* @param {ObjectArray} commits - commit objects -* @param {string} field - field to collect -* @returns {ObjectArray} field elements -*/ -function collectField( commits, field ) { - var out; - var i; - var j; - - out = []; - for ( i = 0; i < commits.length; i++ ) { - for ( j = 0; j < commits[ i ][ field ].length; j++ ) { - out.push( commits[ i ][ field ][ j ] ); - } - } - return out; -} - -/** -* Formats a breaking change. -* -* @private -* @param {Object} note - note object -* @returns {string} changelog entry -* -* @example -* var note = { -* 'title': 'BREAKING CHANGE', -* 'text': 'beep', -* 'hash': 'abcdef1234567890' -* }; -* var out = formatBreakingChange( note ); -* // returns '- [`abcdef1`](https://github.com/stdlib-js/stdlib/commit/abcdef1234567890): beep' -*/ -function formatBreakingChange( note ) { - var parts = note.text.split( '\n' ); - var hash = trim( note.hash ); - var out = '- [`'; - out += hash.substring( 0, 7 ); - out += '`]('; - out += STDLIB_GITHUB_URL; - out += '/'; - out += hash; - out += '): '; - out += parts[ 0 ]; - if ( parts.length > 1 ) { - out +=' \n\n'; - out += ' - '; - out += parts.slice( 1 ).join( '\n ' ); - out += '\n'; - } - return out; -} - - // MAIN // /** @@ -111,23 +42,17 @@ function formatBreakingChange( note ) { * @returns {string} summary */ function releaseSummary( commits, excludeCommits, excludeContributors ) { - var breakingChanges; var groupedFormat; - var closedIssues; - var mentions; var grouped; - var notes; var out; if ( !commits || !commits.length ) { return ''; } grouped = groupBy( commits, groupByType ); - notes = collectField( commits, 'notes' ); - breakingChanges = filter( notes, isBreakingChange ); - out = ''; - groupedFormat = formatCommitFactory( false, false ); + + out = ''; if ( grouped.feat ) { out += '\n\n
\n\n'; out += heading( 'Features', 3 ); @@ -163,21 +88,8 @@ function releaseSummary( commits, excludeCommits, excludeContributors ) { out += '\n\n
\n\n'; out += '\n\n'; } - if ( breakingChanges && breakingChanges.length > 0 ) { - debug( 'Found %d breaking changes...', breakingChanges.length ); - out += '\n\n
\n\n'; - out += heading( 'BREAKING CHANGES', 3 ); - out += map( breakingChanges, formatBreakingChange ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; - } - - mentions = collectField( commits, 'mentions' ); - closedIssues = filter( mentions, isClosingIssue ); - closedIssues.sort( sortByIssueNumber ); - if ( closedIssues && closedIssues.length > 0 ) { - out += formatClosedIssues( closedIssues ); - } + out += breakingChanges( commits ); + out += closedIssues( commits ); if ( !excludeCommits ) { out += formatCommits( commits ); } @@ -196,40 +108,6 @@ function releaseSummary( commits, excludeCommits, excludeContributors ) { function groupByType( commit ) { return commit.type; } - - /** - * Tests whether a note is a breaking change. - * - * @private - * @param {Object} note - note object - * @returns {boolean} boolean indicating whether a note is a breaking change - */ - function isBreakingChange( note ) { - return note.title === 'BREAKING CHANGE'; - } - - /** - * Tests whether a mention references closing an issue. - * - * @private - * @param {Object} mention - mention object - * @returns {boolean} boolean indicating whether a mention references closing an issue - */ - function isClosingIssue( mention ) { - return mention.action === 'Closes' || mention.action === 'Fixes' || mention.action === 'Resolves'; - } - - /** - * Sorts issue mentions by issue number. - * - * @private - * @param {Object} a - first mention - * @param {Object} b - second mention - * @returns {number} difference in issue numbers - */ - function sortByIssueNumber( a, b ) { - return a.ref - b.ref; - } } From aa4c5093c0de7e1337f2d956c2af7c6978bf44cc Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 2 Feb 2024 16:18:59 -0500 Subject: [PATCH 10/46] build: use headers for packages in namespace changelogs --- .../_tools/changelog/generate/lib/main.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 1ef8f615731..d5a968d0ef3 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -33,6 +33,7 @@ var replace = require( '@stdlib/string/replace' ); var groupBy = require( '@stdlib/utils/group-by' ); var filter = require( '@stdlib/array/base/filter' ); var format = require( '@stdlib/string/format' ); +var repeat = require( '@stdlib/string/repeat' ); var isNull = require( '@stdlib/assert/is-null' ); var formatContributors = require( './format_contributors.js' ); var breakingChanges = require( './breaking_changes.js' ); @@ -55,19 +56,25 @@ var RE_EXTRANEOUS_NEWLINES = /\n{3,}/g; // FUNCTIONS // /** -* Changes all Markdown headers to be one level lower. +* Decrements all Markdown headers by a specified number of levels. * * @private * @param {string} str - string to modify +* @param {number} n - number of levels to decrement * @returns {string} modified string * * @example * var str = '## Foo\n\n### Bar\n'; -* var out = decrementHeaders( str ); +* var out = decrementHeaders( str, 1 ); * // returns '### Foo\n\n#### Bar\n' +* +* @example +* var str = '# Beep Boop\n\n## Baz Bar'; +* var out = decrementHeaders( str, 2 ); +* // returns '### Beep Boop\n\n#### Baz Bar' */ -function decrementHeaders( str ) { - return replace( str, RE_MARKDOWN_HEADER, '$1#' ); +function decrementHeaders( str, n ) { + return replace( str, RE_MARKDOWN_HEADER, '$1' + repeat( '#', n ) ); } /** @@ -180,11 +187,10 @@ function packageSummaryWrapper( pkg, version, name, summary ) { id += '-unreleased'; } out = '
\n\n'; + out += heading( '[@stdlib/' + name + '](' + href + ')', 4 ); out += '
\n\n'; - out += ''; - out += '@stdlib/' + name + ''; - out += '\n\n'; - out += decrementHeaders( summary ); + out += 'Details\n\n'; + out += decrementHeaders( summary, 2 ); out += '
\n\n'; out += '
\n\n'; out += '\n\n'; From 4e6f72e747c5d24ddbca1e233144af4f6d15386c Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sat, 3 Feb 2024 10:50:35 -0500 Subject: [PATCH 11/46] build: add section tag helpers and minor clean-up --- .../_tools/changelog/generate/README.md | 6 ++- .../generate/lib/breaking_changes.js | 9 +++-- .../changelog/generate/lib/closed_issues.js | 12 +++--- .../changelog/generate/lib/format_commits.js | 9 +++-- .../generate/lib/format_contributors.js | 8 ++-- .../_tools/changelog/generate/lib/main.js | 39 ++++++++----------- .../changelog/generate/lib/release_summary.js | 27 ++++++------- .../changelog/generate/lib/section_end.js | 37 ++++++++++++++++++ .../changelog/generate/lib/section_start.js | 37 ++++++++++++++++++ 9 files changed, 128 insertions(+), 56 deletions(-) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index 3b465f0b62e..9b449f43fcf 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -76,7 +76,11 @@ The following release types are supported: var generate = require( '@stdlib/_tools/changelog/generate' ); // Generate a changelog for a non-namespace package: -var changelog = generate( 'random/array/levy' ); +var changelog = generate( 'utils/curry' ); +// returns '...' + +// Generate a changelog for a new release: +changelog = generate( 'utils/curry', 'patch' ); // returns '...' // Generate a changelog for a namespace package: diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js index 45c5032384e..bac737b48f7 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js @@ -25,6 +25,8 @@ var filter = require( '@stdlib/array/base/filter' ); var trim = require( '@stdlib/string/trim' ); var map = require( '@stdlib/utils/map' ); var collectField = require( './collect_field.js' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); @@ -68,7 +70,7 @@ function formatBreakingChange( note ) { // MAIN // /** -* Extracts a list of breaking changes from a list of commits and formats the list as a Markdown list. +* Extracts breaking changes from a list of commits and formats them as a Markdown list. * * @private * @param {ObjectArray} commits - commit objects @@ -84,11 +86,10 @@ function breakingChanges( commits ) { if ( !breakingChanges || breakingChanges.length === 0 ) { return ''; } - out = '\n\n
\n\n'; + out = sectionStart( 'breaking-changes' ); out += heading( 'BREAKING CHANGES', 3 ); out += map( breakingChanges, formatBreakingChange ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'breaking-changes' ); return out; /** diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js index 2073dfc5ac1..ff4a5eee2e8 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js @@ -23,6 +23,8 @@ var filter = require( '@stdlib/array/base/filter' ); var map = require( '@stdlib/utils/map' ); var collectField = require( './collect_field.js' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); @@ -70,14 +72,14 @@ function formatIssue( mention ) { } /** -* Formats a list of closed issues as a comma-separated list. +* Formats closed issues as a comma-separated list. * * @private * @param {ObjectArray} closedIssues - issue objects * @returns {string} formatted issues list */ function formatClosedIssues( closedIssues ) { - var out = '
\n\n'; + var out = sectionStart( 'issues' ); out += heading( 'Closed Issues', 3 ); if ( closedIssues.length === 1 ) { out += 'This release closes the following issue:\n\n'; @@ -85,9 +87,7 @@ function formatClosedIssues( closedIssues ) { out += 'A total of ' + closedIssues.length + ' issues were closed in this release:\n\n'; } out += map( closedIssues, formatIssue ).join( ', ' ); - out += '\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'issues' ); return out; } @@ -95,7 +95,7 @@ function formatClosedIssues( closedIssues ) { // MAIN // /** -* Extracts a list of closed issues from a list of commits and formats it as Markdown. +* Extracts closed issues from a list of commits and formats them as Markdown. * * @private * @param {ObjectArray} commits - commit objects diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js index ccea5341cd9..93c8ab899cd 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js @@ -22,6 +22,8 @@ var map = require( '@stdlib/utils/map' ); var formatCommitFactory = require( './format_commit_factory.js' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); @@ -35,13 +37,12 @@ var heading = require( './heading.js' ); * @returns {string} Markdown string */ function formatCommits( commits ) { - var out = '\n\n
'; + var out = sectionStart( 'commits' ); out += heading( 'Commits', 3 ); out += '
\n\n'; out += map( commits, formatCommitFactory( true, true ) ).join( '\n' ); - out += '\n\n
\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += '\n\n'; + out += sectionEnd( 'commits' ); return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index bf25e68277c..6a0e78e7072 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -23,6 +23,8 @@ var contains = require( '@stdlib/assert/contains' ); var replace = require( '@stdlib/string/replace' ); var map = require( '@stdlib/utils/map' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' ); @@ -79,7 +81,7 @@ function formatContributor( contributor ) { */ function formatContributors( commits ) { var contributors = extractContributors( commits ); - var out = '\n\n
'; + var out = sectionStart( 'contributors' ); out += heading( 'Contributors', 3 ); if ( contributors.length === 1 ) { out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n'; @@ -88,9 +90,7 @@ function formatContributors( commits ) { out += 'A total of ' + contributors.length + ' people contributed to this release. Thank you to the following contributors:\n\n'; } out += map( contributors, formatContributor ).join( '\n' ); - out += '\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'contributors' ); return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index d5a968d0ef3..386eccd0eed 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -41,6 +41,8 @@ var closedIssues = require( './closed_issues.js' ); var releaseSummary = require( './release_summary.js' ); var formatCommits = require( './format_commits.js' ); var npmReleases = require( './npm_releases.js' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); @@ -89,13 +91,13 @@ function formatDate( date ) { } /** -* Generates a release section tag. +* Generates an opening release section tag. * * @private * @param {string} [version] - release version * @returns {string} release section tag */ -function releaseSection( version ) { +function releaseSectionStart( version ) { if ( version ) { return '
\n\n'; } @@ -189,11 +191,9 @@ function packageSummaryWrapper( pkg, version, name, summary ) { out = '
\n\n'; out += heading( '[@stdlib/' + name + '](' + href + ')', 4 ); out += '
\n\n'; - out += 'Details\n\n'; out += decrementHeaders( summary, 2 ); - out += '
\n\n'; - out += '
\n\n'; - out += '\n\n'; + out += ''; + out += sectionEnd( 'package' ); return out; } @@ -287,11 +287,11 @@ function generate( pkg, releaseType ) { } if ( isNamespacePkg ) { - out += releaseSection( nextVersion ); + out += releaseSectionStart( nextVersion ); out += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; bySubpackage = groupBySubPackage( commits.unreleased, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); - out += '
\n\n'; + out += sectionStart( 'packages' ); out += heading( 'Packages', 3 ); for ( i = 0; i < pkgNames.length; i++ ) { name = pkgNames[ i ]; @@ -300,22 +300,19 @@ function generate( pkg, releaseType ) { out += packageSummaryWrapper( pkg, '', name, unreleased ); } } - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'packages' ); out += breakingChanges( commits.unreleased ); out += closedIssues( commits.unreleased ); out += formatContributors( commits.unreleased ); out += formatCommits( commits.unreleased ); - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'release' ); } else { unreleased = releaseSummary( commits.unreleased ); if ( unreleased ) { - out += releaseSection( nextVersion ); + out += releaseSectionStart( nextVersion ); out += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; out += unreleased; - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'release' ); } } if ( isNamespacePkg ) { @@ -329,7 +326,7 @@ function generate( pkg, releaseType ) { out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); - out += '
\n\n'; + out += sectionStart( 'packages' ); out += heading( 'Packages', 3 ); for ( j = 0; j < pkgNames.length; j++ ) { name = pkgNames[ j ]; @@ -339,8 +336,7 @@ function generate( pkg, releaseType ) { } out += packageSummaryWrapper( pkg, version, name, summary ); } - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'packages' ); out += breakingChanges( releaseCommits ); out += closedIssues( releaseCommits ); out += formatContributors( releaseCommits ); @@ -354,17 +350,16 @@ function generate( pkg, releaseType ) { continue; } date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += releaseSection( version ); + out += releaseSectionStart( version ); out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; out += summary; - out += '
\n\n'; - out += '\n\n'; + out += sectionEnd( 'release' ); } } return replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); /** - * Indicator function indicating which release a commit belongs to. + * Returns an indicator for a commit based on its release status. * * @private * @param {Object} commit - commit object diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js index 8440b44e79a..b260d484a8c 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -27,6 +27,8 @@ var formatContributors = require( './format_contributors.js' ); var formatCommits = require( './format_commits.js' ); var breakingChanges = require( './breaking_changes.js' ); var closedIssues = require( './closed_issues.js' ); +var sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); @@ -54,39 +56,34 @@ function releaseSummary( commits, excludeCommits, excludeContributors ) { out = ''; if ( grouped.feat ) { - out += '\n\n
\n\n'; + out += sectionStart( 'features' ); out += heading( 'Features', 3 ); out += map( grouped.feat, groupedFormat ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'features' ); } if ( grouped.fix ) { - out += '\n\n
\n\n'; + out += sectionStart( 'bug-fixes' ); out += heading( 'Bug Fixes', 3 ); out += map( grouped.fix, groupedFormat ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'bug-fixes' ); } if ( grouped.perf ) { - out += '\n\n
\n\n'; + out += sectionStart( 'performance' ); out += heading( 'Performance', 3 ); out += map( grouped.perf, groupedFormat ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'performance' ); } if ( grouped.revert ) { - out += '\n\n
\n\n'; + out += sectionStart( 'reverts' ); out += heading( 'Reverts', 3 ); out += map( grouped.revert, groupedFormat ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'reverts' ); } if ( grouped.deprecate ) { - out += '\n\n
\n\n'; + out += sectionStart( 'deprecations' ); out += heading( 'Deprecations', 3 ); out += map( grouped.deprecate, groupedFormat ).join( '\n' ); - out += '\n\n
\n\n'; - out += '\n\n'; + out += sectionEnd( 'deprecations' ); } out += breakingChanges( commits ); out += closedIssues( commits ); diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js new file mode 100644 index 00000000000..e8252c6fafc --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_end.js @@ -0,0 +1,37 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Generates a closing section tag. +* +* @private +* @param {string} name - section name +* @returns {string} closing section tag +*/ +function sectionEnd( name ) { + return '\n\n\n\n\n\n'; +} + + +// EXPORTS // + +module.exports = sectionEnd; diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js new file mode 100644 index 00000000000..bd960e8e188 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/section_start.js @@ -0,0 +1,37 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Generates a starting section tag. +* +* @private +* @param {string} name - section name +* @returns {string} starting section tag +*/ +function sectionStart( name ) { + return '\n\n
\n\n'; +} + + +// EXPORTS // + +module.exports = sectionStart; From 26d9275f950184c52977b5d51ae38a5c6f50c7bd Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sat, 3 Feb 2024 11:36:27 -0500 Subject: [PATCH 12/46] build: extract co-authors from footer --- .../generate/lib/format_commit_factory.js | 16 ++++++++++++---- .../generate/lib/format_contributors.js | 19 +++++++++++++++++++ .../lib/conventional_changelog.js | 14 ++++++++------ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js index db0e22ed234..7a97962450d 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js @@ -27,6 +27,7 @@ var trim = require( '@stdlib/string/trim' ); // VARIABLES // var STDLIB_GITHUB_URL = 'https://github.com/stdlib-js/stdlib/commit'; +var RE_CO_AUTHORED_BY = /co-authored-by/i; var RE_EMAIL = /\s*<[^>]+>\s*/; @@ -37,14 +38,14 @@ var RE_EMAIL = /\s*<[^>]+>\s*/; * * @private * @param {boolean} type - boolean indicating whether to include the commit type -* @param {boolean} author - boolean indicating whether to include the commit author +* @param {boolean} includeAuthors - boolean indicating whether to include the commit author(s) * @returns {Function} formatting function * * @example * var f = formatCommitFactory( true, true ); * // returns */ -function formatCommitFactory( type, author ) { +function formatCommitFactory( type, includeAuthors ) { return formatCommit; /** @@ -55,6 +56,7 @@ function formatCommitFactory( type, author ) { * @returns {string} changelog entry */ function formatCommit( commit ) { + var authors = []; var hash = trim( commit.hash ); var out = '- [`'; out += hash.substring( 0, 7 ); @@ -63,6 +65,9 @@ function formatCommitFactory( type, author ) { out += '/'; out += hash; out += ') - '; + if ( commit.author ) { + authors.push( replace( commit.author, RE_EMAIL, '' ) ); + } if ( type ) { out += '**'; out += commit.type; @@ -78,8 +83,8 @@ function formatCommitFactory( type, author ) { if ( commit.mentions && commit.mentions.length > 0 ) { commit.mentions.forEach( onMention ); } - if ( author ) { - out += ' _(by ' + replace( commit.author, RE_EMAIL, '' ) + ')_'; + if ( includeAuthors ) { + out += ' _(by ' + authors.join( ', ' ) + ')_'; } return out; @@ -93,6 +98,9 @@ function formatCommitFactory( type, author ) { if ( mention.action === 'PR-URL' ) { out += ' [(#' + trim( mention.ref ) + ')](' + mention.url + ')'; } + else if ( RE_CO_AUTHORED_BY.test( mention.action ) ) { + authors.push( replace( mention.ref, RE_EMAIL, '' ) ); + } } } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index 6a0e78e7072..1219ef1452d 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -29,6 +29,11 @@ var heading = require( './heading.js' ); var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' ); +// VARIABLES // + +var RE_CO_AUTHORED_BY = /co-authored-by/i; + + // FUNCTIONS // /** @@ -39,8 +44,11 @@ var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' ); * @returns {StringArray} sorted list of contributors */ function extractContributors( commits ) { + var mentions; + var mention; var out; var i; + var j; out = []; for ( i = 0; i < commits.length; i++ ) { @@ -50,6 +58,17 @@ function extractContributors( commits ) { ) { out.push( commits[ i ].author ); } + mentions = commits[ i ].mentions || []; + for ( j = 0; j < mentions.length; j++ ) { + mention = mentions[ j ]; + if ( + RE_CO_AUTHORED_BY.test( mention.action ) && + !contains( out, mention.ref ) && + !contains( EXCLUDED_CONTRIBUTORS, mention.ref ) + ) { + out.push( mentions[ j ].ref ); + } + } } return out.sort(); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js index 76a351f6cb7..cb5907b1e04 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -29,6 +29,8 @@ var visit = require( 'unist-util-visit' ); var STDLIB_ISSUE_URL = 'https://github.com/stdlib-js/stdlib/issues/'; var STDLIB_PR_URL = 'https://github.com/stdlib-js/stdlib/pull/'; var RE_DIGITS = /^[0-9]+$/; +var RE_CLOSES = /(?:closes|fixes|resolves)/i; +var RE_CO_AUTHORED_BY = /co-authored-by/i; // MAIN // @@ -192,6 +194,7 @@ function toConventionalChangelog( ast ) { */ function processFooter( node ) { var hasRefSepartor = false; + var coAuthoredBy = false; var closesIssue = false; var reference = {}; var hasPRURL = false; @@ -200,7 +203,7 @@ function toConventionalChangelog( ast ) { reference.prefix = '#'; out.references.push( reference ); } - if ( hasPRURL || closesIssue ) { + if ( hasPRURL || closesIssue || coAuthoredBy ) { out.mentions.push( reference ); } @@ -216,13 +219,12 @@ function toConventionalChangelog( ast ) { if ( node.value === 'PR-URL' ) { hasPRURL = true; } - else if ( - node.value === 'Closes' || - node.value === 'Fixes' || - node.value === 'Resolves' - ) { + else if ( RE_CLOSES.test( node.value ) ) { closesIssue = true; } + else if ( RE_CO_AUTHORED_BY.test( node.value ) ) { + coAuthoredBy = true; + } reference.action = node.value; break; case 'separator': From b7faceb8a81484f025935f7a69f5e8c7b67deaf5 Mon Sep 17 00:00:00 2001 From: Athan Date: Wed, 14 Feb 2024 23:59:50 -0800 Subject: [PATCH 13/46] Apply suggestions from code review Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/changelog/generate/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index 9b449f43fcf..b0bdee7408f 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -46,6 +46,7 @@ var changelog = generate( 'assert/contains', 'patch' ); // returns '...' changelog = generate( 'assert/contains', 'minor' ); +// returns '...' ``` The following release types are supported: From 97ad340662f243f4992d93954a1777018b502425 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 15 Feb 2024 00:03:11 -0800 Subject: [PATCH 14/46] Apply suggestions from code review Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/changelog/generate/package.json | 1 - .../@stdlib/_tools/changelog/parse-commits/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json index 817037e098d..be7a5be76cd 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json @@ -13,7 +13,6 @@ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" } ], - "bin": {}, "main": "./lib", "directories": { "example": "./examples", diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json index d49763beca0..0650f7acd1a 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json @@ -13,7 +13,6 @@ "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" } ], - "bin": {}, "main": "./lib", "directories": { "example": "./examples", From 89d4f9465108dc885d37c1ca43a7c2e9d1751534 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 15 Feb 2024 00:18:52 -0800 Subject: [PATCH 15/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/parse-commits/lib/main.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js index 1c2ff92221d..1b7cc827482 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -61,8 +61,6 @@ function parseCommits( pkg ) { } commits = extractCommits( pkg ); out = []; - i = 0; - for ( i = 0; i < commits.length; i++ ) { try { msg = commits[ i ].message; @@ -72,10 +70,8 @@ function parseCommits( pkg ) { merged.notes[ j ].hash = commits[ i ].hash; } out.push( merged ); - } - catch ( err ) { + } catch ( err ) { debug( 'Encountered an error when parsing a commit message: %s. Message: %s', err.message, commits[ i ] ); - continue; } } return out; From bbf1966695041708401ffbffc46ec711e44f8413 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 15 Feb 2024 01:04:42 -0800 Subject: [PATCH 16/46] Apply suggestions from code review Signed-off-by: Athan --- .../_tools/changelog/generate/lib/format_commit_factory.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js index 7a97962450d..2f5b8544496 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js @@ -75,8 +75,7 @@ function formatCommitFactory( type, includeAuthors ) { out += '(' + commit.scope + ')'; } out += ':** '; - } - else if ( commit.scope ) { + } else if ( commit.scope ) { out += '**' + commit.scope + ':** '; } out += commit.subject; @@ -97,8 +96,7 @@ function formatCommitFactory( type, includeAuthors ) { function onMention( mention ) { if ( mention.action === 'PR-URL' ) { out += ' [(#' + trim( mention.ref ) + ')](' + mention.url + ')'; - } - else if ( RE_CO_AUTHORED_BY.test( mention.action ) ) { + } else if ( RE_CO_AUTHORED_BY.test( mention.action ) ) { authors.push( replace( mention.ref, RE_EMAIL, '' ) ); } } From 943db179717c5761ce602271ab0bda822137ddc1 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 15 Feb 2024 19:36:42 -0500 Subject: [PATCH 17/46] build: implement suggestions from code review --- .../generate/lib/breaking_changes.js | 22 ++--- .../changelog/generate/lib/closed_issues.js | 4 +- .../_tools/changelog/generate/lib/main.js | 11 ++- .../changelog/generate/lib/release_summary.js | 25 +++--- .../changelog/parse-commits/lib/commits.js | 3 +- .../lib/conventional_changelog.js | 82 +++++++++---------- 6 files changed, 76 insertions(+), 71 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js index bac737b48f7..19eef413e7e 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js @@ -66,6 +66,17 @@ function formatBreakingChange( note ) { return out; } +/** +* Tests whether a note is a breaking change. +* +* @private +* @param {Object} note - note object +* @returns {boolean} boolean indicating whether a note is a breaking change +*/ +function isBreakingChange( note ) { + return note.title === 'BREAKING CHANGE'; +} + // MAIN // @@ -91,17 +102,6 @@ function breakingChanges( commits ) { out += map( breakingChanges, formatBreakingChange ).join( '\n' ); out += sectionEnd( 'breaking-changes' ); return out; - - /** - * Tests whether a note is a breaking change. - * - * @private - * @param {Object} note - note object - * @returns {boolean} boolean indicating whether a note is a breaking change - */ - function isBreakingChange( note ) { - return note.title === 'BREAKING CHANGE'; - } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js index ff4a5eee2e8..fb833c9f62b 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js @@ -107,10 +107,10 @@ function closedIssues( commits ) { mentions = collectField( commits, 'mentions' ); closedIssues = filter( mentions, isClosingIssue ); - closedIssues.sort( sortByIssueNumber ); - if ( !closedIssues || closedIssues.length === 0 ) { + if ( closedIssues.length === 0 ) { return ''; } + closedIssues.sort( sortByIssueNumber ); return formatClosedIssues( closedIssues ); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 386eccd0eed..d2fda0d21c7 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -277,6 +277,9 @@ function generate( pkg, releaseType ) { // Group commits by release: commits = groupBy( commits, indicator ); + if (!commits.unreleased) { + commits.unreleased = []; + } if ( releaseType ) { newestRelease = releases[ releases.length-1 ][ 0 ]; @@ -322,8 +325,8 @@ function generate( pkg, releaseType ) { if ( !releaseCommits ) { continue; } - date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); - out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; + date = formatDate( releases[ i ][ 1 ] ); + out += '## ' + version + ' (' + date + ')\n\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); out += sectionStart( 'packages' ); @@ -349,9 +352,9 @@ function generate( pkg, releaseType ) { if ( !summary ) { continue; } - date = new Date( releases[ i ][ 1 ] ).toISOString().slice( 0, 10 ); + date = formatDate( releases[ i ][ 1 ] ); out += releaseSectionStart( version ); - out += '## ' + version + ' (' + formatDate( date ) + ')\n\n'; + out += '## ' + version + ' (' + date + ')\n\n'; out += summary; out += sectionEnd( 'release' ); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js index b260d484a8c..e96989e9d69 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -32,6 +32,20 @@ var sectionEnd = require( './section_end.js' ); var heading = require( './heading.js' ); +// FUNCTIONS // + +/** +* Groups commits by type. +* +* @private +* @param {Object} commit - commit object +* @returns {(string|null)} group key +*/ +function groupByType( commit ) { + return commit.type; +} + + // MAIN // /** @@ -94,17 +108,6 @@ function releaseSummary( commits, excludeCommits, excludeContributors ) { out += formatContributors( commits ); } return out; - - /** - * Groups commits by type. - * - * @private - * @param {Object} commit - commit object - * @returns {(string|null)} group key - */ - function groupByType( commit ) { - return commit.type; - } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js index c164d4f4123..e2e855cb019 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -76,8 +76,7 @@ function extractCommits( pkg ) { 'maxBuffer': 1024*1024*60, // 60 MB 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr }; - cmd = 'if [ -d "lib/node_modules/@stdlib/'+pkg+'" ]; then\n'; - cmd = ' git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|"'; + cmd = 'git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|"'; cmd += ' '; cmd += 'lib/node_modules/@stdlib/'+pkg; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js index cb5907b1e04..cd9258c17e2 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -133,19 +133,19 @@ function toConventionalChangelog( ast ) { if ( body ) { visit( body, 'text', extractBody ); } + } - /** - * Callback invoked upon visiting a text node. - * - * @private - * @param {Object} node - AST node - */ - function extractBody( node ) { - if ( out.body !== '' ) { - out.body += '\n'; - } - out.body += node.value; + /** + * Callback invoked upon visiting a text node. + * + * @private + * @param {Object} node - AST node + */ + function extractBody( node ) { + if ( out.body !== '' ) { + out.body += '\n'; } + out.body += node.value; } /** @@ -156,33 +156,33 @@ function toConventionalChangelog( ast ) { */ function onSummary( summary ) { visit( summary, visitSummary ); + } - /** - * Processes a summary node and extracts relevant information. - * - * @private - * @param {Object} node - AST node - */ - function visitSummary( node ) { - switch ( node.type ) { - case 'type': - out.type = node.value; - out.header += node.value; - break; - case 'scope': - out.scope = node.value; - out.header += '(' + node.value + ')'; - break; - case 'breaking-change': - out.header += '!'; - break; - case 'text': - out.subject = node.value; - out.header += ': ' + node.value; - break; - default: - break; - } + /** + * Processes a summary node and extracts relevant information. + * + * @private + * @param {Object} node - AST node + */ + function visitSummary( node ) { + switch ( node.type ) { + case 'type': + out.type = node.value; + out.header += node.value; + break; + case 'scope': + out.scope = node.value; + out.header += '(' + node.value + ')'; + break; + case 'breaking-change': + out.header += '!'; + break; + case 'text': + out.subject = node.value; + out.header += ': ' + node.value; + break; + default: + break; } } @@ -193,13 +193,13 @@ function toConventionalChangelog( ast ) { * @param {Object} node - AST node */ function processFooter( node ) { - var hasRefSepartor = false; + var hasRefSeparator = false; var coAuthoredBy = false; var closesIssue = false; var reference = {}; var hasPRURL = false; visit( node, [ 'type', 'separator', 'text' ], processFooterNode ); - if ( hasRefSepartor && reference.ref.match( RE_DIGITS ) ) { + if ( hasRefSeparator && reference.ref.test( RE_DIGITS ) ) { reference.prefix = '#'; out.references.push( reference ); } @@ -229,7 +229,7 @@ function toConventionalChangelog( ast ) { break; case 'separator': if ( node.value.includes( '#' ) ) { - hasRefSepartor = true; + hasRefSeparator = true; } break; case 'text': @@ -243,7 +243,7 @@ function toConventionalChangelog( ast ) { reference.ref = node.value.split('/').pop(); } else if ( node.value.charAt( 0 ) === '#' ) { - hasRefSepartor = true; + hasRefSeparator = true; reference.ref = node.value.substring( 1 ); if ( !reference.url ) { if ( closesIssue ) { From d819b2c61449d5c6cef25493f2834cbb8d2d8f72 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 15 Feb 2024 19:38:34 -0500 Subject: [PATCH 18/46] Update lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js Co-authored-by: Athan Signed-off-by: Philipp Burckhardt --- .../@stdlib/_tools/changelog/generate/lib/npm_releases.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js index 7d74e33c2b6..9129273a6e0 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js @@ -59,8 +59,7 @@ function npmReleases( pkg ) { out = shell( command, opts ).toString(); out = JSON.parse( out ); out = omit( out, OMITTED_KEYS ); - } - catch ( err ) { + } catch ( err ) { debug( 'Encountered an error when attempting to retrieve package release dates: %s', err.message ); out = {}; } From d281b8795ff8551a83c6557eb4af95f411f5d004 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 15 Feb 2024 19:38:47 -0500 Subject: [PATCH 19/46] Update lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js Co-authored-by: Athan Signed-off-by: Philipp Burckhardt --- .../_tools/changelog/generate/lib/format_contributors.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index 1219ef1452d..a58a201460d 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -104,8 +104,7 @@ function formatContributors( commits ) { out += heading( 'Contributors', 3 ); if ( contributors.length === 1 ) { out += 'A total of 1 person contributed to this release. Thank you to this contributor:\n\n'; - } - else { + } else { out += 'A total of ' + contributors.length + ' people contributed to this release. Thank you to the following contributors:\n\n'; } out += map( contributors, formatContributor ).join( '\n' ); From 8dc4d2e31e142580a3ddf42af1959628770f7d71 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 15 Feb 2024 19:44:14 -0500 Subject: [PATCH 20/46] Update lib/node_modules/@stdlib/_tools/changelog/generate/README.md Co-authored-by: Athan Signed-off-by: Philipp Burckhardt --- lib/node_modules/@stdlib/_tools/changelog/generate/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index b0bdee7408f..0a4dc4849d5 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -20,7 +20,7 @@ limitations under the License. # Generate Changelog -> Generate a changelog for a specified package. +> Generate a changelog for a specified stdlib package.
From 99ba53852100965aef1a874077aaf9bac4396f42 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 15 Feb 2024 20:09:16 -0500 Subject: [PATCH 21/46] build: expect package names with stdlib namespace prefix --- .../_tools/changelog/generate/README.md | 12 +++++------ .../changelog/generate/examples/index.js | 6 +++--- .../generate/lib/breaking_changes.js | 2 +- .../_tools/changelog/generate/lib/index.js | 6 +++--- .../_tools/changelog/generate/lib/main.js | 20 ++++++++++--------- .../_tools/changelog/generate/package.json | 2 +- .../_tools/changelog/generate/test/test.js | 14 ++++++------- .../_tools/scripts/publish_packages.js | 2 +- 8 files changed, 33 insertions(+), 31 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index 0a4dc4849d5..5095cc68447 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -35,17 +35,17 @@ var generate = require( '@stdlib/_tools/changelog/generate' ); Generates a Markdown formatted changelog for a specified package. ```javascript -var changelog = generate( 'assert/contains' ); +var changelog = generate( '@stdlib/assert/contains' ); // returns '...' ``` To generate a changelog for an upcoming release, provide a valid release type as the second argument. ```javascript -var changelog = generate( 'assert/contains', 'patch' ); +var changelog = generate( '@stdlib/assert/contains', 'patch' ); // returns '...' -changelog = generate( 'assert/contains', 'minor' ); +changelog = generate( '@stdlib/assert/contains', 'minor' ); // returns '...' ``` @@ -77,15 +77,15 @@ The following release types are supported: var generate = require( '@stdlib/_tools/changelog/generate' ); // Generate a changelog for a non-namespace package: -var changelog = generate( 'utils/curry' ); +var changelog = generate( '@stdlib/utils/curry' ); // returns '...' // Generate a changelog for a new release: -changelog = generate( 'utils/curry', 'patch' ); +changelog = generate( '@stdlib/utils/curry', 'patch' ); // returns '...' // Generate a changelog for a namespace package: -changelog = generate( 'string/base' ); +changelog = generate( '@stdlib/string/base' ); // returns '...' ``` diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js index aaf0a752bf6..68df4367278 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js @@ -21,16 +21,16 @@ var generate = require( './../lib' ); // Generate a changelog for a non-namespace package: -var changelog = generate( 'utils/curry' ); +var changelog = generate( '@stdlib/utils/curry' ); console.log( changelog ); // => '...' // Generate a changelog for a new release: -changelog = generate( 'utils/curry', 'patch' ); +changelog = generate( '@stdlib/utils/curry', 'patch' ); console.log( changelog ); // => '...' // Generate a changelog for a namespace package: -changelog = generate( 'string/base' ); +changelog = generate( '@stdlib/string/base' ); console.log( changelog ); // => '...' diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js index 19eef413e7e..5c4cad79aee 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.js @@ -94,7 +94,7 @@ function breakingChanges( commits ) { notes = collectField( commits, 'notes' ); breakingChanges = filter( notes, isBreakingChange ); - if ( !breakingChanges || breakingChanges.length === 0 ) { + if ( breakingChanges.length === 0 ) { return ''; } out = sectionStart( 'breaking-changes' ); diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js index 1444eb0f92c..3b7c68c049c 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js @@ -19,7 +19,7 @@ 'use strict'; /** -* Generate a changelog for a specified package. +* Generate a changelog for a specified stdlib package. * * @module @stdlib/_tools/changelog/generate * @@ -27,11 +27,11 @@ * var generateChangelog = require( '@stdlib/_tools/changelog/generate' ); * * // Standard packages: -* var changelog = generateChangelog( 'utils/curry' ); +* var changelog = generateChangelog( '@stdlib/utils/curry' ); * // returns '...' * * // Namespace packages: -* changelog = generateChangelog( 'stats/base/dists' ); +* changelog = generateChangelog( '@stdlib/stats/base/dists' ); * // returns '...' */ diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index d2fda0d21c7..0fdd99af706 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -201,7 +201,7 @@ function packageSummaryWrapper( pkg, version, name, summary ) { // MAIN // /** -* Generates a Markdown formatted changelog for a specified package. +* Generates a Markdown formatted changelog for a specified stdlib package. * * @param {string} pkg - package name * @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, or `premajor`) @@ -211,23 +211,23 @@ function packageSummaryWrapper( pkg, version, name, summary ) { * @returns {string} changelog contents * * @example -* var changelog = generate( 'utils/curry' ); +* var changelog = generate( '@stdlib/utils/curry' ); * // returns '...' * * @example -* var changelog = generate( 'stats/base/dists' ); +* var changelog = generate( '@stdlib/stats/base/dists' ); * // returns '...' * * @example -* var changelog = generate( 'proxy' ); +* var changelog = generate( '@stdlib/proxy' ); * // returns '...' * * @example -* var changelog = generate( 'utils/curry', 'patch' ); +* var changelog = generate( '@stdlib/utils/curry', 'patch' ); * // returns '...' * * @example -* var changelog = generate( 'utils/curry', 'major' ); +* var changelog = generate( '@stdlib/utils/curry', 'major' ); * // returns '...' */ function generate( pkg, releaseType ) { @@ -252,14 +252,16 @@ function generate( pkg, releaseType ) { if ( !isString( pkg ) ) { throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) ); } - if ( pkg === '' ) { + if ( pkg === '@stdlib' || pkg === '@stdlib/stdlib' ) { // Case: root package isNamespacePkg = true; standalone = '@stdlib/stdlib'; + pkg = ''; releases = objectEntries( npmReleases( standalone ) ); } else { // Case: all other packages - isNamespacePkg = contains( STDLIB_NAMESPACE_PKGS, '@stdlib/' + pkg ); + isNamespacePkg = contains( STDLIB_NAMESPACE_PKGS, pkg ); + pkg = replace( pkg, '@stdlib/', '' ); standalone = '@stdlib/' + replace( pkg, '/', '-' ); releases = objectEntries( npmReleases( standalone ) ); } @@ -268,7 +270,7 @@ function generate( pkg, releaseType ) { out += '> Package changelog.\n\n'; commits = parseCommits( pkg ); - if ( !commits || commits.length === 0 ) { + if ( commits.length === 0 ) { throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) ); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json index be7a5be76cd..313dfeb9b77 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/package.json +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json @@ -1,7 +1,7 @@ { "name": "@stdlib/_tools/changelog/generate", "version": "0.0.0", - "description": "Generate a changelog for a specified package.", + "description": "Generate a changelog for a specified stdlib package.", "license": "Apache-2.0", "author": { "name": "The Stdlib Authors", diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js index 04efc2cbdb3..fd4e9aba6d5 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -100,32 +100,32 @@ tape( 'the function throws an error if provided an invalid release type', functi function badValue( value ) { return function badValue() { - generate( 'utils/curry', value ); + generate( '@stdlib/utils/curry', value ); }; } }); tape( 'the function returns a changelog', function test( t ) { - var out = generate( 'utils/curry' ); + var out = generate( '@stdlib/utils/curry' ); t.equal( typeof out, 'string', 'returns a string' ); - out = generate( 'stats/base/dists' ); + out = generate( '@stdlib/stats/base/dists' ); t.equal( typeof out, 'string', 'returns a string' ); - out = generate( 'proxy' ); + out = generate( '@stdlib/proxy' ); t.equal( typeof out, 'string', 'returns a string' ); t.end(); }); tape( 'the function returns a changelog for a new release', function test( t ) { - var out = generate( 'utils/noop', 'patch' ); + var out = generate( '@stdlib/utils/noop', 'patch' ); t.equal( typeof out, 'string', 'returns a string' ); - out = generate( 'utils/noop', 'minor' ); + out = generate( '@stdlib/utils/noop', 'minor' ); t.equal( typeof out, 'string', 'returns a string' ); - out = generate( 'utils/noop', 'major' ); + out = generate( '@stdlib/utils/noop', 'major' ); t.equal( typeof out, 'string', 'returns a string' ); t.end(); diff --git a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js index fb58d4c02c5..be0f54097f7 100644 --- a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js +++ b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js @@ -736,7 +736,7 @@ function publish( pkg, clbk ) { fs.copyFileSync( join( __dirname, 'templates', '.npmignore.txt' ), join( dist, '.npmignore' ) ); fs.copyFileSync( join( __dirname, 'templates', 'Makefile.txt' ), join( dist, 'Makefile' ) ); fs.copyFileSync( join( __dirname, 'templates', '.eslintrc.js.txt' ), join( dist, '.eslintrc.js' ) ); - writeFileSync( join( dist, 'CHANGELOG.md' ), generateChangelog( pkg, flags[ 'release-type' ] ) ); + writeFileSync( join( dist, 'CHANGELOG.md' ), generateChangelog( '@stdlib/'+pkg, flags[ 'release-type' ] ) ); fs.copyFileSync( join( __dirname, 'templates', 'SECURITY.md.txt' ), join( dist, 'SECURITY.md' ) ); pkgJsonPath = join( dist, 'package.json' ); From e0e07169017580cd435263518f7dfb93a295c421 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sat, 17 Feb 2024 17:07:01 -0500 Subject: [PATCH 22/46] refactor: implement code review feedback --- .../_tools/changelog/generate/lib/npm_releases.js | 4 ++-- .../_tools/changelog/parse-commits/lib/main.js | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js index 9129273a6e0..b198ab72b4a 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js @@ -58,11 +58,11 @@ function npmReleases( pkg ) { try { out = shell( command, opts ).toString(); out = JSON.parse( out ); - out = omit( out, OMITTED_KEYS ); } catch ( err ) { debug( 'Encountered an error when attempting to retrieve package release dates: %s', err.message ); - out = {}; + return {}; } + out = omit( out, OMITTED_KEYS ); return out; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js index 1b7cc827482..e353de74da4 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -62,17 +62,18 @@ function parseCommits( pkg ) { commits = extractCommits( pkg ); out = []; for ( i = 0; i < commits.length; i++ ) { + msg = commits[ i ].message; try { - msg = commits[ i ].message; ast = parser( msg ); - merged = merge( commits[ i ], toConventionalChangelog( ast ) ); - for ( j = 0; j < merged.notes.length; j++ ) { - merged.notes[ j ].hash = commits[ i ].hash; - } - out.push( merged ); } catch ( err ) { debug( 'Encountered an error when parsing a commit message: %s. Message: %s', err.message, commits[ i ] ); + continue; // Skip this commit if parsing fails... } + merged = merge( commits[ i ], toConventionalChangelog( ast ) ); + for ( j = 0; j < merged.notes.length; j++ ) { + merged.notes[ j ].hash = commits[ i ].hash; + } + out.push( merged ); } return out; } From 43f14fac501bc322ef3ab8368f1a27c226890200 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sat, 17 Feb 2024 22:13:40 -0500 Subject: [PATCH 23/46] build: refactor to accept directories instead of packages --- .../_tools/changelog/generate/lib/main.js | 7 +- .../_tools/changelog/parse-commits/README.md | 30 ++- .../changelog/parse-commits/examples/index.js | 22 +-- .../changelog/parse-commits/lib/commits.js | 24 ++- .../lib/conventional_changelog.js | 21 +- .../changelog/parse-commits/lib/defaults.js | 39 ++++ .../changelog/parse-commits/lib/index.js | 4 +- .../changelog/parse-commits/lib/main.js | 42 ++-- .../changelog/parse-commits/lib/validate.js | 70 +++++++ .../changelog/parse-commits/package.json | 2 +- .../changelog/parse-commits/test/test.js | 33 +++- .../parse-commits/test/test.validate.js | 179 ++++++++++++++++++ 12 files changed, 399 insertions(+), 74 deletions(-) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 0fdd99af706..8724aaedaaa 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -21,6 +21,7 @@ // MODULES // var dirname = require( 'path' ).dirname; +var join = require( 'path' ).join; var semver = require( 'semver' ); var substringAfter = require( '@stdlib/string/substring-after' ); var objectEntries = require( '@stdlib/utils/entries' ); @@ -29,6 +30,7 @@ var objectKeys = require( '@stdlib/utils/keys' ); var namespaces = require( '@stdlib/_tools/pkgs/namespaces' ).sync; var contains = require( '@stdlib/assert/contains' ); var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var rootDir = require( '@stdlib/_tools/utils/root-dir' ); var replace = require( '@stdlib/string/replace' ); var groupBy = require( '@stdlib/utils/group-by' ); var filter = require( '@stdlib/array/base/filter' ); @@ -48,6 +50,7 @@ var heading = require( './heading.js' ); // VARIABLES // +var STDLIB_LIB_DIR = join( rootDir(), 'lib', 'node_modules', '@stdlib' ); var STDLIB_NAMESPACE_PKGS = namespaces(); var STDLIB_REPO_NODE_PATH = 'https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules'; var RE_PACKAGE_SUBDIRS = /\/?(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)\/?[\s\S]*$/; @@ -269,7 +272,9 @@ function generate( pkg, releaseType ) { out = '# CHANGELOG\n\n'; out += '> Package changelog.\n\n'; - commits = parseCommits( pkg ); + commits = parseCommits({ + 'dir': join( STDLIB_LIB_DIR, pkg ) + }, pkg ); if ( commits.length === 0 ) { throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) ); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md index 2d941180672..9a6ea4f2dd0 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md @@ -20,7 +20,7 @@ limitations under the License. # Parse Commits -> Parse package commit messages into conventional changelog objects. +> Parse commit messages into conventional changelog objects.
@@ -30,15 +30,21 @@ limitations under the License. var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); ``` -#### parseCommits( pkg ) +#### parseCommits( \[options] ) -Parses package commit messages into conventional changelog objects. +Parses commit messages into conventional changelog objects. ```javascript -var commits = parseCommits( 'assert/contains' ); +var commits = parseCommits(); // returns [...] ``` +The function accepts the following `options`: + +- **dir**: directory for which to parse commits. May be either an absolute path or a path relative to the current working directory. Default: current working directory. +- **issueURL**: issue URL. Default: `https://github.com/stdlib-js/stdlib/issues/`. +- **prURL**: pull request URL. Default: `https://github.com/stdlib-js/stdlib/pull/`. +
@@ -56,20 +62,12 @@ var commits = parseCommits( 'assert/contains' ); ```javascript var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); -// Commits for a non-namespace package: -var commits = parseCommits( 'assert/contains' ); -// returns [...] - -// Commits for a namespace package: -commits = parseCommits( 'math/base/special' ); -// returns [...] - -// Commits for entire project: -commits = parseCommits( '' ); +var commits = parseCommits(); // returns [...] -// Commits for non-existent package: -commits = parseCommits( 'beep/boop' ); +commits = parseCommits({ + 'dir': '/path/to/non-existent/directory' +}); // returns [] ``` diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js index 4cfb6aa8bf4..838b1b83588 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/examples/index.js @@ -20,26 +20,12 @@ var parseCommits = require( './../lib' ); -// Commits for a non-namespace package: -var commits = parseCommits( 'assert/contains' ); -console.log( 'Commits for `assert/contains`:' ); +var commits = parseCommits(); console.log( commits ); // => [...] -// Commits for a namespace package: -commits = parseCommits( 'math/base/special' ); -console.log( 'Commits for `math/base/special`:' ); -console.log( commits ); -// => [...] - -// Commits for entire project: -commits = parseCommits( '' ); -console.log( 'Commits for entire project:' ); -console.log( commits ); -// => [...] - -// Commits for non-existent package: -commits = parseCommits( 'beep/boop' ); -console.log( 'Commits for `beep/boop`:' ); +commits = parseCommits({ + 'dir': '/path/to/non-existent/directory' +}); console.log( commits ); // => [] diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js index e2e855cb019..8e057b0faef 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -21,13 +21,14 @@ // MODULES // var shell = require( 'child_process' ).execSync; // eslint-disable-line node/no-sync +var logger = require( 'debug' ); var replace = require( '@stdlib/string/replace' ); -var rootDir = require( '@stdlib/_tools/utils/root-dir' ); var trim = require( '@stdlib/string/trim' ); // VARIABLES // +var debug = logger( 'changelog:parse-commits' ); var GIT_COMMIT_SEP = '$---$'; @@ -52,17 +53,17 @@ function escapeDoubleQuotes( str ) { // MAIN // /** -* Extracts commit details for a specified package. +* Extracts commit details for a specified directory. * * @private -* @param {string} pkg - package name +* @param {string} dir - directory for which to extract commit details * @returns {Array} array of commit details * * @example -* var commits = extractCommits( 'utils/curry' ); +* var commits = extractCommits( '.' ); * // returns [...] */ -function extractCommits( pkg ) { +function extractCommits( dir ) { var commits; var lines; var parts; @@ -72,13 +73,11 @@ function extractCommits( pkg ) { var i; opts = { - 'cwd': rootDir(), + 'cwd': dir, 'maxBuffer': 1024*1024*60, // 60 MB 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr }; - cmd = 'git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|"'; - cmd += ' '; - cmd += 'lib/node_modules/@stdlib/'+pkg; + cmd = 'git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|" .'; // Insert a separator between commits: cmd += ' | '; @@ -86,7 +85,12 @@ function extractCommits( pkg ) { cmd += 'fi\n'; commits = []; - out = trim( shell( cmd, opts ).toString() ); + try { + out = trim( shell( cmd, opts ).toString() ); + } catch ( err ) { + debug( 'Encountered an error when attempting to extract commits: %s', err.message ); + return commits; + } lines = out.split( GIT_COMMIT_SEP ); for ( i = 0; i < lines.length; i++ ) { if ( !lines[ i ] ) { diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js index cd9258c17e2..ea61f312ac3 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -26,8 +26,6 @@ var visit = require( 'unist-util-visit' ); // VARIABLES // -var STDLIB_ISSUE_URL = 'https://github.com/stdlib-js/stdlib/issues/'; -var STDLIB_PR_URL = 'https://github.com/stdlib-js/stdlib/pull/'; var RE_DIGITS = /^[0-9]+$/; var RE_CLOSES = /(?:closes|fixes|resolves)/i; var RE_CO_AUTHORED_BY = /co-authored-by/i; @@ -40,9 +38,12 @@ var RE_CO_AUTHORED_BY = /co-authored-by/i; * * @private * @param {Object} ast - unist commit message AST +* @param {Options} options - function options +* @param {string} options.issueURL - issue URL +* @param {string} options.prURL - PR URL * @returns {Object} conventional changelog format */ -function toConventionalChangelog( ast ) { +function toConventionalChangelog( ast, options ) { var out = { 'type': '', 'scope': null, @@ -199,7 +200,7 @@ function toConventionalChangelog( ast ) { var reference = {}; var hasPRURL = false; visit( node, [ 'type', 'separator', 'text' ], processFooterNode ); - if ( hasRefSeparator && reference.ref.test( RE_DIGITS ) ) { + if ( hasRefSeparator && RE_DIGITS.test( reference.ref ) ) { reference.prefix = '#'; out.references.push( reference ); } @@ -233,11 +234,11 @@ function toConventionalChangelog( ast ) { } break; case 'text': - if ( node.value.includes( STDLIB_ISSUE_URL ) ) { + if ( node.value.includes( options.issueURL ) ) { reference.url = node.value; reference.ref = node.value.split( '/' ).pop(); } - else if ( node.value.includes( STDLIB_PR_URL ) ) { + else if ( node.value.includes( options.prURL ) ) { hasPRURL = true; reference.url = node.value; reference.ref = node.value.split('/').pop(); @@ -247,18 +248,18 @@ function toConventionalChangelog( ast ) { reference.ref = node.value.substring( 1 ); if ( !reference.url ) { if ( closesIssue ) { - reference.url = STDLIB_ISSUE_URL + reference.ref; + reference.url = options.issueURL + reference.ref; } else if ( hasPRURL ) { - reference.url = STDLIB_PR_URL + reference.ref; + reference.url = options.prURL + reference.ref; } } } else { reference.ref = node.value; if ( !reference.url ) { if ( closesIssue ) { - reference.url = STDLIB_ISSUE_URL + reference.ref; + reference.url = options.issueURL + reference.ref; } else if ( hasPRURL ) { - reference.url = STDLIB_PR_URL + reference.ref; + reference.url = options.prURL + reference.ref; } } } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js new file mode 100644 index 00000000000..0783ec1d262 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/defaults.js @@ -0,0 +1,39 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Returns default options. +* +* @private +* @returns {Object} default options +*/ +function defaults() { + return { + 'issueURL': 'https://github.com/stdlib-js/stdlib/issues/', + 'prURL': 'https://github.com/stdlib-js/stdlib/pull/' + }; +} + + +// EXPORTS // + +module.exports = defaults; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js index f2504721a77..70bce8a0a01 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js @@ -24,9 +24,11 @@ * @module @stdlib/_tools/changelog/parse-commits * * @example +* var join = require( 'path' ).join; +* var rootDir = require( '@stdlib/_tools/utils/root-dir' ); * var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); * -* var out = parseCommits( 'utils/curry' ); +* var out = parseCommits( join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' ) ); * // returns [...] */ diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js index e353de74da4..f625bcf3f9c 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -20,13 +20,15 @@ // MODULES // +var resolve = require( 'path' ).resolve; var logger = require( 'debug' ); var parser = require( '@conventional-commits/parser' ).parser; -var isString = require( '@stdlib/assert/is-string' ).isPrimitive; -var format = require( '@stdlib/string/format' ); var merge = require( '@stdlib/utils/merge' ); +var cwd = require( '@stdlib/process/cwd' ); var toConventionalChangelog = require( './conventional_changelog.js' ); var extractCommits = require( './commits.js' ); +var validate = require( './validate.js' ); +var defaults = require( './defaults.js' ); // VARIABLES // @@ -39,27 +41,45 @@ var debug = logger( 'changelog:parse-commits' ); /** * Parses package commit messages into conventional changelog objects. * -* @param {string} pkg - package name -* @throws {TypeError} must provide a string +* @param {Options} [options] - function options +* @param {string} [options.dir] - root directory +* @param {string} [options.issueURL] - issue URL +* @param {string} [options.prURL] - PR URL +* @throws {TypeError} options argument must be an object +* @throws {TypeError} must provide valid options * @returns {Array} array of conventional changelog formatted commit message objects * * @example -* var out = parseCommits( 'utils/curry' ); -* // returns [...] +* var resolve = require( 'path' ).resolve; +* var out = parseCommits({ +* 'dir': resolve( __dirname, '..', '..' ) +* }); */ -function parseCommits( pkg ) { +function parseCommits( options ) { var commits; var merged; + var opts; var msg; var ast; + var dir; + var err; var out; var i; var j; - if ( !isString( pkg ) ) { - throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', pkg ) ); + opts = defaults(); + if ( arguments.length > 0 ) { + err = validate( opts, options ); + if ( err ) { + throw err; + } + } + if ( opts.dir ) { + dir = resolve( cwd(), opts.dir ); + } else { + dir = cwd(); } - commits = extractCommits( pkg ); + commits = extractCommits( dir ); out = []; for ( i = 0; i < commits.length; i++ ) { msg = commits[ i ].message; @@ -69,7 +89,7 @@ function parseCommits( pkg ) { debug( 'Encountered an error when parsing a commit message: %s. Message: %s', err.message, commits[ i ] ); continue; // Skip this commit if parsing fails... } - merged = merge( commits[ i ], toConventionalChangelog( ast ) ); + merged = merge( commits[ i ], toConventionalChangelog( ast, opts ) ); for ( j = 0; j < merged.notes.length; j++ ) { merged.notes[ j ].hash = commits[ i ].hash; } diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js new file mode 100644 index 00000000000..e0ac9d12c67 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/validate.js @@ -0,0 +1,70 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isObject = require( '@stdlib/assert/is-plain-object' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var format = require( '@stdlib/string/format' ); + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination for validated options +* @param {Options} options - function options +* @param {string} [options.dir] - root directory +* @param {string} [options.issueURL] - issue URL +* @param {string} [options.prURL] - PR URL +* @returns {(null|Error)} null or an error +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); + } + if ( hasOwnProp( options, 'dir' ) ) { + opts.dir = options.dir; + if ( !isString( opts.dir ) ) { + return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'dir', opts.dir ) ); + } + } + if ( hasOwnProp( options, 'issueURL' ) ) { + opts.issueURL = options.issueURL; + if ( !isString( opts.issueURL ) ) { + return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'issueURL', opts.issueURL ) ); + } + } + if ( hasOwnProp( options, 'prURL' ) ) { + opts.prURL = options.prURL; + if ( !isString( opts.prURL ) ) { + return new TypeError( format( 'invalid option. `%s` option must be a string. Option: `%s`.', 'prURL', opts.prURL ) ); + } + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json index 0650f7acd1a..9b1f45cd5fd 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json @@ -1,7 +1,7 @@ { "name": "@stdlib/_tools/changelog/parse-commits", "version": "0.0.0", - "description": "Parse package commit messages into conventional changelog objects.", + "description": "Parse commit messages into conventional changelog objects.", "license": "Apache-2.0", "author": { "name": "The Stdlib Authors", diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js index bc06a230ce6..33cefc83f33 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -20,7 +20,9 @@ // MODULES // +var join = require( 'path' ).join; var tape = require( 'tape' ); +var rootDir = require( '@stdlib/_tools/utils/root-dir' ); var isPlainObject = require( '@stdlib/assert/is-plain-object' ); var isArray = require( '@stdlib/assert/is-array' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); @@ -35,18 +37,18 @@ tape( 'main export is a function', function test( t ) { t.end(); }); -tape( 'the function throws an error if not provided a string', function test( t ) { +tape( 'the function throws an error if provided an argument that is not an object', function test( t ) { var values; var i; values = [ + 'abc', 5, null, true, void 0, NaN, [], - {}, function noop() {} ]; @@ -62,18 +64,37 @@ tape( 'the function throws an error if not provided a string', function test( t } }); -tape( 'the function returns an empty array if provided a string not matching a package directory', function test( t ) { - t.deepEqual( parseCommits( 'beep/boop' ), [], 'returns empty array' ); - t.deepEqual( parseCommits( 'foo/bar/baz' ), [], 'returns empty array' ); +tape( 'the function throws an error if provided an invalid option', function test( t ) { + t.throws( badValue, TypeError, 'throws error' ); + t.end(); + function badValue() { + var opts = { + 'dir': null + }; + parseCommits( opts ); + } +}); + +tape( 'the function returns an empty array if unable to resolve a directory or if unable to find any commits', function test( t ) { + var opts = { + 'dir': 'beepboop' + }; + var out = parseCommits( opts ); + t.strictEqual( isArray( out ), true, 'returns an array' ); + t.strictEqual( out.length, 0, 'has length 0' ); t.end(); }); tape( 'the function returns an array of conventional changelog formatted commit message objects', function test( t ) { var commits; var commit; + var opts; var i; - commits = parseCommits( 'utils/curry' ); + opts = { + 'dir': join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' ) + }; + commits = parseCommits( opts ); t.strictEqual( isArray( commits ), true, 'returns an array' ); t.strictEqual( commits.length > 0, true, 'has length greater than 0' ); for ( i = 0; i < commits.length; i++ ) { diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js new file mode 100644 index 00000000000..d7f6010e172 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js @@ -0,0 +1,179 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var validate = require( './../lib/validate.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.equal( typeof validate, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'if provided an `options` argument which is not an `object`, the function returns a type error', function test( t ) { + var values; + var opts; + var err; + var i; + + values = [ + '5', + 5, + NaN, + true, + null, + void 0, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + opts = {}; + err = validate( opts, values[i] ); + t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'if provided a `dir` option which is not a `string`, the function returns a type error', function test( t ) { + var values; + var opts; + var err; + var i; + + values = [ + 5, + NaN, + true, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + opts = {}; + err = validate( opts, { + 'dir': values[i] + }); + t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'if provided a `issueURL` option which is not a `string`, the function returns a type error', function test( t ) { + var values; + var opts; + var err; + var i; + + values = [ + 5, + NaN, + true, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + opts = {}; + err = validate( opts, { + 'prURL': values[i] + }); + t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'if provided a `prURL` option which is not a `string`, the function returns a type error', function test( t ) { + var values; + var opts; + var err; + var i; + + values = [ + 5, + NaN, + true, + null, + void 0, + [], + {}, + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + opts = {}; + err = validate( opts, { + 'prURL': values[i] + }); + t.equal( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns `null` if all options are valid', function test( t ) { + var opts; + var obj; + var err; + + opts = { + 'dir': '/foo/bar/baz', + 'issueURL': 'beep.com', + 'prURL': 'boop.com' + }; + obj = {}; + err = validate( obj, opts ); + + t.equal( err, null, 'returns null' ); + t.strictEqual( obj.dir, opts.dir, 'sets dir option' ); + t.strictEqual( obj.issueURL, opts.issueURL, 'sets issueURL option' ); + t.strictEqual( obj.prURL, opts.prURL, 'sets prURL option' ); + + t.end(); +}); + +tape( 'the function ignores unsupported/unrecognized options', function test( t ) { + var opts; + var obj; + var err; + + opts = { + 'beep': 'boop', + 'a': 'b', + 'c': [ 1, 2, 3 ] + }; + obj = {}; + err = validate( obj, opts ); + + t.equal( err, null, 'returns null' ); + t.deepEqual( obj, {}, 'does not set any options' ); + + t.end(); +}); From 32ef0d4f218c8e4c1f27873b1cc0308bff8b121c Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sat, 17 Feb 2024 22:52:41 -0500 Subject: [PATCH 24/46] build: fix passed arguments --- lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 8724aaedaaa..6ab6eb41416 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -274,7 +274,7 @@ function generate( pkg, releaseType ) { commits = parseCommits({ 'dir': join( STDLIB_LIB_DIR, pkg ) - }, pkg ); + }); if ( commits.length === 0 ) { throw new Error( format( 'invalid argument. Unable to parse commits for package: `%s`.', pkg ) ); } From 32f483aea7f24ef6202e6d6b87ed73245d0644d6 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 18 Feb 2024 15:09:13 -0500 Subject: [PATCH 25/46] build: add tool to suggest version change --- .../recommend-version-bump/README.md | 116 ++++++++++++++++++ .../recommend-version-bump/examples/index.js | 32 +++++ .../recommend-version-bump/lib/index.js | 57 +++++++++ .../recommend-version-bump/lib/main.js | 71 +++++++++++ .../recommend-version-bump/package.json | 64 ++++++++++ .../recommend-version-bump/test/test.js | 94 ++++++++++++++ 6 files changed, 434 insertions(+) create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/package.json create mode 100644 lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md new file mode 100644 index 00000000000..72694c23d43 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md @@ -0,0 +1,116 @@ + + +# Recommend Version Bump + +> Recommend a version bump based on parsed commit messages. + +
+ +## Usage + +```javascript +var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' ); +``` + +#### recommendVersionBump( commits ) + +Recommends a version bump based on parsed commit messages. + +```javascript +var commits = [ + { + 'type': 'fix', + 'notes': [] + }, + { + 'type': 'feat', + 'notes': [{ + 'title': 'BREAKING CHANGE', + 'text': 'Changes in API' + }] + } +]; +var version = recommendVersionBump( commits ); +// returns 'major' + +commits = [ + { + 'type': 'feat', + 'notes': [] + } +]; +version = recommendVersionBump( commits ); +// returns 'minor' +``` + +The function returns a `string` indicating the recommended version bump or `null` if no version bump is recommended: + +- `'patch'`: a patch version bump (bug fixes). +- `'minor'`: a minor version bump (addition of functionality in a backward compatible manner). +- `'major'`: a major version bump (breaking changes, incompatible API changes). +- `null`: no version bump recommended. + +
+ + + +
+ +
+ + + +
+ +## Examples + +```javascript +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' ); + +var commits = parseCommits(); +var version = recommendVersionBump( commits ); +// returns + +commits = []; +version = recommendVersionBump( commits ); +// returns null +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js new file mode 100644 index 00000000000..f1b340fb97d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/examples/index.js @@ -0,0 +1,32 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +var recommendVersionBump = require( './../lib' ); + +var commits = parseCommits(); +var version = recommendVersionBump( commits ); +console.log( version ); +// => + +commits = []; +version = recommendVersionBump( commits ); +console.log( version ); +// => null diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js new file mode 100644 index 00000000000..8620414e5f0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/index.js @@ -0,0 +1,57 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Recommend a version bump based on parsed commit messages. +* +* @module @stdlib/_tools/changelog/recommend-version-bump +* +* @example +* var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' ); +* +* var commits = [ +* { +* 'type': 'fix', +* 'notes': [] +* }, +* { +* 'type': 'feat', +* 'notes': [{ +* 'title': 'BREAKING CHANGE', +* 'text': 'Changes in API' +* }] +* } +* ]; +* var version = recommendVersionBump( commits ); +* // returns 'major' +* +* commits = []; +* version = recommendVersionBump( commits ); +* // returns null +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js new file mode 100644 index 00000000000..afb2879b275 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js @@ -0,0 +1,71 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MAIN // + +/** +* Recommends a version bump based on a list of commits. +* +* @param {Array} commits - conventional changelog formatted commit message objects +* @returns {(string|null)} - `major`, `minor`, `patch`, or null +*/ +function recommendVersionBump( commits ) { + var hasBreakingChange = false; + var hasFeature = false; + var hasFix = false; + var commit; + var i; + var j; + + for ( i = 0; i < commits.length; i++ ) { + commit = commits[ i ]; + for ( j = 0; j < commit.notes.length; j++ ) { + if ( commit.notes[ j ].title === 'BREAKING CHANGE' ) { + hasBreakingChange = true; + break; + } + } + if ( commit.type === 'feat' ) { + hasFeature = true; + } else if ( commit.type === 'fix' ) { + hasFix = true; + } + + // If a breaking change is found, no need to check further commits: + if ( hasBreakingChange ) { + break; + } + } + if ( hasBreakingChange ) { + return 'major'; + } + if ( hasFeature ) { + return 'minor'; + } + if ( hasFix) { + return 'patch'; + } + return null; +} + + +// EXPORTS // + +module.exports = recommendVersionBump; diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/package.json b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/package.json new file mode 100644 index 00000000000..70d6dabca93 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/package.json @@ -0,0 +1,64 @@ +{ + "name": "@stdlib/_tools/changelog/recommend-version-bump", + "version": "0.0.0", + "description": "Recommend a version bump based on parsed commit messages.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib", + "directories": { + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "tools", + "tool", + "changelog", + "parse", + "parser", + "commits", + "conventional", + "metadata", + "semver", + "version", + "bump", + "git" + ], + "__stdlib__": {} +} diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js new file mode 100644 index 00000000000..104b7a0373c --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js @@ -0,0 +1,94 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 by Your Name or Organization. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var recommendVersionBump = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.equal( typeof recommendVersionBump, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function returns `null` for an empty array of commits', function test( t ) { + var result = recommendVersionBump( [] ); + t.equal( result, null, 'returns null for empty commit array' ); + t.end(); +}); + +tape( 'the function returns `\'patch\'` for commits with only bug fixes', function test( t ) { + var commits = [ + { + 'type': 'fix', + 'notes': [] + }, + { + 'type': 'fix', + 'notes': [] + } + ]; + var result = recommendVersionBump(commits); + t.equal( result, 'patch', 'returns `patch` for fix commits' ); + t.end(); +}); + +tape( 'the function returns `\'minor\'` for commits with backward-compatible feature additions', function test( t ) { + var commits = [ + { + 'type': 'feat', + 'notes': [] + }, + { + 'type': 'fix', + 'notes': [] + } + ]; + var result = recommendVersionBump(commits); + t.equal( result, 'minor', 'returns `minor` for feature commits' ); + t.end(); +}); + +tape( 'the function returns `\'major\'` for commits with breaking changes', function test( t ) { + var commits = [ + { + 'type': 'fix', + 'notes': [] + }, + { + 'type': 'feat', + 'notes': [] + }, + { + 'type': 'feat', + 'notes': [{ + 'title': 'BREAKING CHANGE', + 'text': 'Changes in API' + }] + } + ]; + var result = recommendVersionBump(commits); + t.equal( result, 'major', 'returns `major` for breaking change commits' ); + t.end(); +}); From 43cf2656b6d90c11c66276daee3bbfc3966f43ca Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 18 Feb 2024 15:45:04 -0500 Subject: [PATCH 26/46] build: refactor to allow for automatically determined version bumps --- .github/workflows/standalone_publish.yml | 1 + .../workflows/standalone_publish_custom.yml | 1 + .../_tools/changelog/generate/README.md | 21 ++++- .../changelog/generate/examples/index.js | 17 +++- .../_tools/changelog/generate/lib/main.js | 78 ++++++++++--------- .../_tools/changelog/generate/test/test.js | 41 ++++++++-- .../_tools/scripts/publish_packages.js | 15 ++-- 7 files changed, 118 insertions(+), 56 deletions(-) diff --git a/.github/workflows/standalone_publish.yml b/.github/workflows/standalone_publish.yml index dad8b4c55ba..90b09a98125 100644 --- a/.github/workflows/standalone_publish.yml +++ b/.github/workflows/standalone_publish.yml @@ -33,6 +33,7 @@ on: - patch - minor - major + - auto dry-run: type: boolean description: 'Skip uploading packages (dry run):' diff --git a/.github/workflows/standalone_publish_custom.yml b/.github/workflows/standalone_publish_custom.yml index a939dd8c78f..034301d1304 100644 --- a/.github/workflows/standalone_publish_custom.yml +++ b/.github/workflows/standalone_publish_custom.yml @@ -48,6 +48,7 @@ on: - patch - minor - major + - auto only-unpublished: type: boolean description: 'Only publish packages that have not yet been published' diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md index 5095cc68447..3f2791aa8aa 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -36,17 +36,22 @@ Generates a Markdown formatted changelog for a specified package. ```javascript var changelog = generate( '@stdlib/assert/contains' ); -// returns '...' +// returns {...} ``` +The function returns an object with the following properties: + +- **content**: Markdown formatted changelog. +- **releaseType**: release type (`null` if changelog is for a non-release). + To generate a changelog for an upcoming release, provide a valid release type as the second argument. ```javascript var changelog = generate( '@stdlib/assert/contains', 'patch' ); -// returns '...' +// returns {...} changelog = generate( '@stdlib/assert/contains', 'minor' ); -// returns '...' +// returns {...} ``` The following release types are supported: @@ -58,6 +63,7 @@ The following release types are supported: - `prepatch`: a prepatch release. - `preminor`: a preminor release. - `premajor`: a premajor release. +- `auto`: automatically determine the release type based on parsed commit messages.
@@ -78,14 +84,23 @@ var generate = require( '@stdlib/_tools/changelog/generate' ); // Generate a changelog for a non-namespace package: var changelog = generate( '@stdlib/utils/curry' ); +var content = changelog.content; // returns '...' +var releaseType = changelog.releaseType; +// returns null + // Generate a changelog for a new release: changelog = generate( '@stdlib/utils/curry', 'patch' ); +content = changelog.content; // returns '...' +releaseType = changelog.releaseType; +// returns 'patch' + // Generate a changelog for a namespace package: changelog = generate( '@stdlib/string/base' ); +content = changelog.content; // returns '...' ``` diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js index 68df4367278..43b3f5f8ead 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js @@ -22,15 +22,26 @@ var generate = require( './../lib' ); // Generate a changelog for a non-namespace package: var changelog = generate( '@stdlib/utils/curry' ); -console.log( changelog ); +var content = changelog.content; +console.log( content ); // => '...' +var releaseType = changelog.releaseType; +console.log( releaseType ); +// => null + // Generate a changelog for a new release: changelog = generate( '@stdlib/utils/curry', 'patch' ); -console.log( changelog ); +content = changelog.content; +console.log( content ); // => '...' +releaseType = changelog.releaseType; +console.log( releaseType ); +// => 'patch' + // Generate a changelog for a namespace package: changelog = generate( '@stdlib/string/base' ); -console.log( changelog ); +content = changelog.content; +console.log( content ); // => '...' diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 6ab6eb41416..3bf8d004f3e 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -23,6 +23,7 @@ var dirname = require( 'path' ).dirname; var join = require( 'path' ).join; var semver = require( 'semver' ); +var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' ); var substringAfter = require( '@stdlib/string/substring-after' ); var objectEntries = require( '@stdlib/utils/entries' ); var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); @@ -207,11 +208,11 @@ function packageSummaryWrapper( pkg, version, name, summary ) { * Generates a Markdown formatted changelog for a specified stdlib package. * * @param {string} pkg - package name -* @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, or `premajor`) +* @param {string} [releaseType] - release type (`patch`, `minor`, `major`, `prerelease`, `prepatch`, `preminor`, `premajor`, or `auto`) * @throws {TypeError} must provide a string * @throws {Error} must provide a valid package name * @throws {TypeError} must provide a recognized release type -* @returns {string} changelog contents +* @returns {Object} changelog `content` and `releaseType` (`null` if no release) * * @example * var changelog = generate( '@stdlib/utils/curry' ); @@ -246,9 +247,8 @@ function generate( pkg, releaseType ) { var commits; var version; var summary; - var date; var name; - var out; + var str; var i; var j; @@ -269,8 +269,8 @@ function generate( pkg, releaseType ) { releases = objectEntries( npmReleases( standalone ) ); } - out = '# CHANGELOG\n\n'; - out += '> Package changelog.\n\n'; + str = '# CHANGELOG\n\n'; + str += '> Package changelog.\n\n'; commits = parseCommits({ 'dir': join( STDLIB_LIB_DIR, pkg ) @@ -288,6 +288,9 @@ function generate( pkg, releaseType ) { commits.unreleased = []; } + if ( releaseType === 'auto' ) { + releaseType = recommendVersionBump( commits.unreleased ); + } if ( releaseType ) { newestRelease = releases[ releases.length-1 ][ 0 ]; nextVersion = semver.inc( newestRelease, releaseType ); @@ -297,32 +300,32 @@ function generate( pkg, releaseType ) { } if ( isNamespacePkg ) { - out += releaseSectionStart( nextVersion ); - out += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; + str += releaseSectionStart( nextVersion ); + str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; bySubpackage = groupBySubPackage( commits.unreleased, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); - out += sectionStart( 'packages' ); - out += heading( 'Packages', 3 ); + str += sectionStart( 'packages' ); + str += heading( 'Packages', 3 ); for ( i = 0; i < pkgNames.length; i++ ) { name = pkgNames[ i ]; unreleased = releaseSummary( bySubpackage[ name ], true, true ); if ( unreleased ) { - out += packageSummaryWrapper( pkg, '', name, unreleased ); + str += packageSummaryWrapper( pkg, '', name, unreleased ); } } - out += sectionEnd( 'packages' ); - out += breakingChanges( commits.unreleased ); - out += closedIssues( commits.unreleased ); - out += formatContributors( commits.unreleased ); - out += formatCommits( commits.unreleased ); - out += sectionEnd( 'release' ); + str += sectionEnd( 'packages' ); + str += breakingChanges( commits.unreleased ); + str += closedIssues( commits.unreleased ); + str += formatContributors( commits.unreleased ); + str += formatCommits( commits.unreleased ); + str += sectionEnd( 'release' ); } else { unreleased = releaseSummary( commits.unreleased ); if ( unreleased ) { - out += releaseSectionStart( nextVersion ); - out += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; - out += unreleased; - out += sectionEnd( 'release' ); + str += releaseSectionStart( nextVersion ); + str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; + str += unreleased; + str += sectionEnd( 'release' ); } } if ( isNamespacePkg ) { @@ -332,25 +335,24 @@ function generate( pkg, releaseType ) { if ( !releaseCommits ) { continue; } - date = formatDate( releases[ i ][ 1 ] ); - out += '## ' + version + ' (' + date + ')\n\n'; + str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n'; bySubpackage = groupBySubPackage( releaseCommits, pkg ); pkgNames = objectKeys( bySubpackage ).sort(); - out += sectionStart( 'packages' ); - out += heading( 'Packages', 3 ); + str += sectionStart( 'packages' ); + str += heading( 'Packages', 3 ); for ( j = 0; j < pkgNames.length; j++ ) { name = pkgNames[ j ]; summary = releaseSummary( bySubpackage[ name ], true, true ); if ( !summary ) { continue; } - out += packageSummaryWrapper( pkg, version, name, summary ); + str += packageSummaryWrapper( pkg, version, name, summary ); } - out += sectionEnd( 'packages' ); - out += breakingChanges( releaseCommits ); - out += closedIssues( releaseCommits ); - out += formatContributors( releaseCommits ); - out += formatCommits( releaseCommits ); + str += sectionEnd( 'packages' ); + str += breakingChanges( releaseCommits ); + str += closedIssues( releaseCommits ); + str += formatContributors( releaseCommits ); + str += formatCommits( releaseCommits ); } } else { for ( i = releases.length-1; i >= 0; i-- ) { @@ -359,14 +361,16 @@ function generate( pkg, releaseType ) { if ( !summary ) { continue; } - date = formatDate( releases[ i ][ 1 ] ); - out += releaseSectionStart( version ); - out += '## ' + version + ' (' + date + ')\n\n'; - out += summary; - out += sectionEnd( 'release' ); + str += releaseSectionStart( version ); + str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n'; + str += summary; + str += sectionEnd( 'release' ); } } - return replace( out, RE_EXTRANEOUS_NEWLINES, '\n\n' ); + return { + 'content': replace( str, RE_EXTRANEOUS_NEWLINES, '\n\n' ), + 'releaseType': releaseType || null + }; /** * Returns an indicator for a commit based on its release status. diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js index fd4e9aba6d5..ac1b96095ce 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -21,6 +21,7 @@ // MODULES // var tape = require( 'tape' ); +var isObject = require( '@stdlib/assert/is-plain-object' ); var generate = require( './../lib' ); @@ -105,28 +106,52 @@ tape( 'the function throws an error if provided an invalid release type', functi } }); -tape( 'the function returns a changelog', function test( t ) { +tape( 'the function generates a changelog', function test( t ) { var out = generate( '@stdlib/utils/curry' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); out = generate( '@stdlib/stats/base/dists' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); out = generate( '@stdlib/proxy' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); t.end(); }); -tape( 'the function returns a changelog for a new release', function test( t ) { +tape( 'the function generates a changelog for a new release', function test( t ) { var out = generate( '@stdlib/utils/noop', 'patch' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, 'patch', 'has `releaseType` property' ); out = generate( '@stdlib/utils/noop', 'minor' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, 'minor', 'has `releaseType` property' ); out = generate( '@stdlib/utils/noop', 'major' ); - t.equal( typeof out, 'string', 'returns a string' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( out.releaseType, 'major', 'has `releaseType` property' ); + + t.end(); +}); + +tape( 'the function generates a changelog for a new release (auto)', function test( t ) { + var out = generate( '@stdlib/utils/noop', 'auto' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + + out = generate( '@stdlib/array/base/slice', 'auto' ); + t.strictEqual( isObject( out ), true, 'returns an object' ); + t.strictEqual( typeof out.content, 'string', 'has `content` property' ); t.end(); }); diff --git a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js index be0f54097f7..aa34d981407 100644 --- a/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js +++ b/lib/node_modules/@stdlib/_tools/scripts/publish_packages.js @@ -622,10 +622,12 @@ function publish( pkg, clbk ) { var ghpagesOpts; var jscodeshift; var pkgJsonPath; + var releaseType; var extractCLI; var repoExists; var readmePath; var noBundles; + var changelog; var contents; var mainJSON; var schedule; @@ -681,6 +683,9 @@ function publish( pkg, clbk ) { return invokeCallback( null, 'skipped' ); } + changelog = generateChangelog( '@stdlib/'+pkg, flags[ 'release-type' ] ); + releaseType = changelog.releaseType; + mainJSON = readJSON( join( mainDir, 'package.json' ) ); console.log( 'Creating directory and copying source files...' ); @@ -736,7 +741,7 @@ function publish( pkg, clbk ) { fs.copyFileSync( join( __dirname, 'templates', '.npmignore.txt' ), join( dist, '.npmignore' ) ); fs.copyFileSync( join( __dirname, 'templates', 'Makefile.txt' ), join( dist, 'Makefile' ) ); fs.copyFileSync( join( __dirname, 'templates', '.eslintrc.js.txt' ), join( dist, '.eslintrc.js' ) ); - writeFileSync( join( dist, 'CHANGELOG.md' ), generateChangelog( '@stdlib/'+pkg, flags[ 'release-type' ] ) ); + writeFileSync( join( dist, 'CHANGELOG.md' ), changelog.content ); fs.copyFileSync( join( __dirname, 'templates', 'SECURITY.md.txt' ), join( dist, 'SECURITY.md' ) ); pkgJsonPath = join( dist, 'package.json' ); @@ -778,12 +783,12 @@ function publish( pkg, clbk ) { devDeps.push( 'proxyquire' ); } triggerRelease = ( - flags[ 'release-type' ] === 'patch' || - flags[ 'release-type' ] === 'minor' || - flags[ 'release-type' ] === 'major' + releaseType === 'patch' || + releaseType === 'minor' || + releaseType === 'major' ); if ( triggerRelease ) { - version = semver.inc( version, flags[ 'release-type' ] ); + version = semver.inc( version, releaseType ); } debug( 'Copying and populating README.md file...' ); readmePath = join( dist, 'README.md' ); From 1cf64b3c41cf3aae2e2f87f2548d5457abc9456f Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 18 Feb 2024 16:59:54 -0500 Subject: [PATCH 27/46] build: move git log cmd to standalone script --- .../changelog/parse-commits/lib/commits.js | 25 +++++------ .../parse-commits/scripts/commits.sh | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+), 12 deletions(-) create mode 100755 lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js index 8e057b0faef..7f1231e9be4 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -20,7 +20,9 @@ // MODULES // -var shell = require( 'child_process' ).execSync; // eslint-disable-line node/no-sync +var resolve = require( 'path' ).resolve; +var join = require( 'path' ).join; +var spawn = require( 'child_process' ).spawnSync; // eslint-disable-line node/no-sync var logger = require( 'debug' ); var replace = require( '@stdlib/string/replace' ); var trim = require( '@stdlib/string/trim' ); @@ -29,7 +31,7 @@ var trim = require( '@stdlib/string/trim' ); // VARIABLES // var debug = logger( 'changelog:parse-commits' ); -var GIT_COMMIT_SEP = '$---$'; +var GIT_COMMIT_SEP = '^---^'; // FUNCTIONS // @@ -67,26 +69,25 @@ function extractCommits( dir ) { var commits; var lines; var parts; + var args; var opts; var cmd; var out; var i; opts = { - 'cwd': dir, 'maxBuffer': 1024*1024*60, // 60 MB - 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr + 'stdio': ['pipe', 'pipe', 'ignore'], // stdin, stdout, stderr + 'env': { + 'GIT_COMMIT_SEP': GIT_COMMIT_SEP + } }; - cmd = 'git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|" .'; - - // Insert a separator between commits: - cmd += ' | '; - cmd += 'awk \'/^$/{p=1;next} /^[0-9a-f]{40}\\|/{if (p==1) print "'+GIT_COMMIT_SEP+'"; p=0} {print}\''; - cmd += 'fi\n'; - + cmd = join( __dirname, '..', 'scripts', 'commits.sh' ); commits = []; try { - out = trim( shell( cmd, opts ).toString() ); + args = [ resolve( dir ) ]; + out = spawn( cmd, args, opts ); + out = trim( out.stdout.toString() ); } catch ( err ) { debug( 'Encountered an error when attempting to extract commits: %s', err.message ); return commits; diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh new file mode 100755 index 00000000000..c152f355481 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# +# @license Apache-2.0 +# +# Copyright (c) 2024 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Script for collecting commits for a given directory. +# +# Usage: commits.sh +# +# Arguments: +# +# dir Directory to search for commits. +# +# Environment variables: +# +# GIT_COMMIT_SEP: separator for separating commits in the output stream (default: '^---^') +# + +if [ -z "$1" ]; then + echo "Error: must provide a directory to search for commits." >&2; + exit 1; +fi + +if [ -z "$GIT_COMMIT_SEP" ]; then + GIT_COMMIT_SEP="^---^"; # Default separator +fi + +git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|" "$1" | awk -v sep="$GIT_COMMIT_SEP" '/^$/{p=1;next} /^[0-9a-f]{40}\|/{if (p==1) print sep; p=0} {print}'; From 0480d9388f190ebb2c142c3bbba3a02925d6400d Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 18 Feb 2024 17:15:09 -0500 Subject: [PATCH 28/46] build: minor doc fixes and clean-up --- .../@stdlib/_tools/changelog/generate/lib/index.js | 4 ++-- .../@stdlib/_tools/changelog/generate/lib/main.js | 13 +++++++------ .../changelog/recommend-version-bump/README.md | 4 +++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js index 3b7c68c049c..0d55e217101 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/index.js @@ -28,11 +28,11 @@ * * // Standard packages: * var changelog = generateChangelog( '@stdlib/utils/curry' ); -* // returns '...' +* // returns {...} * * // Namespace packages: * changelog = generateChangelog( '@stdlib/stats/base/dists' ); -* // returns '...' +* // returns {...} */ // MODULES // diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 3bf8d004f3e..76fa5a4c7e2 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -24,6 +24,7 @@ var dirname = require( 'path' ).dirname; var join = require( 'path' ).join; var semver = require( 'semver' ); var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version-bump' ); +var name2standalone = require( '@stdlib/_tools/pkgs/name2standalone' ); var substringAfter = require( '@stdlib/string/substring-after' ); var objectEntries = require( '@stdlib/utils/entries' ); var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); @@ -216,23 +217,23 @@ function packageSummaryWrapper( pkg, version, name, summary ) { * * @example * var changelog = generate( '@stdlib/utils/curry' ); -* // returns '...' +* // returns {...} * * @example * var changelog = generate( '@stdlib/stats/base/dists' ); -* // returns '...' +* // returns {...} * * @example * var changelog = generate( '@stdlib/proxy' ); -* // returns '...' +* // returns {...} * * @example * var changelog = generate( '@stdlib/utils/curry', 'patch' ); -* // returns '...' +* // returns {...} * * @example * var changelog = generate( '@stdlib/utils/curry', 'major' ); -* // returns '...' +* // returns {...} */ function generate( pkg, releaseType ) { var isNamespacePkg; @@ -264,8 +265,8 @@ function generate( pkg, releaseType ) { } else { // Case: all other packages isNamespacePkg = contains( STDLIB_NAMESPACE_PKGS, pkg ); + standalone = name2standalone( pkg ); pkg = replace( pkg, '@stdlib/', '' ); - standalone = '@stdlib/' + replace( pkg, '/', '-' ); releases = objectEntries( npmReleases( standalone ) ); } diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md index 72694c23d43..7d594953a8c 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md @@ -32,7 +32,7 @@ var recommendVersionBump = require( '@stdlib/_tools/changelog/recommend-version- #### recommendVersionBump( commits ) -Recommends a version bump based on parsed commit messages. +Recommends a version bump based on [parsed commit messages][@stdlib/_tools/changelog/parse-commits]. ```javascript var commits = [ @@ -111,6 +111,8 @@ version = recommendVersionBump( commits ); From c7469b0de9cde4fc65a9e7aa57d2a2cb38719b0b Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 01:42:43 -0700 Subject: [PATCH 29/46] Apply suggestions from code review Signed-off-by: Athan --- .../_tools/changelog/parse-commits/test/test.validate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js index d7f6010e172..edec79b98f5 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.validate.js @@ -84,7 +84,7 @@ tape( 'if provided a `dir` option which is not a `string`, the function returns t.end(); }); -tape( 'if provided a `issueURL` option which is not a `string`, the function returns a type error', function test( t ) { +tape( 'if provided an `issueURL` option which is not a `string`, the function returns a type error', function test( t ) { var values; var opts; var err; From 63a4a338e5e0ebb03b44033314f161b530545c4c Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 01:44:43 -0700 Subject: [PATCH 30/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/generate/lib/npm_releases.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js index b198ab72b4a..6e455b48e8d 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/npm_releases.js @@ -53,7 +53,7 @@ function npmReleases( pkg ) { command = 'npm view ' + pkg + ' time --json'; opts = { 'cwd': rootDir(), - 'stdio': ['pipe', 'pipe', 'ignore'] // stdin, stdout, stderr + 'stdio': [ 'pipe', 'pipe', 'ignore' ] // stdin, stdout, stderr }; try { out = shell( command, opts ).toString(); From 1605a4a788d20dd1305718ead476c6c12383b9c3 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:15:55 -0700 Subject: [PATCH 31/46] Apply suggestions from code review Signed-off-by: Athan --- .../_tools/changelog/generate/lib/format_contributors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js index a58a201460d..f69a318ab56 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -66,7 +66,7 @@ function extractContributors( commits ) { !contains( out, mention.ref ) && !contains( EXCLUDED_CONTRIBUTORS, mention.ref ) ) { - out.push( mentions[ j ].ref ); + out.push( mention.ref ); } } } From 2059ad3195ecd0f4386e078670d5e6830cbbc720 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:19:28 -0700 Subject: [PATCH 32/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/parse-commits/lib/commits.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js index 7f1231e9be4..2cc66c27bc9 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -77,21 +77,21 @@ function extractCommits( dir ) { opts = { 'maxBuffer': 1024*1024*60, // 60 MB - 'stdio': ['pipe', 'pipe', 'ignore'], // stdin, stdout, stderr + 'stdio': [ 'pipe', 'pipe', 'ignore' ], // stdin, stdout, stderr 'env': { 'GIT_COMMIT_SEP': GIT_COMMIT_SEP } }; cmd = join( __dirname, '..', 'scripts', 'commits.sh' ); commits = []; + args = [ resolve( dir ) ]; try { - args = [ resolve( dir ) ]; out = spawn( cmd, args, opts ); - out = trim( out.stdout.toString() ); } catch ( err ) { debug( 'Encountered an error when attempting to extract commits: %s', err.message ); return commits; } + out = trim( out.stdout.toString() ); lines = out.split( GIT_COMMIT_SEP ); for ( i = 0; i < lines.length; i++ ) { if ( !lines[ i ] ) { From a17fb4049ad48d16bc8d214b815785231983436f Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:22:49 -0700 Subject: [PATCH 33/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/parse-commits/lib/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js index f625bcf3f9c..8a2290d476d 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -86,7 +86,7 @@ function parseCommits( options ) { try { ast = parser( msg ); } catch ( err ) { - debug( 'Encountered an error when parsing a commit message: %s. Message: %s', err.message, commits[ i ] ); + debug( 'Encountered an error when parsing a commit message: %s. Error: %s', msg, err.message ); continue; // Skip this commit if parsing fails... } merged = merge( commits[ i ], toConventionalChangelog( ast, opts ) ); From d7e748f252dd8afca8a3fef50530398d916a3bcc Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:26:12 -0700 Subject: [PATCH 34/46] Apply suggestions from code review Signed-off-by: Athan --- .../changelog/parse-commits/test/test.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js index 33cefc83f33..f70d02999d9 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -81,7 +81,7 @@ tape( 'the function returns an empty array if unable to resolve a directory or i }; var out = parseCommits( opts ); t.strictEqual( isArray( out ), true, 'returns an array' ); - t.strictEqual( out.length, 0, 'has length 0' ); + t.strictEqual( out.length, 0, 'returns expected value' ); t.end(); }); @@ -99,18 +99,18 @@ tape( 'the function returns an array of conventional changelog formatted commit t.strictEqual( commits.length > 0, true, 'has length greater than 0' ); for ( i = 0; i < commits.length; i++ ) { commit = commits[ i ]; - t.strictEqual( isPlainObject( commit ), true, 'element is an object' ); - t.strictEqual( hasOwnProp( commit, 'hash' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'date' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'author' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'message' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'type' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'scope' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'subject' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'body' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'footer' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'notes' ), true, 'has property' ); - t.strictEqual( hasOwnProp( commit, 'references' ), true, 'has property' ); + t.strictEqual( isPlainObject( commit ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'hash' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'date' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'author' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'message' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'type' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'scope' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'subject' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'body' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'footer' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'notes' ), true, 'returns expected value' ); + t.strictEqual( hasOwnProp( commit, 'references' ), true, 'returns expected value' ); } t.end(); }); From bd4c24046fab51451894eac232bd17b3455bc0cd Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:26:36 -0700 Subject: [PATCH 35/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/parse-commits/test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js index f70d02999d9..84cacaf8499 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -80,7 +80,7 @@ tape( 'the function returns an empty array if unable to resolve a directory or i 'dir': 'beepboop' }; var out = parseCommits( opts ); - t.strictEqual( isArray( out ), true, 'returns an array' ); + t.strictEqual( isArray( out ), true, 'returns expected value' ); t.strictEqual( out.length, 0, 'returns expected value' ); t.end(); }); From 4b372aea6f6cae4c7edffdb89d69b14f9a4a306b Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:27:57 -0700 Subject: [PATCH 36/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/parse-commits/test/test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js index 84cacaf8499..60a40669be7 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -67,6 +67,7 @@ tape( 'the function throws an error if provided an argument that is not an objec tape( 'the function throws an error if provided an invalid option', function test( t ) { t.throws( badValue, TypeError, 'throws error' ); t.end(); + function badValue() { var opts = { 'dir': null @@ -95,8 +96,8 @@ tape( 'the function returns an array of conventional changelog formatted commit 'dir': join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' ) }; commits = parseCommits( opts ); - t.strictEqual( isArray( commits ), true, 'returns an array' ); - t.strictEqual( commits.length > 0, true, 'has length greater than 0' ); + t.strictEqual( isArray( commits ), true, 'returns expected value' ); + t.strictEqual( commits.length > 0, true, 'returns expected value' ); for ( i = 0; i < commits.length; i++ ) { commit = commits[ i ]; t.strictEqual( isPlainObject( commit ), true, 'returns expected value' ); From 3e7cac55362733f418103ef66312b36a0b6b0ccc Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:29:14 -0700 Subject: [PATCH 37/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/recommend-version-bump/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md index 7d594953a8c..d59f051ff7f 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md @@ -61,7 +61,7 @@ version = recommendVersionBump( commits ); // returns 'minor' ``` -The function returns a `string` indicating the recommended version bump or `null` if no version bump is recommended: +The function returns a string indicating the recommended version bump or `null` if no version bump is recommended: - `'patch'`: a patch version bump (bug fixes). - `'minor'`: a minor version bump (addition of functionality in a backward compatible manner). From b853de38462bd4dfac5a13dbc61480e8c96fbd05 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:30:43 -0700 Subject: [PATCH 38/46] Apply suggestions from code review Signed-off-by: Athan --- .../_tools/changelog/recommend-version-bump/lib/main.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js index afb2879b275..e6ee5a80198 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js @@ -23,13 +23,13 @@ /** * Recommends a version bump based on a list of commits. * -* @param {Array} commits - conventional changelog formatted commit message objects +* @param {Array} commits - conventional changelog formatted commit message objects * @returns {(string|null)} - `major`, `minor`, `patch`, or null */ function recommendVersionBump( commits ) { - var hasBreakingChange = false; - var hasFeature = false; - var hasFix = false; + var hasBreakingChange; + var hasFeature; + var hasFix; var commit; var i; var j; From 67972c66e6e257e77337005c924cb7d71e06feea Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:33:01 -0700 Subject: [PATCH 39/46] Apply suggestions from code review Signed-off-by: Athan --- .../changelog/recommend-version-bump/lib/main.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js index e6ee5a80198..d061dca3726 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js @@ -27,7 +27,6 @@ * @returns {(string|null)} - `major`, `minor`, `patch`, or null */ function recommendVersionBump( commits ) { - var hasBreakingChange; var hasFeature; var hasFix; var commit; @@ -38,8 +37,7 @@ function recommendVersionBump( commits ) { commit = commits[ i ]; for ( j = 0; j < commit.notes.length; j++ ) { if ( commit.notes[ j ].title === 'BREAKING CHANGE' ) { - hasBreakingChange = true; - break; + return 'major'; } } if ( commit.type === 'feat' ) { @@ -47,14 +45,6 @@ function recommendVersionBump( commits ) { } else if ( commit.type === 'fix' ) { hasFix = true; } - - // If a breaking change is found, no need to check further commits: - if ( hasBreakingChange ) { - break; - } - } - if ( hasBreakingChange ) { - return 'major'; } if ( hasFeature ) { return 'minor'; From 0b84a63587f5f6e717a1a23c63594c98f0c4fa0b Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:36:10 -0700 Subject: [PATCH 40/46] Apply suggestions from code review Signed-off-by: Athan --- .../changelog/recommend-version-bump/test/test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js index 104b7a0373c..539fe882ff2 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/test/test.js @@ -34,7 +34,7 @@ tape( 'main export is a function', function test( t ) { tape( 'the function returns `null` for an empty array of commits', function test( t ) { var result = recommendVersionBump( [] ); - t.equal( result, null, 'returns null for empty commit array' ); + t.equal( result, null, 'returns expected value' ); t.end(); }); @@ -49,8 +49,8 @@ tape( 'the function returns `\'patch\'` for commits with only bug fixes', functi 'notes': [] } ]; - var result = recommendVersionBump(commits); - t.equal( result, 'patch', 'returns `patch` for fix commits' ); + var result = recommendVersionBump( commits ); + t.equal( result, 'patch', 'returns expected value' ); t.end(); }); @@ -65,8 +65,8 @@ tape( 'the function returns `\'minor\'` for commits with backward-compatible fea 'notes': [] } ]; - var result = recommendVersionBump(commits); - t.equal( result, 'minor', 'returns `minor` for feature commits' ); + var result = recommendVersionBump( commits ); + t.equal( result, 'minor', 'returns expected value' ); t.end(); }); @@ -88,7 +88,7 @@ tape( 'the function returns `\'major\'` for commits with breaking changes', func }] } ]; - var result = recommendVersionBump(commits); - t.equal( result, 'major', 'returns `major` for breaking change commits' ); + var result = recommendVersionBump( commits ); + t.equal( result, 'major', 'returns expected value' ); t.end(); }); From 83437baa8de848d5aa9ee408c8ea54581ad78381 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:39:57 -0700 Subject: [PATCH 41/46] Apply suggestions from code review Signed-off-by: Athan --- .../_tools/changelog/generate/test/test.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js index ac1b96095ce..9e40c88bea1 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -108,50 +108,50 @@ tape( 'the function throws an error if provided an invalid release type', functi tape( 'the function generates a changelog', function test( t ) { var out = generate( '@stdlib/utils/curry' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, null, 'returns expected value' ); out = generate( '@stdlib/stats/base/dists' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, null, 'returns expected value' ); out = generate( '@stdlib/proxy' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, null, 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, null, 'returns expected value' ); t.end(); }); tape( 'the function generates a changelog for a new release', function test( t ) { var out = generate( '@stdlib/utils/noop', 'patch' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, 'patch', 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, 'patch', 'returns expected value' ); out = generate( '@stdlib/utils/noop', 'minor' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, 'minor', 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, 'minor', 'returns expected value' ); out = generate( '@stdlib/utils/noop', 'major' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); - t.strictEqual( out.releaseType, 'major', 'has `releaseType` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + t.strictEqual( out.releaseType, 'major', 'returns expected value' ); t.end(); }); tape( 'the function generates a changelog for a new release (auto)', function test( t ) { var out = generate( '@stdlib/utils/noop', 'auto' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); out = generate( '@stdlib/array/base/slice', 'auto' ); - t.strictEqual( isObject( out ), true, 'returns an object' ); - t.strictEqual( typeof out.content, 'string', 'has `content` property' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); t.end(); }); From a638b1a04895f22dd83ec0bb4439318672f63197 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:45:24 -0700 Subject: [PATCH 42/46] Apply suggestions from code review Signed-off-by: Athan --- .../parse-commits/lib/conventional_changelog.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js index ea61f312ac3..377eddab889 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -199,6 +199,7 @@ function toConventionalChangelog( ast, options ) { var closesIssue = false; var reference = {}; var hasPRURL = false; + visit( node, [ 'type', 'separator', 'text' ], processFooterNode ); if ( hasRefSeparator && RE_DIGITS.test( reference.ref ) ) { reference.prefix = '#'; @@ -219,11 +220,9 @@ function toConventionalChangelog( ast, options ) { case 'type': if ( node.value === 'PR-URL' ) { hasPRURL = true; - } - else if ( RE_CLOSES.test( node.value ) ) { + } else if ( RE_CLOSES.test( node.value ) ) { closesIssue = true; - } - else if ( RE_CO_AUTHORED_BY.test( node.value ) ) { + } else if ( RE_CO_AUTHORED_BY.test( node.value ) ) { coAuthoredBy = true; } reference.action = node.value; @@ -237,13 +236,11 @@ function toConventionalChangelog( ast, options ) { if ( node.value.includes( options.issueURL ) ) { reference.url = node.value; reference.ref = node.value.split( '/' ).pop(); - } - else if ( node.value.includes( options.prURL ) ) { + } else if ( node.value.includes( options.prURL ) ) { hasPRURL = true; reference.url = node.value; reference.ref = node.value.split('/').pop(); - } - else if ( node.value.charAt( 0 ) === '#' ) { + } else if ( node.value.charAt( 0 ) === '#' ) { hasRefSeparator = true; reference.ref = node.value.substring( 1 ); if ( !reference.url ) { From c1ed41b6dc3466bf58196118aff23c61a176466f Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:51:03 -0700 Subject: [PATCH 43/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/generate/lib/main.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 76fa5a4c7e2..0da8b8e6856 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -156,18 +156,15 @@ function groupBySubPackage( commits, pkg ) { */ function isNotInternalTooling( commit ) { var files; - var bool; var i; files = commit.files; - bool = false; for ( i = 0; i < files.length; i++ ) { if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) { - bool = true; - break; + return true; } } - return bool; + return false; } /** From ab1e03d547b03500f95ac8caf482ec90f60901ab Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 21:56:35 -0700 Subject: [PATCH 44/46] Apply suggestions from code review Signed-off-by: Athan --- .../@stdlib/_tools/changelog/generate/lib/main.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index 0da8b8e6856..ff39a644b6b 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -282,7 +282,7 @@ function generate( pkg, releaseType ) { // Group commits by release: commits = groupBy( commits, indicator ); - if (!commits.unreleased) { + if ( !commits.unreleased ) { commits.unreleased = []; } @@ -378,16 +378,15 @@ function generate( pkg, releaseType ) { * @returns {string} release indicator */ function indicator( commit ) { - var version; var date; var i; + + date = new Date( commit.date ); // Walk the releases in reverse chronological order: for ( i = releases.length-1; i >= 0; i-- ) { - version = releases[ i ][ 0 ]; - date = releases[ i ][ 1 ]; - if ( new Date( commit.date ) <= new Date( date ) ) { - return version; + if ( date <= new Date( releases[ i ][ 1 ] ) ) { + return releases[ i ][ 0 ]; // version } } return 'unreleased'; From f60c6dc655899466adbd31aba923f0f0d9c411d8 Mon Sep 17 00:00:00 2001 From: Athan Date: Sun, 2 Jun 2024 22:01:57 -0700 Subject: [PATCH 45/46] style: remove trailing spaces Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js index ff39a644b6b..92040e0509f 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -380,7 +380,6 @@ function generate( pkg, releaseType ) { function indicator( commit ) { var date; var i; - date = new Date( commit.date ); // Walk the releases in reverse chronological order: From 2bc6a2958fb5abb186b7c734dac535c19df140bd Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Mon, 3 Jun 2024 13:53:12 -0400 Subject: [PATCH 46/46] fix: replace includes with indexOf checks --- .../changelog/parse-commits/lib/conventional_changelog.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js index 377eddab889..b9089e4cdfb 100644 --- a/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/conventional_changelog.js @@ -228,15 +228,15 @@ function toConventionalChangelog( ast, options ) { reference.action = node.value; break; case 'separator': - if ( node.value.includes( '#' ) ) { + if ( node.value.indexOf( '#' ) !== -1 ) { hasRefSeparator = true; } break; case 'text': - if ( node.value.includes( options.issueURL ) ) { + if ( node.value.indexOf( options.issueURL ) !== 1 ) { reference.url = node.value; reference.ref = node.value.split( '/' ).pop(); - } else if ( node.value.includes( options.prURL ) ) { + } else if ( node.value.indexOf( options.prURL ) !== 1 ) { hasPRURL = true; reference.url = node.value; reference.ref = node.value.split('/').pop();