mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-18 21:27:51 +00:00
Display command suggestions, when command not recognized
Change-type: minor Signed-off-by: Scott Lowe <scott@balena.io>
This commit is contained in:
parent
d56fec6e36
commit
273ea5ce4d
61
lib/hooks/command-not-found/suggest.ts
Normal file
61
lib/hooks/command-not-found/suggest.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019-2020 Balena Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Hook } from '@oclif/config';
|
||||
import type { IConfig } from '@oclif/config';
|
||||
|
||||
/*
|
||||
A modified version of the command-not-found plugin logic,
|
||||
which deals with spaces separators stead of colons, and
|
||||
prints suggested commands instead of prompting interactively.
|
||||
*/
|
||||
|
||||
const hook: Hook<'command-not-found'> = async function (
|
||||
opts: object & { config: IConfig; id?: string },
|
||||
) {
|
||||
const Levenshtein = await import('fast-levenshtein');
|
||||
const _ = await import('lodash');
|
||||
const { color } = await import('@oclif/color');
|
||||
|
||||
const commandId = opts.id || '';
|
||||
const command = opts.id?.replace(':', ' ') || '';
|
||||
|
||||
const commandIDs = [
|
||||
...opts.config.commandIDs,
|
||||
..._.flatten(opts.config.commands.map((c) => c.aliases)),
|
||||
'version',
|
||||
];
|
||||
|
||||
function closest(cmd: string) {
|
||||
return _.minBy(commandIDs, (c) => Levenshtein.get(cmd, c))!;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`${color.yellow(command)} is not a recognized balena command.\n`,
|
||||
);
|
||||
|
||||
const suggestion = closest(commandId).replace(':', ' ') || '';
|
||||
console.log(`Did you mean: ${color.cmd(suggestion)} ? `);
|
||||
console.log(
|
||||
`Run ${color.cmd('balena help -v')} for a list of available commands.`,
|
||||
);
|
||||
|
||||
const COMMAND_NOT_FOUND = 127;
|
||||
process.exit(COMMAND_NOT_FOUND);
|
||||
};
|
||||
|
||||
export default hook;
|
34
npm-shrinkwrap.json
generated
34
npm-shrinkwrap.json
generated
@ -957,6 +957,13 @@
|
||||
"cli-ux": "^4.9.0",
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"lodash": "^4.17.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oclif/plugin-warn-if-update-available": {
|
||||
@ -1564,6 +1571,11 @@
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/fast-levenshtein": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/fast-levenshtein/-/fast-levenshtein-0.0.1.tgz",
|
||||
"integrity": "sha1-OjYVzxc2Rcj8pY0FHk4ygk5L0oY="
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
|
||||
@ -6380,9 +6392,17 @@
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
|
||||
"integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==",
|
||||
"requires": {
|
||||
"fastest-levenshtein": "^1.0.7"
|
||||
}
|
||||
},
|
||||
"fastest-levenshtein": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
|
||||
"integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow=="
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.8.0",
|
||||
@ -11751,6 +11771,14 @@
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2",
|
||||
"word-wrap": "~1.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ordered-read-streams": {
|
||||
|
@ -103,7 +103,8 @@
|
||||
"commands": "./build/actions-oclif",
|
||||
"helpClass": "./build/help",
|
||||
"hooks": {
|
||||
"prerun": "./build/hooks/prerun/track"
|
||||
"prerun": "./build/hooks/prerun/track",
|
||||
"command_not_found": "./build/hooks/command-not-found/suggest"
|
||||
},
|
||||
"macos": {
|
||||
"identifier": "io.balena.cli",
|
||||
@ -197,6 +198,7 @@
|
||||
"@oclif/command": "^1.8.0",
|
||||
"@resin.io/valid-email": "^0.1.0",
|
||||
"@sentry/node": "^5.21.1",
|
||||
"@types/fast-levenshtein": "0.0.1",
|
||||
"@types/update-notifier": "^4.1.1",
|
||||
"@zeit/dockerignore": "0.0.3",
|
||||
"JSONStream": "^1.0.3",
|
||||
@ -230,6 +232,7 @@
|
||||
"event-stream": "3.3.4",
|
||||
"express": "^4.13.3",
|
||||
"fast-boot2": "^1.1.0",
|
||||
"fast-levenshtein": "^3.0.0",
|
||||
"get-stdin": "^8.0.0",
|
||||
"global-agent": "^2.1.12",
|
||||
"global-tunnel-ng": "^2.1.1",
|
||||
|
Loading…
Reference in New Issue
Block a user