11cb0ef41Sopenharmony_ciconst { resolve, relative, sep } = require('path') 21cb0ef41Sopenharmony_ciconst relativePrefix = `.${sep}` 31cb0ef41Sopenharmony_ciconst { EOL } = require('os') 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciconst archy = require('archy') 61cb0ef41Sopenharmony_ciconst { breadth } = require('treeverse') 71cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg') 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciconst _depth = Symbol('depth') 101cb0ef41Sopenharmony_ciconst _dedupe = Symbol('dedupe') 111cb0ef41Sopenharmony_ciconst _filteredBy = Symbol('filteredBy') 121cb0ef41Sopenharmony_ciconst _include = Symbol('include') 131cb0ef41Sopenharmony_ciconst _invalid = Symbol('invalid') 141cb0ef41Sopenharmony_ciconst _name = Symbol('name') 151cb0ef41Sopenharmony_ciconst _missing = Symbol('missing') 161cb0ef41Sopenharmony_ciconst _parent = Symbol('parent') 171cb0ef41Sopenharmony_ciconst _problems = Symbol('problems') 181cb0ef41Sopenharmony_ciconst _required = Symbol('required') 191cb0ef41Sopenharmony_ciconst _type = Symbol('type') 201cb0ef41Sopenharmony_ciconst ArboristWorkspaceCmd = require('../arborist-cmd.js') 211cb0ef41Sopenharmony_ciconst localeCompare = require('@isaacs/string-locale-compare')('en') 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciclass LS extends ArboristWorkspaceCmd { 241cb0ef41Sopenharmony_ci static description = 'List installed packages' 251cb0ef41Sopenharmony_ci static name = 'ls' 261cb0ef41Sopenharmony_ci static usage = ['<package-spec>'] 271cb0ef41Sopenharmony_ci static params = [ 281cb0ef41Sopenharmony_ci 'all', 291cb0ef41Sopenharmony_ci 'json', 301cb0ef41Sopenharmony_ci 'long', 311cb0ef41Sopenharmony_ci 'parseable', 321cb0ef41Sopenharmony_ci 'global', 331cb0ef41Sopenharmony_ci 'depth', 341cb0ef41Sopenharmony_ci 'omit', 351cb0ef41Sopenharmony_ci 'include', 361cb0ef41Sopenharmony_ci 'link', 371cb0ef41Sopenharmony_ci 'package-lock-only', 381cb0ef41Sopenharmony_ci 'unicode', 391cb0ef41Sopenharmony_ci ...super.params, 401cb0ef41Sopenharmony_ci ] 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci // TODO 431cb0ef41Sopenharmony_ci /* istanbul ignore next */ 441cb0ef41Sopenharmony_ci static async completion (opts, npm) { 451cb0ef41Sopenharmony_ci const completion = require('../utils/completion/installed-deep.js') 461cb0ef41Sopenharmony_ci return completion(npm, opts) 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci async exec (args) { 501cb0ef41Sopenharmony_ci const all = this.npm.config.get('all') 511cb0ef41Sopenharmony_ci const chalk = this.npm.chalk 521cb0ef41Sopenharmony_ci const depth = this.npm.config.get('depth') 531cb0ef41Sopenharmony_ci const global = this.npm.global 541cb0ef41Sopenharmony_ci const json = this.npm.config.get('json') 551cb0ef41Sopenharmony_ci const link = this.npm.config.get('link') 561cb0ef41Sopenharmony_ci const long = this.npm.config.get('long') 571cb0ef41Sopenharmony_ci const omit = this.npm.flatOptions.omit 581cb0ef41Sopenharmony_ci const parseable = this.npm.config.get('parseable') 591cb0ef41Sopenharmony_ci const unicode = this.npm.config.get('unicode') 601cb0ef41Sopenharmony_ci const packageLockOnly = this.npm.config.get('package-lock-only') 611cb0ef41Sopenharmony_ci const workspacesEnabled = this.npm.flatOptions.workspacesEnabled 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci const path = global ? resolve(this.npm.globalDir, '..') : this.npm.prefix 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci const Arborist = require('@npmcli/arborist') 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci const arb = new Arborist({ 681cb0ef41Sopenharmony_ci global, 691cb0ef41Sopenharmony_ci ...this.npm.flatOptions, 701cb0ef41Sopenharmony_ci legacyPeerDeps: false, 711cb0ef41Sopenharmony_ci path, 721cb0ef41Sopenharmony_ci }) 731cb0ef41Sopenharmony_ci const tree = await this.initTree({ arb, args, packageLockOnly }) 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_ci // filters by workspaces nodes when using -w <workspace-name> 761cb0ef41Sopenharmony_ci // We only have to filter the first layer of edges, so we don't 771cb0ef41Sopenharmony_ci // explore anything that isn't part of the selected workspace set. 781cb0ef41Sopenharmony_ci let wsNodes 791cb0ef41Sopenharmony_ci if (this.workspaceNames && this.workspaceNames.length) { 801cb0ef41Sopenharmony_ci wsNodes = arb.workspaceNodes(tree, this.workspaceNames) 811cb0ef41Sopenharmony_ci } 821cb0ef41Sopenharmony_ci const filterBySelectedWorkspaces = edge => { 831cb0ef41Sopenharmony_ci if (!workspacesEnabled 841cb0ef41Sopenharmony_ci && edge.from.isProjectRoot 851cb0ef41Sopenharmony_ci && edge.to.isWorkspace 861cb0ef41Sopenharmony_ci ) { 871cb0ef41Sopenharmony_ci return false 881cb0ef41Sopenharmony_ci } 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci if (!wsNodes || !wsNodes.length) { 911cb0ef41Sopenharmony_ci return true 921cb0ef41Sopenharmony_ci } 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci if (this.npm.flatOptions.includeWorkspaceRoot 951cb0ef41Sopenharmony_ci && edge.to && !edge.to.isWorkspace) { 961cb0ef41Sopenharmony_ci return true 971cb0ef41Sopenharmony_ci } 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci if (edge.from.isProjectRoot) { 1001cb0ef41Sopenharmony_ci return (edge.to 1011cb0ef41Sopenharmony_ci && edge.to.isWorkspace 1021cb0ef41Sopenharmony_ci && wsNodes.includes(edge.to.target)) 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci return true 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci const seenItems = new Set() 1091cb0ef41Sopenharmony_ci const seenNodes = new Map() 1101cb0ef41Sopenharmony_ci const problems = new Set() 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci // defines special handling of printed depth when filtering with args 1131cb0ef41Sopenharmony_ci const filterDefaultDepth = depth === null ? Infinity : depth 1141cb0ef41Sopenharmony_ci const depthToPrint = (all || args.length) 1151cb0ef41Sopenharmony_ci ? filterDefaultDepth 1161cb0ef41Sopenharmony_ci : (depth || 0) 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ci // add root node of tree to list of seenNodes 1191cb0ef41Sopenharmony_ci seenNodes.set(tree.path, tree) 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci // tree traversal happens here, using treeverse.breadth 1221cb0ef41Sopenharmony_ci const result = await breadth({ 1231cb0ef41Sopenharmony_ci tree, 1241cb0ef41Sopenharmony_ci // recursive method, `node` is going to be the current elem (starting from 1251cb0ef41Sopenharmony_ci // the `tree` obj) that was just visited in the `visit` method below 1261cb0ef41Sopenharmony_ci // `nodeResult` is going to be the returned `item` from `visit` 1271cb0ef41Sopenharmony_ci getChildren (node, nodeResult) { 1281cb0ef41Sopenharmony_ci const seenPaths = new Set() 1291cb0ef41Sopenharmony_ci const workspace = node.isWorkspace 1301cb0ef41Sopenharmony_ci const currentDepth = workspace ? 0 : node[_depth] 1311cb0ef41Sopenharmony_ci const shouldSkipChildren = 1321cb0ef41Sopenharmony_ci !(node instanceof Arborist.Node) || (currentDepth > depthToPrint) 1331cb0ef41Sopenharmony_ci return (shouldSkipChildren) 1341cb0ef41Sopenharmony_ci ? [] 1351cb0ef41Sopenharmony_ci : [...(node.target).edgesOut.values()] 1361cb0ef41Sopenharmony_ci .filter(filterBySelectedWorkspaces) 1371cb0ef41Sopenharmony_ci .filter(currentDepth === 0 ? filterByEdgesTypes({ 1381cb0ef41Sopenharmony_ci link, 1391cb0ef41Sopenharmony_ci omit, 1401cb0ef41Sopenharmony_ci }) : () => true) 1411cb0ef41Sopenharmony_ci .map(mapEdgesToNodes({ seenPaths })) 1421cb0ef41Sopenharmony_ci .concat(appendExtraneousChildren({ node, seenPaths })) 1431cb0ef41Sopenharmony_ci .sort(sortAlphabetically) 1441cb0ef41Sopenharmony_ci .map(augmentNodesWithMetadata({ 1451cb0ef41Sopenharmony_ci args, 1461cb0ef41Sopenharmony_ci currentDepth, 1471cb0ef41Sopenharmony_ci nodeResult, 1481cb0ef41Sopenharmony_ci seenNodes, 1491cb0ef41Sopenharmony_ci })) 1501cb0ef41Sopenharmony_ci }, 1511cb0ef41Sopenharmony_ci // visit each `node` of the `tree`, returning an `item` - these are 1521cb0ef41Sopenharmony_ci // the elements that will be used to build the final output 1531cb0ef41Sopenharmony_ci visit (node) { 1541cb0ef41Sopenharmony_ci node[_problems] = getProblems(node, { global }) 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci const item = json 1571cb0ef41Sopenharmony_ci ? getJsonOutputItem(node, { global, long }) 1581cb0ef41Sopenharmony_ci : parseable 1591cb0ef41Sopenharmony_ci ? null 1601cb0ef41Sopenharmony_ci : getHumanOutputItem(node, { args, chalk, global, long }) 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci // loop through list of node problems to add them to global list 1631cb0ef41Sopenharmony_ci if (node[_include]) { 1641cb0ef41Sopenharmony_ci for (const problem of node[_problems]) { 1651cb0ef41Sopenharmony_ci problems.add(problem) 1661cb0ef41Sopenharmony_ci } 1671cb0ef41Sopenharmony_ci } 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci seenItems.add(item) 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci // return a promise so we don't blow the stack 1721cb0ef41Sopenharmony_ci return Promise.resolve(item) 1731cb0ef41Sopenharmony_ci }, 1741cb0ef41Sopenharmony_ci }) 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci // handle the special case of a broken package.json in the root folder 1771cb0ef41Sopenharmony_ci const [rootError] = tree.errors.filter(e => 1781cb0ef41Sopenharmony_ci e.code === 'EJSONPARSE' && e.path === resolve(path, 'package.json')) 1791cb0ef41Sopenharmony_ci 1801cb0ef41Sopenharmony_ci this.npm.outputBuffer( 1811cb0ef41Sopenharmony_ci json ? jsonOutput({ path, problems, result, rootError, seenItems }) : 1821cb0ef41Sopenharmony_ci parseable ? parseableOutput({ seenNodes, global, long }) : 1831cb0ef41Sopenharmony_ci humanOutput({ chalk, result, seenItems, unicode }) 1841cb0ef41Sopenharmony_ci ) 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ci // if filtering items, should exit with error code on no results 1871cb0ef41Sopenharmony_ci if (result && !result[_include] && args.length) { 1881cb0ef41Sopenharmony_ci process.exitCode = 1 1891cb0ef41Sopenharmony_ci } 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci if (rootError) { 1921cb0ef41Sopenharmony_ci throw Object.assign( 1931cb0ef41Sopenharmony_ci new Error('Failed to parse root package.json'), 1941cb0ef41Sopenharmony_ci { code: 'EJSONPARSE' } 1951cb0ef41Sopenharmony_ci ) 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci const shouldThrow = problems.size && 1991cb0ef41Sopenharmony_ci ![...problems].every(problem => problem.startsWith('extraneous:')) 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_ci if (shouldThrow) { 2021cb0ef41Sopenharmony_ci throw Object.assign( 2031cb0ef41Sopenharmony_ci new Error([...problems].join(EOL)), 2041cb0ef41Sopenharmony_ci { code: 'ELSPROBLEMS' } 2051cb0ef41Sopenharmony_ci ) 2061cb0ef41Sopenharmony_ci } 2071cb0ef41Sopenharmony_ci } 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci async initTree ({ arb, args, packageLockOnly }) { 2101cb0ef41Sopenharmony_ci const tree = await ( 2111cb0ef41Sopenharmony_ci packageLockOnly 2121cb0ef41Sopenharmony_ci ? arb.loadVirtual() 2131cb0ef41Sopenharmony_ci : arb.loadActual() 2141cb0ef41Sopenharmony_ci ) 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci tree[_include] = args.length === 0 2171cb0ef41Sopenharmony_ci tree[_depth] = 0 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci return tree 2201cb0ef41Sopenharmony_ci } 2211cb0ef41Sopenharmony_ci} 2221cb0ef41Sopenharmony_cimodule.exports = LS 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_ciconst isGitNode = (node) => { 2251cb0ef41Sopenharmony_ci if (!node.resolved) { 2261cb0ef41Sopenharmony_ci return 2271cb0ef41Sopenharmony_ci } 2281cb0ef41Sopenharmony_ci 2291cb0ef41Sopenharmony_ci try { 2301cb0ef41Sopenharmony_ci const { type } = npa(node.resolved) 2311cb0ef41Sopenharmony_ci return type === 'git' || type === 'hosted' 2321cb0ef41Sopenharmony_ci } catch (err) { 2331cb0ef41Sopenharmony_ci return false 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci} 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ciconst isOptional = (node) => 2381cb0ef41Sopenharmony_ci node[_type] === 'optional' || node[_type] === 'peerOptional' 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ciconst isExtraneous = (node, { global }) => 2411cb0ef41Sopenharmony_ci node.extraneous && !global 2421cb0ef41Sopenharmony_ci 2431cb0ef41Sopenharmony_ciconst getProblems = (node, { global }) => { 2441cb0ef41Sopenharmony_ci const problems = new Set() 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci if (node[_missing] && !isOptional(node)) { 2471cb0ef41Sopenharmony_ci problems.add(`missing: ${node.pkgid}, required by ${node[_missing]}`) 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci if (node[_invalid]) { 2511cb0ef41Sopenharmony_ci problems.add(`invalid: ${node.pkgid} ${node.path}`) 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci if (isExtraneous(node, { global })) { 2551cb0ef41Sopenharmony_ci problems.add(`extraneous: ${node.pkgid} ${node.path}`) 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci 2581cb0ef41Sopenharmony_ci return problems 2591cb0ef41Sopenharmony_ci} 2601cb0ef41Sopenharmony_ci 2611cb0ef41Sopenharmony_ci// annotates _parent and _include metadata into the resulting 2621cb0ef41Sopenharmony_ci// item obj allowing for filtering out results during output 2631cb0ef41Sopenharmony_ciconst augmentItemWithIncludeMetadata = (node, item) => { 2641cb0ef41Sopenharmony_ci item[_parent] = node[_parent] 2651cb0ef41Sopenharmony_ci item[_include] = node[_include] 2661cb0ef41Sopenharmony_ci 2671cb0ef41Sopenharmony_ci // append current item to its parent.nodes which is the 2681cb0ef41Sopenharmony_ci // structure expected by archy in order to print tree 2691cb0ef41Sopenharmony_ci if (node[_include]) { 2701cb0ef41Sopenharmony_ci // includes all ancestors of included node 2711cb0ef41Sopenharmony_ci let p = node[_parent] 2721cb0ef41Sopenharmony_ci while (p) { 2731cb0ef41Sopenharmony_ci p[_include] = true 2741cb0ef41Sopenharmony_ci p = p[_parent] 2751cb0ef41Sopenharmony_ci } 2761cb0ef41Sopenharmony_ci } 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_ci return item 2791cb0ef41Sopenharmony_ci} 2801cb0ef41Sopenharmony_ci 2811cb0ef41Sopenharmony_ciconst getHumanOutputItem = (node, { args, chalk, global, long }) => { 2821cb0ef41Sopenharmony_ci const { pkgid, path } = node 2831cb0ef41Sopenharmony_ci const workspacePkgId = chalk.green(pkgid) 2841cb0ef41Sopenharmony_ci let printable = node.isWorkspace ? workspacePkgId : pkgid 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci // special formatting for top-level package name 2871cb0ef41Sopenharmony_ci if (node.isRoot) { 2881cb0ef41Sopenharmony_ci const hasNoPackageJson = !Object.keys(node.package).length 2891cb0ef41Sopenharmony_ci if (hasNoPackageJson || global) { 2901cb0ef41Sopenharmony_ci printable = path 2911cb0ef41Sopenharmony_ci } else { 2921cb0ef41Sopenharmony_ci printable += `${long ? EOL : ' '}${path}` 2931cb0ef41Sopenharmony_ci } 2941cb0ef41Sopenharmony_ci } 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci const highlightDepName = args.length && node[_filteredBy] 2971cb0ef41Sopenharmony_ci const missingColor = isOptional(node) 2981cb0ef41Sopenharmony_ci ? chalk.yellow.bgBlack 2991cb0ef41Sopenharmony_ci : chalk.red.bgBlack 3001cb0ef41Sopenharmony_ci const missingMsg = `UNMET ${isOptional(node) ? 'OPTIONAL ' : ''}DEPENDENCY` 3011cb0ef41Sopenharmony_ci const targetLocation = node.root 3021cb0ef41Sopenharmony_ci ? relative(node.root.realpath, node.realpath) 3031cb0ef41Sopenharmony_ci : node.targetLocation 3041cb0ef41Sopenharmony_ci const invalid = node[_invalid] 3051cb0ef41Sopenharmony_ci ? `invalid: ${node[_invalid]}` 3061cb0ef41Sopenharmony_ci : '' 3071cb0ef41Sopenharmony_ci const label = 3081cb0ef41Sopenharmony_ci ( 3091cb0ef41Sopenharmony_ci node[_missing] 3101cb0ef41Sopenharmony_ci ? missingColor(missingMsg) + ' ' 3111cb0ef41Sopenharmony_ci : '' 3121cb0ef41Sopenharmony_ci ) + 3131cb0ef41Sopenharmony_ci `${highlightDepName ? chalk.yellow.bgBlack(printable) : printable}` + 3141cb0ef41Sopenharmony_ci ( 3151cb0ef41Sopenharmony_ci node[_dedupe] 3161cb0ef41Sopenharmony_ci ? ' ' + chalk.gray('deduped') 3171cb0ef41Sopenharmony_ci : '' 3181cb0ef41Sopenharmony_ci ) + 3191cb0ef41Sopenharmony_ci ( 3201cb0ef41Sopenharmony_ci invalid 3211cb0ef41Sopenharmony_ci ? ' ' + chalk.red.bgBlack(invalid) 3221cb0ef41Sopenharmony_ci : '' 3231cb0ef41Sopenharmony_ci ) + 3241cb0ef41Sopenharmony_ci ( 3251cb0ef41Sopenharmony_ci isExtraneous(node, { global }) 3261cb0ef41Sopenharmony_ci ? ' ' + chalk.green.bgBlack('extraneous') 3271cb0ef41Sopenharmony_ci : '' 3281cb0ef41Sopenharmony_ci ) + 3291cb0ef41Sopenharmony_ci ( 3301cb0ef41Sopenharmony_ci node.overridden 3311cb0ef41Sopenharmony_ci ? ' ' + chalk.gray('overridden') 3321cb0ef41Sopenharmony_ci : '' 3331cb0ef41Sopenharmony_ci ) + 3341cb0ef41Sopenharmony_ci (isGitNode(node) ? ` (${node.resolved})` : '') + 3351cb0ef41Sopenharmony_ci (node.isLink ? ` -> ${relativePrefix}${targetLocation}` : '') + 3361cb0ef41Sopenharmony_ci (long ? `${EOL}${node.package.description || ''}` : '') 3371cb0ef41Sopenharmony_ci 3381cb0ef41Sopenharmony_ci return augmentItemWithIncludeMetadata(node, { label, nodes: [] }) 3391cb0ef41Sopenharmony_ci} 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ciconst getJsonOutputItem = (node, { global, long }) => { 3421cb0ef41Sopenharmony_ci const item = {} 3431cb0ef41Sopenharmony_ci 3441cb0ef41Sopenharmony_ci if (node.version) { 3451cb0ef41Sopenharmony_ci item.version = node.version 3461cb0ef41Sopenharmony_ci } 3471cb0ef41Sopenharmony_ci 3481cb0ef41Sopenharmony_ci if (node.resolved) { 3491cb0ef41Sopenharmony_ci item.resolved = node.resolved 3501cb0ef41Sopenharmony_ci } 3511cb0ef41Sopenharmony_ci 3521cb0ef41Sopenharmony_ci // if the node is the project root, do not add the overridden flag. the project root can't be 3531cb0ef41Sopenharmony_ci // overridden anyway, and if we add the flag it causes undesirable behavior when `npm ls --json` 3541cb0ef41Sopenharmony_ci // is ran in an empty directory since we end up printing an object with only an overridden prop 3551cb0ef41Sopenharmony_ci if (!node.isProjectRoot) { 3561cb0ef41Sopenharmony_ci item.overridden = node.overridden 3571cb0ef41Sopenharmony_ci } 3581cb0ef41Sopenharmony_ci 3591cb0ef41Sopenharmony_ci item[_name] = node.name 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_ci // special formatting for top-level package name 3621cb0ef41Sopenharmony_ci const hasPackageJson = 3631cb0ef41Sopenharmony_ci node && node.package && Object.keys(node.package).length 3641cb0ef41Sopenharmony_ci if (node.isRoot && hasPackageJson) { 3651cb0ef41Sopenharmony_ci item.name = node.package.name || node.name 3661cb0ef41Sopenharmony_ci } 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_ci if (long && !node[_missing]) { 3691cb0ef41Sopenharmony_ci item.name = item[_name] 3701cb0ef41Sopenharmony_ci const { dependencies, ...packageInfo } = node.package 3711cb0ef41Sopenharmony_ci Object.assign(item, packageInfo) 3721cb0ef41Sopenharmony_ci item.extraneous = false 3731cb0ef41Sopenharmony_ci item.path = node.path 3741cb0ef41Sopenharmony_ci item._dependencies = { 3751cb0ef41Sopenharmony_ci ...node.package.dependencies, 3761cb0ef41Sopenharmony_ci ...node.package.optionalDependencies, 3771cb0ef41Sopenharmony_ci } 3781cb0ef41Sopenharmony_ci item.devDependencies = node.package.devDependencies || {} 3791cb0ef41Sopenharmony_ci item.peerDependencies = node.package.peerDependencies || {} 3801cb0ef41Sopenharmony_ci } 3811cb0ef41Sopenharmony_ci 3821cb0ef41Sopenharmony_ci // augment json output items with extra metadata 3831cb0ef41Sopenharmony_ci if (isExtraneous(node, { global })) { 3841cb0ef41Sopenharmony_ci item.extraneous = true 3851cb0ef41Sopenharmony_ci } 3861cb0ef41Sopenharmony_ci 3871cb0ef41Sopenharmony_ci if (node[_invalid]) { 3881cb0ef41Sopenharmony_ci item.invalid = node[_invalid] 3891cb0ef41Sopenharmony_ci } 3901cb0ef41Sopenharmony_ci 3911cb0ef41Sopenharmony_ci if (node[_missing] && !isOptional(node)) { 3921cb0ef41Sopenharmony_ci item.required = node[_required] 3931cb0ef41Sopenharmony_ci item.missing = true 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci if (node[_include] && node[_problems] && node[_problems].size) { 3961cb0ef41Sopenharmony_ci item.problems = [...node[_problems]] 3971cb0ef41Sopenharmony_ci } 3981cb0ef41Sopenharmony_ci 3991cb0ef41Sopenharmony_ci return augmentItemWithIncludeMetadata(node, item) 4001cb0ef41Sopenharmony_ci} 4011cb0ef41Sopenharmony_ci 4021cb0ef41Sopenharmony_ciconst filterByEdgesTypes = ({ link, omit }) => (edge) => { 4031cb0ef41Sopenharmony_ci for (const omitType of omit) { 4041cb0ef41Sopenharmony_ci if (edge[omitType]) { 4051cb0ef41Sopenharmony_ci return false 4061cb0ef41Sopenharmony_ci } 4071cb0ef41Sopenharmony_ci } 4081cb0ef41Sopenharmony_ci return link ? edge.to && edge.to.isLink : true 4091cb0ef41Sopenharmony_ci} 4101cb0ef41Sopenharmony_ci 4111cb0ef41Sopenharmony_ciconst appendExtraneousChildren = ({ node, seenPaths }) => 4121cb0ef41Sopenharmony_ci // extraneous children are not represented 4131cb0ef41Sopenharmony_ci // in edges out, so here we add them to the list: 4141cb0ef41Sopenharmony_ci [...node.children.values()] 4151cb0ef41Sopenharmony_ci .filter(i => !seenPaths.has(i.path) && i.extraneous) 4161cb0ef41Sopenharmony_ci 4171cb0ef41Sopenharmony_ciconst mapEdgesToNodes = ({ seenPaths }) => (edge) => { 4181cb0ef41Sopenharmony_ci let node = edge.to 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_ci // if the edge is linking to a missing node, we go ahead 4211cb0ef41Sopenharmony_ci // and create a new obj that will represent the missing node 4221cb0ef41Sopenharmony_ci if (edge.missing || (edge.optional && !node)) { 4231cb0ef41Sopenharmony_ci const { name, spec } = edge 4241cb0ef41Sopenharmony_ci const pkgid = `${name}@${spec}` 4251cb0ef41Sopenharmony_ci node = { name, pkgid, [_missing]: edge.from.pkgid } 4261cb0ef41Sopenharmony_ci } 4271cb0ef41Sopenharmony_ci 4281cb0ef41Sopenharmony_ci // keeps track of a set of seen paths to avoid the edge case in which a tree 4291cb0ef41Sopenharmony_ci // item would appear twice given that it's a children of an extraneous item, 4301cb0ef41Sopenharmony_ci // so it's marked extraneous but it will ALSO show up in edgesOuts of 4311cb0ef41Sopenharmony_ci // its parent so it ends up as two diff nodes if we don't track it 4321cb0ef41Sopenharmony_ci if (node.path) { 4331cb0ef41Sopenharmony_ci seenPaths.add(node.path) 4341cb0ef41Sopenharmony_ci } 4351cb0ef41Sopenharmony_ci 4361cb0ef41Sopenharmony_ci node[_required] = edge.spec || '*' 4371cb0ef41Sopenharmony_ci node[_type] = edge.type 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci if (edge.invalid) { 4401cb0ef41Sopenharmony_ci const spec = JSON.stringify(node[_required]) 4411cb0ef41Sopenharmony_ci const from = edge.from.location || 'the root project' 4421cb0ef41Sopenharmony_ci node[_invalid] = (node[_invalid] ? node[_invalid] + ', ' : '') + 4431cb0ef41Sopenharmony_ci (`${spec} from ${from}`) 4441cb0ef41Sopenharmony_ci } 4451cb0ef41Sopenharmony_ci 4461cb0ef41Sopenharmony_ci return node 4471cb0ef41Sopenharmony_ci} 4481cb0ef41Sopenharmony_ci 4491cb0ef41Sopenharmony_ciconst filterByPositionalArgs = (args, { node }) => 4501cb0ef41Sopenharmony_ci args.length > 0 ? args.some( 4511cb0ef41Sopenharmony_ci (spec) => (node.satisfies && node.satisfies(spec)) 4521cb0ef41Sopenharmony_ci ) : true 4531cb0ef41Sopenharmony_ci 4541cb0ef41Sopenharmony_ciconst augmentNodesWithMetadata = ({ 4551cb0ef41Sopenharmony_ci args, 4561cb0ef41Sopenharmony_ci currentDepth, 4571cb0ef41Sopenharmony_ci nodeResult, 4581cb0ef41Sopenharmony_ci seenNodes, 4591cb0ef41Sopenharmony_ci}) => (node) => { 4601cb0ef41Sopenharmony_ci // if the original edge was a deduped dep, treeverse will fail to 4611cb0ef41Sopenharmony_ci // revisit that node in tree traversal logic, so we make it so that 4621cb0ef41Sopenharmony_ci // we have a diff obj for deduped nodes: 4631cb0ef41Sopenharmony_ci if (seenNodes.has(node.path)) { 4641cb0ef41Sopenharmony_ci const { realpath, root } = node 4651cb0ef41Sopenharmony_ci const targetLocation = root ? relative(root.realpath, realpath) 4661cb0ef41Sopenharmony_ci : node.targetLocation 4671cb0ef41Sopenharmony_ci node = { 4681cb0ef41Sopenharmony_ci name: node.name, 4691cb0ef41Sopenharmony_ci version: node.version, 4701cb0ef41Sopenharmony_ci pkgid: node.pkgid, 4711cb0ef41Sopenharmony_ci package: node.package, 4721cb0ef41Sopenharmony_ci path: node.path, 4731cb0ef41Sopenharmony_ci isLink: node.isLink, 4741cb0ef41Sopenharmony_ci realpath: node.realpath, 4751cb0ef41Sopenharmony_ci targetLocation, 4761cb0ef41Sopenharmony_ci [_type]: node[_type], 4771cb0ef41Sopenharmony_ci [_invalid]: node[_invalid], 4781cb0ef41Sopenharmony_ci [_missing]: node[_missing], 4791cb0ef41Sopenharmony_ci // if it's missing, it's not deduped, it's just missing 4801cb0ef41Sopenharmony_ci [_dedupe]: !node[_missing], 4811cb0ef41Sopenharmony_ci } 4821cb0ef41Sopenharmony_ci } else { 4831cb0ef41Sopenharmony_ci // keeps track of already seen nodes in order to check for dedupes 4841cb0ef41Sopenharmony_ci seenNodes.set(node.path, node) 4851cb0ef41Sopenharmony_ci } 4861cb0ef41Sopenharmony_ci 4871cb0ef41Sopenharmony_ci // _parent is going to be a ref to a treeverse-visited node (returned from 4881cb0ef41Sopenharmony_ci // getHumanOutputItem, getJsonOutputItem, etc) so that we have an easy 4891cb0ef41Sopenharmony_ci // shortcut to place new nodes in their right place during tree traversal 4901cb0ef41Sopenharmony_ci node[_parent] = nodeResult 4911cb0ef41Sopenharmony_ci // _include is the property that allow us to filter based on position args 4921cb0ef41Sopenharmony_ci // e.g: `npm ls foo`, `npm ls simple-output@2` 4931cb0ef41Sopenharmony_ci // _filteredBy is used to apply extra color info to the item that 4941cb0ef41Sopenharmony_ci // was used in args in order to filter 4951cb0ef41Sopenharmony_ci node[_filteredBy] = node[_include] = 4961cb0ef41Sopenharmony_ci filterByPositionalArgs(args, { node: seenNodes.get(node.path) }) 4971cb0ef41Sopenharmony_ci // _depth keeps track of how many levels deep tree traversal currently is 4981cb0ef41Sopenharmony_ci // so that we can `npm ls --depth=1` 4991cb0ef41Sopenharmony_ci node[_depth] = currentDepth + 1 5001cb0ef41Sopenharmony_ci 5011cb0ef41Sopenharmony_ci return node 5021cb0ef41Sopenharmony_ci} 5031cb0ef41Sopenharmony_ci 5041cb0ef41Sopenharmony_ciconst sortAlphabetically = ({ pkgid: a }, { pkgid: b }) => localeCompare(a, b) 5051cb0ef41Sopenharmony_ci 5061cb0ef41Sopenharmony_ciconst humanOutput = ({ chalk, result, seenItems, unicode }) => { 5071cb0ef41Sopenharmony_ci // we need to traverse the entire tree in order to determine which items 5081cb0ef41Sopenharmony_ci // should be included (since a nested transitive included dep will make it 5091cb0ef41Sopenharmony_ci // so that all its ancestors should be displayed) 5101cb0ef41Sopenharmony_ci // here is where we put items in their expected place for archy output 5111cb0ef41Sopenharmony_ci for (const item of seenItems) { 5121cb0ef41Sopenharmony_ci if (item[_include] && item[_parent]) { 5131cb0ef41Sopenharmony_ci item[_parent].nodes.push(item) 5141cb0ef41Sopenharmony_ci } 5151cb0ef41Sopenharmony_ci } 5161cb0ef41Sopenharmony_ci 5171cb0ef41Sopenharmony_ci if (!result.nodes.length) { 5181cb0ef41Sopenharmony_ci result.nodes = ['(empty)'] 5191cb0ef41Sopenharmony_ci } 5201cb0ef41Sopenharmony_ci 5211cb0ef41Sopenharmony_ci const archyOutput = archy(result, '', { unicode }) 5221cb0ef41Sopenharmony_ci return chalk.reset(archyOutput) 5231cb0ef41Sopenharmony_ci} 5241cb0ef41Sopenharmony_ci 5251cb0ef41Sopenharmony_ciconst jsonOutput = ({ path, problems, result, rootError, seenItems }) => { 5261cb0ef41Sopenharmony_ci if (problems.size) { 5271cb0ef41Sopenharmony_ci result.problems = [...problems] 5281cb0ef41Sopenharmony_ci } 5291cb0ef41Sopenharmony_ci 5301cb0ef41Sopenharmony_ci if (rootError) { 5311cb0ef41Sopenharmony_ci result.problems = [ 5321cb0ef41Sopenharmony_ci ...(result.problems || []), 5331cb0ef41Sopenharmony_ci ...[`error in ${path}: Failed to parse root package.json`], 5341cb0ef41Sopenharmony_ci ] 5351cb0ef41Sopenharmony_ci result.invalid = true 5361cb0ef41Sopenharmony_ci } 5371cb0ef41Sopenharmony_ci 5381cb0ef41Sopenharmony_ci // we need to traverse the entire tree in order to determine which items 5391cb0ef41Sopenharmony_ci // should be included (since a nested transitive included dep will make it 5401cb0ef41Sopenharmony_ci // so that all its ancestors should be displayed) 5411cb0ef41Sopenharmony_ci // here is where we put items in their expected place for json output 5421cb0ef41Sopenharmony_ci for (const item of seenItems) { 5431cb0ef41Sopenharmony_ci // append current item to its parent item.dependencies obj in order 5441cb0ef41Sopenharmony_ci // to provide a json object structure that represents the installed tree 5451cb0ef41Sopenharmony_ci if (item[_include] && item[_parent]) { 5461cb0ef41Sopenharmony_ci if (!item[_parent].dependencies) { 5471cb0ef41Sopenharmony_ci item[_parent].dependencies = {} 5481cb0ef41Sopenharmony_ci } 5491cb0ef41Sopenharmony_ci 5501cb0ef41Sopenharmony_ci item[_parent].dependencies[item[_name]] = item 5511cb0ef41Sopenharmony_ci } 5521cb0ef41Sopenharmony_ci } 5531cb0ef41Sopenharmony_ci 5541cb0ef41Sopenharmony_ci return JSON.stringify(result, null, 2) 5551cb0ef41Sopenharmony_ci} 5561cb0ef41Sopenharmony_ci 5571cb0ef41Sopenharmony_ciconst parseableOutput = ({ global, long, seenNodes }) => { 5581cb0ef41Sopenharmony_ci let out = '' 5591cb0ef41Sopenharmony_ci for (const node of seenNodes.values()) { 5601cb0ef41Sopenharmony_ci if (node.path && node[_include]) { 5611cb0ef41Sopenharmony_ci out += node.path 5621cb0ef41Sopenharmony_ci if (long) { 5631cb0ef41Sopenharmony_ci out += `:${node.pkgid}` 5641cb0ef41Sopenharmony_ci out += node.path !== node.realpath ? `:${node.realpath}` : '' 5651cb0ef41Sopenharmony_ci out += isExtraneous(node, { global }) ? ':EXTRANEOUS' : '' 5661cb0ef41Sopenharmony_ci out += node[_invalid] ? ':INVALID' : '' 5671cb0ef41Sopenharmony_ci out += node.overridden ? ':OVERRIDDEN' : '' 5681cb0ef41Sopenharmony_ci } 5691cb0ef41Sopenharmony_ci out += EOL 5701cb0ef41Sopenharmony_ci } 5711cb0ef41Sopenharmony_ci } 5721cb0ef41Sopenharmony_ci return out.trim() 5731cb0ef41Sopenharmony_ci} 574