diff options
author | Gar <gar+gh@danger.computer> | 2021-09-28 20:05:08 +0300 |
---|---|---|
committer | Gar <gar+gh@danger.computer> | 2021-09-28 20:05:08 +0300 |
commit | e94ddeaca1e75ecc8f54ebcb3df222965e3635d1 (patch) | |
tree | b2ec351450e8392bebd0410fe6367101a1e1b4b2 /node_modules/@npmcli | |
parent | 075fe50565ae5c66df727cdd7df9dd5ed8cd4015 (diff) |
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
Diffstat (limited to 'node_modules/@npmcli')
12 files changed, 111 insertions, 37 deletions
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 c1b64a461..3c1cbd44a 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 c45024d16..b7876b114 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 fa0aa0746..f19601167 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 743794f4b..e48bdd76b 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 2e6c207b3..de97cdc29 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 7e2e1a0e2..6be59093c 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 d7cc7d935..6edd94a38 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 af24ccb95..74925d96d 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 6e7e0e31f..ed2813024 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 da44e7c34..a818cf318 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 384ba447d..1eed06640 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 5d0e31af9..b39818d48 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", |