mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-05 11:31:31 +08:00
R=jacktfranklin@chromium.org Bug: none Change-Id: I961be18530d3ccbc4efed0c2cda9bd603b14e27b Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3372931 Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> Commit-Queue: Tim Van der Lippe <tvanderlippe@chromium.org>
108 lines
2.3 KiB
JavaScript
108 lines
2.3 KiB
JavaScript
'use strict';
|
|
|
|
const EOL = require('os').EOL;
|
|
const levenshtein = require('fastest-levenshtein');
|
|
const { red, cyan } = require('picocolors');
|
|
|
|
/**
|
|
* @param {{ [key: string]: { alias?: string } }} allowedOptions
|
|
* @return {string[]}
|
|
*/
|
|
const buildAllowedOptions = (allowedOptions) => {
|
|
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, invalid) => {
|
|
const maxThreshold = 10;
|
|
|
|
for (let threshold = 1; threshold <= maxThreshold; threshold++) {
|
|
const suggestion = all.find((option) => levenshtein.distance(option, invalid) <= threshold);
|
|
|
|
if (suggestion) {
|
|
return suggestion;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Converts a string to kebab case.
|
|
* For example, `kebabCase('oneTwoThree') === 'one-two-three'`.
|
|
* @param {string} opt
|
|
* @returns {string}
|
|
*/
|
|
const kebabCase = (opt) => {
|
|
const matches = opt.match(/[A-Z]?[a-z]+|[A-Z]|[0-9]+/g);
|
|
|
|
if (matches) {
|
|
return matches.map((s) => s.toLowerCase()).join('-');
|
|
}
|
|
|
|
return '';
|
|
};
|
|
|
|
/**
|
|
* @param {string} opt
|
|
* @return {string}
|
|
*/
|
|
const cliOption = (opt) => {
|
|
if (opt.length === 1) {
|
|
return `"-${opt}"`;
|
|
}
|
|
|
|
return `"--${kebabCase(opt)}"`;
|
|
};
|
|
|
|
/**
|
|
* @param {string} invalid
|
|
* @param {string|null} suggestion
|
|
* @return {string}
|
|
*/
|
|
const buildMessageLine = (invalid, suggestion) => {
|
|
let line = `Invalid option ${red(cliOption(invalid))}.`;
|
|
|
|
if (suggestion) {
|
|
line += ` Did you mean ${cyan(cliOption(suggestion))}?`;
|
|
}
|
|
|
|
return line + EOL;
|
|
};
|
|
|
|
/**
|
|
* @param {{ [key: string]: any }} allowedOptions
|
|
* @param {{ [key: string]: any }} inputOptions
|
|
* @return {string}
|
|
*/
|
|
module.exports = function checkInvalidCLIOptions(allowedOptions, inputOptions) {
|
|
const allOptions = buildAllowedOptions(allowedOptions);
|
|
|
|
return Object.keys(inputOptions)
|
|
.filter((opt) => !allOptions.includes(opt))
|
|
.map((opt) => kebabCase(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);
|
|
}, '');
|
|
};
|