-
-
Notifications
You must be signed in to change notification settings - Fork 929
/
checkInvalidCLIOptions.js
93 lines (75 loc) · 2.18 KB
/
checkInvalidCLIOptions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* @flow */
'use strict';
const _ = require('lodash');
const chalk = require('chalk');
const EOL = require('os').EOL;
const leven = require('leven');
/*:: type allowedOptionsType = { [key: string]: { alias?: string } }*/
/**
* @param {{ [key: string]: { alias?: string } }} allowedOptions
* @return {string[]}
*/
const buildAllowedOptions = (allowedOptions /*: allowedOptionsType */) => {
let options = Object.keys(allowedOptions);
options = options.reduce((opts, opt) => {
const alias = allowedOptions[opt].alias;
if (alias) {
opts.push(alias);
}
return opts;
}, options);
options.sort();
return options;
};
/**
* @param {string[]} all
* @param {string} invalid
* @return {null|string}
*/
const suggest = (all /*: string[]*/, invalid /*: string*/) => {
const maxThreshold = 10;
for (let threshold = 1; threshold <= maxThreshold; threshold++) {
const suggestion = all.find((option) => leven(option, invalid) <= threshold);
if (suggestion) {
return suggestion;
}
}
return null;
};
/**
* @param {string} opt
* @return {string}
*/
const cliOption = (opt /*: string*/) =>
opt.length === 1 ? `"-${opt}"` : `"--${_.kebabCase(opt)}"`;
/**
* @param {string} invalid
* @param {string|null} suggestion
* @return {string}
*/
const buildMessageLine = (invalid /*: string*/, suggestion /*: ?string*/) => {
let line = `Invalid option ${chalk.red(cliOption(invalid))}.`;
if (suggestion) {
line += ` Did you mean ${chalk.cyan(cliOption(suggestion))}?`;
}
return line + EOL;
};
/**
* @param {{ [key: string]: any }} allowedOptions
* @param {{ [key: string]: any }}inputOptions
* @return {string}
*/
module.exports = function checkInvalidCLIOptions(
allowedOptions /*: { [key: string]: any }*/,
inputOptions /*: { [key: string]: any }*/,
) /*: string*/ {
const allOptions = buildAllowedOptions(allowedOptions);
return Object.keys(inputOptions)
.map((opt) => _.kebabCase(opt))
.filter((opt) => !allOptions.includes(opt))
.reduce((msg, invalid) => {
// NOTE: No suggestion for shortcut options because it's too difficult
const suggestion = invalid.length >= 2 ? suggest(allOptions, invalid) : null;
return msg + buildMessageLine(invalid, suggestion);
}, '');
};