codekingpro/portable-devtools
114k
1const { output } = require('proc-log')
2const PackageJson = require('@npmcli/package-json')
3const BaseCommand = require('../base-cmd.js')
4const Queryable = require('../utils/queryable.js')
5
6class Pkg extends BaseCommand {
7 static description = 'Manages your package.json'
8 static name = 'pkg'
9 static usage = [
10 'set <key>=<value> [<key>=<value> ...]',
11 'get [<key> [<key> ...]]',
12 'delete <key> [<key> ...]',
13 'set [<array>[<index>].<key>=<value> ...]',
14 'set [<array>[].<key>=<value> ...]',
15 'fix',
16 ]
17
18 static params = [
19 'force',
20 'json',
21 'workspace',
22 'workspaces',
23 ]
24
25 static workspaces = true
26 static ignoreImplicitWorkspace = false
27
28 async exec (args, { path = this.npm.localPrefix, workspace } = {}) {
29 if (this.npm.global) {
30 throw Object.assign(
31 new Error(`There's no package.json file to manage on global mode`),
32 { code: 'EPKGGLOBAL' }
33 )
34 }
35
36 const [cmd, ..._args] = args
37 switch (cmd) {
38 case 'get':
39 return this.get(_args, { path, workspace })
40 case 'set':
41 return this.set(_args, { path, workspace }).then(p => p.save())
42 case 'delete':
43 return this.delete(_args, { path, workspace }).then(p => p.save())
44 case 'fix':
45 return PackageJson.fix(path).then(p => p.save())
46 default:
47 throw this.usageError()
48 }
49 }
50
51 async execWorkspaces (args) {
52 await this.setWorkspaces()
53 for (const [workspace, path] of this.workspaces.entries()) {
54 await this.exec(args, { path, workspace })
55 }
56 }
57
58 async get (args, { path, workspace }) {
59 this.npm.config.set('json', true)
60 const pkgJson = await PackageJson.load(path)
61
62 let result = pkgJson.content
63
64 if (args.length) {
65 result = new Queryable(result).query(args)
66 // in case there's only a single argument and a single result from the query just prints that one element to stdout.
67 // TODO(BREAKING_CHANGE): much like other places where we unwrap single item arrays this should go away.
68 // it makes the behavior unknown for users who don't already know the shape of the data.
69 if (Object.keys(result).length === 1 && args.length === 1) {
70 result = result[args]
71 }
72 }
73
74 // The display layer is responsible for calling JSON.stringify on the result
75 // TODO: https://github.com/npm/cli/issues/5508 a raw mode has been requested similar to jq -r.
76 // If that was added then this method should no longer set `json:true` all the time
77 output.buffer(workspace ? { [workspace]: result } : result)
78 }
79
80 async set (args, { path }) {
81 const setError = () =>
82 this.usageError('npm pkg set expects a key=value pair of args.')
83
84 if (!args.length) {
85 throw setError()
86 }
87
88 const force = this.npm.config.get('force')
89 const json = this.npm.config.get('json')
90 const pkgJson = await PackageJson.load(path)
91 const q = new Queryable(pkgJson.content)
92 for (const arg of args) {
93 const [key, ...rest] = arg.split('=')
94 const value = rest.join('=')
95 if (!key || !value) {
96 throw setError()
97 }
98
99 q.set(key, json ? JSON.parse(value) : value, { force })
100 }
101
102 return pkgJson.update(q.toJSON())
103 }
104
105 async delete (args, { path }) {
106 const setError = () =>
107 this.usageError('npm pkg delete expects key args.')
108
109 if (!args.length) {
110 throw setError()
111 }
112
113 const pkgJson = await PackageJson.load(path)
114 const q = new Queryable(pkgJson.content)
115 for (const key of args) {
116 if (!key) {
117 throw setError()
118 }
119
120 q.delete(key)
121 }
122
123 return pkgJson.update(q.toJSON())
124 }
125}
126
127module.exports = Pkg
128 