From 881a0fbda314df4ebef0884a9edb90296427338c Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 10 May 2021 15:10:25 -0700 Subject: [PATCH] wip: Add workspaces functionality to reify commands --- docs/content/commands/npm-audit.md | 32 +++++++++++++++++++ docs/content/commands/npm-install.md | 32 +++++++++++++++++++ docs/content/commands/npm-prune.md | 32 +++++++++++++++++++ docs/content/commands/npm-uninstall.md | 32 +++++++++++++++++++ docs/content/commands/npm-update.md | 32 +++++++++++++++++++ lib/audit.js | 7 ++-- lib/base-command.js | 1 + lib/ci.js | 5 +-- lib/dedupe.js | 5 +-- lib/install.js | 6 ++-- lib/prune.js | 7 ++-- lib/rebuild.js | 4 +++ lib/uninstall.js | 8 ++--- lib/update.js | 6 ++-- lib/workspaces/arborist-cmd.js | 1 + .../test/lib/utils/npm-usage.js.test.cjs | 13 +++++++- test/lib/update.js | 16 ++++++++-- 17 files changed, 218 insertions(+), 21 deletions(-) diff --git a/docs/content/commands/npm-audit.md b/docs/content/commands/npm-audit.md index 27c4d972597a8..0771d897df90d 100644 --- a/docs/content/commands/npm-audit.md +++ b/docs/content/commands/npm-audit.md @@ -271,6 +271,38 @@ it will be included. If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment variable will be set to `'production'` for all lifecycle scripts. +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +This value is not exported to the environment for child processes. + ### See Also diff --git a/docs/content/commands/npm-install.md b/docs/content/commands/npm-install.md index 9e61cf7268bd6..e5091e6604c91 100644 --- a/docs/content/commands/npm-install.md +++ b/docs/content/commands/npm-install.md @@ -571,6 +571,38 @@ commands that modify your local installation, eg, `install`, `update`, Note: This is NOT honored by other network related commands, eg `dist-tags`, `owner`, etc. +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +This value is not exported to the environment for child processes. + ### Algorithm diff --git a/docs/content/commands/npm-prune.md b/docs/content/commands/npm-prune.md index 28890193295f2..ecb6bdcd6cb14 100644 --- a/docs/content/commands/npm-prune.md +++ b/docs/content/commands/npm-prune.md @@ -77,6 +77,38 @@ Whether or not to output JSON data, rather than the normal output. Not supported by all npm commands. +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +This value is not exported to the environment for child processes. + ### See Also diff --git a/docs/content/commands/npm-uninstall.md b/docs/content/commands/npm-uninstall.md index cffb9bdb55ffa..b6ba31393834d 100644 --- a/docs/content/commands/npm-uninstall.md +++ b/docs/content/commands/npm-uninstall.md @@ -68,6 +68,38 @@ Save installed packages to a package.json file as dependencies. When used with the `npm rm` command, removes the dependency from package.json. +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +This value is not exported to the environment for child processes. + ### See Also diff --git a/docs/content/commands/npm-update.md b/docs/content/commands/npm-update.md index 044b1a82dc492..4c9271c6633c3 100644 --- a/docs/content/commands/npm-update.md +++ b/docs/content/commands/npm-update.md @@ -258,6 +258,38 @@ commands that modify your local installation, eg, `install`, `update`, Note: This is NOT honored by other network related commands, eg `dist-tags`, `owner`, etc. +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +This value is not exported to the environment for child processes. + ### See Also diff --git a/lib/audit.js b/lib/audit.js index ea27b02001f76..a97fd9b30abd4 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -2,9 +2,9 @@ const Arborist = require('@npmcli/arborist') const auditReport = require('npm-audit-report') const reifyFinish = require('./utils/reify-finish.js') const auditError = require('./utils/audit-error.js') -const BaseCommand = require('./base-command.js') +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') -class Audit extends BaseCommand { +class Audit extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Run a security audit' @@ -24,6 +24,7 @@ class Audit extends BaseCommand { 'json', 'package-lock-only', 'omit', + ...super.params, ] } @@ -57,7 +58,9 @@ class Audit extends BaseCommand { audit: true, path: this.npm.prefix, reporter, + workspaces: this.workspaces, } + const arb = new Arborist(opts) const fix = args[0] === 'fix' await arb.audit({ fix }) diff --git a/lib/base-command.js b/lib/base-command.js index 322fd8963a203..e1efcff5832b3 100644 --- a/lib/base-command.js +++ b/lib/base-command.js @@ -6,6 +6,7 @@ class BaseCommand { constructor (npm) { this.wrapWidth = 80 this.npm = npm + this.workspaces = null } get name () { diff --git a/lib/ci.js b/lib/ci.js index d2a27059c4094..3a2a2b316a150 100644 --- a/lib/ci.js +++ b/lib/ci.js @@ -17,9 +17,9 @@ const removeNodeModules = async where => { await Promise.all(entries.map(f => rimraf(`${path}/${f}`, rimrafOpts))) process.emit('timeEnd', 'npm-ci:rm') } -const BaseCommand = require('./base-command.js') +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') -class CI extends BaseCommand { +class CI extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Install a project with a clean slate' @@ -55,6 +55,7 @@ class CI extends BaseCommand { path: where, log: this.npm.log, save: false, // npm ci should never modify the lockfile or package.json + workspaces: this.workspaces, } const arb = new Arborist(opts) diff --git a/lib/dedupe.js b/lib/dedupe.js index e58b6c55fd370..5a7d6cac8ab10 100644 --- a/lib/dedupe.js +++ b/lib/dedupe.js @@ -2,9 +2,9 @@ const Arborist = require('@npmcli/arborist') const reifyFinish = require('./utils/reify-finish.js') -const BaseCommand = require('./base-command.js') +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') -class Dedupe extends BaseCommand { +class Dedupe extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Reduce duplication in the package tree' @@ -49,6 +49,7 @@ class Dedupe extends BaseCommand { log: this.npm.log, path: where, dryRun, + workspaces: this.workspaces, } const arb = new Arborist(opts) await arb.dedupe(opts) diff --git a/lib/install.js b/lib/install.js index 490792a41a7bf..7c5f55bb9de8a 100644 --- a/lib/install.js +++ b/lib/install.js @@ -9,8 +9,8 @@ const { resolve, join } = require('path') const Arborist = require('@npmcli/arborist') const runScript = require('@npmcli/run-script') -const BaseCommand = require('./base-command.js') -class Install extends BaseCommand { +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') +class Install extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Install a package' @@ -37,6 +37,7 @@ class Install extends BaseCommand { 'bin-links', 'fund', 'dry-run', + ...super.params, ] } @@ -143,6 +144,7 @@ class Install extends BaseCommand { auditLevel: null, path: where, add: args, + workspaces: this.workspaces, } const arb = new Arborist(opts) await arb.reify(opts) diff --git a/lib/prune.js b/lib/prune.js index 4f04c3cddd2e9..a90e7595421ec 100644 --- a/lib/prune.js +++ b/lib/prune.js @@ -2,8 +2,8 @@ const Arborist = require('@npmcli/arborist') const reifyFinish = require('./utils/reify-finish.js') -const BaseCommand = require('./base-command.js') -class Prune extends BaseCommand { +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') +class Prune extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Remove extraneous packages' @@ -16,7 +16,7 @@ class Prune extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['omit', 'dry-run', 'json'] + return ['omit', 'dry-run', 'json', ...super.params] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -34,6 +34,7 @@ class Prune extends BaseCommand { ...this.npm.flatOptions, path: where, log: this.npm.log, + workspaces: this.workspaces, } const arb = new Arborist(opts) await arb.prune(opts) diff --git a/lib/rebuild.js b/lib/rebuild.js index 9c2d729e73f4e..a86154f379c91 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -4,6 +4,8 @@ const npa = require('npm-package-arg') const semver = require('semver') const completion = require('./utils/completion/installed-deep.js') +// TODO: make Arborist.rebuild() understand the workspaces option +// and then extend ArboristWorkspaceCmd instead const BaseCommand = require('./base-command.js') class Rebuild extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -45,6 +47,8 @@ class Rebuild extends BaseCommand { const arb = new Arborist({ ...this.npm.flatOptions, path: where, + // TODO when extending ReifyCmd + // workspaces: this.workspaces, }) if (args.length) { diff --git a/lib/uninstall.js b/lib/uninstall.js index 79a4420d89f39..cbbc62c2a4a75 100644 --- a/lib/uninstall.js +++ b/lib/uninstall.js @@ -5,8 +5,8 @@ const rpj = require('read-package-json-fast') const reifyFinish = require('./utils/reify-finish.js') const completion = require('./utils/completion/installed-shallow.js') -const BaseCommand = require('./base-command.js') -class Uninstall extends BaseCommand { +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') +class Uninstall extends ArboristWorkspaceCmd { static get description () { return 'Remove a package' } @@ -18,7 +18,7 @@ class Uninstall extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['save'] + return ['save', ...super.params] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -66,7 +66,7 @@ class Uninstall extends BaseCommand { path, log: this.npm.log, rm: args, - + workspaces: this.workspaces, } const arb = new Arborist(opts) await arb.reify(opts) diff --git a/lib/update.js b/lib/update.js index ff9ef9d5e8100..3cdeb8ea7dd31 100644 --- a/lib/update.js +++ b/lib/update.js @@ -6,8 +6,8 @@ const log = require('npmlog') const reifyFinish = require('./utils/reify-finish.js') const completion = require('./utils/completion/installed-deep.js') -const BaseCommand = require('./base-command.js') -class Update extends BaseCommand { +const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') +class Update extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get description () { return 'Update packages' @@ -32,6 +32,7 @@ class Update extends BaseCommand { 'bin-links', 'fund', 'dry-run', + ...super.params, ] } @@ -65,6 +66,7 @@ class Update extends BaseCommand { ...this.npm.flatOptions, log: this.npm.log, path: where, + workspaces: this.workspaces, }) await arb.reify({ update }) diff --git a/lib/workspaces/arborist-cmd.js b/lib/workspaces/arborist-cmd.js index f08843bd9ea5a..41af39b94e04b 100644 --- a/lib/workspaces/arborist-cmd.js +++ b/lib/workspaces/arborist-cmd.js @@ -9,6 +9,7 @@ class ArboristCmd extends BaseCommand { static get params () { return [ 'workspace', + 'workspaces', ] } 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 661deec269177..5ab784b65e9e6 100644 --- a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs +++ b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs @@ -210,6 +210,8 @@ All commands: [--audit-level ] [--dry-run] [-f|--force] [--json] [--package-lock-only] [--omit [--omit ...]] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] Run "npm help audit" for more info @@ -554,6 +556,8 @@ All commands: [--strict-peer-deps] [--package-lock] [--omit [--omit ...]] [--ignore-scripts] [--audit] [--bin-links] [--fund] [--dry-run] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] aliases: i, in, ins, inst, insta, instal, isnt, isnta, isntal, add @@ -595,6 +599,8 @@ All commands: [--strict-peer-deps] [--package-lock] [--omit [--omit ...]] [--ignore-scripts] [--audit] [--bin-links] [--fund] [--dry-run] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] alias: it @@ -783,7 +789,8 @@ All commands: Options: [--omit [--omit ...]] [--dry-run] - [--json] + [--json] [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] Run "npm help prune" for more info @@ -1018,6 +1025,8 @@ All commands: Options: [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] aliases: un, unlink, remove, rm, r @@ -1058,6 +1067,8 @@ All commands: [-g|--global] [--global-style] [--legacy-bundling] [--strict-peer-deps] [--package-lock] [--omit [--omit ...]] [--ignore-scripts] [--audit] [--bin-links] [--fund] [--dry-run] + [-w|--workspace [-w|--workspace ...]] + [-ws|--workspaces] aliases: up, upgrade, udpate diff --git a/test/lib/update.js b/test/lib/update.js index 5396397d520cf..411d07592a155 100644 --- a/test/lib/update.js +++ b/test/lib/update.js @@ -37,7 +37,12 @@ t.test('no args', t => { constructor (args) { t.same( args, - { ...npm.flatOptions, path: npm.prefix, log: noop }, + { + ...npm.flatOptions, + path: npm.prefix, + log: noop, + workspaces: null, + }, 'should call arborist contructor with expected args' ) } @@ -71,7 +76,12 @@ t.test('with args', t => { constructor (args) { t.same( args, - { ...npm.flatOptions, path: npm.prefix, log: noop }, + { + ...npm.flatOptions, + path: npm.prefix, + log: noop, + workspaces: null, + }, 'should call arborist contructor with expected args' ) } @@ -139,7 +149,7 @@ t.test('update --global', t => { const { path, ...opts } = args t.same( opts, - { ...npm.flatOptions, log: noop }, + { ...npm.flatOptions, log: noop, workspaces: undefined }, 'should call arborist contructor with expected options' )