1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
const usageUtil = require('./utils/usage.js')
const npm = require('./npm.js')
const { explainNode } = require('./utils/explain-dep.js')
const completion = require('./utils/completion/installed-deep.js')
const output = require('./utils/output.js')
const Arborist = require('@npmcli/arborist')
const npa = require('npm-package-arg')
const semver = require('semver')
const { relative, resolve } = require('path')
const validName = require('validate-npm-package-name')
const usage = usageUtil('explain', 'npm explain <folder | specifier>')
const cmd = (args, cb) => explain(args).then(() => cb()).catch(cb)
const explain = async (args) => {
if (!args.length)
throw usage
const arb = new Arborist({ path: npm.prefix, ...npm.flatOptions })
const tree = await arb.loadActual()
const nodes = new Set()
for (const arg of args) {
for (const node of getNodes(tree, arg))
nodes.add(node)
}
if (nodes.size === 0)
throw `No dependencies found matching ${args.join(', ')}`
const expls = []
for (const node of nodes) {
const { extraneous, dev, optional, devOptional, peer, inBundle } = node
const expl = node.explain()
if (extraneous)
expl.extraneous = true
else {
expl.dev = dev
expl.optional = optional
expl.devOptional = devOptional
expl.peer = peer
expl.bundled = inBundle
}
expls.push(expl)
}
if (npm.flatOptions.json)
output(JSON.stringify(expls, null, 2))
else {
output(expls.map(expl => {
return explainNode(expl, Infinity, npm.color)
}).join('\n\n'))
}
}
const getNodes = (tree, arg) => {
// if it's just a name, return packages by that name
const { validForOldPackages: valid } = validName(arg)
if (valid)
return tree.inventory.query('name', arg)
// if it's a location, get that node
const maybeLoc = arg.replace(/\\/g, '/').replace(/\/+$/, '')
const nodeByLoc = tree.inventory.get(maybeLoc)
if (nodeByLoc)
return [nodeByLoc]
// maybe a path to a node_modules folder
const maybePath = relative(npm.prefix, resolve(maybeLoc))
.replace(/\\/g, '/').replace(/\/+$/, '')
const nodeByPath = tree.inventory.get(maybePath)
if (nodeByPath)
return [nodeByPath]
// otherwise, try to select all matching nodes
try {
return getNodesByVersion(tree, arg)
} catch (er) {
return []
}
}
const getNodesByVersion = (tree, arg) => {
const spec = npa(arg, npm.prefix)
if (spec.type !== 'version' && spec.type !== 'range')
return []
return tree.inventory.filter(node => {
return node.package.name === spec.name &&
semver.satisfies(node.package.version, spec.rawSpec)
})
}
module.exports = Object.assign(cmd, { usage, completion })
|