Skip to content

Commit 9dd2ed5

Browse files
ruyadornowraithgar
andcommitted
fix: empty newline printed to stderr
Starting in v7.7.0 running `npm` (no args) is printing an empty newline to stderr. This fixes that by correctly exiting via errorHandler and avoiding hitting the cb() never called error and adds a test to make sure we avoid that regression moving forward. Fixes: nodejs/node#37678 (comment) Co-authored-by: Gar <gar+gh@danger.computer>
1 parent a28f895 commit 9dd2ed5

File tree

4 files changed

+77
-7
lines changed

4 files changed

+77
-7
lines changed

lib/cli.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,14 @@ module.exports = (process) => {
6161
impl(npm.argv, errorHandler)
6262
else {
6363
try {
64-
// I don't know why this is needed but we get a cb() not called if we
65-
// omit it
66-
npm.log.level = 'silent'
6764
if (cmd) {
6865
const didYouMean = require('./utils/did-you-mean.js')
6966
const suggestions = await didYouMean(npm, npm.localPrefix, cmd)
7067
npm.output(`Unknown command: "${cmd}"${suggestions}\n\nTo see a list of supported npm commands, run:\n npm help`)
7168
} else
7269
npm.output(npm.usage)
7370
process.exitCode = 1
71+
return errorHandler()
7472
} catch (err) {
7573
errorHandler(err)
7674
}

smoke-tests/index.js

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ t.cleanSnapshot = s => s.split(cwd).join('{CWD}')
1212
.split(process.cwd()).join('{CWD}')
1313
.replace(/\\+/g, '/')
1414
.replace(/\r\n/g, '\n')
15+
.replace(/\ \(in a browser\)/g, '')
1516

1617
// setup server
1718
const registryServer = require('./server.js')
@@ -55,6 +56,19 @@ t.test('npm init', async t => {
5556
t.equal(pkg.version, '1.0.0', 'should have expected generated version')
5657
})
5758

59+
t.test('npm (no args)', async t => {
60+
const cmd = `"${process.execPath}" "${npmLocation}" --no-audit --no-update-notifier`
61+
const cmdRes = await execAsync(cmd, { cwd: localPrefix, env })
62+
.catch(err => {
63+
t.equal(err.code, 1, 'should exit with error code')
64+
return err
65+
})
66+
67+
t.equal(cmdRes.stderr, '', 'should have no stderr output')
68+
t.matchSnapshot(String(cmdRes.stdout),
69+
'should have expected no args output')
70+
})
71+
5872
t.test('npm install prodDep@version', async t => {
5973
const cmd = `${npmBin} install abbrev@1.0.4`
6074
const cmdRes = await exec(cmd)

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

+37
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,43 @@
55
* Make sure to inspect the output below. Do not ignore changes!
66
*/
77
'use strict'
8+
exports[`smoke-tests/index.js TAP npm (no args) > should have expected no args output 1`] = `
9+
npm <command>
10+
11+
Usage:
12+
13+
npm install install all the dependencies in your project
14+
npm install <foo> add the <foo> dependency to your project
15+
npm test run this project's tests
16+
npm run <foo> run the script named <foo>
17+
npm <command> -h quick help on <command>
18+
npm -l display usage info for all commands
19+
npm help <term> search for help on <term>
20+
npm help npm more involved overview
21+
22+
All commands:
23+
24+
access, adduser, audit, bin, bugs, cache, ci, completion,
25+
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
26+
edit, exec, explain, explore, find-dupes, fund, get, help,
27+
hook, init, install, install-ci-test, install-test, link,
28+
ll, login, logout, ls, org, outdated, owner, pack, ping,
29+
prefix, profile, prune, publish, rebuild, repo, restart,
30+
root, run-script, search, set, set-script, shrinkwrap, star,
31+
stars, start, stop, team, test, token, uninstall, unpublish,
32+
unstar, update, version, view, whoami
33+
34+
Specify configs in the ini-formatted file:
35+
{CWD}/smoke-tests/index/.npmrc
36+
or on the command line via: npm <command> --key=value
37+
38+
More configuration info: npm help config
39+
Configuration fields: npm help 7 config
40+
41+
npm@7.7.5 {CWD}
42+
43+
`
44+
845
exports[`smoke-tests/index.js TAP npm diff > should have expected diff output 1`] = `
946
diff --git a/package.json b/package.json
1047
index v1.0.4..v1.1.1 100644

test/lib/cli.js

+25-4
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,37 @@ t.test('gracefully handles error printing usage', t => {
172172
t.teardown(() => {
173173
npmock.output = output
174174
errorHandlerCb = null
175+
errorHandlerCalled = null
175176
})
176177
const proc = {
177-
argv: ['node', 'npm', 'asdf'],
178+
argv: ['node', 'npm'],
178179
on: () => {},
179180
}
180181
npmock.argv = []
181-
npmock.output = (msg) => {
182-
throw new Error('test exception')
182+
errorHandlerCb = () => {
183+
t.match(errorHandlerCalled, [], 'should call errorHandler with no args')
184+
t.end()
185+
}
186+
cli(proc)
187+
})
188+
189+
t.test('handles output error', t => {
190+
const { output } = npmock
191+
t.teardown(() => {
192+
npmock.output = output
193+
errorHandlerCb = null
194+
errorHandlerCalled = null
195+
})
196+
const proc = {
197+
argv: ['node', 'npm'],
198+
on: () => {},
199+
}
200+
npmock.argv = []
201+
npmock.output = () => {
202+
throw new Error('ERR')
183203
}
184204
errorHandlerCb = () => {
185-
t.match(errorHandlerCalled, /test exception/)
205+
t.match(errorHandlerCalled, /ERR/, 'should call errorHandler with error')
186206
t.end()
187207
}
188208
cli(proc)
@@ -191,6 +211,7 @@ t.test('gracefully handles error printing usage', t => {
191211
t.test('load error calls error handler', t => {
192212
t.teardown(() => {
193213
errorHandlerCb = null
214+
errorHandlerCalled = null
194215
LOAD_ERROR = null
195216
})
196217

0 commit comments

Comments
 (0)