From 69ab10bbf83d42a5d3b5d3f43e5e5b861f3dfed0 Mon Sep 17 00:00:00 2001 From: Gar Date: Mon, 27 Sep 2021 09:31:42 -0700 Subject: [PATCH 01/14] deps: is-core-module@2.7.0 --- node_modules/is-core-module/core.json | 112 +++++++++++----------- node_modules/is-core-module/package.json | 5 +- node_modules/is-core-module/test/index.js | 22 +++++ package-lock.json | 12 +-- 4 files changed, 87 insertions(+), 64 deletions(-) diff --git a/node_modules/is-core-module/core.json b/node_modules/is-core-module/core.json index ec741dd946f18..5cd90d1732b6b 100644 --- a/node_modules/is-core-module/core.json +++ b/node_modules/is-core-module/core.json @@ -1,105 +1,105 @@ { "assert": true, - "node:assert": ">= 16", + "node:assert": [">= 14.18 && < 15", ">= 16"], "assert/strict": ">= 15", "node:assert/strict": ">= 16", "async_hooks": ">= 8", - "node:async_hooks": ">= 16", + "node:async_hooks": [">= 14.18 && < 15", ">= 16"], "buffer_ieee754": "< 0.9.7", "buffer": true, - "node:buffer": ">= 16", + "node:buffer": [">= 14.18 && < 15", ">= 16"], "child_process": true, - "node:child_process": ">= 16", + "node:child_process": [">= 14.18 && < 15", ">= 16"], "cluster": true, - "node:cluster": ">= 16", + "node:cluster": [">= 14.18 && < 15", ">= 16"], "console": true, - "node:console": ">= 16", + "node:console": [">= 14.18 && < 15", ">= 16"], "constants": true, - "node:constants": ">= 16", + "node:constants": [">= 14.18 && < 15", ">= 16"], "crypto": true, - "node:crypto": ">= 16", + "node:crypto": [">= 14.18 && < 15", ">= 16"], "_debug_agent": ">= 1 && < 8", "_debugger": "< 8", "dgram": true, - "node:dgram": ">= 16", + "node:dgram": [">= 14.18 && < 15", ">= 16"], "diagnostics_channel": [">= 14.17 && < 15", ">= 15.1"], - "node:diagnostics_channel": ">= 16", + "node:diagnostics_channel": [">= 14.18 && < 15", ">= 16"], "dns": true, - "node:dns": ">= 16", + "node:dns": [">= 14.18 && < 15", ">= 16"], "dns/promises": ">= 15", "node:dns/promises": ">= 16", "domain": ">= 0.7.12", - "node:domain": ">= 16", + "node:domain": [">= 14.18 && < 15", ">= 16"], "events": true, - "node:events": ">= 16", + "node:events": [">= 14.18 && < 15", ">= 16"], "freelist": "< 6", "fs": true, - "node:fs": ">= 16", + "node:fs": [">= 14.18 && < 15", ">= 16"], "fs/promises": [">= 10 && < 10.1", ">= 14"], - "node:fs/promises": ">= 16", + "node:fs/promises": [">= 14.18 && < 15", ">= 16"], "_http_agent": ">= 0.11.1", - "node:_http_agent": ">= 16", + "node:_http_agent": [">= 14.18 && < 15", ">= 16"], "_http_client": ">= 0.11.1", - "node:_http_client": ">= 16", + "node:_http_client": [">= 14.18 && < 15", ">= 16"], "_http_common": ">= 0.11.1", - "node:_http_common": ">= 16", + "node:_http_common": [">= 14.18 && < 15", ">= 16"], "_http_incoming": ">= 0.11.1", - "node:_http_incoming": ">= 16", + "node:_http_incoming": [">= 14.18 && < 15", ">= 16"], "_http_outgoing": ">= 0.11.1", - "node:_http_outgoing": ">= 16", + "node:_http_outgoing": [">= 14.18 && < 15", ">= 16"], "_http_server": ">= 0.11.1", - "node:_http_server": ">= 16", + "node:_http_server": [">= 14.18 && < 15", ">= 16"], "http": true, - "node:http": ">= 16", + "node:http": [">= 14.18 && < 15", ">= 16"], "http2": ">= 8.8", - "node:http2": ">= 16", + "node:http2": [">= 14.18 && < 15", ">= 16"], "https": true, - "node:https": ">= 16", + "node:https": [">= 14.18 && < 15", ">= 16"], "inspector": ">= 8", - "node:inspector": ">= 16", + "node:inspector": [">= 14.18 && < 15", ">= 16"], "_linklist": "< 8", "module": true, - "node:module": ">= 16", + "node:module": [">= 14.18 && < 15", ">= 16"], "net": true, - "node:net": ">= 16", + "node:net": [">= 14.18 && < 15", ">= 16"], "node-inspect/lib/_inspect": ">= 7.6 && < 12", "node-inspect/lib/internal/inspect_client": ">= 7.6 && < 12", "node-inspect/lib/internal/inspect_repl": ">= 7.6 && < 12", "os": true, - "node:os": ">= 16", + "node:os": [">= 14.18 && < 15", ">= 16"], "path": true, - "node:path": ">= 16", + "node:path": [">= 14.18 && < 15", ">= 16"], "path/posix": ">= 15.3", "node:path/posix": ">= 16", "path/win32": ">= 15.3", "node:path/win32": ">= 16", "perf_hooks": ">= 8.5", - "node:perf_hooks": ">= 16", + "node:perf_hooks": [">= 14.18 && < 15", ">= 16"], "process": ">= 1", - "node:process": ">= 16", + "node:process": [">= 14.18 && < 15", ">= 16"], "punycode": true, - "node:punycode": ">= 16", + "node:punycode": [">= 14.18 && < 15", ">= 16"], "querystring": true, - "node:querystring": ">= 16", + "node:querystring": [">= 14.18 && < 15", ">= 16"], "readline": true, - "node:readline": ">= 16", + "node:readline": [">= 14.18 && < 15", ">= 16"], "repl": true, - "node:repl": ">= 16", + "node:repl": [">= 14.18 && < 15", ">= 16"], "smalloc": ">= 0.11.5 && < 3", "_stream_duplex": ">= 0.9.4", - "node:_stream_duplex": ">= 16", + "node:_stream_duplex": [">= 14.18 && < 15", ">= 16"], "_stream_transform": ">= 0.9.4", - "node:_stream_transform": ">= 16", + "node:_stream_transform": [">= 14.18 && < 15", ">= 16"], "_stream_wrap": ">= 1.4.1", - "node:_stream_wrap": ">= 16", + "node:_stream_wrap": [">= 14.18 && < 15", ">= 16"], "_stream_passthrough": ">= 0.9.4", - "node:_stream_passthrough": ">= 16", + "node:_stream_passthrough": [">= 14.18 && < 15", ">= 16"], "_stream_readable": ">= 0.9.4", - "node:_stream_readable": ">= 16", + "node:_stream_readable": [">= 14.18 && < 15", ">= 16"], "_stream_writable": ">= 0.9.4", - "node:_stream_writable": ">= 16", + "node:_stream_writable": [">= 14.18 && < 15", ">= 16"], "stream": true, - "node:stream": ">= 16", + "node:stream": [">= 14.18 && < 15", ">= 16"], "stream/consumers": ">= 16.7", "node:stream/consumers": ">= 16.7", "stream/promises": ">= 15", @@ -107,28 +107,28 @@ "stream/web": ">= 16.5", "node:stream/web": ">= 16.5", "string_decoder": true, - "node:string_decoder": ">= 16", + "node:string_decoder": [">= 14.18 && < 15", ">= 16"], "sys": [">= 0.6 && < 0.7", ">= 0.8"], - "node:sys": ">= 16", + "node:sys": [">= 14.18 && < 15", ">= 16"], "timers": true, - "node:timers": ">= 16", + "node:timers": [">= 14.18 && < 15", ">= 16"], "timers/promises": ">= 15", "node:timers/promises": ">= 16", "_tls_common": ">= 0.11.13", - "node:_tls_common": ">= 16", + "node:_tls_common": [">= 14.18 && < 15", ">= 16"], "_tls_legacy": ">= 0.11.3 && < 10", "_tls_wrap": ">= 0.11.3", - "node:_tls_wrap": ">= 16", + "node:_tls_wrap": [">= 14.18 && < 15", ">= 16"], "tls": true, - "node:tls": ">= 16", + "node:tls": [">= 14.18 && < 15", ">= 16"], "trace_events": ">= 10", - "node:trace_events": ">= 16", + "node:trace_events": [">= 14.18 && < 15", ">= 16"], "tty": true, - "node:tty": ">= 16", + "node:tty": [">= 14.18 && < 15", ">= 16"], "url": true, - "node:url": ">= 16", + "node:url": [">= 14.18 && < 15", ">= 16"], "util": true, - "node:util": ">= 16", + "node:util": [">= 14.18 && < 15", ">= 16"], "util/types": ">= 15.3", "node:util/types": ">= 16", "v8/tools/arguments": ">= 10 && < 12", @@ -139,12 +139,12 @@ "v8/tools/profile_view": [">= 4.4 && < 5", ">= 5.2 && < 12"], "v8/tools/splaytree": [">= 4.4 && < 5", ">= 5.2 && < 12"], "v8": ">= 1", - "node:v8": ">= 16", + "node:v8": [">= 14.18 && < 15", ">= 16"], "vm": true, - "node:vm": ">= 16", + "node:vm": [">= 14.18 && < 15", ">= 16"], "wasi": ">= 13.4 && < 13.5", "worker_threads": ">= 11.7", - "node:worker_threads": ">= 16", + "node:worker_threads": [">= 14.18 && < 15", ">= 16"], "zlib": true, - "node:zlib": ">= 16" + "node:zlib": [">= 14.18 && < 15", ">= 16"] } diff --git a/node_modules/is-core-module/package.json b/node_modules/is-core-module/package.json index a12bd4889a60d..2b58b2332cd8e 100644 --- a/node_modules/is-core-module/package.json +++ b/node_modules/is-core-module/package.json @@ -1,8 +1,9 @@ { "name": "is-core-module", - "version": "2.6.0", + "version": "2.7.0", "description": "Is this specifier a node.js core module?", "main": "index.js", + "sideEffects": false, "exports": { ".": [ { @@ -48,7 +49,7 @@ "has": "^1.0.3" }, "devDependencies": { - "@ljharb/eslint-config": "^17.6.0", + "@ljharb/eslint-config": "^18.0.0", "aud": "^1.1.5", "auto-changelog": "^2.3.0", "eslint": "^7.32.0", diff --git a/node_modules/is-core-module/test/index.js b/node_modules/is-core-module/test/index.js index 59a0055e72e99..392678e85c684 100644 --- a/node_modules/is-core-module/test/index.js +++ b/node_modules/is-core-module/test/index.js @@ -104,5 +104,27 @@ test('core modules', function (t) { st.end(); }); + t.test('Object.prototype pollution', function (st) { + /* eslint no-extend-native: 1 */ + var nonKey = 'not a core module'; + st.teardown(function () { + delete Object.prototype.fs; + delete Object.prototype.path; + delete Object.prototype.http; + delete Object.prototype[nonKey]; + }); + Object.prototype.fs = false; + Object.prototype.path = '>= 999999999'; + Object.prototype.http = data.http; + Object.prototype[nonKey] = true; + + st.equal(isCore('fs'), true, 'fs is a core module even if Object.prototype lies'); + st.equal(isCore('path'), true, 'path is a core module even if Object.prototype lies'); + st.equal(isCore('http'), true, 'path is a core module even if Object.prototype matches data'); + st.equal(isCore(nonKey), false, '"' + nonKey + '" is not a core module even if Object.prototype lies'); + + st.end(); + }); + t.end(); }); diff --git a/package-lock.json b/package-lock.json index e44c83feb822b..b9181803c103a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4029,9 +4029,9 @@ } }, "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "inBundle": true, "dependencies": { "has": "^1.0.3" @@ -13413,9 +13413,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.7.0.tgz", + "integrity": "sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ==", "requires": { "has": "^1.0.3" } From 56d6cfdc0745fe919645389b94efb36760eb4179 Mon Sep 17 00:00:00 2001 From: Gar Date: Mon, 27 Sep 2021 10:04:05 -0700 Subject: [PATCH 02/14] fix: encode url before opening will filter out a small subset of non-URL-safe characters that still parse properly with `new URL` PR-URL: https://github.com/npm/cli/pull/3804 Credit: @isaacs Close: #3804 Reviewed-by: @wraithgar --- lib/utils/open-url.js | 1 + test/lib/utils/open-url.js | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/utils/open-url.js b/lib/utils/open-url.js index 41fac33ec66e9..331ca96fa96d0 100644 --- a/lib/utils/open-url.js +++ b/lib/utils/open-url.js @@ -4,6 +4,7 @@ const { URL } = require('url') // attempt to open URL in web-browser, print address otherwise: const open = async (npm, url, errMsg) => { + url = encodeURI(url) const browser = npm.config.get('browser') function printAlternateMsg () { diff --git a/test/lib/utils/open-url.js b/test/lib/utils/open-url.js index a31a8cb6867df..36724d0adf7bb 100644 --- a/test/lib/utils/open-url.js +++ b/test/lib/utils/open-url.js @@ -47,11 +47,10 @@ t.test('returns error for non-https and non-file url', async (t) => { openerOpts = null OUTPUT.length = 0 }) - t.rejects(openUrl(npm, 'ftp://www.npmjs.com', 'npm home'), /Invalid URL/, 'got the correct error') + await t.rejects(openUrl(npm, 'ftp://www.npmjs.com', 'npm home'), /Invalid URL/, 'got the correct error') t.equal(openerUrl, null, 'did not open') t.same(openerOpts, null, 'did not open') t.same(OUTPUT, [], 'printed no output') - t.end() }) t.test('returns error for non-parseable url', async (t) => { @@ -60,11 +59,22 @@ t.test('returns error for non-parseable url', async (t) => { openerOpts = null OUTPUT.length = 0 }) - t.rejects(openUrl(npm, 'git+ssh://user@host:repo.git', 'npm home'), /Invalid URL/, 'got the correct error') + await t.rejects(openUrl(npm, 'git+ssh://user@host:repo.git', 'npm home'), /Invalid URL/, 'got the correct error') t.equal(openerUrl, null, 'did not open') t.same(openerOpts, null, 'did not open') t.same(OUTPUT, [], 'printed no output') - t.end() +}) + +t.test('encodes non-URL-safe characters in url provided', async (t) => { + t.teardown(() => { + openerUrl = null + openerOpts = null + OUTPUT.length = 0 + }) + await openUrl(npm, 'https://www.npmjs.com/|cat', 'npm home') + t.equal(openerUrl, 'https://www.npmjs.com/%7Ccat', 'opened the encoded url') + t.same(openerOpts, { command: null }, 'passed command as null (the default)') + t.same(OUTPUT, [], 'printed no output') }) t.test('opens a url with the given browser', async (t) => { @@ -79,7 +89,6 @@ t.test('opens a url with the given browser', async (t) => { t.equal(openerUrl, 'https://www.npmjs.com', 'opened the given url') t.same(openerOpts, { command: 'chrome' }, 'passed the given browser as command') t.same(OUTPUT, [], 'printed no output') - t.end() }) t.test('prints where to go when browser is disabled', async (t) => { @@ -96,7 +105,6 @@ t.test('prints where to go when browser is disabled', async (t) => { t.equal(OUTPUT.length, 1, 'got one logged message') t.equal(OUTPUT[0].length, 1, 'logged message had one value') t.matchSnapshot(OUTPUT[0][0], 'printed expected message') - t.end() }) t.test('prints where to go when browser is disabled and json is enabled', async (t) => { @@ -115,7 +123,6 @@ t.test('prints where to go when browser is disabled and json is enabled', async t.equal(OUTPUT.length, 1, 'got one logged message') t.equal(OUTPUT[0].length, 1, 'logged message had one value') t.matchSnapshot(OUTPUT[0][0], 'printed expected message') - t.end() }) t.test('prints where to go when given browser does not exist', async (t) => { @@ -133,7 +140,6 @@ t.test('prints where to go when given browser does not exist', async (t) => { t.equal(OUTPUT.length, 1, 'got one logged message') t.equal(OUTPUT[0].length, 1, 'logged message had one value') t.matchSnapshot(OUTPUT[0][0], 'printed expected message') - t.end() }) t.test('handles unknown opener error', async (t) => { @@ -146,5 +152,4 @@ t.test('handles unknown opener error', async (t) => { npm.config.set('browser', true) }) t.rejects(openUrl(npm, 'https://www.npmjs.com', 'npm home'), 'failed', 'got the correct error') - t.end() }) From 075fe50565ae5c66df727cdd7df9dd5ed8cd4015 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Sun, 26 Sep 2021 03:18:17 +0000 Subject: [PATCH 03/14] fix: restore exit code on "npm outdated" closes: https://github.com/npm/cli/issues/2556 xref: https://github.com/npm/cli/pull/1750 The xref'ed PR apparently dropped this behavior without any explanation. PR-URL: https://github.com/npm/cli/pull/3799 Credit: @gfyoung Close: #3799 Reviewed-by: @wraithgar --- lib/outdated.js | 3 +++ smoke-tests/index.js | 8 ++++++-- test/lib/outdated.js | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/outdated.js b/lib/outdated.js index 01e268fe96aee..a5be13cdaf108 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -87,6 +87,9 @@ class Outdated extends ArboristWorkspaceCmd { // sorts list alphabetically const outdated = this.list.sort((a, b) => a.name.localeCompare(b.name, 'en')) + if (outdated.length > 0) + process.exitCode = 1 + // return if no outdated packages if (outdated.length === 0 && !this.npm.config.get('json')) return diff --git a/smoke-tests/index.js b/smoke-tests/index.js index 5e2d5e071ae12..076c53e78a0b7 100644 --- a/smoke-tests/index.js +++ b/smoke-tests/index.js @@ -174,9 +174,13 @@ t.test('npm diff', async t => { t.test('npm outdated', async t => { const cmd = `${npmBin} outdated` - const cmdRes = await exec(cmd) + const cmdRes = await exec(cmd).catch(err => { + t.equal(err.code, 1, 'should exit with error code') + return err + }) - t.matchSnapshot(cmdRes, + t.not(cmdRes.stderr, '', 'should have stderr output') + t.matchSnapshot(String(cmdRes.stdout), 'should have expected outdated output') }) diff --git a/test/lib/outdated.js b/test/lib/outdated.js index 34a0aa6c9e03e..518436d0af4ec 100644 --- a/test/lib/outdated.js +++ b/test/lib/outdated.js @@ -102,6 +102,10 @@ const outdated = (dir, opts) => { t.beforeEach(() => logs = '') +const { exitCode } = process + +t.afterEach(() => process.exitCode = exitCode) + const redactCwd = (path) => { const normalizePath = p => p .replace(/\\+/g, '/') @@ -175,6 +179,7 @@ t.test('should display outdated deps', t => { outdated(null, { config: { global: true }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -187,6 +192,7 @@ t.test('should display outdated deps', t => { }, color: true, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -200,6 +206,7 @@ t.test('should display outdated deps', t => { }, color: true, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -213,6 +220,7 @@ t.test('should display outdated deps', t => { }, color: true, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -226,6 +234,7 @@ t.test('should display outdated deps', t => { }, color: true, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -238,6 +247,7 @@ t.test('should display outdated deps', t => { long: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -250,6 +260,7 @@ t.test('should display outdated deps', t => { json: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -263,6 +274,7 @@ t.test('should display outdated deps', t => { long: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -275,6 +287,7 @@ t.test('should display outdated deps', t => { parseable: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -288,6 +301,7 @@ t.test('should display outdated deps', t => { long: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -299,6 +313,7 @@ t.test('should display outdated deps', t => { all: true, }, }).exec([], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -310,6 +325,7 @@ t.test('should display outdated deps', t => { global: false, }, }).exec(['cat'], () => { + t.equal(process.exitCode, 1) t.matchSnapshot(logs) t.end() }) @@ -540,6 +556,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display ws outdated deps human output') + t.equal(process.exitCode, 1) res() }) }) @@ -554,6 +571,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display ws outdated deps json output') + t.equal(process.exitCode, 1) res() }) }) @@ -568,6 +586,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display ws outdated deps parseable output') + t.equal(process.exitCode, 1) res() }) }) @@ -582,6 +601,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display all dependencies') + t.equal(process.exitCode, 1) res() }) }) @@ -594,6 +614,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should highlight ws in dependend by section') + t.equal(process.exitCode, 1) res() }) }) @@ -604,6 +625,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display results filtered by ws') + t.equal(process.exitCode, 1) res() }) }) @@ -618,6 +640,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display json results filtered by ws') + t.equal(process.exitCode, 1) res() }) }) @@ -632,6 +655,7 @@ t.test('workspaces', async t => { rej(err) t.matchSnapshot(logs, 'should display parseable results filtered by ws') + t.equal(process.exitCode, 1) res() }) }) @@ -647,6 +671,7 @@ t.test('workspaces', async t => { t.matchSnapshot(logs, 'should display nested deps when filtering by ws and using --all') + t.equal(process.exitCode, 1) res() }) }) @@ -669,6 +694,7 @@ t.test('workspaces', async t => { t.matchSnapshot(logs, 'should display missing deps when filtering by ws') + t.equal(process.exitCode, 1) res() }) }) From e94ddeaca1e75ecc8f54ebcb3df222965e3635d1 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 28 Sep 2021 10:05:08 -0700 Subject: [PATCH 04/14] deps: @npmcli/arborist@2.9.0 * fix: avoid infinite loops in peer dep replacements * fix: use Intl.Collator for string sorting when available * feat(vuln): expose isDirect --- .../@isaacs/string-locale-compare/LICENSE | 15 +++++ .../@isaacs/string-locale-compare/index.js | 22 +++++++ .../string-locale-compare/package.json | 28 +++++++++ .../@npmcli/arborist/lib/add-rm-pkg-deps.js | 4 +- .../arborist/lib/arborist/build-ideal-tree.js | 14 +++-- .../arborist/lib/arborist/load-virtual.js | 5 +- .../@npmcli/arborist/lib/arborist/rebuild.js | 4 +- .../@npmcli/arborist/lib/audit-report.js | 3 +- .../@npmcli/arborist/lib/can-place-dep.js | 5 +- .../@npmcli/arborist/lib/place-dep.js | 63 ++++++++++++++++--- .../@npmcli/arborist/lib/printable.js | 9 +-- .../@npmcli/arborist/lib/shrinkwrap.js | 3 +- node_modules/@npmcli/arborist/lib/vuln.js | 22 +++++-- .../@npmcli/arborist/lib/yarn-lock.js | 13 ++-- node_modules/@npmcli/arborist/package.json | 3 +- package-lock.json | 27 +++++--- package.json | 2 +- 17 files changed, 197 insertions(+), 45 deletions(-) create mode 100644 node_modules/@isaacs/string-locale-compare/LICENSE create mode 100644 node_modules/@isaacs/string-locale-compare/index.js create mode 100644 node_modules/@isaacs/string-locale-compare/package.json diff --git a/node_modules/@isaacs/string-locale-compare/LICENSE b/node_modules/@isaacs/string-locale-compare/LICENSE new file mode 100644 index 0000000000000..05eeeb88c2ef4 --- /dev/null +++ b/node_modules/@isaacs/string-locale-compare/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/@isaacs/string-locale-compare/index.js b/node_modules/@isaacs/string-locale-compare/index.js new file mode 100644 index 0000000000000..a6cec27efb8ec --- /dev/null +++ b/node_modules/@isaacs/string-locale-compare/index.js @@ -0,0 +1,22 @@ +const hasIntl = typeof Intl === 'object' && !!Intl +const Collator = hasIntl && Intl.Collator +const cache = new Map() + +const collatorCompare = locale => { + const collator = new Collator(locale) + return (a, b) => collator.compare(a, b) +} + +const localeCompare = locale => (a, b) => a.localeCompare(b, locale) + +module.exports = locale => { + if (!locale || typeof locale !== 'string') + throw new TypeError('locale required') + + if (cache.has(locale)) + return cache.get(locale) + + const compare = hasIntl ? collatorCompare(locale) : localeCompare(locale) + cache.set(locale, compare) + return compare +} diff --git a/node_modules/@isaacs/string-locale-compare/package.json b/node_modules/@isaacs/string-locale-compare/package.json new file mode 100644 index 0000000000000..a322c1c92d3cc --- /dev/null +++ b/node_modules/@isaacs/string-locale-compare/package.json @@ -0,0 +1,28 @@ +{ + "name": "@isaacs/string-locale-compare", + "version": "1.0.1", + "files": [ + "index.js" + ], + "main": "index.js", + "description": "Compare strings with Intl.Collator if available, falling back to String.localeCompare otherwise", + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/string-locale-compare" + }, + "author": "Isaac Z. Schlueter (https://izs.me)", + "license": "ISC", + "scripts": { + "test": "tap", + "snap": "tap", + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags" + }, + "tap": { + "check-coverage": true + }, + "devDependencies": { + "tap": "^15.0.9" + } +} diff --git a/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js b/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js index c1b64a461af8a..3c1cbd44abcc9 100644 --- a/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js +++ b/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js @@ -1,5 +1,7 @@ // add and remove dependency specs to/from pkg manifest +const localeCompare = require('@isaacs/string-locale-compare')('en') + const add = ({pkg, add, saveBundle, saveType, log}) => { for (const spec of add) { addSingle({pkg, spec, saveBundle, saveType, log}) @@ -79,7 +81,7 @@ const addSingle = ({pkg, spec, saveBundle, saveType, log}) => { // keep it sorted, keep it unique const bd = new Set(pkg.bundleDependencies || []) bd.add(spec.name) - pkg.bundleDependencies = [...bd].sort((a, b) => a.localeCompare(b, 'en')) + pkg.bundleDependencies = [...bd].sort(localeCompare) } } diff --git a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js index c45024d16e86b..b7876b1147e10 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -1,4 +1,5 @@ // mixin implementing the buildIdealTree method +const localeCompare = require('@isaacs/string-locale-compare')('en') const rpj = require('read-package-json-fast') const npa = require('npm-package-arg') const pacote = require('pacote') @@ -771,7 +772,7 @@ This is a one-time fix-up, please be patient... // sort physically shallower deps up to the front of the queue, // because they'll affect things deeper in, then alphabetical this[_depsQueue].sort((a, b) => - (a.depth - b.depth) || a.path.localeCompare(b.path, 'en')) + (a.depth - b.depth) || localeCompare(a.path, b.path)) const node = this[_depsQueue].shift() const bd = node.package.bundleDependencies @@ -916,7 +917,7 @@ This is a one-time fix-up, please be patient... } const placeDeps = tasks - .sort((a, b) => a.edge.name.localeCompare(b.edge.name, 'en')) + .sort((a, b) => localeCompare(a.edge.name, b.edge.name)) .map(({ edge, dep }) => new PlaceDep({ edge, dep, @@ -993,8 +994,13 @@ This is a one-time fix-up, please be patient... return } - // lastly, also check for the missing deps of the node we placed + // lastly, also check for the missing deps of the node we placed, + // and any holes created by pruning out conflicted peer sets. this[_depsQueue].push(placed) + for (const dep of pd.needEvaluation) { + this[_depsSeen].delete(dep) + this[_depsQueue].push(dep) + } // pre-fetch any problem edges, since we'll need these soon // if it fails at this point, though, dont' worry because it @@ -1242,7 +1248,7 @@ This is a one-time fix-up, please be patient... // we typically only install non-optional peers, but we have to // factor them into the peerSet so that we can avoid conflicts .filter(e => e.peer && !(e.valid && e.to)) - .sort(({name: a}, {name: b}) => a.localeCompare(b, 'en')) + .sort(({name: a}, {name: b}) => localeCompare(a, b)) for (const edge of peerEdges) { // already placed this one, and we're happy with it. diff --git a/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js b/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js index fa0aa0746e11a..f1960116737e8 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js +++ b/node_modules/@npmcli/arborist/lib/arborist/load-virtual.js @@ -1,4 +1,5 @@ // mixin providing the loadVirtual method +const localeCompare = require('@isaacs/string-locale-compare')('en') const {resolve} = require('path') @@ -167,12 +168,12 @@ module.exports = cls => class VirtualLoader extends cls { ...depsToEdges('peerOptional', peerOptional), ...lockWS, ].sort(([atype, aname], [btype, bname]) => - atype.localeCompare(btype, 'en') || aname.localeCompare(bname, 'en')) + localeCompare(atype, btype) || localeCompare(aname, bname)) const rootEdges = [...root.edgesOut.values()] .map(e => [e.type, e.name, e.spec]) .sort(([atype, aname], [btype, bname]) => - atype.localeCompare(btype, 'en') || aname.localeCompare(bname, 'en')) + localeCompare(atype, btype) || localeCompare(aname, bname)) if (rootEdges.length !== lockEdges.length) { // something added or removed diff --git a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js index 743794f4bda51..e48bdd76b538c 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js +++ b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js @@ -1,6 +1,7 @@ // Arborist.rebuild({path = this.path}) will do all the binlinks and // bundle building needed. Called by reify, and by `npm rebuild`. +const localeCompare = require('@isaacs/string-locale-compare')('en') const {depth: dfwalk} = require('treeverse') const promiseAllRejectLate = require('promise-all-reject-late') const rpj = require('read-package-json-fast') @@ -14,7 +15,8 @@ const { } = require('@npmcli/node-gyp') const boolEnv = b => b ? '1' : '' -const sortNodes = (a, b) => (a.depth - b.depth) || a.path.localeCompare(b.path, 'en') +const sortNodes = (a, b) => + (a.depth - b.depth) || localeCompare(a.path, b.path) const _workspaces = Symbol.for('workspaces') const _build = Symbol('build') diff --git a/node_modules/@npmcli/arborist/lib/audit-report.js b/node_modules/@npmcli/arborist/lib/audit-report.js index 2e6c207b33eb3..de97cdc29e3a5 100644 --- a/node_modules/@npmcli/arborist/lib/audit-report.js +++ b/node_modules/@npmcli/arborist/lib/audit-report.js @@ -1,6 +1,7 @@ // an object representing the set of vulnerabilities in a tree /* eslint camelcase: "off" */ +const localeCompare = require('@isaacs/string-locale-compare')('en') const npa = require('npm-package-arg') const pickManifest = require('npm-pick-manifest') @@ -79,7 +80,7 @@ class AuditReport extends Map { } obj.vulnerabilities = vulnerabilities - .sort(([a], [b]) => a.localeCompare(b, 'en')) + .sort(([a], [b]) => localeCompare(a, b)) .reduce((set, [name, vuln]) => { set[name] = vuln return set diff --git a/node_modules/@npmcli/arborist/lib/can-place-dep.js b/node_modules/@npmcli/arborist/lib/can-place-dep.js index 7e2e1a0e2d29b..6be59093c034f 100644 --- a/node_modules/@npmcli/arborist/lib/can-place-dep.js +++ b/node_modules/@npmcli/arborist/lib/can-place-dep.js @@ -35,6 +35,7 @@ // then we will return REPLACE rather than CONFLICT, and Arborist will queue // the replaced node for resolution elsewhere. +const localeCompare = require('@isaacs/string-locale-compare')('en') const semver = require('semver') const debug = require('./debug.js') const peerEntrySets = require('./peer-entry-sets.js') @@ -79,7 +80,7 @@ class CanPlaceDep { this._treeSnapshot = JSON.stringify([...target.root.inventory.entries()] .map(([loc, {packageName, version, resolved}]) => { return [loc, packageName, version, resolved] - }).sort(([a], [b]) => a.localeCompare(b, 'en'))) + }).sort(([a], [b]) => localeCompare(a, b))) }) // the result of whether we can place it or not @@ -119,7 +120,7 @@ class CanPlaceDep { const treeSnapshot = JSON.stringify([...target.root.inventory.entries()] .map(([loc, {packageName, version, resolved}]) => { return [loc, packageName, version, resolved] - }).sort(([a], [b]) => a.localeCompare(b, 'en'))) + }).sort(([a], [b]) => localeCompare(a, b))) /* istanbul ignore if */ if (this._treeSnapshot !== treeSnapshot) { throw Object.assign(new Error('tree changed in CanPlaceDep'), { diff --git a/node_modules/@npmcli/arborist/lib/place-dep.js b/node_modules/@npmcli/arborist/lib/place-dep.js index d7cc7d935afc8..6edd94a38e749 100644 --- a/node_modules/@npmcli/arborist/lib/place-dep.js +++ b/node_modules/@npmcli/arborist/lib/place-dep.js @@ -7,6 +7,7 @@ // and saves a set of what was placed and what needs re-evaluation as // a result. +const localeCompare = require('@isaacs/string-locale-compare')('en') const log = require('proc-log') const deepestNestingTarget = require('./deepest-nesting-target.js') const CanPlaceDep = require('./can-place-dep.js') @@ -63,6 +64,8 @@ class PlaceDep { this.parent = parent this.peerConflict = null + this.needEvaluation = new Set() + this.checks = new Map() this.place() @@ -365,6 +368,8 @@ class PlaceDep { } replaceOldDep () { + const target = this.oldDep.parent + // XXX handle replacing an entire peer group? // what about cases where we need to push some other peer groups deeper // into the tree? all the tree updating should be done here, and track @@ -383,8 +388,47 @@ class PlaceDep { oldDeps.push(...gatherDepSet([edge.to], e => e.to !== edge.to)) } } + + // gather all peer edgesIn which are at this level, and will not be + // satisfied by the new dependency. Those are the peer sets that need + // to be either warned about (if they cannot go deeper), or removed and + // re-placed (if they can). + const prunePeerSets = [] + for (const edge of this.oldDep.edgesIn) { + if (this.placed.satisfies(edge) || + !edge.peer || + edge.from.parent !== target || + edge.overridden) { + // not a peer dep, not invalid, or not from this level, so it's fine + // to just let it re-evaluate as a problemEdge later, or let it be + // satisfied by the new dep being placed. + continue + } + for (const entryEdge of peerEntrySets(edge.from).keys()) { + // either this one needs to be pruned and re-evaluated, or marked + // as overridden and warned about. If the entryEdge comes in from + // the root, then we have to leave it alone, and in that case, it + // will have already warned or crashed by getting to this point. + const entryNode = entryEdge.to + const deepestTarget = deepestNestingTarget(entryNode) + if (deepestTarget !== target && !entryEdge.from.isRoot) { + prunePeerSets.push(...gatherDepSet([entryNode], e => { + return e.to !== entryNode && !e.overridden + })) + } else { + this.warnPeerConflict(edge, this.dep) + } + } + } + this.placed.replace(this.oldDep) this.pruneForReplacement(this.placed, oldDeps) + for (const dep of prunePeerSets) { + for (const edge of dep.edgesIn) { + this.needEvaluation.add(edge.from) + } + dep.root = null + } } pruneForReplacement (node, oldDeps) { @@ -430,7 +474,7 @@ class PlaceDep { // sort these so that they're deterministically ordered // otherwise, resulting tree shape is dependent on the order // in which they happened to be resolved. - const nodeSort = (a, b) => a.location.localeCompare(b.location, 'en') + const nodeSort = (a, b) => localeCompare(a.location, b.location) const children = [...node.children.values()].sort(nodeSort) for (const child of children) { @@ -485,19 +529,22 @@ class PlaceDep { return false } - warnPeerConflict () { - this.edge.overridden = true - const expl = this.explainPeerConflict() + warnPeerConflict (edge, dep) { + edge = edge || this.edge + dep = dep || this.dep + edge.overridden = true + const expl = this.explainPeerConflict(edge, dep) log.warn('ERESOLVE', 'overriding peer dependency', expl) } - failPeerConflict () { - const expl = this.explainPeerConflict() + failPeerConflict (edge, dep) { + edge = edge || this.top.edge + dep = dep || this.top.dep + const expl = this.explainPeerConflict(edge, dep) throw Object.assign(new Error('could not resolve'), expl) } - explainPeerConflict () { - const { edge, dep } = this.top + explainPeerConflict (edge, dep) { const { from: node } = edge const curNode = node.resolve(edge.name) diff --git a/node_modules/@npmcli/arborist/lib/printable.js b/node_modules/@npmcli/arborist/lib/printable.js index af24ccb959288..74925d96d2587 100644 --- a/node_modules/@npmcli/arborist/lib/printable.js +++ b/node_modules/@npmcli/arborist/lib/printable.js @@ -1,6 +1,7 @@ // helper function to output a clearer visualization // of the current node and its descendents +const localeCompare = require('@isaacs/string-locale-compare')('en') const util = require('util') const relpath = require('./relpath.js') @@ -67,14 +68,14 @@ class ArboristNode { // edgesOut sorted by name if (tree.edgesOut.size) { this.edgesOut = new Map([...tree.edgesOut.entries()] - .sort(([a], [b]) => a.localeCompare(b, 'en')) + .sort(([a], [b]) => localeCompare(a, b)) .map(([name, edge]) => [name, new EdgeOut(edge)])) } // edgesIn sorted by location if (tree.edgesIn.size) { this.edgesIn = new Set([...tree.edgesIn] - .sort((a, b) => a.from.location.localeCompare(b.from.location, 'en')) + .sort((a, b) => localeCompare(a.from.location, b.from.location)) .map(edge => new EdgeIn(edge))) } @@ -86,14 +87,14 @@ class ArboristNode { // fsChildren sorted by path if (tree.fsChildren.size) { this.fsChildren = new Set([...tree.fsChildren] - .sort(({path: a}, {path: b}) => a.localeCompare(b, 'en')) + .sort(({path: a}, {path: b}) => localeCompare(a, b)) .map(tree => printableTree(tree, path))) } // children sorted by name if (tree.children.size) { this.children = new Map([...tree.children.entries()] - .sort(([a], [b]) => a.localeCompare(b, 'en')) + .sort(([a], [b]) => localeCompare(a, b)) .map(([name, tree]) => [name, printableTree(tree, path)])) } } diff --git a/node_modules/@npmcli/arborist/lib/shrinkwrap.js b/node_modules/@npmcli/arborist/lib/shrinkwrap.js index 6e7e0e31f166b..ed28130249ea0 100644 --- a/node_modules/@npmcli/arborist/lib/shrinkwrap.js +++ b/node_modules/@npmcli/arborist/lib/shrinkwrap.js @@ -9,6 +9,7 @@ // We cannot bump to v3 until npm v6 is out of common usage, and // definitely not before npm v8. +const localeCompare = require('@isaacs/string-locale-compare')('en') const lockfileVersion = 2 // for comparing nodes to yarn.lock entries @@ -911,7 +912,7 @@ class Shrinkwrap { /* istanbul ignore next - sort calling order is indeterminate */ return aloc.length > bloc.length ? 1 : bloc.length > aloc.length ? -1 - : aloc[aloc.length - 1].localeCompare(bloc[bloc.length - 1], 'en') + : localeCompare(aloc[aloc.length - 1], bloc[bloc.length - 1]) })[0] const res = consistentResolve(node.resolved, this.path, this.path, true) diff --git a/node_modules/@npmcli/arborist/lib/vuln.js b/node_modules/@npmcli/arborist/lib/vuln.js index da44e7c34d63c..a818cf318eb89 100644 --- a/node_modules/@npmcli/arborist/lib/vuln.js +++ b/node_modules/@npmcli/arborist/lib/vuln.js @@ -14,6 +14,7 @@ const {satisfies, simplifyRange} = require('semver') const semverOpt = { loose: true, includePrerelease: true } +const localeCompare = require('@isaacs/string-locale-compare')('en') const npa = require('npm-package-arg') const _range = Symbol('_range') const _simpleRange = Symbol('_simpleRange') @@ -81,6 +82,17 @@ class Vuln { } } + get isDirect () { + for (const node of this.nodes.values()) { + for (const edge of node.edgesIn) { + if (edge.from.isProjectRoot || edge.from.isWorkspace) { + return true + } + } + } + return false + } + testSpec (spec) { const specObj = npa(spec) if (!specObj.registry) { @@ -100,10 +112,10 @@ class Vuln { } toJSON () { - // sort so that they're always in a consistent order return { name: this.name, severity: this.severity, + isDirect: this.isDirect, // just loop over the advisories, since via is only Vuln objects, // and calculated advisories have all the info we need via: [...this.advisories].map(v => v.type === 'metavuln' ? v.dependency : { @@ -112,12 +124,10 @@ class Vuln { vulnerableVersions: undefined, id: undefined, }).sort((a, b) => - String(a.source || a).localeCompare(String(b.source || b, 'en'))), - effects: [...this.effects].map(v => v.name) - .sort(/* istanbul ignore next */(a, b) => a.localeCompare(b, 'en')), + localeCompare(String(a.source || a), String(b.source || b))), + effects: [...this.effects].map(v => v.name).sort(localeCompare), range: this.simpleRange, - nodes: [...this.nodes].map(n => n.location) - .sort(/* istanbul ignore next */(a, b) => a.localeCompare(b, 'en')), + nodes: [...this.nodes].map(n => n.location).sort(localeCompare), fixAvailable: this[_fixAvailable], } } diff --git a/node_modules/@npmcli/arborist/lib/yarn-lock.js b/node_modules/@npmcli/arborist/lib/yarn-lock.js index 384ba447d72fa..1eed0664083ac 100644 --- a/node_modules/@npmcli/arborist/lib/yarn-lock.js +++ b/node_modules/@npmcli/arborist/lib/yarn-lock.js @@ -28,13 +28,14 @@ // is an impenetrable 10kloc of webpack flow output, which is overkill // for something relatively simple and tailored to Arborist's use case. +const localeCompare = require('@isaacs/string-locale-compare')('en') const consistentResolve = require('./consistent-resolve.js') const {dirname} = require('path') const {breadth} = require('treeverse') // sort a key/value object into a string of JSON stringified keys and vals const sortKV = obj => Object.keys(obj) - .sort((a, b) => a.localeCompare(b, 'en')) + .sort(localeCompare) .map(k => ` ${JSON.stringify(k)} ${JSON.stringify(obj[k])}`) .join('\n') @@ -170,7 +171,7 @@ class YarnLock { toString () { return prefix + [...new Set([...this.entries.values()])] .map(e => e.toString()) - .sort((a, b) => a.localeCompare(b, 'en')).join('\n\n') + '\n' + .sort(localeCompare).join('\n\n') + '\n' } fromTree (tree) { @@ -180,7 +181,7 @@ class YarnLock { tree, visit: node => this.addEntryFromNode(node), getChildren: node => [...node.children.values(), ...node.fsChildren] - .sort((a, b) => a.depth - b.depth || a.name.localeCompare(b.name, 'en')), + .sort((a, b) => a.depth - b.depth || localeCompare(a.name, b.name)), }) return this } @@ -188,7 +189,7 @@ class YarnLock { addEntryFromNode (node) { const specs = [...node.edgesIn] .map(e => `${node.name}@${e.spec}`) - .sort((a, b) => a.localeCompare(b, 'en')) + .sort(localeCompare) // Note: // yarn will do excessive duplication in a case like this: @@ -321,7 +322,7 @@ class YarnLockEntry { toString () { // sort objects to the bottom, then alphabetical return ([...this[_specs]] - .sort((a, b) => a.localeCompare(b, 'en')) + .sort(localeCompare) .map(JSON.stringify).join(', ') + ':\n' + Object.getOwnPropertyNames(this) @@ -330,7 +331,7 @@ class YarnLockEntry { (a, b) => /* istanbul ignore next - sort call order is unpredictable */ (typeof this[a] === 'object') === (typeof this[b] === 'object') - ? a.localeCompare(b, 'en') + ? localeCompare(a, b) : typeof this[a] === 'object' ? 1 : -1) .map(prop => typeof this[prop] !== 'object' diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 5d0e31af975d2..b39818d481503 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,8 +1,9 @@ { "name": "@npmcli/arborist", - "version": "2.8.3", + "version": "2.9.0", "description": "Manage node_modules trees", "dependencies": { + "@isaacs/string-locale-compare": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", "@npmcli/metavuln-calculator": "^1.1.0", diff --git a/package-lock.json b/package-lock.json index b9181803c103a..246b517ab3764 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,7 +84,7 @@ "packages/*" ], "dependencies": { - "@npmcli/arborist": "^2.8.3", + "@npmcli/arborist": "^2.9.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", "@npmcli/map-workspaces": "^1.0.4", @@ -618,6 +618,12 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@isaacs/string-locale-compare": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.0.1.tgz", + "integrity": "sha512-AknEkBKSyAcIpl7SIUp12bs1rOmTDp9ojfDI9hvXl6qHqUCcaswkZOslbfdEbzI+8OPatiixY9AFKaUUpgGoBw==", + "inBundle": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -764,11 +770,12 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.8.3.tgz", - "integrity": "sha512-miFcxbZjmQqeFTeRSLLh+lc/gxIKDO5L4PVCp+dp+kmcwJmYsEJmF7YvHR2yi3jF+fxgvLf3CCFzboPIXAuabg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.9.0.tgz", + "integrity": "sha512-21DTow2xC0GlkowlE4zOu99UY21nSymW14fHZmB0yeAqhagmttJPmCUZXU+ngJmJ/Dwe5YP9QJUTgEVRLqnwcg==", "inBundle": true, "dependencies": { + "@isaacs/string-locale-compare": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", "@npmcli/metavuln-calculator": "^1.1.0", @@ -10900,6 +10907,11 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@isaacs/string-locale-compare": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.0.1.tgz", + "integrity": "sha512-AknEkBKSyAcIpl7SIUp12bs1rOmTDp9ojfDI9hvXl6qHqUCcaswkZOslbfdEbzI+8OPatiixY9AFKaUUpgGoBw==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -11010,10 +11022,11 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.8.3.tgz", - "integrity": "sha512-miFcxbZjmQqeFTeRSLLh+lc/gxIKDO5L4PVCp+dp+kmcwJmYsEJmF7YvHR2yi3jF+fxgvLf3CCFzboPIXAuabg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.9.0.tgz", + "integrity": "sha512-21DTow2xC0GlkowlE4zOu99UY21nSymW14fHZmB0yeAqhagmttJPmCUZXU+ngJmJ/Dwe5YP9QJUTgEVRLqnwcg==", "requires": { + "@isaacs/string-locale-compare": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", "@npmcli/metavuln-calculator": "^1.1.0", diff --git a/package.json b/package.json index 6ff77fdb35836..912600026eb8e 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.8.3", + "@npmcli/arborist": "^2.9.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", "@npmcli/map-workspaces": "^1.0.4", From dbb90f7997900b8ae6026dddaa718efe9a1db2f4 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 28 Sep 2021 11:18:01 -0700 Subject: [PATCH 05/14] fix: use Intl.Collator for string sorting when available The npm/cli form of https://github.com/npm/arborist/pull/324 Required adding options support to package used for this. PR-URL: https://github.com/npm/cli/pull/3809 Credit: @isaacs Close: #3809 Reviewed-by: @wraithgar --- lib/cache.js | 5 +-- lib/config.js | 5 +-- lib/help.js | 3 +- lib/ls.js | 4 +-- lib/outdated.js | 3 +- lib/utils/completion/installed-deep.js | 4 +-- lib/utils/config/describe-all.js | 3 +- lib/utils/npm-usage.js | 3 +- lib/utils/tar.js | 11 +++--- .../@isaacs/string-locale-compare/index.js | 36 ++++++++++++++----- .../string-locale-compare/package.json | 2 +- package-lock.json | 13 +++---- package.json | 1 + 13 files changed, 60 insertions(+), 33 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index aed2cce31e63f..4a5665111949f 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -8,6 +8,7 @@ const semver = require('semver') const BaseCommand = require('./base-command.js') const npa = require('npm-package-arg') const jsonParse = require('json-parse-even-better-errors') +const localeCompare = require('@isaacs/string-locale-compare')('en') const searchCachePackage = async (path, spec, cacheKeys) => { const parsed = npa(spec) @@ -212,10 +213,10 @@ class Cache extends BaseCommand { for (const key of keySet) results.add(key) } - [...results].sort((a, b) => a.localeCompare(b, 'en')).forEach(key => this.npm.output(key)) + [...results].sort(localeCompare).forEach(key => this.npm.output(key)) return } - cacheKeys.sort((a, b) => a.localeCompare(b, 'en')).forEach(key => this.npm.output(key)) + cacheKeys.sort(localeCompare).forEach(key => this.npm.output(key)) } } diff --git a/lib/config.js b/lib/config.js index 2df7bf513437c..a1f706d930e6a 100644 --- a/lib/config.js +++ b/lib/config.js @@ -10,6 +10,7 @@ const writeFile = promisify(fs.writeFile) const { spawn } = require('child_process') const { EOL } = require('os') const ini = require('ini') +const localeCompare = require('@isaacs/string-locale-compare')('en') // take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into // { key: value, k2: v2, k3: v3 } @@ -209,7 +210,7 @@ class Config extends BaseCommand { ; Configs like \`///:_authToken\` are auth that is restricted ; to the registry host specified. -${data.split('\n').sort((a, b) => a.localeCompare(b, 'en')).join('\n').trim()} +${data.split('\n').sort(localeCompare).join('\n').trim()} ;;;; ; all available options shown below with default values @@ -238,7 +239,7 @@ ${defData} if (where === 'default' && !long) continue - const keys = Object.keys(data).sort((a, b) => a.localeCompare(b, 'en')) + const keys = Object.keys(data).sort(localeCompare) if (!keys.length) continue diff --git a/lib/help.js b/lib/help.js index 8e4ff67bc284c..9a6f950e05953 100644 --- a/lib/help.js +++ b/lib/help.js @@ -3,6 +3,7 @@ const path = require('path') const openUrl = require('./utils/open-url.js') const { promisify } = require('util') const glob = promisify(require('glob')) +const localeCompare = require('@isaacs/string-locale-compare')('en') const BaseCommand = require('./base-command.js') @@ -82,7 +83,7 @@ class Help extends BaseCommand { if (aManNumber !== bManNumber) return aManNumber - bManNumber - return a.localeCompare(b, 'en') + return localeCompare(a, b) }) const man = mans[0] diff --git a/lib/ls.js b/lib/ls.js index 7e41892c53442..495b6348a0360 100644 --- a/lib/ls.js +++ b/lib/ls.js @@ -22,6 +22,7 @@ const _problems = Symbol('problems') const _required = Symbol('required') const _type = Symbol('type') const ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js') +const localeCompare = require('@isaacs/string-locale-compare')('en') class LS extends ArboristWorkspaceCmd { /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -503,8 +504,7 @@ const augmentNodesWithMetadata = ({ return node } -const sortAlphabetically = (a, b) => - a.pkgid.localeCompare(b.pkgid, 'en') +const sortAlphabetically = ({ pkgid: a }, { pkgid: b }) => localeCompare(a, b) const humanOutput = ({ color, result, seenItems, unicode }) => { // we need to traverse the entire tree in order to determine which items diff --git a/lib/outdated.js b/lib/outdated.js index a5be13cdaf108..b3b630421c488 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -6,6 +6,7 @@ const color = require('chalk') const styles = require('ansistyles') const npa = require('npm-package-arg') const pickManifest = require('npm-pick-manifest') +const localeCompare = require('@isaacs/string-locale-compare')('en') const Arborist = require('@npmcli/arborist') @@ -85,7 +86,7 @@ class Outdated extends ArboristWorkspaceCmd { })) // sorts list alphabetically - const outdated = this.list.sort((a, b) => a.name.localeCompare(b.name, 'en')) + const outdated = this.list.sort((a, b) => localeCompare(a.name, b.name)) if (outdated.length > 0) process.exitCode = 1 diff --git a/lib/utils/completion/installed-deep.js b/lib/utils/completion/installed-deep.js index 2430688612cd4..590955a1ee520 100644 --- a/lib/utils/completion/installed-deep.js +++ b/lib/utils/completion/installed-deep.js @@ -1,5 +1,6 @@ const { resolve } = require('path') const Arborist = require('@npmcli/arborist') +const localeCompare = require('@isaacs/string-locale-compare')('en') const installedDeep = async (npm) => { const { @@ -15,8 +16,7 @@ const installedDeep = async (npm) => { return i }) .filter(i => (i.depth - 1) <= depth) - .sort((a, b) => a.depth - b.depth) - .sort((a, b) => a.depth === b.depth ? a.name.localeCompare(b.name, 'en') : 0) + .sort((a, b) => (a.depth - b.depth) || localeCompare(a.name, b.name)) const res = new Set() const gArb = new Arborist({ global: true, path: resolve(npm.globalDir, '..') }) diff --git a/lib/utils/config/describe-all.js b/lib/utils/config/describe-all.js index c8a973cc0fd21..23a10ae97783c 100644 --- a/lib/utils/config/describe-all.js +++ b/lib/utils/config/describe-all.js @@ -1,4 +1,5 @@ const definitions = require('./definitions.js') +const localeCompare = require('@isaacs/string-locale-compare')('en') const describeAll = () => { // sort not-deprecated ones to the top /* istanbul ignore next - typically already sorted in the definitions file, @@ -7,7 +8,7 @@ const describeAll = () => { const sort = ([keya, {deprecated: depa}], [keyb, {deprecated: depb}]) => { return depa && !depb ? 1 : !depa && depb ? -1 - : keya.localeCompare(keyb, 'en') + : localeCompare(keya, keyb) } return Object.entries(definitions).sort(sort) .map(([key, def]) => def.describe()) diff --git a/lib/utils/npm-usage.js b/lib/utils/npm-usage.js index ddb0bab0bc9a2..f6785867c0ae5 100644 --- a/lib/utils/npm-usage.js +++ b/lib/utils/npm-usage.js @@ -1,5 +1,6 @@ const { dirname } = require('path') const { cmdList } = require('./cmd-list') +const localeCompare = require('@isaacs/string-locale-compare')('en') module.exports = (npm) => { const usesBrowser = npm.config.get('viewer') === 'browser' @@ -62,7 +63,7 @@ const usages = (npm) => { maxLen = Math.max(maxLen, c.length) return set }, []) - .sort((a, b) => a[0].localeCompare(b[0], 'en')) + .sort(([a], [b]) => localeCompare(a, b)) .map(([c, usage]) => `\n ${c}${' '.repeat(maxLen - c.length + 1)}${ (usage.split('\n').join('\n' + ' '.repeat(maxLen + 5)))}`) .join('\n') diff --git a/lib/utils/tar.js b/lib/utils/tar.js index c3071c1bd47a5..0ff822370d4da 100644 --- a/lib/utils/tar.js +++ b/lib/utils/tar.js @@ -3,6 +3,10 @@ const ssri = require('ssri') const npmlog = require('npmlog') const formatBytes = require('./format-bytes.js') const columnify = require('columnify') +const localeCompare = require('@isaacs/string-locale-compare')('en', { + sensitivity: 'case', + numeric: true, +}) const logTar = (tarball, opts = {}) => { const { unicode = false, log = npmlog } = opts @@ -75,12 +79,7 @@ const getContents = async (manifest, tarball) => { algorithms: ['sha1', 'sha512'], }) - const comparator = (a, b) => { - return a.path.localeCompare(b.path, 'en', { - sensitivity: 'case', - numeric: true, - }) - } + const comparator = ({ path: a }, { path: b }) => localeCompare(a, b) const isUpper = (str) => { const ch = str.charAt(0) diff --git a/node_modules/@isaacs/string-locale-compare/index.js b/node_modules/@isaacs/string-locale-compare/index.js index a6cec27efb8ec..0f68ab6774e1a 100644 --- a/node_modules/@isaacs/string-locale-compare/index.js +++ b/node_modules/@isaacs/string-locale-compare/index.js @@ -2,21 +2,41 @@ const hasIntl = typeof Intl === 'object' && !!Intl const Collator = hasIntl && Intl.Collator const cache = new Map() -const collatorCompare = locale => { - const collator = new Collator(locale) +const collatorCompare = (locale, opts) => { + const collator = new Collator(locale, opts) return (a, b) => collator.compare(a, b) } -const localeCompare = locale => (a, b) => a.localeCompare(b, locale) +const localeCompare = (locale, opts) => (a, b) => a.localeCompare(b, locale, opts) -module.exports = locale => { +const knownOptions = [ + 'sensitivity', + 'numeric', + 'ignorePunctuation', + 'caseFirst', +] + +const { hasOwnProperty } = Object.prototype + +module.exports = (locale, options = {}) => { if (!locale || typeof locale !== 'string') throw new TypeError('locale required') - if (cache.has(locale)) - return cache.get(locale) + const opts = knownOptions.reduce((opts, k) => { + if (hasOwnProperty.call(options, k)) { + opts[k] = options[k] + } + return opts + }, {}) + const key = `${locale}\n${JSON.stringify(opts)}` + + if (cache.has(key)) + return cache.get(key) + + const compare = hasIntl + ? collatorCompare(locale, opts) + : localeCompare(locale, opts) + cache.set(key, compare) - const compare = hasIntl ? collatorCompare(locale) : localeCompare(locale) - cache.set(locale, compare) return compare } diff --git a/node_modules/@isaacs/string-locale-compare/package.json b/node_modules/@isaacs/string-locale-compare/package.json index a322c1c92d3cc..58de848a00377 100644 --- a/node_modules/@isaacs/string-locale-compare/package.json +++ b/node_modules/@isaacs/string-locale-compare/package.json @@ -1,6 +1,6 @@ { "name": "@isaacs/string-locale-compare", - "version": "1.0.1", + "version": "1.1.0", "files": [ "index.js" ], diff --git a/package-lock.json b/package-lock.json index 246b517ab3764..e8cc960ecfec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -84,6 +84,7 @@ "packages/*" ], "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^2.9.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", @@ -619,9 +620,9 @@ "dev": true }, "node_modules/@isaacs/string-locale-compare": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.0.1.tgz", - "integrity": "sha512-AknEkBKSyAcIpl7SIUp12bs1rOmTDp9ojfDI9hvXl6qHqUCcaswkZOslbfdEbzI+8OPatiixY9AFKaUUpgGoBw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "inBundle": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -10908,9 +10909,9 @@ "dev": true }, "@isaacs/string-locale-compare": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.0.1.tgz", - "integrity": "sha512-AknEkBKSyAcIpl7SIUp12bs1rOmTDp9ojfDI9hvXl6qHqUCcaswkZOslbfdEbzI+8OPatiixY9AFKaUUpgGoBw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==" }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", diff --git a/package.json b/package.json index 912600026eb8e..705fb779e2ad3 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "./package.json": "./package.json" }, "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^2.9.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", From f425950a6ca671b2df20703f70b59022c2858d4d Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 27 Sep 2021 15:25:48 -0400 Subject: [PATCH 06/14] docs: remove npm Enterprise from documentation PR-URL: https://github.com/npm/cli/pull/3805 Credit: @ethomson Close: #3805 Reviewed-by: @lukekarrys --- docs/content/using-npm/scope.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/content/using-npm/scope.md b/docs/content/using-npm/scope.md index 62b4a6851641a..911d7ea5177c9 100644 --- a/docs/content/using-npm/scope.md +++ b/docs/content/using-npm/scope.md @@ -98,7 +98,8 @@ desired, with `npm access` or on the npmjs.com website. Scopes can be associated with a separate registry. This allows you to seamlessly use a mix of packages from the primary npm registry and one or more -private registries, such as npm Enterprise. +private registries, such as [GitHub Packages](https://github.com/features/packages) or the open source [Verdaccio](https://verdaccio.org) +project. You can associate a scope with a registry at login, e.g. From bb0b2da6c0275dd8c9eda894452ce45b2e8c4c51 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Tue, 31 Aug 2021 09:53:16 +0430 Subject: [PATCH 07/14] fix(docs): add note about workspace script order PR-URL: https://github.com/npm/cli/pull/3699 Credit: @behnammodi Close: #3699 Reviewed-by: @wraithgar --- docs/content/using-npm/workspaces.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/content/using-npm/workspaces.md b/docs/content/using-npm/workspaces.md index ae834c0cc7e22..baf84f543f017 100644 --- a/docs/content/using-npm/workspaces.md +++ b/docs/content/using-npm/workspaces.md @@ -176,6 +176,22 @@ npm run test --workspaces Will run the `test` script in both `./packages/a` and `./packages/b`. +Commands will be run in each workspace in the order they appear in your `package.json` + +``` +{ + "workspaces": [ "packages/a", "packages/b" ] +} +``` + +Order of run is different with: + +``` +{ + "workspaces": [ "packages/b", "packages/a" ] +} +``` + ### Ignoring missing scripts It is not required for all of the workspaces to implement scripts run with the `npm run` command. From 8349c3c1557ac52973ad08c10db492e3a5a30204 Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 30 Sep 2021 12:42:46 -0700 Subject: [PATCH 08/14] deps: arborist@2.10.0 * includeWorkspaceRoot support * workspacesEnabled=false support PR-URL: https://github.com/npm/cli/pull/3815 Credit: @wraithgar Close: #3815 Reviewed-by: @isaacs --- .../@npmcli/arborist/lib/arborist/audit.js | 11 ++++++- .../arborist/lib/arborist/build-ideal-tree.js | 20 +++++++++---- .../@npmcli/arborist/lib/arborist/index.js | 29 ++++++++++++++++++- .../@npmcli/arborist/lib/arborist/rebuild.js | 7 ++++- .../@npmcli/arborist/lib/arborist/reify.js | 16 +++++++++- node_modules/@npmcli/arborist/package.json | 2 +- package-lock.json | 14 ++++----- package.json | 2 +- 8 files changed, 83 insertions(+), 18 deletions(-) diff --git a/node_modules/@npmcli/arborist/lib/arborist/audit.js b/node_modules/@npmcli/arborist/lib/arborist/audit.js index c0cd79bb13e36..eb4a3565531cc 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/audit.js +++ b/node_modules/@npmcli/arborist/lib/arborist/audit.js @@ -5,6 +5,7 @@ const AuditReport = require('../audit-report.js') // shared with reify const _global = Symbol.for('global') const _workspaces = Symbol.for('workspaces') +const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot') module.exports = cls => class Auditor extends cls { async audit (options = {}) { @@ -23,7 +24,15 @@ module.exports = cls => class Auditor extends cls { process.emit('time', 'audit') const tree = await this.loadVirtual() if (this[_workspaces] && this[_workspaces].length) { - options.filterSet = this.workspaceDependencySet(tree, this[_workspaces]) + options.filterSet = this.workspaceDependencySet( + tree, + this[_workspaces], + this[_includeWorkspaceRoot] + ) + } + if (!options.workspacesEnabled) { + options.filterSet = + this.excludeWorkspacesDependencySet(tree) } this.auditReport = await AuditReport.load(tree, options) const ret = options.fix ? this.reify(options) : this.auditReport diff --git a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js index b7876b1147e10..3e6a9838f8f40 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -93,6 +93,7 @@ const _checkEngine = Symbol('checkEngine') const _checkPlatform = Symbol('checkPlatform') const _virtualRoots = Symbol('virtualRoots') const _virtualRoot = Symbol('virtualRoot') +const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot') const _failPeerConflict = Symbol('failPeerConflict') const _explainPeerConflict = Symbol('explainPeerConflict') @@ -115,12 +116,13 @@ module.exports = cls => class IdealTreeBuilder extends cls { options.registry = this.registry = registry.replace(/\/+$/, '') + '/' const { - idealTree = null, - global = false, follow = false, + force = false, + global = false, globalStyle = false, + idealTree = null, + includeWorkspaceRoot = false, legacyPeerDeps = false, - force = false, packageLock = true, strictPeerDeps = false, workspaces = [], @@ -162,6 +164,8 @@ module.exports = cls => class IdealTreeBuilder extends cls { // don't hold onto references for nodes that are garbage collected. this[_peerSetSource] = new WeakMap() this[_virtualRoots] = new Map() + + this[_includeWorkspaceRoot] = includeWorkspaceRoot } get explicitRequests () { @@ -394,8 +398,14 @@ module.exports = cls => class IdealTreeBuilder extends cls { if (!this[_workspaces].length) { await this[_applyUserRequestsToNode](tree, options) } else { - await Promise.all(this.workspaceNodes(tree, this[_workspaces]) - .map(node => this[_applyUserRequestsToNode](node, options))) + const nodes = this.workspaceNodes(tree, this[_workspaces]) + if (this[_includeWorkspaceRoot]) { + nodes.push(tree) + } + const appliedRequests = nodes.map( + node => this[_applyUserRequestsToNode](node, options) + ) + await Promise.all(appliedRequests) } process.emit('timeEnd', 'idealTree:userRequests') diff --git a/node_modules/@npmcli/arborist/lib/arborist/index.js b/node_modules/@npmcli/arborist/lib/arborist/index.js index d8ca67faa6065..ccfa7cad9c04d 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/index.js +++ b/node_modules/@npmcli/arborist/lib/arborist/index.js @@ -58,6 +58,7 @@ class Arborist extends Base { cache: options.cache || `${homedir()}/.npm/_cacache`, packumentCache: options.packumentCache || new Map(), log: options.log || procLog, + workspacesEnabled: options.workspacesEnabled !== false, } if (options.saveType && !saveTypeMap.get(options.saveType)) { throw new Error(`Invalid saveType ${options.saveType}`) @@ -73,8 +74,15 @@ class Arborist extends Base { } // returns a set of workspace nodes and all their deps - workspaceDependencySet (tree, workspaces) { + workspaceDependencySet (tree, workspaces, includeWorkspaceRoot) { const wsNodes = this.workspaceNodes(tree, workspaces) + if (includeWorkspaceRoot) { + for (const edge of tree.edgesOut.values()) { + if (edge.type !== 'workspace' && edge.to) { + wsNodes.push(edge.to) + } + } + } const set = new Set(wsNodes) const extraneous = new Set() for (const node of set) { @@ -96,6 +104,25 @@ class Arborist extends Base { for (const extra of extraneous) { set.add(extra) } + + return set + } + + excludeWorkspacesDependencySet (tree) { + const set = new Set() + for (const edge of tree.edgesOut.values()) { + if (edge.type !== 'workspace' && edge.to) { + set.add(edge.to) + } + } + for (const node of set) { + for (const edge of node.edgesOut.values()) { + if (edge.to) { + set.add(edge.to) + } + } + } + return set } } diff --git a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js index e48bdd76b538c..6fa5c00116fc2 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/rebuild.js +++ b/node_modules/@npmcli/arborist/lib/arborist/rebuild.js @@ -34,6 +34,7 @@ const _addToBuildSet = Symbol('addToBuildSet') const _checkBins = Symbol.for('checkBins') const _queues = Symbol('queues') const _scriptShell = Symbol('scriptShell') +const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot') const _force = Symbol.for('force') @@ -77,7 +78,11 @@ module.exports = cls => class Builder extends cls { if (!nodes) { const tree = await this.loadActual() if (this[_workspaces] && this[_workspaces].length) { - const filterSet = this.workspaceDependencySet(tree, this[_workspaces]) + const filterSet = this.workspaceDependencySet( + tree, + this[_workspaces], + this[_includeWorkspaceRoot] + ) nodes = tree.inventory.filter(node => filterSet.has(node)) } else { nodes = tree.inventory.values() diff --git a/node_modules/@npmcli/arborist/lib/arborist/reify.js b/node_modules/@npmcli/arborist/lib/arborist/reify.js index 3a9c479748864..a279d8956f243 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -83,6 +83,7 @@ const _validateNodeModules = Symbol('validateNodeModules') const _nmValidated = Symbol('nmValidated') const _validatePath = Symbol('validatePath') const _reifyPackages = Symbol.for('reifyPackages') +const _includeWorkspaceRoot = Symbol.for('includeWorkspaceRoot') const _omitDev = Symbol('omitDev') const _omitOptional = Symbol('omitOptional') @@ -340,6 +341,15 @@ module.exports = cls => class Reifier extends cls { filterNodes.push(actual) } } + if (this[_includeWorkspaceRoot] && (this[_workspaces].length > 0)) { + for (const tree of [this.idealTree, this.actualTree]) { + for (const {type, to} of tree.edgesOut.values()) { + if (type !== 'workspace' && to) { + filterNodes.push(to) + } + } + } + } } // find all the nodes that need to change between the actual @@ -901,7 +911,11 @@ module.exports = cls => class Reifier extends cls { // if we're operating on a workspace, only audit the workspace deps if (this[_workspaces] && this[_workspaces].length) { - options.filterSet = this.workspaceDependencySet(tree, this[_workspaces]) + options.filterSet = this.workspaceDependencySet( + tree, + this[_workspaces], + this[_includeWorkspaceRoot] + ) } this.auditReport = AuditReport.load(tree, options) diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index b39818d481503..f912d2af11590 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "2.9.0", + "version": "2.10.0", "description": "Manage node_modules trees", "dependencies": { "@isaacs/string-locale-compare": "^1.0.1", diff --git a/package-lock.json b/package-lock.json index e8cc960ecfec2..b8decbc1afedd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,7 +85,7 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^2.9.0", + "@npmcli/arborist": "^2.10.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", "@npmcli/map-workspaces": "^1.0.4", @@ -771,9 +771,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.9.0.tgz", - "integrity": "sha512-21DTow2xC0GlkowlE4zOu99UY21nSymW14fHZmB0yeAqhagmttJPmCUZXU+ngJmJ/Dwe5YP9QJUTgEVRLqnwcg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.10.0.tgz", + "integrity": "sha512-CLnD+zXG9oijEEzViimz8fbOoFVb7hoypiaf7p6giJhvYtrxLAyY3cZAMPIFQvsG731+02eMDp3LqVBNo7BaZA==", "inBundle": true, "dependencies": { "@isaacs/string-locale-compare": "^1.0.1", @@ -11023,9 +11023,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.9.0.tgz", - "integrity": "sha512-21DTow2xC0GlkowlE4zOu99UY21nSymW14fHZmB0yeAqhagmttJPmCUZXU+ngJmJ/Dwe5YP9QJUTgEVRLqnwcg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.10.0.tgz", + "integrity": "sha512-CLnD+zXG9oijEEzViimz8fbOoFVb7hoypiaf7p6giJhvYtrxLAyY3cZAMPIFQvsG731+02eMDp3LqVBNo7BaZA==", "requires": { "@isaacs/string-locale-compare": "^1.0.1", "@npmcli/installed-package-contents": "^1.0.7", diff --git a/package.json b/package.json index 705fb779e2ad3..31083ff0bf930 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ }, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^2.9.0", + "@npmcli/arborist": "^2.10.0", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.3.0", "@npmcli/map-workspaces": "^1.0.4", From f17dfa0ced7d8df9bb7baf378bb20d33175c8e8b Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 27 Jul 2021 15:38:07 -0700 Subject: [PATCH 09/14] feat(workspaces): --include-workspace-root Adds a new config item that includes the workspace root when running non-arborist commands (i.e. repo, version, publish). Arborist will need to be udpated to look for this flag to change its behavior to include the workspace root for its functions. This also changes --workspaces to a trinary, so that setting it to false will explicitly exclude workspaces altogether. This is also going to require an arborist change so that it ignores workspaces altogether. Co-author: @fritzy PR-URL: https://github.com/npm/cli/pull/3816 Credit: @isaacs Close: #3816 Reviewed-by: @wraithgar --- docs/content/commands/npm-audit.md | 32 ++++- docs/content/commands/npm-dedupe.md | 32 ++++- docs/content/commands/npm-diff.md | 32 ++++- docs/content/commands/npm-dist-tag.md | 32 ++++- docs/content/commands/npm-docs.md | 32 ++++- docs/content/commands/npm-exec.md | 32 ++++- docs/content/commands/npm-explain.md | 4 +- docs/content/commands/npm-find-dupes.md | 32 ++++- docs/content/commands/npm-fund.md | 4 +- docs/content/commands/npm-init.md | 18 ++- docs/content/commands/npm-install-test.md | 32 ++++- docs/content/commands/npm-install.md | 32 ++++- docs/content/commands/npm-link.md | 32 ++++- docs/content/commands/npm-ls.md | 32 ++++- docs/content/commands/npm-outdated.md | 4 +- docs/content/commands/npm-pack.md | 32 ++++- docs/content/commands/npm-pkg.md | 18 ++- docs/content/commands/npm-prune.md | 32 ++++- docs/content/commands/npm-publish.md | 32 ++++- docs/content/commands/npm-rebuild.md | 32 ++++- docs/content/commands/npm-repo.md | 32 ++++- docs/content/commands/npm-run-script.md | 32 ++++- docs/content/commands/npm-set-script.md | 32 ++++- docs/content/commands/npm-uninstall.md | 32 ++++- docs/content/commands/npm-unpublish.md | 18 ++- docs/content/commands/npm-update.md | 32 ++++- docs/content/commands/npm-version.md | 32 ++++- docs/content/commands/npm-view.md | 32 ++++- docs/content/using-npm/config.md | 32 ++++- lib/base-command.js | 10 +- lib/diff.js | 1 + lib/dist-tag.js | 2 +- lib/docs.js | 8 +- lib/exec.js | 8 +- lib/explain.js | 9 +- lib/fund.js | 1 + lib/init.js | 4 + lib/link.js | 1 + lib/ls.js | 9 +- lib/npm.js | 12 ++ lib/outdated.js | 9 +- lib/pack.js | 1 + lib/publish.js | 10 +- lib/repo.js | 10 +- lib/run-script.js | 1 + lib/set-script.js | 2 +- lib/utils/completion/installed-deep.js | 9 +- lib/utils/config/definitions.js | 41 +++++- lib/version.js | 1 + lib/view.js | 1 + lib/workspaces/arborist-cmd.js | 7 +- lib/workspaces/get-workspaces.js | 9 +- package-lock.json | 1 + package.json | 1 + tap-snapshots/test/lib/config.js.test.cjs | 6 +- tap-snapshots/test/lib/dist-tag.js.test.cjs | 12 +- tap-snapshots/test/lib/init.js.test.cjs | 4 + .../test/lib/load-all-commands.js.test.cjs | 46 +++---- tap-snapshots/test/lib/ls.js.test.cjs | 6 + tap-snapshots/test/lib/outdated.js.test.cjs | 8 ++ tap-snapshots/test/lib/publish.js.test.cjs | 2 +- .../lib/utils/config/definitions.js.test.cjs | 32 ++++- .../lib/utils/config/describe-all.js.test.cjs | 32 ++++- .../test/lib/utils/npm-usage.js.test.cjs | 46 +++---- test/lib/explain.js | 79 +++++++++++- test/lib/init.js | 23 ++++ test/lib/ls.js | 20 +++ test/lib/npm.js | 38 ++++++ test/lib/outdated.js | 29 +++++ test/lib/publish.js | 1 + test/lib/repo.js | 118 +++++++++--------- test/lib/utils/completion/installed-deep.js | 1 + test/lib/utils/config/definitions.js | 17 +++ test/lib/workspaces/get-workspaces.js | 11 ++ 74 files changed, 1219 insertions(+), 282 deletions(-) diff --git a/docs/content/commands/npm-audit.md b/docs/content/commands/npm-audit.md index 9fa8c0fcc6f8c..58c614d793db2 100644 --- a/docs/content/commands/npm-audit.md +++ b/docs/content/commands/npm-audit.md @@ -313,8 +313,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -327,17 +327,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-dedupe.md b/docs/content/commands/npm-dedupe.md index dcb2a98fe9fd1..377e17d814a6f 100644 --- a/docs/content/commands/npm-dedupe.md +++ b/docs/content/commands/npm-dedupe.md @@ -247,8 +247,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -261,17 +261,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-diff.md b/docs/content/commands/npm-diff.md index cf0bbec15f46f..8d05df779f3ca 100644 --- a/docs/content/commands/npm-diff.md +++ b/docs/content/commands/npm-diff.md @@ -286,8 +286,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -300,17 +300,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ## See Also diff --git a/docs/content/commands/npm-dist-tag.md b/docs/content/commands/npm-dist-tag.md index 4ad68ff56d067..a4e0243aac87b 100644 --- a/docs/content/commands/npm-dist-tag.md +++ b/docs/content/commands/npm-dist-tag.md @@ -106,8 +106,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -120,17 +120,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-docs.md b/docs/content/commands/npm-docs.md index 9f1387daef8ed..970d17aa829c6 100644 --- a/docs/content/commands/npm-docs.md +++ b/docs/content/commands/npm-docs.md @@ -63,8 +63,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -77,17 +77,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-exec.md b/docs/content/commands/npm-exec.md index 82908d65e01d3..db23536628e47 100644 --- a/docs/content/commands/npm-exec.md +++ b/docs/content/commands/npm-exec.md @@ -164,8 +164,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -178,17 +178,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### Examples diff --git a/docs/content/commands/npm-explain.md b/docs/content/commands/npm-explain.md index 4a8af2f70003e..5f05cac0f906b 100644 --- a/docs/content/commands/npm-explain.md +++ b/docs/content/commands/npm-explain.md @@ -85,8 +85,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 diff --git a/docs/content/commands/npm-find-dupes.md b/docs/content/commands/npm-find-dupes.md index e9dbac6d18148..f7dc84f9c5306 100644 --- a/docs/content/commands/npm-find-dupes.md +++ b/docs/content/commands/npm-find-dupes.md @@ -174,8 +174,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -188,17 +188,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-fund.md b/docs/content/commands/npm-fund.md index 83c0b13d8cbb4..606b0a188c554 100644 --- a/docs/content/commands/npm-fund.md +++ b/docs/content/commands/npm-fund.md @@ -122,8 +122,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 diff --git a/docs/content/commands/npm-init.md b/docs/content/commands/npm-init.md index 7355c75884b8f..d5a02494fe49f 100644 --- a/docs/content/commands/npm-init.md +++ b/docs/content/commands/npm-init.md @@ -200,8 +200,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -214,12 +214,20 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. diff --git a/docs/content/commands/npm-install-test.md b/docs/content/commands/npm-install-test.md index c2f9ab3d2f46c..c464e5bd0b8c6 100644 --- a/docs/content/commands/npm-install-test.md +++ b/docs/content/commands/npm-install-test.md @@ -241,8 +241,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -255,17 +255,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-install.md b/docs/content/commands/npm-install.md index de73787181a50..a103845d1a675 100644 --- a/docs/content/commands/npm-install.md +++ b/docs/content/commands/npm-install.md @@ -625,8 +625,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -639,17 +639,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### Algorithm diff --git a/docs/content/commands/npm-link.md b/docs/content/commands/npm-link.md index 5811a8b23b9a5..d4ef41ae96462 100644 --- a/docs/content/commands/npm-link.md +++ b/docs/content/commands/npm-link.md @@ -325,8 +325,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -339,17 +339,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-ls.md b/docs/content/commands/npm-ls.md index 528140bee95a9..3b33f0a3605e0 100644 --- a/docs/content/commands/npm-ls.md +++ b/docs/content/commands/npm-ls.md @@ -227,8 +227,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -241,17 +241,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-outdated.md b/docs/content/commands/npm-outdated.md index 8eb5528c3d0f4..1b58a6afda64b 100644 --- a/docs/content/commands/npm-outdated.md +++ b/docs/content/commands/npm-outdated.md @@ -167,8 +167,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 diff --git a/docs/content/commands/npm-pack.md b/docs/content/commands/npm-pack.md index dda30512db8e3..53945986837b9 100644 --- a/docs/content/commands/npm-pack.md +++ b/docs/content/commands/npm-pack.md @@ -69,8 +69,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -83,17 +83,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### Description diff --git a/docs/content/commands/npm-pkg.md b/docs/content/commands/npm-pkg.md index bf6a2df5da6bc..beee9c1c4e78a 100644 --- a/docs/content/commands/npm-pkg.md +++ b/docs/content/commands/npm-pkg.md @@ -223,8 +223,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -237,12 +237,20 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. diff --git a/docs/content/commands/npm-prune.md b/docs/content/commands/npm-prune.md index 7bd7ad1311323..658ab2610e0ed 100644 --- a/docs/content/commands/npm-prune.md +++ b/docs/content/commands/npm-prune.md @@ -103,8 +103,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -117,17 +117,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-publish.md b/docs/content/commands/npm-publish.md index 946109de404c2..6958b1066de7f 100644 --- a/docs/content/commands/npm-publish.md +++ b/docs/content/commands/npm-publish.md @@ -188,8 +188,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -202,17 +202,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-rebuild.md b/docs/content/commands/npm-rebuild.md index 19684796f8f53..75e71c60e6810 100644 --- a/docs/content/commands/npm-rebuild.md +++ b/docs/content/commands/npm-rebuild.md @@ -89,8 +89,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -103,17 +103,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-repo.md b/docs/content/commands/npm-repo.md index c3c509e0b4b98..cd47fde47127e 100644 --- a/docs/content/commands/npm-repo.md +++ b/docs/content/commands/npm-repo.md @@ -50,8 +50,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -64,17 +64,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-run-script.md b/docs/content/commands/npm-run-script.md index e436bc27f9d2a..6dd602d03e00a 100644 --- a/docs/content/commands/npm-run-script.md +++ b/docs/content/commands/npm-run-script.md @@ -152,8 +152,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -166,17 +166,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + #### `if-present` * Default: false diff --git a/docs/content/commands/npm-set-script.md b/docs/content/commands/npm-set-script.md index e39b7a18c09f4..869ceede045ae 100644 --- a/docs/content/commands/npm-set-script.md +++ b/docs/content/commands/npm-set-script.md @@ -44,8 +44,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -58,17 +58,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-uninstall.md b/docs/content/commands/npm-uninstall.md index c04e1a7fd417b..824d0d8760e0b 100644 --- a/docs/content/commands/npm-uninstall.md +++ b/docs/content/commands/npm-uninstall.md @@ -85,8 +85,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -99,17 +99,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-unpublish.md b/docs/content/commands/npm-unpublish.md index 1678bb305dc3d..13589a03ee69f 100644 --- a/docs/content/commands/npm-unpublish.md +++ b/docs/content/commands/npm-unpublish.md @@ -107,8 +107,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -121,12 +121,20 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. diff --git a/docs/content/commands/npm-update.md b/docs/content/commands/npm-update.md index 57b9b91f9783e..ad02118e4687f 100644 --- a/docs/content/commands/npm-update.md +++ b/docs/content/commands/npm-update.md @@ -341,8 +341,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -355,17 +355,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### See Also diff --git a/docs/content/commands/npm-version.md b/docs/content/commands/npm-version.md index a02bf5bb99ba2..91ab0dee042ec 100644 --- a/docs/content/commands/npm-version.md +++ b/docs/content/commands/npm-version.md @@ -103,8 +103,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -117,17 +117,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### Description diff --git a/docs/content/commands/npm-view.md b/docs/content/commands/npm-view.md index af354deb88aba..9a1793f13fa56 100644 --- a/docs/content/commands/npm-view.md +++ b/docs/content/commands/npm-view.md @@ -127,8 +127,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -141,17 +141,39 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + ### Output diff --git a/docs/content/using-npm/config.md b/docs/content/using-npm/config.md index 10e8ca9b9dcff..647a9e1e0cdde 100644 --- a/docs/content/using-npm/config.md +++ b/docs/content/using-npm/config.md @@ -806,6 +806,20 @@ This is experimental, and not implemented by the npm public registry. +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + + + + #### `init-author-email` * Default: "" @@ -1744,8 +1758,8 @@ 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) +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -1758,12 +1772,20 @@ This value is not exported to the environment for child processes. #### `workspaces` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean -Enable running a command in the context of **all** the configured +Set to true to run the command in the context of **all** configured workspaces. +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + This value is not exported to the environment for child processes. diff --git a/lib/base-command.js b/lib/base-command.js index 870c69acc492d..c5bd3fd94f960 100644 --- a/lib/base-command.js +++ b/lib/base-command.js @@ -7,8 +7,6 @@ class BaseCommand { constructor (npm) { this.wrapWidth = 80 this.npm = npm - this.workspaces = null - this.workspacePaths = null } get name () { @@ -75,7 +73,13 @@ class BaseCommand { } async setWorkspaces (filters) { - const ws = await getWorkspaces(filters, { path: this.npm.localPrefix }) + if (this.isArboristCmd) + this.includeWorkspaceRoot = false + + const ws = await getWorkspaces(filters, { + path: this.npm.localPrefix, + includeWorkspaceRoot: this.includeWorkspaceRoot, + }) this.workspaces = ws this.workspaceNames = [...ws.keys()] this.workspacePaths = [...ws.values()] diff --git a/lib/diff.js b/lib/diff.js index 01658c4664d05..b1a32705c0644 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -43,6 +43,7 @@ class Diff extends BaseCommand { 'tag', 'workspace', 'workspaces', + 'include-workspace-root', ] } diff --git a/lib/dist-tag.js b/lib/dist-tag.js index e32dcf61fff80..be44f39ff2533 100644 --- a/lib/dist-tag.js +++ b/lib/dist-tag.js @@ -14,7 +14,7 @@ class DistTag extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['workspace', 'workspaces'] + return ['workspace', 'workspaces', 'include-workspace-root'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/docs.js b/lib/docs.js index 69a19c35c3a13..51f8be3882179 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -17,7 +17,13 @@ class Docs extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['browser', 'registry', 'workspace', 'workspaces'] + return [ + 'browser', + 'registry', + 'workspace', + 'workspaces', + 'include-workspace-root', + ] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/exec.js b/lib/exec.js index 8c64c2f240581..d11947483e26e 100644 --- a/lib/exec.js +++ b/lib/exec.js @@ -35,7 +35,13 @@ class Exec extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['package', 'call', 'workspace', 'workspaces'] + return [ + 'package', + 'call', + 'workspace', + 'workspaces', + 'include-workspace-root', + ] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/explain.js b/lib/explain.js index 7d785d7bfcf44..fc7f57891b986 100644 --- a/lib/explain.js +++ b/lib/explain.js @@ -46,8 +46,15 @@ class Explain extends ArboristWorkspaceCmd { const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions }) const tree = await arb.loadActual() - if (this.workspaceNames && this.workspaceNames.length) + if (this.npm.flatOptions.workspacesEnabled + && this.workspaceNames + && this.workspaceNames.length + ) this.filterSet = arb.workspaceDependencySet(tree, this.workspaceNames) + else if (!this.npm.flatOptions.workspacesEnabled) { + this.filterSet = + arb.excludeWorkspacesDependencySet(tree) + } const nodes = new Set() for (const arg of args) { diff --git a/lib/fund.js b/lib/fund.js index 1e0fa1ecb9d73..97139f5bba09c 100644 --- a/lib/fund.js +++ b/lib/fund.js @@ -92,6 +92,7 @@ class Fund extends ArboristWorkspaceCmd { return } + // TODO: add !workspacesEnabled option handling to libnpmfund const fundingInfo = getFundingInfo(tree, { ...this.flatOptions, log: this.npm.log, diff --git a/lib/init.js b/lib/init.js index e4bd20b7210e8..30fd7e2f7f970 100644 --- a/lib/init.js +++ b/lib/init.js @@ -54,6 +54,10 @@ class Init extends BaseCommand { } async initWorkspaces (args, filters) { + // if the root package is uninitiated, take care of it first + if (this.npm.flatOptions.includeWorkspaceRoot) + await this.init(args) + // reads package.json for the top-level folder first, by doing this we // ensure the command throw if no package.json is found before trying // to create a workspace package.json file or its folders diff --git a/lib/link.js b/lib/link.js index febd908718be3..2437eb12ed7d6 100644 --- a/lib/link.js +++ b/lib/link.js @@ -185,6 +185,7 @@ class Link extends ArboristWorkspaceCmd { // atm but should be simple once we have a mocked registry again if (arg.name !== node.name /* istanbul ignore next */ || ( arg.version && + /* istanbul ignore next */ !semver.satisfies(node.version, arg.version) )) { foundNodes.push(node) diff --git a/lib/ls.js b/lib/ls.js index 495b6348a0360..46cfb2462d327 100644 --- a/lib/ls.js +++ b/lib/ls.js @@ -82,6 +82,7 @@ class LS extends ArboristWorkspaceCmd { const production = this.npm.config.get('production') const unicode = this.npm.config.get('unicode') const packageLockOnly = this.npm.config.get('package-lock-only') + const workspacesEnabled = this.npm.flatOptions.workspacesEnabled const path = global ? resolve(this.npm.globalDir, '..') : this.npm.prefix @@ -100,12 +101,18 @@ class LS extends ArboristWorkspaceCmd { if (this.workspaceNames && this.workspaceNames.length) wsNodes = arb.workspaceNodes(tree, this.workspaceNames) const filterBySelectedWorkspaces = edge => { + if (!workspacesEnabled + && edge.from.isProjectRoot + && edge.to.isWorkspace + ) + return false + if (!wsNodes || !wsNodes.length) return true if (edge.from.isProjectRoot) { return edge.to && - edge.to.isWorkspace & + edge.to.isWorkspace && wsNodes.includes(edge.to.target) } diff --git a/lib/npm.js b/lib/npm.js index 966d11210c275..9b9f3b238b74c 100644 --- a/lib/npm.js +++ b/lib/npm.js @@ -137,7 +137,19 @@ const npm = module.exports = new class extends EventEmitter { const workspacesEnabled = this.config.get('workspaces') const workspacesFilters = this.config.get('workspace') + if (workspacesEnabled === false && workspacesFilters.length > 0) + return cb(new Error('Can not use --no-workspaces and --workspace at the same time')) + const filterByWorkspaces = workspacesEnabled || workspacesFilters.length > 0 + // normally this would go in the constructor, but our tests don't + // actually use a real npm object so this.npm.config isn't always + // populated. this is the compromise until we can make that a reality + // and then move this into the constructor. + impl.workspaces = this.config.get('workspaces') + impl.workspacePaths = null + // normally this would be evaluated in base-command#setWorkspaces, see + // above for explanation + impl.includeWorkspaceRoot = this.config.get('include-workspace-root') if (this.config.get('usage')) { this.output(impl.usage) diff --git a/lib/outdated.js b/lib/outdated.js index b3b630421c488..ab46b45360801 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -62,7 +62,14 @@ class Outdated extends ArboristWorkspaceCmd { if (this.workspaceNames && this.workspaceNames.length) { this.filterSet = - arb.workspaceDependencySet(this.tree, this.workspaceNames) + arb.workspaceDependencySet( + this.tree, + this.workspaceNames, + this.npm.flatOptions.includeWorkspaceRoot + ) + } else if (!this.npm.flatOptions.workspacesEnabled) { + this.filterSet = + arb.excludeWorkspacesDependencySet(this.tree) } if (args.length !== 0) { diff --git a/lib/pack.js b/lib/pack.js index 8fc89db1a0b2b..848f8afd5ea87 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -30,6 +30,7 @@ class Pack extends BaseCommand { 'pack-destination', 'workspace', 'workspaces', + 'include-workspace-root', ] } diff --git a/lib/publish.js b/lib/publish.js index 9c747eb5068f0..32e70129f2c03 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -36,7 +36,15 @@ class Publish extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['tag', 'access', 'dry-run', 'otp', 'workspace', 'workspaces'] + return [ + 'tag', + 'access', + 'dry-run', + 'otp', + 'workspace', + 'workspaces', + 'include-workspace-root', + ] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/repo.js b/lib/repo.js index e0172d01f63d1..bf1d1e7ff886d 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -19,7 +19,7 @@ class Repo extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['browser', 'workspace', 'workspaces'] + return ['browser', 'workspace', 'workspaces', 'include-workspace-root'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -48,7 +48,13 @@ class Repo extends BaseCommand { } async get (pkg) { - const opts = { ...this.npm.flatOptions, fullMetadata: true } + // XXX It is very odd that `where` is how pacote knows to look anywhere + // other than the cwd. + const opts = { + ...this.npm.flatOptions, + where: this.npm.localPrefix, + fullMetadata: true, + } const mani = await pacote.manifest(pkg, opts) const r = mani.repository diff --git a/lib/run-script.js b/lib/run-script.js index 1daaeb9900de1..de847ff28b3b5 100644 --- a/lib/run-script.js +++ b/lib/run-script.js @@ -38,6 +38,7 @@ class RunScript extends BaseCommand { return [ 'workspace', 'workspaces', + 'include-workspace-root', 'if-present', 'ignore-scripts', 'script-shell', diff --git a/lib/set-script.js b/lib/set-script.js index 24e4d8f20f666..185c4cc901d2e 100644 --- a/lib/set-script.js +++ b/lib/set-script.js @@ -12,7 +12,7 @@ class SetScript extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['workspace', 'workspaces'] + return ['workspace', 'workspaces', 'include-workspace-root'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/lib/utils/completion/installed-deep.js b/lib/utils/completion/installed-deep.js index 590955a1ee520..62686f9b2d3c9 100644 --- a/lib/utils/completion/installed-deep.js +++ b/lib/utils/completion/installed-deep.js @@ -7,6 +7,7 @@ const installedDeep = async (npm) => { depth, global, prefix, + workspacesEnabled, } = npm.flatOptions const getValues = (tree) => @@ -19,14 +20,18 @@ const installedDeep = async (npm) => { .sort((a, b) => (a.depth - b.depth) || localeCompare(a.name, b.name)) const res = new Set() - const gArb = new Arborist({ global: true, path: resolve(npm.globalDir, '..') }) + const gArb = new Arborist({ + global: true, + path: resolve(npm.globalDir, '..'), + workspacesEnabled, + }) const gTree = await gArb.loadActual({ global: true }) for (const node of getValues(gTree)) res.add(global ? node.name : [node.name, '-g']) if (!global) { - const arb = new Arborist({ global: false, path: prefix }) + const arb = new Arborist({ global: false, path: prefix, workspacesEnabled }) const tree = await arb.loadActual() for (const node of getValues(tree)) res.add(node.name) diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index 009f60a7bce61..57a50d1c8bb28 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -918,6 +918,19 @@ define('include-staged', { flatten, }) +define('include-workspace-root', { + default: false, + type: Boolean, + description: ` + Include the workspace root when workspaces are enabled for a command. + + When false, specifying individual workspaces via the \`workspace\` config, + or all workspaces via the \`workspaces\` flag, will cause npm to operate only + on the specified workspaces, and not on the root project. + `, + flatten, +}) + define('init-author-email', { default: '', type: String, @@ -2137,8 +2150,8 @@ define('workspace', { * Workspace names * Path to a workspace directory - * Path to a parent workspace directory (will result to selecting all of the - nested workspaces) + * Path to a parent workspace directory (will result in selecting all + workspaces within that folder) 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 @@ -2150,16 +2163,34 @@ define('workspace', { }) define('workspaces', { - default: false, - type: Boolean, + default: null, + type: [null, Boolean], short: 'ws', envExport: false, description: ` - Enable running a command in the context of **all** the configured + Set to true to run the command in the context of **all** configured workspaces. + + Explicitly setting this to false will cause commands like \`install\` to + ignore workspaces altogether. + When not set explicitly: + + - Commands that operate on the \`node_modules\` tree (install, update, + etc.) will link workspaces into the \`node_modules\` folder. + - Commands that do other things (test, exec, publish, etc.) will operate + on the root project, _unless_ one or more workspaces are specified in + the \`workspace\` config. `, flatten: (key, obj, flatOptions) => { definitions['user-agent'].flatten('user-agent', obj, flatOptions) + + // TODO: this is a derived value, and should be reworked when we have a + // pattern for derived value + + // workspacesEnabled is true whether workspaces is null or true + // commands contextually work with workspaces or not regardless of + // configuration, so we need an option specifically to disable workspaces + flatOptions.workspacesEnabled = obj[key] !== false }, }) diff --git a/lib/version.js b/lib/version.js index f3680fe8b7a01..917a647474b95 100644 --- a/lib/version.js +++ b/lib/version.js @@ -26,6 +26,7 @@ class Version extends BaseCommand { 'sign-git-tag', 'workspace', 'workspaces', + 'include-workspace-root', ] } diff --git a/lib/view.js b/lib/view.js index 0124bfb7d3543..46b1b5edfea7a 100644 --- a/lib/view.js +++ b/lib/view.js @@ -31,6 +31,7 @@ class View extends BaseCommand { 'json', 'workspace', 'workspaces', + 'include-workspace-root', ] } diff --git a/lib/workspaces/arborist-cmd.js b/lib/workspaces/arborist-cmd.js index cb6b66b8cb257..a75b351be4759 100644 --- a/lib/workspaces/arborist-cmd.js +++ b/lib/workspaces/arborist-cmd.js @@ -4,16 +4,21 @@ const BaseCommand = require('../base-command.js') class ArboristCmd extends BaseCommand { + get isArboristCmd () { + return true + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { return [ 'workspace', 'workspaces', + 'include-workspace-root', ] } execWorkspaces (args, filters, cb) { - this.setWorkspaces(filters) + this.setWorkspaces(filters, true) .then(() => { this.exec(args, cb) }) diff --git a/lib/workspaces/get-workspaces.js b/lib/workspaces/get-workspaces.js index 91b0074556ae7..3eb8e4865b706 100644 --- a/lib/workspaces/get-workspaces.js +++ b/lib/workspaces/get-workspaces.js @@ -5,11 +5,16 @@ 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 }) => { +const getWorkspaces = async (filters, { path, includeWorkspaceRoot }) => { // 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 + let res = new Map() + if (includeWorkspaceRoot) + res.set(pkg.name, path) + + if (!filters.length) + res = new Map([...res, ...workspaces]) for (const filterArg of filters) { for (const [workspaceName, workspacePath] of workspaces.entries()) { diff --git a/package-lock.json b/package-lock.json index b8decbc1afedd..ec78e50f995ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "npm", "version": "7.24.1", "bundleDependencies": [ + "@isaacs/string-locale-compare", "@npmcli/arborist", "@npmcli/ci-detect", "@npmcli/config", diff --git a/package.json b/package.json index 31083ff0bf930..f4cde1d1771b6 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,7 @@ "write-file-atomic": "^3.0.3" }, "bundleDependencies": [ + "@isaacs/string-locale-compare", "@npmcli/arborist", "@npmcli/ci-detect", "@npmcli/config", diff --git a/tap-snapshots/test/lib/config.js.test.cjs b/tap-snapshots/test/lib/config.js.test.cjs index b21b75cd25e11..805370351eee6 100644 --- a/tap-snapshots/test/lib/config.js.test.cjs +++ b/tap-snapshots/test/lib/config.js.test.cjs @@ -68,6 +68,7 @@ exports[`test/lib/config.js TAP config list --json > output matches snapshot 1`] "ignore-scripts": false, "include": [], "include-staged": false, + "include-workspace-root": false, "init-author-email": "", "init-author-name": "", "init-author-url": "", @@ -152,7 +153,7 @@ exports[`test/lib/config.js TAP config list --json > output matches snapshot 1`] "viewer": "{VIEWER}", "which": null, "workspace": [], - "workspaces": false, + "workspaces": null, "yes": null, "metrics-registry": "https://registry.npmjs.org/" } @@ -217,6 +218,7 @@ if-present = false ignore-scripts = false include = [] include-staged = false +include-workspace-root = false init-author-email = "" init-author-name = "" init-author-url = "" @@ -303,7 +305,7 @@ versions = false viewer = "{VIEWER}" which = null workspace = [] -workspaces = false +workspaces = null yes = null ; "global" config from {GLOBALPREFIX}/npmrc diff --git a/tap-snapshots/test/lib/dist-tag.js.test.cjs b/tap-snapshots/test/lib/dist-tag.js.test.cjs index 21d9331db1299..f651f7b67b782 100644 --- a/tap-snapshots/test/lib/dist-tag.js.test.cjs +++ b/tap-snapshots/test/lib/dist-tag.js.test.cjs @@ -18,7 +18,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -40,7 +40,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -71,7 +71,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -93,7 +93,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -121,7 +121,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -179,7 +179,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags diff --git a/tap-snapshots/test/lib/init.js.test.cjs b/tap-snapshots/test/lib/init.js.test.cjs index 95abbe6c1d830..37ad8c3a86368 100644 --- a/tap-snapshots/test/lib/init.js.test.cjs +++ b/tap-snapshots/test/lib/init.js.test.cjs @@ -5,6 +5,10 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' +exports[`test/lib/init.js TAP npm init workspces with root > should print helper info 1`] = ` +Array [] +` + exports[`test/lib/init.js TAP workspaces no args > should print helper info 1`] = ` Array [ Array [ diff --git a/tap-snapshots/test/lib/load-all-commands.js.test.cjs b/tap-snapshots/test/lib/load-all-commands.js.test.cjs index 9f811a0058fd1..d287d1284831e 100644 --- a/tap-snapshots/test/lib/load-all-commands.js.test.cjs +++ b/tap-snapshots/test/lib/load-all-commands.js.test.cjs @@ -56,7 +56,7 @@ Options: [--json] [--package-lock-only] [--omit [--omit ...]] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help audit" for more info ` @@ -173,7 +173,7 @@ Options: [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: ddp @@ -208,7 +208,7 @@ Options: [--diff-no-prefix] [--diff-src-prefix ] [--diff-dst-prefix ] [--diff-text] [-g|--global] [--tag ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help diff" for more info ` @@ -225,7 +225,7 @@ npm dist-tag ls [] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: dist-tags @@ -243,7 +243,7 @@ npm docs [ [ ...]] Options: [--no-browser|--browser ] [--registry ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: home @@ -293,7 +293,7 @@ Options: [--package [@] [--package [@] ...]] [-c|--call ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: x @@ -343,7 +343,7 @@ Options: [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help find-dupes" for more info ` @@ -452,7 +452,7 @@ Options: [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] aliases: i, in, ins, inst, insta, instal, isnt, isnta, isntal, add @@ -499,7 +499,7 @@ Options: [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: it @@ -522,7 +522,7 @@ Options: [--omit [--omit ...]] [--ignore-scripts] [--no-audit] [--no-bin-links] [--no-fund] [--dry-run] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: ln @@ -542,7 +542,7 @@ Options: [--omit [--omit ...]] [--link] [--package-lock-only] [--unicode] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: la @@ -592,7 +592,7 @@ Options: [--omit [--omit ...]] [--link] [--package-lock-only] [--unicode] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: list @@ -661,7 +661,7 @@ npm pack [[<@scope>/]...] Options: [--dry-run] [--json] [--pack-destination ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help pack" for more info ` @@ -740,7 +740,7 @@ npm prune [[<@scope>/]...] Options: [--omit [--omit ...]] [--dry-run] [--json] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help prune" for more info ` @@ -756,7 +756,7 @@ npm publish [] Options: [--tag ] [--access ] [--dry-run] [--otp ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help publish" for more info ` @@ -772,7 +772,7 @@ npm rebuild [[<@scope>/][@] ...] Options: [-g|--global] [--no-bin-links] [--ignore-scripts] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] alias: rb @@ -790,7 +790,7 @@ npm repo [ [ ...]] Options: [--no-browser|--browser ] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] +[-ws|--workspaces] [--include-workspace-root] Run "npm help repo" for more info ` @@ -833,7 +833,7 @@ npm run-script [-- ] Options: [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] [--if-present] [--ignore-scripts] +[-ws|--workspaces] [--include-workspace-root] [--if-present] [--ignore-scripts] [--script-shell ] aliases: run, rum, urn @@ -880,7 +880,7 @@ npm set-script [