@npmcli/arborist 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/add-rm-pkg-deps.js +84 -82
- package/lib/arborist/build-ideal-tree.js +1 -0
- package/lib/arborist/index.js +3 -0
- package/lib/arborist/reify.js +48 -15
- package/lib/audit-report.js +13 -6
- package/lib/inventory.js +1 -1
- package/lib/node.js +23 -22
- package/lib/printable.js +2 -2
- package/lib/shrinkwrap.js +5 -5
- package/lib/update-root-package-json.js +14 -2
- package/lib/vuln.js +3 -0
- package/package.json +1 -1
- package/lib/dep-spec.js +0 -43
package/lib/add-rm-pkg-deps.js
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
// add and remove dependency specs to/from pkg manifest
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const others = new Set([
|
|
5
|
-
'dependencies',
|
|
6
|
-
'optionalDependencies',
|
|
7
|
-
'devDependencies',
|
|
8
|
-
'peerDependenciesMeta',
|
|
9
|
-
'peerDependencies',
|
|
10
|
-
])
|
|
11
|
-
|
|
12
|
-
switch (type) {
|
|
13
|
-
case 'prod':
|
|
14
|
-
others.delete('dependencies')
|
|
15
|
-
break
|
|
16
|
-
case 'dev':
|
|
17
|
-
others.delete('devDependencies')
|
|
18
|
-
others.delete('peerDependencies')
|
|
19
|
-
others.delete('peerDependenciesMeta')
|
|
20
|
-
break
|
|
21
|
-
case 'optional':
|
|
22
|
-
others.delete('optionalDependencies')
|
|
23
|
-
break
|
|
24
|
-
case 'peer':
|
|
25
|
-
case 'peerOptional':
|
|
26
|
-
others.delete('devDependencies')
|
|
27
|
-
others.delete('peerDependencies')
|
|
28
|
-
others.delete('peerDependenciesMeta')
|
|
29
|
-
break
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (const other of others)
|
|
33
|
-
deleteSubKey(pkg, other, name)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const add = ({pkg, add, saveBundle, saveType}) => {
|
|
3
|
+
const add = ({pkg, add, saveBundle, saveType, log}) => {
|
|
37
4
|
for (const spec of add)
|
|
38
|
-
addSingle({pkg, spec, saveBundle, saveType})
|
|
5
|
+
addSingle({pkg, spec, saveBundle, saveType, log})
|
|
39
6
|
|
|
40
7
|
return pkg
|
|
41
8
|
}
|
|
42
9
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
10
|
+
// Canonical source of both the map between saveType and where it correlates to
|
|
11
|
+
// in the package, and the names of all our dependencies attributes
|
|
12
|
+
const saveTypeMap = new Map([
|
|
13
|
+
['dev', 'devDependencies'],
|
|
14
|
+
['optional', 'optionalDependencies'],
|
|
15
|
+
['prod', 'dependencies'],
|
|
16
|
+
['peerOptional', 'peerDependencies'],
|
|
17
|
+
['peer', 'peerDependencies'],
|
|
18
|
+
])
|
|
46
19
|
|
|
20
|
+
const addSingle = ({pkg, spec, saveBundle, saveType, log}) => {
|
|
47
21
|
const { name, rawSpec } = spec
|
|
48
|
-
removeFromOthers(name, saveType, pkg)
|
|
49
|
-
const type = saveType === 'prod' ? 'dependencies'
|
|
50
|
-
: saveType === 'optional' ? 'optionalDependencies'
|
|
51
|
-
: saveType === 'peer' || saveType === 'peerOptional' ? 'peerDependencies'
|
|
52
|
-
: saveType === 'dev' ? 'devDependencies'
|
|
53
|
-
: /* istanbul ignore next */ null
|
|
54
22
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
23
|
+
// if the user does not give us a type, we infer which type(s)
|
|
24
|
+
// to keep based on the same order of priority we do when
|
|
25
|
+
// building the tree as defined in the _loadDeps method of
|
|
26
|
+
// the node class.
|
|
27
|
+
if (!saveType)
|
|
28
|
+
saveType = inferSaveType(pkg, spec.name)
|
|
29
|
+
|
|
30
|
+
if (saveType === 'prod') {
|
|
31
|
+
// a production dependency can only exist as production (rpj ensures it
|
|
32
|
+
// doesn't coexist w/ optional)
|
|
33
|
+
deleteSubKey(pkg, 'devDependencies', name, 'dependencies', log)
|
|
34
|
+
deleteSubKey(pkg, 'peerDependencies', name, 'dependencies', log)
|
|
35
|
+
} else if (saveType === 'dev') {
|
|
36
|
+
// a dev dependency may co-exist as peer, or optional, but not production
|
|
37
|
+
deleteSubKey(pkg, 'dependencies', name, 'devDependencies', log)
|
|
38
|
+
} else if (saveType === 'optional') {
|
|
39
|
+
// an optional dependency may co-exist as dev (rpj ensures it doesn't
|
|
40
|
+
// coexist w/ prod)
|
|
41
|
+
deleteSubKey(pkg, 'peerDependencies', name, 'optionalDependencies', log)
|
|
42
|
+
} else { // peer or peerOptional is all that's left
|
|
43
|
+
// a peer dependency may coexist as dev
|
|
44
|
+
deleteSubKey(pkg, 'dependencies', name, 'peerDependencies', log)
|
|
45
|
+
deleteSubKey(pkg, 'optionalDependencies', name, 'peerDependencies', log)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const depType = saveTypeMap.get(saveType)
|
|
49
|
+
|
|
50
|
+
pkg[depType] = pkg[depType] || {}
|
|
51
|
+
if (rawSpec !== '' || pkg[depType][name] === undefined)
|
|
52
|
+
pkg[depType][name] = rawSpec || '*'
|
|
53
|
+
if (saveType === 'optional') {
|
|
54
|
+
// Affordance for previous npm versions that require this behaviour
|
|
55
|
+
pkg.dependencies = pkg.dependencies || {}
|
|
56
|
+
pkg.dependencies[name] = pkg.optionalDependencies[name]
|
|
57
|
+
}
|
|
58
58
|
|
|
59
59
|
if (saveType === 'peer' || saveType === 'peerOptional') {
|
|
60
60
|
const pdm = pkg.peerDependenciesMeta || {}
|
|
@@ -79,47 +79,49 @@ const addSingle = ({pkg, spec, saveBundle, saveType}) => {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return 'peer'
|
|
97
|
-
} else if (devDeps && devDeps[name] !== undefined)
|
|
98
|
-
return 'dev'
|
|
99
|
-
else if (optDeps && optDeps[name] !== undefined)
|
|
100
|
-
return 'optional'
|
|
101
|
-
else
|
|
102
|
-
return 'prod'
|
|
82
|
+
// Finds where the package is already in the spec and infers saveType from that
|
|
83
|
+
const inferSaveType = (pkg, name) => {
|
|
84
|
+
for (const saveType of saveTypeMap.keys()) {
|
|
85
|
+
if (hasSubKey(pkg, saveTypeMap.get(saveType), name)) {
|
|
86
|
+
if (
|
|
87
|
+
saveType === 'peerOptional' &&
|
|
88
|
+
(!hasSubKey(pkg, 'peerDependenciesMeta', name) ||
|
|
89
|
+
!pkg.peerDependenciesMeta[name].optional)
|
|
90
|
+
)
|
|
91
|
+
return 'peer'
|
|
92
|
+
return saveType
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return 'prod'
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const hasSubKey = (pkg, depType, name) => {
|
|
99
|
+
return pkg[depType] && Object.prototype.hasOwnProperty.call(pkg[depType], name)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Removes a subkey and warns about it if it's being replaced
|
|
103
|
+
const deleteSubKey = (pkg, depType, name, replacedBy, log) => {
|
|
104
|
+
if (hasSubKey(pkg, depType, name)) {
|
|
105
|
+
if (replacedBy && log)
|
|
106
|
+
log.warn('idealTree', `Removing ${depType}.${name} in favor of ${replacedBy}.${name}`)
|
|
107
|
+
delete pkg[depType][name]
|
|
108
|
+
|
|
109
|
+
// clean up peerDependenciesMeta if we are removing something from peerDependencies
|
|
110
|
+
if (depType === 'peerDependencies' && pkg.peerDependenciesMeta) {
|
|
111
|
+
delete pkg.peerDependenciesMeta[name]
|
|
112
|
+
if (!Object.keys(pkg.peerDependenciesMeta).length)
|
|
113
|
+
delete pkg.peerDependenciesMeta
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!Object.keys(pkg[depType]).length)
|
|
117
|
+
delete pkg[depType]
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
const rm = (pkg, rm) => {
|
|
114
|
-
for (const
|
|
115
|
-
'dependencies',
|
|
116
|
-
'optionalDependencies',
|
|
117
|
-
'peerDependencies',
|
|
118
|
-
'peerDependenciesMeta',
|
|
119
|
-
'devDependencies',
|
|
120
|
-
]) {
|
|
122
|
+
for (const depType of new Set(saveTypeMap.values())) {
|
|
121
123
|
for (const name of rm)
|
|
122
|
-
deleteSubKey(pkg,
|
|
124
|
+
deleteSubKey(pkg, depType, name)
|
|
123
125
|
}
|
|
124
126
|
if (pkg.bundleDependencies) {
|
|
125
127
|
pkg.bundleDependencies = pkg.bundleDependencies
|
|
@@ -130,4 +132,4 @@ const rm = (pkg, rm) => {
|
|
|
130
132
|
return pkg
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
module.exports = { add, rm }
|
|
135
|
+
module.exports = { add, rm, saveTypeMap, hasSubKey }
|
package/lib/arborist/index.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
const {resolve} = require('path')
|
|
30
30
|
const {homedir} = require('os')
|
|
31
31
|
const procLog = require('../proc-log.js')
|
|
32
|
+
const { saveTypeMap } = require('../add-rm-pkg-deps.js')
|
|
32
33
|
|
|
33
34
|
const mixins = [
|
|
34
35
|
require('../tracker.js'),
|
|
@@ -57,6 +58,8 @@ class Arborist extends Base {
|
|
|
57
58
|
packumentCache: options.packumentCache || new Map(),
|
|
58
59
|
log: options.log || procLog,
|
|
59
60
|
}
|
|
61
|
+
if (options.saveType && !saveTypeMap.get(options.saveType))
|
|
62
|
+
throw new Error(`Invalid saveType ${options.saveType}`)
|
|
60
63
|
this.cache = resolve(this.options.cache)
|
|
61
64
|
this.path = resolve(this.options.path)
|
|
62
65
|
process.emit('timeEnd', 'arborist:ctor')
|
package/lib/arborist/reify.js
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
const onExit = require('../signal-handling.js')
|
|
4
4
|
const pacote = require('pacote')
|
|
5
5
|
const rpj = require('read-package-json-fast')
|
|
6
|
-
const { updateDepSpec } = require('../dep-spec.js')
|
|
7
6
|
const AuditReport = require('../audit-report.js')
|
|
8
|
-
const {subset} = require('semver')
|
|
7
|
+
const {subset, intersects} = require('semver')
|
|
9
8
|
const npa = require('npm-package-arg')
|
|
10
9
|
|
|
11
10
|
const {dirname, resolve, relative} = require('path')
|
|
@@ -28,6 +27,7 @@ const promiseAllRejectLate = require('promise-all-reject-late')
|
|
|
28
27
|
const optionalSet = require('../optional-set.js')
|
|
29
28
|
const updateRootPackageJson = require('../update-root-package-json.js')
|
|
30
29
|
const calcDepFlags = require('../calc-dep-flags.js')
|
|
30
|
+
const { saveTypeMap, hasSubKey } = require('../add-rm-pkg-deps.js')
|
|
31
31
|
|
|
32
32
|
const _retiredPaths = Symbol('retiredPaths')
|
|
33
33
|
const _retiredUnchanged = Symbol('retiredUnchanged')
|
|
@@ -406,11 +406,14 @@ module.exports = cls => class Reifier extends cls {
|
|
|
406
406
|
return
|
|
407
407
|
|
|
408
408
|
process.emit('time', 'reify:trashOmits')
|
|
409
|
+
// node.parent is checked to make sure this is a node that's in the tree, and
|
|
410
|
+
// not the parent-less top level nodes
|
|
409
411
|
const filter = node =>
|
|
410
|
-
node.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
node.isDescendantOf(this.idealTree) &&
|
|
413
|
+
(node.peer && this[_omitPeer] ||
|
|
414
|
+
node.dev && this[_omitDev] ||
|
|
415
|
+
node.optional && this[_omitOptional] ||
|
|
416
|
+
node.devOptional && this[_omitOptional] && this[_omitDev])
|
|
414
417
|
|
|
415
418
|
for (const node of this.idealTree.inventory.filter(filter))
|
|
416
419
|
this[_addNodeToTrashList](node)
|
|
@@ -539,8 +542,8 @@ module.exports = cls => class Reifier extends cls {
|
|
|
539
542
|
// Do the best with what we have, or else remove it from the tree
|
|
540
543
|
// entirely, since we can't possibly reify it.
|
|
541
544
|
const res = node.resolved ? `${node.name}@${this[_registryResolved](node.resolved)}`
|
|
542
|
-
: node.
|
|
543
|
-
? `${node.
|
|
545
|
+
: node.packageName && node.version
|
|
546
|
+
? `${node.packageName}@${node.version}`
|
|
544
547
|
: null
|
|
545
548
|
|
|
546
549
|
// no idea what this thing is. remove it from the tree.
|
|
@@ -959,6 +962,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
959
962
|
const spec = subSpec ? subSpec.rawSpec : rawSpec
|
|
960
963
|
const child = root.children.get(name)
|
|
961
964
|
|
|
965
|
+
let newSpec
|
|
962
966
|
if (req.registry) {
|
|
963
967
|
const version = child.version
|
|
964
968
|
const prefixRange = version ? this[_savePrefix] + version : '*'
|
|
@@ -970,16 +974,17 @@ module.exports = cls => class Reifier extends cls {
|
|
|
970
974
|
const isRange = (subSpec || req).type === 'range'
|
|
971
975
|
const range = !isRange || subset(prefixRange, spec, { loose: true })
|
|
972
976
|
? prefixRange : spec
|
|
973
|
-
const pname = child.
|
|
977
|
+
const pname = child.packageName
|
|
974
978
|
const alias = name !== pname
|
|
975
|
-
|
|
979
|
+
newSpec = alias ? `npm:${pname}@${range}` : range
|
|
976
980
|
} else if (req.hosted) {
|
|
977
981
|
// save the git+https url if it has auth, otherwise shortcut
|
|
978
982
|
const h = req.hosted
|
|
979
983
|
const opt = { noCommittish: false }
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
984
|
+
if (h.https && h.auth)
|
|
985
|
+
newSpec = `git+${h.https(opt)}`
|
|
986
|
+
else
|
|
987
|
+
newSpec = h.shortcut(opt)
|
|
983
988
|
} else if (req.type === 'directory' || req.type === 'file') {
|
|
984
989
|
// save the relative path in package.json
|
|
985
990
|
// Normally saveSpec is updated with the proper relative
|
|
@@ -988,9 +993,37 @@ module.exports = cls => class Reifier extends cls {
|
|
|
988
993
|
// thing, so just get the ultimate fetchSpec and relativize it.
|
|
989
994
|
const p = req.fetchSpec.replace(/^file:/, '')
|
|
990
995
|
const rel = relpath(root.realpath, p)
|
|
991
|
-
|
|
996
|
+
newSpec = `file:${rel}`
|
|
992
997
|
} else
|
|
993
|
-
|
|
998
|
+
newSpec = req.saveSpec
|
|
999
|
+
|
|
1000
|
+
if (options.saveType) {
|
|
1001
|
+
const depType = saveTypeMap.get(options.saveType)
|
|
1002
|
+
pkg[depType][name] = newSpec
|
|
1003
|
+
// rpj will have moved it here if it was in both
|
|
1004
|
+
// if it is empty it will be deleted later
|
|
1005
|
+
if (options.saveType === 'prod' && pkg.optionalDependencies)
|
|
1006
|
+
delete pkg.optionalDependencies[name]
|
|
1007
|
+
} else {
|
|
1008
|
+
if (hasSubKey(pkg, 'dependencies', name))
|
|
1009
|
+
pkg.dependencies[name] = newSpec
|
|
1010
|
+
|
|
1011
|
+
if (hasSubKey(pkg, 'devDependencies', name)) {
|
|
1012
|
+
pkg.devDependencies[name] = newSpec
|
|
1013
|
+
// don't update peer or optional if we don't have to
|
|
1014
|
+
if (hasSubKey(pkg, 'peerDependencies', name) && !intersects(newSpec, pkg.peerDependencies[name]))
|
|
1015
|
+
pkg.peerDependencies[name] = newSpec
|
|
1016
|
+
|
|
1017
|
+
if (hasSubKey(pkg, 'optionalDependencies', name) && !intersects(newSpec, pkg.optionalDependencies[name]))
|
|
1018
|
+
pkg.optionalDependencies[name] = newSpec
|
|
1019
|
+
} else {
|
|
1020
|
+
if (hasSubKey(pkg, 'peerDependencies', name))
|
|
1021
|
+
pkg.peerDependencies[name] = newSpec
|
|
1022
|
+
|
|
1023
|
+
if (hasSubKey(pkg, 'optionalDependencies', name))
|
|
1024
|
+
pkg.optionalDependencies[name] = newSpec
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
994
1027
|
}
|
|
995
1028
|
|
|
996
1029
|
// refresh the edges so they have the correct specs
|
package/lib/audit-report.js
CHANGED
|
@@ -101,13 +101,14 @@ class AuditReport extends Map {
|
|
|
101
101
|
|
|
102
102
|
async run () {
|
|
103
103
|
this.report = await this[_getReport]()
|
|
104
|
+
this.log.silly('audit report', this.report)
|
|
104
105
|
if (this.report)
|
|
105
106
|
await this[_init]()
|
|
106
107
|
return this
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
isVulnerable (node) {
|
|
110
|
-
const vuln = this.get(node.
|
|
111
|
+
const vuln = this.get(node.packageName)
|
|
111
112
|
return !!(vuln && vuln.isVulnerable(node))
|
|
112
113
|
}
|
|
113
114
|
|
|
@@ -144,7 +145,7 @@ class AuditReport extends Map {
|
|
|
144
145
|
super.set(name, vuln)
|
|
145
146
|
|
|
146
147
|
const p = []
|
|
147
|
-
for (const node of this.tree.inventory.query('
|
|
148
|
+
for (const node of this.tree.inventory.query('packageName', name)) {
|
|
148
149
|
if (shouldOmit(node, this[_omit]))
|
|
149
150
|
continue
|
|
150
151
|
|
|
@@ -167,7 +168,7 @@ class AuditReport extends Map {
|
|
|
167
168
|
this[_checkTopNode](dep, vuln, spec)
|
|
168
169
|
else {
|
|
169
170
|
// calculate a metavuln, if necessary
|
|
170
|
-
p.push(this.calculator.calculate(dep.
|
|
171
|
+
p.push(this.calculator.calculate(dep.packageName, advisory).then(meta => {
|
|
171
172
|
if (meta.testVersion(dep.version, spec))
|
|
172
173
|
advisories.add(meta)
|
|
173
174
|
}))
|
|
@@ -228,6 +229,9 @@ class AuditReport extends Map {
|
|
|
228
229
|
if (!specObj.registry)
|
|
229
230
|
return false
|
|
230
231
|
|
|
232
|
+
if (specObj.subSpec)
|
|
233
|
+
spec = specObj.subSpec.rawSpec
|
|
234
|
+
|
|
231
235
|
// We don't provide fixes for top nodes other than root, but we
|
|
232
236
|
// still check to see if the node is fixable with a different version,
|
|
233
237
|
// and if that is a semver major bump.
|
|
@@ -289,6 +293,7 @@ class AuditReport extends Map {
|
|
|
289
293
|
try {
|
|
290
294
|
// first try the super fast bulk advisory listing
|
|
291
295
|
const body = prepareBulkData(this.tree, this[_omit])
|
|
296
|
+
this.log.silly('audit', 'bulk request', body)
|
|
292
297
|
|
|
293
298
|
// no sense asking if we don't have anything to audit,
|
|
294
299
|
// we know it'll be empty
|
|
@@ -304,7 +309,8 @@ class AuditReport extends Map {
|
|
|
304
309
|
})
|
|
305
310
|
|
|
306
311
|
return await res.json()
|
|
307
|
-
} catch (
|
|
312
|
+
} catch (er) {
|
|
313
|
+
this.log.silly('audit', 'bulk request failed', String(er.body))
|
|
308
314
|
// that failed, try the quick audit endpoint
|
|
309
315
|
const body = prepareData(this.tree, this.options)
|
|
310
316
|
const res = await fetch('/-/npm/v1/security/audits/quick', {
|
|
@@ -330,6 +336,7 @@ class AuditReport extends Map {
|
|
|
330
336
|
// return true if we should ignore this one
|
|
331
337
|
const shouldOmit = (node, omit) =>
|
|
332
338
|
!node.version ? true
|
|
339
|
+
: node.isRoot ? true
|
|
333
340
|
: omit.size === 0 ? false
|
|
334
341
|
: node.dev && omit.has('dev') ||
|
|
335
342
|
node.optional && omit.has('optional') ||
|
|
@@ -338,9 +345,9 @@ const shouldOmit = (node, omit) =>
|
|
|
338
345
|
|
|
339
346
|
const prepareBulkData = (tree, omit) => {
|
|
340
347
|
const payload = {}
|
|
341
|
-
for (const name of tree.inventory.query('
|
|
348
|
+
for (const name of tree.inventory.query('packageName')) {
|
|
342
349
|
const set = new Set()
|
|
343
|
-
for (const node of tree.inventory.query('
|
|
350
|
+
for (const node of tree.inventory.query('packageName', name)) {
|
|
344
351
|
if (shouldOmit(node, omit))
|
|
345
352
|
continue
|
|
346
353
|
|
package/lib/inventory.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// keys is the set of fields to be able to query.
|
|
5
5
|
const _primaryKey = Symbol('_primaryKey')
|
|
6
6
|
const _index = Symbol('_index')
|
|
7
|
-
const defaultKeys = ['name', 'license', 'funding', 'realpath']
|
|
7
|
+
const defaultKeys = ['name', 'license', 'funding', 'realpath', 'packageName']
|
|
8
8
|
const { hasOwnProperty } = Object.prototype
|
|
9
9
|
const debug = require('./debug.js')
|
|
10
10
|
class Inventory extends Map {
|
package/lib/node.js
CHANGED
|
@@ -291,6 +291,10 @@ class Node {
|
|
|
291
291
|
return this[_package].version || ''
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
get packageName () {
|
|
295
|
+
return this[_package].name || null
|
|
296
|
+
}
|
|
297
|
+
|
|
294
298
|
get pkgid () {
|
|
295
299
|
const { name = '', version = '' } = this.package
|
|
296
300
|
// root package will prefer package name over folder name,
|
|
@@ -350,10 +354,10 @@ class Node {
|
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
const why = {
|
|
353
|
-
name: this.isProjectRoot ? this.
|
|
357
|
+
name: this.isProjectRoot ? this.packageName : this.name,
|
|
354
358
|
version: this.package.version,
|
|
355
359
|
}
|
|
356
|
-
if (this.errors.length || !this.
|
|
360
|
+
if (this.errors.length || !this.packageName || !this.package.version) {
|
|
357
361
|
why.errors = this.errors.length ? this.errors : [
|
|
358
362
|
new Error('invalid package: lacks name and/or version'),
|
|
359
363
|
]
|
|
@@ -460,7 +464,7 @@ class Node {
|
|
|
460
464
|
if (this.isProjectRoot)
|
|
461
465
|
return false
|
|
462
466
|
const { root } = this
|
|
463
|
-
const { type, to } = root.edgesOut.get(this.
|
|
467
|
+
const { type, to } = root.edgesOut.get(this.packageName) || {}
|
|
464
468
|
return type === 'workspace' && to && (to.target === this || to === this)
|
|
465
469
|
}
|
|
466
470
|
|
|
@@ -730,20 +734,14 @@ class Node {
|
|
|
730
734
|
|
|
731
735
|
[_loadDeps] () {
|
|
732
736
|
// Caveat! Order is relevant!
|
|
733
|
-
//
|
|
734
|
-
//
|
|
737
|
+
// Packages in optionalDependencies are optional.
|
|
738
|
+
// Packages in both deps and devDeps are required.
|
|
735
739
|
// Note the subtle breaking change from v6: it is no longer possible
|
|
736
740
|
// to have a different spec for a devDep than production dep.
|
|
737
|
-
this[_loadDepType](this.package.optionalDependencies, 'optional')
|
|
738
741
|
|
|
739
742
|
// Linked targets that are disconnected from the tree are tops,
|
|
740
743
|
// but don't have a 'path' field, only a 'realpath', because we
|
|
741
744
|
// don't know their canonical location. We don't need their devDeps.
|
|
742
|
-
const { isTop, path, sourceReference } = this
|
|
743
|
-
const { isTop: srcTop, path: srcPath } = sourceReference || {}
|
|
744
|
-
if (isTop && path && (!sourceReference || srcTop && srcPath))
|
|
745
|
-
this[_loadDepType](this.package.devDependencies, 'dev')
|
|
746
|
-
|
|
747
745
|
const pd = this.package.peerDependencies
|
|
748
746
|
if (pd && typeof pd === 'object' && !this.legacyPeerDeps) {
|
|
749
747
|
const pm = this.package.peerDependenciesMeta || {}
|
|
@@ -760,19 +758,22 @@ class Node {
|
|
|
760
758
|
}
|
|
761
759
|
|
|
762
760
|
this[_loadDepType](this.package.dependencies, 'prod')
|
|
761
|
+
this[_loadDepType](this.package.optionalDependencies, 'optional')
|
|
762
|
+
|
|
763
|
+
const { isTop, path, sourceReference } = this
|
|
764
|
+
const { isTop: srcTop, path: srcPath } = sourceReference || {}
|
|
765
|
+
if (isTop && path && (!sourceReference || srcTop && srcPath))
|
|
766
|
+
this[_loadDepType](this.package.devDependencies, 'dev')
|
|
763
767
|
}
|
|
764
768
|
|
|
765
|
-
[_loadDepType] (
|
|
766
|
-
const from = this
|
|
769
|
+
[_loadDepType] (deps, type) {
|
|
767
770
|
const ad = this.package.acceptDependencies || {}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
// Prod deps should not be marked as dev, however.
|
|
772
|
-
// NB: the Edge ctor adds itself to from.edgesOut
|
|
771
|
+
// Because of the order in which _loadDeps runs, we always want to
|
|
772
|
+
// prioritize a new edge over an existing one
|
|
773
|
+
for (const [name, spec] of Object.entries(deps || {})) {
|
|
773
774
|
const current = this.edgesOut.get(name)
|
|
774
|
-
if (!current || current.
|
|
775
|
-
new Edge({ from, name, spec, accept, type })
|
|
775
|
+
if (!current || current.type !== 'workspace')
|
|
776
|
+
new Edge({ from: this, name, spec, accept: ad[name], type })
|
|
776
777
|
}
|
|
777
778
|
}
|
|
778
779
|
|
|
@@ -965,8 +966,8 @@ class Node {
|
|
|
965
966
|
|
|
966
967
|
// if no resolved, check both package name and version
|
|
967
968
|
// otherwise, conclude that they are different things
|
|
968
|
-
return this.
|
|
969
|
-
this.
|
|
969
|
+
return this.packageName && node.packageName &&
|
|
970
|
+
this.packageName === node.packageName &&
|
|
970
971
|
this.version && node.version &&
|
|
971
972
|
this.version === node.version
|
|
972
973
|
}
|
package/lib/printable.js
CHANGED
|
@@ -7,8 +7,8 @@ const relpath = require('./relpath.js')
|
|
|
7
7
|
class ArboristNode {
|
|
8
8
|
constructor (tree, path) {
|
|
9
9
|
this.name = tree.name
|
|
10
|
-
if (tree.
|
|
11
|
-
this.packageName = tree.
|
|
10
|
+
if (tree.packageName && tree.packageName !== this.name)
|
|
11
|
+
this.packageName = tree.packageName
|
|
12
12
|
if (tree.version)
|
|
13
13
|
this.version = tree.version
|
|
14
14
|
this.location = tree.location
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -254,7 +254,7 @@ class Shrinkwrap {
|
|
|
254
254
|
meta[key.replace(/^_/, '')] = val
|
|
255
255
|
})
|
|
256
256
|
// we only include name if different from the node path name
|
|
257
|
-
const pname = node.
|
|
257
|
+
const pname = node.packageName
|
|
258
258
|
if (pname && pname !== node.name)
|
|
259
259
|
meta.name = pname
|
|
260
260
|
|
|
@@ -825,7 +825,7 @@ class Shrinkwrap {
|
|
|
825
825
|
[_buildLegacyLockfile] (node, lock, path = []) {
|
|
826
826
|
if (node === this.tree) {
|
|
827
827
|
// the root node
|
|
828
|
-
lock.name = node.
|
|
828
|
+
lock.name = node.packageName || node.name
|
|
829
829
|
if (node.version)
|
|
830
830
|
lock.version = node.version
|
|
831
831
|
}
|
|
@@ -870,9 +870,9 @@ class Shrinkwrap {
|
|
|
870
870
|
lock.from = spec.raw
|
|
871
871
|
} else if (!node.isRoot &&
|
|
872
872
|
node.package &&
|
|
873
|
-
node.
|
|
874
|
-
node.
|
|
875
|
-
lock.version = `npm:${node.
|
|
873
|
+
node.packageName &&
|
|
874
|
+
node.packageName !== node.name)
|
|
875
|
+
lock.version = `npm:${node.packageName}@${node.version}`
|
|
876
876
|
else if (node.package && node.version)
|
|
877
877
|
lock.version = node.version
|
|
878
878
|
|
|
@@ -6,8 +6,6 @@ const {resolve} = require('path')
|
|
|
6
6
|
|
|
7
7
|
const parseJSON = require('json-parse-even-better-errors')
|
|
8
8
|
|
|
9
|
-
const { orderDeps } = require('./dep-spec.js')
|
|
10
|
-
|
|
11
9
|
const depTypes = new Set([
|
|
12
10
|
'dependencies',
|
|
13
11
|
'optionalDependencies',
|
|
@@ -15,6 +13,20 @@ const depTypes = new Set([
|
|
|
15
13
|
'peerDependencies',
|
|
16
14
|
])
|
|
17
15
|
|
|
16
|
+
// sort alphabetically all types of deps for a given package
|
|
17
|
+
const orderDeps = (pkg) => {
|
|
18
|
+
for (const type of depTypes) {
|
|
19
|
+
if (pkg && pkg[type]) {
|
|
20
|
+
pkg[type] = Object.keys(pkg[type])
|
|
21
|
+
.sort((a, b) => a.localeCompare(b))
|
|
22
|
+
.reduce((res, key) => {
|
|
23
|
+
res[key] = pkg[type][key]
|
|
24
|
+
return res
|
|
25
|
+
}, {})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return pkg
|
|
29
|
+
}
|
|
18
30
|
const parseJsonSafe = json => {
|
|
19
31
|
try {
|
|
20
32
|
return parseJSON(json)
|
package/lib/vuln.js
CHANGED
package/package.json
CHANGED
package/lib/dep-spec.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const types = [
|
|
2
|
-
'peerDependencies',
|
|
3
|
-
'devDependencies',
|
|
4
|
-
'optionalDependencies',
|
|
5
|
-
'dependencies',
|
|
6
|
-
]
|
|
7
|
-
|
|
8
|
-
const findType = (pkg, name) => {
|
|
9
|
-
for (const t of types) {
|
|
10
|
-
if (pkg[t] && typeof pkg[t] === 'object' && pkg[t][name] !== undefined)
|
|
11
|
-
return t
|
|
12
|
-
}
|
|
13
|
-
return 'dependencies'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// given a dep name and spec, update it wherever it exists in
|
|
17
|
-
// the manifest, or add the spec to 'dependencies' if not found.
|
|
18
|
-
const updateDepSpec = (pkg, name, newSpec) => {
|
|
19
|
-
const type = findType(pkg, name)
|
|
20
|
-
pkg[type] = pkg[type] || {}
|
|
21
|
-
pkg[type][name] = newSpec
|
|
22
|
-
return pkg
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// sort alphabetically all types of deps for a given package
|
|
26
|
-
const orderDeps = (pkg) => {
|
|
27
|
-
for (const type of types) {
|
|
28
|
-
if (pkg && pkg[type]) {
|
|
29
|
-
pkg[type] = Object.keys(pkg[type])
|
|
30
|
-
.sort((a, b) => a.localeCompare(b))
|
|
31
|
-
.reduce((res, key) => {
|
|
32
|
-
res[key] = pkg[type][key]
|
|
33
|
-
return res
|
|
34
|
-
}, {})
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return pkg
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = {
|
|
41
|
-
orderDeps,
|
|
42
|
-
updateDepSpec,
|
|
43
|
-
}
|