• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const PackageJson = require('@npmcli/package-json')
2const BaseCommand = require('../base-command.js')
3const Queryable = require('../utils/queryable.js')
4
5class Pkg extends BaseCommand {
6  static description = 'Manages your package.json'
7  static name = 'pkg'
8  static usage = [
9    'set <key>=<value> [<key>=<value> ...]',
10    'get [<key> [<key> ...]]',
11    'delete <key> [<key> ...]',
12    'set [<array>[<index>].<key>=<value> ...]',
13    'set [<array>[].<key>=<value> ...]',
14    'fix',
15  ]
16
17  static params = [
18    'force',
19    'json',
20    'workspace',
21    'workspaces',
22  ]
23
24  static workspaces = true
25  static ignoreImplicitWorkspace = false
26
27  async exec (args, { prefix } = {}) {
28    if (!prefix) {
29      this.prefix = this.npm.localPrefix
30    } else {
31      this.prefix = prefix
32    }
33
34    if (this.npm.global) {
35      throw Object.assign(
36        new Error(`There's no package.json file to manage on global mode`),
37        { code: 'EPKGGLOBAL' }
38      )
39    }
40
41    const [cmd, ..._args] = args
42    switch (cmd) {
43      case 'get':
44        return this.get(_args)
45      case 'set':
46        return this.set(_args)
47      case 'delete':
48        return this.delete(_args)
49      case 'fix':
50        return this.fix(_args)
51      default:
52        throw this.usageError()
53    }
54  }
55
56  async execWorkspaces (args) {
57    await this.setWorkspaces()
58    const result = {}
59    for (const [workspaceName, workspacePath] of this.workspaces.entries()) {
60      this.prefix = workspacePath
61      result[workspaceName] = await this.exec(args, { prefix: workspacePath })
62    }
63    // when running in workspaces names, make sure to key by workspace
64    // name the results of each value retrieved in each ws
65    this.npm.output(JSON.stringify(result, null, 2))
66  }
67
68  async get (args) {
69    const pkgJson = await PackageJson.load(this.prefix)
70
71    const { content } = pkgJson
72    let result = !args.length && content
73
74    if (!result) {
75      const q = new Queryable(content)
76      result = q.query(args)
77
78      // in case there's only a single result from the query
79      // just prints that one element to stdout
80      if (Object.keys(result).length === 1) {
81        result = result[args]
82      }
83    }
84
85    // only outputs if not running with workspaces config
86    // execWorkspaces will handle the output otherwise
87    if (!this.workspaces) {
88      this.npm.output(JSON.stringify(result, null, 2))
89    }
90
91    return result
92  }
93
94  async set (args) {
95    const setError = () =>
96      this.usageError('npm pkg set expects a key=value pair of args.')
97
98    if (!args.length) {
99      throw setError()
100    }
101
102    const force = this.npm.config.get('force')
103    const json = this.npm.config.get('json')
104    const pkgJson = await PackageJson.load(this.prefix)
105    const q = new Queryable(pkgJson.content)
106    for (const arg of args) {
107      const [key, ...rest] = arg.split('=')
108      const value = rest.join('=')
109      if (!key || !value) {
110        throw setError()
111      }
112
113      q.set(key, json ? JSON.parse(value) : value, { force })
114    }
115
116    pkgJson.update(q.toJSON())
117    await pkgJson.save()
118  }
119
120  async delete (args) {
121    const setError = () =>
122      this.usageError('npm pkg delete expects key args.')
123
124    if (!args.length) {
125      throw setError()
126    }
127
128    const pkgJson = await PackageJson.load(this.prefix)
129    const q = new Queryable(pkgJson.content)
130    for (const key of args) {
131      if (!key) {
132        throw setError()
133      }
134
135      q.delete(key)
136    }
137
138    pkgJson.update(q.toJSON())
139    await pkgJson.save()
140  }
141
142  async fix () {
143    const pkgJson = await PackageJson.fix(this.prefix)
144    await pkgJson.save()
145  }
146}
147
148module.exports = Pkg
149