Skip to content

Commit 7ca6d84

Browse files
committed
fix: use proc-log META for flush and force
1 parent 2538438 commit 7ca6d84

File tree

6 files changed

+64
-103
lines changed

6 files changed

+64
-103
lines changed

lib/cli/exit-handler.js

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { log, output, time } = require('proc-log')
1+
const { log, output, time, META } = require('proc-log')
22
const errorMessage = require('../utils/error-message.js')
33
const { redactLog: replaceInfo } = require('@npmcli/redact')
44

@@ -32,9 +32,10 @@ process.on('exit', code => {
3232
log.error('', 'This is an error with npm itself. Please report this error at:')
3333
log.error('', ' <https://github.com/npm/cli/issues>')
3434

35-
// This gets logged regardless of silent mode
36-
// eslint-disable-next-line no-console
37-
console.error('')
35+
if (!npm || !npm.silent) {
36+
// eslint-disable-next-line no-console
37+
console.error('')
38+
}
3839

3940
showLogFileError = true
4041
}
@@ -120,8 +121,7 @@ const exitHandler = err => {
120121

121122
// only show the notification if it finished.
122123
if (typeof npm.updateNotification === 'string') {
123-
// TODO: use proc-log to send `force: true` along with event
124-
npm.forceLog('notice', '', npm.updateNotification)
124+
log.notice('', npm.updateNotification, { [META]: true, force: true })
125125
}
126126

127127
let exitCode = process.exitCode || 0
@@ -200,8 +200,7 @@ const exitHandler = err => {
200200
}
201201

202202
if (hasLoadedNpm) {
203-
// TODO: use proc-log.output.flush() once it exists
204-
npm.flushOutput(jsonError)
203+
output.flush({ [META]: true, jsonError })
205204
}
206205

207206
log.verbose('exit', exitCode || 0)

lib/npm.js

-10
Original file line numberDiff line numberDiff line change
@@ -379,16 +379,6 @@ class Npm {
379379
get usage () {
380380
return usage(this)
381381
}
382-
383-
// TODO: move to proc-log and remove
384-
forceLog (...args) {
385-
this.#display.forceLog(...args)
386-
}
387-
388-
// TODO: move to proc-log and remove
389-
flushOutput (jsonError) {
390-
this.#display.flushOutput(jsonError)
391-
}
392382
}
393383

394384
module.exports = Npm

lib/utils/display.js

+48-74
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const proggy = require('proggy')
2-
const { log, output } = require('proc-log')
2+
const { log, output, META } = require('proc-log')
33
const { explain } = require('./explain-eresolve.js')
44
const { formatWithOptions } = require('./format')
55

@@ -32,17 +32,6 @@ const COLOR_PALETTE = ({ chalk: c }) => ({
3232
silly: c.blue.dim,
3333
})
3434

35-
const LOG_LEVELS = log.LEVELS.reduce((acc, key) => {
36-
acc[key] = key
37-
return acc
38-
}, {})
39-
40-
// TODO: move flush to proc-log
41-
const OUTPUT_LEVELS = ['flush', ...output.LEVELS].reduce((acc, key) => {
42-
acc[key] = key
43-
return acc
44-
}, {})
45-
4635
const LEVEL_OPTIONS = {
4736
silent: {
4837
index: 0,
@@ -76,7 +65,7 @@ const LEVEL_OPTIONS = {
7665

7766
const LEVEL_METHODS = {
7867
...LEVEL_OPTIONS,
79-
[LOG_LEVELS.timing]: {
68+
[log.KEYS.timing]: {
8069
show: ({ timing, index }) => !!timing && index !== 0,
8170
},
8271
}
@@ -102,11 +91,13 @@ const setBlocking = (stream) => {
10291
return stream
10392
}
10493

105-
const getLevel = (stringOrLevelObject) => {
106-
if (typeof stringOrLevelObject === 'string') {
107-
return { level: stringOrLevelObject }
94+
const withMeta = (handler) => (level, ...args) => {
95+
let meta = {}
96+
const last = args.at(-1)
97+
if (last && typeof last === 'object' && Object.hasOwn(last, META)) {
98+
meta = args.pop()
10899
}
109-
return stringOrLevelObject
100+
return handler(level, meta, ...args)
110101
}
111102

112103
class Display {
@@ -136,6 +127,7 @@ class Display {
136127
#timing
137128
#json
138129
#heading
130+
#silent
139131

140132
// display streams
141133
#stdout
@@ -205,19 +197,11 @@ class Display {
205197
this.#timing = timing
206198
this.#json = json
207199
this.#heading = heading
208-
209-
// In silent mode we remove all the handlers
210-
if (this.#levelIndex <= 0) {
211-
this.off()
212-
return
213-
}
200+
this.#silent = this.#levelIndex <= 0
214201

215202
// Emit resume event on the logs which will flush output
216203
log.resume()
217-
218-
// TODO: this should be a proc-log method `proc-log.output.flush`?
219-
this.#outputHandler(OUTPUT_LEVELS.flush)
220-
204+
output.flush()
221205
this.#startProgress({ progress, unicode })
222206
}
223207

@@ -236,107 +220,98 @@ class Display {
236220

237221
// Arrow function assigned to a private class field so it can be passed
238222
// directly as a listener and still reference "this"
239-
#logHandler = (...args) => {
240-
if (args[0] === LOG_LEVELS.resume) {
223+
#logHandler = withMeta((level, meta, ...args) => {
224+
if (level === log.KEYS.resume) {
241225
this.#logState.buffering = false
242226
this.#logState.buffer.forEach((item) => this.#tryWriteLog(...item))
243227
this.#logState.buffer.length = 0
244228
return
245229
}
246230

247-
if (args[0] === LOG_LEVELS.pause) {
231+
if (level === log.KEYS.pause) {
248232
this.#logState.buffering = true
249233
return
250234
}
251235

252236
if (this.#logState.buffering) {
253-
this.#logState.buffer.push(args)
237+
this.#logState.buffer.push([level, meta, ...args])
254238
return
255239
}
256240

257-
this.#tryWriteLog(...args)
258-
}
241+
this.#tryWriteLog(level, meta, ...args)
242+
})
259243

260244
// Arrow function assigned to a private class field so it can be passed
261245
// directly as a listener and still reference "this"
262-
#outputHandler = (...args) => {
263-
if (args[0] === OUTPUT_LEVELS.flush) {
246+
#outputHandler = withMeta((level, meta, ...args) => {
247+
if (level === output.KEYS.flush) {
264248
this.#outputState.buffering = false
265-
if (args[1] && this.#json) {
249+
250+
if (meta.jsonError && this.#json) {
266251
const json = {}
267-
for (const [, item] of this.#outputState.buffer) {
268-
Object.assign(json, tryJsonParse(item))
252+
for (const item of this.#outputState.buffer) {
253+
// index 2 skips the level and meta
254+
Object.assign(json, tryJsonParse(item[2]))
269255
}
270-
this.#writeOutput('standard', JSON.stringify({ ...json, ...args[1] }, null, 2))
256+
this.#writeOutput(
257+
output.KEYS.standard,
258+
meta,
259+
JSON.stringify({ ...json, error: meta.jsonError }, null, 2)
260+
)
271261
} else {
272262
this.#outputState.buffer.forEach((item) => this.#writeOutput(...item))
273263
}
264+
274265
this.#outputState.buffer.length = 0
275266
return
276267
}
277268

278-
if (args[0] === OUTPUT_LEVELS.buffer) {
279-
this.#outputState.buffer.push(['standard', ...args.slice(1)])
269+
if (level === output.KEYS.buffer) {
270+
this.#outputState.buffer.push([output.KEYS.standard, meta, ...args])
280271
return
281272
}
282273

283274
if (this.#outputState.buffering) {
284-
this.#outputState.buffer.push(args)
275+
this.#outputState.buffer.push([level, meta, ...args])
285276
return
286277
}
287278

288-
this.#writeOutput(...args)
289-
}
279+
this.#writeOutput(level, meta, ...args)
280+
})
290281

291282
// OUTPUT
292283

293-
#writeOutput (...args) {
294-
const { level } = getLevel(args.shift())
295-
296-
if (level === OUTPUT_LEVELS.standard) {
284+
#writeOutput (level, meta, ...args) {
285+
if (level === output.KEYS.standard) {
297286
this.#stdoutWrite({}, ...args)
298287
return
299288
}
300289

301-
if (level === OUTPUT_LEVELS.error) {
290+
if (level === output.KEYS.error) {
302291
this.#stderrWrite({}, ...args)
303292
}
304293
}
305294

306-
// TODO: move this to proc-log and remove this public method
307-
flushOutput (jsonError) {
308-
this.#outputHandler(OUTPUT_LEVELS.flush, jsonError)
309-
}
310-
311295
// LOGS
312296

313-
// TODO: make proc-log able to send signal data like `force`
314-
// when that happens, remove this public method
315-
forceLog (level, ...args) {
316-
// This will show the log regardless of the current loglevel except when silent
317-
if (this.#levelIndex !== 0) {
318-
this.#logHandler({ level, force: true }, ...args)
319-
}
320-
}
321-
322-
#tryWriteLog (...args) {
297+
#tryWriteLog (level, meta, ...args) {
323298
try {
324299
// Also (and this is a really inexcusable kludge), we patch the
325300
// log.warn() method so that when we see a peerDep override
326301
// explanation from Arborist, we can replace the object with a
327302
// highly abbreviated explanation of what's being overridden.
328303
// TODO: this could probably be moved to arborist now that display is refactored
329-
const [level, heading, message, expl] = args
330-
if (level === LOG_LEVELS.warn && heading === 'ERESOLVE' && expl && typeof expl === 'object') {
331-
this.#writeLog(level, heading, message)
332-
this.#writeLog(level, '', explain(expl, this.#stderrChalk, 2))
304+
const [heading, message, expl] = args
305+
if (level === log.KEYS.warn && heading === 'ERESOLVE' && expl && typeof expl === 'object') {
306+
this.#writeLog(level, meta, heading, message)
307+
this.#writeLog(level, meta, '', explain(expl, this.#stderrChalk, 2))
333308
return
334309
}
335-
this.#writeLog(...args)
310+
this.#writeLog(level, meta, ...args)
336311
} catch (ex) {
337312
try {
338313
// if it crashed once, it might again!
339-
this.#writeLog(LOG_LEVELS.verbose, null, `attempt to log crashed`, ...args, ex)
314+
this.#writeLog(log.KEYS.verbose, meta, '', `attempt to log crashed`, ...args, ex)
340315
} catch (ex2) {
341316
// This happens if the object has an inspect method that crashes so just console.error
342317
// with the errors but don't do anything else that might error again.
@@ -346,11 +321,10 @@ class Display {
346321
}
347322
}
348323

349-
#writeLog (...args) {
350-
const { level, force = false } = getLevel(args.shift())
351-
324+
#writeLog (level, meta, ...args) {
352325
const levelOpts = LEVEL_METHODS[level]
353326
const show = levelOpts.show ?? (({ index }) => levelOpts.index <= index)
327+
const force = meta.force && !this.#silent
354328

355329
if (force || show({ index: this.#levelIndex, timing: this.#timing })) {
356330
// this mutates the array so we can pass args directly to format later
@@ -369,7 +343,7 @@ class Display {
369343
// PROGRESS
370344

371345
#startProgress ({ progress, unicode }) {
372-
if (!progress) {
346+
if (!progress || this.#silent) {
373347
return
374348
}
375349
this.#progress = proggy.createClient({ normalize: true })

lib/utils/error-message.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ const messageText = msg => msg.map(line => line.slice(1).join(' ')).join('\n')
88
const jsonError = (er, npm, { summary, detail }) => {
99
if (npm?.config.loaded && npm.config.get('json')) {
1010
return {
11-
error: {
12-
code: er.code,
13-
summary: messageText(summary),
14-
detail: messageText(detail),
15-
},
11+
code: er.code,
12+
summary: messageText(summary),
13+
detail: messageText(detail),
1614
}
1715
}
1816
}

test/fixtures/mock-npm.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const os = require('os')
22
const fs = require('fs').promises
33
const path = require('path')
44
const tap = require('tap')
5+
const { output, META } = require('proc-log')
56
const errorMessage = require('../../lib/utils/error-message')
67
const mockLogs = require('./mock-logs.js')
78
const mockGlobals = require('@npmcli/mock-globals')
@@ -79,7 +80,8 @@ const getMockNpm = async (t, { mocks, init, load, npm: npmOpts }) => {
7980
// error message fn. This is necessary for commands with buffered output
8081
// to read the output after exec is called. This is not *exactly* how it
8182
// works in practice, but it is close enough for now.
82-
this.flushOutput(err ? errorMessage(err, this).json : null)
83+
const jsonError = err && errorMessage(err, this).json
84+
output.flush({ [META]: true, jsonError })
8385
if (err) {
8486
throw err
8587
}

test/lib/cli/exit-handler.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,9 @@ const mockExitHandler = async (t, { config, mocks, files, ...opts } = {}) => {
7878
detail: [['ERR DETAIL', err.message]],
7979
...(files ? { files } : {}),
8080
json: {
81-
error: {
82-
code: err.code,
83-
summary: err.message,
84-
detail: err.message,
85-
},
81+
code: err.code,
82+
summary: err.message,
83+
detail: err.message,
8684
},
8785
}),
8886
...mocks,

0 commit comments

Comments
 (0)