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 0de10044bd77d..789d1763fb22a 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js +++ b/node_modules/@npmcli/arborist/lib/arborist/build-ideal-tree.js @@ -1121,8 +1121,11 @@ This is a one-time fix-up, please be patient... // we don't like it. always fail strictly, always allow forcibly or // in non-strict mode if it's not our fault. don't warn here, because // we are going to warn again when we place the deps, if we end up - // overriding for something else. - if (conflictOK) + // overriding for something else. If the thing that has this dep + // isn't also required, then there's a good chance we won't need it, + // so allow it for now and let it conflict if it turns out to actually + // be necessary for the installation. + if (conflictOK || !required.has(edge.from)) continue // ok, it's the root, or we're in unforced strict mode, so this is bad diff --git a/node_modules/@npmcli/arborist/lib/arborist/reify.js b/node_modules/@npmcli/arborist/lib/arborist/reify.js index b33823e461223..9854d2d0b59d3 100644 --- a/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -16,6 +16,7 @@ const mkdirp = require('mkdirp-infer-owner') const moveFile = require('@npmcli/move-file') const rimraf = promisify(require('rimraf')) const packageContents = require('@npmcli/installed-package-contents') +const { checkEngine, checkPlatform } = require('npm-install-checks') const treeCheck = require('../tree-check.js') const relpath = require('../relpath.js') @@ -43,6 +44,7 @@ const _loadTrees = Symbol.for('loadTrees') const _diffTrees = Symbol.for('diffTrees') const _createSparseTree = Symbol.for('createSparseTree') const _loadShrinkwrapsAndUpdateTrees = Symbol.for('loadShrinkwrapsAndUpdateTrees') +const _shrinkwrapUnpacked = Symbol('shrinkwrapUnpacked') const _reifyNode = Symbol.for('reifyNode') const _extractOrLink = Symbol('extractOrLink') // defined by rebuild mixin @@ -102,6 +104,7 @@ module.exports = cls => class Reifier extends cls { this.diff = null this[_retiredPaths] = {} + this[_shrinkwrapUnpacked] = new Set() this[_retiredUnchanged] = {} this[_sparseTreeDirs] = new Set() this[_sparseTreeRoots] = new Set() @@ -404,7 +407,8 @@ module.exports = cls => class Reifier extends cls { // shrinkwrap nodes define their dependency branches with a file, so // we need to unpack them, read that shrinkwrap file, and then update // the tree by calling loadVirtual with the node as the root. - [_loadShrinkwrapsAndUpdateTrees] (seen = new Set()) { + [_loadShrinkwrapsAndUpdateTrees] () { + const seen = this[_shrinkwrapUnpacked] const shrinkwraps = this.diff.leaves .filter(d => (d.action === 'CHANGE' || d.action === 'ADD') && d.ideal.hasShrinkwrap && !seen.has(d.ideal) && @@ -428,6 +432,8 @@ module.exports = cls => class Reifier extends cls { // reload the diff and sparse tree because the ideal tree changed .then(() => this[_diffTrees]()) .then(() => this[_createSparseTree]()) + .then(() => this[_addOmitsToTrashList]()) + .then(() => this[_loadShrinkwrapsAndUpdateTrees]()) .then(() => process.emit('timeEnd', 'reify:loadShrinkwraps')) } @@ -446,7 +452,19 @@ module.exports = cls => class Reifier extends cls { process.emit('time', timer) this.addTracker('reify', node.name, node.location) + const { npmVersion, nodeVersion } = this.options const p = Promise.resolve() + .then(() => { + // when we reify an optional node, check the engine and platform + // first. be sure to ignore the --force and --engine-strict flags, + // since we always want to skip any optional packages we can't install. + // these checks throwing will result in a rollback and removal + // of the mismatches + if (node.optional) { + checkEngine(node.package, npmVersion, nodeVersion, false) + checkPlatform(node.package, false) + } + }) .then(() => this[_checkBins](node)) .then(() => this[_extractOrLink](node)) .then(() => this[_warnDeprecated](node)) @@ -718,7 +736,7 @@ module.exports = cls => class Reifier extends cls { const node = diff.ideal const bd = node.package.bundleDependencies - const sw = node.hasShrinkwrap + const sw = this[_shrinkwrapUnpacked].has(node) // check whether we still need to unpack this one. // test the inDepBundle last, since that's potentially a tree walk. diff --git a/node_modules/@npmcli/arborist/lib/audit-report.js b/node_modules/@npmcli/arborist/lib/audit-report.js index 15e17330addc0..77cd6511aea3b 100644 --- a/node_modules/@npmcli/arborist/lib/audit-report.js +++ b/node_modules/@npmcli/arborist/lib/audit-report.js @@ -268,8 +268,8 @@ class AuditReport extends Map { id, url, title, - severity, - vulnerable_versions, + severity = 'high', + vulnerable_versions = '*', module_name: name, } = advisory bulk[name] = bulk[name] || [] diff --git a/node_modules/@npmcli/arborist/lib/node.js b/node_modules/@npmcli/arborist/lib/node.js index 9a6b86e4021b8..fa39bed5ef9d4 100644 --- a/node_modules/@npmcli/arborist/lib/node.js +++ b/node_modules/@npmcli/arborist/lib/node.js @@ -731,7 +731,6 @@ class Node { // 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') - this[_loadDepType](this.package.dependencies, 'prod') // Linked targets that are disconnected from the tree are tops, // but don't have a 'path' field, only a 'realpath', because we @@ -755,6 +754,8 @@ class Node { this[_loadDepType](peerDependencies, 'peer') this[_loadDepType](peerOptional, 'peerOptional') } + + this[_loadDepType](this.package.dependencies, 'prod') } [_loadDepType] (obj, type) { @@ -763,8 +764,10 @@ class Node { 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 - if (!this.edgesOut.get(name)) + const current = this.edgesOut.get(name) + if (!current || current.dev && type === 'prod') new Edge({ from, name, spec, accept, type }) } } 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 735ebd10ad16f..aba5614924ec7 100644 --- a/node_modules/@npmcli/arborist/lib/update-root-package-json.js +++ b/node_modules/@npmcli/arborist/lib/update-root-package-json.js @@ -15,11 +15,18 @@ const depTypes = new Set([ 'peerDependencies', ]) +const parseJsonSafe = json => { + try { + return parseJSON(json) + } catch (er) { + return null + } +} + const updateRootPackageJson = async tree => { const filename = resolve(tree.path, 'package.json') - const originalContent = await readFile(filename, 'utf8') - .then(data => parseJSON(data)) - .catch(() => null) + const originalJson = await readFile(filename, 'utf8').catch(() => null) + const originalContent = parseJsonSafe(originalJson) const depsData = orderDeps({ ...tree.package, @@ -36,12 +43,29 @@ const updateRootPackageJson = async tree => { } // if there's no package.json, just use internal pkg info as source of truth - const packageJsonContent = originalContent || depsData + // clone the object though, so we can still refer to what it originally was + const packageJsonContent = !originalContent ? depsData + : Object.assign({}, originalContent) // loop through all types of dependencies and update package json content for (const type of depTypes) packageJsonContent[type] = depsData[type] + // if original package.json had dep in peerDeps AND deps, preserve that. + const { dependencies: origProd, peerDependencies: origPeer } = + originalContent || {} + const { peerDependencies: newPeer } = packageJsonContent + if (origProd && origPeer && newPeer) { + // we have original prod/peer deps, and new peer deps + // copy over any that were in both in the original + for (const name of Object.keys(origPeer)) { + if (origProd[name] !== undefined && newPeer[name] !== undefined) { + packageJsonContent.dependencies = packageJsonContent.dependencies || {} + packageJsonContent.dependencies[name] = newPeer[name] + } + } + } + // format content const { [Symbol.for('indent')]: indent, @@ -52,7 +76,8 @@ const updateRootPackageJson = async tree => { const content = (JSON.stringify(packageJsonContent, null, format) + '\n') .replace(/\n/g, eol) - return writeFile(filename, content) + if (content !== originalJson) + return writeFile(filename, content) } module.exports = updateRootPackageJson diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index b57922dd6c6d2..678c51c980e63 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,11 +1,11 @@ { "name": "@npmcli/arborist", - "version": "2.2.2", + "version": "2.2.3", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.6", "@npmcli/map-workspaces": "^1.0.2", - "@npmcli/metavuln-calculator": "^1.0.1", + "@npmcli/metavuln-calculator": "^1.1.0", "@npmcli/move-file": "^1.1.0", "@npmcli/name-from-folder": "^1.0.1", "@npmcli/node-gyp": "^1.0.1", diff --git a/node_modules/@npmcli/metavuln-calculator/lib/advisory.js b/node_modules/@npmcli/metavuln-calculator/lib/advisory.js index 15340f5dc70e8..d0900e3732846 100644 --- a/node_modules/@npmcli/metavuln-calculator/lib/advisory.js +++ b/node_modules/@npmcli/metavuln-calculator/lib/advisory.js @@ -35,11 +35,15 @@ class Advisory { this.url = null } - this.severity = source.severity + this.severity = source.severity || 'high' this.versions = [] this.vulnerableVersions = [] + // advisories have the range, metavulns do not - this.range = source.vulnerable_versions || null + // if an advisory doesn't specify range, assume all are vulnerable + this.range = this.type === 'advisory' ? source.vulnerable_versions || '*' + : null + this.id = hash(this) this[_packument] = null @@ -66,12 +70,12 @@ class Advisory { // load up the data from a cache entry and a fetched packument load (cached, packument) { // basic data integrity gutcheck - if (!cached || typeof cached !== 'object') { + if (!cached || typeof cached !== 'object') throw new TypeError('invalid cached data, expected object') - } - if (!packument || typeof packument !== 'object') { + + if (!packument || typeof packument !== 'object') throw new TypeError('invalid packument data, expected object') - } + if (cached.id && cached.id !== this.id) { throw Object.assign(new Error('loading from incorrect cache entry'), { expected: this.id, @@ -103,9 +107,8 @@ class Advisory { if (!this.versions.includes(v)) { versionsAdded.push(v) this.versions.push(v) - } else if (!pakuVersions.includes(v)) { + } else if (!pakuVersions.includes(v)) versionsRemoved.push(v) - } } // strip out any removed versions from our lists, and sort by semver @@ -249,9 +252,8 @@ class Advisory { // try to pick a version of the dep that isn't vulnerable const avoid = this[_source].range - if (bundled) { + if (bundled) return semver.intersects(spec, avoid, semverOpt) - } return this[_source].testSpec(spec) } @@ -261,9 +263,8 @@ class Advisory { // consistent across multiple versions, so memoize this as well, in case // we're testing lots of versions. const memo = this[_specVulnMemo] - if (memo.has(spec)) { + if (memo.has(spec)) return memo.get(spec) - } const res = this[_testSpec](spec) memo.set(spec, res) @@ -379,14 +380,14 @@ class Advisory { // marking as vulnerable if the midpoint item we picked is. if (!/-/.test(String(pre[0]))) { const midVuln = this.testVersion(pre[pre.length - 1]) - while (/-/.test(String(pre[pre.length-1]))) { + while (/-/.test(String(pre[pre.length - 1]))) { const v = pre.pop() if (midVuln) this[_markVulnerable](v) } } - if (!/-/.test(String(post[post.length-1]))) { + if (!/-/.test(String(post[post.length - 1]))) { const midVuln = this.testVersion(post[0]) while (/-/.test(String(post[0]))) { const v = post.shift() diff --git a/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js b/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js index 61602898db7ab..35e83d02a1b63 100644 --- a/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js +++ b/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js @@ -2,12 +2,14 @@ module.exports = (mani, name) => { // skip dev because that only matters at the root, // where we aren't fetching a manifest from the registry // with multiple versions anyway. - return mani.dependencies && typeof mani.dependencies[name] === 'string' - ? mani.dependencies[name] - : mani.optionalDependencies && typeof mani.optionalDependencies[name] === 'string' - ? mani.optionalDependencies[name] - : mani.peerDependencies && typeof mani.peerDependencies[name] === 'string' - ? mani.peerDependencies[name] + const { + dependencies: deps = {}, + optionalDependencies: optDeps = {}, + peerDependencies: peerDeps = {}, + } = mani + + return typeof deps[name] === 'string' ? deps[name] + : typeof optDeps[name] === 'string' ? optDeps[name] + : typeof peerDeps[name] === 'string' ? peerDeps[name] : null } - diff --git a/node_modules/@npmcli/metavuln-calculator/lib/index.js b/node_modules/@npmcli/metavuln-calculator/lib/index.js index e8d88641776cb..02c1ed018b8b5 100644 --- a/node_modules/@npmcli/metavuln-calculator/lib/index.js +++ b/node_modules/@npmcli/metavuln-calculator/lib/index.js @@ -53,7 +53,7 @@ class Calculator { // load packument and cached advisory const [cached, packument] = await Promise.all([ this[_cacheGet](advisory), - this[_packument](name) + this[_packument](name), ]) process.emit('time', `metavuln:load:${k}`) advisory.load(cached, packument) diff --git a/node_modules/@npmcli/metavuln-calculator/package.json b/node_modules/@npmcli/metavuln-calculator/package.json index 636382170f177..f7a4f5cc47a7e 100644 --- a/node_modules/@npmcli/metavuln-calculator/package.json +++ b/node_modules/@npmcli/metavuln-calculator/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/metavuln-calculator", - "version": "1.0.2", + "version": "1.1.0", "main": "lib/index.js", "files": [ "lib" @@ -11,22 +11,32 @@ "license": "ISC", "scripts": { "test": "tap", + "posttest": "npm run lint", "snap": "tap", + "postsnap": "npm run lint", "preversion": "npm test", "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags" + "prepublishOnly": "git push origin --follow-tags", + "eslint": "eslint", + "lint": "npm run eslint -- \"lib/**/*.js\" \"test/**/*.js\"", + "lintfix": "npm run lint -- --fix" }, "tap": { "check-coverage": true, "coverage-map": "map.js" }, "devDependencies": { - "tap": "^14.10.8", - "require-inject": "^1.4.4" + "eslint": "^7.20.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-standard": "^4.1.0", + "require-inject": "^1.4.4", + "tap": "^14.10.8" }, "dependencies": { - "pacote": "^11.1.11", "cacache": "^15.0.5", + "pacote": "^11.1.11", "semver": "^7.3.2" } } diff --git a/package-lock.json b/package-lock.json index 37fa0e6326846..30d88b935d09e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -252,7 +252,7 @@ ], "license": "Artistic-2.0", "dependencies": { - "@npmcli/arborist": "^2.2.2", + "@npmcli/arborist": "^2.2.3", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", "@npmcli/run-script": "^1.8.3", @@ -813,14 +813,14 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.2.tgz", - "integrity": "sha512-X6sl303t4UQUD42JKqgicOG1kEUoncu1x8IH4s3YUq/m3ALIMFAsorJ8DNa8RDVbjOvJ+aB9X9Aif/pB1xQLog==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.3.tgz", + "integrity": "sha512-K7yWh9uQZ87o8ktcsr+5ummcshP6Jsif05T4F7j1jA8WNCN6CP6I/1ePEsi1fTjCwKF/TAYn2gLX719LbW7gCA==", "inBundle": true, "dependencies": { "@npmcli/installed-package-contents": "^1.0.6", "@npmcli/map-workspaces": "^1.0.2", - "@npmcli/metavuln-calculator": "^1.0.1", + "@npmcli/metavuln-calculator": "^1.1.0", "@npmcli/move-file": "^1.1.0", "@npmcli/name-from-folder": "^1.0.1", "@npmcli/node-gyp": "^1.0.1", @@ -933,9 +933,9 @@ } }, "node_modules/@npmcli/metavuln-calculator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.0.2.tgz", - "integrity": "sha512-mQuOq4sZYOdjz49KH/DUwL+FsLVxiN5KDO/bnBXFon+kUxGHDoUYL+bvOD1o00IYL1q3LtXoPlFlQ+OYJQffhw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.1.0.tgz", + "integrity": "sha512-fb51NyiWHjeqqFez9FXhvr+E2Dv4ZjPGVgnj8QC1xjHRSw4gMRIO8pNCzU11WYQ2wZxoHBhPMgovZGxP5lP74g==", "inBundle": true, "dependencies": { "cacache": "^15.0.5", @@ -11221,13 +11221,13 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.2.tgz", - "integrity": "sha512-X6sl303t4UQUD42JKqgicOG1kEUoncu1x8IH4s3YUq/m3ALIMFAsorJ8DNa8RDVbjOvJ+aB9X9Aif/pB1xQLog==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.2.3.tgz", + "integrity": "sha512-K7yWh9uQZ87o8ktcsr+5ummcshP6Jsif05T4F7j1jA8WNCN6CP6I/1ePEsi1fTjCwKF/TAYn2gLX719LbW7gCA==", "requires": { "@npmcli/installed-package-contents": "^1.0.6", "@npmcli/map-workspaces": "^1.0.2", - "@npmcli/metavuln-calculator": "^1.0.1", + "@npmcli/metavuln-calculator": "^1.1.0", "@npmcli/move-file": "^1.1.0", "@npmcli/name-from-folder": "^1.0.1", "@npmcli/node-gyp": "^1.0.1", @@ -11316,9 +11316,9 @@ } }, "@npmcli/metavuln-calculator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.0.2.tgz", - "integrity": "sha512-mQuOq4sZYOdjz49KH/DUwL+FsLVxiN5KDO/bnBXFon+kUxGHDoUYL+bvOD1o00IYL1q3LtXoPlFlQ+OYJQffhw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.1.0.tgz", + "integrity": "sha512-fb51NyiWHjeqqFez9FXhvr+E2Dv4ZjPGVgnj8QC1xjHRSw4gMRIO8pNCzU11WYQ2wZxoHBhPMgovZGxP5lP74g==", "requires": { "cacache": "^15.0.5", "pacote": "^11.1.11", diff --git a/package.json b/package.json index e3c07774a2d00..f0cd794635c9f 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.2.2", + "@npmcli/arborist": "^2.2.3", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^1.2.9", "@npmcli/run-script": "^1.8.3",