From e94a45851dcfa08d5ce92b6f17cd868acae94ee4 Mon Sep 17 00:00:00 2001 From: Gar Date: Fri, 19 Mar 2021 07:33:40 -0700 Subject: [PATCH] fix(suggestions): clarify Unknown command output Base commands and `npm run` need different outputs PR-URL: https://github.com/npm/cli/pull/2906 Credit: @wraithgar Close: #2906 Reviewed-by: @ruyadorno --- lib/cli.js | 4 +--- lib/run-script.js | 4 ++-- lib/utils/did-you-mean.js | 9 +++++---- test/lib/cli.js | 4 ++-- test/lib/run-script.js | 22 +++++++++++----------- test/lib/utils/did-you-mean.js | 6 +----- 6 files changed, 22 insertions(+), 27 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 086335725c740..0879224654b7e 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -66,14 +66,12 @@ module.exports = (process) => { npm.log.level = 'silent' if (cmd) { const didYouMean = require('./utils/did-you-mean.js') - console.error(npm.localPrefix) const suggestions = await didYouMean(npm, npm.localPrefix, cmd) - npm.output(suggestions) + npm.output(`Unknown command: "${cmd}"${suggestions}`) } else npm.output(npm.usage) process.exitCode = 1 } catch (err) { - console.error(err) errorHandler(err) } } diff --git a/lib/run-script.js b/lib/run-script.js index e59340225e2e4..0f4c40b0d1ca9 100644 --- a/lib/run-script.js +++ b/lib/run-script.js @@ -92,7 +92,7 @@ class RunScript extends BaseCommand { return const suggestions = await didYouMean(this.npm, path, event) - throw new Error(suggestions) + throw new Error(`Missing script: "${event}"${suggestions}`) } // positional args only added to the main event, not pre/post @@ -228,7 +228,7 @@ class RunScript extends BaseCommand { log.error(` in workspace: ${pkg._id || pkg.name}`) log.error(` at location: ${workspacePath}`) - const scriptMissing = err.message.startsWith('Unknown command') + const scriptMissing = err.message.startsWith('Missing script') // avoids exiting with error code in case there's scripts missing // in some workspaces since other scripts might have succeeded diff --git a/lib/utils/did-you-mean.js b/lib/utils/did-you-mean.js index 5e41af67a65b2..98133196e3c56 100644 --- a/lib/utils/did-you-mean.js +++ b/lib/utils/did-you-mean.js @@ -23,10 +23,11 @@ const didYouMean = async (npm, path, scmd) => { const best = [...bestCmd, ...bestRun, ...bestBin] - const suggestion = best.length === 0 ? '' - : best.length === 1 ? `\n\nDid you mean this?\n${best[0]}` + if (best.length === 0) + return '' + + const suggestion = best.length === 1 ? `\n\nDid you mean this?\n${best[0]}` : `\n\nDid you mean one of these?\n${best.slice(0, 3).join('\n')}` - const result = `Unknown command: "${scmd}"${suggestion}` - return result + return suggestion } module.exports = didYouMean diff --git a/test/lib/cli.js b/test/lib/cli.js index 59769e13fd0e7..40da77bf44e3d 100644 --- a/test/lib/cli.js +++ b/test/lib/cli.js @@ -46,7 +46,7 @@ const npmlogMock = { const requireInject = require('require-inject') const cli = requireInject.installGlobally('../../lib/cli.js', { '../../lib/npm.js': npmock, - '../../lib/utils/did-you-mean.js': () => 'test did you mean', + '../../lib/utils/did-you-mean.js': () => '\ntest did you mean', '../../lib/utils/unsupported.js': unsupportedMock, '../../lib/utils/error-handler.js': errorHandlerMock, npmlog: npmlogMock, @@ -160,7 +160,7 @@ t.test('print usage if non-command param provided', t => { npmock.argv = ['asdf'] npmock.output = (msg) => { if (msg) { - t.match(msg, 'test did you mean', 'outputs did you mean') + t.match(msg, 'Unknown command: "asdf"\ntest did you mean', 'outputs did you mean') t.end() } } diff --git a/test/lib/run-script.js b/test/lib/run-script.js index db1fc4b5c560a..265565705ad30 100644 --- a/test/lib/run-script.js +++ b/test/lib/run-script.js @@ -290,7 +290,7 @@ t.test('try to run missing script', t => { t.test('no suggestions', t => { runScript.exec(['notevenclose'], er => { t.match(er, { - message: 'Unknown command: "notevenclose"', + message: 'Missing script: "notevenclose"', }) t.end() }) @@ -298,7 +298,7 @@ t.test('try to run missing script', t => { t.test('script suggestions', t => { runScript.exec(['helo'], er => { t.match(er, { - message: 'Unknown command: "helo"', + message: 'Missing script: "helo"', }) t.match(er, { message: 'npm run hello', @@ -309,7 +309,7 @@ t.test('try to run missing script', t => { t.test('bin suggestions', t => { runScript.exec(['goodneght'], er => { t.match(er, { - message: 'Unknown command: "goodneght"', + message: 'Missing script: "goodneght"', }) t.match(er, { message: 'npm exec goodnight', @@ -896,27 +896,27 @@ t.test('workspaces', t => { t.match(RUN_SCRIPTS, []) t.strictSame(LOG.map(cleanOutput), [ 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: a@1.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: b@2.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: c@1.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/c', 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: d@1.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/d', 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: e', ' at location: {CWD}/test/lib/run-script-workspaces/packages/e', 'Lifecycle script `missing-script` failed with error:', - 'Error: Unknown command: "missing-script"', + 'Error: Missing script: "missing-script"', ' in workspace: noscripts@1.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/noscripts', ], 'should log error msgs for each workspace script') @@ -937,11 +937,11 @@ t.test('workspaces', t => { t.match(RUN_SCRIPTS, []) t.strictSame(LOG.map(cleanOutput), [ 'Lifecycle script `test` failed with error:', - 'Error: Unknown command: "test"', + 'Error: Missing script: "test"', ' in workspace: a@1.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', 'Lifecycle script `test` failed with error:', - 'Error: Unknown command: "test"', + 'Error: Missing script: "test"', ' in workspace: b@2.0.0', ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', ], 'should log error msgs for each workspace script') diff --git a/test/lib/utils/did-you-mean.js b/test/lib/utils/did-you-mean.js index 898806aa1c26b..68893a800fda4 100644 --- a/test/lib/utils/did-you-mean.js +++ b/test/lib/utils/did-you-mean.js @@ -8,23 +8,20 @@ t.test('did-you-mean', t => { t.notOk(err) t.test('nistall', async t => { const result = await dym(npm, npm.localPrefix, 'nistall') - t.match(result, 'Unknown command') t.match(result, 'npm install') }) t.test('sttest', async t => { const result = await dym(npm, npm.localPrefix, 'sttest') - t.match(result, 'Unknown command') t.match(result, 'npm test') t.match(result, 'npm run posttest') }) t.test('npz', async t => { const result = await dym(npm, npm.localPrefix, 'npxx') - t.match(result, 'Unknown command') t.match(result, 'npm exec npx') }) t.test('qwuijbo', async t => { const result = await dym(npm, npm.localPrefix, 'qwuijbo') - t.match(result, 'Unknown command') + t.match(result, '') }) t.end() }) @@ -38,6 +35,5 @@ t.test('missing bin and script properties', async t => { }) const result = await dym(npm, path, 'nistall') - t.match(result, 'Unknown command') t.match(result, 'npm install') })