11cb0ef41Sopenharmony_ciconst { explainNode } = require('../utils/explain-dep.js')
21cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
31cb0ef41Sopenharmony_ciconst semver = require('semver')
41cb0ef41Sopenharmony_ciconst { relative, resolve } = require('path')
51cb0ef41Sopenharmony_ciconst validName = require('validate-npm-package-name')
61cb0ef41Sopenharmony_ciconst ArboristWorkspaceCmd = require('../arborist-cmd.js')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciclass Explain extends ArboristWorkspaceCmd {
91cb0ef41Sopenharmony_ci  static description = 'Explain installed packages'
101cb0ef41Sopenharmony_ci  static name = 'explain'
111cb0ef41Sopenharmony_ci  static usage = ['<package-spec>']
121cb0ef41Sopenharmony_ci  static params = [
131cb0ef41Sopenharmony_ci    'json',
141cb0ef41Sopenharmony_ci    'workspace',
151cb0ef41Sopenharmony_ci  ]
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci  static ignoreImplicitWorkspace = false
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci  // TODO
201cb0ef41Sopenharmony_ci  /* istanbul ignore next */
211cb0ef41Sopenharmony_ci  static async completion (opts, npm) {
221cb0ef41Sopenharmony_ci    const completion = require('../utils/completion/installed-deep.js')
231cb0ef41Sopenharmony_ci    return completion(npm, opts)
241cb0ef41Sopenharmony_ci  }
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci  async exec (args) {
271cb0ef41Sopenharmony_ci    if (!args.length) {
281cb0ef41Sopenharmony_ci      throw this.usageError()
291cb0ef41Sopenharmony_ci    }
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci    const Arborist = require('@npmcli/arborist')
321cb0ef41Sopenharmony_ci    const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions })
331cb0ef41Sopenharmony_ci    const tree = await arb.loadActual()
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci    if (this.npm.flatOptions.workspacesEnabled
361cb0ef41Sopenharmony_ci      && this.workspaceNames
371cb0ef41Sopenharmony_ci      && this.workspaceNames.length
381cb0ef41Sopenharmony_ci    ) {
391cb0ef41Sopenharmony_ci      this.filterSet = arb.workspaceDependencySet(tree, this.workspaceNames)
401cb0ef41Sopenharmony_ci    } else if (!this.npm.flatOptions.workspacesEnabled) {
411cb0ef41Sopenharmony_ci      this.filterSet =
421cb0ef41Sopenharmony_ci        arb.excludeWorkspacesDependencySet(tree)
431cb0ef41Sopenharmony_ci    }
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci    const nodes = new Set()
461cb0ef41Sopenharmony_ci    for (const arg of args) {
471cb0ef41Sopenharmony_ci      for (const node of this.getNodes(tree, arg)) {
481cb0ef41Sopenharmony_ci        const filteredOut = this.filterSet
491cb0ef41Sopenharmony_ci          && this.filterSet.size > 0
501cb0ef41Sopenharmony_ci          && !this.filterSet.has(node)
511cb0ef41Sopenharmony_ci        if (!filteredOut) {
521cb0ef41Sopenharmony_ci          nodes.add(node)
531cb0ef41Sopenharmony_ci        }
541cb0ef41Sopenharmony_ci      }
551cb0ef41Sopenharmony_ci    }
561cb0ef41Sopenharmony_ci    if (nodes.size === 0) {
571cb0ef41Sopenharmony_ci      throw new Error(`No dependencies found matching ${args.join(', ')}`)
581cb0ef41Sopenharmony_ci    }
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci    const expls = []
611cb0ef41Sopenharmony_ci    for (const node of nodes) {
621cb0ef41Sopenharmony_ci      const { extraneous, dev, optional, devOptional, peer, inBundle, overridden } = node
631cb0ef41Sopenharmony_ci      const expl = node.explain()
641cb0ef41Sopenharmony_ci      if (extraneous) {
651cb0ef41Sopenharmony_ci        expl.extraneous = true
661cb0ef41Sopenharmony_ci      } else {
671cb0ef41Sopenharmony_ci        expl.dev = dev
681cb0ef41Sopenharmony_ci        expl.optional = optional
691cb0ef41Sopenharmony_ci        expl.devOptional = devOptional
701cb0ef41Sopenharmony_ci        expl.peer = peer
711cb0ef41Sopenharmony_ci        expl.bundled = inBundle
721cb0ef41Sopenharmony_ci        expl.overridden = overridden
731cb0ef41Sopenharmony_ci      }
741cb0ef41Sopenharmony_ci      expls.push(expl)
751cb0ef41Sopenharmony_ci    }
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ci    if (this.npm.flatOptions.json) {
781cb0ef41Sopenharmony_ci      this.npm.output(JSON.stringify(expls, null, 2))
791cb0ef41Sopenharmony_ci    } else {
801cb0ef41Sopenharmony_ci      this.npm.output(expls.map(expl => {
811cb0ef41Sopenharmony_ci        return explainNode(expl, Infinity, this.npm.chalk)
821cb0ef41Sopenharmony_ci      }).join('\n\n'))
831cb0ef41Sopenharmony_ci    }
841cb0ef41Sopenharmony_ci  }
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  getNodes (tree, arg) {
871cb0ef41Sopenharmony_ci    // if it's just a name, return packages by that name
881cb0ef41Sopenharmony_ci    const { validForOldPackages: valid } = validName(arg)
891cb0ef41Sopenharmony_ci    if (valid) {
901cb0ef41Sopenharmony_ci      return tree.inventory.query('packageName', arg)
911cb0ef41Sopenharmony_ci    }
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci    // if it's a location, get that node
941cb0ef41Sopenharmony_ci    const maybeLoc = arg.replace(/\\/g, '/').replace(/\/+$/, '')
951cb0ef41Sopenharmony_ci    const nodeByLoc = tree.inventory.get(maybeLoc)
961cb0ef41Sopenharmony_ci    if (nodeByLoc) {
971cb0ef41Sopenharmony_ci      return [nodeByLoc]
981cb0ef41Sopenharmony_ci    }
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci    // maybe a path to a node_modules folder
1011cb0ef41Sopenharmony_ci    const maybePath = relative(this.npm.prefix, resolve(maybeLoc))
1021cb0ef41Sopenharmony_ci      .replace(/\\/g, '/').replace(/\/+$/, '')
1031cb0ef41Sopenharmony_ci    const nodeByPath = tree.inventory.get(maybePath)
1041cb0ef41Sopenharmony_ci    if (nodeByPath) {
1051cb0ef41Sopenharmony_ci      return [nodeByPath]
1061cb0ef41Sopenharmony_ci    }
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci    // otherwise, try to select all matching nodes
1091cb0ef41Sopenharmony_ci    try {
1101cb0ef41Sopenharmony_ci      return this.getNodesByVersion(tree, arg)
1111cb0ef41Sopenharmony_ci    } catch (er) {
1121cb0ef41Sopenharmony_ci      return []
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci  }
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci  getNodesByVersion (tree, arg) {
1171cb0ef41Sopenharmony_ci    const spec = npa(arg, this.npm.prefix)
1181cb0ef41Sopenharmony_ci    if (spec.type !== 'version' && spec.type !== 'range') {
1191cb0ef41Sopenharmony_ci      return []
1201cb0ef41Sopenharmony_ci    }
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    return tree.inventory.filter(node => {
1231cb0ef41Sopenharmony_ci      return node.package.name === spec.name &&
1241cb0ef41Sopenharmony_ci        semver.satisfies(node.package.version, spec.rawSpec)
1251cb0ef41Sopenharmony_ci    })
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci}
1281cb0ef41Sopenharmony_cimodule.exports = Explain
129