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/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..3f2791aa8aa --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/README.md @@ -0,0 +1,125 @@ + + +# Generate Changelog + +> Generate a changelog for a specified stdlib package. + +
+ +## Usage + +```javascript +var generate = require( '@stdlib/_tools/changelog/generate' ); +``` + +#### generate( pkg\[, releaseType] ) + +Generates a Markdown formatted changelog for a specified package. + +```javascript +var changelog = generate( '@stdlib/assert/contains' ); +// 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 {...} + +changelog = generate( '@stdlib/assert/contains', 'minor' ); +// returns {...} +``` + +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. +- `auto`: automatically determine the release type based on parsed commit messages. + +
+ + + +
+ +
+ + + +
+ +## Examples + +```javascript +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 new file mode 100644 index 00000000000..43b3f5f8ead --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/examples/index.js @@ -0,0 +1,47 @@ +/** +* @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( '@stdlib/utils/curry' ); +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' ); +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' ); +content = changelog.content; +console.log( content ); +// => '...' 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..5c4cad79aee --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/breaking_changes.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 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' ); + + +// 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; +} + +/** +* 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 // + +/** +* Extracts breaking changes from a list of commits and formats them 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.length === 0 ) { + return ''; + } + out = sectionStart( 'breaking-changes' ); + out += heading( 'BREAKING CHANGES', 3 ); + out += map( breakingChanges, formatBreakingChange ).join( '\n' ); + out += sectionEnd( 'breaking-changes' ); + return out; +} + + +// EXPORTS // + +module.exports = breakingChanges; 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 new file mode 100644 index 00000000000..fb833c9f62b --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/closed_issues.js @@ -0,0 +1,120 @@ +/** +* @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 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' ); + + +// 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. +* +* @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 + ')'; +} + +/** +* Formats closed issues as a comma-separated list. +* +* @private +* @param {ObjectArray} closedIssues - issue objects +* @returns {string} formatted issues list +*/ +function formatClosedIssues( closedIssues ) { + var out = sectionStart( 'issues' ); + out += heading( 'Closed Issues', 3 ); + 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 += sectionEnd( 'issues' ); + return out; +} + + +// MAIN // + +/** +* Extracts closed issues from a list of commits and formats them 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 ); + if ( closedIssues.length === 0 ) { + return ''; + } + closedIssues.sort( sortByIssueNumber ); + return formatClosedIssues( closedIssues ); +} + + +// EXPORTS // + +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/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_commit_factory.js b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commit_factory.js new file mode 100644 index 00000000000..2f5b8544496 --- /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_CO_AUTHORED_BY = /co-authored-by/i; +var RE_EMAIL = /\s*<[^>]+>\s*/; + + +// MAIN // + +/** +* Returns a function which formats a commit message for inclusion in a changelog. +* +* @private +* @param {boolean} type - boolean indicating whether to include the commit type +* @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, includeAuthors ) { + 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 authors = []; + var hash = trim( commit.hash ); + var out = '- [`'; + out += hash.substring( 0, 7 ); + out += '`]('; + out += STDLIB_GITHUB_URL; + out += '/'; + out += hash; + out += ') - '; + if ( commit.author ) { + authors.push( replace( commit.author, RE_EMAIL, '' ) ); + } + if ( type ) { + out += '**'; + out += commit.type; + if ( commit.scope ) { + out += '(' + commit.scope + ')'; + } + out += ':** '; + } else if ( commit.scope ) { + out += '**' + commit.scope + ':** '; + } + out += commit.subject; + if ( commit.mentions && commit.mentions.length > 0 ) { + commit.mentions.forEach( onMention ); + } + if ( includeAuthors ) { + out += ' _(by ' + authors.join( ', ' ) + ')_'; + } + return out; + + /** + * Callback invoked upon encountering a mention. + * + * @private + * @param {Object} mention - mention object + */ + function onMention( mention ) { + 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, '' ) ); + } + } + } +} + + +// 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..93c8ab899cd --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_commits.js @@ -0,0 +1,52 @@ +/** +* @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 sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.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 = sectionStart( 'commits' ); + out += heading( 'Commits', 3 ); + out += '
\n\n'; + out += map( commits, formatCommitFactory( true, true ) ).join( '\n' ); + out += '\n\n
'; + out += sectionEnd( 'commits' ); + 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..f69a318ab56 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/format_contributors.js @@ -0,0 +1,118 @@ +/** +* @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 sectionStart = require( './section_start.js' ); +var sectionEnd = require( './section_end.js' ); +var heading = require( './heading.js' ); +var EXCLUDED_CONTRIBUTORS = require( './excluded_contributors.json' ); + + +// VARIABLES // + +var RE_CO_AUTHORED_BY = /co-authored-by/i; + + +// 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 mentions; + var mention; + var out; + var i; + var j; + + 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 ); + } + 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( mention.ref ); + } + } + } + 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 ) { + return '- ' + replace( contributor, /\s*<[^>]+>\s*/, '' ); +} + + +// 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 = 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'; + } 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 += sectionEnd( 'contributors' ); + 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..04394a1ebdb --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/heading.js @@ -0,0 +1,54 @@ +/** +* @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 = '\n\n'; + str += repeat( '#', level ) + ' ' + text; + str += '\n\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..0d55e217101 --- /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 stdlib package. +* +* @module @stdlib/_tools/changelog/generate +* +* @example +* var generateChangelog = require( '@stdlib/_tools/changelog/generate' ); +* +* // Standard packages: +* var changelog = generateChangelog( '@stdlib/utils/curry' ); +* // returns {...} +* +* // Namespace packages: +* changelog = generateChangelog( '@stdlib/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..92040e0509f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/main.js @@ -0,0 +1,398 @@ +/** +* @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 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' ); +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' ); +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' ); +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' ); + + +// 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]*$/; +var RE_MARKDOWN_HEADER = /(^#+)/gm; +var RE_EXTRANEOUS_NEWLINES = /\n{3,}/g; + + +// FUNCTIONS // + +/** +* 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, 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, n ) { + return replace( str, RE_MARKDOWN_HEADER, '$1' + repeat( '#', n ) ); +} + +/** +* 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 ); +} + +/** +* Generates an opening release section tag. +* +* @private +* @param {string} [version] - release version +* @returns {string} release section tag +*/ +function releaseSectionStart( version ) { + if ( version ) { + return '
\n\n'; + } + return '
\n\n'; +} + +/** +* 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; +} + +/** +* 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 i; + + files = commit.files; + for ( i = 0; i < files.length; i++ ) { + if ( files[ i ].indexOf( 'lib/node_modules/@stdlib/_tools/' ) === -1 ) { + return true; + } + } + return false; +} + +/** +* 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, '/', '-' ); + if ( version ) { + id += '-v' + version; + } else { + id += '-unreleased'; + } + out = '
\n\n'; + out += heading( '[@stdlib/' + name + '](' + href + ')', 4 ); + out += '
\n\n'; + out += decrementHeaders( summary, 2 ); + out += '
'; + out += sectionEnd( 'package' ); + return out; +} + + +// MAIN // + +/** +* 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`, `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 {Object} changelog `content` and `releaseType` (`null` if no release) +* +* @example +* var changelog = generate( '@stdlib/utils/curry' ); +* // returns {...} +* +* @example +* var changelog = generate( '@stdlib/stats/base/dists' ); +* // returns {...} +* +* @example +* var changelog = generate( '@stdlib/proxy' ); +* // returns {...} +* +* @example +* var changelog = generate( '@stdlib/utils/curry', 'patch' ); +* // returns {...} +* +* @example +* var changelog = generate( '@stdlib/utils/curry', 'major' ); +* // returns {...} +*/ +function generate( pkg, releaseType ) { + var isNamespacePkg; + var releaseCommits; + var newestRelease; + var bySubpackage; + var nextVersion; + var standalone; + var unreleased; + var releases; + var pkgNames; + var commits; + var version; + var summary; + var name; + var str; + var i; + var j; + + if ( !isString( pkg ) ) { + throw new TypeError( format( 'invalid argument. Must provide a string. Value: `%s`.', 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, pkg ); + standalone = name2standalone( pkg ); + pkg = replace( pkg, '@stdlib/', '' ); + releases = objectEntries( npmReleases( standalone ) ); + } + + str = '# CHANGELOG\n\n'; + str += '> Package changelog.\n\n'; + + commits = parseCommits({ + 'dir': join( STDLIB_LIB_DIR, pkg ) + }); + if ( 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: + commits = filter( commits, isNotInternalTooling ); + + // Group commits by release: + commits = groupBy( commits, indicator ); + if ( !commits.unreleased ) { + commits.unreleased = []; + } + + if ( releaseType === 'auto' ) { + releaseType = recommendVersionBump( commits.unreleased ); + } + if ( releaseType ) { + newestRelease = releases[ releases.length-1 ][ 0 ]; + nextVersion = semver.inc( newestRelease, releaseType ); + if ( isNull( nextVersion ) ) { + throw new TypeError( format( 'invalid argument. Unrecognized release type: `%s`.', releaseType ) ); + } + } + + if ( isNamespacePkg ) { + str += releaseSectionStart( nextVersion ); + str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; + bySubpackage = groupBySubPackage( commits.unreleased, pkg ); + pkgNames = objectKeys( bySubpackage ).sort(); + 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 ) { + str += packageSummaryWrapper( pkg, '', name, unreleased ); + } + } + 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 ) { + str += releaseSectionStart( nextVersion ); + str += '## ' + ( nextVersion || 'Unreleased' ) + ' (' + formatDate() + ')\n\n'; + str += unreleased; + str += sectionEnd( 'release' ); + } + } + if ( isNamespacePkg ) { + for ( i = releases.length-1; i >= 0; i-- ) { + version = releases[ i ][ 0 ]; + releaseCommits = commits[ version ]; + if ( !releaseCommits ) { + continue; + } + str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n'; + bySubpackage = groupBySubPackage( releaseCommits, pkg ); + pkgNames = objectKeys( bySubpackage ).sort(); + 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; + } + str += packageSummaryWrapper( pkg, version, name, summary ); + } + str += sectionEnd( 'packages' ); + str += breakingChanges( releaseCommits ); + str += closedIssues( releaseCommits ); + str += formatContributors( releaseCommits ); + str += formatCommits( releaseCommits ); + } + } else { + for ( i = releases.length-1; i >= 0; i-- ) { + version = releases[ i ][ 0 ]; + summary = releaseSummary( commits[ version ] ); + if ( !summary ) { + continue; + } + str += releaseSectionStart( version ); + str += '## ' + version + ' (' + formatDate( releases[ i ][ 1 ] ) + ')\n\n'; + str += summary; + str += sectionEnd( 'release' ); + } + } + return { + 'content': replace( str, RE_EXTRANEOUS_NEWLINES, '\n\n' ), + 'releaseType': releaseType || null + }; + + /** + * Returns an indicator for a commit based on its release status. + * + * @private + * @param {Object} commit - commit object + * @returns {string} release indicator + */ + function indicator( commit ) { + var date; + var i; + date = new Date( commit.date ); + + // Walk the releases in reverse chronological order: + for ( i = releases.length-1; i >= 0; i-- ) { + if ( date <= new Date( releases[ i ][ 1 ] ) ) { + return releases[ i ][ 0 ]; // version + } + } + return 'unreleased'; + } +} + + +// 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..6e455b48e8d --- /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(), + 'stdio': [ 'pipe', 'pipe', 'ignore' ] // stdin, stdout, stderr + }; + try { + out = shell( command, opts ).toString(); + out = JSON.parse( out ); + } catch ( err ) { + debug( 'Encountered an error when attempting to retrieve package release dates: %s', err.message ); + return {}; + } + out = omit( out, OMITTED_KEYS ); + 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..e96989e9d69 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/lib/release_summary.js @@ -0,0 +1,116 @@ +/** +* @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 groupBy = require( '@stdlib/utils/group-by' ); +var map = require( '@stdlib/utils/map' ); +var formatCommitFactory = require( './format_commit_factory.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 sectionStart = require( './section_start.js' ); +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 // + +/** +* 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 groupedFormat; + var grouped; + var out; + + if ( !commits || !commits.length ) { + return ''; + } + grouped = groupBy( commits, groupByType ); + groupedFormat = formatCommitFactory( false, false ); + + out = ''; + if ( grouped.feat ) { + out += sectionStart( 'features' ); + out += heading( 'Features', 3 ); + out += map( grouped.feat, groupedFormat ).join( '\n' ); + out += sectionEnd( 'features' ); + } + if ( grouped.fix ) { + out += sectionStart( 'bug-fixes' ); + out += heading( 'Bug Fixes', 3 ); + out += map( grouped.fix, groupedFormat ).join( '\n' ); + out += sectionEnd( 'bug-fixes' ); + } + if ( grouped.perf ) { + out += sectionStart( 'performance' ); + out += heading( 'Performance', 3 ); + out += map( grouped.perf, groupedFormat ).join( '\n' ); + out += sectionEnd( 'performance' ); + } + if ( grouped.revert ) { + out += sectionStart( 'reverts' ); + out += heading( 'Reverts', 3 ); + out += map( grouped.revert, groupedFormat ).join( '\n' ); + out += sectionEnd( 'reverts' ); + } + if ( grouped.deprecate ) { + out += sectionStart( 'deprecations' ); + out += heading( 'Deprecations', 3 ); + out += map( grouped.deprecate, groupedFormat ).join( '\n' ); + out += sectionEnd( 'deprecations' ); + } + out += breakingChanges( commits ); + out += closedIssues( commits ); + if ( !excludeCommits ) { + out += formatCommits( commits ); + } + if ( !excludeContributors ) { + out += formatContributors( commits ); + } + return out; +} + + +// EXPORTS // + +module.exports = releaseSummary; 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; 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..313dfeb9b77 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/package.json @@ -0,0 +1,62 @@ +{ + "name": "@stdlib/_tools/changelog/generate", + "version": "0.0.0", + "description": "Generate a changelog for a specified stdlib 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" + } + ], + "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..9e40c88bea1 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/generate/test/test.js @@ -0,0 +1,157 @@ +/** +* @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 isObject = require( '@stdlib/assert/is-plain-object' ); +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 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( '@stdlib/utils/curry', value ); + }; + } +}); + +tape( 'the function generates a changelog', function test( t ) { + var out = generate( '@stdlib/utils/curry' ); + 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 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 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 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 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 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 expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + + out = generate( '@stdlib/array/base/slice', 'auto' ); + t.strictEqual( isObject( out ), true, 'returns expected value' ); + t.strictEqual( typeof out.content, 'string', 'returns expected value' ); + + 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..9a6ea4f2dd0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/README.md @@ -0,0 +1,92 @@ + + +# Parse Commits + +> Parse commit messages into conventional changelog objects. + +
+ +## Usage + +```javascript +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +``` + +#### parseCommits( \[options] ) + +Parses commit messages into conventional changelog objects. + +```javascript +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/`. + +
+ + + +
+ +
+ + + +
+ +## Examples + +```javascript +var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); + +var commits = parseCommits(); +// returns [...] + +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 new file mode 100644 index 00000000000..838b1b83588 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/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 parseCommits = require( './../lib' ); + +var commits = parseCommits(); +console.log( commits ); +// => [...] + +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 new file mode 100644 index 00000000000..2cc66c27bc9 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js @@ -0,0 +1,115 @@ +/** +* @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 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' ); + + +// VARIABLES // + +var debug = logger( 'changelog:parse-commits' ); +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 directory. +* +* @private +* @param {string} dir - directory for which to extract commit details +* @returns {Array} array of commit details +* +* @example +* var commits = extractCommits( '.' ); +* // returns [...] +*/ +function extractCommits( dir ) { + var commits; + var lines; + var parts; + var args; + var opts; + var cmd; + var out; + var i; + + opts = { + 'maxBuffer': 1024*1024*60, // 60 MB + '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 { + out = spawn( cmd, args, opts ); + } 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 ] ) { + 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..b9089e4cdfb --- /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 RE_DIGITS = /^[0-9]+$/; +var RE_CLOSES = /(?:closes|fixes|resolves)/i; +var RE_CO_AUTHORED_BY = /co-authored-by/i; + + +// MAIN // + +/** +* Converts a commit message AST to the conventional changelog format. +* +* @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, options ) { + 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 hasRefSeparator = false; + var coAuthoredBy = false; + var closesIssue = false; + var reference = {}; + var hasPRURL = false; + + visit( node, [ 'type', 'separator', 'text' ], processFooterNode ); + if ( hasRefSeparator && RE_DIGITS.test( reference.ref ) ) { + reference.prefix = '#'; + out.references.push( reference ); + } + if ( hasPRURL || closesIssue || coAuthoredBy ) { + 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 ( 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': + if ( node.value.indexOf( '#' ) !== -1 ) { + hasRefSeparator = true; + } + break; + case 'text': + if ( node.value.indexOf( options.issueURL ) !== 1 ) { + reference.url = node.value; + reference.ref = node.value.split( '/' ).pop(); + } else if ( node.value.indexOf( options.prURL ) !== 1 ) { + hasPRURL = true; + reference.url = node.value; + reference.ref = node.value.split('/').pop(); + } else if ( node.value.charAt( 0 ) === '#' ) { + hasRefSeparator = true; + reference.ref = node.value.substring( 1 ); + if ( !reference.url ) { + if ( closesIssue ) { + reference.url = options.issueURL + reference.ref; + } else if ( hasPRURL ) { + reference.url = options.prURL + reference.ref; + } + } + } else { + reference.ref = node.value; + if ( !reference.url ) { + if ( closesIssue ) { + reference.url = options.issueURL + reference.ref; + } else if ( hasPRURL ) { + reference.url = options.prURL + reference.ref; + } + } + } + break; + default: + break; + } + } + } +} + + +// EXPORTS // + +module.exports = toConventionalChangelog; 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 new file mode 100644 index 00000000000..70bce8a0a01 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/index.js @@ -0,0 +1,42 @@ +/** +* @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 join = require( 'path' ).join; +* var rootDir = require( '@stdlib/_tools/utils/root-dir' ); +* var parseCommits = require( '@stdlib/_tools/changelog/parse-commits' ); +* +* var out = parseCommits( join( rootDir(), 'lib', 'node_modules', '@stdlib', '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..8a2290d476d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/main.js @@ -0,0 +1,104 @@ +/** +* @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 resolve = require( 'path' ).resolve; +var logger = require( 'debug' ); +var parser = require( '@conventional-commits/parser' ).parser; +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 // + +var debug = logger( 'changelog:parse-commits' ); + + +// MAIN // + +/** +* Parses package commit messages into conventional changelog objects. +* +* @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 resolve = require( 'path' ).resolve; +* var out = parseCommits({ +* 'dir': resolve( __dirname, '..', '..' ) +* }); +*/ +function parseCommits( options ) { + var commits; + var merged; + var opts; + var msg; + var ast; + var dir; + var err; + var out; + var i; + var j; + + 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( dir ); + out = []; + for ( i = 0; i < commits.length; i++ ) { + msg = commits[ i ].message; + try { + ast = parser( msg ); + } catch ( err ) { + 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 ) ); + for ( j = 0; j < merged.notes.length; j++ ) { + merged.notes[ j ].hash = commits[ i ].hash; + } + out.push( merged ); + } + return out; +} + + +// EXPORTS // + +module.exports = parseCommits; 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 new file mode 100644 index 00000000000..9b1f45cd5fd --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/package.json @@ -0,0 +1,61 @@ +{ + "name": "@stdlib/_tools/changelog/parse-commits", + "version": "0.0.0", + "description": "Parse 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" + } + ], + "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/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}'; 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..60a40669be7 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/parse-commits/test/test.js @@ -0,0 +1,117 @@ +/** +* @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 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' ); +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 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() {} + ]; + + 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 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 expected value' ); + t.strictEqual( out.length, 0, 'returns expected value' ); + 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; + + opts = { + 'dir': join( rootDir(), 'lib', 'node_modules', '@stdlib', 'utils', 'curry' ) + }; + commits = parseCommits( opts ); + 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' ); + 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(); +}); 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..edec79b98f5 --- /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 an `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(); +}); 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..d59f051ff7f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/README.md @@ -0,0 +1,118 @@ + + +# 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][@stdlib/_tools/changelog/parse-commits]. + +```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..d061dca3726 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/changelog/recommend-version-bump/lib/main.js @@ -0,0 +1,61 @@ +/** +* @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 hasFeature; + var hasFix; + 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' ) { + return 'major'; + } + } + if ( commit.type === 'feat' ) { + hasFeature = true; + } else if ( commit.type === 'fix' ) { + hasFix = true; + } + } + 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..539fe882ff2 --- /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 expected value' ); + 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 expected value' ); + 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 expected value' ); + 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 expected value' ); + 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 3ffabe2f10b..aa34d981407 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 = [ '