diff --git a/.eslintrc b/.eslintrc index 8afc31e2b..ca4880fb6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,14 +1,14 @@ { - "extends": ["standard", "plugin:mocha/recommended"], - "plugins": [ - "mocha", - "@fintechstudios/eslint-plugin-chai-as-promised", - ], + "extends": ["prettier", "eslint:recommended", "plugin:mocha/recommended"], + "env": { "node": true, "es6": true }, + "parserOptions": { "ecmaVersion": 2023 }, + "plugins": ["mocha", "@fintechstudios/eslint-plugin-chai-as-promised"], "rules": { "no-var": "error", + "no-unused-vars": ["error", { "argsIgnorePattern": "_.*" }], "mocha/max-top-level-suites": "off", "mocha/no-setup-in-describe": "off", "@fintechstudios/chai-as-promised/no-unhandled-promises": "error", "@fintechstudios/chai-as-promised/no-await-in-condition": "error" } -} \ No newline at end of file +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8cd04782b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.ts text eol=lf +*.js text eol=lf \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..8db60caac --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/bin/cli.js b/bin/cli.js index f9de11293..4f7e08310 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -3,12 +3,12 @@ /* istanbul ignore if */ if (process.version.match(/v(\d+)\./)[1] < 6) { console.error( - 'commit-and-tag-version: Node v6 or greater is required. `commit-and-tag-version` did not run.' - ) + 'commit-and-tag-version: Node v6 or greater is required. `commit-and-tag-version` did not run.', + ); } else { - const standardVersion = require('../index') - const cmdParser = require('../command') + const standardVersion = require('../index'); + const cmdParser = require('../command'); standardVersion(cmdParser.argv).catch(() => { - process.exit(1) - }) + process.exit(1); + }); } diff --git a/command.js b/command.js index afe65fe8d..eef680f57 100755 --- a/command.js +++ b/command.js @@ -1,138 +1,139 @@ -const spec = require('conventional-changelog-config-spec') -const { getConfiguration } = require('./lib/configuration') -const defaults = require('./defaults') +const spec = require('conventional-changelog-config-spec'); +const { getConfiguration } = require('./lib/configuration'); +const defaults = require('./defaults'); const yargs = require('yargs') .usage('Usage: $0 [options]') .option('packageFiles', { default: defaults.packageFiles, - array: true + array: true, }) .option('bumpFiles', { default: defaults.bumpFiles, - array: true + array: true, }) .option('release-as', { alias: 'r', describe: 'Specify the release type manually (like npm version )', requiresArg: true, - string: true + string: true, }) .option('prerelease', { alias: 'p', describe: 'make a pre-release with optional option value to specify a tag id', - string: true + string: true, }) .option('infile', { alias: 'i', describe: 'Read the CHANGELOG from this file', - default: defaults.infile + default: defaults.infile, }) .option('message', { alias: ['m'], describe: '[DEPRECATED] Commit message, replaces %s with new version.\nThis option will be removed in the next major version, please use --releaseCommitMessageFormat.', - type: 'string' + type: 'string', }) .option('first-release', { alias: 'f', describe: 'Is this the first release?', type: 'boolean', - default: defaults.firstRelease + default: defaults.firstRelease, }) .option('sign', { alias: 's', describe: 'Should the git commit and tag be signed?', type: 'boolean', - default: defaults.sign + default: defaults.sign, }) .option('no-verify', { alias: 'n', describe: 'Bypass pre-commit or commit-msg git hooks during the commit phase', type: 'boolean', - default: defaults.noVerify + default: defaults.noVerify, }) .option('commit-all', { alias: 'a', describe: 'Commit all staged changes, not just files affected by commit-and-tag-version', type: 'boolean', - default: defaults.commitAll + default: defaults.commitAll, }) .option('silent', { describe: "Don't print logs and errors", type: 'boolean', - default: defaults.silent + default: defaults.silent, }) .option('tag-prefix', { alias: 't', describe: 'Set a custom prefix for the git tag to be created', type: 'string', - default: defaults.tagPrefix + default: defaults.tagPrefix, }) .option('release-count', { - describe: 'How many releases of changelog you want to generate. It counts from the upcoming release. Useful when you forgot to generate any previous changelog. Set to 0 to regenerate all.', + describe: + 'How many releases of changelog you want to generate. It counts from the upcoming release. Useful when you forgot to generate any previous changelog. Set to 0 to regenerate all.', type: 'number', - default: defaults.releaseCount + default: defaults.releaseCount, }) .option('tag-force', { describe: 'Allow tag replacement', type: 'boolean', - default: defaults.tagForce + default: defaults.tagForce, }) .option('scripts', { describe: 'Provide scripts to execute for lifecycle events (prebump, precommit, etc.,)', - default: defaults.scripts + default: defaults.scripts, }) .option('skip', { describe: 'Map of steps in the release process that should be skipped', - default: defaults.skip + default: defaults.skip, }) .option('dry-run', { type: 'boolean', default: defaults.dryRun, - describe: 'See the commands that running commit-and-tag-version would run' + describe: 'See the commands that running commit-and-tag-version would run', }) .option('git-tag-fallback', { type: 'boolean', default: defaults.gitTagFallback, describe: - 'fallback to git tags for version, if no meta-information file is found (e.g., package.json)' + 'fallback to git tags for version, if no meta-information file is found (e.g., package.json)', }) .option('path', { type: 'string', - describe: 'Only populate commits made under this path' + describe: 'Only populate commits made under this path', }) .option('changelogHeader', { type: 'string', describe: - '[DEPRECATED] Use a custom header when generating and updating changelog.\nThis option will be removed in the next major version, please use --header.' + '[DEPRECATED] Use a custom header when generating and updating changelog.\nThis option will be removed in the next major version, please use --header.', }) .option('preset', { type: 'string', default: defaults.preset, - describe: 'Commit message guideline preset' + describe: 'Commit message guideline preset', }) .option('lerna-package', { type: 'string', - describe: 'Name of the package from which the tags will be extracted' + describe: 'Name of the package from which the tags will be extracted', }) .option('npmPublishHint', { type: 'string', default: defaults.npmPublishHint, - describe: 'Customized publishing hint' + describe: 'Customized publishing hint', }) .check((argv) => { if (typeof argv.scripts !== 'object' || Array.isArray(argv.scripts)) { - throw Error('scripts must be an object') + throw Error('scripts must be an object'); } else if (typeof argv.skip !== 'object' || Array.isArray(argv.skip)) { - throw Error('skip must be an object') + throw Error('skip must be an object'); } else { - return true + return true; } }) .alias('version', 'v') @@ -140,21 +141,21 @@ const yargs = require('yargs') .example('$0', 'Update changelog and tag release') .example( '$0 -m "%s: see changelog for details"', - 'Update changelog and tag release with custom commit message' + 'Update changelog and tag release with custom commit message', ) .pkgConf('standard-version') .pkgConf('commit-and-tag-version') .config(getConfiguration()) - .wrap(97) + .wrap(97); Object.keys(spec.properties).forEach((propertyKey) => { - const property = spec.properties[propertyKey] + const property = spec.properties[propertyKey]; yargs.option(propertyKey, { type: property.type, describe: property.description, default: defaults[propertyKey] ? defaults[propertyKey] : property.default, - group: 'Preset Configuration:' - }) -}) + group: 'Preset Configuration:', + }); +}); -module.exports = yargs +module.exports = yargs; diff --git a/defaults.js b/defaults.js index ed9884fd0..b6eca902e 100644 --- a/defaults.js +++ b/defaults.js @@ -1,4 +1,4 @@ -const spec = require('conventional-changelog-config-spec') +const spec = require('conventional-changelog-config-spec'); const defaults = { infile: 'CHANGELOG.md', @@ -15,29 +15,29 @@ const defaults = { tagForce: false, gitTagFallback: true, preset: require.resolve('conventional-changelog-conventionalcommits'), - npmPublishHint: undefined -} + npmPublishHint: undefined, +}; /** * Merge in defaults provided by the spec */ Object.keys(spec.properties).forEach((propertyKey) => { - const property = spec.properties[propertyKey] - defaults[propertyKey] = property.default -}) + const property = spec.properties[propertyKey]; + defaults[propertyKey] = property.default; +}); /** * Sets the default for `header` (provided by the spec) for backwards * compatibility. This should be removed in the next major version. */ defaults.header = - '# Changelog\n\nAll notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.\n' + '# Changelog\n\nAll notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.\n'; -defaults.packageFiles = ['package.json', 'bower.json', 'manifest.json'] +defaults.packageFiles = ['package.json', 'bower.json', 'manifest.json']; defaults.bumpFiles = defaults.packageFiles.concat([ 'package-lock.json', - 'npm-shrinkwrap.json' -]) + 'npm-shrinkwrap.json', +]); -module.exports = defaults +module.exports = defaults; diff --git a/index.js b/index.js index 8941e654a..ecb193458 100755 --- a/index.js +++ b/index.js @@ -1,39 +1,39 @@ -const bump = require('./lib/lifecycles/bump') -const changelog = require('./lib/lifecycles/changelog') -const commit = require('./lib/lifecycles/commit') -const fs = require('fs') -const latestSemverTag = require('./lib/latest-semver-tag') -const path = require('path') -const printError = require('./lib/print-error') -const tag = require('./lib/lifecycles/tag') -const { resolveUpdaterObjectFromArgument } = require('./lib/updaters') +const bump = require('./lib/lifecycles/bump'); +const changelog = require('./lib/lifecycles/changelog'); +const commit = require('./lib/lifecycles/commit'); +const fs = require('fs'); +const latestSemverTag = require('./lib/latest-semver-tag'); +const path = require('path'); +const printError = require('./lib/print-error'); +const tag = require('./lib/lifecycles/tag'); +const { resolveUpdaterObjectFromArgument } = require('./lib/updaters'); -module.exports = async function standardVersion (argv) { - const defaults = require('./defaults') +module.exports = async function standardVersion(argv) { + const defaults = require('./defaults'); /** * `--message` (`-m`) support will be removed in the next major version. */ - const message = argv.m || argv.message + const message = argv.m || argv.message; if (message) { /** * The `--message` flag uses `%s` for version substitutions, we swap this * for the substitution defined in the config-spec for future-proofing upstream * handling. */ - argv.releaseCommitMessageFormat = message.replace(/%s/g, '{{currentTag}}') + argv.releaseCommitMessageFormat = message.replace(/%s/g, '{{currentTag}}'); if (!argv.silent) { console.warn( - '[commit-and-tag-version]: --message (-m) will be removed in the next major release. Use --releaseCommitMessageFormat.' - ) + '[commit-and-tag-version]: --message (-m) will be removed in the next major release. Use --releaseCommitMessageFormat.', + ); } } if (argv.changelogHeader) { - argv.header = argv.changelogHeader + argv.header = argv.changelogHeader; if (!argv.silent) { console.warn( - '[commit-and-tag-version]: --changelogHeader will be removed in the next major release. Use --header.' - ) + '[commit-and-tag-version]: --changelogHeader will be removed in the next major release. Use --header.', + ); } } @@ -42,51 +42,53 @@ module.exports = async function standardVersion (argv) { argv.header.search(changelog.START_OF_LAST_RELEASE_PATTERN) !== -1 ) { throw Error( - `custom changelog header must not match ${changelog.START_OF_LAST_RELEASE_PATTERN}` - ) + `custom changelog header must not match ${changelog.START_OF_LAST_RELEASE_PATTERN}`, + ); } /** * If an argument for `packageFiles` provided, we include it as a "default" `bumpFile`. */ if (argv.packageFiles) { - defaults.bumpFiles = defaults.bumpFiles.concat(argv.packageFiles) + defaults.bumpFiles = defaults.bumpFiles.concat(argv.packageFiles); } - const args = Object.assign({}, defaults, argv) - let pkg + const args = Object.assign({}, defaults, argv); + let pkg; for (const packageFile of args.packageFiles) { - const updater = resolveUpdaterObjectFromArgument(packageFile) - if (!updater) return - const pkgPath = path.resolve(process.cwd(), updater.filename) + const updater = resolveUpdaterObjectFromArgument(packageFile); + if (!updater) return; + const pkgPath = path.resolve(process.cwd(), updater.filename); try { - const contents = fs.readFileSync(pkgPath, 'utf8') + const contents = fs.readFileSync(pkgPath, 'utf8'); pkg = { version: updater.updater.readVersion(contents), private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) - : false - } - break - } catch (err) {} + : false, + }; + break; + } catch (err) { + /* This probably shouldn't be empty? */ + } } try { - let version + let version; if (pkg && pkg.version) { - version = pkg.version + version = pkg.version; } else if (args.gitTagFallback) { - version = await latestSemverTag(args.tagPrefix) + version = await latestSemverTag(args.tagPrefix); } else { - throw new Error('no package file found') + throw new Error('no package file found'); } - const newVersion = await bump(args, version) - await changelog(args, newVersion) - await commit(args, newVersion) - await tag(newVersion, pkg ? pkg.private : false, args) + const newVersion = await bump(args, version); + await changelog(args, newVersion); + await commit(args, newVersion); + await tag(newVersion, pkg ? pkg.private : false, args); } catch (err) { - printError(args, err.message) - throw err + printError(args, err.message); + throw err; } -} +}; diff --git a/lib/checkpoint.js b/lib/checkpoint.js index c37275b78..634d55534 100644 --- a/lib/checkpoint.js +++ b/lib/checkpoint.js @@ -1,12 +1,23 @@ -const chalk = require('chalk') -const figures = require('figures') -const util = require('util') +const chalk = require('chalk'); +const figures = require('figures'); +const util = require('util'); module.exports = function (argv, msg, args, figure) { - const defaultFigure = argv.dryRun ? chalk.yellow(figures.tick) : chalk.green(figures.tick) + const defaultFigure = argv.dryRun + ? chalk.yellow(figures.tick) + : chalk.green(figures.tick); if (!argv.silent) { - console.info((figure || defaultFigure) + ' ' + util.format.apply(util, [msg].concat(args.map(function (arg) { - return chalk.bold(arg) - })))) + console.info( + (figure || defaultFigure) + + ' ' + + util.format.apply( + util, + [msg].concat( + args.map(function (arg) { + return chalk.bold(arg); + }), + ), + ), + ); } -} +}; diff --git a/lib/configuration.js b/lib/configuration.js index b73e0b1ca..f169631ea 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -1,30 +1,30 @@ -const path = require('path') -const findUp = require('find-up') -const { readFileSync } = require('fs') +const path = require('path'); +const findUp = require('find-up'); +const { readFileSync } = require('fs'); const CONFIGURATION_FILES = [ '.versionrc', '.versionrc.cjs', '.versionrc.json', - '.versionrc.js' -] + '.versionrc.js', +]; module.exports.getConfiguration = function () { - let config = {} - const configPath = findUp.sync(CONFIGURATION_FILES) + let config = {}; + const configPath = findUp.sync(CONFIGURATION_FILES); if (!configPath) { - return config + return config; } - const ext = path.extname(configPath) + const ext = path.extname(configPath); if (ext === '.js' || ext === '.cjs') { - const jsConfiguration = require(configPath) + const jsConfiguration = require(configPath); if (typeof jsConfiguration === 'function') { - config = jsConfiguration() + config = jsConfiguration(); } else { - config = jsConfiguration + config = jsConfiguration; } } else { - config = JSON.parse(readFileSync(configPath)) + config = JSON.parse(readFileSync(configPath)); } /** @@ -33,9 +33,9 @@ module.exports.getConfiguration = function () { */ if (typeof config !== 'object') { throw Error( - `[commit-and-tag-version] Invalid configuration in ${configPath} provided. Expected an object but found ${typeof config}.` - ) + `[commit-and-tag-version] Invalid configuration in ${configPath} provided. Expected an object but found ${typeof config}.`, + ); } - return config -} + return config; +}; diff --git a/lib/detect-package-manager.js b/lib/detect-package-manager.js index 99680bc2b..421da6ed0 100644 --- a/lib/detect-package-manager.js +++ b/lib/detect-package-manager.js @@ -4,50 +4,50 @@ * modified to support only detecting lock file and not detecting global package manager */ -const { promises: fs } = require('fs') -const { resolve } = require('path') +const { promises: fs } = require('fs'); +const { resolve } = require('path'); /** * Check if a path exists */ -async function pathExists (p) { +async function pathExists(p) { try { - await fs.access(p) - return true + await fs.access(p); + return true; } catch { - return false + return false; } } -function getTypeofLockFile (cwd = '.') { +function getTypeofLockFile(cwd = '.') { return Promise.all([ pathExists(resolve(cwd, 'yarn.lock')), pathExists(resolve(cwd, 'package-lock.json')), - pathExists(resolve(cwd, 'pnpm-lock.yaml')) + pathExists(resolve(cwd, 'pnpm-lock.yaml')), ]).then(([isYarn, isNpm, isPnpm]) => { - let value = null + let value = null; if (isYarn) { - value = 'yarn' + value = 'yarn'; } else if (isPnpm) { - value = 'pnpm' + value = 'pnpm'; } else if (isNpm) { - value = 'npm' + value = 'npm'; } - return value - }) + return value; + }); } const detectPMByLockFile = async (cwd) => { - const type = await getTypeofLockFile(cwd) + const type = await getTypeofLockFile(cwd); if (type) { - return type + return type; } - return 'npm' -} + return 'npm'; +}; module.exports = { - detectPMByLockFile -} + detectPMByLockFile, +}; diff --git a/lib/format-commit-message.js b/lib/format-commit-message.js index a597b5198..f174c66a7 100644 --- a/lib/format-commit-message.js +++ b/lib/format-commit-message.js @@ -1,4 +1,4 @@ module.exports = function (rawMsg, newVersion) { - const message = String(rawMsg) - return message.replace(/{{currentTag}}/g, newVersion) -} + const message = String(rawMsg); + return message.replace(/{{currentTag}}/g, newVersion); +}; diff --git a/lib/latest-semver-tag.js b/lib/latest-semver-tag.js index eec93af2b..1230a0d4c 100644 --- a/lib/latest-semver-tag.js +++ b/lib/latest-semver-tag.js @@ -1,17 +1,19 @@ -const gitSemverTags = require('git-semver-tags') -const semver = require('semver') +const gitSemverTags = require('git-semver-tags'); +const semver = require('semver'); module.exports = function (tagPrefix = undefined) { return new Promise((resolve, reject) => { gitSemverTags({ tagPrefix }, function (err, tags) { - if (err) return reject(err) - else if (!tags.length) return resolve('1.0.0') + if (err) return reject(err); + else if (!tags.length) return resolve('1.0.0'); // Respect tagPrefix - tags = tags.map(tag => tag.replace(new RegExp('^' + tagPrefix), '')) + tags = tags.map((tag) => tag.replace(new RegExp('^' + tagPrefix), '')); // ensure that the largest semver tag is at the head. - tags = tags.map(tag => { return semver.clean(tag) }) - tags.sort(semver.rcompare) - return resolve(tags[0]) - }) - }) -} + tags = tags.map((tag) => { + return semver.clean(tag); + }); + tags.sort(semver.rcompare); + return resolve(tags[0]); + }); + }); +}; diff --git a/lib/lifecycles/bump.js b/lib/lifecycles/bump.js index c0a2ab2c2..ad5b2386b 100644 --- a/lib/lifecycles/bump.js +++ b/lib/lifecycles/bump.js @@ -1,76 +1,103 @@ -'use strict' - -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const conventionalRecommendedBump = require('conventional-recommended-bump') -const figures = require('figures') -const fs = require('fs') -const DotGitignore = require('dotgitignore') -const path = require('path') -const presetLoader = require('../preset-loader') -const runLifecycleScript = require('../run-lifecycle-script') -const semver = require('semver') -const writeFile = require('../write-file') -const { resolveUpdaterObjectFromArgument } = require('../updaters') -let configsToUpdate = {} - -async function Bump (args, version) { +'use strict'; + +const chalk = require('chalk'); +const checkpoint = require('../checkpoint'); +const conventionalRecommendedBump = require('conventional-recommended-bump'); +const figures = require('figures'); +const fs = require('fs'); +const DotGitignore = require('dotgitignore'); +const path = require('path'); +const presetLoader = require('../preset-loader'); +const runLifecycleScript = require('../run-lifecycle-script'); +const semver = require('semver'); +const writeFile = require('../write-file'); +const { resolveUpdaterObjectFromArgument } = require('../updaters'); +let configsToUpdate = {}; + +async function Bump(args, version) { // reset the cache of updated config files each // time we perform the version bump step. - configsToUpdate = {} - - if (args.skip.bump) return version - - if (args.releaseAs && !(['major', 'minor', 'patch'].includes(args.releaseAs.toLowerCase()) || semver.valid(args.releaseAs))) { - throw new Error("releaseAs must be one of 'major', 'minor' or 'patch', or a valid semvar version.") + configsToUpdate = {}; + + if (args.skip.bump) return version; + + if ( + args.releaseAs && + !( + ['major', 'minor', 'patch'].includes(args.releaseAs.toLowerCase()) || + semver.valid(args.releaseAs) + ) + ) { + throw new Error( + "releaseAs must be one of 'major', 'minor' or 'patch', or a valid semvar version.", + ); } - let newVersion = version - await runLifecycleScript(args, 'prerelease') - const stdout = await runLifecycleScript(args, 'prebump') + let newVersion = version; + await runLifecycleScript(args, 'prerelease'); + const stdout = await runLifecycleScript(args, 'prebump'); if (stdout?.trim().length) { - const prebumpString = stdout.trim() - if (semver.valid(prebumpString)) args.releaseAs = prebumpString + const prebumpString = stdout.trim(); + if (semver.valid(prebumpString)) args.releaseAs = prebumpString; } if (!args.firstRelease) { if (semver.valid(args.releaseAs)) { - const releaseAs = new semver.SemVer(args.releaseAs) - if (isString(args.prerelease) && releaseAs.prerelease.length && releaseAs.prerelease.slice(0, -1).join('.') !== args.prerelease) { + const releaseAs = new semver.SemVer(args.releaseAs); + if ( + isString(args.prerelease) && + releaseAs.prerelease.length && + releaseAs.prerelease.slice(0, -1).join('.') !== args.prerelease + ) { // If both releaseAs and the prerelease identifier are supplied, they must match. The behavior // for a mismatch is undefined, so error out instead. - throw new Error('releaseAs and prerelease have conflicting prerelease identifiers') + throw new Error( + 'releaseAs and prerelease have conflicting prerelease identifiers', + ); } else if (isString(args.prerelease) && releaseAs.prerelease.length) { - newVersion = releaseAs.version + newVersion = releaseAs.version; } else if (isString(args.prerelease)) { - newVersion = `${releaseAs.major}.${releaseAs.minor}.${releaseAs.patch}-${args.prerelease}.0` + newVersion = `${releaseAs.major}.${releaseAs.minor}.${releaseAs.patch}-${args.prerelease}.0`; } else { - newVersion = releaseAs.version + newVersion = releaseAs.version; } // Check if the previous version is the same version and prerelease, and increment if so - if (isString(args.prerelease) && ['prerelease', null].includes(semver.diff(version, newVersion)) && semver.lte(newVersion, version)) { - newVersion = semver.inc(version, 'prerelease', args.prerelease) + if ( + isString(args.prerelease) && + ['prerelease', null].includes(semver.diff(version, newVersion)) && + semver.lte(newVersion, version) + ) { + newVersion = semver.inc(version, 'prerelease', args.prerelease); } // Append any build info from releaseAs - newVersion = semvarToVersionStr(newVersion, releaseAs.build) + newVersion = semvarToVersionStr(newVersion, releaseAs.build); } else { - const release = await bumpVersion(args.releaseAs, version, args) - const releaseType = getReleaseType(args.prerelease, release.releaseType, version) - - newVersion = semver.inc(version, releaseType, args.prerelease) + const release = await bumpVersion(args.releaseAs, version, args); + const releaseType = getReleaseType( + args.prerelease, + release.releaseType, + version, + ); + + newVersion = semver.inc(version, releaseType, args.prerelease); } - updateConfigs(args, newVersion) + updateConfigs(args, newVersion); } else { - checkpoint(args, 'skip version bump on first release', [], chalk.red(figures.cross)) + checkpoint( + args, + 'skip version bump on first release', + [], + chalk.red(figures.cross), + ); } - await runLifecycleScript(args, 'postbump') - return newVersion + await runLifecycleScript(args, 'postbump'); + return newVersion; } Bump.getUpdatedConfigs = function () { - return configsToUpdate -} + return configsToUpdate; +}; /** * Convert a semver object to a full version string including build metadata @@ -78,28 +105,30 @@ Bump.getUpdatedConfigs = function () { * @param {string[]} semverBuild An array of the build metadata elements, to be joined with '.' * @returns {string} */ -function semvarToVersionStr (semverVersion, semverBuild) { - return [semverVersion, semverBuild.join('.')].filter(Boolean).join('+') +function semvarToVersionStr(semverVersion, semverBuild) { + return [semverVersion, semverBuild.join('.')].filter(Boolean).join('+'); } -function getReleaseType (prerelease, expectedReleaseType, currentVersion) { +function getReleaseType(prerelease, expectedReleaseType, currentVersion) { if (isString(prerelease)) { if (isInPrerelease(currentVersion)) { - if (shouldContinuePrerelease(currentVersion, expectedReleaseType) || - getTypePriority(getCurrentActiveType(currentVersion)) > getTypePriority(expectedReleaseType) + if ( + shouldContinuePrerelease(currentVersion, expectedReleaseType) || + getTypePriority(getCurrentActiveType(currentVersion)) > + getTypePriority(expectedReleaseType) ) { - return 'prerelease' + return 'prerelease'; } } - return 'pre' + expectedReleaseType + return 'pre' + expectedReleaseType; } else { - return expectedReleaseType + return expectedReleaseType; } } -function isString (val) { - return typeof val === 'string' +function isString(val) { + return typeof val === 'string'; } /** @@ -111,15 +140,15 @@ function isString (val) { * @param expectType * @return {boolean} */ -function shouldContinuePrerelease (version, expectType) { - return getCurrentActiveType(version) === expectType +function shouldContinuePrerelease(version, expectType) { + return getCurrentActiveType(version) === expectType; } -function isInPrerelease (version) { - return Array.isArray(semver.prerelease(version)) +function isInPrerelease(version) { + return Array.isArray(semver.prerelease(version)); } -const TypeList = ['major', 'minor', 'patch'].reverse() +const TypeList = ['major', 'minor', 'patch'].reverse(); /** * extract the in-pre-release type in target version @@ -127,11 +156,11 @@ const TypeList = ['major', 'minor', 'patch'].reverse() * @param version * @return {string} */ -function getCurrentActiveType (version) { - const typelist = TypeList +function getCurrentActiveType(version) { + const typelist = TypeList; for (let i = 0; i < typelist.length; i++) { if (semver[typelist[i]](version)) { - return typelist[i] + return typelist[i]; } } } @@ -143,33 +172,39 @@ function getCurrentActiveType (version) { * @param type * @return {number} */ -function getTypePriority (type) { - return TypeList.indexOf(type) +function getTypePriority(type) { + return TypeList.indexOf(type); } -function bumpVersion (releaseAs, currentVersion, args) { +function bumpVersion(releaseAs, currentVersion, args) { return new Promise((resolve, reject) => { if (releaseAs) { return resolve({ - releaseType: releaseAs - }) + releaseType: releaseAs, + }); } else { - const presetOptions = presetLoader(args) + const presetOptions = presetLoader(args); if (typeof presetOptions === 'object') { - if (semver.lt(currentVersion, '1.0.0')) presetOptions.preMajor = true + if (semver.lt(currentVersion, '1.0.0')) presetOptions.preMajor = true; } - conventionalRecommendedBump({ - debug: args.verbose && console.info.bind(console, 'conventional-recommended-bump'), - preset: presetOptions, - path: args.path, - tagPrefix: args.tagPrefix, - lernaPackage: args.lernaPackage - }, args.parserOpts, function (err, release) { - if (err) return reject(err) - else return resolve(release) - }) + conventionalRecommendedBump( + { + debug: + args.verbose && + console.info.bind(console, 'conventional-recommended-bump'), + preset: presetOptions, + path: args.path, + tagPrefix: args.tagPrefix, + lernaPackage: args.lernaPackage, + }, + args.parserOpts, + function (err, release) { + if (err) return reject(err); + else return resolve(release); + }, + ); } - }) + }); } /** @@ -178,39 +213,35 @@ function bumpVersion (releaseAs, currentVersion, args) { * @param newVersion version number to update to. * @return void */ -function updateConfigs (args, newVersion) { - const dotgit = DotGitignore() +function updateConfigs(args, newVersion) { + const dotgit = DotGitignore(); args.bumpFiles.forEach(function (bumpFile) { - const updater = resolveUpdaterObjectFromArgument(bumpFile) + const updater = resolveUpdaterObjectFromArgument(bumpFile); if (!updater) { - return + return; } - const configPath = path.resolve(process.cwd(), updater.filename) + const configPath = path.resolve(process.cwd(), updater.filename); try { - if (dotgit.ignore(updater.filename)) return - const stat = fs.lstatSync(configPath) + if (dotgit.ignore(updater.filename)) return; + const stat = fs.lstatSync(configPath); - if (!stat.isFile()) return - const contents = fs.readFileSync(configPath, 'utf8') - const newContents = updater.updater.writeVersion(contents, newVersion) - const realNewVersion = updater.updater.readVersion(newContents) + if (!stat.isFile()) return; + const contents = fs.readFileSync(configPath, 'utf8'); + const newContents = updater.updater.writeVersion(contents, newVersion); + const realNewVersion = updater.updater.readVersion(newContents); checkpoint( args, 'bumping version in ' + updater.filename + ' from %s to %s', - [updater.updater.readVersion(contents), realNewVersion] - ) - writeFile( - args, - configPath, - newContents - ) + [updater.updater.readVersion(contents), realNewVersion], + ); + writeFile(args, configPath, newContents); // flag any config files that we modify the version # for // as having been updated. - configsToUpdate[updater.filename] = true + configsToUpdate[updater.filename] = true; } catch (err) { - if (err.code !== 'ENOENT') console.warn(err.message) + if (err.code !== 'ENOENT') console.warn(err.message); } - }) + }); } -module.exports = Bump +module.exports = Bump; diff --git a/lib/lifecycles/changelog.js b/lib/lifecycles/changelog.js index 5bdd573ac..073af8750 100644 --- a/lib/lifecycles/changelog.js +++ b/lib/lifecycles/changelog.js @@ -1,88 +1,107 @@ -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const conventionalChangelog = require('conventional-changelog') -const fs = require('fs') -const presetLoader = require('../preset-loader') -const runLifecycleScript = require('../run-lifecycle-script') -const writeFile = require('../write-file') -const START_OF_LAST_RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+| { - createIfMissing(args) - const header = args.header + createIfMissing(args); + const header = args.header; - const oldContent = args.dryRun || args.releaseCount === 0 ? '' : fs.readFileSync(args.infile, 'utf-8') + const oldContent = + args.dryRun || args.releaseCount === 0 + ? '' + : fs.readFileSync(args.infile, 'utf-8'); - const oldContentBody = extractChangelogBody(oldContent) + const oldContentBody = extractChangelogBody(oldContent); - const changelogFrontMatter = extractFrontMatter(oldContent) + const changelogFrontMatter = extractFrontMatter(oldContent); - let content = '' - const context = { version: newVersion } - const changelogStream = conventionalChangelog({ - debug: args.verbose && console.info.bind(console, 'conventional-changelog'), - preset: presetLoader(args), - tagPrefix: args.tagPrefix, - releaseCount: args.releaseCount - }, context, { merges: null, path: args.path, showSignature: false }, args.parserOpts, args.writerOpts) - .on('error', function (err) { - return reject(err) - }) + let content = ''; + const context = { version: newVersion }; + const changelogStream = conventionalChangelog( + { + debug: + args.verbose && console.info.bind(console, 'conventional-changelog'), + preset: presetLoader(args), + tagPrefix: args.tagPrefix, + releaseCount: args.releaseCount, + }, + context, + { merges: null, path: args.path, showSignature: false }, + args.parserOpts, + args.writerOpts, + ).on('error', function (err) { + return reject(err); + }); changelogStream.on('data', function (buffer) { - content += buffer.toString() - }) + content += buffer.toString(); + }); changelogStream.on('end', function () { - checkpoint(args, 'outputting changes to %s', [args.infile]) - if (args.dryRun) console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`) - else writeFile(args, args.infile, changelogFrontMatter + header + '\n' + (content + oldContentBody).replace(/\n+$/, '\n')) - return resolve() - }) - }) + checkpoint(args, 'outputting changes to %s', [args.infile]); + if (args.dryRun) + console.info(`\n---\n${chalk.gray(content.trim())}\n---\n`); + else + writeFile( + args, + args.infile, + changelogFrontMatter + + header + + '\n' + + (content + oldContentBody).replace(/\n+$/, '\n'), + ); + return resolve(); + }); + }); } -function createIfMissing (args) { +function createIfMissing(args) { try { - fs.accessSync(args.infile, fs.F_OK) + fs.accessSync(args.infile, fs.F_OK); } catch (err) { if (err.code === 'ENOENT') { - checkpoint(args, 'created %s', [args.infile]) - args.outputUnreleased = true - writeFile(args, args.infile, '\n') + checkpoint(args, 'created %s', [args.infile]); + args.outputUnreleased = true; + writeFile(args, args.infile, '\n'); } } } diff --git a/lib/lifecycles/commit.js b/lib/lifecycles/commit.js index 1f56ec632..4f14407f9 100644 --- a/lib/lifecycles/commit.js +++ b/lib/lifecycles/commit.js @@ -1,69 +1,67 @@ -const bump = require('../lifecycles/bump') -const checkpoint = require('../checkpoint') -const formatCommitMessage = require('../format-commit-message') -const path = require('path') -const runExecFile = require('../run-execFile') -const runLifecycleScript = require('../run-lifecycle-script') +const bump = require('../lifecycles/bump'); +const checkpoint = require('../checkpoint'); +const formatCommitMessage = require('../format-commit-message'); +const path = require('path'); +const runExecFile = require('../run-execFile'); +const runLifecycleScript = require('../run-lifecycle-script'); module.exports = async function (args, newVersion) { - if (args.skip.commit) return - const message = await runLifecycleScript(args, 'precommit') - if (message && message.length) args.releaseCommitMessageFormat = message - await execCommit(args, newVersion) - await runLifecycleScript(args, 'postcommit') -} + if (args.skip.commit) return; + const message = await runLifecycleScript(args, 'precommit'); + if (message && message.length) args.releaseCommitMessageFormat = message; + await execCommit(args, newVersion); + await runLifecycleScript(args, 'postcommit'); +}; -async function execCommit (args, newVersion) { - let msg = 'committing %s' - let paths = [] - const verify = args.verify === false || args.n ? ['--no-verify'] : [] - const sign = args.sign ? ['-S'] : [] - const toAdd = [] +async function execCommit(args, newVersion) { + let msg = 'committing %s'; + let paths = []; + const verify = args.verify === false || args.n ? ['--no-verify'] : []; + const sign = args.sign ? ['-S'] : []; + const toAdd = []; // only start with a pre-populated paths list when CHANGELOG processing is not skipped if (!args.skip.changelog) { - paths = [args.infile] - toAdd.push(args.infile) + paths = [args.infile]; + toAdd.push(args.infile); } // commit any of the config files that we've updated // the version # for. Object.keys(bump.getUpdatedConfigs()).forEach(function (p) { - paths.unshift(p) - toAdd.push(path.relative(process.cwd(), p)) + paths.unshift(p); + toAdd.push(path.relative(process.cwd(), p)); // account for multiple files in the output message if (paths.length > 1) { - msg += ' and %s' + msg += ' and %s'; } - }) + }); if (args.commitAll) { - msg += ' and %s' - paths.push('all staged files') + msg += ' and %s'; + paths.push('all staged files'); } - checkpoint(args, msg, paths) + checkpoint(args, msg, paths); // nothing to do, exit without commit anything - if (!args.commitAll && args.skip.changelog && args.skip.bump && toAdd.length === 0) { - return + if ( + !args.commitAll && + args.skip.changelog && + args.skip.bump && + toAdd.length === 0 + ) { + return; } - await runExecFile(args, 'git', ['add'].concat(toAdd)) + await runExecFile(args, 'git', ['add'].concat(toAdd)); await runExecFile( args, 'git', - [ - 'commit' - ].concat( - verify, - sign, - args.commitAll ? [] : toAdd, - [ - '-m', - `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}` - ] - ) - ) + ['commit'].concat(verify, sign, args.commitAll ? [] : toAdd, [ + '-m', + `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`, + ]), + ); } diff --git a/lib/lifecycles/tag.js b/lib/lifecycles/tag.js index 5093990b8..8a0edab0a 100644 --- a/lib/lifecycles/tag.js +++ b/lib/lifecycles/tag.js @@ -1,50 +1,60 @@ -const bump = require('../lifecycles/bump') -const chalk = require('chalk') -const checkpoint = require('../checkpoint') -const figures = require('figures') -const formatCommitMessage = require('../format-commit-message') -const runExecFile = require('../run-execFile') -const runLifecycleScript = require('../run-lifecycle-script') -const { detectPMByLockFile } = require('../detect-package-manager') +const bump = require('../lifecycles/bump'); +const chalk = require('chalk'); +const checkpoint = require('../checkpoint'); +const figures = require('figures'); +const formatCommitMessage = require('../format-commit-message'); +const runExecFile = require('../run-execFile'); +const runLifecycleScript = require('../run-lifecycle-script'); +const { detectPMByLockFile } = require('../detect-package-manager'); module.exports = async function (newVersion, pkgPrivate, args) { - if (args.skip.tag) return - await runLifecycleScript(args, 'pretag') - await execTag(newVersion, pkgPrivate, args) - await runLifecycleScript(args, 'posttag') -} + if (args.skip.tag) return; + await runLifecycleScript(args, 'pretag'); + await execTag(newVersion, pkgPrivate, args); + await runLifecycleScript(args, 'posttag'); +}; -async function detectPublishHint () { - const npmClientName = await detectPMByLockFile() - const publishCommand = 'publish' - return `${npmClientName} ${publishCommand}` +async function detectPublishHint() { + const npmClientName = await detectPMByLockFile(); + const publishCommand = 'publish'; + return `${npmClientName} ${publishCommand}`; } -async function execTag (newVersion, pkgPrivate, args) { - const tagOption = [] +async function execTag(newVersion, pkgPrivate, args) { + const tagOption = []; if (args.sign) { - tagOption.push('-s') + tagOption.push('-s'); } else { - tagOption.push('-a') + tagOption.push('-a'); } if (args.tagForce) { - tagOption.push('-f') + tagOption.push('-f'); } - checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]) - await runExecFile(args, 'git', ['tag', ...tagOption, args.tagPrefix + newVersion, '-m', `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`]) - const currentBranch = await runExecFile('', 'git', ['rev-parse', '--abbrev-ref', 'HEAD']) - let message = 'git push --follow-tags origin ' + currentBranch.trim() + checkpoint(args, 'tagging release %s%s', [args.tagPrefix, newVersion]); + await runExecFile(args, 'git', [ + 'tag', + ...tagOption, + args.tagPrefix + newVersion, + '-m', + `${formatCommitMessage(args.releaseCommitMessageFormat, newVersion)}`, + ]); + const currentBranch = await runExecFile('', 'git', [ + 'rev-parse', + '--abbrev-ref', + 'HEAD', + ]); + let message = 'git push --follow-tags origin ' + currentBranch.trim(); if (pkgPrivate !== true && bump.getUpdatedConfigs()['package.json']) { - const npmPublishHint = args.npmPublishHint || await detectPublishHint() - message += ` && ${npmPublishHint}` + const npmPublishHint = args.npmPublishHint || (await detectPublishHint()); + message += ` && ${npmPublishHint}`; if (args.prerelease !== undefined) { if (args.prerelease === '') { - message += ' --tag prerelease' + message += ' --tag prerelease'; } else { - message += ' --tag ' + args.prerelease + message += ' --tag ' + args.prerelease; } } } - checkpoint(args, 'Run `%s` to publish', [message], chalk.blue(figures.info)) + checkpoint(args, 'Run `%s` to publish', [message], chalk.blue(figures.info)); } diff --git a/lib/preset-loader.js b/lib/preset-loader.js index a7c077281..0d5bde9ab 100644 --- a/lib/preset-loader.js +++ b/lib/preset-loader.js @@ -1,17 +1,19 @@ // TODO: this should be replaced with an object we maintain and // describe in: https://github.com/conventional-changelog/conventional-changelog-config-spec -const spec = require('conventional-changelog-config-spec') +const spec = require('conventional-changelog-config-spec'); module.exports = (args) => { - const defaultPreset = require.resolve('conventional-changelog-conventionalcommits') - let preset = args.preset || defaultPreset + const defaultPreset = require.resolve( + 'conventional-changelog-conventionalcommits', + ); + let preset = args.preset || defaultPreset; if (preset === defaultPreset) { preset = { - name: defaultPreset - } - Object.keys(spec.properties).forEach(key => { - if (args[key] !== undefined) preset[key] = args[key] - }) + name: defaultPreset, + }; + Object.keys(spec.properties).forEach((key) => { + if (args[key] !== undefined) preset[key] = args[key]; + }); } - return preset -} + return preset; +}; diff --git a/lib/print-error.js b/lib/print-error.js index 84aaa8257..298964e8c 100644 --- a/lib/print-error.js +++ b/lib/print-error.js @@ -1,12 +1,15 @@ -const chalk = require('chalk') +const chalk = require('chalk'); module.exports = function (args, msg, opts) { if (!args.silent) { - opts = Object.assign({ - level: 'error', - color: 'red' - }, opts) + opts = Object.assign( + { + level: 'error', + color: 'red', + }, + opts, + ); - console[opts.level](chalk[opts.color](msg)) + console[opts.level](chalk[opts.color](msg)); } -} +}; diff --git a/lib/run-exec.js b/lib/run-exec.js index eec2feb3d..6d4441717 100644 --- a/lib/run-exec.js +++ b/lib/run-exec.js @@ -1,18 +1,18 @@ -const { promisify } = require('util') -const printError = require('./print-error') +const { promisify } = require('util'); +const printError = require('./print-error'); -const exec = promisify(require('child_process').exec) +const exec = promisify(require('child_process').exec); module.exports = async function (args, cmd) { - if (args.dryRun) return + if (args.dryRun) return; try { - const { stderr, stdout } = await exec(cmd) + const { stderr, stdout } = await exec(cmd); // If exec returns content in stderr, but no error, print it as a warning - if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }) - return stdout + if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }); + return stdout; } catch (error) { // If exec returns an error, print it and exit with return code 1 - printError(args, error.stderr || error.message) - throw error + printError(args, error.stderr || error.message); + throw error; } -} +}; diff --git a/lib/run-execFile.js b/lib/run-execFile.js index c0c814d45..e20488ad6 100644 --- a/lib/run-execFile.js +++ b/lib/run-execFile.js @@ -1,18 +1,18 @@ -const { promisify } = require('util') -const printError = require('./print-error') +const { promisify } = require('util'); +const printError = require('./print-error'); -const execFile = promisify(require('child_process').execFile) +const execFile = promisify(require('child_process').execFile); module.exports = async function (args, cmd, cmdArgs) { - if (args.dryRun) return + if (args.dryRun) return; try { - const { stderr, stdout } = await execFile(cmd, cmdArgs) + const { stderr, stdout } = await execFile(cmd, cmdArgs); // If execFile returns content in stderr, but no error, print it as a warning - if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }) - return stdout + if (stderr) printError(args, stderr, { level: 'warn', color: 'yellow' }); + return stdout; } catch (error) { // If execFile returns an error, print it and exit with return code 1 - printError(args, error.stderr || error.message) - throw error + printError(args, error.stderr || error.message); + throw error; } -} +}; diff --git a/lib/run-lifecycle-script.js b/lib/run-lifecycle-script.js index a4c88c2e9..4e10193cd 100644 --- a/lib/run-lifecycle-script.js +++ b/lib/run-lifecycle-script.js @@ -1,13 +1,18 @@ -const chalk = require('chalk') -const checkpoint = require('./checkpoint') -const figures = require('figures') -const runExec = require('./run-exec') +const chalk = require('chalk'); +const checkpoint = require('./checkpoint'); +const figures = require('figures'); +const runExec = require('./run-exec'); module.exports = function (args, hookName) { - const scripts = args.scripts - if (!scripts || !scripts[hookName]) return Promise.resolve() - const command = scripts[hookName] - checkpoint(args, 'Running lifecycle script "%s"', [hookName]) - checkpoint(args, '- execute command: "%s"', [command], chalk.blue(figures.info)) - return runExec(args, command) -} + const scripts = args.scripts; + if (!scripts || !scripts[hookName]) return Promise.resolve(); + const command = scripts[hookName]; + checkpoint(args, 'Running lifecycle script "%s"', [hookName]); + checkpoint( + args, + '- execute command: "%s"', + [command], + chalk.blue(figures.info), + ); + return runExec(args, command); +}; diff --git a/lib/stringify-package.js b/lib/stringify-package.js index 46594cf78..8fd32a9ec 100644 --- a/lib/stringify-package.js +++ b/lib/stringify-package.js @@ -16,21 +16,21 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. https://github.com/npm/stringify-package/blob/main/LICENSE */ -'use strict' +'use strict'; -module.exports = stringifyPackage +module.exports = stringifyPackage; -const DEFAULT_INDENT = 2 -const CRLF = '\r\n' -const LF = '\n' +const DEFAULT_INDENT = 2; +const CRLF = '\r\n'; +const LF = '\n'; -function stringifyPackage (data, indent, newline) { - indent = indent || (indent === 0 ? 0 : DEFAULT_INDENT) - const json = JSON.stringify(data, null, indent) +function stringifyPackage(data, indent, newline) { + indent = indent || (indent === 0 ? 0 : DEFAULT_INDENT); + const json = JSON.stringify(data, null, indent); if (newline === CRLF) { - return json.replace(/\n/g, CRLF) + CRLF + return json.replace(/\n/g, CRLF) + CRLF; } - return json + LF + return json + LF; } diff --git a/lib/updaters/index.js b/lib/updaters/index.js index a931e183a..44c711ff6 100644 --- a/lib/updaters/index.js +++ b/lib/updaters/index.js @@ -1,61 +1,63 @@ -const path = require('path') -const JSON_BUMP_FILES = require('../../defaults').bumpFiles +const path = require('path'); +const JSON_BUMP_FILES = require('../../defaults').bumpFiles; const updatersByType = { json: require('./types/json'), 'plain-text': require('./types/plain-text'), gradle: require('./types/gradle'), - csproj: require('./types/csproj') -} -const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt'] + csproj: require('./types/csproj'), +}; +const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt']; -function getUpdaterByType (type) { - const updater = updatersByType[type] +function getUpdaterByType(type) { + const updater = updatersByType[type]; if (!updater) { - throw Error(`Unable to locate updater for provided type (${type}).`) + throw Error(`Unable to locate updater for provided type (${type}).`); } - return updater + return updater; } -function getUpdaterByFilename (filename) { +function getUpdaterByFilename(filename) { if (JSON_BUMP_FILES.includes(path.basename(filename))) { - return getUpdaterByType('json') + return getUpdaterByType('json'); } if (PLAIN_TEXT_BUMP_FILES.includes(filename)) { - return getUpdaterByType('plain-text') + return getUpdaterByType('plain-text'); } if (/build.gradle/.test(filename)) { - return getUpdaterByType('gradle') + return getUpdaterByType('gradle'); } if (filename.endsWith('.csproj')) { - return getUpdaterByType('csproj') + return getUpdaterByType('csproj'); } throw Error( - `Unsupported file (${filename}) provided for bumping.\n Please specify the updater \`type\` or use a custom \`updater\`.` - ) + `Unsupported file (${filename}) provided for bumping.\n Please specify the updater \`type\` or use a custom \`updater\`.`, + ); } -function getCustomUpdaterFromPath (updater) { +function getCustomUpdaterFromPath(updater) { if (typeof updater === 'string') { - return require(path.resolve(process.cwd(), updater)) + return require(path.resolve(process.cwd(), updater)); } if ( typeof updater.readVersion === 'function' && typeof updater.writeVersion === 'function' ) { - return updater + return updater; } - throw new Error('Updater must be a string path or an object with readVersion and writeVersion methods') + throw new Error( + 'Updater must be a string path or an object with readVersion and writeVersion methods', + ); } /** * Simple check to determine if the object provided is a compatible updater. */ -function isValidUpdater (obj) { +function isValidUpdater(obj) { return ( obj && typeof obj.readVersion === 'function' && typeof obj.writeVersion === 'function' - ) + ); } module.exports.resolveUpdaterObjectFromArgument = function (arg) { @@ -63,35 +65,40 @@ module.exports.resolveUpdaterObjectFromArgument = function (arg) { * If an Object was not provided, we assume it's the path/filename * of the updater. */ - let updater = arg + let updater = arg; if (isValidUpdater(updater)) { - return updater + return updater; } if (typeof updater !== 'object') { updater = { - filename: arg - } + filename: arg, + }; } if (!isValidUpdater(updater.updater)) { try { if (typeof updater.updater === 'string') { - updater.updater = getCustomUpdaterFromPath(updater.updater) + updater.updater = getCustomUpdaterFromPath(updater.updater); } else if (updater.type) { - updater.updater = getUpdaterByType(updater.type) + updater.updater = getUpdaterByType(updater.type); } else { - updater.updater = getUpdaterByFilename(updater.filename) + updater.updater = getUpdaterByFilename(updater.filename); } } catch (err) { - if (err.code !== 'ENOENT') console.warn(`Unable to obtain updater for: ${JSON.stringify(arg)}\n - Error: ${err.message}\n - Skipping...`) + if (err.code !== 'ENOENT') + console.warn( + `Unable to obtain updater for: ${JSON.stringify(arg)}\n - Error: ${ + err.message + }\n - Skipping...`, + ); } } /** * We weren't able to resolve an updater for the argument. */ if (!isValidUpdater(updater.updater)) { - return false + return false; } - return updater -} + return updater; +}; diff --git a/lib/updaters/types/csproj.js b/lib/updaters/types/csproj.js index 906b64330..286ea5b7c 100644 --- a/lib/updaters/types/csproj.js +++ b/lib/updaters/types/csproj.js @@ -1,13 +1,15 @@ -const versionRegex = /(.*)<\/Version>/ +const versionRegex = /(.*)<\/Version>/; module.exports.readVersion = function (contents) { - const matches = versionRegex.exec(contents) + const matches = versionRegex.exec(contents); if (matches === null || matches.length !== 2) { - throw new Error('Failed to read the Version field in your csproj file - is it present?') + throw new Error( + 'Failed to read the Version field in your csproj file - is it present?', + ); } - return matches[1] -} + return matches[1]; +}; module.exports.writeVersion = function (contents, version) { - return contents.replace(versionRegex, `${version}`) -} + return contents.replace(versionRegex, `${version}`); +}; diff --git a/lib/updaters/types/gradle.js b/lib/updaters/types/gradle.js index 818922e3f..58d9dc879 100644 --- a/lib/updaters/types/gradle.js +++ b/lib/updaters/types/gradle.js @@ -1,16 +1,18 @@ -const versionRegex = /^version\s+=\s+['"]([\d.]+)['"]/m +const versionRegex = /^version\s+=\s+['"]([\d.]+)['"]/m; module.exports.readVersion = function (contents) { - const matches = versionRegex.exec(contents) + const matches = versionRegex.exec(contents); if (matches === null) { - throw new Error('Failed to read the version field in your gradle file - is it present?') + throw new Error( + 'Failed to read the version field in your gradle file - is it present?', + ); } - return matches[1] -} + return matches[1]; +}; module.exports.writeVersion = function (contents, version) { return contents.replace(versionRegex, () => { - return `version = "${version}"` - }) -} + return `version = "${version}"`; + }); +}; diff --git a/lib/updaters/types/json.js b/lib/updaters/types/json.js index e0cba511c..73e7c7e58 100644 --- a/lib/updaters/types/json.js +++ b/lib/updaters/types/json.js @@ -1,25 +1,25 @@ -const stringifyPackage = require('../../stringify-package') -const detectIndent = require('detect-indent') -const detectNewline = require('detect-newline') +const stringifyPackage = require('../../stringify-package'); +const detectIndent = require('detect-indent'); +const detectNewline = require('detect-newline'); module.exports.readVersion = function (contents) { - return JSON.parse(contents).version -} + return JSON.parse(contents).version; +}; module.exports.writeVersion = function (contents, version) { - const json = JSON.parse(contents) - const indent = detectIndent(contents).indent - const newline = detectNewline(contents) - json.version = version + const json = JSON.parse(contents); + const indent = detectIndent(contents).indent; + const newline = detectNewline(contents); + json.version = version; if (json.packages && json.packages['']) { // package-lock v2 stores version there too - json.packages[''].version = version + json.packages[''].version = version; } - return stringifyPackage(json, indent, newline) -} + return stringifyPackage(json, indent, newline); +}; module.exports.isPrivate = function (contents) { - return JSON.parse(contents).private -} + return JSON.parse(contents).private; +}; diff --git a/lib/updaters/types/plain-text.js b/lib/updaters/types/plain-text.js index 18bcabed2..ecc19d33b 100644 --- a/lib/updaters/types/plain-text.js +++ b/lib/updaters/types/plain-text.js @@ -1,7 +1,7 @@ module.exports.readVersion = function (contents) { - return contents -} + return contents; +}; module.exports.writeVersion = function (_contents, version) { - return version -} + return version; +}; diff --git a/lib/write-file.js b/lib/write-file.js index b6aaa19f7..b50ad4222 100644 --- a/lib/write-file.js +++ b/lib/write-file.js @@ -1,6 +1,6 @@ -const fs = require('fs') +const fs = require('fs'); module.exports = function (args, filePath, content) { - if (args.dryRun) return - fs.writeFileSync(filePath, content, 'utf8') -} + if (args.dryRun) return; + fs.writeFileSync(filePath, content, 'utf8'); +}; diff --git a/package-lock.json b/package-lock.json index 9f197df33..a03d9654b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "chai": "^4.3.10", "chai-as-promised": "^7.1.1", "eslint": "^8.52.0", - "eslint-config-standard": "^17.1.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.29.0", "eslint-plugin-mocha": "^10.2.0", "eslint-plugin-n": "^15.2.0", @@ -40,6 +40,7 @@ "mock-fs": "^5.2.0", "mockery": "^2.1.0", "nyc": "^15.1.0", + "prettier": "3.0.3", "shelljs": "^0.8.5", "std-mocks": "^2.0.0", "strip-ansi": "^6.0.0" @@ -2062,33 +2063,16 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=12.0.0" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, "peerDependencies": { - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0" + "eslint": ">=7.0.0" } }, "node_modules/eslint-import-resolver-node": { @@ -5078,6 +5062,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -7978,10 +7977,10 @@ } } }, - "eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "requires": {} }, @@ -10080,6 +10079,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", diff --git a/package.json b/package.json index e6fd0b576..87953c8fb 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,10 @@ "bin": "bin/cli.js", "scripts": { "fix": "eslint . --fix", - "posttest": "eslint .", + "posttest": "eslint . && npm run format:check", + "format:base": "prettier \"./**/*.{ts,js}\"", + "format:fix": "npm run format:base -- --write", + "format:check": "npm run format:base -- --check", "test": "nyc mocha --timeout=30000", "test:unit": "mocha --exclude test/git.spec.js", "coverage": "nyc report --reporter=lcov", @@ -59,7 +62,7 @@ "chai": "^4.3.10", "chai-as-promised": "^7.1.1", "eslint": "^8.52.0", - "eslint-config-standard": "^17.1.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.29.0", "eslint-plugin-mocha": "^10.2.0", "eslint-plugin-n": "^15.2.0", @@ -68,6 +71,7 @@ "mock-fs": "^5.2.0", "mockery": "^2.1.0", "nyc": "^15.1.0", + "prettier": "3.0.3", "shelljs": "^0.8.5", "std-mocks": "^2.0.0", "strip-ansi": "^6.0.0" diff --git a/test/config-files.spec.js b/test/config-files.spec.js index 632738478..c092df98b 100644 --- a/test/config-files.spec.js +++ b/test/config-files.spec.js @@ -1,22 +1,20 @@ -/* global describe it beforeEach afterEach */ - -'use strict' - -const shell = require('shelljs') -const fs = require('fs') -const { Readable } = require('stream') -const mockery = require('mockery') -const stdMocks = require('std-mocks') - -const chai = require('chai') -const expect = chai.expect -chai.use(require('chai-as-promised')) - -function exec () { - const cli = require('../command') - const opt = cli.parse('commit-and-tag-version') - opt.skip = { commit: true, tag: true } - return require('../index')(opt) +'use strict'; + +const shell = require('shelljs'); +const fs = require('fs'); +const { Readable } = require('stream'); +const mockery = require('mockery'); +const stdMocks = require('std-mocks'); + +const chai = require('chai'); +const expect = chai.expect; +chai.use(require('chai-as-promised')); + +function exec() { + const cli = require('../command'); + const opt = cli.parse('commit-and-tag-version'); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); } /** @@ -28,149 +26,152 @@ function exec () { * changelog?: string | Error | Array string | null> * tags?: string[] | Error */ -function mock ({ bump, changelog, tags } = {}) { - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, parserOpts, cb) { - if (typeof bump === 'function') bump(opt, parserOpts, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, bump ? { releaseType: bump } : {}) - }) +function mock({ bump, changelog, tags } = {}) { + mockery.enable({ warnOnUnregistered: false, useCleanCache: true }); - if (!Array.isArray(changelog)) changelog = [changelog] + mockery.registerMock( + 'conventional-recommended-bump', + function (opt, parserOpts, cb) { + if (typeof bump === 'function') bump(opt, parserOpts, cb); + else if (bump instanceof Error) cb(bump); + else cb(null, bump ? { releaseType: bump } : {}); + }, + ); + + if (!Array.isArray(changelog)) changelog = [changelog]; mockery.registerMock( 'conventional-changelog', (opt) => new Readable({ - read (_size) { - const next = changelog.shift() + read(_size) { + const next = changelog.shift(); if (next instanceof Error) { - this.destroy(next) + this.destroy(next); } else if (typeof next === 'function') { - this.push(next(opt)) + this.push(next(opt)); } else { - this.push(next ? Buffer.from(next, 'utf8') : null) + this.push(next ? Buffer.from(next, 'utf8') : null); } - } - }) - ) + }, + }), + ); mockery.registerMock('git-semver-tags', function (cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags | []) - }) + if (tags instanceof Error) cb(tags); + else cb(null, tags | []); + }); - stdMocks.use() - return () => stdMocks.flush() + stdMocks.use(); + return () => stdMocks.flush(); } describe('config files', function () { beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') + shell.rm('-rf', 'tmp'); + shell.config.silent = true; + shell.mkdir('tmp'); + shell.cd('tmp'); fs.writeFileSync( 'package.json', JSON.stringify({ version: '1.0.0' }), - 'utf-8' - ) - }) + 'utf-8', + ); + }); afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') + shell.cd('../'); + shell.rm('-rf', 'tmp'); - mockery.deregisterAll() - mockery.disable() - stdMocks.restore() + mockery.deregisterAll(); + mockery.disable(); + stdMocks.restore(); // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() + const { stdout } = stdMocks.flush(); for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) + if (str.startsWith(' ')) process.stdout.write(str); } - }) + }); - const configKeys = ['commit-and-tag-version', 'standard-version'] + const configKeys = ['commit-and-tag-version', 'standard-version']; configKeys.forEach((configKey) => { it(`reads config from package.json key '${configKey}'`, async function () { const issueUrlFormat = - 'https://commit-and-tag-version.company.net/browse/{{id}}' + 'https://commit-and-tag-version.company.net/browse/{{id}}'; mock({ bump: 'minor', - changelog: ({ preset }) => preset.issueUrlFormat - }) + changelog: ({ preset }) => preset.issueUrlFormat, + }); const pkg = { version: '1.0.0', repository: { url: 'git+https://company@scm.org/office/app.git' }, - [configKey]: { issueUrlFormat } - } - fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8') + [configKey]: { issueUrlFormat }, + }; + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(issueUrlFormat); + }); + }); it('reads config from .versionrc', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc', JSON.stringify({ issueUrlFormat }), 'utf-8') + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc', JSON.stringify({ issueUrlFormat }), 'utf-8'); - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(issueUrlFormat); + }); it('reads config from .versionrc.json', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); fs.writeFileSync( '.versionrc.json', JSON.stringify({ issueUrlFormat }), - 'utf-8' - ) + 'utf-8', + ); - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(issueUrlFormat); + }); it('evaluates a config-function from .versionrc.js', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' + const issueUrlFormat = 'http://www.foo.com/{{id}}'; const src = `module.exports = function() { return ${JSON.stringify({ - issueUrlFormat - })} }` - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc.js', src, 'utf-8') + issueUrlFormat, + })} }`; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc.js', src, 'utf-8'); - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(issueUrlFormat); + }); it('evaluates a config-object from .versionrc.js', async function () { - const issueUrlFormat = 'http://www.foo.com/{{id}}' - const src = `module.exports = ${JSON.stringify({ issueUrlFormat })}` - const changelog = ({ preset }) => preset.issueUrlFormat - mock({ bump: 'minor', changelog }) - fs.writeFileSync('.versionrc.js', src, 'utf-8') + const issueUrlFormat = 'http://www.foo.com/{{id}}'; + const src = `module.exports = ${JSON.stringify({ issueUrlFormat })}`; + const changelog = ({ preset }) => preset.issueUrlFormat; + mock({ bump: 'minor', changelog }); + fs.writeFileSync('.versionrc.js', src, 'utf-8'); - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(issueUrlFormat) - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(issueUrlFormat); + }); it('throws an error when a non-object is returned from .versionrc.js', async function () { - mock({ bump: 'minor' }) - fs.writeFileSync('.versionrc.js', 'module.exports = 3', 'utf-8') + mock({ bump: 'minor' }); + fs.writeFileSync('.versionrc.js', 'module.exports = 3', 'utf-8'); - expect(exec).to.throw(/Invalid configuration/) - }) -}) + expect(exec).to.throw(/Invalid configuration/); + }); +}); diff --git a/test/core.spec.js b/test/core.spec.js index 8b7f17c69..44b41dc90 100644 --- a/test/core.spec.js +++ b/test/core.spec.js @@ -1,37 +1,35 @@ -/* global describe it afterEach */ +'use strict'; -'use strict' +const shell = require('shelljs'); +const fs = require('fs'); +const { resolve } = require('path'); +const { Readable } = require('stream'); +const mockFS = require('mock-fs'); +const mockery = require('mockery'); +const stdMocks = require('std-mocks'); +const stripAnsi = require('strip-ansi'); -const shell = require('shelljs') -const fs = require('fs') -const { resolve } = require('path') -const { Readable } = require('stream') -const mockFS = require('mock-fs') -const mockery = require('mockery') -const stdMocks = require('std-mocks') -const stripAnsi = require('strip-ansi') +const cli = require('../command'); +const formatCommitMessage = require('../lib/format-commit-message'); -const cli = require('../command') -const formatCommitMessage = require('../lib/format-commit-message') - -const chai = require('chai') -const should = chai.should() -const expect = chai.expect -chai.use(require('chai-as-promised')) +const chai = require('chai'); +const should = chai.should(); +const expect = chai.expect; +chai.use(require('chai-as-promised')); // set by mock() -let standardVersion +let standardVersion; -function exec (opt = '', git) { +function exec(opt = '', git) { if (typeof opt === 'string') { - opt = cli.parse(`commit-and-tag-version ${opt}`) + opt = cli.parse(`commit-and-tag-version ${opt}`); } - if (!git) opt.skip = Object.assign({}, opt.skip, { commit: true, tag: true }) - return standardVersion(opt) + if (!git) opt.skip = Object.assign({}, opt.skip, { commit: true, tag: true }); + return standardVersion(opt); } -function getPackageVersion () { - return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version +function getPackageVersion() { + return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version; } /** @@ -46,695 +44,746 @@ function getPackageVersion () { * pkg?: { [string]: any } * tags?: string[] | Error */ -function mock ({ bump, changelog, execFile, fs, pkg, tags } = {}) { - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, parserOpts, cb) { - if (typeof bump === 'function') bump(opt, parserOpts, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, bump ? { releaseType: bump } : {}) - }) +function mock({ bump, changelog, execFile, fs, pkg, tags } = {}) { + mockery.enable({ warnOnUnregistered: false, useCleanCache: true }); - if (!Array.isArray(changelog)) changelog = [changelog] + mockery.registerMock( + 'conventional-recommended-bump', + function (opt, parserOpts, cb) { + if (typeof bump === 'function') bump(opt, parserOpts, cb); + else if (bump instanceof Error) cb(bump); + else cb(null, bump ? { releaseType: bump } : {}); + }, + ); + + if (!Array.isArray(changelog)) changelog = [changelog]; mockery.registerMock( 'conventional-changelog', (opt) => new Readable({ - read (_size) { - const next = changelog.shift() + read(_size) { + const next = changelog.shift(); if (next instanceof Error) { - this.destroy(next) + this.destroy(next); } else if (typeof next === 'function') { - this.push(next(opt)) + this.push(next(opt)); } else { - this.push(next ? Buffer.from(next, 'utf8') : null) + this.push(next ? Buffer.from(next, 'utf8') : null); } - } - }) - ) + }, + }), + ); mockery.registerMock('git-semver-tags', function (cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags | []) - }) + if (tags instanceof Error) cb(tags); + else cb(null, tags | []); + }); if (typeof execFile === 'function') { // called from commit & tag lifecycle methods - mockery.registerMock('../run-execFile', execFile) + mockery.registerMock('../run-execFile', execFile); } // needs to be set after mockery, but before mock-fs - standardVersion = require('../index') + standardVersion = require('../index'); - fs = Object.assign({}, fs) + fs = Object.assign({}, fs); if (pkg) { - fs['package.json'] = JSON.stringify(pkg) + fs['package.json'] = JSON.stringify(pkg); } else if (pkg === undefined && !fs['package.json']) { - fs['package.json'] = JSON.stringify({ version: '1.0.0' }) + fs['package.json'] = JSON.stringify({ version: '1.0.0' }); } - mockFS(fs) + mockFS(fs); - stdMocks.use() - return () => stdMocks.flush() + stdMocks.use(); + return () => stdMocks.flush(); } -function unmock () { - mockery.deregisterAll() - mockery.disable() - mockFS.restore() - stdMocks.restore() - standardVersion = null +function unmock() { + mockery.deregisterAll(); + mockery.disable(); + mockFS.restore(); + stdMocks.restore(); + standardVersion = null; // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() + const { stdout } = stdMocks.flush(); for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) + if (str.startsWith(' ')) process.stdout.write(str); } } describe('format-commit-message', function () { it('works for no {{currentTag}}', function () { formatCommitMessage('chore(release): 1.0.0', '1.0.0').should.equal( - 'chore(release): 1.0.0' - ) - }) + 'chore(release): 1.0.0', + ); + }); it('works for one {{currentTag}}', function () { formatCommitMessage('chore(release): {{currentTag}}', '1.0.0').should.equal( - 'chore(release): 1.0.0' - ) - }) + 'chore(release): 1.0.0', + ); + }); it('works for two {{currentTag}}', function () { formatCommitMessage( 'chore(release): {{currentTag}} \n\n* CHANGELOG: https://github.com/absolute-version/commit-and-tag-version/blob/v{{currentTag}}/CHANGELOG.md', - '1.0.0' + '1.0.0', ).should.equal( - 'chore(release): 1.0.0 \n\n* CHANGELOG: https://github.com/absolute-version/commit-and-tag-version/blob/v1.0.0/CHANGELOG.md' - ) - }) -}) + 'chore(release): 1.0.0 \n\n* CHANGELOG: https://github.com/absolute-version/commit-and-tag-version/blob/v1.0.0/CHANGELOG.md', + ); + }); +}); describe('cli', function () { - afterEach(unmock) + afterEach(unmock); describe('CHANGELOG.md does not exist', function () { it('populates changelog with commits since last tag by default', async function () { - mock({ bump: 'patch', changelog: 'patch release\n', tags: ['v1.0.0'] }) - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/patch release/) - }) + mock({ bump: 'patch', changelog: 'patch release\n', tags: ['v1.0.0'] }); + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/patch release/); + }); it('includes all commits if --first-release is true', async function () { mock({ bump: 'minor', changelog: 'first commit\npatch release\n', - pkg: { version: '1.0.1' } - }) - await exec('--first-release') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/patch release/) - content.should.match(/first commit/) - }) + pkg: { version: '1.0.1' }, + }); + await exec('--first-release'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/patch release/); + content.should.match(/first commit/); + }); it('skipping changelog will not create a changelog file', async function () { - mock({ bump: 'minor', changelog: 'foo\n' }) - await exec('--skip.changelog true') - getPackageVersion().should.equal('1.1.0') - expect(() => fs.readFileSync('CHANGELOG.md', 'utf-8')).to.throw(/ENOENT/) - }) - }) + mock({ bump: 'minor', changelog: 'foo\n' }); + await exec('--skip.changelog true'); + getPackageVersion().should.equal('1.1.0'); + expect(() => fs.readFileSync('CHANGELOG.md', 'utf-8')).to.throw(/ENOENT/); + }); + }); describe('CHANGELOG.md exists', function () { it('appends the new release above the last release, removing the old header (legacy format), and does not retain any front matter', async function () { - const frontMatter = - '---\nstatus: new\n---\n' + const frontMatter = '---\nstatus: new\n---\n'; mock({ bump: 'patch', changelog: 'release 1.0.1\n', - fs: { 'CHANGELOG.md': frontMatter + 'legacy header format\n' }, - tags: ['v1.0.0'] - }) - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/1\.0\.1/) - content.should.not.match(/legacy header format/) - content.should.not.match(/---status: new---/) - }) + fs: { + 'CHANGELOG.md': + frontMatter + 'legacy header format\n', + }, + tags: ['v1.0.0'], + }); + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/1\.0\.1/); + content.should.not.match(/legacy header format/); + content.should.not.match(/---status: new---/); + }); it('appends the new release above the last release, replacing the old header (standard-version format) with header (new format), and retains any front matter', async function () { - const { header } = require('../defaults') + const { header } = require('../defaults'); const standardVersionHeader = - '# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.' + '# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.'; - const frontMatter = - '---\nstatus: new\n---\n' + const frontMatter = '---\nstatus: new\n---\n'; const changelog101 = - '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n' + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; const changelog100 = - '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n' + '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'; - const initialChangelog = frontMatter + '\n' + standardVersionHeader + '\n' + changelog100 + const initialChangelog = + frontMatter + '\n' + standardVersionHeader + '\n' + changelog100; mock({ bump: 'patch', changelog: changelog101, fs: { 'CHANGELOG.md': initialChangelog }, - tags: ['v1.0.0'] - }) - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(frontMatter + '\n' + header + '\n' + changelog101 + changelog100) - }) + tags: ['v1.0.0'], + }); + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.equal( + frontMatter + '\n' + header + '\n' + changelog101 + changelog100, + ); + }); it('appends the new release above the last release, removing the old header (new format), and retains any front matter', async function () { - const { header } = require('../defaults') - const frontMatter = - '---\nstatus: new\n---\n' + const { header } = require('../defaults'); + const frontMatter = '---\nstatus: new\n---\n'; const changelog101 = - '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n' + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; const changelog100 = - '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n' + '### [1.0.0](/compare/v0.0.1...v1.0.0) (YYYY-MM-DD)\n\n\n### Features\n\n* Version one feature set\n'; - const initialChangelog = frontMatter + '\n' + header + '\n' + changelog100 + const initialChangelog = + frontMatter + '\n' + header + '\n' + changelog100; mock({ bump: 'patch', changelog: changelog101, fs: { 'CHANGELOG.md': initialChangelog }, - tags: ['v1.0.0'] - }) - await exec() + tags: ['v1.0.0'], + }); + await exec(); - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(frontMatter + '\n' + header + '\n' + changelog101 + changelog100) - }) + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.equal( + frontMatter + '\n' + header + '\n' + changelog101 + changelog100, + ); + }); it('appends the new release above the last release, removing the old header (new format)', async function () { - const { header } = require('../defaults') + const { header } = require('../defaults'); const changelog1 = - '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n' - mock({ bump: 'patch', changelog: changelog1, tags: ['v1.0.0'] }) - await exec() - let content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(header + '\n' + changelog1) + '### [1.0.1](/compare/v1.0.0...v1.0.1) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* patch release ABCDEFXY\n'; + mock({ bump: 'patch', changelog: changelog1, tags: ['v1.0.0'] }); + await exec(); + let content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.equal(header + '\n' + changelog1); const changelog2 = - '### [1.0.2](/compare/v1.0.1...v1.0.2) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* another patch release ABCDEFXY\n' - unmock() + '### [1.0.2](/compare/v1.0.1...v1.0.2) (YYYY-MM-DD)\n\n\n### Bug Fixes\n\n* another patch release ABCDEFXY\n'; + unmock(); mock({ bump: 'patch', changelog: changelog2, fs: { 'CHANGELOG.md': content }, - tags: ['v1.0.0', 'v1.0.1'] - }) - await exec() - content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(header + '\n' + changelog2 + changelog1) - }) + tags: ['v1.0.0', 'v1.0.1'], + }); + await exec(); + content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.equal(header + '\n' + changelog2 + changelog1); + }); it('[DEPRECATED] (--changelogHeader) allows for a custom changelog header', async function () { - const header = '# Pork Chop Log' + const header = '# Pork Chop Log'; mock({ bump: 'minor', changelog: header + '\n', - fs: { 'CHANGELOG.md': '' } - }) - await exec(`--changelogHeader="${header}"`) - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(new RegExp(header)) - }) + fs: { 'CHANGELOG.md': '' }, + }); + await exec(`--changelogHeader="${header}"`); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(new RegExp(header)); + }); it('[DEPRECATED] (--changelogHeader) exits with error if changelog header matches last version search regex', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) - await expect(exec('--changelogHeader="## 3.0.2"')).to.be.rejectedWith(/custom changelog header must not match/) - }) - }) + mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }); + await expect(exec('--changelogHeader="## 3.0.2"')).to.be.rejectedWith( + /custom changelog header must not match/, + ); + }); + }); describe('lifecycle scripts', function () { describe('prerelease hook', function () { it('should run the prerelease hook when provided', async function () { const flush = mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - prerelease: "node -e \"console.error('prerelease' + ' ran')\"" - } - }) - const { stderr } = flush() - stderr.join('\n').should.match(/prerelease ran/) - }) + prerelease: "node -e \"console.error('prerelease' + ' ran')\"", + }, + }); + const { stderr } = flush(); + stderr.join('\n').should.match(/prerelease ran/); + }); it('should abort if the hook returns a non-zero exit code', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - - await expect(exec({ - scripts: { - prerelease: "node -e \"throw new Error('prerelease' + ' fail')\"" - } - })).to.be.rejectedWith(/prerelease fail/) - }) - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + + await expect( + exec({ + scripts: { + prerelease: "node -e \"throw new Error('prerelease' + ' fail')\"", + }, + }), + ).to.be.rejectedWith(/prerelease fail/); + }); + }); describe('prebump hook', function () { it('should allow prebump hook to return an alternate version #', async function () { const flush = mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - prebump: "node -e \"console.log(Array.of(9, 9, 9).join('.'))\"" - } - }) - const { stdout } = flush() - stdout.join('').should.match(/9\.9\.9/) - getPackageVersion().should.equal('9.9.9') - }) + prebump: 'node -e "console.log(Array.of(9, 9, 9).join(\'.\'))"', + }, + }); + const { stdout } = flush(); + stdout.join('').should.match(/9\.9\.9/); + getPackageVersion().should.equal('9.9.9'); + }); it('should not allow prebump hook to return a releaseAs command', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - prebump: "node -e \"console.log('major')\"" - } - }) - getPackageVersion().should.equal('1.1.0') - }) + prebump: 'node -e "console.log(\'major\')"', + }, + }); + getPackageVersion().should.equal('1.1.0'); + }); it('should allow prebump hook to return an arbitrary string', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - prebump: "node -e \"console.log('Hello World')\"" - } - }) - getPackageVersion().should.equal('1.1.0') - }) + prebump: 'node -e "console.log(\'Hello World\')"', + }, + }); + getPackageVersion().should.equal('1.1.0'); + }); it('should allow prebump hook to return a version with build info', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - prebump: "node -e \"console.log('9.9.9-test+build')\"" - } - }) - getPackageVersion().should.equal('9.9.9-test+build') - }) - }) + prebump: 'node -e "console.log(\'9.9.9-test+build\')"', + }, + }); + getPackageVersion().should.equal('9.9.9-test+build'); + }); + }); describe('postbump hook', function () { it('should run the postbump hook when provided', async function () { const flush = mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); await exec({ scripts: { - postbump: "node -e \"console.error('postbump' + ' ran')\"" - } - }) - const { stderr } = flush() - stderr.join('\n').should.match(/postbump ran/) - }) + postbump: "node -e \"console.error('postbump' + ' ran')\"", + }, + }); + const { stderr } = flush(); + stderr.join('\n').should.match(/postbump ran/); + }); it('should run the postbump and exit with error when postbump fails', async function () { mock({ bump: 'minor', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - - await expect(exec({ - scripts: { - postbump: "node -e \"throw new Error('postbump' + ' fail')\"" - } - })).to.be.rejectedWith(/postbump fail/) - }) - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + + await expect( + exec({ + scripts: { + postbump: "node -e \"throw new Error('postbump' + ' fail')\"", + }, + }), + ).to.be.rejectedWith(/postbump fail/); + }); + }); describe('manual-release', function () { describe('release-types', function () { - const regularTypes = ['major', 'minor', 'patch'] - const nextVersion = { major: '2.0.0', minor: '1.1.0', patch: '1.0.1' } + const regularTypes = ['major', 'minor', 'patch']; + const nextVersion = { major: '2.0.0', minor: '1.1.0', patch: '1.0.1' }; regularTypes.forEach(function (type) { it('creates a ' + type + ' release', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as ' + type) - getPackageVersion().should.equal(nextVersion[type]) - }) - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + await exec('--release-as ' + type); + getPackageVersion().should.equal(nextVersion[type]); + }); + }); // this is for pre-releases regularTypes.forEach(function (type) { it('creates a pre' + type + ' release', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as ' + type + ' --prerelease ' + type) - getPackageVersion().should.equal(`${nextVersion[type]}-${type}.0`) - }) - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + await exec('--release-as ' + type + ' --prerelease ' + type); + getPackageVersion().should.equal(`${nextVersion[type]}-${type}.0`); + }); + }); it('exits with error if an invalid release type is provided', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) + mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }); - await expect(exec('--release-as invalid')).to.be.rejectedWith(/releaseAs must be one of/) - }) - }) + await expect(exec('--release-as invalid')).to.be.rejectedWith( + /releaseAs must be one of/, + ); + }); + }); describe('release-as-exact', function () { it('releases as v100.0.0', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as v100.0.0') - getPackageVersion().should.equal('100.0.0') - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + await exec('--release-as v100.0.0'); + getPackageVersion().should.equal('100.0.0'); + }); it('releases as 200.0.0-amazing', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) - await exec('--release-as 200.0.0-amazing') - getPackageVersion().should.equal('200.0.0-amazing') - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); + await exec('--release-as 200.0.0-amazing'); + getPackageVersion().should.equal('200.0.0-amazing'); + }); it('releases as 100.0.0 with prerelease amazing', async function () { mock({ bump: 'patch', fs: { 'CHANGELOG.md': 'legacy header format\n' }, pkg: { - version: '1.0.0' - } - }) - await exec('--release-as 100.0.0 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.0') - }) + version: '1.0.0', + }, + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.0'); + }); it('release 100.0.0 with prerelease amazing bumps build', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '100.0.0-amazing.0' - } - }) - await exec('--release-as 100.0.0 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.1') - }) + version: '100.0.0-amazing.0', + }, + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.1'); + }); it('release 100.0.0-amazing.0 with prerelease amazing bumps build', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '100.0.0-amazing.1' - } - }) - await exec('--release-as 100.0.0-amazing.0 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.2') - }) + version: '100.0.0-amazing.1', + }, + }); + await exec('--release-as 100.0.0-amazing.0 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.2'); + }); it('release 100.0.0 with prerelease amazing correctly sets version', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '99.0.0-amazing.0' - } - }) - await exec('--release-as 100.0.0 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.0') - }) + version: '99.0.0-amazing.0', + }, + }); + await exec('--release-as 100.0.0 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.0'); + }); it('release 100.0.0-amazing.0 with prerelease amazing correctly sets version', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '99.0.0-amazing.0' - } - }) - await exec('--release-as 100.0.0-amazing.0 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.0') - }) + version: '99.0.0-amazing.0', + }, + }); + await exec('--release-as 100.0.0-amazing.0 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.0'); + }); it('release 100.0.0-amazing.0 with prerelease amazing retains build metadata', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '100.0.0-amazing.0' - } - }) - await exec('--release-as 100.0.0-amazing.0+build.1234 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.1+build.1234') - }) + version: '100.0.0-amazing.0', + }, + }); + await exec( + '--release-as 100.0.0-amazing.0+build.1234 --prerelease amazing', + ); + should.equal(getPackageVersion(), '100.0.0-amazing.1+build.1234'); + }); it('release 100.0.0-amazing.3 with prerelease amazing correctly sets prerelease version', async function () { mock({ bump: 'patch', - fs: { 'CHANGELOG.md': 'legacy header format\n' }, + fs: { + 'CHANGELOG.md': + 'legacy header format\n', + }, pkg: { - version: '100.0.0-amazing.0' - } - }) - await exec('--release-as 100.0.0-amazing.3 --prerelease amazing') - should.equal(getPackageVersion(), '100.0.0-amazing.3') - }) - }) + version: '100.0.0-amazing.0', + }, + }); + await exec('--release-as 100.0.0-amazing.3 --prerelease amazing'); + should.equal(getPackageVersion(), '100.0.0-amazing.3'); + }); + }); it('creates a prerelease with a new minor version after two prerelease patches', async function () { - let releaseType = 'patch' - const bump = (_, __, cb) => cb(null, { releaseType }) + let releaseType = 'patch'; + const bump = (_, __, cb) => cb(null, { releaseType }); mock({ bump, - fs: { 'CHANGELOG.md': 'legacy header format\n' } - }) + fs: { 'CHANGELOG.md': 'legacy header format\n' }, + }); - await exec('--release-as patch --prerelease dev') - getPackageVersion().should.equal('1.0.1-dev.0') + await exec('--release-as patch --prerelease dev'); + getPackageVersion().should.equal('1.0.1-dev.0'); - await exec('--prerelease dev') - getPackageVersion().should.equal('1.0.1-dev.1') + await exec('--prerelease dev'); + getPackageVersion().should.equal('1.0.1-dev.1'); - releaseType = 'minor' - await exec('--release-as minor --prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.0') + releaseType = 'minor'; + await exec('--release-as minor --prerelease dev'); + getPackageVersion().should.equal('1.1.0-dev.0'); - await exec('--release-as minor --prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.1') + await exec('--release-as minor --prerelease dev'); + getPackageVersion().should.equal('1.1.0-dev.1'); - await exec('--prerelease dev') - getPackageVersion().should.equal('1.1.0-dev.2') - }) + await exec('--prerelease dev'); + getPackageVersion().should.equal('1.1.0-dev.2'); + }); it('exits with error if an invalid release version is provided', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) + mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }); - await expect(exec('--release-as 10.2')).to.be.rejectedWith(/releaseAs must be one of/) - }) + await expect(exec('--release-as 10.2')).to.be.rejectedWith( + /releaseAs must be one of/, + ); + }); it('exits with error if release version conflicts with prerelease', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) + mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }); - await expect(exec('--release-as 1.2.3-amazing.2 --prerelease awesome')).to.be - .rejectedWith(/releaseAs and prerelease have conflicting prerelease identifiers/) - }) - }) + await expect( + exec('--release-as 1.2.3-amazing.2 --prerelease awesome'), + ).to.be.rejectedWith( + /releaseAs and prerelease have conflicting prerelease identifiers/, + ); + }); + }); it('appends line feed at end of package.json', async function () { - mock({ bump: 'patch' }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n "version": "1.0.1"\n}\n') - }) + mock({ bump: 'patch' }); + await exec(); + const pkgJson = fs.readFileSync('package.json', 'utf-8'); + pkgJson.should.equal('{\n "version": "1.0.1"\n}\n'); + }); it('preserves indentation of tabs in package.json', async function () { mock({ bump: 'patch', - fs: { 'package.json': '{\n\t"version": "1.0.0"\n}\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n\t"version": "1.0.1"\n}\n') - }) + fs: { 'package.json': '{\n\t"version": "1.0.0"\n}\n' }, + }); + await exec(); + const pkgJson = fs.readFileSync('package.json', 'utf-8'); + pkgJson.should.equal('{\n\t"version": "1.0.1"\n}\n'); + }); it('preserves indentation of spaces in package.json', async function () { mock({ bump: 'patch', - fs: { 'package.json': '{\n "version": "1.0.0"\n}\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\n "version": "1.0.1"\n}\n') - }) + fs: { 'package.json': '{\n "version": "1.0.0"\n}\n' }, + }); + await exec(); + const pkgJson = fs.readFileSync('package.json', 'utf-8'); + pkgJson.should.equal('{\n "version": "1.0.1"\n}\n'); + }); it('preserves carriage return + line feed in package.json', async function () { mock({ bump: 'patch', - fs: { 'package.json': '{\r\n "version": "1.0.0"\r\n}\r\n' } - }) - await exec() - const pkgJson = fs.readFileSync('package.json', 'utf-8') - pkgJson.should.equal('{\r\n "version": "1.0.1"\r\n}\r\n') - }) + fs: { 'package.json': '{\r\n "version": "1.0.0"\r\n}\r\n' }, + }); + await exec(); + const pkgJson = fs.readFileSync('package.json', 'utf-8'); + pkgJson.should.equal('{\r\n "version": "1.0.1"\r\n}\r\n'); + }); it('does not print output when the --silent flag is passed', async function () { - const flush = mock() - await exec('--silent') - flush().should.eql({ stdout: [], stderr: [] }) - }) - }) + const flush = mock(); + await exec('--silent'); + flush().should.eql({ stdout: [], stderr: [] }); + }); + }); describe('commit-and-tag-version', function () { - afterEach(unmock) + afterEach(unmock); it('should exit on bump error', async function () { - mock({ bump: new Error('bump err') }) + mock({ bump: new Error('bump err') }); - await expect(exec()).to.be.rejectedWith(/bump err/) - }) + await expect(exec()).to.be.rejectedWith(/bump err/); + }); it('should exit on changelog error', async function () { - mock({ bump: 'minor', changelog: new Error('changelog err') }) + mock({ bump: 'minor', changelog: new Error('changelog err') }); - await expect(exec()).to.be.rejectedWith(/changelog err/) - }) + await expect(exec()).to.be.rejectedWith(/changelog err/); + }); it('should exit with error without a package file to bump', async function () { - mock({ bump: 'patch', pkg: false }) + mock({ bump: 'patch', pkg: false }); - await expect(exec({ gitTagFallback: false })).to.be.rejectedWith('no package file found') - }) + await expect(exec({ gitTagFallback: false })).to.be.rejectedWith( + 'no package file found', + ); + }); it('bumps version # in bower.json', async function () { mock({ bump: 'minor', fs: { 'bower.json': JSON.stringify({ version: '1.0.0' }) }, - tags: ['v1.0.0'] - }) - await exec() + tags: ['v1.0.0'], + }); + await exec(); JSON.parse(fs.readFileSync('bower.json', 'utf-8')).version.should.equal( - '1.1.0' - ) - getPackageVersion().should.equal('1.1.0') - }) + '1.1.0', + ); + getPackageVersion().should.equal('1.1.0'); + }); it('bumps version # in manifest.json', async function () { mock({ bump: 'minor', fs: { 'manifest.json': JSON.stringify({ version: '1.0.0' }) }, - tags: ['v1.0.0'] - }) - await exec() - JSON.parse(fs.readFileSync('manifest.json', 'utf-8')).version.should.equal( - '1.1.0' - ) - getPackageVersion().should.equal('1.1.0') - }) + tags: ['v1.0.0'], + }); + await exec(); + JSON.parse( + fs.readFileSync('manifest.json', 'utf-8'), + ).version.should.equal('1.1.0'); + getPackageVersion().should.equal('1.1.0'); + }); describe('custom `bumpFiles` support', function () { it('mix.exs + version.txt', async function () { - const updater = 'custom-updater.js' - const updaterModule = require('./mocks/updater/customer-updater') + const updater = 'custom-updater.js'; + const updaterModule = require('./mocks/updater/customer-updater'); mock({ bump: 'minor', fs: { 'mix.exs': fs.readFileSync('./test/mocks/mix.exs'), - 'version.txt': fs.readFileSync('./test/mocks/version.txt') + 'version.txt': fs.readFileSync('./test/mocks/version.txt'), }, - tags: ['v1.0.0'] - }) - mockery.registerMock(resolve(process.cwd(), updater), updaterModule) + tags: ['v1.0.0'], + }); + mockery.registerMock(resolve(process.cwd(), updater), updaterModule); await exec({ bumpFiles: [ 'version.txt', - { filename: 'mix.exs', updater: 'custom-updater.js' } - ] - }) - fs.readFileSync('mix.exs', 'utf-8').should.contain('version: "1.1.0"') - fs.readFileSync('version.txt', 'utf-8').should.equal('1.1.0') - }) + { filename: 'mix.exs', updater: 'custom-updater.js' }, + ], + }); + fs.readFileSync('mix.exs', 'utf-8').should.contain('version: "1.1.0"'); + fs.readFileSync('version.txt', 'utf-8').should.equal('1.1.0'); + }); it('bumps a custom `plain-text` file', async function () { mock({ bump: 'minor', fs: { 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-1.0.0.txt' - ) - } - }) + './test/mocks/VERSION-1.0.0.txt', + ), + }, + }); await exec({ - bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }] - }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('1.1.0') - }) + bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], + }); + fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('1.1.0'); + }); it('displays the new version from custom bumper with --dry-run', async function () { - const updater = 'increment-updater.js' - const updaterModule = require('./mocks/updater/increment-updater') + const updater = 'increment-updater.js'; + const updaterModule = require('./mocks/updater/increment-updater'); mock({ bump: 'minor', fs: { 'increment-version.txt': fs.readFileSync( - './test/mocks/increment-version.txt' - ) - } - }) - mockery.registerMock(resolve(process.cwd(), updater), updaterModule) + './test/mocks/increment-version.txt', + ), + }, + }); + mockery.registerMock(resolve(process.cwd(), updater), updaterModule); - const origInfo = console.info - const capturedOutput = [] + const origInfo = console.info; + const capturedOutput = []; console.info = (...args) => { - capturedOutput.push(...args) - origInfo(...args) - } + capturedOutput.push(...args); + origInfo(...args); + }; try { await exec({ - bumpFiles: [{ filename: 'increment-version.txt', updater: 'increment-updater.js' }], - dryRun: true - }) - const logOutput = capturedOutput.join(' ') - stripAnsi(logOutput).should.include('bumping version in increment-version.txt from 1 to 2') + bumpFiles: [ + { + filename: 'increment-version.txt', + updater: 'increment-updater.js', + }, + ], + dryRun: true, + }); + const logOutput = capturedOutput.join(' '); + stripAnsi(logOutput).should.include( + 'bumping version in increment-version.txt from 1 to 2', + ); } finally { - console.info = origInfo + console.info = origInfo; } - }) - }) + }); + }); describe('custom `packageFiles` support', function () { it('reads and writes to a custom `plain-text` file', async function () { @@ -742,39 +791,44 @@ describe('cli', function () { bump: 'minor', fs: { 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-6.3.1.txt' - ) - } - }) + './test/mocks/VERSION-6.3.1.txt', + ), + }, + }); await exec({ - packageFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], - bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }] - }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0') - }) + packageFiles: [ + { filename: 'VERSION_TRACKER.txt', type: 'plain-text' }, + ], + bumpFiles: [{ filename: 'VERSION_TRACKER.txt', type: 'plain-text' }], + }); + fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0'); + }); it('allows same object to be used in packageFiles and bumpFiles', async function () { mock({ bump: 'minor', fs: { 'VERSION_TRACKER.txt': fs.readFileSync( - './test/mocks/VERSION-6.3.1.txt' - ) - } - }) - const origWarn = console.warn + './test/mocks/VERSION-6.3.1.txt', + ), + }, + }); + const origWarn = console.warn; console.warn = () => { - throw new Error('console.warn should not be called') - } - const filedesc = { filename: 'VERSION_TRACKER.txt', type: 'plain-text' } + throw new Error('console.warn should not be called'); + }; + const filedesc = { + filename: 'VERSION_TRACKER.txt', + type: 'plain-text', + }; try { - await exec({ packageFiles: [filedesc], bumpFiles: [filedesc] }) - fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0') + await exec({ packageFiles: [filedesc], bumpFiles: [filedesc] }); + fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0'); } finally { - console.warn = origWarn + console.warn = origWarn; } - }) - }) + }); + }); it('`packageFiles` are bumped along with `bumpFiles` defaults [commit-and-tag-version#533]', async function () { mock({ @@ -782,109 +836,117 @@ describe('cli', function () { fs: { '.gitignore': '', 'package-lock.json': JSON.stringify({ version: '1.0.0' }), - 'manifest.json': fs.readFileSync('./test/mocks/manifest-6.3.1.json') + 'manifest.json': fs.readFileSync('./test/mocks/manifest-6.3.1.json'), }, - tags: ['v1.0.0'] - }) + tags: ['v1.0.0'], + }); await exec({ silent: true, packageFiles: [ { filename: 'manifest.json', - type: 'json' - } - ] - }) + type: 'json', + }, + ], + }); - JSON.parse(fs.readFileSync('manifest.json', 'utf-8')).version.should.equal( - '6.4.0' - ) + JSON.parse( + fs.readFileSync('manifest.json', 'utf-8'), + ).version.should.equal('6.4.0'); JSON.parse(fs.readFileSync('package.json', 'utf-8')).version.should.equal( - '6.4.0' - ) + '6.4.0', + ); JSON.parse( - fs.readFileSync('package-lock.json', 'utf-8') - ).version.should.equal('6.4.0') - }) + fs.readFileSync('package-lock.json', 'utf-8'), + ).version.should.equal('6.4.0'); + }); it('bumps version in Gradle `build.gradle.kts` file', async function () { - const expected = fs.readFileSync('./test/mocks/build-6.4.0.gradle.kts', 'utf-8') + const expected = fs.readFileSync( + './test/mocks/build-6.4.0.gradle.kts', + 'utf-8', + ); mock({ bump: 'minor', fs: { - 'build.gradle.kts': fs.readFileSync('./test/mocks/build-6.3.1.gradle.kts') - } - }) + 'build.gradle.kts': fs.readFileSync( + './test/mocks/build-6.3.1.gradle.kts', + ), + }, + }); await exec({ packageFiles: [{ filename: 'build.gradle.kts', type: 'gradle' }], - bumpFiles: [{ filename: 'build.gradle.kts', type: 'gradle' }] - }) - fs.readFileSync('build.gradle.kts', 'utf-8').should.equal(expected) - }) + bumpFiles: [{ filename: 'build.gradle.kts', type: 'gradle' }], + }); + fs.readFileSync('build.gradle.kts', 'utf-8').should.equal(expected); + }); it('bumps version in .NET `Project.csproj` file', async function () { - const expected = fs.readFileSync('./test/mocks/Project-6.4.0.csproj', 'utf-8') - const filename = 'Project.csproj' + const expected = fs.readFileSync( + './test/mocks/Project-6.4.0.csproj', + 'utf-8', + ); + const filename = 'Project.csproj'; mock({ bump: 'minor', fs: { - [filename]: fs.readFileSync('./test/mocks/Project-6.3.1.csproj') - } - }) + [filename]: fs.readFileSync('./test/mocks/Project-6.3.1.csproj'), + }, + }); await exec({ packageFiles: [{ filename, type: 'csproj' }], - bumpFiles: [{ filename, type: 'csproj' }] - }) - fs.readFileSync(filename, 'utf-8').should.equal(expected) - }) + bumpFiles: [{ filename, type: 'csproj' }], + }); + fs.readFileSync(filename, 'utf-8').should.equal(expected); + }); it('bumps version # in npm-shrinkwrap.json', async function () { mock({ bump: 'minor', fs: { - 'npm-shrinkwrap.json': JSON.stringify({ version: '1.0.0' }) + 'npm-shrinkwrap.json': JSON.stringify({ version: '1.0.0' }), }, - tags: ['v1.0.0'] - }) - await exec() + tags: ['v1.0.0'], + }); + await exec(); JSON.parse( - fs.readFileSync('npm-shrinkwrap.json', 'utf-8') - ).version.should.equal('1.1.0') - getPackageVersion().should.equal('1.1.0') - }) + fs.readFileSync('npm-shrinkwrap.json', 'utf-8'), + ).version.should.equal('1.1.0'); + getPackageVersion().should.equal('1.1.0'); + }); it('bumps version # in package-lock.json', async function () { mock({ bump: 'minor', fs: { '.gitignore': '', - 'package-lock.json': JSON.stringify({ version: '1.0.0' }) + 'package-lock.json': JSON.stringify({ version: '1.0.0' }), }, - tags: ['v1.0.0'] - }) - await exec() + tags: ['v1.0.0'], + }); + await exec(); JSON.parse( - fs.readFileSync('package-lock.json', 'utf-8') - ).version.should.equal('1.1.0') - getPackageVersion().should.equal('1.1.0') - }) + fs.readFileSync('package-lock.json', 'utf-8'), + ).version.should.equal('1.1.0'); + getPackageVersion().should.equal('1.1.0'); + }); describe('skip', function () { it('allows bump and changelog generation to be skipped', async function () { - const changelogContent = 'legacy header format\n' + const changelogContent = 'legacy header format\n'; mock({ bump: 'minor', changelog: 'foo\n', - fs: { 'CHANGELOG.md': changelogContent } - }) + fs: { 'CHANGELOG.md': changelogContent }, + }); - await exec('--skip.bump true --skip.changelog true') - getPackageVersion().should.equal('1.0.0') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.equal(changelogContent) - }) - }) + await exec('--skip.bump true --skip.changelog true'); + getPackageVersion().should.equal('1.0.0'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.equal(changelogContent); + }); + }); it('does not update files present in .gitignore', async function () { mock({ @@ -897,40 +959,40 @@ describe('cli', function () { 'package-lock.json': JSON.stringify({ name: '@org/package', version: '1.0.0', - lockfileVersion: 1 - }) + lockfileVersion: 1, + }), }, - tags: ['v1.0.0'] - }) - await exec() + tags: ['v1.0.0'], + }); + await exec(); JSON.parse( - fs.readFileSync('package-lock.json', 'utf-8') - ).version.should.equal('1.0.0') + fs.readFileSync('package-lock.json', 'utf-8'), + ).version.should.equal('1.0.0'); JSON.parse(fs.readFileSync('bower.json', 'utf-8')).version.should.equal( - '1.0.0' - ) - getPackageVersion().should.equal('1.1.0') - }) + '1.0.0', + ); + getPackageVersion().should.equal('1.1.0'); + }); describe('configuration', function () { it('--header', async function () { - mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }) - await exec('--header="# Welcome to our CHANGELOG.md"') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/# Welcome to our CHANGELOG.md/) - }) + mock({ bump: 'minor', fs: { 'CHANGELOG.md': '' } }); + await exec('--header="# Welcome to our CHANGELOG.md"'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/# Welcome to our CHANGELOG.md/); + }); it('--issuePrefixes and --issueUrlFormat', async function () { - const format = 'http://www.foo.com/{{prefix}}{{id}}' - const prefix = 'ABC-' + const format = 'http://www.foo.com/{{prefix}}{{id}}'; + const prefix = 'ABC-'; const changelog = ({ preset }) => - preset.issueUrlFormat + ':' + preset.issuePrefixes - mock({ bump: 'minor', changelog }) - await exec(`--issuePrefixes="${prefix}" --issueUrlFormat="${format}"`) - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.include(`${format}:${prefix}`) - }) - }) + preset.issueUrlFormat + ':' + preset.issuePrefixes; + mock({ bump: 'minor', changelog }); + await exec(`--issuePrefixes="${prefix}" --issueUrlFormat="${format}"`); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.include(`${format}:${prefix}`); + }); + }); describe('pre-major', function () { it('bumps the minor rather than major, if version < 1.0.0', async function () { @@ -938,44 +1000,44 @@ describe('cli', function () { bump: 'minor', pkg: { version: '0.5.0', - repository: { url: 'https://github.com/yargs/yargs.git' } - } - }) - await exec() - getPackageVersion().should.equal('0.6.0') - }) + repository: { url: 'https://github.com/yargs/yargs.git' }, + }, + }); + await exec(); + getPackageVersion().should.equal('0.6.0'); + }); it('bumps major if --release-as=major specified, if version < 1.0.0', async function () { mock({ bump: 'major', pkg: { version: '0.5.0', - repository: { url: 'https://github.com/yargs/yargs.git' } - } - }) - await exec('-r major') - getPackageVersion().should.equal('1.0.0') - }) - }) - }) + repository: { url: 'https://github.com/yargs/yargs.git' }, + }, + }); + await exec('-r major'); + getPackageVersion().should.equal('1.0.0'); + }); + }); + }); describe('GHSL-2020-111', function () { - afterEach(unmock) + afterEach(unmock); it('does not allow command injection via basic configuration', async function () { - mock({ bump: 'patch' }) + mock({ bump: 'patch' }); await exec({ noVerify: true, infile: 'foo.txt', - releaseCommitMessageFormat: 'bla `touch exploit`' - }) - const stat = shell.test('-f', './exploit') - stat.should.equal(false) - }) - }) + releaseCommitMessageFormat: 'bla `touch exploit`', + }); + const stat = shell.test('-f', './exploit'); + stat.should.equal(false); + }); + }); describe('with mocked git', function () { - afterEach(unmock) + afterEach(unmock); it('--sign signs the commit and tag', async function () { const gitArgs = [ @@ -986,101 +1048,119 @@ describe('cli', function () { 'CHANGELOG.md', 'package.json', '-m', - 'chore(release): 1.0.1' + 'chore(release): 1.0.1', ], ['tag', '-s', 'v1.0.1', '-m', 'chore(release): 1.0.1'], - ['rev-parse', '--abbrev-ref', 'HEAD'] - ] + ['rev-parse', '--abbrev-ref', 'HEAD'], + ]; const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'rev-parse') return Promise.resolve('master') - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - await exec('--sign', true) - gitArgs.should.have.lengthOf(0) - }) + cmd.should.equal('git'); + const expected = gitArgs.shift(); + cmdArgs.should.deep.equal(expected); + if (expected[0] === 'rev-parse') return Promise.resolve('master'); + return Promise.resolve(''); + }; + mock({ bump: 'patch', changelog: 'foo\n', execFile }); + + await exec('--sign', true); + gitArgs.should.have.lengthOf(0); + }); it('--tag-force forces tag replacement', async function () { const gitArgs = [ ['add', 'CHANGELOG.md', 'package.json'], - ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'], + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + '-m', + 'chore(release): 1.0.1', + ], ['tag', '-a', '-f', 'v1.0.1', '-m', 'chore(release): 1.0.1'], - ['rev-parse', '--abbrev-ref', 'HEAD'] - ] + ['rev-parse', '--abbrev-ref', 'HEAD'], + ]; const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) - if (expected[0] === 'rev-parse') return Promise.resolve('master') - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - await exec('--tag-force', true) - gitArgs.should.have.lengthOf(0) - }) + cmd.should.equal('git'); + const expected = gitArgs.shift(); + cmdArgs.should.deep.equal(expected); + if (expected[0] === 'rev-parse') return Promise.resolve('master'); + return Promise.resolve(''); + }; + mock({ bump: 'patch', changelog: 'foo\n', execFile }); + + await exec('--tag-force', true); + gitArgs.should.have.lengthOf(0); + }); it('fails if git add fails', async function () { - const gitArgs = [['add', 'CHANGELOG.md', 'package.json']] - const gitError = new Error('Command failed: git\nfailed add') + const gitArgs = [['add', 'CHANGELOG.md', 'package.json']]; + const gitError = new Error('Command failed: git\nfailed add'); const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) + cmd.should.equal('git'); + const expected = gitArgs.shift(); + cmdArgs.should.deep.equal(expected); if (expected[0] === 'add') { - return Promise.reject(gitError) + return Promise.reject(gitError); } - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) + return Promise.resolve(''); + }; + mock({ bump: 'patch', changelog: 'foo\n', execFile }); - await expect(exec({}, true)).to.be.rejectedWith(gitError) - }) + await expect(exec({}, true)).to.be.rejectedWith(gitError); + }); it('fails if git commit fails', async function () { const gitArgs = [ ['add', 'CHANGELOG.md', 'package.json'], - ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'] - ] - const gitError = new Error('Command failed: git\nfailed commit') + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + '-m', + 'chore(release): 1.0.1', + ], + ]; + const gitError = new Error('Command failed: git\nfailed commit'); const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) + cmd.should.equal('git'); + const expected = gitArgs.shift(); + cmdArgs.should.deep.equal(expected); if (expected[0] === 'commit') { - return Promise.reject(gitError) + return Promise.reject(gitError); } - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) + return Promise.resolve(''); + }; + mock({ bump: 'patch', changelog: 'foo\n', execFile }); - await expect(exec({}, true)).to.be.rejectedWith(gitError) - }) + await expect(exec({}, true)).to.be.rejectedWith(gitError); + }); it('fails if git tag fails', async function () { const gitArgs = [ ['add', 'CHANGELOG.md', 'package.json'], - ['commit', 'CHANGELOG.md', 'package.json', '-m', 'chore(release): 1.0.1'], - ['tag', '-a', 'v1.0.1', '-m', 'chore(release): 1.0.1'] - ] - const gitError = new Error('Command failed: git\nfailed tag') + [ + 'commit', + 'CHANGELOG.md', + 'package.json', + '-m', + 'chore(release): 1.0.1', + ], + ['tag', '-a', 'v1.0.1', '-m', 'chore(release): 1.0.1'], + ]; + const gitError = new Error('Command failed: git\nfailed tag'); const execFile = (_args, cmd, cmdArgs) => { - cmd.should.equal('git') - const expected = gitArgs.shift() - cmdArgs.should.deep.equal(expected) + cmd.should.equal('git'); + const expected = gitArgs.shift(); + cmdArgs.should.deep.equal(expected); if (expected[0] === 'tag') { - return Promise.reject(gitError) + return Promise.reject(gitError); } - return Promise.resolve('') - } - mock({ bump: 'patch', changelog: 'foo\n', execFile }) - - await expect(exec({}, true)).to.be.rejectedWith(gitError) - }) - }) -}) + return Promise.resolve(''); + }; + mock({ bump: 'patch', changelog: 'foo\n', execFile }); + + await expect(exec({}, true)).to.be.rejectedWith(gitError); + }); + }); +}); diff --git a/test/git.spec.js b/test/git.spec.js index ed399045f..197d0bc1c 100644 --- a/test/git.spec.js +++ b/test/git.spec.js @@ -1,40 +1,38 @@ -/* global describe it beforeEach afterEach */ +'use strict'; -'use strict' +const shell = require('shelljs'); +const fs = require('fs'); +const { Readable } = require('stream'); +const mockery = require('mockery'); +const stdMocks = require('std-mocks'); -const shell = require('shelljs') -const fs = require('fs') -const { Readable } = require('stream') -const mockery = require('mockery') -const stdMocks = require('std-mocks') +const chai = require('chai'); +const expect = chai.expect; +chai.use(require('chai-as-promised')); -const chai = require('chai') -const expect = chai.expect -chai.use(require('chai-as-promised')) - -function exec (opt = '') { +function exec(opt = '') { if (typeof opt === 'string') { - const cli = require('../command') - opt = cli.parse(`commit-and-tag-version ${opt}`) + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt}`); } - return require('../index')(opt) + return require('../index')(opt); } -function writePackageJson (version, option) { - const pkg = Object.assign({}, option, { version }) - fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8') +function writePackageJson(version, option) { + const pkg = Object.assign({}, option, { version }); + fs.writeFileSync('package.json', JSON.stringify(pkg), 'utf-8'); } -function writeHook (hookName, causeError, script) { - shell.mkdir('-p', 'scripts') - let content = script || 'console.error("' + hookName + ' ran")' - content += causeError ? '\nthrow new Error("' + hookName + '-failure")' : '' - fs.writeFileSync('scripts/' + hookName + '.js', content, 'utf-8') - fs.chmodSync('scripts/' + hookName + '.js', '755') +function writeHook(hookName, causeError, script) { + shell.mkdir('-p', 'scripts'); + let content = script || 'console.error("' + hookName + ' ran")'; + content += causeError ? '\nthrow new Error("' + hookName + '-failure")' : ''; + fs.writeFileSync('scripts/' + hookName + '.js', content, 'utf-8'); + fs.chmodSync('scripts/' + hookName + '.js', '755'); } -function getPackageVersion () { - return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version +function getPackageVersion() { + return JSON.parse(fs.readFileSync('package.json', 'utf-8')).version; } /** @@ -44,222 +42,225 @@ function getPackageVersion () { * changelog?: string | Error | Array string | null> * tags?: string[] | Error */ -function mock ({ bump, changelog, tags }) { - if (bump === undefined) throw new Error('bump must be defined for mock()') - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - - mockery.registerMock('conventional-recommended-bump', function (opt, parserOpts, cb) { - if (typeof bump === 'function') bump(opt, parserOpts, cb) - else if (bump instanceof Error) cb(bump) - else cb(null, { releaseType: bump }) - }) +function mock({ bump, changelog, tags }) { + if (bump === undefined) throw new Error('bump must be defined for mock()'); + mockery.enable({ warnOnUnregistered: false, useCleanCache: true }); - if (!Array.isArray(changelog)) changelog = [changelog] + mockery.registerMock( + 'conventional-recommended-bump', + function (opt, parserOpts, cb) { + if (typeof bump === 'function') bump(opt, parserOpts, cb); + else if (bump instanceof Error) cb(bump); + else cb(null, { releaseType: bump }); + }, + ); + + if (!Array.isArray(changelog)) changelog = [changelog]; mockery.registerMock( 'conventional-changelog', (opt) => new Readable({ - read (_size) { - const next = changelog.shift() + read(_size) { + const next = changelog.shift(); if (next instanceof Error) { - this.destroy(next) + this.destroy(next); } else if (typeof next === 'function') { - this.push(next(opt)) + this.push(next(opt)); } else { - this.push(next ? Buffer.from(next, 'utf8') : null) + this.push(next ? Buffer.from(next, 'utf8') : null); } - } - }) - ) + }, + }), + ); mockery.registerMock('git-semver-tags', function (_, cb) { - if (tags instanceof Error) cb(tags) - else cb(null, tags || []) - }) + if (tags instanceof Error) cb(tags); + else cb(null, tags || []); + }); - stdMocks.use() - return () => stdMocks.flush() + stdMocks.use(); + return () => stdMocks.flush(); } describe('git', function () { beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') - shell.exec('git init') - shell.exec('git config commit.gpgSign false') - shell.exec('git config core.autocrlf false') - shell.exec('git commit --allow-empty -m"root-commit"') - writePackageJson('1.0.0') - }) + shell.rm('-rf', 'tmp'); + shell.config.silent = true; + shell.mkdir('tmp'); + shell.cd('tmp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m"root-commit"'); + writePackageJson('1.0.0'); + }); afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') + shell.cd('../'); + shell.rm('-rf', 'tmp'); - mockery.deregisterAll() - mockery.disable() - stdMocks.restore() + mockery.deregisterAll(); + mockery.disable(); + stdMocks.restore(); // push out prints from the Mocha reporter - const { stdout } = stdMocks.flush() + const { stdout } = stdMocks.flush(); for (const str of stdout) { - if (str.startsWith(' ')) process.stdout.write(str) + if (str.startsWith(' ')) process.stdout.write(str); } - }) + }); describe('tagPrefix', function () { // TODO: Use unmocked git-semver-tags and stage a git environment it('will add prefix onto tag based on version from package', async function () { - writePackageJson('1.2.0') - mock({ bump: 'minor', tags: ['p-v1.2.0'] }) - await exec('--tag-prefix p-v') - shell.exec('git tag').stdout.should.match(/p-v1\.3\.0/) - }) + writePackageJson('1.2.0'); + mock({ bump: 'minor', tags: ['p-v1.2.0'] }); + await exec('--tag-prefix p-v'); + shell.exec('git tag').stdout.should.match(/p-v1\.3\.0/); + }); it('will add prefix onto tag via when gitTagFallback is true and no package [cli]', async function () { - shell.rm('package.json') + shell.rm('package.json'); mock({ bump: 'minor', - tags: ['android/production/v1.2.0', 'android/production/v1.0.0'] - }) - await exec('--tag-prefix android/production/v') + tags: ['android/production/v1.2.0', 'android/production/v1.0.0'], + }); + await exec('--tag-prefix android/production/v'); shell .exec('git tag') - .stdout.should.match(/android\/production\/v1\.3\.0/) - }) + .stdout.should.match(/android\/production\/v1\.3\.0/); + }); it('will add prefix onto tag via when gitTagFallback is true and no package [options]', async function () { mock({ bump: 'minor', - tags: ['android/production/v1.2.0', 'android/production/v1.0.0'] - }) - await exec({ tagPrefix: 'android/production/v', packageFiles: [] }) + tags: ['android/production/v1.2.0', 'android/production/v1.0.0'], + }); + await exec({ tagPrefix: 'android/production/v', packageFiles: [] }); shell .exec('git tag') - .stdout.should.match(/android\/production\/v1\.3\.0/) - }) - }) + .stdout.should.match(/android\/production\/v1\.3\.0/); + }); + }); it('formats the commit and tag messages appropriately', async function () { - mock({ bump: 'minor', tags: ['v1.0.0'] }) - await exec({}) + mock({ bump: 'minor', tags: ['v1.0.0'] }); + await exec({}); // check last commit message shell .exec('git log --oneline -n1') - .stdout.should.match(/chore\(release\): 1\.1\.0/) + .stdout.should.match(/chore\(release\): 1\.1\.0/); // check annotated tag message shell .exec('git tag -l -n1 v1.1.0') - .stdout.should.match(/chore\(release\): 1\.1\.0/) - }) + .stdout.should.match(/chore\(release\): 1\.1\.0/); + }); it('formats the tag if --first-release is true', async function () { - writePackageJson('1.0.1') - mock({ bump: 'minor' }) - await exec('--first-release') - shell.exec('git tag').stdout.should.match(/1\.0\.1/) - }) + writePackageJson('1.0.1'); + mock({ bump: 'minor' }); + await exec('--first-release'); + shell.exec('git tag').stdout.should.match(/1\.0\.1/); + }); it('commits all staged files', async function () { fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) - fs.writeFileSync('STUFF.md', 'stuff\n', 'utf-8') - shell.exec('git add STUFF.md') - - mock({ bump: 'patch', changelog: 'release 1.0.1\n', tags: ['v1.0.0'] }) - await exec('--commit-all') - const status = shell.exec('git status --porcelain') // see http://unix.stackexchange.com/questions/155046/determine-if-git-working-directory-is-clean-from-a-script - status.should.equal('') - status.should.not.match(/STUFF.md/) - - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/1\.0\.1/) - content.should.not.match(/legacy header format/) - }) + 'utf-8', + ); + fs.writeFileSync('STUFF.md', 'stuff\n', 'utf-8'); + shell.exec('git add STUFF.md'); + + mock({ bump: 'patch', changelog: 'release 1.0.1\n', tags: ['v1.0.0'] }); + await exec('--commit-all'); + const status = shell.exec('git status --porcelain'); // see http://unix.stackexchange.com/questions/155046/determine-if-git-working-directory-is-clean-from-a-script + status.should.equal(''); + status.should.not.match(/STUFF.md/); + + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/1\.0\.1/); + content.should.not.match(/legacy header format/); + }); it('does not run git hooks if the --no-verify flag is passed', async function () { fs.writeFileSync( '.git/hooks/pre-commit', '#!/bin/sh\necho "precommit ran"\nexit 1', - 'utf-8' - ) - fs.chmodSync('.git/hooks/pre-commit', '755') + 'utf-8', + ); + fs.chmodSync('.git/hooks/pre-commit', '755'); - mock({ bump: 'minor' }) - await exec('--no-verify') - await exec('-n') - }) + mock({ bump: 'minor' }); + await exec('--no-verify'); + await exec('-n'); + }); it('replaces tags if version not bumped', async function () { - mock({ bump: 'minor', tags: ['v1.0.0'] }) - await exec({}) - shell.exec('git describe').stdout.should.match(/v1\.1\.0/) - await exec('--tag-force --skip.bump') - shell.exec('git describe').stdout.should.match(/v1\.1\.0/) - }) + mock({ bump: 'minor', tags: ['v1.0.0'] }); + await exec({}); + shell.exec('git describe').stdout.should.match(/v1\.1\.0/); + await exec('--tag-force --skip.bump'); + shell.exec('git describe').stdout.should.match(/v1\.1\.0/); + }); it('allows the commit phase to be skipped', async function () { - const changelogContent = 'legacy header format\n' - writePackageJson('1.0.0') - fs.writeFileSync('CHANGELOG.md', changelogContent, 'utf-8') - - mock({ bump: 'minor', changelog: 'new feature\n' }) - await exec('--skip.commit true') - getPackageVersion().should.equal('1.1.0') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.match(/new feature/) - shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/) - }) + const changelogContent = 'legacy header format\n'; + writePackageJson('1.0.0'); + fs.writeFileSync('CHANGELOG.md', changelogContent, 'utf-8'); + + mock({ bump: 'minor', changelog: 'new feature\n' }); + await exec('--skip.commit true'); + getPackageVersion().should.equal('1.1.0'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.match(/new feature/); + shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/); + }); it('dry-run skips all non-idempotent steps', async function () { - shell.exec('git tag -a v1.0.0 -m "my awesome first release"') + shell.exec('git tag -a v1.0.0 -m "my awesome first release"'); const flush = mock({ bump: 'minor', changelog: '### Features\n', - tags: ['v1.0.0'] - }) - await exec('--dry-run') - const { stdout } = flush() - stdout.join('').should.match(/### Features/) - shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/) - shell.exec('git tag').stdout.should.match(/1\.0\.0/) - getPackageVersion().should.equal('1.0.0') - }) + tags: ['v1.0.0'], + }); + await exec('--dry-run'); + const { stdout } = flush(); + stdout.join('').should.match(/### Features/); + shell.exec('git log --oneline -n1').stdout.should.match(/root-commit/); + shell.exec('git tag').stdout.should.match(/1\.0\.0/); + getPackageVersion().should.equal('1.0.0'); + }); it('works fine without specifying a tag id when prereleasing', async function () { - writePackageJson('1.0.0') + writePackageJson('1.0.0'); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) - mock({ bump: 'minor' }) - await exec('--prerelease') - getPackageVersion().should.equal('1.1.0-0') - }) + 'utf-8', + ); + mock({ bump: 'minor' }); + await exec('--prerelease'); + getPackageVersion().should.equal('1.1.0-0'); + }); describe('gitTagFallback', function () { it('defaults to 1.0.0 if no tags in git history', async function () { - shell.rm('package.json') - mock({ bump: 'minor' }) - await exec({}) - const output = shell.exec('git tag') - output.stdout.should.include('v1.1.0') - }) + shell.rm('package.json'); + mock({ bump: 'minor' }); + await exec({}); + const output = shell.exec('git tag'); + output.stdout.should.include('v1.1.0'); + }); it('bases version on greatest version tag, if tags are found', async function () { - shell.rm('package.json') - mock({ bump: 'minor', tags: ['v3.9.0', 'v5.0.0', 'v3.0.0'] }) - await exec({}) - const output = shell.exec('git tag') - output.stdout.should.include('v5.1.0') - }) - }) + shell.rm('package.json'); + mock({ bump: 'minor', tags: ['v3.9.0', 'v5.0.0', 'v3.0.0'] }); + await exec({}); + const output = shell.exec('git tag'); + output.stdout.should.include('v5.1.0'); + }); + }); describe('configuration', function () { it('.versionrc : releaseCommitMessageFormat', async function () { @@ -267,214 +268,214 @@ describe('git', function () { '.versionrc', JSON.stringify({ releaseCommitMessageFormat: - 'This commit represents release: {{currentTag}}' + 'This commit represents release: {{currentTag}}', }), - 'utf-8' - ) - mock({ bump: 'minor' }) - await exec('') + 'utf-8', + ); + mock({ bump: 'minor' }); + await exec(''); shell .exec('git log --oneline -n1') - .should.include('This commit represents release: 1.1.0') - }) + .should.include('This commit represents release: 1.1.0'); + }); it('--releaseCommitMessageFormat', async function () { - mock({ bump: 'minor' }) + mock({ bump: 'minor' }); await exec( - '--releaseCommitMessageFormat="{{currentTag}} is the version."' - ) + '--releaseCommitMessageFormat="{{currentTag}} is the version."', + ); shell .exec('git log --oneline -n1') - .should.include('1.1.0 is the version.') - }) + .should.include('1.1.0 is the version.'); + }); it('[LEGACY] supports --message (and single %s replacement)', async function () { - mock({ bump: 'minor' }) - await exec('--message="V:%s"') - shell.exec('git log --oneline -n1').should.include('V:1.1.0') - }) + mock({ bump: 'minor' }); + await exec('--message="V:%s"'); + shell.exec('git log --oneline -n1').should.include('V:1.1.0'); + }); it('[LEGACY] supports -m (and multiple %s replacements)', async function () { - mock({ bump: 'minor' }) - await exec('--message="V:%s is the %s."') + mock({ bump: 'minor' }); + await exec('--message="V:%s is the %s."'); shell .exec('git log --oneline -n1') - .should.include('V:1.1.0 is the 1.1.0.') - }) - }) + .should.include('V:1.1.0 is the 1.1.0.'); + }); + }); describe('precommit hook', function () { it('should run the precommit hook when provided via .versionrc.json (#371)', async function () { fs.writeFileSync( '.versionrc.json', JSON.stringify({ - scripts: { precommit: 'node scripts/precommit' } + scripts: { precommit: 'node scripts/precommit' }, }), - 'utf-8' - ) + 'utf-8', + ); - writeHook('precommit') + writeHook('precommit'); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) - const flush = mock({ bump: 'minor' }) - await exec('') - const { stderr } = flush() - stderr[0].should.match(/precommit ran/) - }) + 'utf-8', + ); + const flush = mock({ bump: 'minor' }); + await exec(''); + const { stderr } = flush(); + stderr[0].should.match(/precommit ran/); + }); it('should run the precommit hook when provided', async function () { writePackageJson('1.0.0', { 'commit-and-tag-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) - writeHook('precommit') + scripts: { precommit: 'node scripts/precommit' }, + }, + }); + writeHook('precommit'); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) + 'utf-8', + ); - const flush = mock({ bump: 'minor' }) - await exec('--patch') - const { stderr } = flush() - stderr[0].should.match(/precommit ran/) - }) + const flush = mock({ bump: 'minor' }); + await exec('--patch'); + const { stderr } = flush(); + stderr[0].should.match(/precommit ran/); + }); it('should run the precommit hook and exit with error when precommit fails', async function () { writePackageJson('1.0.0', { 'commit-and-tag-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) - writeHook('precommit', true) + scripts: { precommit: 'node scripts/precommit' }, + }, + }); + writeHook('precommit', true); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) + 'utf-8', + ); - mock({ bump: 'minor' }) - await expect(exec('--patch')).to.be.rejectedWith(/precommit-failure/) - }) + mock({ bump: 'minor' }); + await expect(exec('--patch')).to.be.rejectedWith(/precommit-failure/); + }); it('should allow an alternate commit message to be provided by precommit script', async function () { writePackageJson('1.0.0', { 'commit-and-tag-version': { - scripts: { precommit: 'node scripts/precommit' } - } - }) + scripts: { precommit: 'node scripts/precommit' }, + }, + }); writeHook( 'precommit', false, - 'console.log("releasing %s delivers #222")' - ) + 'console.log("releasing %s delivers #222")', + ); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) + 'utf-8', + ); - mock({ bump: 'minor' }) - await exec('--patch') - shell.exec('git log --oneline -n1').should.match(/delivers #222/) - }) - }) + mock({ bump: 'minor' }); + await exec('--patch'); + shell.exec('git log --oneline -n1').should.match(/delivers #222/); + }); + }); describe('Run ... to publish', function () { it('does normally display `npm publish`', async function () { - const flush = mock({ bump: 'patch' }) - await exec('') + const flush = mock({ bump: 'patch' }); + await exec(''); flush() .stdout.join('') - .should.match(/npm publish/) - }) + .should.match(/npm publish/); + }); it('can display publish hints with custom npm client name', async function () { - const flush = mock({ bump: 'patch' }) - await exec('--npmPublishHint "yarn publish"') + const flush = mock({ bump: 'patch' }); + await exec('--npmPublishHint "yarn publish"'); flush() .stdout.join('') - .should.match(/yarn publish/) - }) + .should.match(/yarn publish/); + }); it('does not display `npm publish` if the package is private', async function () { - writePackageJson('1.0.0', { private: true }) - const flush = mock({ bump: 'patch' }) - await exec('') + writePackageJson('1.0.0', { private: true }); + const flush = mock({ bump: 'patch' }); + await exec(''); flush() .stdout.join('') - .should.not.match(/npm publish/) - }) + .should.not.match(/npm publish/); + }); it('does not display `npm publish` if there is no package.json', async function () { - shell.rm('package.json') - const flush = mock({ bump: 'patch' }) - await exec('') + shell.rm('package.json'); + const flush = mock({ bump: 'patch' }); + await exec(''); flush() .stdout.join('') - .should.not.match(/npm publish/) - }) + .should.not.match(/npm publish/); + }); it('does not display `all staged files` without the --commit-all flag', async function () { - const flush = mock({ bump: 'patch' }) - await exec('') + const flush = mock({ bump: 'patch' }); + await exec(''); flush() .stdout.join('') - .should.not.match(/all staged files/) - }) + .should.not.match(/all staged files/); + }); it('does display `all staged files` if the --commit-all flag is passed', async function () { - const flush = mock({ bump: 'patch' }) - await exec('--commit-all') + const flush = mock({ bump: 'patch' }); + await exec('--commit-all'); flush() .stdout.join('') - .should.match(/all staged files/) - }) + .should.match(/all staged files/); + }); it('advises use of --tag prerelease for publishing to npm', async function () { - writePackageJson('1.0.0') + writePackageJson('1.0.0'); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) + 'utf-8', + ); - const flush = mock({ bump: 'patch' }) - await exec('--prerelease') - const { stdout } = flush() - stdout.join('').should.include('--tag prerelease') - }) + const flush = mock({ bump: 'patch' }); + await exec('--prerelease'); + const { stdout } = flush(); + stdout.join('').should.include('--tag prerelease'); + }); it('advises use of --tag alpha for publishing to npm when tagging alpha', async function () { - writePackageJson('1.0.0') + writePackageJson('1.0.0'); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) + 'utf-8', + ); - const flush = mock({ bump: 'patch' }) - await exec('--prerelease alpha') - const { stdout } = flush() - stdout.join('').should.include('--tag alpha') - }) + const flush = mock({ bump: 'patch' }); + await exec('--prerelease alpha'); + const { stdout } = flush(); + stdout.join('').should.include('--tag alpha'); + }); it('does not advise use of --tag prerelease for private modules', async function () { - writePackageJson('1.0.0', { private: true }) + writePackageJson('1.0.0', { private: true }); fs.writeFileSync( 'CHANGELOG.md', 'legacy header format\n', - 'utf-8' - ) - - const flush = mock({ bump: 'minor' }) - await exec('--prerelease') - const { stdout } = flush() - stdout.join('').should.not.include('--tag prerelease') - }) - }) -}) + 'utf-8', + ); + + const flush = mock({ bump: 'minor' }); + await exec('--prerelease'); + const { stdout } = flush(); + stdout.join('').should.not.include('--tag prerelease'); + }); + }); +}); diff --git a/test/mocks/updater/customer-updater.js b/test/mocks/updater/customer-updater.js index 989aba3c0..320204acb 100644 --- a/test/mocks/updater/customer-updater.js +++ b/test/mocks/updater/customer-updater.js @@ -1,12 +1,9 @@ -const REPLACER = /version: "(.*)"/ +const REPLACER = /version: "(.*)"/; module.exports.readVersion = function (contents) { - return REPLACER.exec(contents)[1] -} + return REPLACER.exec(contents)[1]; +}; module.exports.writeVersion = function (contents, version) { - return contents.replace( - REPLACER.exec(contents)[0], - `version: "${version}"` - ) -} + return contents.replace(REPLACER.exec(contents)[0], `version: "${version}"`); +}; diff --git a/test/mocks/updater/increment-updater.js b/test/mocks/updater/increment-updater.js index 823fa0195..86f274fa7 100644 --- a/test/mocks/updater/increment-updater.js +++ b/test/mocks/updater/increment-updater.js @@ -1,7 +1,7 @@ module.exports.readVersion = function (contents) { - return Number.parseInt(contents) -} + return Number.parseInt(contents); +}; -module.exports.writeVersion = function (contents, version) { - return this.readVersion(contents) + 1 -} +module.exports.writeVersion = function (contents) { + return this.readVersion(contents) + 1; +}; diff --git a/test/preset.spec.js b/test/preset.spec.js index a597e83dd..eb890046f 100644 --- a/test/preset.spec.js +++ b/test/preset.spec.js @@ -1,52 +1,50 @@ -/* global describe it beforeEach, afterEach */ +const shell = require('shelljs'); +const fs = require('fs'); -const shell = require('shelljs') -const fs = require('fs') +require('chai').should(); -require('chai').should() - -function exec (opt) { - const cli = require('../command') - opt = cli.parse(`commit-and-tag-version ${opt} --silent`) - opt.skip = { commit: true, tag: true } - return require('../index')(opt) +function exec(opt) { + const cli = require('../command'); + opt = cli.parse(`commit-and-tag-version ${opt} --silent`); + opt.skip = { commit: true, tag: true }; + return require('../index')(opt); } describe('presets', function () { beforeEach(function () { - shell.rm('-rf', 'tmp') - shell.config.silent = true - shell.mkdir('tmp') - shell.cd('tmp') - shell.exec('git init') - shell.exec('git config commit.gpgSign false') - shell.exec('git config core.autocrlf false') - shell.exec('git commit --allow-empty -m "initial commit"') - shell.exec('git commit --allow-empty -m "feat: A feature commit."') - shell.exec('git commit --allow-empty -m "perf: A performance change."') - shell.exec('git commit --allow-empty -m "chore: A chore commit."') - shell.exec('git commit --allow-empty -m "ci: A ci commit."') - shell.exec('git commit --allow-empty -m "custom: A custom commit."') - }) + shell.rm('-rf', 'tmp'); + shell.config.silent = true; + shell.mkdir('tmp'); + shell.cd('tmp'); + shell.exec('git init'); + shell.exec('git config commit.gpgSign false'); + shell.exec('git config core.autocrlf false'); + shell.exec('git commit --allow-empty -m "initial commit"'); + shell.exec('git commit --allow-empty -m "feat: A feature commit."'); + shell.exec('git commit --allow-empty -m "perf: A performance change."'); + shell.exec('git commit --allow-empty -m "chore: A chore commit."'); + shell.exec('git commit --allow-empty -m "ci: A ci commit."'); + shell.exec('git commit --allow-empty -m "custom: A custom commit."'); + }); afterEach(function () { - shell.cd('../') - shell.rm('-rf', 'tmp') - }) + shell.cd('../'); + shell.rm('-rf', 'tmp'); + }); it('Conventional Commits (default)', async function () { - await exec() - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.contain('### Features') - content.should.not.contain('### Performance Improvements') - content.should.not.contain('### Custom') - }) + await exec(); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.contain('### Features'); + content.should.not.contain('### Performance Improvements'); + content.should.not.contain('### Custom'); + }); it('Angular', async function () { - await exec('--preset angular') - const content = fs.readFileSync('CHANGELOG.md', 'utf-8') - content.should.contain('### Features') - content.should.contain('### Performance Improvements') - content.should.not.contain('### Custom') - }) -}) + await exec('--preset angular'); + const content = fs.readFileSync('CHANGELOG.md', 'utf-8'); + content.should.contain('### Features'); + content.should.contain('### Performance Improvements'); + content.should.not.contain('### Custom'); + }); +}); diff --git a/test/stringify-package.spec.js b/test/stringify-package.spec.js index 08a2dd908..1768e8049 100644 --- a/test/stringify-package.spec.js +++ b/test/stringify-package.spec.js @@ -1,39 +1,39 @@ -/* global describe it */ +'use strict'; -'use strict' +const stringifyPackage = require('../lib/stringify-package'); -const stringifyPackage = require('../lib/stringify-package') - -require('chai').should() +require('chai').should(); describe('stringifyPackage()', function () { - const dummy = { name: 'dummy' } + const dummy = { name: 'dummy' }; it('with no params uses \\n', function () { - stringifyPackage(dummy).should.match(/\n$/m) - }) + stringifyPackage(dummy).should.match(/\n$/m); + }); it('uses \\n', function () { - stringifyPackage(dummy, 2, '\n').should.match(/\n$/m) - }) + stringifyPackage(dummy, 2, '\n').should.match(/\n$/m); + }); it('uses \\r\\n', function () { - stringifyPackage(dummy, 2, '\r\n').should.match(/\r\n$/m) - }) + stringifyPackage(dummy, 2, '\r\n').should.match(/\r\n$/m); + }); it('with no params uses 2-space indent', function () { - stringifyPackage(dummy).should.match(/^ {2}"name": "dummy"/m) - }) + stringifyPackage(dummy).should.match(/^ {2}"name": "dummy"/m); + }); it('uses 2-space indent', function () { - stringifyPackage(dummy, 2, '\n').should.match(/^ {2}"name": "dummy"/m) - }) + stringifyPackage(dummy, 2, '\n').should.match(/^ {2}"name": "dummy"/m); + }); it('uses 4-space indent', function () { - stringifyPackage(dummy, 4, '\n').should.match(/^ {4}"name": "dummy"/m) - }) + stringifyPackage(dummy, 4, '\n').should.match(/^ {4}"name": "dummy"/m); + }); it('0 works', function () { - stringifyPackage(dummy, 0).split(/\r\n|\r|\n/).length.should.equal(2) - }) -}) + stringifyPackage(dummy, 0) + .split(/\r\n|\r|\n/) + .length.should.equal(2); + }); +}); diff --git a/test/utils.spec.js b/test/utils.spec.js index 661a4f591..4ee853e98 100644 --- a/test/utils.spec.js +++ b/test/utils.spec.js @@ -1,52 +1,50 @@ -/* global describe it */ +const mockery = require('mockery'); +const { promises: fsp } = require('fs'); +require('chai').should(); -const mockery = require('mockery') -const { promises: fsp } = require('fs') -require('chai').should() - -function mockNpm () { - mockery.enable({ warnOnUnregistered: false, useCleanCache: true }) - let lockFile = '' +function mockNpm() { + mockery.enable({ warnOnUnregistered: false, useCleanCache: true }); + let lockFile = ''; const fsMock = { promises: { access: async function (path) { if (lockFile && path.endsWith(lockFile)) { - return true + return true; } - await fsp.access(path) - } - } - } - mockery.registerMock('fs', fsMock) + await fsp.access(path); + }, + }, + }; + mockery.registerMock('fs', fsMock); return { - setLockFile (file) { - lockFile = file - } - } + setLockFile(file) { + lockFile = file; + }, + }; } describe('utils', function () { it('detectPMByLockFile should work', async function () { - const { setLockFile } = mockNpm() - const { detectPMByLockFile } = require('../lib/detect-package-manager') + const { setLockFile } = mockNpm(); + const { detectPMByLockFile } = require('../lib/detect-package-manager'); - let pm = await detectPMByLockFile() - pm.should.equal('npm') + let pm = await detectPMByLockFile(); + pm.should.equal('npm'); - setLockFile('yarn.lock') - pm = await detectPMByLockFile() - pm.should.equal('yarn') + setLockFile('yarn.lock'); + pm = await detectPMByLockFile(); + pm.should.equal('yarn'); - setLockFile('package-lock.json') - pm = await detectPMByLockFile() - pm.should.equal('npm') + setLockFile('package-lock.json'); + pm = await detectPMByLockFile(); + pm.should.equal('npm'); - setLockFile('pnpm-lock.yaml') - pm = await detectPMByLockFile() - pm.should.equal('pnpm') + setLockFile('pnpm-lock.yaml'); + pm = await detectPMByLockFile(); + pm.should.equal('pnpm'); - mockery.deregisterAll() - mockery.disable() - }) -}) + mockery.deregisterAll(); + mockery.disable(); + }); +});