From 8c9e24778db867cb3148bc247c7e321639aa9f58 Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 8 Apr 2021 10:08:23 -0700 Subject: [PATCH] feat(version): add workspace support PR-URL: https://github.com/npm/cli/pull/3055 Credit: @wraithgar Close: #3055 Reviewed-by: @darcyclarke --- docs/content/commands/npm-version.md | 83 +++++---- lib/base-command.js | 20 +- lib/utils/config/definitions.js | 1 + lib/version.js | 78 +++++++- lib/workspaces/get-workspaces.js | 3 + tap-snapshots/test/lib/dist-tag.js.test.cjs | 15 +- .../test/lib/utils/npm-usage.js.test.cjs | 47 +++-- test/lib/version.js | 172 ++++++++++++++++++ 8 files changed, 358 insertions(+), 61 deletions(-) diff --git a/docs/content/commands/npm-version.md b/docs/content/commands/npm-version.md index 0eb814b9899b0..cdf0f977f503d 100644 --- a/docs/content/commands/npm-version.md +++ b/docs/content/commands/npm-version.md @@ -14,6 +14,56 @@ npm version [ | major | minor | patch | premajor | preminor | prepat 'npm ls' to inspect current package/dependency versions ``` +### Configuration + +#### `allow-same-version` + +* Default: `false` +* Type: Boolean + +Prevents throwing an error when `npm version` is used to set the new version +to the same value as the current version. + +#### `git-tag-version` + +* Default: `true` +* Type: Boolean + +Commit and tag the version change. + +#### `commit-hooks` + +* Default: `true` +* Type: Boolean + +Run git commit hooks when committing the version change. + +#### `sign-git-tag` + +* Default: `false` +* Type: Boolean + +Pass the `-s` flag to git to sign the tag. + +Note that you must have a default GPG key set up in your git config for this to work properly. + +#### workspaces + +* Default: `false` +* Type: Boolean + +Enables workspaces context and includes workspaces in reported output +when getting versions. When setting a new version *only the workspaces +will be changed*. + +#### workspace + +* Default: [] +* Type: Array + +Enables workspaces context and limits results to only those specified by +this config item. + ### Description Run this in a package directory to bump the version and write the new @@ -87,39 +137,6 @@ This runs all your tests and proceeds only if they pass. Then runs your `build` adds everything in the `dist` directory to the commit. After the commit, it pushes the new commit and tag up to the server, and deletes the `build/temp` directory. -### Configuration - -#### `allow-same-version` - -* Default: `false` -* Type: Boolean - -Prevents throwing an error when `npm version` is used to set the new version -to the same value as the current version. - -#### `git-tag-version` - -* Default: `true` -* Type: Boolean - -Commit and tag the version change. - -#### `commit-hooks` - -* Default: `true` -* Type: Boolean - -Run git commit hooks when committing the version change. - -#### `sign-git-tag` - -* Default: `false` -* Type: Boolean - -Pass the `-s` flag to git to sign the tag. - -Note that you must have a default GPG key set up in your git config for this to work properly. - ### See Also * [npm init](/commands/npm-init) diff --git a/lib/base-command.js b/lib/base-command.js index 91c7c5357c9a0..322fd8963a203 100644 --- a/lib/base-command.js +++ b/lib/base-command.js @@ -4,6 +4,7 @@ const ConfigDefinitions = require('./utils/config/definitions.js') class BaseCommand { constructor (npm) { + this.wrapWidth = 80 this.npm = npm } @@ -27,8 +28,7 @@ class BaseCommand { usage = `${usage}${this.constructor.usage.map(u => `npm ${this.constructor.name} ${u}`).join('\n')}` if (this.constructor.params) - // TODO word wrap this along params boundaries - usage = `${usage}\n\nOptions:\n[${this.constructor.params.map(p => ConfigDefinitions[p].usage).join('] [')}]` + usage = `${usage}\n\nOptions:\n${this.wrappedParams}` // Mostly this just appends aliases, this could be more clear usage = usageUtil(this.constructor.name, usage) @@ -36,6 +36,22 @@ class BaseCommand { return usage } + get wrappedParams () { + let results = '' + let line = '' + + for (const param of this.constructor.params) { + const usage = `[${ConfigDefinitions[param].usage}]` + if (line.length && (line.length + usage.length) > this.wrapWidth) { + results = [results, line].filter(Boolean).join('\n') + line = '' + } + line = [line, usage].filter(Boolean).join(' ') + } + results = [results, line].filter(Boolean).join('\n') + return results + } + usageError (msg) { if (!msg) { return Object.assign(new Error(`\nUsage: ${this.usage}`), { diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index d87c43ddade50..db1f25e9517de 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -1389,6 +1389,7 @@ define('prefix', { define('preid', { default: '', + hint: 'prerelease-id', type: String, description: ` The "prerelease identifier" to use as a prefix for the "prerelease" part diff --git a/lib/version.js b/lib/version.js index 18b7d7d6c5d84..3ef3801e74e81 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,6 +1,11 @@ -const libversion = require('libnpmversion') +const libnpmversion = require('libnpmversion') +const { resolve } = require('path') +const { promisify } = require('util') +const readFile = promisify(require('fs').readFile) +const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') + class Version extends BaseCommand { static get description () { return 'Bump a package version' @@ -11,9 +16,23 @@ class Version extends BaseCommand { return 'version' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return [ + 'allow-same-version', + 'commit-hooks', + 'git-tag-version', + 'json', + 'preid', + 'sign-git-tag', + 'workspace', + 'workspaces', + ] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[ | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=] | from-git]'] + return ['[ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]'] } async completion (opts) { @@ -37,6 +56,10 @@ class Version extends BaseCommand { return this.version(args).then(() => cb()).catch(cb) } + execWorkspaces (args, filters, cb) { + this.versionWorkspaces(args, filters).then(() => cb()).catch(cb) + } + async version (args) { switch (args.length) { case 0: @@ -48,20 +71,42 @@ class Version extends BaseCommand { } } + async versionWorkspaces (args, filters) { + switch (args.length) { + case 0: + return this.listWorkspaces(filters) + case 1: + return this.changeWorkspaces(args, filters) + default: + throw this.usage + } + } + async change (args) { const prefix = this.npm.config.get('tag-version-prefix') - const version = await libversion(args[0], { + const version = await libnpmversion(args[0], { ...this.npm.flatOptions, path: this.npm.prefix, }) return this.npm.output(`${prefix}${version}`) } - async list () { - const results = {} - const { promisify } = require('util') - const { resolve } = require('path') - const readFile = promisify(require('fs').readFile) + async changeWorkspaces (args, filters) { + const prefix = this.npm.config.get('tag-version-prefix') + const workspaces = + await getWorkspaces(filters, { path: this.npm.localPrefix }) + for (const [name, path] of workspaces) { + this.npm.output(name) + const version = await libnpmversion(args[0], { + ...this.npm.flatOptions, + 'git-tag-version': false, + path, + }) + this.npm.output(`${prefix}${version}`) + } + } + + async list (results = {}) { const pj = resolve(this.npm.prefix, 'package.json') const pkg = await readFile(pj, 'utf8') @@ -80,5 +125,22 @@ class Version extends BaseCommand { else this.npm.output(results) } + + async listWorkspaces (filters) { + const results = {} + const workspaces = + await getWorkspaces(filters, { path: this.npm.localPrefix }) + for (const [, path] of workspaces) { + const pj = resolve(path, 'package.json') + // getWorkspaces has already parsed this so we know it won't error + const pkg = await readFile(pj, 'utf8') + .then(data => JSON.parse(data)) + + if (pkg.name && pkg.version) + results[pkg.name] = pkg.version + } + return this.list(results) + } } + module.exports = Version diff --git a/lib/workspaces/get-workspaces.js b/lib/workspaces/get-workspaces.js index 64812d5403576..91b0074556ae7 100644 --- a/lib/workspaces/get-workspaces.js +++ b/lib/workspaces/get-workspaces.js @@ -3,7 +3,10 @@ const mapWorkspaces = require('@npmcli/map-workspaces') const minimatch = require('minimatch') const rpj = require('read-package-json-fast') +// Returns an Map of paths to workspaces indexed by workspace name +// { foo => '/path/to/foo' } const getWorkspaces = async (filters, { path }) => { + // TODO we need a better error to be bubbled up here if this rpj call fails const pkg = await rpj(resolve(path, 'package.json')) const workspaces = await mapWorkspaces({ cwd: path, pkg }) const res = filters.length ? new Map() : workspaces diff --git a/tap-snapshots/test/lib/dist-tag.js.test.cjs b/tap-snapshots/test/lib/dist-tag.js.test.cjs index ea25b568b0662..86a9c84eb1eb6 100644 --- a/tap-snapshots/test/lib/dist-tag.js.test.cjs +++ b/tap-snapshots/test/lib/dist-tag.js.test.cjs @@ -16,7 +16,8 @@ npm dist-tag rm npm dist-tag ls [] Options: -[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] alias: dist-tags @@ -34,7 +35,8 @@ npm dist-tag rm npm dist-tag ls [] Options: -[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] alias: dist-tags @@ -61,7 +63,8 @@ npm dist-tag rm npm dist-tag ls [] Options: -[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] alias: dist-tags @@ -85,7 +88,8 @@ npm dist-tag rm npm dist-tag ls [] Options: -[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] alias: dist-tags @@ -139,7 +143,8 @@ npm dist-tag rm npm dist-tag ls [] Options: -[-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] alias: dist-tags diff --git a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs index 6bd38772ee368..e32d5e9f4928f 100644 --- a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs +++ b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs @@ -204,7 +204,8 @@ All commands: npm audit [fix] Options: - [--audit-level ] [--dry-run] [-f|--force] [--json] [--package-lock-only] [--production] + [--audit-level ] [--dry-run] [-f|--force] + [--json] [--package-lock-only] [--production] Run "npm help audit" for more info @@ -324,7 +325,8 @@ All commands: npm dist-tag ls [] Options: - [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] alias: dist-tags @@ -338,7 +340,9 @@ All commands: npm docs [ [ ...]] Options: - [--browser|--browser ] [--registry ] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [--browser|--browser ] [--registry ] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] alias: home @@ -373,7 +377,8 @@ All commands: npm exec --package=foo -c ' [args...]' Options: - [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] alias: x @@ -416,7 +421,8 @@ All commands: npm fund [[<@scope>/]] Options: - [--json] [--browser|--browser ] [--unicode] [--which ] + [--json] [--browser|--browser ] [--unicode] + [--which ] Run "npm help fund" for more info @@ -482,7 +488,8 @@ All commands: npm install / Options: - [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] + [-E|--save-exact] aliases: i, in, ins, inst, insta, instal, isnt, isnta, isntal, add @@ -516,7 +523,8 @@ All commands: npm install-test / Options: - [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] + [-E|--save-exact] alias: it @@ -625,7 +633,9 @@ All commands: npm pack [[<@scope>/]...] Options: - [--dry-run] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [--dry-run] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] Run "npm help pack" for more info @@ -705,7 +715,9 @@ All commands: npm repo [ [ ...]] Options: - [--browser|--browser ] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [--browser|--browser ] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] Run "npm help repo" for more info @@ -738,7 +750,8 @@ All commands: npm run-script [-- ] Options: - [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] aliases: run, rum, urn @@ -775,7 +788,8 @@ All commands: npm set-script [