Skip to content

Commit f2e0073

Browse files
committed
fixup! feat: do all ouput over proc-log events
1 parent 7a3540c commit f2e0073

6 files changed

Lines changed: 178 additions & 106 deletions

File tree

lib/npm.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ class Npm {
244244
this.time('npm:load:display', () => {
245245
this.#display.load({
246246
loglevel: this.config.get('loglevel'),
247+
// TODO: only pass in logColor and color and create chalk instances
248+
// in display load method. Then remove chalk getters from npm and
249+
// producers should emit chalk-templates (or something else).
247250
stdoutChalk: this.#chalk,
248251
stdoutColor: this.color,
249252
stderrChalk: this.#logChalk,
@@ -436,10 +439,12 @@ class Npm {
436439
return usage(this)
437440
}
438441

442+
// TODO: move to proc-log and remove
439443
forceLog (...args) {
440444
this.#display.forceLog(...args)
441445
}
442446

447+
// TODO: move to proc-log and remove
443448
flushOutput (jsonError) {
444449
this.#display.flushOutput(jsonError)
445450
}

lib/utils/display.js

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const proggy = require('proggy')
2-
const { log } = require('proc-log')
2+
const { log, output } = require('proc-log')
33
const { explain } = require('./explain-eresolve.js')
44
const { formatWithOptions } = require('./format')
55

@@ -17,7 +17,13 @@ const COLOR_PALETTE = ({ chalk: c }) => ({
1717
silly: c.inverse,
1818
})
1919

20-
const LEVELS = log.LEVELS.reduce((acc, key) => {
20+
const LOG_LEVELS = log.LEVELS.reduce((acc, key) => {
21+
acc[key] = key
22+
return acc
23+
}, {})
24+
25+
// TODO: move flush to proc-log
26+
const OUTPUT_LEVELS = ['flush', ...output.LEVELS].reduce((acc, key) => {
2127
acc[key] = key
2228
return acc
2329
}, {})
@@ -55,20 +61,20 @@ const LEVEL_OPTIONS = {
5561

5662
const LEVEL_METHODS = {
5763
...LEVEL_OPTIONS,
58-
[LEVELS.timing]: {
64+
[LOG_LEVELS.timing]: {
5965
show: ({ timing, index }) => !!timing && index !== 0,
6066
},
6167
}
6268

63-
const safeJsonParse = (maybeJsonStr) => {
64-
if (typeof maybeJsonStr !== 'string') {
65-
return maybeJsonStr
66-
}
67-
try {
68-
return JSON.parse(maybeJsonStr)
69-
} catch {
70-
return {}
69+
const tryJsonParse = (value) => {
70+
if (typeof value === 'string') {
71+
try {
72+
return JSON.parse(value)
73+
} catch {
74+
return {}
75+
}
7176
}
77+
return value
7278
}
7379

7480
const setBlocking = (stream) => {
@@ -122,6 +128,7 @@ class Display {
122128
constructor ({ stdout, stderr }) {
123129
this.#stdout = setBlocking(stdout)
124130
this.#stderr = setBlocking(stderr)
131+
125132
// Handlers are set immediately so they can buffer all events
126133
process.on('log', this.#logHandler)
127134
process.on('output', this.#outputHandler)
@@ -174,21 +181,34 @@ class Display {
174181
log.resume()
175182

176183
// TODO: this should be a proc-log method `proc-log.output.flush`?
177-
this.#outputHandler('flush')
184+
this.#outputHandler(OUTPUT_LEVELS.flush)
178185

179186
this.#startProgress({ progress, unicode })
180187
}
181188

189+
// STREAM WRITES
190+
191+
// Write formatted and (non-)colorized output to streams
192+
#stdoutWrite (options, ...args) {
193+
this.#stdout.write(formatWithOptions({ colors: this.#stdoutColor, ...options }, ...args))
194+
}
195+
196+
#stderrWrite (options, ...args) {
197+
this.#stderr.write(formatWithOptions({ colors: this.#stderrColor, ...options }, ...args))
198+
}
199+
200+
// HANDLERS
201+
182202
// Public class field so it can be passed directly as a listener
183203
#logHandler = (...args) => {
184-
if (args[0] === LEVELS.resume) {
204+
if (args[0] === LOG_LEVELS.resume) {
185205
this.#logState.buffering = false
186206
this.#logState.buffer.forEach((item) => this.#tryWriteLog(...item))
187207
this.#logState.buffer.length = 0
188208
return
189209
}
190210

191-
if (args[0] === LEVELS.pause) {
211+
if (args[0] === LOG_LEVELS.pause) {
192212
this.#logState.buffering = true
193213
return
194214
}
@@ -203,23 +223,22 @@ class Display {
203223

204224
// Public class field so it can be passed directly as a listener
205225
#outputHandler = (...args) => {
206-
if (args[0] === 'flush') {
226+
if (args[0] === OUTPUT_LEVELS.flush) {
207227
this.#outputState.buffering = false
208228
if (args[1] && this.#json) {
209-
const mergedJsonOutput = {}
210-
for (const [, ...items] of this.#outputState.buffer) {
211-
Object.assign(mergedJsonOutput, ...items.map(safeJsonParse))
229+
const json = {}
230+
for (const [, item] of this.#outputState.buffer) {
231+
Object.assign(json, tryJsonParse(item))
212232
}
213-
Object.assign(args[1])
214-
this.#writeOutput('standard', JSON.stringify(mergedJsonOutput, null, 2))
233+
this.#writeOutput('standard', JSON.stringify({ ...json, ...args[1] }, null, 2))
215234
} else {
216235
this.#outputState.buffer.forEach((item) => this.#writeOutput(...item))
217236
}
218237
this.#outputState.buffer.length = 0
219238
return
220239
}
221240

222-
if (args[0] === 'buffer') {
241+
if (args[0] === OUTPUT_LEVELS.buffer) {
223242
this.#outputState.buffer.push(['standard', ...args.slice(1)])
224243
return
225244
}
@@ -237,25 +256,27 @@ class Display {
237256
#writeOutput (...args) {
238257
const { level } = getLevel(args.shift())
239258

240-
if (level === 'standard') {
241-
this.#stdout.write(formatWithOptions({ colors: this.#stdoutColor }, ...args))
259+
if (level === OUTPUT_LEVELS.standard) {
260+
this.#stdoutWrite({}, ...args)
242261
return
243262
}
244263

245-
if (level === 'error') {
246-
this.#stderr.write(formatWithOptions({ colors: this.#stderrColor }, ...args))
264+
if (level === OUTPUT_LEVELS.error) {
265+
this.#stderrWrite({}, ...args)
247266
}
248267
}
249268

250269
// TODO: move this to proc-log and remove this public method
251270
flushOutput (jsonError) {
252-
this.#outputHandler('flush', jsonError)
271+
this.#outputHandler(OUTPUT_LEVELS.flush, jsonError)
253272
}
254273

255274
// LOGS
256275

276+
// TODO: make proc-log able to send signal data like `force`
277+
// when that happens, remove this public method
257278
forceLog (level, ...args) {
258-
// This will show the log regardless of the current loglevel
279+
// This will show the log regardless of the current loglevel except when silent
259280
if (this.#levelIndex !== 0) {
260281
this.#logHandler({ level, force: true }, ...args)
261282
}
@@ -269,7 +290,7 @@ class Display {
269290
// highly abbreviated explanation of what's being overridden.
270291
// TODO: this could probably be moved to arborist now that display is refactored
271292
const [level, heading, message, expl] = args
272-
if (level === LEVELS.warn && heading === 'ERESOLVE' && expl && typeof expl === 'object') {
293+
if (level === LOG_LEVELS.warn && heading === 'ERESOLVE' && expl && typeof expl === 'object') {
273294
this.#writeLog(level, heading, message)
274295
this.#writeLog(level, '', explain(expl, this.#stderrChalk, 2))
275296
return
@@ -278,9 +299,10 @@ class Display {
278299
} catch (ex) {
279300
try {
280301
// if it crashed once, it might again!
281-
this.#writeLog(LEVELS.verbose, null, `attempt to log crashed`, ...args, ex)
302+
this.#writeLog(LOG_LEVELS.verbose, null, `attempt to log crashed`, ...args, ex)
282303
} catch (ex2) {
283-
/* istanbul ignore next - this happens if the object has an inspect method that crashes */
304+
// This happens if the object has an inspect method that crashes so just console.error
305+
// with the errors but don't do anything else that might error again.
284306
// eslint-disable-next-line no-console
285307
console.error(`attempt to log crashed`, ex, ex2)
286308
}
@@ -301,7 +323,7 @@ class Display {
301323
this.#logColors[level](levelOpts.label ?? level),
302324
title ? this.#logColors.title(title) : null,
303325
]
304-
this.#stderr.write(formatWithOptions({ prefix, colors: this.stderrColor }, ...args))
326+
this.#stderrWrite({ prefix }, ...args)
305327
} else if (this.#progress) {
306328
// TODO: make this display a single log line of filtered messages
307329
}

lib/utils/exit-handler.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ let npm = null // set by the cli
88
let exitHandlerCalled = false
99
let showLogFileError = false
1010

11-
const outputError = (v) => {
12-
if (npm) {
13-
output.error(v)
14-
} else {
15-
// Use console for output if there is no npm
16-
// eslint-disable-next-line no-console
17-
console.error(v)
18-
}
19-
}
2011
process.on('exit', code => {
2112
// process.emit is synchronous, so the timeEnd handler will run before the
2213
// unfinished timer check below
@@ -40,9 +31,13 @@ process.on('exit', code => {
4031
if (!exitHandlerCalled) {
4132
process.exitCode = code || 1
4233
log.error('', 'Exit handler never called!')
43-
outputError('')
4434
log.error('', 'This is an error with npm itself. Please report this error at:')
4535
log.error('', ' <https://github.com/npm/cli/issues>')
36+
37+
// This gets logged regardless of silent mode
38+
// eslint-disable-next-line no-console
39+
console.error('')
40+
4641
showLogFileError = true
4742
}
4843

@@ -67,10 +62,7 @@ process.on('exit', code => {
6762
const logMethod = showLogFileError ? 'error' : timing ? 'info' : null
6863

6964
if (logMethod) {
70-
if (!npm.silent) {
71-
// just a line break if not in silent mode
72-
outputError('')
73-
}
65+
output.error('')
7466

7567
const message = []
7668

@@ -116,17 +108,20 @@ const exitHandler = err => {
116108
err = err || new Error('Exit prior to setting npm in exit handler')
117109
// Don't use proc-log here since npm was never set
118110
// eslint-disable-next-line no-console
119-
outputError(err.stack || err.message)
111+
console.error(err.stack || err.message)
120112
return process.exit(1)
121113
}
122114

123115
if (!hasLoadedNpm) {
124116
err = err || new Error('Exit prior to config file resolving.')
125-
outputError(err.stack || err.message)
117+
// Don't use proc-log here since npm was never loaded
118+
// eslint-disable-next-line no-console
119+
console.error(err.stack || err.message)
126120
}
127121

128122
// only show the notification if it finished.
129123
if (typeof npm.updateNotification === 'string') {
124+
// TODO: use proc-log to send `force: true` along with event
130125
npm.forceLog('notice', '', npm.updateNotification)
131126
}
132127

@@ -204,6 +199,7 @@ const exitHandler = err => {
204199
}
205200

206201
if (hasLoadedNpm) {
202+
// TODO: use proc-log.output.flush() once it exists
207203
npm.flushOutput(jsonError)
208204
}
209205

lib/utils/format.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,4 @@ const formatWithOptions = ({ prefix: prefixes = [], eol = '\n', ...options }, ..
4747
return lines.reduce((acc, l) => `${acc}${prefix}${prefix && l ? ' ' : ''}${l}${eol}`, '')
4848
}
4949

50-
const format = (...args) => formatWithOptions({}, ...args)
51-
52-
module.exports = { format, formatWithOptions }
50+
module.exports = { formatWithOptions }

0 commit comments

Comments
 (0)