@npmcli/arborist 2.1.0 → 2.2.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/bin/actual.js +21 -0
- package/bin/audit.js +48 -0
- package/bin/funding.js +32 -0
- package/bin/ideal.js +20 -0
- package/bin/index.js +77 -0
- package/bin/lib/logging.js +33 -0
- package/bin/lib/options.js +49 -0
- package/bin/lib/print-tree.js +5 -0
- package/bin/lib/timers.js +22 -0
- package/bin/license.js +34 -0
- package/bin/reify.js +46 -0
- package/bin/shrinkwrap.js +12 -0
- package/bin/virtual.js +15 -0
- package/lib/arborist/build-ideal-tree.js +54 -17
- package/lib/arborist/load-virtual.js +31 -11
- package/lib/arborist/reify.js +13 -7
- package/lib/calc-dep-flags.js +1 -1
- package/lib/node.js +15 -10
- package/lib/update-root-package-json.js +11 -1
- package/package.json +12 -8
package/bin/actual.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
const print = require('./lib/print-tree.js')
|
|
3
|
+
const options = require('./lib/options.js')
|
|
4
|
+
require('./lib/logging.js')
|
|
5
|
+
require('./lib/timers.js')
|
|
6
|
+
|
|
7
|
+
const start = process.hrtime()
|
|
8
|
+
new Arborist(options).loadActual(options).then(tree => {
|
|
9
|
+
const end = process.hrtime(start)
|
|
10
|
+
if (!process.argv.includes('--quiet'))
|
|
11
|
+
print(tree)
|
|
12
|
+
|
|
13
|
+
console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
|
|
14
|
+
if (options.save)
|
|
15
|
+
tree.meta.save()
|
|
16
|
+
if (options.saveHidden) {
|
|
17
|
+
tree.meta.hiddenLockfile = true
|
|
18
|
+
tree.meta.filename = options.path + '/node_modules/.package-lock.json'
|
|
19
|
+
tree.meta.save()
|
|
20
|
+
}
|
|
21
|
+
}).catch(er => console.error(er))
|
package/bin/audit.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
|
|
3
|
+
const print = require('./lib/print-tree.js')
|
|
4
|
+
const options = require('./lib/options.js')
|
|
5
|
+
require('./lib/timers.js')
|
|
6
|
+
require('./lib/logging.js')
|
|
7
|
+
|
|
8
|
+
const Vuln = require('../lib/vuln.js')
|
|
9
|
+
const printReport = report => {
|
|
10
|
+
for (const vuln of report.values())
|
|
11
|
+
console.log(printVuln(vuln))
|
|
12
|
+
if (report.topVulns.size) {
|
|
13
|
+
console.log('\n# top-level vulnerabilities')
|
|
14
|
+
for (const vuln of report.topVulns.values())
|
|
15
|
+
console.log(printVuln(vuln))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const printVuln = vuln => {
|
|
20
|
+
return {
|
|
21
|
+
__proto__: { constructor: Vuln },
|
|
22
|
+
name: vuln.name,
|
|
23
|
+
issues: [...vuln.advisories].map(a => printAdvisory(a)),
|
|
24
|
+
range: vuln.simpleRange,
|
|
25
|
+
nodes: [...vuln.nodes].map(node => `${node.name} ${node.location || '#ROOT'}`),
|
|
26
|
+
...(vuln.topNodes.size === 0 ? {} : {
|
|
27
|
+
topNodes: [...vuln.topNodes].map(node => `${node.location || '#ROOT'}`),
|
|
28
|
+
}),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const printAdvisory = a => `${a.title}${a.url ? ' ' + a.url : ''}`
|
|
33
|
+
|
|
34
|
+
const start = process.hrtime()
|
|
35
|
+
process.emit('time', 'audit script')
|
|
36
|
+
const arb = new Arborist(options)
|
|
37
|
+
arb.audit(options).then(tree => {
|
|
38
|
+
process.emit('timeEnd', 'audit script')
|
|
39
|
+
const end = process.hrtime(start)
|
|
40
|
+
if (options.fix)
|
|
41
|
+
print(tree)
|
|
42
|
+
if (!options.quiet)
|
|
43
|
+
printReport(arb.auditReport)
|
|
44
|
+
if (options.fix)
|
|
45
|
+
console.error(`resolved ${tree.inventory.size} deps in ${end[0] + end[1] / 1e9}s`)
|
|
46
|
+
if (tree.meta && options.save)
|
|
47
|
+
tree.meta.save()
|
|
48
|
+
}).catch(er => console.error(er))
|
package/bin/funding.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const options = require('./lib/options.js')
|
|
2
|
+
require('./lib/logging.js')
|
|
3
|
+
require('./lib/timers.js')
|
|
4
|
+
|
|
5
|
+
const Arborist = require('../')
|
|
6
|
+
const a = new Arborist(options)
|
|
7
|
+
const query = options._.shift()
|
|
8
|
+
const start = process.hrtime()
|
|
9
|
+
a.loadVirtual().then(tree => {
|
|
10
|
+
// only load the actual tree if the virtual one doesn't have modern metadata
|
|
11
|
+
if (!tree.meta || !(tree.meta.originalLockfileVersion >= 2)) {
|
|
12
|
+
console.error('old metadata, load actual')
|
|
13
|
+
throw 'load actual'
|
|
14
|
+
} else {
|
|
15
|
+
console.error('meta ok, return virtual tree')
|
|
16
|
+
return tree
|
|
17
|
+
}
|
|
18
|
+
}).catch(() => a.loadActual()).then(tree => {
|
|
19
|
+
const end = process.hrtime(start)
|
|
20
|
+
if (!query) {
|
|
21
|
+
for (const node of tree.inventory.values()) {
|
|
22
|
+
if (node.package.funding)
|
|
23
|
+
console.log(node.name, node.location, node.package.funding)
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
for (const node of tree.inventory.query('name', query)) {
|
|
27
|
+
if (node.package.funding)
|
|
28
|
+
console.log(node.name, node.location, node.package.funding)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
|
|
32
|
+
})
|
package/bin/ideal.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
|
|
3
|
+
const { inspect } = require('util')
|
|
4
|
+
const options = require('./lib/options.js')
|
|
5
|
+
const print = require('./lib/print-tree.js')
|
|
6
|
+
require('./lib/logging.js')
|
|
7
|
+
require('./lib/timers.js')
|
|
8
|
+
|
|
9
|
+
const start = process.hrtime()
|
|
10
|
+
new Arborist(options).buildIdealTree(options).then(tree => {
|
|
11
|
+
const end = process.hrtime(start)
|
|
12
|
+
print(tree)
|
|
13
|
+
console.error(`resolved ${tree.inventory.size} deps in ${end[0] + end[1] / 10e9}s`)
|
|
14
|
+
if (tree.meta && options.save)
|
|
15
|
+
tree.meta.save()
|
|
16
|
+
}).catch(er => {
|
|
17
|
+
const opt = { depth: Infinity, color: true }
|
|
18
|
+
console.error(er.code === 'ERESOLVE' ? inspect(er, opt) : er)
|
|
19
|
+
process.exitCode = 1
|
|
20
|
+
})
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const [cmd] = process.argv.splice(2, 1)
|
|
3
|
+
|
|
4
|
+
const usage = () => `Arborist - the npm tree doctor
|
|
5
|
+
|
|
6
|
+
Version: ${require('../package.json').version}
|
|
7
|
+
|
|
8
|
+
# USAGE
|
|
9
|
+
arborist <cmd> [path] [options...]
|
|
10
|
+
|
|
11
|
+
# COMMANDS
|
|
12
|
+
|
|
13
|
+
* reify: reify ideal tree to node_modules (install, update, rm, ...)
|
|
14
|
+
* ideal: generate and print the ideal tree
|
|
15
|
+
* actual: read and print the actual tree in node_modules
|
|
16
|
+
* virtual: read and print the virtual tree in the local shrinkwrap file
|
|
17
|
+
* shrinkwrap: load a local shrinkwrap and print its data
|
|
18
|
+
* audit: perform a security audit on project dependencies
|
|
19
|
+
* funding: query funding information in the local package tree. A second
|
|
20
|
+
positional argument after the path name can limit to a package name.
|
|
21
|
+
* license: query license information in the local package tree. A second
|
|
22
|
+
positional argument after the path name can limit to a license type.
|
|
23
|
+
* help: print this text
|
|
24
|
+
|
|
25
|
+
# OPTIONS
|
|
26
|
+
|
|
27
|
+
Most npm options are supported, but in camelCase rather than css-case. For
|
|
28
|
+
example, instead of '--dry-run', use '--dryRun'.
|
|
29
|
+
|
|
30
|
+
Additionally:
|
|
31
|
+
|
|
32
|
+
* --quiet will supppress the printing of package trees
|
|
33
|
+
* Instead of 'npm install <pkg>', use 'arborist reify --add=<pkg>'.
|
|
34
|
+
The '--add=<pkg>' option can be specified multiple times.
|
|
35
|
+
* Instead of 'npm rm <pkg>', use 'arborist reify --rm=<pkg>'.
|
|
36
|
+
The '--rm=<pkg>' option can be specified multiple times.
|
|
37
|
+
* Instead of 'npm update', use 'arborist reify --update-all'.
|
|
38
|
+
* 'npm audit fix' is 'arborist audit --fix'
|
|
39
|
+
`
|
|
40
|
+
|
|
41
|
+
const help = () => console.log(usage())
|
|
42
|
+
|
|
43
|
+
switch (cmd) {
|
|
44
|
+
case 'actual':
|
|
45
|
+
require('./actual.js')
|
|
46
|
+
break
|
|
47
|
+
case 'virtual':
|
|
48
|
+
require('./virtual.js')
|
|
49
|
+
break
|
|
50
|
+
case 'ideal':
|
|
51
|
+
require('./ideal.js')
|
|
52
|
+
break
|
|
53
|
+
case 'reify':
|
|
54
|
+
require('./reify.js')
|
|
55
|
+
break
|
|
56
|
+
case 'audit':
|
|
57
|
+
require('./audit.js')
|
|
58
|
+
break
|
|
59
|
+
case 'funding':
|
|
60
|
+
require('./funding.js')
|
|
61
|
+
break
|
|
62
|
+
case 'license':
|
|
63
|
+
require('./license.js')
|
|
64
|
+
break
|
|
65
|
+
case 'shrinkwrap':
|
|
66
|
+
require('./shrinkwrap.js')
|
|
67
|
+
break
|
|
68
|
+
case 'help':
|
|
69
|
+
case '-h':
|
|
70
|
+
case '--help':
|
|
71
|
+
help()
|
|
72
|
+
break
|
|
73
|
+
default:
|
|
74
|
+
process.exitCode = 1
|
|
75
|
+
console.error(usage())
|
|
76
|
+
break
|
|
77
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const options = require('./options.js')
|
|
2
|
+
const { quiet = false } = options
|
|
3
|
+
const { loglevel = quiet ? 'warn' : 'silly' } = options
|
|
4
|
+
|
|
5
|
+
const levels = [
|
|
6
|
+
'silly',
|
|
7
|
+
'verbose',
|
|
8
|
+
'info',
|
|
9
|
+
'timing',
|
|
10
|
+
'http',
|
|
11
|
+
'notice',
|
|
12
|
+
'warn',
|
|
13
|
+
'error',
|
|
14
|
+
'silent',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const levelMap = new Map(levels.reduce((set, level, index) => {
|
|
18
|
+
set.push([level, index], [index, level])
|
|
19
|
+
return set
|
|
20
|
+
}, []))
|
|
21
|
+
|
|
22
|
+
const { inspect, format } = require('util')
|
|
23
|
+
if (loglevel !== 'silent') {
|
|
24
|
+
process.on('log', (level, ...args) => {
|
|
25
|
+
if (levelMap.get(level) < levelMap.get(loglevel))
|
|
26
|
+
return
|
|
27
|
+
const pref = `${process.pid} ${level} `
|
|
28
|
+
if (level === 'warn' && args[0] === 'ERESOLVE')
|
|
29
|
+
args[2] = inspect(args[2], { depth: 10 })
|
|
30
|
+
const msg = pref + format(...args).trim().split('\n').join(`\n${pref}`)
|
|
31
|
+
console.error(msg)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const options = module.exports = {
|
|
2
|
+
path: undefined,
|
|
3
|
+
cache: `${process.env.HOME}/.npm/_cacache`,
|
|
4
|
+
_: [],
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
for (const arg of process.argv.slice(2)) {
|
|
8
|
+
if (/^--add=/.test(arg)) {
|
|
9
|
+
options.add = options.add || []
|
|
10
|
+
options.add.push(arg.substr('--add='.length))
|
|
11
|
+
} else if (/^--rm=/.test(arg)) {
|
|
12
|
+
options.rm = options.rm || []
|
|
13
|
+
options.rm.push(arg.substr('--rm='.length))
|
|
14
|
+
} else if (arg === '--global')
|
|
15
|
+
options.global = true
|
|
16
|
+
else if (arg === '--global-style')
|
|
17
|
+
options.globalStyle = true
|
|
18
|
+
else if (arg === '--prefer-dedupe')
|
|
19
|
+
options.preferDedupe = true
|
|
20
|
+
else if (arg === '--legacy-peer-deps')
|
|
21
|
+
options.legacyPeerDeps = true
|
|
22
|
+
else if (arg === '--force')
|
|
23
|
+
options.force = true
|
|
24
|
+
else if (arg === '--update-all') {
|
|
25
|
+
options.update = options.update || {}
|
|
26
|
+
options.update.all = true
|
|
27
|
+
} else if (/^--update=/.test(arg)) {
|
|
28
|
+
options.update = options.update || {}
|
|
29
|
+
options.update.names = options.update.names || []
|
|
30
|
+
options.update.names.push(arg.substr('--update='.length))
|
|
31
|
+
} else if (/^--omit=/.test(arg)) {
|
|
32
|
+
options.omit = options.omit || []
|
|
33
|
+
options.omit.push(arg.substr('--omit='.length))
|
|
34
|
+
} else if (/^--[^=]+=/.test(arg)) {
|
|
35
|
+
const [key, ...v] = arg.replace(/^--/, '').split('=')
|
|
36
|
+
const val = v.join('=')
|
|
37
|
+
options[key] = val === 'false' ? false : val === 'true' ? true : val
|
|
38
|
+
} else if (/^--.+/.test(arg))
|
|
39
|
+
options[arg.replace(/^--/, '')] = true
|
|
40
|
+
else if (options.path === undefined)
|
|
41
|
+
options.path = arg
|
|
42
|
+
else
|
|
43
|
+
options._.push(arg)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (options.path === undefined)
|
|
47
|
+
options.path = '.'
|
|
48
|
+
|
|
49
|
+
console.error(options)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const timers = Object.create(null)
|
|
2
|
+
|
|
3
|
+
process.on('time', name => {
|
|
4
|
+
if (timers[name])
|
|
5
|
+
throw new Error('conflicting timer! ' + name)
|
|
6
|
+
timers[name] = process.hrtime()
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
process.on('timeEnd', name => {
|
|
10
|
+
if (!timers[name])
|
|
11
|
+
throw new Error('timer not started! ' + name)
|
|
12
|
+
const res = process.hrtime(timers[name])
|
|
13
|
+
delete timers[name]
|
|
14
|
+
console.error(`${process.pid} ${name}`, res[0] * 1e3 + res[1] / 1e6)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
process.on('exit', () => {
|
|
18
|
+
for (const name of Object.keys(timers)) {
|
|
19
|
+
console.error('Dangling timer: ', name)
|
|
20
|
+
process.exitCode = 1
|
|
21
|
+
}
|
|
22
|
+
})
|
package/bin/license.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
const options = require('./lib/options.js')
|
|
3
|
+
require('./lib/logging.js')
|
|
4
|
+
require('./lib/timers.js')
|
|
5
|
+
|
|
6
|
+
const a = new Arborist(options)
|
|
7
|
+
const query = options._.shift()
|
|
8
|
+
|
|
9
|
+
a.loadVirtual().then(tree => {
|
|
10
|
+
// only load the actual tree if the virtual one doesn't have modern metadata
|
|
11
|
+
if (!tree.meta || !(tree.meta.originalLockfileVersion >= 2))
|
|
12
|
+
throw 'load actual'
|
|
13
|
+
else
|
|
14
|
+
return tree
|
|
15
|
+
}).catch((er) => {
|
|
16
|
+
console.error('loading actual tree', er)
|
|
17
|
+
return a.loadActual()
|
|
18
|
+
}).then(tree => {
|
|
19
|
+
if (!query) {
|
|
20
|
+
const set = []
|
|
21
|
+
for (const license of tree.inventory.query('license'))
|
|
22
|
+
set.push([tree.inventory.query('license', license).size, license])
|
|
23
|
+
|
|
24
|
+
for (const [count, license] of set.sort((a, b) =>
|
|
25
|
+
a[1] && b[1] ? b[0] - a[0] || a[1].localeCompare(b[1])
|
|
26
|
+
: a[1] ? -1
|
|
27
|
+
: b[1] ? 1
|
|
28
|
+
: 0))
|
|
29
|
+
console.log(count, license)
|
|
30
|
+
} else {
|
|
31
|
+
for (const node of tree.inventory.query('license', query === 'undefined' ? undefined : query))
|
|
32
|
+
console.log(`${node.name} ${node.location} ${node.package.description || ''}`)
|
|
33
|
+
}
|
|
34
|
+
})
|
package/bin/reify.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
|
|
3
|
+
const options = require('./lib/options.js')
|
|
4
|
+
const print = require('./lib/print-tree.js')
|
|
5
|
+
require('./lib/logging.js')
|
|
6
|
+
require('./lib/timers.js')
|
|
7
|
+
|
|
8
|
+
const printDiff = diff => {
|
|
9
|
+
const {depth} = require('treeverse')
|
|
10
|
+
depth({
|
|
11
|
+
tree: diff,
|
|
12
|
+
visit: d => {
|
|
13
|
+
if (d.location === '')
|
|
14
|
+
return
|
|
15
|
+
switch (d.action) {
|
|
16
|
+
case 'REMOVE':
|
|
17
|
+
console.error('REMOVE', d.actual.location)
|
|
18
|
+
break
|
|
19
|
+
case 'ADD':
|
|
20
|
+
console.error('ADD', d.ideal.location, d.ideal.resolved)
|
|
21
|
+
break
|
|
22
|
+
case 'CHANGE':
|
|
23
|
+
console.error('CHANGE', d.actual.location, {
|
|
24
|
+
from: d.actual.resolved,
|
|
25
|
+
to: d.ideal.resolved,
|
|
26
|
+
})
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
getChildren: d => d.children,
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const start = process.hrtime()
|
|
35
|
+
process.emit('time', 'install')
|
|
36
|
+
const arb = new Arborist(options)
|
|
37
|
+
arb.reify(options).then(tree => {
|
|
38
|
+
process.emit('timeEnd', 'install')
|
|
39
|
+
const end = process.hrtime(start)
|
|
40
|
+
print(tree)
|
|
41
|
+
if (options.dryRun)
|
|
42
|
+
printDiff(arb.diff)
|
|
43
|
+
console.error(`resolved ${tree.inventory.size} deps in ${end[0] + end[1] / 1e9}s`)
|
|
44
|
+
if (tree.meta && options.save)
|
|
45
|
+
tree.meta.save()
|
|
46
|
+
}).catch(er => console.error(require('util').inspect(er, { depth: Infinity })))
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const Shrinkwrap = require('../lib/shrinkwrap.js')
|
|
2
|
+
const options = require('./lib/options.js')
|
|
3
|
+
require('./lib/logging.js')
|
|
4
|
+
require('./lib/timers.js')
|
|
5
|
+
|
|
6
|
+
const { quiet } = options
|
|
7
|
+
Shrinkwrap.load(options)
|
|
8
|
+
.then(s => quiet || console.log(JSON.stringify(s.data, 0, 2)))
|
|
9
|
+
.catch(er => {
|
|
10
|
+
console.error('shrinkwrap load failure', er)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
})
|
package/bin/virtual.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const Arborist = require('../')
|
|
2
|
+
|
|
3
|
+
const print = require('./lib/print-tree.js')
|
|
4
|
+
const options = require('./lib/options.js')
|
|
5
|
+
require('./lib/logging.js')
|
|
6
|
+
require('./lib/timers.js')
|
|
7
|
+
|
|
8
|
+
const start = process.hrtime()
|
|
9
|
+
new Arborist(options).loadVirtual().then(tree => {
|
|
10
|
+
const end = process.hrtime(start)
|
|
11
|
+
print(tree)
|
|
12
|
+
if (options.save)
|
|
13
|
+
tree.meta.save()
|
|
14
|
+
console.error(`read ${tree.inventory.size} deps in ${end[0] * 1000 + end[1] / 1e6}ms`)
|
|
15
|
+
}).catch(er => console.error(er))
|
|
@@ -397,7 +397,8 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
397
397
|
// that they're there, and not reinstall the world unnecessarily.
|
|
398
398
|
if (this[_global] && (this[_updateAll] || this[_updateNames].length)) {
|
|
399
399
|
const nm = resolve(this.path, 'node_modules')
|
|
400
|
-
for (const name of await readdir(nm)) {
|
|
400
|
+
for (const name of await readdir(nm).catch(() => [])) {
|
|
401
|
+
tree.package.dependencies = tree.package.dependencies || {}
|
|
401
402
|
if (this[_updateAll] || this[_updateNames].includes(name))
|
|
402
403
|
tree.package.dependencies[name] = '*'
|
|
403
404
|
}
|
|
@@ -416,7 +417,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
416
417
|
await this[_add](options)
|
|
417
418
|
|
|
418
419
|
// triggers a refresh of all edgesOut
|
|
419
|
-
if (options.add && options.add.length || options.rm && options.rm.length)
|
|
420
|
+
if (options.add && options.add.length || options.rm && options.rm.length || this[_global])
|
|
420
421
|
tree.package = tree.package
|
|
421
422
|
process.emit('timeEnd', 'idealTree:userRequests')
|
|
422
423
|
}
|
|
@@ -490,7 +491,8 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
490
491
|
/* istanbul ignore else - should also be covered by realpath failure */
|
|
491
492
|
if (filepath) {
|
|
492
493
|
const { name } = spec
|
|
493
|
-
|
|
494
|
+
const tree = this.idealTree.target || this.idealTree
|
|
495
|
+
spec = npa(`file:${relpath(tree.path, filepath)}`, tree.path)
|
|
494
496
|
spec.name = name
|
|
495
497
|
}
|
|
496
498
|
return spec
|
|
@@ -662,6 +664,11 @@ This is a one-time fix-up, please be patient...
|
|
|
662
664
|
})
|
|
663
665
|
}
|
|
664
666
|
await promiseCallLimit(queue)
|
|
667
|
+
|
|
668
|
+
// have to re-calc dep flags, because the nodes don't have edges
|
|
669
|
+
// until their packages get assigned, so everything looks extraneous
|
|
670
|
+
calcDepFlags(this.idealTree)
|
|
671
|
+
|
|
665
672
|
// yes, yes, this isn't the "original" version, but now that it's been
|
|
666
673
|
// upgraded, we need to make sure we don't do the work to upgrade it
|
|
667
674
|
// again, since it's now as new as can be.
|
|
@@ -799,6 +806,7 @@ This is a one-time fix-up, please be patient...
|
|
|
799
806
|
// a virtual root of whatever brought in THIS node.
|
|
800
807
|
// so we VR the node itself if the edge is not a peer
|
|
801
808
|
const source = edge.peer ? peerSource : node
|
|
809
|
+
|
|
802
810
|
const virtualRoot = this[_virtualRoot](source, true)
|
|
803
811
|
// reuse virtual root if we already have one, but don't
|
|
804
812
|
// try to do the override ahead of time, since we MAY be able
|
|
@@ -820,13 +828,17 @@ This is a one-time fix-up, please be patient...
|
|
|
820
828
|
// +-- z@1
|
|
821
829
|
// But if x and y are loaded in the same virtual root, then they will
|
|
822
830
|
// be forced to agree on a version of z.
|
|
831
|
+
const required = new Set([edge.from])
|
|
832
|
+
const parent = edge.peer ? virtualRoot : null
|
|
823
833
|
const dep = vrDep && vrDep.satisfies(edge) ? vrDep
|
|
824
|
-
: await this[_nodeFromEdge](edge,
|
|
834
|
+
: await this[_nodeFromEdge](edge, parent, null, required)
|
|
835
|
+
|
|
825
836
|
/* istanbul ignore next */
|
|
826
837
|
debug(() => {
|
|
827
838
|
if (!dep)
|
|
828
839
|
throw new Error('no dep??')
|
|
829
840
|
})
|
|
841
|
+
|
|
830
842
|
tasks.push({edge, dep})
|
|
831
843
|
}
|
|
832
844
|
|
|
@@ -863,7 +875,7 @@ This is a one-time fix-up, please be patient...
|
|
|
863
875
|
|
|
864
876
|
// loads a node from an edge, and then loads its peer deps (and their
|
|
865
877
|
// peer deps, on down the line) into a virtual root parent.
|
|
866
|
-
async [_nodeFromEdge] (edge, parent_, secondEdge
|
|
878
|
+
async [_nodeFromEdge] (edge, parent_, secondEdge, required) {
|
|
867
879
|
// create a virtual root node with the same deps as the node that
|
|
868
880
|
// is requesting this one, so that we can get all the peer deps in
|
|
869
881
|
// a context where they're likely to be resolvable.
|
|
@@ -894,6 +906,11 @@ This is a one-time fix-up, please be patient...
|
|
|
894
906
|
// ensure the one we want is the one that's placed
|
|
895
907
|
node.parent = parent
|
|
896
908
|
|
|
909
|
+
if (required.has(edge.from) && edge.type !== 'peerOptional' ||
|
|
910
|
+
secondEdge && (
|
|
911
|
+
required.has(secondEdge.from) && secondEdge.type !== 'peerOptional'))
|
|
912
|
+
required.add(node)
|
|
913
|
+
|
|
897
914
|
// handle otherwise unresolvable dependency nesting loops by
|
|
898
915
|
// creating a symbolic link
|
|
899
916
|
// a1 -> b1 -> a2 -> b2 -> a1 -> ...
|
|
@@ -907,7 +924,7 @@ This is a one-time fix-up, please be patient...
|
|
|
907
924
|
// keep track of the thing that caused this node to be included.
|
|
908
925
|
const src = parent.sourceReference
|
|
909
926
|
this[_peerSetSource].set(node, src)
|
|
910
|
-
return this[_loadPeerSet](node)
|
|
927
|
+
return this[_loadPeerSet](node, required)
|
|
911
928
|
}
|
|
912
929
|
|
|
913
930
|
[_virtualRoot] (node, reuse = false) {
|
|
@@ -1052,7 +1069,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1052
1069
|
// gets placed first. In non-strict mode, we behave strictly if the
|
|
1053
1070
|
// virtual root is based on the root project, and allow non-peer parent
|
|
1054
1071
|
// deps to override, but throw if no preference can be determined.
|
|
1055
|
-
async [_loadPeerSet] (node) {
|
|
1072
|
+
async [_loadPeerSet] (node, required) {
|
|
1056
1073
|
const peerEdges = [...node.edgesOut.values()]
|
|
1057
1074
|
// we typically only install non-optional peers, but we have to
|
|
1058
1075
|
// factor them into the peerSet so that we can avoid conflicts
|
|
@@ -1067,10 +1084,12 @@ This is a one-time fix-up, please be patient...
|
|
|
1067
1084
|
const parentEdge = node.parent.edgesOut.get(edge.name)
|
|
1068
1085
|
const {isProjectRoot, isWorkspace} = node.parent.sourceReference
|
|
1069
1086
|
const isMine = isProjectRoot || isWorkspace
|
|
1087
|
+
const conflictOK = this[_force] || !isMine && !this[_strictPeerDeps]
|
|
1088
|
+
|
|
1070
1089
|
if (!edge.to) {
|
|
1071
1090
|
if (!parentEdge) {
|
|
1072
1091
|
// easy, just put the thing there
|
|
1073
|
-
await this[_nodeFromEdge](edge, node.parent)
|
|
1092
|
+
await this[_nodeFromEdge](edge, node.parent, null, required)
|
|
1074
1093
|
continue
|
|
1075
1094
|
} else {
|
|
1076
1095
|
// if the parent's edge is very broad like >=1, and the edge in
|
|
@@ -1081,14 +1100,16 @@ This is a one-time fix-up, please be patient...
|
|
|
1081
1100
|
// a conflict. this is always a problem in strict mode, never
|
|
1082
1101
|
// in force mode, and a problem in non-strict mode if this isn't
|
|
1083
1102
|
// on behalf of our project. in all such cases, we warn at least.
|
|
1084
|
-
await this[_nodeFromEdge](parentEdge, node.parent, edge)
|
|
1103
|
+
const dep = await this[_nodeFromEdge](parentEdge, node.parent, edge, required)
|
|
1085
1104
|
|
|
1086
1105
|
// hooray! that worked!
|
|
1087
1106
|
if (edge.valid)
|
|
1088
1107
|
continue
|
|
1089
1108
|
|
|
1090
|
-
// allow it
|
|
1091
|
-
|
|
1109
|
+
// allow it. either we're overriding, or it's not something
|
|
1110
|
+
// that will be installed by default anyway, and we'll fail when
|
|
1111
|
+
// we get to the point where we need to, if we need to.
|
|
1112
|
+
if (conflictOK || !required.has(dep))
|
|
1092
1113
|
continue
|
|
1093
1114
|
|
|
1094
1115
|
// problem
|
|
@@ -1101,7 +1122,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1101
1122
|
// in non-strict mode if it's not our fault. don't warn here, because
|
|
1102
1123
|
// we are going to warn again when we place the deps, if we end up
|
|
1103
1124
|
// overriding for something else.
|
|
1104
|
-
if (
|
|
1125
|
+
if (conflictOK)
|
|
1105
1126
|
continue
|
|
1106
1127
|
|
|
1107
1128
|
// ok, it's the root, or we're in unforced strict mode, so this is bad
|
|
@@ -1148,6 +1169,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1148
1169
|
[_placeDep] (dep, node, edge, peerEntryEdge = null, peerPath = []) {
|
|
1149
1170
|
if (edge.to &&
|
|
1150
1171
|
!edge.error &&
|
|
1172
|
+
!this[_explicitRequests].has(edge.name) &&
|
|
1151
1173
|
!this[_updateNames].includes(edge.name) &&
|
|
1152
1174
|
!this[_isVulnerable](edge.to))
|
|
1153
1175
|
return []
|
|
@@ -1196,8 +1218,25 @@ This is a one-time fix-up, please be patient...
|
|
|
1196
1218
|
break
|
|
1197
1219
|
}
|
|
1198
1220
|
|
|
1199
|
-
if
|
|
1200
|
-
|
|
1221
|
+
// if we can't find a target, that means that the last placed checked
|
|
1222
|
+
// (and all the places before it) had a copy already. if we're in
|
|
1223
|
+
// --force mode, then the user has explicitly said that they're ok
|
|
1224
|
+
// with conflicts. This can only occur in --force mode in the case
|
|
1225
|
+
// when a node was added to the tree with a peerOptional dep that we
|
|
1226
|
+
// ignored, and then later, that edge became invalid, and we fail to
|
|
1227
|
+
// resolve it. We will warn about it in a moment.
|
|
1228
|
+
if (!target) {
|
|
1229
|
+
if (this[_force]) {
|
|
1230
|
+
// we know that there is a dep (not the root) which is the target
|
|
1231
|
+
// of this edge, or else it wouldn't have been a conflict.
|
|
1232
|
+
target = edge.to.resolveParent
|
|
1233
|
+
canPlace = KEEP
|
|
1234
|
+
} else
|
|
1235
|
+
this[_failPeerConflict](edge)
|
|
1236
|
+
} else {
|
|
1237
|
+
// it worked, so we clearly have no peer conflicts at this point.
|
|
1238
|
+
this[_peerConflict] = null
|
|
1239
|
+
}
|
|
1201
1240
|
|
|
1202
1241
|
this.log.silly(
|
|
1203
1242
|
'placeDep',
|
|
@@ -1208,9 +1247,6 @@ This is a one-time fix-up, please be patient...
|
|
|
1208
1247
|
`want: ${edge.spec || '*'}`
|
|
1209
1248
|
)
|
|
1210
1249
|
|
|
1211
|
-
// it worked, so we clearly have no peer conflicts at this point.
|
|
1212
|
-
this[_peerConflict] = null
|
|
1213
|
-
|
|
1214
1250
|
// Can only get KEEP here if the original edge was valid,
|
|
1215
1251
|
// and we're checking for an update but it's already up to date.
|
|
1216
1252
|
if (canPlace === KEEP) {
|
|
@@ -1396,6 +1432,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1396
1432
|
})
|
|
1397
1433
|
const entryEdge = peerEntryEdge || edge
|
|
1398
1434
|
const source = this[_peerSetSource].get(dep)
|
|
1435
|
+
|
|
1399
1436
|
isSource = isSource || target === source
|
|
1400
1437
|
// if we're overriding the source, then we care if the *target* is
|
|
1401
1438
|
// ours, even if it wasn't actually the original source, since we
|
|
@@ -24,6 +24,7 @@ const loadWorkspacesVirtual = Symbol.for('loadWorkspacesVirtual')
|
|
|
24
24
|
const flagsSuspect = Symbol.for('flagsSuspect')
|
|
25
25
|
const reCalcDepFlags = Symbol('reCalcDepFlags')
|
|
26
26
|
const checkRootEdges = Symbol('checkRootEdges')
|
|
27
|
+
const rootOptionProvided = Symbol('rootOptionProvided')
|
|
27
28
|
|
|
28
29
|
const depsToEdges = (type, deps) =>
|
|
29
30
|
Object.entries(deps).map(d => [type, ...d])
|
|
@@ -63,6 +64,8 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
63
64
|
root = await this[loadRoot](s),
|
|
64
65
|
} = options
|
|
65
66
|
|
|
67
|
+
this[rootOptionProvided] = options.root
|
|
68
|
+
|
|
66
69
|
await this[loadFromShrinkwrap](s, root)
|
|
67
70
|
return treeCheck(this.virtualTree)
|
|
68
71
|
}
|
|
@@ -74,13 +77,17 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
async [loadFromShrinkwrap] (s, root) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
if (!this[rootOptionProvided]) {
|
|
81
|
+
// root is never any of these things, but might be a brand new
|
|
82
|
+
// baby Node object that never had its dep flags calculated.
|
|
83
|
+
root.extraneous = false
|
|
84
|
+
root.dev = false
|
|
85
|
+
root.optional = false
|
|
86
|
+
root.devOptional = false
|
|
87
|
+
root.peer = false
|
|
88
|
+
} else
|
|
89
|
+
this[flagsSuspect] = true
|
|
90
|
+
|
|
84
91
|
this[checkRootEdges](s, root)
|
|
85
92
|
root.meta = s
|
|
86
93
|
this.virtualTree = root
|
|
@@ -88,20 +95,23 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
88
95
|
await this[resolveLinks](links, nodes)
|
|
89
96
|
this[assignBundles](nodes)
|
|
90
97
|
if (this[flagsSuspect])
|
|
91
|
-
this[reCalcDepFlags]()
|
|
98
|
+
this[reCalcDepFlags](nodes.values())
|
|
92
99
|
return root
|
|
93
100
|
}
|
|
94
101
|
|
|
95
|
-
[reCalcDepFlags] () {
|
|
102
|
+
[reCalcDepFlags] (nodes) {
|
|
96
103
|
// reset all dep flags
|
|
97
|
-
|
|
104
|
+
// can't use inventory here, because virtualTree might not be root
|
|
105
|
+
for (const node of nodes) {
|
|
106
|
+
if (node.isRoot || node === this[rootOptionProvided])
|
|
107
|
+
continue
|
|
98
108
|
node.extraneous = true
|
|
99
109
|
node.dev = true
|
|
100
110
|
node.optional = true
|
|
101
111
|
node.devOptional = true
|
|
102
112
|
node.peer = true
|
|
103
113
|
}
|
|
104
|
-
calcDepFlags(this.virtualTree,
|
|
114
|
+
calcDepFlags(this.virtualTree, !this[rootOptionProvided])
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
// check the lockfile deps, and see if they match. if they do not
|
|
@@ -237,6 +247,12 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
237
247
|
// shrinkwrap doesn't include package name unless necessary
|
|
238
248
|
if (!sw.name)
|
|
239
249
|
sw.name = nameFromFolder(path)
|
|
250
|
+
|
|
251
|
+
const dev = sw.dev
|
|
252
|
+
const optional = sw.optional
|
|
253
|
+
const devOptional = dev || optional || sw.devOptional
|
|
254
|
+
const peer = sw.peer
|
|
255
|
+
|
|
240
256
|
const node = new Node({
|
|
241
257
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
242
258
|
root: this.virtualTree,
|
|
@@ -246,6 +262,10 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
246
262
|
resolved: consistentResolve(sw.resolved, this.path, path),
|
|
247
263
|
pkg: sw,
|
|
248
264
|
hasShrinkwrap: sw.hasShrinkwrap,
|
|
265
|
+
dev,
|
|
266
|
+
optional,
|
|
267
|
+
devOptional,
|
|
268
|
+
peer,
|
|
249
269
|
})
|
|
250
270
|
// cast to boolean because they're undefined in the lock file when false
|
|
251
271
|
node.extraneous = !!sw.extraneous
|
package/lib/arborist/reify.js
CHANGED
|
@@ -233,7 +233,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
233
233
|
const actualOpt = this[_global] ? {
|
|
234
234
|
ignoreMissing: true,
|
|
235
235
|
global: true,
|
|
236
|
-
filter: (node, kid) =>
|
|
236
|
+
filter: (node, kid) => this[_explicitRequests].size === 0 || !node.isProjectRoot
|
|
237
237
|
? true
|
|
238
238
|
: (node.edgesOut.has(kid) || this[_explicitRequests].has(kid)),
|
|
239
239
|
} : { ignoreMissing: true }
|
|
@@ -442,7 +442,8 @@ module.exports = cls => class Reifier extends cls {
|
|
|
442
442
|
if (this[_trashList].has(node.path))
|
|
443
443
|
return node
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
const timer = `reifyNode:${node.location}`
|
|
446
|
+
process.emit('time', timer)
|
|
446
447
|
this.addTracker('reify', node.name, node.location)
|
|
447
448
|
|
|
448
449
|
const p = Promise.resolve()
|
|
@@ -454,7 +455,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
454
455
|
return this[_handleOptionalFailure](node, p)
|
|
455
456
|
.then(() => {
|
|
456
457
|
this.finishTracker('reify', node.name, node.location)
|
|
457
|
-
process.emit('timeEnd',
|
|
458
|
+
process.emit('timeEnd', timer)
|
|
458
459
|
return node
|
|
459
460
|
})
|
|
460
461
|
}
|
|
@@ -474,9 +475,14 @@ module.exports = cls => class Reifier extends cls {
|
|
|
474
475
|
|
|
475
476
|
// no idea what this thing is. remove it from the tree.
|
|
476
477
|
if (!res) {
|
|
477
|
-
|
|
478
|
+
const warning = 'invalid or damaged lockfile detected\n' +
|
|
479
|
+
'please re-try this operation once it completes\n' +
|
|
480
|
+
'so that the damage can be corrected, or perform\n' +
|
|
481
|
+
'a fresh install with no lockfile if the problem persists.'
|
|
482
|
+
this.log.warn('reify', warning)
|
|
478
483
|
this.log.verbose('reify', 'unrecognized node in tree', node.path)
|
|
479
484
|
node.parent = null
|
|
485
|
+
node.fsParent = null
|
|
480
486
|
this[_addNodeToTrashList](node)
|
|
481
487
|
return
|
|
482
488
|
}
|
|
@@ -605,7 +611,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
605
611
|
tree: this.diff,
|
|
606
612
|
visit: diff => {
|
|
607
613
|
const node = diff.ideal
|
|
608
|
-
if (node && !node.
|
|
614
|
+
if (node && !node.isProjectRoot && node.package.bundleDependencies &&
|
|
609
615
|
node.package.bundleDependencies.length) {
|
|
610
616
|
maxBundleDepth = Math.max(maxBundleDepth, node.depth)
|
|
611
617
|
if (!bundlesByDepth.has(node.depth))
|
|
@@ -811,7 +817,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
811
817
|
dfwalk({
|
|
812
818
|
tree: this.diff,
|
|
813
819
|
leave: diff => {
|
|
814
|
-
if (!diff.ideal.
|
|
820
|
+
if (!diff.ideal.isProjectRoot)
|
|
815
821
|
nodes.push(diff.ideal)
|
|
816
822
|
},
|
|
817
823
|
// process adds before changes, ignore removals
|
|
@@ -907,7 +913,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
907
913
|
|
|
908
914
|
return Promise.all([
|
|
909
915
|
this[_saveLockFile](saveOpt),
|
|
910
|
-
updateRootPackageJson(
|
|
916
|
+
updateRootPackageJson(this.idealTree),
|
|
911
917
|
]).then(() => process.emit('timeEnd', 'reify:save'))
|
|
912
918
|
}
|
|
913
919
|
|
package/lib/calc-dep-flags.js
CHANGED
|
@@ -11,7 +11,7 @@ const calcDepFlags = (tree, resetRoot = true) => {
|
|
|
11
11
|
tree,
|
|
12
12
|
visit: node => calcDepFlagsStep(node),
|
|
13
13
|
filter: node => node,
|
|
14
|
-
getChildren: node => [...
|
|
14
|
+
getChildren: (node, tree) => [...tree.edgesOut.values()].map(edge => edge.to),
|
|
15
15
|
})
|
|
16
16
|
return ret
|
|
17
17
|
}
|
package/lib/node.js
CHANGED
|
@@ -247,7 +247,7 @@ class Node {
|
|
|
247
247
|
|
|
248
248
|
// true for packages installed directly in the global node_modules folder
|
|
249
249
|
get globalTop () {
|
|
250
|
-
return this.global && this.parent.
|
|
250
|
+
return this.global && this.parent.isProjectRoot
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
get workspaces () {
|
|
@@ -294,8 +294,11 @@ class Node {
|
|
|
294
294
|
const { name = '', version = '' } = this.package
|
|
295
295
|
// root package will prefer package name over folder name,
|
|
296
296
|
// and never be called an alias.
|
|
297
|
-
const
|
|
298
|
-
const
|
|
297
|
+
const { isProjectRoot } = this
|
|
298
|
+
const myname = isProjectRoot ? name || this.name
|
|
299
|
+
: this.name
|
|
300
|
+
const alias = !isProjectRoot && name && myname !== name ? `npm:${name}@`
|
|
301
|
+
: ''
|
|
299
302
|
return `${myname}@${alias}${version}`
|
|
300
303
|
}
|
|
301
304
|
|
|
@@ -339,14 +342,14 @@ class Node {
|
|
|
339
342
|
}
|
|
340
343
|
|
|
341
344
|
[_explain] (edge, seen) {
|
|
342
|
-
if (this.
|
|
345
|
+
if (this.isProjectRoot && !this.sourceReference) {
|
|
343
346
|
return {
|
|
344
347
|
location: this.path,
|
|
345
348
|
}
|
|
346
349
|
}
|
|
347
350
|
|
|
348
351
|
const why = {
|
|
349
|
-
name: this.
|
|
352
|
+
name: this.isProjectRoot ? this.package.name : this.name,
|
|
350
353
|
version: this.package.version,
|
|
351
354
|
}
|
|
352
355
|
if (this.errors.length || !this.package.name || !this.package.version) {
|
|
@@ -384,7 +387,7 @@ class Node {
|
|
|
384
387
|
// and are not keeping it held in this spot anyway.
|
|
385
388
|
const edges = []
|
|
386
389
|
for (const edge of this.edgesIn) {
|
|
387
|
-
if (!edge.valid && !edge.from.
|
|
390
|
+
if (!edge.valid && !edge.from.isProjectRoot)
|
|
388
391
|
continue
|
|
389
392
|
|
|
390
393
|
edges.push(edge)
|
|
@@ -453,7 +456,7 @@ class Node {
|
|
|
453
456
|
}
|
|
454
457
|
|
|
455
458
|
get isWorkspace () {
|
|
456
|
-
if (this.
|
|
459
|
+
if (this.isProjectRoot)
|
|
457
460
|
return false
|
|
458
461
|
const { root } = this
|
|
459
462
|
const { type, to } = root.edgesOut.get(this.package.name) || {}
|
|
@@ -733,7 +736,9 @@ class Node {
|
|
|
733
736
|
// Linked targets that are disconnected from the tree are tops,
|
|
734
737
|
// but don't have a 'path' field, only a 'realpath', because we
|
|
735
738
|
// don't know their canonical location. We don't need their devDeps.
|
|
736
|
-
|
|
739
|
+
const { isTop, path, sourceReference } = this
|
|
740
|
+
const { isTop: srcTop, path: srcPath } = sourceReference || {}
|
|
741
|
+
if (isTop && path && (!sourceReference || srcTop && srcPath))
|
|
737
742
|
this[_loadDepType](this.package.devDependencies, 'dev')
|
|
738
743
|
|
|
739
744
|
const pd = this.package.peerDependencies
|
|
@@ -902,8 +907,8 @@ class Node {
|
|
|
902
907
|
if (this.isLink)
|
|
903
908
|
return node.isLink && this.target.matches(node.target)
|
|
904
909
|
|
|
905
|
-
// if they're two root nodes, they're different if the paths differ
|
|
906
|
-
if (this.
|
|
910
|
+
// if they're two project root nodes, they're different if the paths differ
|
|
911
|
+
if (this.isProjectRoot && node.isProjectRoot)
|
|
907
912
|
return this.path === node.path
|
|
908
913
|
|
|
909
914
|
// if the integrity matches, then they're the same.
|
|
@@ -15,7 +15,7 @@ const depTypes = new Set([
|
|
|
15
15
|
'peerDependencies',
|
|
16
16
|
])
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const updateRootPackageJson = async tree => {
|
|
19
19
|
const filename = resolve(tree.path, 'package.json')
|
|
20
20
|
const originalContent = await readFile(filename, 'utf8')
|
|
21
21
|
.then(data => parseJSON(data))
|
|
@@ -25,6 +25,16 @@ async function updateRootPackageJson ({ tree }) {
|
|
|
25
25
|
...tree.package,
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
// optionalDependencies don't need to be repeated in two places
|
|
29
|
+
if (depsData.dependencies) {
|
|
30
|
+
if (depsData.optionalDependencies) {
|
|
31
|
+
for (const name of Object.keys(depsData.optionalDependencies))
|
|
32
|
+
delete depsData.dependencies[name]
|
|
33
|
+
}
|
|
34
|
+
if (Object.keys(depsData.dependencies).length === 0)
|
|
35
|
+
delete depsData.dependencies
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
// if there's no package.json, just use internal pkg info as source of truth
|
|
29
39
|
const packageJsonContent = originalContent || depsData
|
|
30
40
|
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@npmcli/installed-package-contents": "^1.0.
|
|
7
|
-
"@npmcli/map-workspaces": "^1.0.
|
|
6
|
+
"@npmcli/installed-package-contents": "^1.0.6",
|
|
7
|
+
"@npmcli/map-workspaces": "^1.0.2",
|
|
8
8
|
"@npmcli/metavuln-calculator": "^1.0.1",
|
|
9
9
|
"@npmcli/move-file": "^1.1.0",
|
|
10
10
|
"@npmcli/name-from-folder": "^1.0.1",
|
|
11
11
|
"@npmcli/node-gyp": "^1.0.1",
|
|
12
|
-
"@npmcli/run-script": "^1.8.
|
|
12
|
+
"@npmcli/run-script": "^1.8.2",
|
|
13
13
|
"bin-links": "^2.2.1",
|
|
14
14
|
"cacache": "^15.0.3",
|
|
15
15
|
"common-ancestor-path": "^1.0.1",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"npm-package-arg": "^8.1.0",
|
|
21
21
|
"npm-pick-manifest": "^6.1.0",
|
|
22
22
|
"npm-registry-fetch": "^9.0.0",
|
|
23
|
-
"pacote": "^11.2.
|
|
23
|
+
"pacote": "^11.2.6",
|
|
24
24
|
"parse-conflict-json": "^1.1.1",
|
|
25
25
|
"promise-all-reject-late": "^1.0.0",
|
|
26
26
|
"promise-call-limit": "^1.0.1",
|
|
27
|
-
"read-package-json-fast": "^
|
|
27
|
+
"read-package-json-fast": "^2.0.1",
|
|
28
28
|
"readdir-scoped-modules": "^1.1.0",
|
|
29
29
|
"semver": "^7.3.4",
|
|
30
30
|
"tar": "^6.1.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"postversion": "npm publish",
|
|
56
56
|
"prepublishOnly": "git push origin --follow-tags",
|
|
57
57
|
"eslint": "eslint",
|
|
58
|
-
"lint": "npm run eslint -- \"lib/**/*.js\"",
|
|
58
|
+
"lint": "npm run eslint -- \"lib/**/*.js\" \"test/arborist/*.js\" \"test/*.js\" \"bin/**/*.js\"",
|
|
59
59
|
"lintfix": "npm run lint -- --fix",
|
|
60
60
|
"benchmark": "node scripts/benchmark.js",
|
|
61
61
|
"benchclean": "rm -rf scripts/benchmark/*/"
|
|
@@ -67,9 +67,13 @@
|
|
|
67
67
|
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
|
68
68
|
"license": "ISC",
|
|
69
69
|
"files": [
|
|
70
|
-
"lib/**/*.js"
|
|
70
|
+
"lib/**/*.js",
|
|
71
|
+
"bin/**/*.js"
|
|
71
72
|
],
|
|
72
73
|
"main": "lib/index.js",
|
|
74
|
+
"bin": {
|
|
75
|
+
"arborist": "bin/index.js"
|
|
76
|
+
},
|
|
73
77
|
"tap": {
|
|
74
78
|
"100": true,
|
|
75
79
|
"node-arg": [
|