diff --git a/AUTHORS b/AUTHORS index 2b3e48e765163..3c3d13b2369ba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -772,3 +772,5 @@ Seth Thomas Andreas Felipe Santos Luigi Pinca +Marco Sirabella +wangsai diff --git a/CHANGELOG.md b/CHANGELOG.md index ce79e9368a6e5..02d66dfc323f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +## v7.12.0 (2021-05-06) + +### FEATURES + +* [`701627c51`](https://github.com/npm/cli/commit/701627c5169934e59da2959d76a49c77278cc9dc) + [#3098](https://github.com/npm/cli/issues/3098) + feat(cache): Allow `add` to accept multiple specs + ([@mjsir911](https://github.com/mjsir911)) +* [`59171f030`](https://github.com/npm/cli/commit/59171f0304f048a009f1697eec6f74f778bc52ff) + [#3187](https://github.com/npm/cli/issues/3187) + feat(config): add workspaces boolean to user-agent + ([@nlf](https://github.com/nlf)) + +### BUG FIXES + +* [`2c9b8713c`](https://github.com/npm/cli/commit/2c9b8713c4c88fbd0c3c48eb0de84dbd7269398f) + [#3182](https://github.com/npm/cli/issues/3182) + fix(docs): fix broken links + ([@wangsai](https://github.com/wangsai)) +* [`88cbc8c44`](https://github.com/npm/cli/commit/88cbc8c447cbaef20b5a8f19246211ce4918f4d8) + [#3198](https://github.com/npm/cli/issues/3198) + fix(tests): reflect new libnpmexec logic + +### DEPENDENCIES + +* [`d01ce5e13`](https://github.com/npm/cli/commit/d01ce5e132cb4661698012fd5017753c2bdb660b) + `libnpmexec@1.1.0`: + * feat: add walk up dir lookup to satisfy local bins +* [`81c1dfaaa`](https://github.com/npm/cli/commit/81c1dfaaaf918229316a975aa8075769ffafdb6d) + `@npmcli/arborist@2.4.2`: + * fix(add): save packages in the right place + * fix(reify): do not clean up nodes with no parent + * fix(audit): support alias specs & root package names +* [`87c2303ea`](https://github.com/npm/cli/commit/87c2303eaa6edfa5309da0a30f5ad291b6d57640) + `@npmcli/git@2.0.9`: + * fix(clone): Do not allow git replacement objects by default +* [`99ff40dff`](https://github.com/npm/cli/commit/99ff40dff5e5e55a5d5f045ba90e76c08174ca38) + `npm-packlist@2.2.0`: + * feat(npmignore): Do not force include history, changelogs, notice + * fix(package.json): add missing bin/index.js to files + ## v7.11.2 (2021-04-29) ### BUG FIXES @@ -76,8 +117,8 @@ [#3126](https://github.com/npm/cli/issues/3126) fix(logout): use isBasicAuth attribute ([@wraithgar](https://github.com/wraithgar)) -### DOCUMENTATION +### DOCUMENTATION * [`c93f1c39e`](https://github.com/npm/cli/commit/c93f1c39e326feff0857712a10ef6183fbafe1ab) [#3101](https://github.com/npm/cli/issues/3101) @@ -89,7 +130,6 @@ fix(usage): fix refs to ws shorthand ([@ruyadorno](https://github.com/ruyadorno)) - ### DEPENDENCIES * [`83166ebcc`](https://github.com/npm/cli/commit/83166ebcc4ba5e3bf215f08151437d96637f4f33) diff --git a/docs/content/commands/npm-cache.md b/docs/content/commands/npm-cache.md index bcc2989b7d3c3..0bbb2756a40fe 100644 --- a/docs/content/commands/npm-cache.md +++ b/docs/content/commands/npm-cache.md @@ -7,10 +7,10 @@ description: Manipulates packages cache ### Synopsis ```bash -npm cache add -npm cache add -npm cache add -npm cache add @ +npm cache add ... +npm cache add ... +npm cache add ... +npm cache add @... npm cache clean aliases: npm cache clear, npm cache rm @@ -25,7 +25,7 @@ Note: This command is unaware of workspaces. Used to add, list, or clean the npm cache folder. * add: - Add the specified package to the local cache. This command is primarily + Add the specified packages to the local cache. This command is primarily intended to be used internally by npm, but it can provide a way to add data to the local installation cache explicitly. diff --git a/docs/content/commands/npm-ci.md b/docs/content/commands/npm-ci.md index 925ba8de2e5b7..937553631ace5 100644 --- a/docs/content/commands/npm-ci.md +++ b/docs/content/commands/npm-ci.md @@ -12,7 +12,7 @@ npm ci ### Description -This command is similar to [`npm install`](/cli-commands/install), except +This command is similar to [`npm install`](/commands/npm-install), except it's meant to be used in automated environments such as test platforms, continuous integration, and deployment -- or any situation where you want to make sure you're doing a clean install of your dependencies. diff --git a/docs/content/commands/npm-dedupe.md b/docs/content/commands/npm-dedupe.md index c6d26126d3077..f9bfd9e01532e 100644 --- a/docs/content/commands/npm-dedupe.md +++ b/docs/content/commands/npm-dedupe.md @@ -74,7 +74,7 @@ Using `npm find-dupes` will run the command in `--dry-run` mode. ### See Also -* [npm find-dupes](/cli-commands/find-dupes) -* [npm ls](/cli-commands/ls) -* [npm update](/cli-commands/update) -* [npm install](/cli-commands/install) +* [npm find-dupes](/commands/npm-find-dupes) +* [npm ls](/commands/npm-ls) +* [npm update](/commands/npm-update) +* [npm install](/commands/npm-install) diff --git a/docs/content/commands/npm-deprecate.md b/docs/content/commands/npm-deprecate.md index 0603797661055..73d88b3717bd1 100644 --- a/docs/content/commands/npm-deprecate.md +++ b/docs/content/commands/npm-deprecate.md @@ -44,5 +44,5 @@ format an empty string. * [npm publish](/commands/npm-publish) * [npm registry](/using-npm/registry) -* [npm owner](/cli-commands/owner) -* [npm owner](/cli-commands/adduser) +* [npm owner](/commands/npm-owner) +* [npm owner](/commands/npm-adduser) diff --git a/docs/content/commands/npm-explore.md b/docs/content/commands/npm-explore.md index 7e2004b84c041..8bad293052ead 100644 --- a/docs/content/commands/npm-explore.md +++ b/docs/content/commands/npm-explore.md @@ -44,5 +44,4 @@ The shell to run for the `npm explore` command. * [npm folders](/configuring-npm/folders) * [npm edit](/commands/npm-edit) * [npm rebuild](/commands/npm-rebuild) -* [npm build](/commands/npm-build) * [npm install](/commands/npm-install) diff --git a/docs/content/commands/npm-find-dupes.md b/docs/content/commands/npm-find-dupes.md index 6f55d47bfd7f3..6557a071c82c0 100644 --- a/docs/content/commands/npm-find-dupes.md +++ b/docs/content/commands/npm-find-dupes.md @@ -17,8 +17,8 @@ duplications, without actually changing the package tree. ### See Also -* [npm dedupe](/cli-commands/dedupe) -* [npm ls](/cli-commands/ls) -* [npm update](/cli-commands/update) -* [npm install](/cli-commands/install) +* [npm dedupe](/commands/npm-dedupe) +* [npm ls](/commands/npm-ls) +* [npm update](/commands/npm-update) +* [npm install](/commands/npm-install) diff --git a/docs/content/commands/npm-install.md b/docs/content/commands/npm-install.md index 5ab9275ee4eaf..f81a8a2463989 100644 --- a/docs/content/commands/npm-install.md +++ b/docs/content/commands/npm-install.md @@ -538,7 +538,6 @@ the specific folder structures that npm creates. * [npm link](/commands/npm-link) * [npm rebuild](/commands/npm-rebuild) * [npm scripts](/using-npm/scripts) -* [npm build](/commands/npm-build) * [npm config](/commands/npm-config) * [npmrc](/configuring-npm/npmrc) * [npm registry](/using-npm/registry) diff --git a/docs/content/commands/npm-unpublish.md b/docs/content/commands/npm-unpublish.md index e9d6e9045c6f9..2ce52d3f8cd44 100644 --- a/docs/content/commands/npm-unpublish.md +++ b/docs/content/commands/npm-unpublish.md @@ -34,7 +34,7 @@ This removes a package version from the registry, deleting its entry and removing the tarball. The npm registry will return an error if you are not [logged -in](/commands/npm-login). +in](/commands/npm-adduser). If you do not specify a version or if you remove all of a package's versions then the registry will remove the root package entry entirely. @@ -52,4 +52,4 @@ passed. * [npm registry](/using-npm/registry) * [npm adduser](/commands/npm-adduser) * [npm owner](/commands/npm-owner) -* [npm login](/commands/npm-login) +* [npm login](/commands/npm-adduser) diff --git a/docs/content/commands/npm-view.md b/docs/content/commands/npm-view.md index 8f7e886ed192a..982ba29955179 100644 --- a/docs/content/commands/npm-view.md +++ b/docs/content/commands/npm-view.md @@ -73,7 +73,7 @@ npm view express contributors.name contributors.email "Person" fields are shown as a string if they would be shown as an object. So, for example, this will show the list of `npm` contributors in -the shortened string format. (See [`package.json`](/configuring-npm/package.json) for more on this.) +the shortened string format. (See [`package.json`](/configuring-npm/package-json) for more on this.) ```bash npm view npm contributors diff --git a/docs/content/commands/npm.md b/docs/content/commands/npm.md index d01146d37041c..2d86aa62c0080 100644 --- a/docs/content/commands/npm.md +++ b/docs/content/commands/npm.md @@ -107,7 +107,7 @@ following help topics: Create an account or log in. When you do this, npm will store credentials in the user config file config file. * publish: - Use the [`npm publish`](/commands/npm-publish`) command to upload your + Use the [`npm publish`](/commands/npm-publish) command to upload your code to the registry. #### Configuration diff --git a/docs/content/configuring-npm/folders.md b/docs/content/configuring-npm/folders.md index 3ec716f2c67fd..75e31cd733161 100644 --- a/docs/content/configuring-npm/folders.md +++ b/docs/content/configuring-npm/folders.md @@ -205,7 +205,7 @@ not be included in the package tarball. This allows a package maintainer to install all of their dependencies (and dev dependencies) locally, but only re-publish those items that -cannot be found elsewhere. See [`package.json`](/configuring-npm/package.json) for more information. +cannot be found elsewhere. See [`package.json`](/configuring-npm/package-json) for more information. ### See also diff --git a/docs/content/configuring-npm/package-json.md b/docs/content/configuring-npm/package-json.md index f5519f62bb32e..3ed0399021447 100644 --- a/docs/content/configuring-npm/package-json.md +++ b/docs/content/configuring-npm/package-json.md @@ -566,7 +566,7 @@ tarball or git URL. **Please do not put test harnesses or transpilers or other "development" time tools in your `dependencies` object.** See `devDependencies`, below. -See [semver](/using-npm/semver#versions) for more details about specifying version ranges. +See [semver](https://github.com/npm/node-semver#versions) for more details about specifying version ranges. * `version` Must match `version` exactly * `>version` Must be greater than `version` diff --git a/docs/content/using-npm/config.md b/docs/content/using-npm/config.md index 76ac4192f1351..d8fb70fb74bed 100644 --- a/docs/content/using-npm/config.md +++ b/docs/content/using-npm/config.md @@ -1253,7 +1253,8 @@ Show short usage output about the command specified. #### `user-agent` -* Default: "npm/{npm-version} node/{node-version} {platform} {arch} {ci}" +* Default: "npm/{npm-version} node/{node-version} {platform} {arch} + workspaces/{workspaces} {ci}" * Type: String Sets the User-Agent request header. The following fields are replaced with @@ -1263,6 +1264,8 @@ their actual counterparts: * `{node-version}` - The Node.js version in use * `{platform}` - The value of `process.platform` * `{arch}` - The value of `process.arch` +* `{workspaces}` - Set to `true` if the `workspaces` or `workspace` options + are set. * `{ci}` - The value of the `ci-name` config, if set, prefixed with `ci/`, or an empty string if `ci-name` is empty. diff --git a/docs/content/using-npm/scripts.md b/docs/content/using-npm/scripts.md index 9312a21546d6f..82cde7d79094d 100644 --- a/docs/content/using-npm/scripts.md +++ b/docs/content/using-npm/scripts.md @@ -118,11 +118,6 @@ The advantage of doing these things at `prepublish` time is that they can be don * `prepare` -#### [`npm env`](/commands/npm-env) - -* `env` (You can override the default behavior of `npm env` by defining - a custom `env` entry in your `scripts` object) - #### [`npm install`](/commands/npm-install) These also run when you run `npm install -g ` @@ -179,7 +174,7 @@ If there is a `restart` script defined, these events are run, otherwise * `restart` * `postrestart` -#### [`npm run `](/commands/npm-run) +#### [`npm run `](/commands/npm-run-script) * `pre` * `` diff --git a/lib/cache.js b/lib/cache.js index 43902f43bbee1..5d544b2dbd185 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -86,32 +86,30 @@ with --force.`) return rimraf(cachePath) } - // npm cache add - // npm cache add - // npm cache add - // npm cache add + // npm cache add ... + // npm cache add ... + // npm cache add ... + // npm cache add ... async add (args) { const usage = 'Usage:\n' + - ' npm cache add \n' + - ' npm cache add @\n' + - ' npm cache add \n' + - ' npm cache add \n' + ' npm cache add ...\n' + + ' npm cache add @...\n' + + ' npm cache add ...\n' + + ' npm cache add ...\n' log.silly('cache add', 'args', args) - const spec = args[0] && args[0] + - (args[1] === undefined || args[1] === null ? '' : `@${args[1]}`) - - if (!spec) + if (args.length === 0) throw Object.assign(new Error(usage), { code: 'EUSAGE' }) - log.silly('cache add', 'spec', spec) - - // we ask pacote for the thing, and then just throw the data - // away so that it tee-pipes it into the cache like it does - // for a normal request. - await pacote.tarball.stream(spec, stream => { - stream.resume() - return stream.promise() - }, this.npm.flatOptions) + return Promise.all(args.map(spec => { + log.silly('cache add', 'spec', spec) + // we ask pacote for the thing, and then just throw the data + // away so that it tee-pipes it into the cache like it does + // for a normal request. + return pacote.tarball.stream(spec, stream => { + stream.resume() + return stream.promise() + }, this.npm.flatOptions) + })) } async verify () { diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index 3a50175d5db64..aa90de8e760b7 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -1943,6 +1943,7 @@ define('user-agent', { 'node/{node-version} ' + '{platform} ' + '{arch} ' + + 'workspaces/{workspaces} ' + '{ci}', type: String, description: ` @@ -1953,17 +1954,23 @@ define('user-agent', { * \`{node-version}\` - The Node.js version in use * \`{platform}\` - The value of \`process.platform\` * \`{arch}\` - The value of \`process.arch\` + * \`{workspaces}\` - Set to \`true\` if the \`workspaces\` or \`workspace\` + options are set. * \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with \`ci/\`, or an empty string if \`ci-name\` is empty. `, flatten (key, obj, flatOptions) { const value = obj[key] const ciName = obj['ci-name'] + let inWorkspaces = false + if (obj.workspaces || obj.workspace && obj.workspace.length) + inWorkspaces = true flatOptions.userAgent = value.replace(/\{node-version\}/gi, obj['node-version']) .replace(/\{npm-version\}/gi, obj['npm-version']) .replace(/\{platform\}/gi, process.platform) .replace(/\{arch\}/gi, process.arch) + .replace(/\{workspaces\}/gi, inWorkspaces) .replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '') .trim() // user-agent is a unique kind of config item that gets set from a template 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 9a96fd1b3797c..25113cbedff39 100644 --- a/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js +++ b/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js @@ -1,60 +1,60 @@ // add and remove dependency specs to/from pkg manifest -const removeFromOthers = (name, type, pkg) => { - const others = new Set([ - 'dependencies', - 'optionalDependencies', - 'devDependencies', - 'peerDependenciesMeta', - 'peerDependencies', - ]) - - switch (type) { - case 'prod': - others.delete('dependencies') - break - case 'dev': - others.delete('devDependencies') - others.delete('peerDependencies') - others.delete('peerDependenciesMeta') - break - case 'optional': - others.delete('optionalDependencies') - break - case 'peer': - case 'peerOptional': - others.delete('devDependencies') - others.delete('peerDependencies') - others.delete('peerDependenciesMeta') - break - } - - for (const other of others) - deleteSubKey(pkg, other, name) -} - -const add = ({pkg, add, saveBundle, saveType}) => { +const add = ({pkg, add, saveBundle, saveType, log}) => { for (const spec of add) - addSingle({pkg, spec, saveBundle, saveType}) + addSingle({pkg, spec, saveBundle, saveType, log}) return pkg } -const addSingle = ({pkg, spec, saveBundle, saveType}) => { - if (!saveType) - saveType = getSaveType(pkg, spec) +// Canonical source of both the map between saveType and where it correlates to +// in the package, and the names of all our dependencies attributes +const saveTypeMap = new Map([ + ['dev', 'devDependencies'], + ['optional', 'optionalDependencies'], + ['prod', 'dependencies'], + ['peerOptional', 'peerDependencies'], + ['peer', 'peerDependencies'], +]) +const addSingle = ({pkg, spec, saveBundle, saveType, log}) => { const { name, rawSpec } = spec - removeFromOthers(name, saveType, pkg) - const type = saveType === 'prod' ? 'dependencies' - : saveType === 'optional' ? 'optionalDependencies' - : saveType === 'peer' || saveType === 'peerOptional' ? 'peerDependencies' - : saveType === 'dev' ? 'devDependencies' - : /* istanbul ignore next */ null - pkg[type] = pkg[type] || {} - if (rawSpec !== '' || pkg[type][name] === undefined) - pkg[type][name] = rawSpec || '*' + // if the user does not give us a type, we infer which type(s) + // to keep based on the same order of priority we do when + // building the tree as defined in the _loadDeps method of + // the node class. + if (!saveType) + saveType = inferSaveType(pkg, spec.name) + + if (saveType === 'prod') { + // a production dependency can only exist as production (rpj ensures it + // doesn't coexist w/ optional) + deleteSubKey(pkg, 'devDependencies', name, 'dependencies', log) + deleteSubKey(pkg, 'peerDependencies', name, 'dependencies', log) + } else if (saveType === 'dev') { + // a dev dependency may co-exist as peer, or optional, but not production + deleteSubKey(pkg, 'dependencies', name, 'devDependencies', log) + } else if (saveType === 'optional') { + // an optional dependency may co-exist as dev (rpj ensures it doesn't + // coexist w/ prod) + deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies', log) + } else { // peer or peerOptional is all that's left + // a peer dependency may coexist as dev + deleteSubKey(pkg, 'dependencies', name, 'peerDependencies', log) + deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies', log) + } + + const depType = saveTypeMap.get(saveType) + + pkg[depType] = pkg[depType] || {} + if (rawSpec !== '' || pkg[depType][name] === undefined) + pkg[depType][name] = rawSpec || '*' + if (saveType === 'optional') { + // Affordance for previous npm versions that require this behaviour + pkg.dependencies = pkg.dependencies || {} + pkg.dependencies[name] = pkg.optionalDependencies[name] + } if (saveType === 'peer' || saveType === 'peerOptional') { const pdm = pkg.peerDependenciesMeta || {} @@ -79,47 +79,49 @@ const addSingle = ({pkg, spec, saveBundle, saveType}) => { } } -const getSaveType = (pkg, spec) => { - const {name} = spec - const { - // these names are so lonnnnngggg - devDependencies: devDeps, - optionalDependencies: optDeps, - peerDependencies: peerDeps, - peerDependenciesMeta: peerDepsMeta, - } = pkg - - if (peerDeps && peerDeps[name] !== undefined) { - if (peerDepsMeta && peerDepsMeta[name] && peerDepsMeta[name].optional) - return 'peerOptional' - else - return 'peer' - } else if (devDeps && devDeps[name] !== undefined) - return 'dev' - else if (optDeps && optDeps[name] !== undefined) - return 'optional' - else - return 'prod' +// Finds where the package is already in the spec and infers saveType from that +const inferSaveType = (pkg, name) => { + for (const saveType of saveTypeMap.keys()) { + if (hasSubKey(pkg, saveTypeMap.get(saveType), name)) { + if ( + saveType === 'peerOptional' && + (!hasSubKey(pkg, 'peerDependenciesMeta', name) || + !pkg.peerDependenciesMeta[name].optional) + ) + return 'peer' + return saveType + } + } + return 'prod' } -const deleteSubKey = (obj, k, sk) => { - if (obj[k]) { - delete obj[k][sk] - if (!Object.keys(obj[k]).length) - delete obj[k] +const hasSubKey = (pkg, depType, name) => { + return pkg[depType] && Object.prototype.hasOwnProperty.call(pkg[depType], name) +} + +// Removes a subkey and warns about it if it's being replaced +const deleteSubKey = (pkg, depType, name, replacedBy, log) => { + if (hasSubKey(pkg, depType, name)) { + if (replacedBy && log) + log.warn('idealTree', `Removing ${depType}.${name} in favor of ${replacedBy}.${name}`) + delete pkg[depType][name] + + // clean up peerDependenciesMeta if we are removing something from peerDependencies + if (depType === 'peerDependencies' && pkg.peerDependenciesMeta) { + delete pkg.peerDependenciesMeta[name] + if (!Object.keys(pkg.peerDependenciesMeta).length) + delete pkg.peerDependenciesMeta + } + + if (!Object.keys(pkg[depType]).length) + delete pkg[depType] } } const rm = (pkg, rm) => { - for (const type of [ - 'dependencies', - 'optionalDependencies', - 'peerDependencies', - 'peerDependenciesMeta', - 'devDependencies', - ]) { + for (const depType of new Set(saveTypeMap.values())) { for (const name of rm) - deleteSubKey(pkg, type, name) + deleteSubKey(pkg, depType, name) } if (pkg.bundleDependencies) { pkg.bundleDependencies = pkg.bundleDependencies @@ -130,4 +132,4 @@ const rm = (pkg, rm) => { return pkg } -module.exports = { add, rm } +module.exports = { add, rm, saveTypeMap, hasSubKey } 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 7ee8dae35be1b..6176707c3c651 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -504,6 +504,7 @@ module.exports = cls => class IdealTreeBuilder extends cls { saveBundle, saveType, path: this.path, + log: this.log, }) }) } diff --git a/node_modules/@npmcli/arborist/lib/arborist/index.js b/node_modules/@npmcli/arborist/lib/arborist/index.js index 93b9aa3829820..3578d50389ea6 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/index.js +++ b/node_modules/@npmcli/arborist/lib/arborist/index.js @@ -29,6 +29,7 @@ const {resolve} = require('path') const {homedir} = require('os') const procLog = require('../proc-log.js') +const { saveTypeMap } = require('../add-rm-pkg-deps.js') const mixins = [ require('../tracker.js'), @@ -57,6 +58,8 @@ class Arborist extends Base { packumentCache: options.packumentCache || new Map(), log: options.log || procLog, } + if (options.saveType && !saveTypeMap.get(options.saveType)) + throw new Error(`Invalid saveType ${options.saveType}`) this.cache = resolve(this.options.cache) this.path = resolve(this.options.path) process.emit('timeEnd', 'arborist:ctor') diff --git a/node_modules/@npmcli/arborist/lib/arborist/reify.js b/node_modules/@npmcli/arborist/lib/arborist/reify.js index 64f08756263c7..f7731e1c90da0 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -3,9 +3,8 @@ const onExit = require('../signal-handling.js') const pacote = require('pacote') const rpj = require('read-package-json-fast') -const { updateDepSpec } = require('../dep-spec.js') const AuditReport = require('../audit-report.js') -const {subset} = require('semver') +const {subset, intersects} = require('semver') const npa = require('npm-package-arg') const {dirname, resolve, relative} = require('path') @@ -28,6 +27,7 @@ const promiseAllRejectLate = require('promise-all-reject-late') const optionalSet = require('../optional-set.js') const updateRootPackageJson = require('../update-root-package-json.js') const calcDepFlags = require('../calc-dep-flags.js') +const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js') const _retiredPaths = Symbol('retiredPaths') const _retiredUnchanged = Symbol('retiredUnchanged') @@ -406,11 +406,14 @@ module.exports = cls => class Reifier extends cls { return process.emit('time', 'reify:trashOmits') + // node.parent is checked to make sure this is a node that's in the tree, and + // not the parent-less top level nodes const filter = node => - node.peer && this[_omitPeer] || - node.dev && this[_omitDev] || - node.optional && this[_omitOptional] || - node.devOptional && this[_omitOptional] && this[_omitDev] + node.isDescendantOf(this.idealTree) && + (node.peer && this[_omitPeer] || + node.dev && this[_omitDev] || + node.optional && this[_omitOptional] || + node.devOptional && this[_omitOptional] && this[_omitDev]) for (const node of this.idealTree.inventory.filter(filter)) this[_addNodeToTrashList](node) @@ -539,8 +542,8 @@ module.exports = cls => class Reifier extends cls { // Do the best with what we have, or else remove it from the tree // entirely, since we can't possibly reify it. const res = node.resolved ? `${node.name}@${this[_registryResolved](node.resolved)}` - : node.package.name && node.version - ? `${node.package.name}@${node.version}` + : node.packageName && node.version + ? `${node.packageName}@${node.version}` : null // no idea what this thing is. remove it from the tree. @@ -959,6 +962,7 @@ module.exports = cls => class Reifier extends cls { const spec = subSpec ? subSpec.rawSpec : rawSpec const child = root.children.get(name) + let newSpec if (req.registry) { const version = child.version const prefixRange = version ? this[_savePrefix] + version : '*' @@ -970,16 +974,17 @@ module.exports = cls => class Reifier extends cls { const isRange = (subSpec || req).type === 'range' const range = !isRange || subset(prefixRange, spec, { loose: true }) ? prefixRange : spec - const pname = child.package.name + const pname = child.packageName const alias = name !== pname - updateDepSpec(pkg, name, (alias ? `npm:${pname}@` : '') + range) + newSpec = alias ? `npm:${pname}@${range}` : range } else if (req.hosted) { // save the git+https url if it has auth, otherwise shortcut const h = req.hosted const opt = { noCommittish: false } - const save = h.https && h.auth ? `git+${h.https(opt)}` - : h.shortcut(opt) - updateDepSpec(pkg, name, save) + if (h.https && h.auth) + newSpec = `git+${h.https(opt)}` + else + newSpec = h.shortcut(opt) } else if (req.type === 'directory' || req.type === 'file') { // save the relative path in package.json // Normally saveSpec is updated with the proper relative @@ -988,9 +993,37 @@ module.exports = cls => class Reifier extends cls { // thing, so just get the ultimate fetchSpec and relativize it. const p = req.fetchSpec.replace(/^file:/, '') const rel = relpath(root.realpath, p) - updateDepSpec(pkg, name, `file:${rel}`) + newSpec = `file:${rel}` } else - updateDepSpec(pkg, name, req.saveSpec) + newSpec = req.saveSpec + + if (options.saveType) { + const depType = saveTypeMap.get(options.saveType) + pkg[depType][name] = newSpec + // rpj will have moved it here if it was in both + // if it is empty it will be deleted later + if (options.saveType === 'prod' && pkg.optionalDependencies) + delete pkg.optionalDependencies[name] + } else { + if (hasSubKey(pkg, 'dependencies', name)) + pkg.dependencies[name] = newSpec + + if (hasSubKey(pkg, 'devDependencies', name)) { + pkg.devDependencies[name] = newSpec + // don't update peer or optional if we don't have to + if (hasSubKey(pkg, 'peerDependencies', name) && !intersects(newSpec, pkg.peerDependencies[name])) + pkg.peerDependencies[name] = newSpec + + if (hasSubKey(pkg, 'optionalDependencies', name) && !intersects(newSpec, pkg.optionalDependencies[name])) + pkg.optionalDependencies[name] = newSpec + } else { + if (hasSubKey(pkg, 'peerDependencies', name)) + pkg.peerDependencies[name] = newSpec + + if (hasSubKey(pkg, 'optionalDependencies', name)) + pkg.optionalDependencies[name] = newSpec + } + } } // refresh the edges so they have the correct specs diff --git a/node_modules/@npmcli/arborist/lib/audit-report.js b/node_modules/@npmcli/arborist/lib/audit-report.js index 77cd6511aea3b..9a0178c59c9e6 100644 --- a/node_modules/@npmcli/arborist/lib/audit-report.js +++ b/node_modules/@npmcli/arborist/lib/audit-report.js @@ -101,13 +101,14 @@ class AuditReport extends Map { async run () { this.report = await this[_getReport]() + this.log.silly('audit report', this.report) if (this.report) await this[_init]() return this } isVulnerable (node) { - const vuln = this.get(node.package.name) + const vuln = this.get(node.packageName) return !!(vuln && vuln.isVulnerable(node)) } @@ -144,7 +145,7 @@ class AuditReport extends Map { super.set(name, vuln) const p = [] - for (const node of this.tree.inventory.query('name', name)) { + for (const node of this.tree.inventory.query('packageName', name)) { if (shouldOmit(node, this[_omit])) continue @@ -167,7 +168,7 @@ class AuditReport extends Map { this[_checkTopNode](dep, vuln, spec) else { // calculate a metavuln, if necessary - p.push(this.calculator.calculate(dep.name, advisory).then(meta => { + p.push(this.calculator.calculate(dep.packageName, advisory).then(meta => { if (meta.testVersion(dep.version, spec)) advisories.add(meta) })) @@ -228,6 +229,9 @@ class AuditReport extends Map { if (!specObj.registry) return false + if (specObj.subSpec) + spec = specObj.subSpec.rawSpec + // We don't provide fixes for top nodes other than root, but we // still check to see if the node is fixable with a different version, // and if that is a semver major bump. @@ -289,6 +293,7 @@ class AuditReport extends Map { try { // first try the super fast bulk advisory listing const body = prepareBulkData(this.tree, this[_omit]) + this.log.silly('audit', 'bulk request', body) // no sense asking if we don't have anything to audit, // we know it'll be empty @@ -304,7 +309,8 @@ class AuditReport extends Map { }) return await res.json() - } catch (_) { + } catch (er) { + this.log.silly('audit', 'bulk request failed', String(er.body)) // that failed, try the quick audit endpoint const body = prepareData(this.tree, this.options) const res = await fetch('/-/npm/v1/security/audits/quick', { @@ -330,6 +336,7 @@ class AuditReport extends Map { // return true if we should ignore this one const shouldOmit = (node, omit) => !node.version ? true + : node.isRoot ? true : omit.size === 0 ? false : node.dev && omit.has('dev') || node.optional && omit.has('optional') || @@ -338,9 +345,9 @@ const shouldOmit = (node, omit) => const prepareBulkData = (tree, omit) => { const payload = {} - for (const name of tree.inventory.query('name')) { + for (const name of tree.inventory.query('packageName')) { const set = new Set() - for (const node of tree.inventory.query('name', name)) { + for (const node of tree.inventory.query('packageName', name)) { if (shouldOmit(node, omit)) continue diff --git a/node_modules/@npmcli/arborist/lib/dep-spec.js b/node_modules/@npmcli/arborist/lib/dep-spec.js deleted file mode 100644 index 92911543e1684..0000000000000 --- a/node_modules/@npmcli/arborist/lib/dep-spec.js +++ /dev/null @@ -1,43 +0,0 @@ -const types = [ - 'peerDependencies', - 'devDependencies', - 'optionalDependencies', - 'dependencies', -] - -const findType = (pkg, name) => { - for (const t of types) { - if (pkg[t] && typeof pkg[t] === 'object' && pkg[t][name] !== undefined) - return t - } - return 'dependencies' -} - -// given a dep name and spec, update it wherever it exists in -// the manifest, or add the spec to 'dependencies' if not found. -const updateDepSpec = (pkg, name, newSpec) => { - const type = findType(pkg, name) - pkg[type] = pkg[type] || {} - pkg[type][name] = newSpec - return pkg -} - -// sort alphabetically all types of deps for a given package -const orderDeps = (pkg) => { - for (const type of types) { - if (pkg && pkg[type]) { - pkg[type] = Object.keys(pkg[type]) - .sort((a, b) => a.localeCompare(b)) - .reduce((res, key) => { - res[key] = pkg[type][key] - return res - }, {}) - } - } - return pkg -} - -module.exports = { - orderDeps, - updateDepSpec, -} diff --git a/node_modules/@npmcli/arborist/lib/inventory.js b/node_modules/@npmcli/arborist/lib/inventory.js index cef0c4e265899..7578291885223 100644 --- a/node_modules/@npmcli/arborist/lib/inventory.js +++ b/node_modules/@npmcli/arborist/lib/inventory.js @@ -4,7 +4,7 @@ // keys is the set of fields to be able to query. const _primaryKey = Symbol('_primaryKey') const _index = Symbol('_index') -const defaultKeys = ['name', 'license', 'funding', 'realpath'] +const defaultKeys = ['name', 'license', 'funding', 'realpath', 'packageName'] const { hasOwnProperty } = Object.prototype const debug = require('./debug.js') class Inventory extends Map { diff --git a/node_modules/@npmcli/arborist/lib/node.js b/node_modules/@npmcli/arborist/lib/node.js index a54f76afcdf3b..370bfc9567d28 100644 --- a/node_modules/@npmcli/arborist/lib/node.js +++ b/node_modules/@npmcli/arborist/lib/node.js @@ -291,6 +291,10 @@ class Node { return this[_package].version || '' } + get packageName () { + return this[_package].name || null + } + get pkgid () { const { name = '', version = '' } = this.package // root package will prefer package name over folder name, @@ -350,10 +354,10 @@ class Node { } const why = { - name: this.isProjectRoot ? this.package.name : this.name, + name: this.isProjectRoot ? this.packageName : this.name, version: this.package.version, } - if (this.errors.length || !this.package.name || !this.package.version) { + if (this.errors.length || !this.packageName || !this.package.version) { why.errors = this.errors.length ? this.errors : [ new Error('invalid package: lacks name and/or version'), ] @@ -460,7 +464,7 @@ class Node { if (this.isProjectRoot) return false const { root } = this - const { type, to } = root.edgesOut.get(this.package.name) || {} + const { type, to } = root.edgesOut.get(this.packageName) || {} return type === 'workspace' && to && (to.target === this || to === this) } @@ -730,20 +734,14 @@ class Node { [_loadDeps] () { // Caveat! Order is relevant! - // packages in optionalDependencies and prod/peer/dev are - // optional. Packages in both deps and devDeps are required. + // Packages in optionalDependencies are optional. + // Packages in both deps and devDeps are required. // Note the subtle breaking change from v6: it is no longer possible // to have a different spec for a devDep than production dep. - this[_loadDepType](this.package.optionalDependencies, 'optional') // Linked targets that are disconnected from the tree are tops, // but don't have a 'path' field, only a 'realpath', because we // don't know their canonical location. We don't need their devDeps. - const { isTop, path, sourceReference } = this - const { isTop: srcTop, path: srcPath } = sourceReference || {} - if (isTop && path && (!sourceReference || srcTop && srcPath)) - this[_loadDepType](this.package.devDependencies, 'dev') - const pd = this.package.peerDependencies if (pd && typeof pd === 'object' && !this.legacyPeerDeps) { const pm = this.package.peerDependenciesMeta || {} @@ -760,19 +758,22 @@ class Node { } this[_loadDepType](this.package.dependencies, 'prod') + this[_loadDepType](this.package.optionalDependencies, 'optional') + + const { isTop, path, sourceReference } = this + const { isTop: srcTop, path: srcPath } = sourceReference || {} + if (isTop && path && (!sourceReference || srcTop && srcPath)) + this[_loadDepType](this.package.devDependencies, 'dev') } - [_loadDepType] (obj, type) { - const from = this + [_loadDepType] (deps, type) { const ad = this.package.acceptDependencies || {} - for (const [name, spec] of Object.entries(obj || {})) { - const accept = ad[name] - // if it's already set, then we keep the existing edge - // Prod deps should not be marked as dev, however. - // NB: the Edge ctor adds itself to from.edgesOut + // Because of the order in which _loadDeps runs, we always want to + // prioritize a new edge over an existing one + for (const [name, spec] of Object.entries(deps || {})) { const current = this.edgesOut.get(name) - if (!current || current.dev && type === 'prod') - new Edge({ from, name, spec, accept, type }) + if (!current || current.type !== 'workspace') + new Edge({ from: this, name, spec, accept: ad[name], type }) } } @@ -965,8 +966,8 @@ class Node { // if no resolved, check both package name and version // otherwise, conclude that they are different things - return this.package.name && node.package.name && - this.package.name === node.package.name && + return this.packageName && node.packageName && + this.packageName === node.packageName && this.version && node.version && this.version === node.version } diff --git a/node_modules/@npmcli/arborist/lib/printable.js b/node_modules/@npmcli/arborist/lib/printable.js index 79f46a9e93c4a..e611f55a4bd63 100644 --- a/node_modules/@npmcli/arborist/lib/printable.js +++ b/node_modules/@npmcli/arborist/lib/printable.js @@ -7,8 +7,8 @@ const relpath = require('./relpath.js') class ArboristNode { constructor (tree, path) { this.name = tree.name - if (tree.package.name && tree.package.name !== this.name) - this.packageName = tree.package.name + if (tree.packageName && tree.packageName !== this.name) + this.packageName = tree.packageName if (tree.version) this.version = tree.version this.location = tree.location diff --git a/node_modules/@npmcli/arborist/lib/shrinkwrap.js b/node_modules/@npmcli/arborist/lib/shrinkwrap.js index 342e78e9e3a7b..d9065ffa0258f 100644 --- a/node_modules/@npmcli/arborist/lib/shrinkwrap.js +++ b/node_modules/@npmcli/arborist/lib/shrinkwrap.js @@ -254,7 +254,7 @@ class Shrinkwrap { meta[key.replace(/^_/, '')] = val }) // we only include name if different from the node path name - const pname = node.package.name + const pname = node.packageName if (pname && pname !== node.name) meta.name = pname @@ -825,7 +825,7 @@ class Shrinkwrap { [_buildLegacyLockfile] (node, lock, path = []) { if (node === this.tree) { // the root node - lock.name = node.package.name || node.name + lock.name = node.packageName || node.name if (node.version) lock.version = node.version } @@ -870,9 +870,9 @@ class Shrinkwrap { lock.from = spec.raw } else if (!node.isRoot && node.package && - node.package.name && - node.package.name !== node.name) - lock.version = `npm:${node.package.name}@${node.version}` + node.packageName && + node.packageName !== node.name) + lock.version = `npm:${node.packageName}@${node.version}` else if (node.package && node.version) lock.version = node.version diff --git a/node_modules/@npmcli/arborist/lib/update-root-package-json.js b/node_modules/@npmcli/arborist/lib/update-root-package-json.js index aba5614924ec7..4a88707b9c479 100644 --- a/node_modules/@npmcli/arborist/lib/update-root-package-json.js +++ b/node_modules/@npmcli/arborist/lib/update-root-package-json.js @@ -6,8 +6,6 @@ const {resolve} = require('path') const parseJSON = require('json-parse-even-better-errors') -const { orderDeps } = require('./dep-spec.js') - const depTypes = new Set([ 'dependencies', 'optionalDependencies', @@ -15,6 +13,20 @@ const depTypes = new Set([ 'peerDependencies', ]) +// sort alphabetically all types of deps for a given package +const orderDeps = (pkg) => { + for (const type of depTypes) { + if (pkg && pkg[type]) { + pkg[type] = Object.keys(pkg[type]) + .sort((a, b) => a.localeCompare(b)) + .reduce((res, key) => { + res[key] = pkg[type][key] + return res + }, {}) + } + } + return pkg +} const parseJsonSafe = json => { try { return parseJSON(json) diff --git a/node_modules/@npmcli/arborist/lib/vuln.js b/node_modules/@npmcli/arborist/lib/vuln.js index 8f887a3fc96cb..2561bc80646cf 100644 --- a/node_modules/@npmcli/arborist/lib/vuln.js +++ b/node_modules/@npmcli/arborist/lib/vuln.js @@ -83,6 +83,9 @@ class Vuln { if (!specObj.registry) return true + if (specObj.subSpec) + spec = specObj.subSpec.rawSpec + for (const v of this.versions) { if (satisfies(v, spec) && !satisfies(v, this.range, semverOpt)) return false diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index e7ac932e08d8f..ebc84f6fc32a9 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "2.4.1", + "version": "2.4.2", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", diff --git a/node_modules/@npmcli/git/lib/clone.js b/node_modules/@npmcli/git/lib/clone.js index 1fea5acc84621..6754fd7606009 100644 --- a/node_modules/@npmcli/git/lib/clone.js +++ b/node_modules/@npmcli/git/lib/clone.js @@ -30,7 +30,7 @@ const pickManifest = require('npm-pick-manifest') const fs = require('fs') const mkdirp = require('mkdirp') -module.exports = (repo, ref = 'HEAD', target = null, /* istanbul ignore next */ opts = {}) => +module.exports = (repo, ref = 'HEAD', target = null, opts = {}) => revs(repo, opts).then(revs => clone( repo, revs, diff --git a/node_modules/@npmcli/git/lib/spawn.js b/node_modules/@npmcli/git/lib/spawn.js index cee3a7baf4078..337164a9a012d 100644 --- a/node_modules/@npmcli/git/lib/spawn.js +++ b/node_modules/@npmcli/git/lib/spawn.js @@ -10,6 +10,11 @@ module.exports = (gitArgs, opts = {}) => { if (gitPath instanceof Error) { return Promise.reject(gitPath) } + // undocumented option, mostly only here for tests + const args = opts.allowReplace || gitArgs[0] === '--no-replace-objects' + ? gitArgs + : ['--no-replace-objects', ...gitArgs] + const log = opts.log || procLog let retry = opts.retry if (retry === null || retry === undefined) { @@ -22,11 +27,11 @@ module.exports = (gitArgs, opts = {}) => { } return promiseRetry((retry, number) => { if (number !== 1) { - log.silly('pacote', `Retrying git command: ${ - gitArgs.join(' ')} attempt # ${number}`) + log.silly('git', `Retrying git command: ${ + args.join(' ')} attempt # ${number}`) } - return spawn(gitPath, gitArgs, makeOpts(opts)) + return spawn(gitPath, args, makeOpts(opts)) .catch(er => { if (!shouldRetry(er.stderr, number)) { throw er diff --git a/node_modules/@npmcli/git/package.json b/node_modules/@npmcli/git/package.json index 9b368c31e262d..0fe94686ece20 100644 --- a/node_modules/@npmcli/git/package.json +++ b/node_modules/@npmcli/git/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/git", - "version": "2.0.8", + "version": "2.0.9", "main": "lib/index.js", "files": [ "lib/*.js" @@ -29,7 +29,7 @@ "devDependencies": { "slash": "^3.0.0", "standard": "^16.0.3", - "tap": "^14.11.0" + "tap": "^15.0.6" }, "dependencies": { "@npmcli/promise-spawn": "^1.3.2", diff --git a/node_modules/libnpmexec/CHANGELOG.md b/node_modules/libnpmexec/CHANGELOG.md index fe3ac0def623d..28cb71028868e 100644 --- a/node_modules/libnpmexec/CHANGELOG.md +++ b/node_modules/libnpmexec/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v1.1.0 + +- Add add walk up dir lookup logic to satisfy local bins, +similar to `@npmcli/run-script` + +## v1.0.1 + +- Fix `scriptShell` option name. + ## v1.0.0 - Initial implementation, moves the code that used to live in the **npm cli**, diff --git a/node_modules/libnpmexec/README.md b/node_modules/libnpmexec/README.md index a436c9a5a2bc1..fb7a771760019 100644 --- a/node_modules/libnpmexec/README.md +++ b/node_modules/libnpmexec/README.md @@ -31,7 +31,7 @@ await libexec({ - `call`: An alternative command to run when using `packages` option **String**, defaults to empty string. - `cache`: The path location to where the npm cache folder is placed **String** - `color`: Output should use color? **Boolean**, defaults to `false` - - `localBin`: Location to the `node_modules/.bin` folder of the local project **String**, defaults to empty string. + - `localBin`: Location to the `node_modules/.bin` folder of the local project to start scanning for bin files **String**, defaults to `./node_modules/.bin`. **libexec** will walk up the directory structure looking for `node_modules/.bin` folders in parent folders that might satisfy the current `arg` and will use that bin if found. - `locationMsg`: Overrides "at location" message when entering interactive mode **String** - `log`: Sets an optional logger **Object**, defaults to `proc-log` module usage. - `globalBin`: Location to the global space bin folder, same as: `$(npm bin -g)` **String**, defaults to empty string. diff --git a/node_modules/libnpmexec/lib/file-exists.js b/node_modules/libnpmexec/lib/file-exists.js new file mode 100644 index 0000000000000..a115be14b0042 --- /dev/null +++ b/node_modules/libnpmexec/lib/file-exists.js @@ -0,0 +1,29 @@ +const { resolve } = require('path') +const { promisify } = require('util') +const stat = promisify(require('fs').stat) +const walkUp = require('walk-up-path') + +const fileExists = (file) => stat(file) + .then((stat) => stat.isFile()) + .catch(() => false) + +const localFileExists = async (dir, binName, root = '/') => { + root = resolve(root).toLowerCase() + + for (const path of walkUp(resolve(dir))) { + const binDir = resolve(path, 'node_modules', '.bin') + + if (await fileExists(resolve(binDir, binName))) + return binDir + + if (path.toLowerCase() === root) + return false + } + + return false +} + +module.exports = { + fileExists, + localFileExists, +} diff --git a/node_modules/libnpmexec/lib/index.js b/node_modules/libnpmexec/lib/index.js index 906a0b5407c13..0bab753f9fda1 100644 --- a/node_modules/libnpmexec/lib/index.js +++ b/node_modules/libnpmexec/lib/index.js @@ -1,7 +1,6 @@ -const { delimiter, resolve } = require('path') +const { delimiter, dirname, resolve } = require('path') const { promisify } = require('util') const read = promisify(require('read')) -const stat = promisify(require('fs').stat) const Arborist = require('@npmcli/arborist') const ciDetect = require('@npmcli/ci-detect') @@ -12,15 +11,12 @@ const pacote = require('pacote') const readPackageJson = require('read-package-json-fast') const cacheInstallDir = require('./cache-install-dir.js') +const { fileExists, localFileExists } = require('./file-exists.js') const getBinFromManifest = require('./get-bin-from-manifest.js') const manifestMissing = require('./manifest-missing.js') const noTTY = require('./no-tty.js') const runScript = require('./run-script.js') -const fileExists = (file) => stat(file) - .then((stat) => stat.isFile()) - .catch(() => false) - /* istanbul ignore next */ const PATH = ( process.env.PATH || process.env.Path || process.env.path @@ -31,7 +27,7 @@ const exec = async (opts) => { args = [], call = '', color = false, - localBin = '', + localBin = resolve('./node_modules/.bin'), locationMsg = undefined, globalBin = '', output, @@ -72,8 +68,10 @@ const exec = async (opts) => { // the behavior of treating the single argument as a package name if (needPackageCommandSwap) { let binExists = false - if (await fileExists(`${localBin}/${args[0]}`)) { - pathArr.unshift(localBin) + const dir = dirname(dirname(localBin)) + const localBinPath = await localFileExists(dir, args[0]) + if (localBinPath) { + pathArr.unshift(localBinPath) binExists = true } else if (await fileExists(`${globalBin}/${args[0]}`)) { pathArr.unshift(globalBin) diff --git a/node_modules/libnpmexec/package.json b/node_modules/libnpmexec/package.json index 1b7d24103be7a..bc5c0483a7de7 100644 --- a/node_modules/libnpmexec/package.json +++ b/node_modules/libnpmexec/package.json @@ -1,6 +1,6 @@ { "name": "libnpmexec", - "version": "1.0.1", + "version": "1.1.0", "files": [ "lib" ], @@ -46,7 +46,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", - "tap": "^15.0.2" + "tap": "^15.0.6" }, "dependencies": { "@npmcli/arborist": "^2.3.0", @@ -58,6 +58,7 @@ "pacote": "^11.3.1", "proc-log": "^1.0.0", "read": "^1.0.7", - "read-package-json-fast": "^2.0.2" + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" } } diff --git a/node_modules/npm-packlist/index.js b/node_modules/npm-packlist/index.js index 8f62983e6f6c0..12b51316c5e27 100644 --- a/node_modules/npm-packlist/index.js +++ b/node_modules/npm-packlist/index.js @@ -26,8 +26,7 @@ const normalizePackageBin = require('npm-normalize-package-bin') // localized documentation and other use cases. Adding a `/` to // these rules, while tempting and arguably more "correct", is a // significant change that will break existing use cases. -const packageMustHaveFileNames = - 'readme|copying|license|licence|notice|changes|changelog|history' +const packageMustHaveFileNames = 'readme|copying|license|licence' const packageMustHaves = `@(${packageMustHaveFileNames}){,.*[^~$]}` const packageMustHavesRE = new RegExp(`^(${packageMustHaveFileNames})(\\..*[^~\$])?$`, 'i') diff --git a/node_modules/npm-packlist/package.json b/node_modules/npm-packlist/package.json index 1276b48413699..e909559bdb204 100644 --- a/node_modules/npm-packlist/package.json +++ b/node_modules/npm-packlist/package.json @@ -1,6 +1,6 @@ { "name": "npm-packlist", - "version": "2.1.5", + "version": "2.2.0", "description": "Get a list of the files to add from a folder into an npm package", "directories": { "test": "test" @@ -15,12 +15,12 @@ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "license": "ISC", "files": [ + "bin/index.js", "index.js" ], "devDependencies": { "mutate-fs": "^2.1.1", - "require-inject": "^1.4.4", - "tap": "^14.10.8" + "tap": "^15.0.6" }, "scripts": { "test": "tap", diff --git a/package-lock.json b/package-lock.json index 39d1a9bb828c5..a1664057fbddd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "npm", - "version": "7.11.2", + "version": "7.12.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "npm", - "version": "7.11.2", + "version": "7.12.0", "bundleDependencies": [ "@npmcli/arborist", "@npmcli/ci-detect", @@ -78,7 +78,7 @@ ], "license": "Artistic-2.0", "dependencies": { - "@npmcli/arborist": "^2.4.1", + "@npmcli/arborist": "^2.4.2", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.2.0", "@npmcli/run-script": "^1.8.5", @@ -103,7 +103,7 @@ "leven": "^3.1.0", "libnpmaccess": "^4.0.2", "libnpmdiff": "^2.0.4", - "libnpmexec": "^1.0.1", + "libnpmexec": "^1.1.0", "libnpmfund": "^1.0.2", "libnpmhook": "^6.0.2", "libnpmorg": "^2.0.2", @@ -153,10 +153,10 @@ "devDependencies": { "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", - "eslint": "^7.23.0", + "eslint": "^7.25.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", "jsdom": "^16.5.2", "licensee": "^8.1.0", @@ -712,9 +712,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.4.1.tgz", - "integrity": "sha512-LivXfK+LjtvzFjnwK6E41Pkw1C8+MYrgdXinzqpDc8MDYp7gMT0nvGvnpQd47OV2GhLRyBkbUSEcLk6P1d1s0g==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.4.2.tgz", + "integrity": "sha512-QrsMrRWzO1D2EmPQheyPz1yRnnmln6vPe4SujV4cRF0v9qIAQbD8M0dMH6K3y+w/2X3t7vg5lx20LHXsbcu7lw==", "inBundle": true, "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", @@ -784,9 +784,9 @@ } }, "node_modules/@npmcli/git": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.8.tgz", - "integrity": "sha512-LPnzyBZ+1p7+JzHVwwKycMF8M3lr1ze3wxGRnxn/QxJtk++Y3prSJQrdBDGCxJyRpFsup6J3lrRBVYBhJVrM8Q==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.9.tgz", + "integrity": "sha512-hTMbMryvOqGLwnmMBKs5usbPsJtyEsMsgXwJbmNrsEuQQh1LAIMDU77IoOrwkCg+NgQWl+ySlarJASwM3SutCA==", "inBundle": true, "dependencies": { "@npmcli/promise-spawn": "^1.3.2", @@ -2495,9 +2495,9 @@ } }, "node_modules/eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", + "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", @@ -2714,12 +2714,15 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz", - "integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", + "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", "dev": true, "engines": { - "node": ">=6" + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0" } }, "node_modules/eslint-plugin-standard": { @@ -4623,9 +4626,9 @@ } }, "node_modules/libnpmexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.0.1.tgz", - "integrity": "sha512-YK2kEhZNCcaDEqOIUWjadhJl9MgS69YHgMmGD5P3yntF0UXNOQfEqABoMTZv9ngPOJTJQGlU4Dfp2xb3bpUKzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.1.0.tgz", + "integrity": "sha512-OWpsPWtD6CAn66JouyjBfhQ9eS1mAtXgZXXd1SoAyUP3Mol+ao9IJ2THcJQcgX96keVmZkUA11uJS5ZNEd9DwA==", "inBundle": true, "dependencies": { "@npmcli/arborist": "^2.3.0", @@ -4637,7 +4640,8 @@ "pacote": "^11.3.1", "proc-log": "^1.0.0", "read": "^1.0.7", - "read-package-json-fast": "^2.0.2" + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" }, "engines": { "node": ">=10" @@ -5424,9 +5428,9 @@ } }, "node_modules/npm-packlist": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.1.5.tgz", - "integrity": "sha512-KCfK3Vi2F+PH1klYauoQzg81GQ8/GGjQRKYY6tRnpQUPKTs/1gBZSRWtTEd7jGdSn1LZL7gpAmJT+BcS55k2XQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.0.tgz", + "integrity": "sha512-d3da2MEaYliq7h+PNOHqUhlQjRm0M6gNPi6yHsZYzsCj6bLqUTWCC+JMzW/u9Aaxu8i4F1AA0eJUPUSoFU5izA==", "inBundle": true, "dependencies": { "glob": "^7.1.6", @@ -10826,9 +10830,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.4.1.tgz", - "integrity": "sha512-LivXfK+LjtvzFjnwK6E41Pkw1C8+MYrgdXinzqpDc8MDYp7gMT0nvGvnpQd47OV2GhLRyBkbUSEcLk6P1d1s0g==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.4.2.tgz", + "integrity": "sha512-QrsMrRWzO1D2EmPQheyPz1yRnnmln6vPe4SujV4cRF0v9qIAQbD8M0dMH6K3y+w/2X3t7vg5lx20LHXsbcu7lw==", "requires": { "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", @@ -10885,9 +10889,9 @@ } }, "@npmcli/git": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.8.tgz", - "integrity": "sha512-LPnzyBZ+1p7+JzHVwwKycMF8M3lr1ze3wxGRnxn/QxJtk++Y3prSJQrdBDGCxJyRpFsup6J3lrRBVYBhJVrM8Q==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.9.tgz", + "integrity": "sha512-hTMbMryvOqGLwnmMBKs5usbPsJtyEsMsgXwJbmNrsEuQQh1LAIMDU77IoOrwkCg+NgQWl+ySlarJASwM3SutCA==", "requires": { "@npmcli/promise-spawn": "^1.3.2", "lru-cache": "^6.0.0", @@ -12155,9 +12159,9 @@ } }, "eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", + "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", @@ -12381,10 +12385,11 @@ } }, "eslint-plugin-promise": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz", - "integrity": "sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ==", - "dev": true + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz", + "integrity": "sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==", + "dev": true, + "requires": {} }, "eslint-plugin-standard": { "version": "5.0.0", @@ -13689,9 +13694,9 @@ } }, "libnpmexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.0.1.tgz", - "integrity": "sha512-YK2kEhZNCcaDEqOIUWjadhJl9MgS69YHgMmGD5P3yntF0UXNOQfEqABoMTZv9ngPOJTJQGlU4Dfp2xb3bpUKzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.1.0.tgz", + "integrity": "sha512-OWpsPWtD6CAn66JouyjBfhQ9eS1mAtXgZXXd1SoAyUP3Mol+ao9IJ2THcJQcgX96keVmZkUA11uJS5ZNEd9DwA==", "requires": { "@npmcli/arborist": "^2.3.0", "@npmcli/ci-detect": "^1.3.0", @@ -13702,7 +13707,8 @@ "pacote": "^11.3.1", "proc-log": "^1.0.0", "read": "^1.0.7", - "read-package-json-fast": "^2.0.2" + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" } }, "libnpmfund": { @@ -14294,9 +14300,9 @@ } }, "npm-packlist": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.1.5.tgz", - "integrity": "sha512-KCfK3Vi2F+PH1klYauoQzg81GQ8/GGjQRKYY6tRnpQUPKTs/1gBZSRWtTEd7jGdSn1LZL7gpAmJT+BcS55k2XQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.0.tgz", + "integrity": "sha512-d3da2MEaYliq7h+PNOHqUhlQjRm0M6gNPi6yHsZYzsCj6bLqUTWCC+JMzW/u9Aaxu8i4F1AA0eJUPUSoFU5izA==", "requires": { "glob": "^7.1.6", "ignore-walk": "^3.0.3", diff --git a/package.json b/package.json index 19326a9aa59df..7360d8f9640d8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.11.2", + "version": "7.12.0", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -42,7 +42,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.4.1", + "@npmcli/arborist": "^2.4.2", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.2.0", "@npmcli/run-script": "^1.8.5", @@ -67,7 +67,7 @@ "leven": "^3.1.0", "libnpmaccess": "^4.0.2", "libnpmdiff": "^2.0.4", - "libnpmexec": "^1.0.1", + "libnpmexec": "^1.1.0", "libnpmfund": "^1.0.2", "libnpmhook": "^6.0.2", "libnpmorg": "^2.0.2", @@ -182,10 +182,10 @@ "devDependencies": { "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", - "eslint": "^7.23.0", + "eslint": "^7.25.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", "jsdom": "^16.5.2", "licensee": "^8.1.0", diff --git a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs index d6761ea30c74b..53aef86f79d1b 100644 --- a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs +++ b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs @@ -1132,7 +1132,8 @@ Show short usage output about the command specified. #### \`user-agent\` -* Default: "npm/{npm-version} node/{node-version} {platform} {arch} {ci}" +* Default: "npm/{npm-version} node/{node-version} {platform} {arch} + workspaces/{workspaces} {ci}" * Type: String Sets the User-Agent request header. The following fields are replaced with @@ -1142,6 +1143,8 @@ their actual counterparts: * \`{node-version}\` - The Node.js version in use * \`{platform}\` - The value of \`process.platform\` * \`{arch}\` - The value of \`process.arch\` +* \`{workspaces}\` - Set to \`true\` if the \`workspaces\` or \`workspace\` options + are set. * \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with \`ci/\`, or an empty string if \`ci-name\` is empty. diff --git a/test/lib/cache.js b/test/lib/cache.js index bbebae8894bab..bad0ede89e101 100644 --- a/test/lib/cache.js +++ b/test/lib/cache.js @@ -134,20 +134,21 @@ t.test('cache add pkg only', t => { }) }) -t.test('cache add pkg w/ spec modifier', t => { +t.test('cache add multiple pkgs', t => { t.teardown(() => { logOutput = [] tarballStreamSpec = '' tarballStreamOpts = {} }) - cache.exec(['add', 'mypkg', 'latest'], err => { + cache.exec(['add', 'mypkg', 'anotherpkg'], err => { t.error(err) t.strictSame(logOutput, [ - ['silly', 'cache add', 'args', ['mypkg', 'latest']], - ['silly', 'cache add', 'spec', 'mypkg@latest'], + ['silly', 'cache add', 'args', ['mypkg', 'anotherpkg']], + ['silly', 'cache add', 'spec', 'mypkg'], + ['silly', 'cache add', 'spec', 'anotherpkg'], ], 'logs correctly') - t.equal(tarballStreamSpec, 'mypkg@latest', 'passes the correct spec to pacote') + t.equal(tarballStreamSpec, 'anotherpkg', 'passes the correct spec to pacote') t.same(tarballStreamOpts, npm.flatOptions, 'passes the correct options to pacote') t.end() }) diff --git a/test/lib/exec.js b/test/lib/exec.js index 5ecc73274876a..3d2da32a76675 100644 --- a/test/lib/exec.js +++ b/test/lib/exec.js @@ -121,11 +121,15 @@ t.afterEach(() => { t.test('npx foo, bin already exists locally', t => { const path = t.testdir({ - foo: 'just some file', + node_modules: { + '.bin': { + foo: 'just some file', + }, + }, }) PROGRESS_IGNORED = true - npm.localBin = path + npm.localBin = resolve(path, 'node_modules', '.bin') exec.exec(['foo', 'one arg', 'two arg'], er => { t.error(er, 'npm exec') @@ -137,7 +141,7 @@ t.test('npx foo, bin already exists locally', t => { stdioString: true, event: 'npx', env: { - PATH: [path, ...PATH].join(delimiter), + PATH: [npm.localBin, ...PATH].join(delimiter), }, stdio: 'inherit', }]) @@ -147,11 +151,15 @@ t.test('npx foo, bin already exists locally', t => { t.test('npx foo, bin already exists globally', t => { const path = t.testdir({ - foo: 'just some file', + node_modules: { + '.bin': { + foo: 'just some file', + }, + }, }) PROGRESS_IGNORED = true - npm.globalBin = path + npm.globalBin = resolve(path, 'node_modules', '.bin') exec.exec(['foo', 'one arg', 'two arg'], er => { t.error(er, 'npm exec') @@ -163,7 +171,7 @@ t.test('npx foo, bin already exists globally', t => { stdioString: true, event: 'npx', env: { - PATH: [path, ...PATH].join(delimiter), + PATH: [npm.globalBin, ...PATH].join(delimiter), }, stdio: 'inherit', }]) diff --git a/test/lib/utils/config/definitions.js b/test/lib/utils/config/definitions.js index f735223655f50..49e4152883795 100644 --- a/test/lib/utils/config/definitions.js +++ b/test/lib/utils/config/definitions.js @@ -729,7 +729,7 @@ t.test('user-agent', t => { } const flat = {} const expectNoCI = `npm/1.2.3 node/9.8.7 ` + - `${process.platform} ${process.arch}` + `${process.platform} ${process.arch} workspaces/false` definitions['user-agent'].flatten('user-agent', obj, flat) t.equal(flat.userAgent, expectNoCI) t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') @@ -742,6 +742,23 @@ t.test('user-agent', t => { t.equal(flat.userAgent, expectCI) t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') t.equal(obj['user-agent'], flat.userAgent, 'config user-agent template is translated') + + delete obj['ci-name'] + obj.workspaces = true + obj['user-agent'] = definitions['user-agent'].default + const expectWorkspaces = expectNoCI.replace('workspaces/false', 'workspaces/true') + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectWorkspaces) + t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') + t.equal(obj['user-agent'], flat.userAgent, 'config user-agent template is translated') + + delete obj.workspaces + obj.workspace = ['foo'] + obj['user-agent'] = definitions['user-agent'].default + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectWorkspaces) + t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') + t.equal(obj['user-agent'], flat.userAgent, 'config user-agent template is translated') t.end() })