Skip to content

Commit b3de0de

Browse files
committed
fix(view): output full json object with errors with workspaces
Fixes: #5444
1 parent 8add914 commit b3de0de

File tree

10 files changed

+326
-284
lines changed

10 files changed

+326
-284
lines changed

lib/commands/run-script.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,13 @@ class RunScript extends BaseCommand {
7474
return
7575
}
7676

77-
const didYouMean = require('../utils/did-you-mean.js')
78-
const suggestions = await didYouMean(path, event)
79-
throw new Error(
80-
`Missing script: "${event}"${suggestions}\n\nTo see a list of scripts, run:\n npm run`
81-
)
77+
const suggestions = require('../utils/did-you-mean.js')(pkg, event)
78+
throw new Error([
79+
`Missing script: "${event}"`,
80+
...suggestions ? ['', ...suggestions] : [],
81+
'To see a list of scripts, run:',
82+
' npm run',
83+
].join('\n'))
8284
}
8385

8486
// positional args only added to the main event, not pre/post

lib/commands/view.js

Lines changed: 89 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const { inspect } = require('util')
1111
const { packument } = require('pacote')
1212
const Queryable = require('../utils/queryable.js')
1313
const BaseCommand = require('../base-cmd.js')
14+
const { getError, jsonError, outputError } = require('../utils/error-message.js')
1415

1516
const readJson = async file => jsonParse(await readFile(file, 'utf8'))
1617

@@ -77,11 +78,7 @@ class View extends BaseCommand {
7778
}
7879

7980
async exec (args) {
80-
if (!args.length) {
81-
args = ['.']
82-
}
83-
let pkg = args.shift()
84-
const local = /^\.@/.test(pkg) || pkg === '.'
81+
let { pkg, local, rest } = parseArgs(args)
8582

8683
if (local) {
8784
if (this.npm.global) {
@@ -96,91 +93,87 @@ class View extends BaseCommand {
9693
pkg = `${manifest.name}${pkg.slice(1)}`
9794
}
9895

99-
let wholePackument = false
100-
if (!args.length) {
101-
args = ['']
102-
wholePackument = true
103-
}
104-
const [pckmnt, data] = await this.getData(pkg, args)
105-
106-
if (!this.npm.config.get('json') && wholePackument) {
107-
// pretty view (entire packument)
108-
data.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]]['']))
109-
} else {
110-
// JSON formatted output (JSON or specific attributes from packument)
111-
let reducedData = data.reduce(reducer, {})
112-
if (wholePackument) {
113-
// No attributes
114-
reducedData = cleanBlanks(reducedData)
115-
log.silly('view', reducedData)
116-
}
117-
118-
const msg = await this.jsonData(reducedData, pckmnt._id)
119-
if (msg !== '') {
120-
output.standard(msg)
121-
}
122-
}
96+
await this.#viewPackage(pkg, rest)
12397
}
12498

12599
async execWorkspaces (args) {
126-
if (!args.length) {
127-
args = ['.']
128-
}
129-
130-
const pkg = args.shift()
100+
const { pkg, local, rest } = parseArgs(args)
131101

132-
const local = /^\.@/.test(pkg) || pkg === '.'
133102
if (!local) {
134103
log.warn('Ignoring workspaces for specified package(s)')
135-
return this.exec([pkg, ...args])
104+
return this.exec([pkg, ...rest])
136105
}
137-
let wholePackument = false
138-
if (!args.length) {
139-
wholePackument = true
140-
args = [''] // getData relies on this
141-
}
142-
const results = {}
106+
107+
const json = this.npm.config.get('json')
143108
await this.setWorkspaces()
144-
for (const name of this.workspaceNames) {
145-
const wsPkg = `${name}${pkg.slice(1)}`
146-
const [pckmnt, data] = await this.getData(wsPkg, args)
147-
148-
let reducedData = data.reduce(reducer, {})
149-
if (wholePackument) {
150-
// No attributes
151-
reducedData = cleanBlanks(reducedData)
152-
log.silly('view', reducedData)
153-
}
154109

155-
if (!this.npm.config.get('json')) {
156-
if (wholePackument) {
157-
data.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]]['']))
110+
let hasError = false
111+
for (const name of this.workspaceNames) {
112+
try {
113+
await this.#viewPackage(
114+
`${name}${pkg.slice(1)}`,
115+
rest,
116+
{ multiple: !!this.workspaceNames.length }
117+
)
118+
} catch (e) {
119+
hasError = true
120+
const err = getError(e, { npm: this.npm, command: this })
121+
if (json) {
122+
output.buffer({ [name]: jsonError(err, this.npm) })
158123
} else {
159-
output.standard(`${name}:`)
160-
const msg = await this.jsonData(reducedData, pckmnt._id)
161-
if (msg !== '') {
162-
output.standard(msg)
163-
}
164-
}
165-
} else {
166-
const msg = await this.jsonData(reducedData, pckmnt._id)
167-
if (msg !== '') {
168-
results[name] = JSON.parse(msg)
124+
outputError(err)
169125
}
170126
}
171127
}
172-
if (Object.keys(results).length > 0) {
173-
output.standard(JSON.stringify(results, null, 2))
128+
129+
if (hasError) {
130+
process.exitCode = 1
174131
}
175132
}
176133

177-
async getData (pkg, args) {
178-
const opts = {
179-
...this.npm.flatOptions,
180-
preferOnline: true,
181-
fullMetadata: true,
134+
async #viewPackage (name, args, { multiple = false } = {}) {
135+
const wholePackument = !args.length
136+
const json = this.npm.config.get('json')
137+
138+
// If we are viewing many packages output the name before doing
139+
// any async activity
140+
if (!json && !wholePackument && multiple) {
141+
output.standard(`${name}:`)
182142
}
183143

144+
const [pckmnt, data, version] = await this.#getData(name, wholePackument ? [''] : args)
145+
if (wholePackument) {
146+
pckmnt.version = version
147+
}
148+
149+
// This already outputs to the terminal
150+
if (!json && wholePackument) {
151+
// pretty view (entire packument)
152+
for (const v of data) {
153+
this.#prettyView(pckmnt, v[Object.keys(v)[0]][''])
154+
}
155+
return
156+
}
157+
158+
// JSON formatted output (JSON or specific attributes from packument)
159+
let reducedData = data.reduce(reducer, {})
160+
if (wholePackument) {
161+
// No attributes
162+
reducedData = cleanBlanks(reducedData)
163+
log.silly('view', reducedData)
164+
}
165+
166+
const msg = this.#packageOutput(reducedData, pckmnt._id)
167+
if (msg !== '') {
168+
if (json && multiple) {
169+
output.buffer({ [name]: JSON.parse(msg) })
170+
} else {
171+
output.standard(msg)
172+
}
173+
}
174+
}
175+
176+
async #getData (pkg, args = []) {
184177
const spec = npa(pkg)
185178

186179
// get the data about this package
@@ -190,13 +183,17 @@ class View extends BaseCommand {
190183
version = spec.rawSpec
191184
}
192185

193-
const pckmnt = await packument(spec, opts)
186+
const pckmnt = await packument(spec, {
187+
...this.npm.flatOptions,
188+
preferOnline: true,
189+
fullMetadata: true,
190+
})
194191

195192
if (pckmnt['dist-tags']?.[version]) {
196193
version = pckmnt['dist-tags'][version]
197194
}
198195

199-
if (pckmnt.time && pckmnt.time.unpublished) {
196+
if (pckmnt.time?.unpublished) {
200197
const u = pckmnt.time.unpublished
201198
const er = new Error(`Unpublished on ${u.time}`)
202199
er.statusCode = 404
@@ -234,26 +231,18 @@ class View extends BaseCommand {
234231
})
235232

236233
// No data has been pushed because no data is matching the specified version
237-
if (data.length === 0 && version !== 'latest') {
234+
if (!data.length && version !== 'latest') {
238235
const er = new Error(`No match found for version ${version}`)
239236
er.statusCode = 404
240237
er.code = 'E404'
241238
er.pkgid = `${pckmnt._id}@${version}`
242239
throw er
243240
}
244241

245-
if (
246-
!this.npm.config.get('json') &&
247-
args.length === 1 &&
248-
args[0] === ''
249-
) {
250-
pckmnt.version = version
251-
}
252-
253-
return [pckmnt, data]
242+
return [pckmnt, data, version]
254243
}
255244

256-
async jsonData (data, name) {
245+
#packageOutput (data, name) {
257246
const versions = Object.keys(data)
258247
let msg = ''
259248
let msgJson = []
@@ -313,7 +302,7 @@ class View extends BaseCommand {
313302
return msg.trim()
314303
}
315304

316-
prettyView (packu, manifest) {
305+
#prettyView (packu, manifest) {
317306
// More modern, pretty printing of default view
318307
const unicode = this.npm.config.get('unicode')
319308
const chalk = this.npm.chalk
@@ -409,6 +398,20 @@ class View extends BaseCommand {
409398

410399
module.exports = View
411400

401+
function parseArgs (args) {
402+
if (!args.length) {
403+
args = ['.']
404+
}
405+
406+
const pkg = args.shift()
407+
408+
return {
409+
pkg,
410+
local: /^\.@/.test(pkg) || pkg === '.',
411+
rest: args,
412+
}
413+
}
414+
412415
function cleanBlanks (obj) {
413416
const clean = {}
414417
Object.keys(obj).forEach((version) => {

0 commit comments

Comments
 (0)