summaryrefslogtreecommitdiff
path: root/deps/npm/lib/commands/query.js
blob: ba39f004fae237a09e02c82ec7cdb05b4aa25093 (plain)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
'use strict'

const { resolve } = require('path')
const BaseCommand = require('../base-command.js')

class QuerySelectorItem {
  constructor (node) {
    // all enumerable properties from the target
    Object.assign(this, node.target.package)

    // append extra info
    this.pkgid = node.target.pkgid
    this.location = node.target.location
    this.path = node.target.path
    this.realpath = node.target.realpath
    this.resolved = node.target.resolved
    this.from = []
    this.to = []
    this.dev = node.target.dev
    this.inBundle = node.target.inBundle
    this.deduped = this.from.length > 1
    this.overridden = node.overridden
    this.queryContext = node.queryContext
    for (const edge of node.target.edgesIn) {
      this.from.push(edge.from.location)
    }
    for (const [, edge] of node.target.edgesOut) {
      if (edge.to) {
        this.to.push(edge.to.location)
      }
    }
  }
}

class Query extends BaseCommand {
  #response = [] // response is the query response
  #seen = new Set() // paths we've seen so we can keep response deduped

  static description = 'Retrieve a filtered list of packages'
  static name = 'query'
  static usage = ['<selector>']

  static workspaces = true
  static ignoreImplicitWorkspace = false

  static params = [
    'global',
    'workspace',
    'workspaces',
    'include-workspace-root',
  ]

  get parsedResponse () {
    return JSON.stringify(this.#response, null, 2)
  }

  async exec (args) {
    // one dir up from wherever node_modules lives
    const where = resolve(this.npm.dir, '..')
    const Arborist = require('@npmcli/arborist')
    const opts = {
      ...this.npm.flatOptions,
      path: where,
      forceActual: true,
    }
    const arb = new Arborist(opts)
    const tree = await arb.loadActual(opts)
    const items = await tree.querySelectorAll(args[0], this.npm.flatOptions)
    this.buildResponse(items)

    this.npm.output(this.parsedResponse)
  }

  async execWorkspaces (args) {
    await this.setWorkspaces()
    const Arborist = require('@npmcli/arborist')
    const opts = {
      ...this.npm.flatOptions,
      path: this.npm.prefix,
    }
    const arb = new Arborist(opts)
    const tree = await arb.loadActual(opts)
    for (const workspacePath of this.workspacePaths) {
      let items
      if (workspacePath === tree.root.path) {
        // include-workspace-root
        items = await tree.querySelectorAll(args[0])
      } else {
        const [workspace] = await tree.querySelectorAll(`.workspace:path(${workspacePath})`)
        items = await workspace.target.querySelectorAll(args[0], this.npm.flatOptions)
      }
      this.buildResponse(items)
    }
    this.npm.output(this.parsedResponse)
  }

  // builds a normalized inventory
  buildResponse (items) {
    for (const node of items) {
      if (!this.#seen.has(node.target.location)) {
        const item = new QuerySelectorItem(node)
        this.#response.push(item)
        this.#seen.add(item.location)
      }
    }
  }
}

module.exports = Query