From 102d4e6fb3c3b02148dbeee977a7d1e6372340d5 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 15 Jun 2021 10:50:14 -0700 Subject: [PATCH] fix(workspaces): explicitly error in global mode Also includes a preliminary refactor to consolidate workspace logic now that every command that supports workspaces has it implemented. PR-URL: https://github.com/npm/cli/pull/3417 Credit: @wraithgar Close: #3417 Reviewed-by: @ruyadorno --- lib/audit.js | 2 +- lib/base-command.js | 9 +++++ lib/ci.js | 2 +- lib/dedupe.js | 2 +- lib/diff.js | 7 ++-- lib/dist-tag.js | 6 ++-- lib/docs.js | 6 ++-- lib/exec.js | 12 +++---- lib/explain.js | 4 +-- lib/fund.js | 2 +- lib/install.js | 2 +- lib/link.js | 2 +- lib/ls.js | 4 +-- lib/npm.js | 3 ++ lib/outdated.js | 6 ++-- lib/pack.js | 6 ++-- lib/prune.js | 2 +- lib/publish.js | 10 +++--- lib/rebuild.js | 2 +- lib/repo.js | 6 ++-- lib/run-script.js | 15 ++++---- lib/set-script.js | 20 +++++------ lib/uninstall.js | 2 +- lib/unpublish.js | 6 ++-- lib/update.js | 2 +- lib/version.js | 13 +++---- lib/view.js | 12 +++---- lib/workspaces/arborist-cmd.js | 7 ++-- test/lib/npm.js | 56 ++++++++++++++++++++++++++++- test/lib/workspaces/arborist-cmd.js | 12 +++---- 30 files changed, 139 insertions(+), 101 deletions(-) diff --git a/lib/audit.js b/lib/audit.js index a97fd9b30abd4..54480d1f0cbf9 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -58,7 +58,7 @@ class Audit extends ArboristWorkspaceCmd { audit: true, path: this.npm.prefix, reporter, - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) diff --git a/lib/base-command.js b/lib/base-command.js index 843fb2d4b1358..4077733a934b0 100644 --- a/lib/base-command.js +++ b/lib/base-command.js @@ -1,6 +1,7 @@ // Base class for npm.commands[cmd] const usageUtil = require('./utils/usage.js') const ConfigDefinitions = require('./utils/config/definitions.js') +const getWorkspaces = require('./workspaces/get-workspaces.js') class BaseCommand { constructor (npm) { @@ -72,5 +73,13 @@ class BaseCommand { { code: 'ENOWORKSPACES' } ) } + + async setWorkspaces (filters) { + // TODO npm guards workspaces/global mode so we should use this.npm.prefix? + const ws = await getWorkspaces(filters, { path: this.npm.localPrefix }) + this.workspaces = ws + this.workspaceNames = [...ws.keys()] + this.workspacePaths = [...ws.values()] + } } module.exports = BaseCommand diff --git a/lib/ci.js b/lib/ci.js index 3a2a2b316a150..3ff4b65badb49 100644 --- a/lib/ci.js +++ b/lib/ci.js @@ -55,7 +55,7 @@ class CI extends ArboristWorkspaceCmd { path: where, log: this.npm.log, save: false, // npm ci should never modify the lockfile or package.json - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) diff --git a/lib/dedupe.js b/lib/dedupe.js index 9a58316b80109..aaa7a30d10416 100644 --- a/lib/dedupe.js +++ b/lib/dedupe.js @@ -50,7 +50,7 @@ class Dedupe extends ArboristWorkspaceCmd { log: this.npm.log, path: where, dryRun, - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) await arb.dedupe(opts) diff --git a/lib/diff.js b/lib/diff.js index d315551d443a5..01658c4664d05 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -8,7 +8,6 @@ const npmlog = require('npmlog') const pacote = require('pacote') const pickManifest = require('npm-pick-manifest') -const getWorkspaces = require('./workspaces/get-workspaces.js') const readPackageName = require('./utils/read-package-name.js') const BaseCommand = require('./base-command.js') @@ -90,9 +89,8 @@ class Diff extends BaseCommand { } async diffWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - for (const workspacePath of workspaces.values()) { + await this.setWorkspaces(filters) + for (const workspacePath of this.workspacePaths) { this.top = workspacePath this.prefix = workspacePath await this.diff(args) @@ -104,7 +102,6 @@ class Diff extends BaseCommand { async packageName (path) { let name try { - // TODO this won't work as expected in global mode name = await readPackageName(this.prefix) } catch (e) { npmlog.verbose('diff', 'could not read project dir package.json') diff --git a/lib/dist-tag.js b/lib/dist-tag.js index 11b1ad931e18b..e32dcf61fff80 100644 --- a/lib/dist-tag.js +++ b/lib/dist-tag.js @@ -5,7 +5,6 @@ const semver = require('semver') const otplease = require('./utils/otplease.js') const readPackageName = require('./utils/read-package-name.js') -const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') class DistTag extends BaseCommand { @@ -180,10 +179,9 @@ class DistTag extends BaseCommand { } async listWorkspaces (filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) - for (const [name] of workspaces) { + for (const name of this.workspaceNames) { try { this.npm.output(`${name}:`) await this.list(npa(name), this.npm.flatOptions) diff --git a/lib/docs.js b/lib/docs.js index 24bbe9c854a62..69a19c35c3a13 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -2,7 +2,6 @@ const log = require('npmlog') const pacote = require('pacote') const openUrl = require('./utils/open-url.js') const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') -const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') class Docs extends BaseCommand { @@ -42,9 +41,8 @@ class Docs extends BaseCommand { } async docsWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - return this.docs([...workspaces.values()]) + await this.setWorkspaces(filters) + return this.docs(this.workspacePaths) } async getDocs (pkg) { diff --git a/lib/exec.js b/lib/exec.js index 8a87615d9749e..7e5f6886a3ed7 100644 --- a/lib/exec.js +++ b/lib/exec.js @@ -1,7 +1,6 @@ const libexec = require('libnpmexec') const BaseCommand = require('./base-command.js') const getLocationMsg = require('./exec/get-workspace-location-msg.js') -const getWorkspaces = require('./workspaces/get-workspaces.js') // it's like this: // @@ -105,16 +104,15 @@ class Exec extends BaseCommand { } async _execWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) const color = this.npm.config.get('color') - for (const workspacePath of workspaces.values()) { - const locationMsg = await getLocationMsg({ color, path: workspacePath }) + for (const path of this.workspacePaths) { + const locationMsg = await getLocationMsg({ color, path }) await this._exec(args, { locationMsg, - path: workspacePath, - runPath: workspacePath, + path, + runPath: path, }) } } diff --git a/lib/explain.js b/lib/explain.js index de04c69857240..7d785d7bfcf44 100644 --- a/lib/explain.js +++ b/lib/explain.js @@ -46,8 +46,8 @@ class Explain extends ArboristWorkspaceCmd { const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions }) const tree = await arb.loadActual() - if (this.workspaces && this.workspaces.length) - this.filterSet = arb.workspaceDependencySet(tree, this.workspaces) + if (this.workspaceNames && this.workspaceNames.length) + this.filterSet = arb.workspaceDependencySet(tree, this.workspaceNames) const nodes = new Set() for (const arg of args) { diff --git a/lib/fund.js b/lib/fund.js index 55d2f65dc4b55..92580a756e8af 100644 --- a/lib/fund.js +++ b/lib/fund.js @@ -95,7 +95,7 @@ class Fund extends ArboristWorkspaceCmd { const fundingInfo = getFundingInfo(tree, { ...this.flatOptions, log: this.npm.log, - workspaces: this.workspaces, + workspaces: this.workspaceNames, }) if (this.npm.config.get('json')) diff --git a/lib/install.js b/lib/install.js index 7c5f55bb9de8a..6611763978e61 100644 --- a/lib/install.js +++ b/lib/install.js @@ -144,7 +144,7 @@ class Install extends ArboristWorkspaceCmd { auditLevel: null, path: where, add: args, - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) await arb.reify(opts) diff --git a/lib/link.js b/lib/link.js index d6abf139730bd..febd908718be3 100644 --- a/lib/link.js +++ b/lib/link.js @@ -146,7 +146,7 @@ class Link extends ArboristWorkspaceCmd { log: this.npm.log, add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`), save, - workspaces: this.workspaces, + workspaces: this.workspaceNames, }) await reifyFinish(this.npm, localArb) diff --git a/lib/ls.js b/lib/ls.js index d92b73ddfcdbb..78263554ca0a1 100644 --- a/lib/ls.js +++ b/lib/ls.js @@ -94,8 +94,8 @@ class LS extends ArboristWorkspaceCmd { // We only have to filter the first layer of edges, so we don't // explore anything that isn't part of the selected workspace set. let wsNodes - if (this.workspaces && this.workspaces.length) - wsNodes = arb.workspaceNodes(tree, this.workspaces) + if (this.workspaceNames && this.workspaceNames.length) + wsNodes = arb.workspaceNodes(tree, this.workspaceNames) const filterBySelectedWorkspaces = edge => { if (!wsNodes || !wsNodes.length) return true diff --git a/lib/npm.js b/lib/npm.js index 5f8b2ff3d703d..937459501c0a5 100644 --- a/lib/npm.js +++ b/lib/npm.js @@ -108,6 +108,9 @@ const npm = module.exports = new class extends EventEmitter { this.output(impl.usage) cb() } else if (filterByWorkspaces) { + if (this.config.get('global')) + return cb(new Error('Workspaces not supported for global packages')) + impl.execWorkspaces(args, this.config.get('workspace'), er => { process.emit('timeEnd', `command:${cmd}`) cb(er) diff --git a/lib/outdated.js b/lib/outdated.js index 1be92b9349fe7..9d60d143d71ce 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -59,8 +59,10 @@ class Outdated extends ArboristWorkspaceCmd { this.list = [] this.tree = await arb.loadActual() - if (this.workspaces && this.workspaces.length) - this.filterSet = arb.workspaceDependencySet(this.tree, this.workspaces) + if (this.workspaceNames && this.workspaceNames.length) { + this.filterSet = + arb.workspaceDependencySet(this.tree, this.workspaceNames) + } if (args.length !== 0) { // specific deps diff --git a/lib/pack.js b/lib/pack.js index 52d4c3e7f900a..f4364d29033c4 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -3,7 +3,6 @@ const log = require('npmlog') const pacote = require('pacote') const libpack = require('libnpmpack') const npa = require('npm-package-arg') -const getWorkspaces = require('./workspaces/get-workspaces.js') const { getContents, logTar } = require('./utils/tar.js') @@ -97,9 +96,8 @@ class Pack extends BaseCommand { return this.pack(args) } - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - return this.pack([...workspaces.values(), ...args.filter(a => a !== '.')]) + await this.setWorkspaces(filters) + return this.pack([...this.workspacePaths, ...args.filter(a => a !== '.')]) } } module.exports = Pack diff --git a/lib/prune.js b/lib/prune.js index a90e7595421ec..a91276fc4fa27 100644 --- a/lib/prune.js +++ b/lib/prune.js @@ -34,7 +34,7 @@ class Prune extends ArboristWorkspaceCmd { ...this.npm.flatOptions, path: where, log: this.npm.log, - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) await arb.prune(opts) diff --git a/lib/publish.js b/lib/publish.js index 3cb8b0627e974..f35388a30f4ed 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -11,7 +11,6 @@ const chalk = require('chalk') const otplease = require('./utils/otplease.js') const { getContents, logTar } = require('./utils/tar.js') -const getWorkspaces = require('./workspaces/get-workspaces.js') // for historical reasons, publishConfig in package.json can contain ANY config // keys that npm supports in .npmrc files and elsewhere. We *may* want to @@ -138,7 +137,7 @@ class Publish extends BaseCommand { }) } - if (!this.workspaces) { + if (!this.suppressOutput) { if (!silent && json) this.npm.output(JSON.stringify(pkgContents, null, 2)) else if (!silent) @@ -150,17 +149,16 @@ class Publish extends BaseCommand { async publishWorkspaces (args, filters) { // Suppresses JSON output in publish() so we can handle it here - this.workspaces = true + this.suppressOutput = true const results = {} const json = this.npm.config.get('json') const silent = log.level === 'silent' const noop = a => a const color = this.npm.color ? chalk : { green: noop, bold: noop } - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) - for (const [name, workspace] of workspaces.entries()) { + for (const [name, workspace] of this.workspaces.entries()) { let pkgContents try { pkgContents = await this.publish([workspace]) diff --git a/lib/rebuild.js b/lib/rebuild.js index ef88dc5168d0c..9aa0e27f87eb4 100644 --- a/lib/rebuild.js +++ b/lib/rebuild.js @@ -47,7 +47,7 @@ class Rebuild extends ArboristWorkspaceCmd { ...this.npm.flatOptions, path: where, // TODO when extending ReifyCmd - // workspaces: this.workspaces, + // workspaces: this.workspaceNames, }) if (args.length) { diff --git a/lib/repo.js b/lib/repo.js index 645c0eeae32fe..e0172d01f63d1 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -1,6 +1,5 @@ const log = require('npmlog') const pacote = require('pacote') -const getWorkspaces = require('./workspaces/get-workspaces.js') const { URL } = require('url') const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') @@ -44,9 +43,8 @@ class Repo extends BaseCommand { } async repoWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - return this.repo([...workspaces.values()]) + await this.setWorkspaces(filters) + return this.repo(this.workspacePaths) } async get (pkg) { diff --git a/lib/run-script.js b/lib/run-script.js index ec1042828fad8..b94d2fce07180 100644 --- a/lib/run-script.js +++ b/lib/run-script.js @@ -6,7 +6,6 @@ const rpj = require('read-package-json-fast') const log = require('npmlog') const didYouMean = require('./utils/did-you-mean.js') const isWindowsShell = require('./utils/is-windows-shell.js') -const getWorkspaces = require('./workspaces/get-workspaces.js') const cmdList = [ 'publish', @@ -195,10 +194,9 @@ class RunScript extends BaseCommand { async runWorkspaces (args, filters) { const res = [] - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) - for (const workspacePath of workspaces.values()) { + for (const workspacePath of this.workspacePaths) { const pkg = await rpj(`${workspacePath}/package.json`) const runResult = await this.run(args, { path: workspacePath, @@ -227,15 +225,14 @@ class RunScript extends BaseCommand { } async listWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) if (log.level === 'silent') return if (this.npm.config.get('json')) { const res = {} - for (const workspacePath of workspaces.values()) { + for (const workspacePath of this.workspacePaths) { const { scripts, name } = await rpj(`${workspacePath}/package.json`) res[name] = { ...scripts } } @@ -244,7 +241,7 @@ class RunScript extends BaseCommand { } if (this.npm.config.get('parseable')) { - for (const workspacePath of workspaces.values()) { + for (const workspacePath of this.workspacePaths) { const { scripts, name } = await rpj(`${workspacePath}/package.json`) for (const [script, cmd] of Object.entries(scripts || {})) this.npm.output(`${name}:${script}:${cmd}`) @@ -252,7 +249,7 @@ class RunScript extends BaseCommand { return } - for (const workspacePath of workspaces.values()) + for (const workspacePath of this.workspacePaths) await this.list(args, workspacePath) } } diff --git a/lib/set-script.js b/lib/set-script.js index b31e123becd8b..cd01e28b56b06 100644 --- a/lib/set-script.js +++ b/lib/set-script.js @@ -3,7 +3,6 @@ const fs = require('fs') const parseJSON = require('json-parse-even-better-errors') const rpj = require('read-package-json-fast') const { resolve } = require('path') -const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') class SetScript extends BaseCommand { @@ -47,28 +46,27 @@ class SetScript extends BaseCommand { } exec (args, cb) { - this.set(args).then(() => cb()).catch(cb) + this.setScript(args).then(() => cb()).catch(cb) } - async set (args) { + async setScript (args) { this.validate(args) - const warn = this.setScript(this.npm.localPrefix, args[0], args[1]) + const warn = this.doSetScript(this.npm.localPrefix, args[0], args[1]) if (warn) log.warn('set-script', `Script "${args[0]}" was overwritten`) } execWorkspaces (args, filters, cb) { - this.setWorkspaces(args, filters).then(() => cb()).catch(cb) + this.setScriptWorkspaces(args, filters).then(() => cb()).catch(cb) } - async setWorkspaces (args, filters) { + async setScriptWorkspaces (args, filters) { this.validate(args) - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) - for (const [name, path] of workspaces) { + for (const [name, path] of this.workspaces) { try { - const warn = this.setScript(path, args[0], args[1]) + const warn = this.doSetScript(path, args[0], args[1]) if (warn) { log.warn('set-script', `Script "${args[0]}" was overwritten`) log.warn(` in workspace: ${name}`) @@ -86,7 +84,7 @@ class SetScript extends BaseCommand { // returns a Boolean that will be true if // the requested script was overwritten // and false if it was set as a new script - setScript (path, name, value) { + doSetScript (path, name, value) { // Set the script let manifest let warn = false diff --git a/lib/uninstall.js b/lib/uninstall.js index cbbc62c2a4a75..fbb2cef0fbf18 100644 --- a/lib/uninstall.js +++ b/lib/uninstall.js @@ -66,7 +66,7 @@ class Uninstall extends ArboristWorkspaceCmd { path, log: this.npm.log, rm: args, - workspaces: this.workspaces, + workspaces: this.workspaceNames, } const arb = new Arborist(opts) await arb.reify(opts) diff --git a/lib/unpublish.js b/lib/unpublish.js index 1571fd88ef781..32a634013a7c4 100644 --- a/lib/unpublish.js +++ b/lib/unpublish.js @@ -6,7 +6,6 @@ const npmFetch = require('npm-registry-fetch') const libunpub = require('libnpmpublish').unpublish const readJson = util.promisify(require('read-package-json')) -const getWorkspaces = require('./workspaces/get-workspaces.js') const otplease = require('./utils/otplease.js') const getIdentity = require('./utils/get-identity.js') @@ -129,8 +128,7 @@ class Unpublish extends BaseCommand { } async unpublishWorkspaces (args, filters) { - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) + await this.setWorkspaces(filters) const force = this.npm.config.get('force') if (!force) { @@ -140,7 +138,7 @@ class Unpublish extends BaseCommand { ) } - for (const [name] of workspaces.entries()) + for (const name of this.workspaceNames) await this.unpublish([name]) } } diff --git a/lib/update.js b/lib/update.js index 3cdeb8ea7dd31..393c8f0f67e5f 100644 --- a/lib/update.js +++ b/lib/update.js @@ -66,7 +66,7 @@ class Update extends ArboristWorkspaceCmd { ...this.npm.flatOptions, log: this.npm.log, path: where, - workspaces: this.workspaces, + workspaces: this.workspaceNames, }) await arb.reify({ update }) diff --git a/lib/version.js b/lib/version.js index 3ef3801e74e81..f3680fe8b7a01 100644 --- a/lib/version.js +++ b/lib/version.js @@ -3,7 +3,6 @@ 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 { @@ -93,9 +92,8 @@ class Version extends BaseCommand { 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) { + await this.setWorkspaces(filters) + for (const [name, path] of this.workspaces) { this.npm.output(name) const version = await libnpmversion(args[0], { ...this.npm.flatOptions, @@ -128,11 +126,10 @@ class Version extends BaseCommand { async listWorkspaces (filters) { const results = {} - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - for (const [, path] of workspaces) { + await this.setWorkspaces(filters) + for (const path of this.workspacePaths) { const pj = resolve(path, 'package.json') - // getWorkspaces has already parsed this so we know it won't error + // setWorkspaces has already parsed package.json so we know it won't error const pkg = await readFile(pj, 'utf8') .then(data => JSON.parse(data)) diff --git a/lib/view.js b/lib/view.js index 9cc1aed914488..788df3ed0b4d8 100644 --- a/lib/view.js +++ b/lib/view.js @@ -13,7 +13,6 @@ const semver = require('semver') const style = require('ansistyles') const { inspect, promisify } = require('util') const { packument } = require('pacote') -const getWorkspaces = require('./workspaces/get-workspaces.js') const readFile = promisify(fs.readFile) const readJson = async file => jsonParse(await readFile(file, 'utf8')) @@ -160,10 +159,9 @@ class View extends BaseCommand { args = [''] // getData relies on this } const results = {} - const workspaces = - await getWorkspaces(filters, { path: this.npm.localPrefix }) - for (const workspace of [...workspaces.entries()]) { - const wsPkg = `${workspace[0]}${pkg.slice(1)}` + await this.setWorkspaces(filters) + for (const name of this.workspaceNames) { + const wsPkg = `${name}${pkg.slice(1)}` const [pckmnt, data] = await this.getData(wsPkg, args) let reducedData = data.reduce(reducer, {}) @@ -177,7 +175,7 @@ class View extends BaseCommand { if (wholePackument) data.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]][''])) else { - console.log(`${workspace[0]}:`) + console.log(`${name}:`) const msg = await this.jsonData(reducedData, pckmnt._id) if (msg !== '') console.log(msg) @@ -185,7 +183,7 @@ class View extends BaseCommand { } else { const msg = await this.jsonData(reducedData, pckmnt._id) if (msg !== '') - results[workspace[0]] = JSON.parse(msg) + results[name] = JSON.parse(msg) } } if (Object.keys(results).length > 0) diff --git a/lib/workspaces/arborist-cmd.js b/lib/workspaces/arborist-cmd.js index 337e7f9d8f932..cb6b66b8cb257 100644 --- a/lib/workspaces/arborist-cmd.js +++ b/lib/workspaces/arborist-cmd.js @@ -3,7 +3,6 @@ // be able to run a filtered Arborist.reify() at some point. const BaseCommand = require('../base-command.js') -const getWorkspaces = require('./get-workspaces.js') class ArboristCmd extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { @@ -14,10 +13,8 @@ class ArboristCmd extends BaseCommand { } execWorkspaces (args, filters, cb) { - getWorkspaces(filters, { path: this.npm.localPrefix }) - .then(workspaces => { - this.workspaces = [...workspaces.keys()] - this.workspacePaths = [...workspaces.values()] + this.setWorkspaces(filters) + .then(() => { this.exec(args, cb) }) .catch(er => cb(er)) diff --git a/test/lib/npm.js b/test/lib/npm.js index 6f8f8936d3f73..6909c43e4ff0e 100644 --- a/test/lib/npm.js +++ b/test/lib/npm.js @@ -355,7 +355,7 @@ t.test('npm.load', t => { await new Promise((res) => setTimeout(res)) }) - t.test('workpaces-aware configs and commands', async t => { + t.test('workspace-aware configs and commands', async t => { const dir = t.testdir({ packages: { a: { @@ -438,6 +438,60 @@ t.test('npm.load', t => { }) }) + t.test('workspaces in global mode', async t => { + const dir = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { test: 'echo test a' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + scripts: { test: 'echo test b' }, + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'root', + version: '1.0.0', + workspaces: ['./packages/*'], + }), + }) + const { execPath } = process + freshConfig({ + argv: [ + execPath, + process.argv[1], + '--userconfig', + resolve(dir, '.npmrc'), + '--color', + 'false', + '--workspaces', + '--global', + 'true', + ], + }) + await npm.load(er => { + if (er) + throw er + }) + npm.localPrefix = dir + await new Promise((res, rej) => { + // verify that calling the command with a short name still sets + // the npm.command property to the full canonical name of the cmd. + npm.command = null + npm.commands.run([], er => { + t.match(er, /Workspaces not supported for global packages/) + res() + }) + }) + }) + t.end() }) diff --git a/test/lib/workspaces/arborist-cmd.js b/test/lib/workspaces/arborist-cmd.js index 740ddb1ff0dc5..75ac8f4ebf804 100644 --- a/test/lib/workspaces/arborist-cmd.js +++ b/test/lib/workspaces/arborist-cmd.js @@ -49,7 +49,7 @@ t.test('arborist-cmd', async t => { // check filtering for a single workspace name cmd.exec = function (args, cb) { - t.same(this.workspaces, ['a'], 'should set array with single ws name') + t.same(this.workspaceNames, ['a'], 'should set array with single ws name') t.same(args, ['foo'], 'should get received args') cb() } @@ -59,7 +59,7 @@ t.test('arborist-cmd', async t => { // check filtering single workspace by path cmd.exec = function (args, cb) { - t.same(this.workspaces, ['a'], + t.same(this.workspaceNames, ['a'], 'should set array with single ws name from path') cb() } @@ -69,7 +69,7 @@ t.test('arborist-cmd', async t => { // check filtering single workspace by full path cmd.exec = function (args, cb) { - t.same(this.workspaces, ['a'], + t.same(this.workspaceNames, ['a'], 'should set array with single ws name from full path') cb() } @@ -79,7 +79,7 @@ t.test('arborist-cmd', async t => { // filtering multiple workspaces by name cmd.exec = function (args, cb) { - t.same(this.workspaces, ['a', 'c'], + t.same(this.workspaceNames, ['a', 'c'], 'should set array with multiple listed ws names') cb() } @@ -89,7 +89,7 @@ t.test('arborist-cmd', async t => { // filtering multiple workspaces by path names cmd.exec = function (args, cb) { - t.same(this.workspaces, ['a', 'c'], + t.same(this.workspaceNames, ['a', 'c'], 'should set array with multiple ws names from paths') cb() } @@ -99,7 +99,7 @@ t.test('arborist-cmd', async t => { // filtering multiple workspaces by parent path name cmd.exec = function (args, cb) { - t.same(this.workspaces, ['c', 'd'], + t.same(this.workspaceNames, ['c', 'd'], 'should set array with multiple ws names from a parent folder name') cb() }