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