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