refactor: move cli leven suggestion to options-normalizer (#5057)

Option-related processing should be done in `options-normalizer`.

- unknown flag suggestions are colored
  ![image](https://user-images.githubusercontent.com/8341033/45103027-866c7900-b161-11e8-8ee3-6467a9ca0090.png)
- unknown flags with no suggestion are now errors:
  ```
  $ bin/prettier.js --help noseminosemi
  [error] Invalid `--help` value. Expected `a flag`, but received `"noseminosemi"`.
  ```
master
Ika 2018-09-08 07:40:09 +08:00 committed by GitHub
parent 3c77c5dcfb
commit 4ff0f26d3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 61 deletions

View File

@ -156,7 +156,8 @@ const options = {
description: dedent`
Show CLI usage, or details about the given flag.
Example: --help write
`
`,
exception: value => value === ""
},
"ignore-path": {
type: "path",

View File

@ -7,7 +7,6 @@ const fs = require("fs");
const globby = require("globby");
const chalk = require("chalk");
const readline = require("readline");
const leven = require("leven");
const stringify = require("json-stable-stringify");
const minimist = require("./minimist");
@ -642,40 +641,6 @@ function flattenArray(array) {
return [].concat.apply([], array);
}
function getOptionWithLevenSuggestion(context, options, optionName) {
// support aliases
const optionNameContainers = flattenArray(
options.map((option, index) => [
{ value: option.name, index },
option.alias ? { value: option.alias, index } : null
])
).filter(Boolean);
const optionNameContainer = optionNameContainers.find(
optionNameContainer => optionNameContainer.value === optionName
);
if (optionNameContainer !== undefined) {
return options[optionNameContainer.index];
}
const suggestedOptionNameContainer = optionNameContainers.find(
optionNameContainer => leven(optionNameContainer.value, optionName) < 3
);
if (suggestedOptionNameContainer !== undefined) {
const suggestedOptionName = suggestedOptionNameContainer.value;
context.logger.warn(
`Unknown option name "${optionName}", did you mean "${suggestedOptionName}"?`
);
return options[suggestedOptionNameContainer.index];
}
context.logger.warn(`Unknown option name "${optionName}"`);
return options.find(option => option.name === "help");
}
function createChoiceUsages(choices, margin, indentation) {
const activeChoices = choices.filter(
choice => !choice.deprecated && choice.since !== null
@ -692,11 +657,9 @@ function createChoiceUsages(choices, margin, indentation) {
);
}
function createDetailedUsage(context, optionName) {
const option = getOptionWithLevenSuggestion(
context,
getOptionsWithOpposites(context.detailedOptions),
optionName
function createDetailedUsage(context, flag) {
const option = getOptionsWithOpposites(context.detailedOptions).find(
option => option.name === flag || option.alias === flag
);
const header = createOptionUsageHeader(option);

View File

@ -1,6 +1,8 @@
"use strict";
const vnopts = require("vnopts");
const leven = require("leven");
const chalk = require("chalk");
const cliDescriptor = {
key: key => (key.length === 1 ? `-${key}` : `--${key}`),
@ -15,6 +17,35 @@ const cliDescriptor = {
: `${cliDescriptor.key(key)}=${value}`
};
class FlagSchema extends vnopts.ChoiceSchema {
constructor({ name, flags }) {
super({ name, choices: flags });
this._flags = flags.slice().sort();
}
preprocess(value, utils) {
if (
typeof value === "string" &&
value.length !== 0 &&
this._flags.indexOf(value) === -1
) {
const suggestion = this._flags.find(flag => leven(flag, value) < 3);
if (suggestion) {
utils.logger.warn(
[
`Unknown flag ${chalk.yellow(utils.descriptor.value(value))},`,
`did you mean ${chalk.blue(utils.descriptor.value(suggestion))}?`
].join(" ")
);
return suggestion;
}
}
return value;
}
expected() {
return "a flag";
}
}
function normalizeOptions(
options,
optionInfos,
@ -40,7 +71,7 @@ function optionInfosToSchemas(optionInfos, { isCLI }) {
}
for (const optionInfo of optionInfos) {
schemas.push(optionInfoToSchema(optionInfo, { isCLI }));
schemas.push(optionInfoToSchema(optionInfo, { isCLI, optionInfos }));
if (optionInfo.alias && isCLI) {
schemas.push(
@ -55,7 +86,7 @@ function optionInfosToSchemas(optionInfos, { isCLI }) {
return schemas;
}
function optionInfoToSchema(optionInfo, { isCLI }) {
function optionInfoToSchema(optionInfo, { isCLI, optionInfos }) {
let SchemaConstructor;
const parameters = { name: optionInfo.name };
const handlers = {};
@ -84,6 +115,17 @@ function optionInfoToSchema(optionInfo, { isCLI }) {
SchemaConstructor = vnopts.BooleanSchema;
break;
case "flag":
SchemaConstructor = FlagSchema;
parameters.flags = optionInfos
.map(optionInfo =>
[].concat(
optionInfo.alias || [],
optionInfo.description ? optionInfo.name : [],
optionInfo.oppositeDescription ? `no-${optionInfo.name}` : []
)
)
.reduce((a, b) => a.concat(b), []);
break;
case "path":
SchemaConstructor = vnopts.StringSchema;
break;

View File

@ -146,21 +146,8 @@ exports[`show version with --version (stderr) 1`] = `""`;
exports[`show version with --version (write) 1`] = `Array []`;
exports[`show warning with --help not-found (stderr) 1`] = `
"[warn] Unknown option name \\"not-found\\"
"
`;
exports[`show warning with --help not-found (stdout) 1`] = `
"-h, --help <flag>
Show CLI usage, or details about the given flag.
Example: --help write
"
`;
exports[`show warning with --help not-found (typo) (stderr) 1`] = `
"[warn] Unknown option name \\"parserr\\", did you mean \\"parser\\"?
"[warn] Unknown flag \\"parserr\\", did you mean \\"parser\\"?
"
`;
@ -190,8 +177,6 @@ Valid options:
exports[`show warning with --help not-found (typo) (write) 1`] = `Array []`;
exports[`show warning with --help not-found (write) 1`] = `Array []`;
exports[`throw error and show usage with something unexpected (stderr) 1`] = `""`;
exports[`throw error and show usage with something unexpected (stdout) 1`] = `
@ -311,6 +296,15 @@ exports[`throw error with --find-config-path + multiple files (stdout) 1`] = `""
exports[`throw error with --find-config-path + multiple files (write) 1`] = `Array []`;
exports[`throw error with --help not-found (stderr) 1`] = `
"[error] Invalid --help value. Expected a flag, but received \\"not-found\\".
"
`;
exports[`throw error with --help not-found (stdout) 1`] = `""`;
exports[`throw error with --help not-found (write) 1`] = `Array []`;
exports[`throw error with --write + --debug-check (stderr) 1`] = `
"[error] Cannot use --write and --debug-check together.
"

View File

@ -44,9 +44,9 @@ describe(`show detailed usage with plugin options (manual resolution)`, () => {
});
});
describe("show warning with --help not-found", () => {
describe("throw error with --help not-found", () => {
runPrettier("cli", ["--help", "not-found"]).test({
status: 0
status: 1
});
});