123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- const { explainNode } = require('./utils/explain-dep.js')
- const completion = require('./utils/completion/installed-deep.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 ArboristWorkspaceCmd = require('./workspaces/arborist-cmd.js')
- class Explain extends ArboristWorkspaceCmd {
- static get description () {
- return 'Explain installed packages'
- }
- /* istanbul ignore next - see test/lib/load-all-commands.js */
- static get name () {
- return 'explain'
- }
- /* istanbul ignore next - see test/lib/load-all-commands.js */
- static get usage () {
- return ['<folder | specifier>']
- }
- /* istanbul ignore next - see test/lib/load-all-commands.js */
- static get params () {
- return [
- 'json',
- 'workspace',
- ]
- }
- /* istanbul ignore next - see test/lib/load-all-commands.js */
- async completion (opts) {
- return completion(this.npm, opts)
- }
- exec (args, cb) {
- this.explain(args).then(() => cb()).catch(cb)
- }
- async explain (args) {
- if (!args.length)
- throw this.usage
- const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions })
- const tree = await arb.loadActual()
- if (this.npm.flatOptions.workspacesEnabled
- && this.workspaceNames
- && this.workspaceNames.length
- )
- this.filterSet = arb.workspaceDependencySet(tree, this.workspaceNames)
- else if (!this.npm.flatOptions.workspacesEnabled) {
- this.filterSet =
- arb.excludeWorkspacesDependencySet(tree)
- }
- const nodes = new Set()
- for (const arg of args) {
- for (const node of this.getNodes(tree, arg)) {
- const filteredOut = this.filterSet
- && this.filterSet.size > 0
- && !this.filterSet.has(node)
- if (!filteredOut)
- 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 (this.npm.flatOptions.json)
- this.npm.output(JSON.stringify(expls, null, 2))
- else {
- this.npm.output(expls.map(expl => {
- return explainNode(expl, Infinity, this.npm.color)
- }).join('\n\n'))
- }
- }
- 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('packageName', 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(this.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 this.getNodesByVersion(tree, arg)
- } catch (er) {
- return []
- }
- }
- getNodesByVersion (tree, arg) {
- const spec = npa(arg, this.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 = Explain
|