Skip to content

Commit 01d9acd

Browse files
committed
fix(pkg): output like npm view does, do not force json output
BREAKING CHANGE: The `npm pkg` output is no longer forced to json. This means you can get single values without having to worry about wrapping of the values. It also outputs non-json content more similarly to `npm view`. Fixes npm/statusboard#1080
1 parent cc468a8 commit 01d9acd

5 files changed

Lines changed: 509 additions & 411 deletions

File tree

lib/commands/pkg.js

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const { inspect } = require('node:util')
12
const { output } = require('proc-log')
23
const PackageJson = require('@npmcli/package-json')
34
const BaseCommand = require('../base-cmd.js')
@@ -56,25 +57,40 @@ class Pkg extends BaseCommand {
5657
}
5758

5859
async get (args, { path, workspace }) {
59-
this.npm.config.set('json', true)
6060
const pkgJson = await PackageJson.load(path)
61+
const json = this.npm.config.get('json')
6162

62-
let result = pkgJson.content
63+
// filter out the newline/indent symbols from the package-json object
64+
let result = JSON.parse(JSON.stringify(pkgJson.content))
6365

6466
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]
67+
result = new Queryable(result).query(args, { unwrapSingleItemArrays: false })
68+
if (args.length === 1 && !json && args[0] in result) {
69+
if (workspace) {
70+
return output.standard(`${workspace} ${result[args[0]]}`)
71+
}
72+
return output.standard(result[args[0]])
7173
}
7274
}
7375

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)
76+
if (json) {
77+
output.buffer(workspace ? { [workspace]: result } : result)
78+
} else {
79+
for (let [f, d] of Object.entries(result)) {
80+
d = inspect(d, {
81+
showHidden: false,
82+
depth: 5,
83+
colors: this.npm.color,
84+
maxArrayLength: null,
85+
})
86+
87+
if (workspace) {
88+
output.standard(`${workspace} ${f} = ${d}`)
89+
} else {
90+
output.standard(`${f} = ${d}`)
91+
}
92+
}
93+
}
7894
}
7995

8096
async set (args, { path }) {

lib/commands/view.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class View extends BaseCommand {
204204
const includeVersions = versions.length > 1
205205

206206
let includeFields
207+
// TODO if we ask for two fields but only one existed we treat it as if we only asked for one field, this needs to be fixed
207208
const res = versions.flatMap((v) => {
208209
const fields = Object.entries(data[v])
209210

smoke-tests/tap-snapshots/test/index.js.test.cjs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -344,36 +344,28 @@ exports[`test/index.js TAP basic npm pkg > should have expected pkg delete outpu
344344
`
345345

346346
exports[`test/index.js TAP basic npm pkg > should have expected pkg get output 1`] = `
347-
"ISC"
347+
ISC
348348
`
349349

350350
exports[`test/index.js TAP basic npm pkg > should have expected pkg set output 1`] = `
351351
352352
`
353353

354354
exports[`test/index.js TAP basic npm pkg > should print package.json contents 1`] = `
355-
{
356-
"name": "project",
357-
"version": "1.0.0",
358-
"description": "",
359-
"main": "index.js",
360-
"scripts": {
361-
"test": "echo /"Error: no test specified/" && exit 1",
362-
"hello": "echo Hello"
363-
},
364-
"keywords": [],
365-
"author": "",
366-
"license": "ISC",
367-
"type": "commonjs",
368-
"dependencies": {
369-
"abbrev": "^1.0.4"
370-
},
371-
"tap": {
372-
"test-env": [
373-
"LC_ALL=sk"
374-
]
375-
}
355+
name = 'project'
356+
version = '1.0.0'
357+
description = ''
358+
main = 'index.js'
359+
scripts = {
360+
test: 'echo "Error: no test specified" && exit 1',
361+
hello: 'echo Hello'
376362
}
363+
keywords = []
364+
author = ''
365+
license = 'ISC'
366+
type = 'commonjs'
367+
dependencies = { abbrev: '^1.0.4' }
368+
tap = { 'test-env': [ 'LC_ALL=sk' ] }
377369
`
378370

379371
exports[`test/index.js TAP basic npm pkg set scripts > should have expected script added package.json result 1`] = `

tap-snapshots/test/lib/commands/pkg.js.test.cjs

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
* Make sure to inspect the output below. Do not ignore changes!
66
*/
77
'use strict'
8-
exports[`test/lib/commands/pkg.js TAP delete multiple field > should delete multiple fields from package.json 1`] = `
8+
exports[`test/lib/commands/pkg.js TAP delete delete multiple field > should delete multiple fields from package.json 1`] = `
99
Object {
1010
"name": "foo",
1111
}
1212
`
1313

14-
exports[`test/lib/commands/pkg.js TAP delete nested field > should delete nested fields from package.json 1`] = `
14+
exports[`test/lib/commands/pkg.js TAP delete delete nested field > should delete nested fields from package.json 1`] = `
1515
Object {
1616
"info": Object {
1717
"foo": Object {
@@ -25,7 +25,7 @@ Object {
2525
}
2626
`
2727

28-
exports[`test/lib/commands/pkg.js TAP delete single field > should delete single field from package.json 1`] = `
28+
exports[`test/lib/commands/pkg.js TAP delete delete single field > should delete single field from package.json 1`] = `
2929
Object {
3030
"name": "foo",
3131
}
@@ -39,59 +39,63 @@ Object {
3939
`
4040

4141
exports[`test/lib/commands/pkg.js TAP get array field > should print retrieved array field 1`] = `
42-
[
43-
"index.js",
44-
"cli.js"
45-
]
42+
[ 'index.js', 'cli.js' ]
4643
`
4744

4845
exports[`test/lib/commands/pkg.js TAP get array item > should print retrieved array field 1`] = `
49-
46+
index.js
5047
`
5148

52-
exports[`test/lib/commands/pkg.js TAP get array nested items notation > should print json result containing matching results 1`] = `
53-
{
54-
"contributors[0].name": "Ruy",
55-
"contributors[1].name": "Gar"
56-
}
49+
exports[`test/lib/commands/pkg.js TAP get get array nested items notation > should print json result containing matching results 1`] = `
50+
contributors[0].name = 'Ruy'
51+
contributors[1].name = 'Gar'
5752
`
5853

59-
exports[`test/lib/commands/pkg.js TAP get multiple arg > should print retrieved package.json fields 1`] = `
54+
exports[`test/lib/commands/pkg.js TAP get json no args > should print package.json content 1`] = `
6055
{
6156
"name": "foo",
6257
"version": "1.1.1"
6358
}
6459
`
6560

66-
exports[`test/lib/commands/pkg.js TAP get multiple arg with empty value > should print retrieved package.json field regardless of empty value 1`] = `
61+
exports[`test/lib/commands/pkg.js TAP get json with args > should print package.json content 1`] = `
6762
{
68-
"name": "foo",
69-
"author": ""
63+
"name": "foo"
7064
}
7165
`
7266

67+
exports[`test/lib/commands/pkg.js TAP get multiple arg > should print retrieved package.json fields 1`] = `
68+
name = 'foo'
69+
version = '1.1.1'
70+
`
71+
72+
exports[`test/lib/commands/pkg.js TAP get multiple arg with empty value > should print retrieved package.json field regardless of empty value 1`] = `
73+
name = 'foo'
74+
author = ''
75+
`
76+
7377
exports[`test/lib/commands/pkg.js TAP get multiple arg with only one arg existing > should print retrieved package.json field 1`] = `
74-
{
75-
"name": "foo"
76-
}
78+
name = 'foo'
7779
`
7880

7981
exports[`test/lib/commands/pkg.js TAP get nested arg > node test.js 1`] = `
80-
"node test.js"
82+
node test.js
8183
`
8284

8385
exports[`test/lib/commands/pkg.js TAP get no args > should print package.json content 1`] = `
84-
{
85-
"name": "foo",
86-
"version": "1.1.1"
87-
}
86+
name = 'foo'
87+
version = '1.1.1'
88+
`
89+
90+
exports[`test/lib/commands/pkg.js TAP get non string > should print retrieved package.json field 1`] = `
91+
{ '@npmcli/test': '*' }
8892
`
8993

9094
exports[`test/lib/commands/pkg.js TAP get single arg > should print retrieved package.json field 1`] = `
91-
"1.1.1"
95+
1.1.1
9296
`
9397

94-
exports[`test/lib/commands/pkg.js TAP push to array syntax > should append to arrays using empty bracket syntax 1`] = `
98+
exports[`test/lib/commands/pkg.js TAP set push to array syntax > should append to arrays using empty bracket syntax 1`] = `
9599
Object {
96100
"keywords": Array [
97101
"foo",
@@ -103,7 +107,7 @@ Object {
103107
}
104108
`
105109

106-
exports[`test/lib/commands/pkg.js TAP set --json > should add fields to package.json 1`] = `
110+
exports[`test/lib/commands/pkg.js TAP set set --json > should add fields to package.json 1`] = `
107111
Object {
108112
"description": "awesome",
109113
"foo": Object {
@@ -123,7 +127,7 @@ Object {
123127
}
124128
`
125129

126-
exports[`test/lib/commands/pkg.js TAP set = separate value > should add single field to package.json 1`] = `
130+
exports[`test/lib/commands/pkg.js TAP set set = separate value > should add single field to package.json 1`] = `
127131
Object {
128132
"name": "foo",
129133
"tap": Object {
@@ -135,7 +139,7 @@ Object {
135139
}
136140
`
137141

138-
exports[`test/lib/commands/pkg.js TAP set multiple fields > should add single field to package.json 1`] = `
142+
exports[`test/lib/commands/pkg.js TAP set set multiple fields > should add single field to package.json 1`] = `
139143
Object {
140144
"bin": Object {
141145
"foo": "foo.js",
@@ -148,7 +152,7 @@ Object {
148152
}
149153
`
150154

151-
exports[`test/lib/commands/pkg.js TAP set single field > should add single field to package.json 1`] = `
155+
exports[`test/lib/commands/pkg.js TAP set set single field > should add single field to package.json 1`] = `
152156
Object {
153157
"description": "Awesome stuff",
154158
"name": "foo",
@@ -157,21 +161,22 @@ Object {
157161
`
158162

159163
exports[`test/lib/commands/pkg.js TAP single workspace multiple args > should only return info for one workspace 1`] = `
160-
{
161-
"a": {
162-
"name": "a",
163-
"version": "1.0.0"
164-
}
165-
}
164+
a name = 'a'
165+
a version = '1.0.0'
166166
`
167167

168168
exports[`test/lib/commands/pkg.js TAP single workspace single arg > should only return info for one workspace 1`] = `
169-
{
170-
"a": "1.0.0"
171-
}
169+
a 1.0.0
172170
`
173171

174172
exports[`test/lib/commands/pkg.js TAP workspaces get > should return expected result for configured workspaces 1`] = `
173+
a name = 'a'
174+
a version = '1.0.0'
175+
b name = 'b'
176+
b version = '1.2.3'
177+
`
178+
179+
exports[`test/lib/commands/pkg.js TAP workspaces get json > should return expected json result for configured workspaces 1`] = `
175180
{
176181
"a": {
177182
"name": "a",

0 commit comments

Comments
 (0)