All files / lib explain.js

100% Statements 62/62
100% Branches 20/20
100% Functions 7/7
100% Lines 60/60

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 941x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x   10x   1x 10x 1x   9x 9x   9x 9x 11x 7x   9x 3x   6x 6x 7x 7x 7x 1x   6x 6x 6x 6x   7x     6x 1x   5x 6x         1x   11x 11x 2x     9x 9x 9x 2x     7x   7x 7x 1x     6x 6x   1x       1x 6x 5x 2x   3x 10x         1x  
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 } = node
    const expl = node.explain()
    if (extraneous)
      expl.extraneous = true
    else {
      expl.dev = dev
      expl.optional = optional
      expl.devOptional = devOptional
      expl.peer = peer
    }
    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 })