Skip to content

Commit 54eae30

Browse files
committed
chore(errorHandler): rename to exit handler
Cleaned the code up too PR-URL: #3416 Credit: @wraithgar Close: #3416 Reviewed-by: @nlf
1 parent 78da60f commit 54eae30

File tree

8 files changed

+140
-214
lines changed

8 files changed

+140
-214
lines changed

lib/cli.js

+23-22
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ module.exports = (process) => {
1919
checkForUnsupportedNode()
2020

2121
const npm = require('../lib/npm.js')
22-
const errorHandler = require('../lib/utils/error-handler.js')
23-
errorHandler.setNpm(npm)
22+
const exitHandler = require('../lib/utils/exit-handler.js')
23+
exitHandler.setNpm(npm)
2424

2525
// if npm is called as "npmg" or "npm_g", then
2626
// run in global mode.
@@ -32,20 +32,22 @@ module.exports = (process) => {
3232
log.info('using', 'npm@%s', npm.version)
3333
log.info('using', 'node@%s', process.version)
3434

35-
process.on('uncaughtException', errorHandler)
36-
process.on('unhandledRejection', errorHandler)
35+
process.on('uncaughtException', exitHandler)
36+
process.on('unhandledRejection', exitHandler)
37+
38+
const updateNotifier = require('../lib/utils/update-notifier.js')
3739

3840
// now actually fire up npm and run the command.
3941
// this is how to use npm programmatically:
40-
const updateNotifier = require('../lib/utils/update-notifier.js')
4142
npm.load(async er => {
43+
// Any exceptions here will be picked up by the uncaughtException handler
4244
if (er)
43-
return errorHandler(er)
45+
return exitHandler(er)
4446

4547
// npm --version=cli
4648
if (npm.config.get('version', 'cli')) {
4749
npm.output(npm.version)
48-
return errorHandler.exit(0)
50+
return exitHandler()
4951
}
5052

5153
// npm --versions=cli
@@ -57,22 +59,21 @@ module.exports = (process) => {
5759
updateNotifier(npm)
5860

5961
const cmd = npm.argv.shift()
62+
if (!cmd) {
63+
npm.output(npm.usage)
64+
process.exitCode = 1
65+
return exitHandler()
66+
}
67+
6068
const impl = npm.commands[cmd]
61-
if (impl)
62-
impl(npm.argv, errorHandler)
63-
else {
64-
try {
65-
if (cmd) {
66-
const didYouMean = require('./utils/did-you-mean.js')
67-
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
68-
npm.output(`Unknown command: "${cmd}"${suggestions}\n\nTo see a list of supported npm commands, run:\n npm help`)
69-
} else
70-
npm.output(npm.usage)
71-
process.exitCode = 1
72-
return errorHandler()
73-
} catch (err) {
74-
errorHandler(err)
75-
}
69+
if (!impl) {
70+
const didYouMean = require('./utils/did-you-mean.js')
71+
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
72+
npm.output(`Unknown command: "${cmd}"${suggestions}\n\nTo see a list of supported npm commands, run:\n npm help`)
73+
process.exitCode = 1
74+
return exitHandler()
7675
}
76+
77+
impl(npm.argv, exitHandler)
7778
})
7879
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
let npm // set by the cli
2-
let cbCalled = false
31
const log = require('npmlog')
4-
let itWorked = false
2+
const os = require('os')
53
const path = require('path')
64
const writeFileAtomic = require('write-file-atomic')
75
const mkdirp = require('mkdirp-infer-owner')
86
const fs = require('graceful-fs')
9-
let wroteLogFile = false
10-
let exitCode = 0
7+
118
const errorMessage = require('./error-message.js')
129
const replaceInfo = require('./replace-info.js')
1310

11+
let exitHandlerCalled = false
1412
let logFileName
13+
let npm // set by the cli
14+
let wroteLogFile = false
15+
16+
const timings = {}
17+
1518
const getLogFile = () => {
1619
// we call this multiple times, so we need to treat it as a singleton because
1720
// the date is part of the name
@@ -21,7 +24,6 @@ const getLogFile = () => {
2124
return logFileName
2225
}
2326

24-
const timings = {}
2527
process.on('timing', (name, value) => {
2628
if (timings[name])
2729
timings[name] += value
@@ -53,22 +55,20 @@ process.on('exit', code => {
5355
}
5456
}
5557

56-
if (code)
57-
itWorked = false
58-
if (itWorked)
58+
if (!code)
5959
log.info('ok')
6060
else {
61-
if (!cbCalled) {
62-
log.error('', 'cb() never called!')
61+
if (!exitHandlerCalled) {
62+
log.error('', 'Exit handler never called!')
6363
console.error('')
6464
log.error('', 'This is an error with npm itself. Please report this error at:')
6565
log.error('', ' <https://github.com/npm/cli/issues>')
66+
// TODO this doesn't have an npm.config.loaded guard
6667
writeLogFile()
6768
}
68-
69-
if (code)
70-
log.verbose('code', code)
69+
log.verbose('code', code)
7170
}
71+
// In timing mode we always write the log file
7272
if (npm.config && npm.config.loaded && npm.config.get('timing') && !wroteLogFile)
7373
writeLogFile()
7474
if (wroteLogFile) {
@@ -83,52 +83,46 @@ process.on('exit', code => {
8383
' ' + getLogFile(),
8484
].join('\n')
8585
)
86-
wroteLogFile = false
8786
}
8887

89-
// actually exit.
90-
if (exitCode === 0 && !itWorked)
91-
exitCode = 1
88+
// these are needed for the tests to have a clean slate in each test case
89+
exitHandlerCalled = false
90+
wroteLogFile = false
9291

93-
if (exitCode !== 0)
94-
process.exit(exitCode)
92+
// actually exit.
93+
process.exit(code)
9594
})
9695

9796
const exit = (code, noLog) => {
98-
exitCode = exitCode || process.exitCode || code
99-
100-
log.verbose('exit', code)
97+
log.verbose('exit', code || 0)
10198
if (log.level === 'silent')
10299
noLog = true
103100

104-
const reallyExit = () => {
105-
itWorked = !code
106-
107-
// Exit directly -- nothing in the CLI should still be running in the
108-
// background at this point, and this makes sure anything left dangling
109-
// for whatever reason gets thrown away, instead of leaving the CLI open
110-
//
111-
// Commands that expect long-running actions should just delay `cb()`
112-
process.stdout.write('', () => {
113-
process.exit(code)
114-
})
115-
}
116-
101+
// noLog is true if there was an error, including if config wasn't loaded, so
102+
// this doesn't need a config.loaded guard
117103
if (code && !noLog)
118104
writeLogFile()
119-
reallyExit()
105+
106+
// Exit directly -- nothing in the CLI should still be running in the
107+
// background at this point, and this makes sure anything left dangling
108+
// for whatever reason gets thrown away, instead of leaving the CLI open
109+
process.stdout.write('', () => {
110+
// `|| process.exitCode` supports a single use case, where we set the exit
111+
// code to 1 if npm is called with no arguments
112+
process.exit(code)
113+
})
120114
}
121115

122-
const errorHandler = (er) => {
116+
const exitHandler = (err) => {
123117
log.disableProgress()
124118
if (!npm.config || !npm.config.loaded) {
125119
// logging won't work unless we pretend that it's ready
126-
er = er || new Error('Exit prior to config file resolving.')
127-
console.error(er.stack || er.message)
120+
err = err || new Error('Exit prior to config file resolving.')
121+
console.error(err.stack || err.message)
128122
}
129123

130-
if (cbCalled)
131-
er = er || new Error('Callback called more than once.')
124+
if (exitHandlerCalled)
125+
err = err || new Error('Exit handler called more than once.')
132126

133127
// only show the notification if it finished before the other stuff we
134128
// were doing. no need to hang on `npm -v` or something.
@@ -139,68 +133,67 @@ const errorHandler = (er) => {
139133
log.level = level
140134
}
141135

142-
cbCalled = true
143-
if (!er)
144-
return exit(0)
136+
exitHandlerCalled = true
137+
if (!err)
138+
return exit()
145139

146140
// if we got a command that just shells out to something else, then it
147141
// will presumably print its own errors and exit with a proper status
148142
// code if there's a problem. If we got an error with a code=0, then...
149143
// something else went wrong along the way, so maybe an npm problem?
150144
const isShellout = npm.shelloutCommands.includes(npm.command)
151-
const quietShellout = isShellout && typeof er.code === 'number' && er.code
145+
const quietShellout = isShellout && typeof err.code === 'number' && err.code
152146
if (quietShellout)
153-
return exit(er.code, true)
154-
else if (typeof er === 'string') {
155-
log.error('', er)
147+
return exit(err.code, true)
148+
else if (typeof err === 'string') {
149+
log.error('', err)
156150
return exit(1, true)
157-
} else if (!(er instanceof Error)) {
158-
log.error('weird error', er)
151+
} else if (!(err instanceof Error)) {
152+
log.error('weird error', err)
159153
return exit(1, true)
160154
}
161155

162-
if (!er.code) {
163-
const matchErrorCode = er.message.match(/^(?:Error: )?(E[A-Z]+)/)
164-
er.code = matchErrorCode && matchErrorCode[1]
156+
if (!err.code) {
157+
const matchErrorCode = err.message.match(/^(?:Error: )?(E[A-Z]+)/)
158+
err.code = matchErrorCode && matchErrorCode[1]
165159
}
166160

167161
for (const k of ['type', 'stack', 'statusCode', 'pkgid']) {
168-
const v = er[k]
162+
const v = err[k]
169163
if (v)
170164
log.verbose(k, replaceInfo(v))
171165
}
172166

173167
log.verbose('cwd', process.cwd())
174168

175-
const os = require('os')
176169
const args = replaceInfo(process.argv)
177170
log.verbose('', os.type() + ' ' + os.release())
178171
log.verbose('argv', args.map(JSON.stringify).join(' '))
179172
log.verbose('node', process.version)
180173
log.verbose('npm ', 'v' + npm.version)
181174

182175
for (const k of ['code', 'syscall', 'file', 'path', 'dest', 'errno']) {
183-
const v = er[k]
176+
const v = err[k]
184177
if (v)
185178
log.error(k, v)
186179
}
187180

188-
const msg = errorMessage(er, npm)
181+
const msg = errorMessage(err, npm)
189182
for (const errline of [...msg.summary, ...msg.detail])
190183
log.error(...errline)
191184

192185
if (npm.config && npm.config.get('json')) {
193186
const error = {
194187
error: {
195-
code: er.code,
188+
code: err.code,
196189
summary: messageText(msg.summary),
197190
detail: messageText(msg.detail),
198191
},
199192
}
200193
console.error(JSON.stringify(error, null, 2))
201194
}
202195

203-
exit(typeof er.errno === 'number' ? er.errno : typeof er.code === 'number' ? er.code : 1)
196+
exit(typeof err.errno === 'number' ? err.errno : typeof err.code === 'number' ? err.code : 1)
204197
}
205198

206199
const messageText = msg => msg.map(line => line.slice(1).join(' ')).join('\n')
@@ -209,8 +202,6 @@ const writeLogFile = () => {
209202
if (wroteLogFile)
210203
return
211204

212-
const os = require('os')
213-
214205
try {
215206
let logOutput = ''
216207
log.record.forEach(m => {
@@ -243,8 +234,7 @@ const writeLogFile = () => {
243234
}
244235
}
245236

246-
module.exports = errorHandler
247-
module.exports.exit = exit
237+
module.exports = exitHandler
248238
module.exports.setNpm = (n) => {
249239
npm = n
250240
}

lib/utils/explain-eresolve.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// this is called when an ERESOLVE error is caught in the error-handler,
1+
// this is called when an ERESOLVE error is caught in the exit-handler,
22
// or when there's a log.warn('eresolve', msg, explanation), to turn it
33
// into a human-intelligible explanation of what's wrong and how to fix.
44
const { writeFileSync } = require('fs')

tap-snapshots/test/lib/utils/error-handler.js.test.cjs tap-snapshots/test/lib/utils/exit-handler.js.test.cjs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
* Make sure to inspect the output below. Do not ignore changes!
66
*/
77
'use strict'
8-
exports[`test/lib/utils/error-handler.js TAP handles unknown error > should have expected log contents for unknown error 1`] = `
8+
exports[`test/lib/utils/exit-handler.js TAP handles unknown error > should have expected log contents for unknown error 1`] = `
99
0 verbose code 1
1010
1 error foo A complete log of this run can be found in:
11-
1 error foo {CWD}/test/lib/utils/tap-testdir-error-handler/_logs/expecteddate-debug.log
11+
1 error foo {CWD}/test/lib/utils/tap-testdir-exit-handler/_logs/expecteddate-debug.log
1212
2 verbose stack Error: ERROR
1313
3 verbose cwd {CWD}
1414
4 verbose Foo 1.0.0
15-
5 verbose argv "/node" "{CWD}/test/lib/utils/error-handler.js"
15+
5 verbose argv "/node" "{CWD}/test/lib/utils/exit-handler.js"
1616
6 verbose node v1.0.0
1717
7 verbose npm v1.0.0
1818
8 error foo code ERROR

0 commit comments

Comments
 (0)