refactor(options): use supportOptions to generate CLI options (#3622)

* refactor(cli-constant): use supportOptions

* refactor(options): use supportOptions

* refactor(cli-util): use supportOptions

* fix: do not infer parser in multiparser

* chore: remove unnecessary package

* chore: trigger another travis build

* test: add kebab-case test to ensure no regression

* test: update snapshots
master
Ika 2018-01-18 15:26:27 +08:00 committed by GitHub
parent 3f788d4fb6
commit dc26445e51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 701 additions and 744 deletions

View File

@ -36,7 +36,6 @@
"graphql": "0.12.3",
"ignore": "3.3.7",
"jest-docblock": "21.3.0-beta.11",
"jest-validate": "21.1.0",
"leven": "2.1.0",
"mem": "1.1.0",
"minimatch": "3.0.4",

View File

@ -1,7 +1,8 @@
"use strict";
const camelCase = require("camelcase");
const dedent = require("dedent");
const dashify = require("dashify");
const getSupportInfo = require("../common/support").getSupportInfo;
const CATEGORY_CONFIG = "Config";
const CATEGORY_EDITOR = "Editor";
@ -64,318 +65,187 @@ const categoryOrder = [
* // If the option has a value that is an exception to the regular value
* // constraints, indicate that value here (or use a function for more
* // flexibility).
* exception?: any | ((value: any) => boolean);
* exception?: ((value: any) => boolean);
*
* // Indicate that the option is deprecated. Use a string to add an extra
* // message to --help for the option, for example to suggest a replacement
* // option.
* deprecated?: true | string;
*
* // Custom function to get the value for the option. Useful for handling
* // deprecated options.
* // --parser example: (value, argv) => argv["flow-parser"] ? "flow" : value
* getter?: (value: any, argv: any) => any;
* }
* }
*
* Note: The options below are sorted alphabetically.
*/
const detailedOptions = normalizeDetailedOptions({
"arrow-parens": {
type: "choice",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Include parentheses around a sole arrow function parameter.",
choices: [
{
value: "avoid",
description: "Omit parens when possible. Example: `x => x`"
},
{
value: "always",
description: "Always include parens. Example: `(x) => x`"
const detailedOptions = normalizeDetailedOptions(
Object.assign(
getSupportInfo(null, {
showDeprecated: true,
showUnreleased: true
}).options.reduce((reduced, option) => {
const newOption = Object.assign({}, option, {
name: dashify(option.name),
forwardToApi: option.name
});
switch (option.name) {
case "filepath":
Object.assign(newOption, {
name: "stdin-filepath",
description: "Path to the file to pretend that stdin comes from."
});
break;
case "useFlowParser":
newOption.name = "flow-parser";
break;
case "plugins":
newOption.name = "plugin";
break;
}
]
},
"bracket-spacing": {
type: "boolean",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Print spaces between brackets.",
oppositeDescription: "Do not print spaces between brackets."
},
color: {
// The supports-color package (a sub sub dependency) looks directly at
// `process.argv` for `--no-color` and such-like options. The reason it is
// listed here is to avoid "Ignored unknown option: --no-color" warnings.
// See https://github.com/chalk/supports-color/#info for more information.
type: "boolean",
default: true,
description: "Colorize error messages.",
oppositeDescription: "Do not colorize error messages."
},
config: {
type: "path",
category: CATEGORY_CONFIG,
description:
"Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js).",
oppositeDescription: "Do not look for a configuration file."
},
"config-precedence": {
type: "choice",
category: CATEGORY_CONFIG,
default: "cli-override",
choices: [
{
value: "cli-override",
description: "CLI options take precedence over config file"
switch (newOption.name) {
case "cursor-offset":
case "range-start":
case "range-end":
newOption.category = CATEGORY_EDITOR;
break;
case "stdin-filepath":
case "insert-pragma":
case "require-pragma":
newOption.category = CATEGORY_OTHER;
break;
case "plugin":
newOption.category = CATEGORY_CONFIG;
break;
default:
newOption.category = CATEGORY_FORMAT;
break;
}
if (option.deprecated) {
delete newOption.forwardToApi;
delete newOption.description;
delete newOption.oppositeDescription;
newOption.deprecated = true;
}
return Object.assign(reduced, { [newOption.name]: newOption });
}, {}),
{
color: {
// The supports-color package (a sub sub dependency) looks directly at
// `process.argv` for `--no-color` and such-like options. The reason it is
// listed here is to avoid "Ignored unknown option: --no-color" warnings.
// See https://github.com/chalk/supports-color/#info for more information.
type: "boolean",
default: true,
description: "Colorize error messages.",
oppositeDescription: "Do not colorize error messages."
},
{
value: "file-override",
description: "Config file take precedence over CLI options"
config: {
type: "path",
category: CATEGORY_CONFIG,
description:
"Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js).",
oppositeDescription: "Do not look for a configuration file."
},
{
value: "prefer-file",
"config-precedence": {
type: "choice",
category: CATEGORY_CONFIG,
default: "cli-override",
choices: [
{
value: "cli-override",
description: "CLI options take precedence over config file"
},
{
value: "file-override",
description: "Config file take precedence over CLI options"
},
{
value: "prefer-file",
description: dedent`
If a config file is found will evaluate it and ignore other CLI options.
If no config file is found CLI options will evaluate as normal.
`
}
],
description:
"Define in which order config files and CLI options should be evaluated."
},
"debug-check": {
type: "boolean"
},
"debug-print-doc": {
type: "boolean"
},
editorconfig: {
type: "boolean",
category: CATEGORY_CONFIG,
description:
"Take .editorconfig into account when parsing configuration.",
oppositeDescription:
"Don't take .editorconfig into account when parsing configuration.",
default: true
},
"find-config-path": {
type: "path",
category: CATEGORY_CONFIG,
description:
"Find and print the path to a configuration file for the given input file."
},
help: {
type: "flag",
alias: "h",
description: dedent`
If a config file is found will evaluate it and ignore other CLI options.
If no config file is found CLI options will evaluate as normal.
Show CLI usage, or details about the given flag.
Example: --help write
`
},
"ignore-path": {
type: "path",
category: CATEGORY_CONFIG,
default: ".prettierignore",
description: "Path to a file with patterns describing files to ignore."
},
"list-different": {
type: "boolean",
category: CATEGORY_OUTPUT,
alias: "l",
description:
"Print the names of files that are different from Prettier's formatting."
},
loglevel: {
type: "choice",
description: "What level of logs to report.",
default: "log",
choices: ["silent", "error", "warn", "log", "debug"]
},
stdin: {
type: "boolean",
description: "Force reading input from stdin."
},
"support-info": {
type: "boolean",
description: "Print support information as JSON."
},
version: {
type: "boolean",
alias: "v",
description: "Print Prettier version."
},
"with-node-modules": {
type: "boolean",
category: CATEGORY_CONFIG,
description: "Process files inside 'node_modules' directory."
},
write: {
type: "boolean",
category: CATEGORY_OUTPUT,
description: "Edit files in-place. (Beware!)"
}
],
description:
"Define in which order config files and CLI options should be evaluated."
},
"cursor-offset": {
type: "int",
category: CATEGORY_EDITOR,
exception: -1,
forwardToApi: true,
description: dedent`
Print (to stderr) where a cursor at the given position would move to after formatting.
This option cannot be used with --range-start and --range-end.
`
},
"debug-check": {
type: "boolean"
},
"debug-print-doc": {
type: "boolean"
},
editorconfig: {
type: "boolean",
category: CATEGORY_CONFIG,
description: "Take .editorconfig into account when parsing configuration.",
oppositeDescription:
"Don't take .editorconfig into account when parsing configuration.",
default: true
},
"find-config-path": {
type: "path",
category: CATEGORY_CONFIG,
description:
"Find and print the path to a configuration file for the given input file."
},
"flow-parser": {
// Deprecated in 0.0.10
type: "boolean",
category: CATEGORY_FORMAT,
deprecated: "Use `--parser flow` instead."
},
help: {
type: "flag",
alias: "h",
description: dedent`
Show CLI usage, or details about the given flag.
Example: --help write
`
},
"ignore-path": {
type: "path",
category: CATEGORY_CONFIG,
default: ".prettierignore",
description: "Path to a file with patterns describing files to ignore."
},
"insert-pragma": {
type: "boolean",
forwardToApi: true,
description: dedent`
Insert @format pragma into file's first docblock comment.
`
},
"jsx-bracket-same-line": {
type: "boolean",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Put > on the last line instead of at a new line."
},
"list-different": {
type: "boolean",
category: CATEGORY_OUTPUT,
alias: "l",
description:
"Print the names of files that are different from Prettier's formatting."
},
loglevel: {
type: "choice",
description: "What level of logs to report.",
default: "log",
choices: ["silent", "error", "warn", "log", "debug"]
},
parser: {
type: "choice",
category: CATEGORY_FORMAT,
forwardToApi: true,
exception: value => typeof value === "string", // Allow path to a parser module.
choices: [
"flow",
"babylon",
"typescript",
"css",
{ value: "postcss", deprecated: true, redirect: "css" },
"less",
"scss",
"json",
// "glimmer",
"graphql",
"markdown",
"vue"
],
description: "Which parser to use.",
getter: (value, argv) => (argv["flow-parser"] ? "flow" : value)
},
plugin: {
type: "path",
category: CATEGORY_CONFIG,
description:
"Add a plugin. Multiple plugins can be passed as separate `--plugin`s.",
forwardToApi: "plugins",
array: true
},
"print-width": {
type: "int",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "The line length where Prettier will try wrap."
},
"prose-wrap": {
type: "choice",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "How to wrap prose. (markdown)",
choices: [
{
value: "always",
description: "Wrap prose if it exceeds the print width."
},
{ value: "never", description: "Do not wrap prose." },
{ value: "preserve", description: "Wrap prose as-is." },
{ value: false, deprecated: true, redirect: "never" }
]
},
"range-end": {
type: "int",
category: CATEGORY_EDITOR,
forwardToApi: true,
exception: Infinity,
description: dedent`
Format code ending at a given character offset (exclusive).
The range will extend forwards to the end of the selected statement.
This option cannot be used with --cursor-offset.
`
},
"range-start": {
type: "int",
category: CATEGORY_EDITOR,
forwardToApi: true,
description: dedent`
Format code starting at a given character offset.
The range will extend backwards to the start of the first line containing the selected statement.
This option cannot be used with --cursor-offset.
`
},
"require-pragma": {
type: "boolean",
forwardToApi: true,
description: dedent`
Require either '@prettier' or '@format' to be present in the file's first docblock comment
in order for it to be formatted.
`
},
semi: {
type: "boolean",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Print semicolons.",
oppositeDescription:
"Do not print semicolons, except at the beginning of lines which may need them."
},
"single-quote": {
type: "boolean",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Use single quotes instead of double quotes."
},
stdin: {
type: "boolean",
description: "Force reading input from stdin."
},
"stdin-filepath": {
type: "path",
forwardToApi: "filepath",
description: "Path to the file to pretend that stdin comes from."
},
"support-info": {
type: "boolean",
description: "Print support information as JSON."
},
"tab-width": {
type: "int",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Number of spaces per indentation level."
},
"trailing-comma": {
type: "choice",
category: CATEGORY_FORMAT,
forwardToApi: true,
choices: [
{ value: "none", description: "No trailing commas." },
{
value: "es5",
description:
"Trailing commas where valid in ES5 (objects, arrays, etc.)"
},
{
value: "all",
description:
"Trailing commas wherever possible (including function arguments)."
},
{ value: "", deprecated: true, redirect: "es5" }
],
description: "Print trailing commas wherever possible when multi-line."
},
"use-tabs": {
type: "boolean",
category: CATEGORY_FORMAT,
forwardToApi: true,
description: "Indent with tabs instead of spaces."
},
version: {
type: "boolean",
alias: "v",
description: "Print Prettier version."
},
"with-node-modules": {
type: "boolean",
category: CATEGORY_CONFIG,
description: "Process files inside 'node_modules' directory."
},
write: {
type: "boolean",
category: CATEGORY_OUTPUT,
description: "Edit files in-place. (Beware!)"
}
});
}
)
);
const minimistOptions = {
boolean: detailedOptions
@ -385,6 +255,7 @@ const minimistOptions = {
.filter(option => option.type !== "boolean")
.map(option => option.name),
default: detailedOptions
.filter(option => !option.deprecated)
.filter(option => option.default !== undefined)
.reduce(
(current, option) =>
@ -415,20 +286,18 @@ function normalizeDetailedOptions(rawDetailedOptions) {
return Object.assign({}, option, {
name,
category: option.category || CATEGORY_OTHER,
forwardToApi:
option.forwardToApi &&
(typeof option.forwardToApi === "string"
? option.forwardToApi
: camelCase(name)),
choices:
option.choices &&
option.choices.map(choice =>
Object.assign(
option.choices.map(choice => {
const newChoice = Object.assign(
{ description: "", deprecated: false },
typeof choice === "object" ? choice : { value: choice }
)
),
getter: option.getter || (value => value)
);
if (newChoice.value === true) {
newChoice.value = ""; // backward compability for original boolean option
}
return newChoice;
})
});
});

View File

@ -5,58 +5,76 @@ const minimist = require("minimist");
const prettier = require("../../index");
const constant = require("./constant");
const util = require("./util");
const validator = require("./validator");
const normalizer = require("../main/options-normalizer");
const logger = require("./logger");
function run(args) {
const rawArgv = minimist(args, constant.minimistOptions);
try {
const rawArgv = minimist(args, constant.minimistOptions);
process.env[logger.ENV_LOG_LEVEL] =
rawArgv["loglevel"] || constant.detailedOptionMap["loglevel"].default;
process.env[logger.ENV_LOG_LEVEL] =
rawArgv["loglevel"] || constant.detailedOptionMap["loglevel"].default;
const argv = util.normalizeConfig("cli", rawArgv);
logger.debug(`normalized argv: ${JSON.stringify(argv)}`);
argv.__args = args;
argv.__filePatterns = argv["_"];
validator.validateArgv(argv);
if (argv["version"]) {
logger.log(prettier.version);
process.exit(0);
}
if (argv["help"] !== undefined) {
logger.log(
typeof argv["help"] === "string" && argv["help"] !== ""
? util.createDetailedUsage(argv["help"])
: util.createUsage()
const argv = normalizer.normalizeCliOptions(
rawArgv,
constant.detailedOptions,
{ logger }
);
process.exit(0);
}
if (argv["support-info"]) {
logger.log(
prettier.format(JSON.stringify(prettier.getSupportInfo()), {
parser: "json"
})
);
process.exit(0);
}
logger.debug(`normalized argv: ${JSON.stringify(argv)}`);
const hasFilePatterns = argv.__filePatterns.length !== 0;
const useStdin = argv["stdin"] || (!hasFilePatterns && !process.stdin.isTTY);
argv.__args = args;
argv.__filePatterns = argv["_"];
if (argv["find-config-path"]) {
util.logResolvedConfigPathOrDie(argv["find-config-path"]);
} else if (useStdin) {
util.formatStdin(argv);
} else if (hasFilePatterns) {
util.formatFiles(argv);
} else {
logger.log(util.createUsage());
if (argv["write"] && argv["debug-check"]) {
logger.error("Cannot use --write and --debug-check together.");
process.exit(1);
}
if (argv["find-config-path"] && argv.__filePatterns.length) {
logger.error("Cannot use --find-config-path with multiple files");
process.exit(1);
}
if (argv["version"]) {
logger.log(prettier.version);
process.exit(0);
}
if (argv["help"] !== undefined) {
logger.log(
typeof argv["help"] === "string" && argv["help"] !== ""
? util.createDetailedUsage(argv["help"])
: util.createUsage()
);
process.exit(0);
}
if (argv["support-info"]) {
logger.log(
prettier.format(JSON.stringify(prettier.getSupportInfo()), {
parser: "json"
})
);
process.exit(0);
}
const hasFilePatterns = argv.__filePatterns.length !== 0;
const useStdin =
argv["stdin"] || (!hasFilePatterns && !process.stdin.isTTY);
if (argv["find-config-path"]) {
util.logResolvedConfigPathOrDie(argv["find-config-path"]);
} else if (useStdin) {
util.formatStdin(argv);
} else if (hasFilePatterns) {
util.formatFiles(argv);
} else {
logger.log(util.createUsage());
process.exit(1);
}
} catch (error) {
logger.error(error.message);
process.exit(1);
}
}

View File

@ -16,27 +16,28 @@ const cleanAST = require("../common/clean-ast").cleanAST;
const errors = require("../common/errors");
const resolver = require("../config/resolve-config");
const constant = require("./constant");
const validator = require("./validator");
const options = require("../main/options");
const apiDefaultOptions = options.defaults;
const normalizeOptions = options.normalize;
const optionsModule = require("../main/options");
const apiDefaultOptions = optionsModule.defaults;
const optionsNormalizer = require("../main/options-normalizer");
const logger = require("./logger");
const thirdParty = require("../common/third-party");
const optionInfos = require("../common/support").getSupportInfo(null, {
showDeprecated: true,
showUnreleased: true
}).options;
const OPTION_USAGE_THRESHOLD = 25;
const CHOICE_USAGE_MARGIN = 3;
const CHOICE_USAGE_INDENTATION = 2;
function getOptions(argv) {
return constant.detailedOptions.filter(option => option.forwardToApi).reduce(
(current, option) =>
Object.assign(current, {
[option.forwardToApi]: option.array
? [].concat(argv[option.name] || [])
: argv[option.name]
}),
{}
);
return constant.detailedOptions
.filter(option => option.forwardToApi)
.reduce(
(current, option) =>
Object.assign(current, { [option.forwardToApi]: argv[option.name] }),
{}
);
}
function cliifyOptions(object) {
@ -130,7 +131,7 @@ function format(argv, input, opt) {
"prettier(input) !== prettier(prettier(input))\n" + diff(pp, pppp)
);
} else {
const normalizedOpts = normalizeOptions(opt);
const normalizedOpts = optionsModule.normalize(opt);
const ast = cleanAST(
prettier.__debug.parse(input, opt).ast,
normalizedOpts
@ -192,7 +193,8 @@ function getOptionsForFile(argv, filepath) {
{ filepath },
applyConfigPrecedence(
argv,
options && normalizeConfig("api", options, constant.detailedOptionMap)
options &&
optionsNormalizer.normalizeApiOptions(options, optionInfos, { logger })
)
);
@ -205,8 +207,7 @@ function getOptionsForFile(argv, filepath) {
function parseArgsToOptions(argv, overrideDefaults) {
return getOptions(
normalizeConfig(
"cli",
optionsNormalizer.normalizeCliOptions(
minimist(
argv.__args,
Object.assign({
@ -219,7 +220,8 @@ function parseArgsToOptions(argv, overrideDefaults) {
)
})
),
{ warning: false }
constant.detailedOptions,
{ logger: false }
)
);
}
@ -463,12 +465,18 @@ function createOptionUsage(option, threshold) {
`${option.description}${
optionDefaultValue === undefined
? ""
: `\nDefaults to ${optionDefaultValue}.`
: `\nDefaults to ${createDefaultValueDisplay(optionDefaultValue)}.`
}`,
threshold
);
}
function createDefaultValueDisplay(value) {
return Array.isArray(value)
? `[${value.map(createDefaultValueDisplay).join(", ")}]`
: value;
}
function createOptionUsageHeader(option) {
const name = `--${option.name}`;
const alias = option.alias ? `-${option.alias},` : null;
@ -574,7 +582,7 @@ function createDetailedUsage(optionName) {
const optionDefaultValue = getOptionDefaultValue(option.name);
const defaults =
optionDefaultValue !== undefined
? `\n\nDefault: ${optionDefaultValue}`
? `\n\nDefault: ${createDefaultValueDisplay(optionDefaultValue)}`
: "";
return `${header}${description}${choices}${defaults}`;
@ -612,135 +620,11 @@ function groupBy(array, getKey) {
}, Object.create(null));
}
/** @param {'api' | 'cli'} type */
function normalizeConfig(type, rawConfig, options) {
if (type === "api" && rawConfig === null) {
return null;
}
options = options || {};
const consoleWarn =
options.warning === false ? () => {} : logger.warn.bind(logger);
const normalized = {};
Object.keys(rawConfig).forEach(rawKey => {
const rawValue = rawConfig[rawKey];
const key = type === "cli" ? rawKey : dashify(rawKey);
if (type === "cli" && key === "_") {
normalized[rawKey] = rawValue;
return;
}
if (type === "cli" && key.length === 1) {
// do nothing with alias
return;
}
let option = constant.detailedOptionMap[key];
if (type === "api" && option === undefined) {
option = constant.apiDetailedOptionMap[key];
}
// unknown option
if (option === undefined) {
if (type === "api") {
consoleWarn(`Ignored unknown option: ${rawKey}`);
} else {
const optionName = rawValue === false ? `no-${rawKey}` : rawKey;
consoleWarn(`Ignored unknown option: --${optionName}`);
}
return;
}
const value = getValue(rawValue, option);
if (option.exception !== undefined) {
if (typeof option.exception === "function") {
if (option.exception(value)) {
normalized[rawKey] = value;
return;
}
} else {
if (value === option.exception) {
normalized[rawKey] = value;
return;
}
}
}
try {
switch (option.type) {
case "int":
validator.validateIntOption(type, value, option);
normalized[rawKey] = Number(value);
break;
case "choice":
validator.validateChoiceOption(type, value, option);
normalized[rawKey] = value;
break;
default:
normalized[rawKey] = value;
break;
}
} catch (error) {
logger.error(error.message);
process.exit(2);
}
});
return normalized;
function getOptionName(option) {
return type === "cli" ? `--${option.name}` : camelCase(option.name);
}
function getRedirectName(option, choice) {
return type === "cli"
? `--${option.name}=${choice.redirect}`
: `{ ${camelCase(option.name)}: ${JSON.stringify(choice.redirect)} }`;
}
function getValue(rawValue, option) {
const optionName = getOptionName(option);
if (rawValue && option.deprecated) {
let warning = `\`${optionName}\` is deprecated.`;
if (typeof option.deprecated === "string") {
warning += ` ${option.deprecated}`;
}
consoleWarn(warning);
}
const value = option.getter(rawValue, rawConfig);
if (option.type === "choice") {
const choice = option.choices.find(choice => choice.value === rawValue);
if (choice !== undefined && choice.deprecated) {
const warningDescription =
rawValue === ""
? "without an argument"
: `with value \`${rawValue}\``;
const redirectName = getRedirectName(option, choice);
consoleWarn(
`\`${optionName}\` ${warningDescription} is deprecated. Prettier now treats it as: \`${redirectName}\`.`
);
return choice.redirect;
}
}
return value;
}
}
module.exports = {
logResolvedConfigPathOrDie,
format,
formatStdin,
formatFiles,
createUsage,
createDetailedUsage,
normalizeConfig
createDetailedUsage
};

View File

@ -1,55 +0,0 @@
"use strict";
const camelCase = require("camelcase");
const logger = require("./logger");
function validateArgv(argv) {
if (argv["write"] && argv["debug-check"]) {
logger.error("Cannot use --write and --debug-check together.");
process.exit(1);
}
if (argv["find-config-path"] && argv.__filePatterns.length) {
logger.error("Cannot use --find-config-path with multiple files");
process.exit(1);
}
}
function getOptionName(type, option) {
return type === "cli" ? `--${option.name}` : camelCase(option.name);
}
function validateIntOption(type, value, option) {
if (!/^\d+$/.test(value) || (type === "api" && typeof value !== "number")) {
const optionName = getOptionName(type, option);
throw new Error(
`Invalid ${optionName} value.\n` +
`Expected an integer, but received: ${JSON.stringify(value)}`
);
}
}
function validateChoiceOption(type, value, option) {
if (!option.choices.some(choice => choice.value === value)) {
const optionName = getOptionName(type, option);
throw new Error(
`Invalid option for ${optionName}.\n` +
`Expected ${getJoinedChoices()}, but received: ${JSON.stringify(value)}`
);
}
function getJoinedChoices() {
const choices = option.choices
.filter(choice => !choice.deprecated)
.map(choice => `"${choice.value}"`);
const head = choices.slice(0, -2);
const tail = choices.slice(-2);
return head.concat(tail.join(" or ")).join(", ");
}
}
module.exports = {
validateArgv,
validateIntOption,
validateChoiceOption
};

View File

@ -14,6 +14,7 @@ const CATEGORY_SPECIAL = "Special";
* @property {string} since - available since version
* @property {string} category
* @property {'int' | 'boolean' | 'choice' | 'path'} type
* @property {boolean} array - indicate it's an array of the specified type
* @property {boolean?} deprecated - deprecated since version
* @property {OptionRedirectInfo?} redirect - redirect deprecated option
* @property {string} description
@ -21,9 +22,10 @@ const CATEGORY_SPECIAL = "Special";
* @property {OptionValueInfo} default
* @property {OptionRangeInfo?} range - for type int
* @property {OptionChoiceInfo?} choices - for type choice
* @property {(value: any) => boolean} exception
*
* @typedef {number | boolean | string} OptionValue
* @typedef {OptionValue | Array<{ since: string, value: OptionValue}>} OptionValueInfo
* @typedef {OptionValue | [{ value: OptionValue[] }] | Array<{ since: string, value: OptionValue}>} OptionValueInfo
*
* @typedef {Object} OptionRedirectInfo
* @property {string} option
@ -75,9 +77,11 @@ const supportOptions = {
type: "choice",
default: "babylon",
description: "Which parser to use.",
exception: value =>
typeof value === "string" || typeof value === "function",
choices: [
{ value: "babylon", description: "JavaScript" },
{ value: "flow", description: "Flow" },
{ value: "babylon", description: "JavaScript" },
{ value: "typescript", since: "1.4.0", description: "TypeScript" },
{ value: "css", since: "1.7.1", description: "CSS" },
{
@ -91,9 +95,20 @@ const supportOptions = {
{ value: "scss", since: "1.7.1", description: "SCSS" },
{ value: "json", since: "1.5.0", description: "JSON" },
{ value: "graphql", since: "1.5.0", description: "GraphQL" },
{ value: "markdown", since: "1.8.0", description: "Markdown" }
{ value: "markdown", since: "1.8.0", description: "Markdown" },
{ value: "vue", since: "1.10.0", description: "Vue" }
]
},
plugins: {
since: "1.10.0",
type: "path",
array: true,
default: [{ value: [] }],
category: CATEGORY_GLOBAL,
description:
"Add a plugin. Multiple plugins can be passed as separate `--plugin`s.",
exception: value => typeof value === "string" || typeof value === "object"
},
printWidth: {
since: "0.0.0",
category: CATEGORY_GLOBAL,
@ -208,11 +223,14 @@ function getSupportInfo(version, opts) {
const newOption = Object.assign({}, option);
if (Array.isArray(newOption.default)) {
newOption.default = newOption.default
.filter(filterSince)
.sort((info1, info2) =>
semver.compare(info2.since, info1.since)
)[0].value;
newOption.default =
newOption.default.length === 1
? newOption.default[0].value
: newOption.default
.filter(filterSince)
.sort((info1, info2) =>
semver.compare(info2.since, info1.since)
)[0].value;
}
if (Array.isArray(newOption.choices)) {

View File

@ -34,7 +34,7 @@ function embed(path, print, textToDoc, options) {
return null;
function getParserName(lang) {
const supportInfo = support.getSupportInfo(undefined, {
const supportInfo = support.getSupportInfo(null, {
plugins: options.plugins,
pluginsLoaded: true
});

View File

@ -20,7 +20,8 @@ function textToDoc(text, partialNextOptions, parentOptions) {
Object.assign({}, parentOptions, partialNextOptions, {
parentParser: parentOptions.parser,
originalText: text
})
}),
{ passThrough: true, inferParser: false }
);
const result = require("./parser").parse(text, nextOptions);

View File

@ -0,0 +1,22 @@
"use strict";
function apiDescriptor(name, value) {
return arguments.length === 1
? JSON.stringify(name)
: `\`{ ${apiDescriptor(name)}: ${JSON.stringify(value)} }\``;
}
function cliDescriptor(name, value) {
return value === false
? `\`--no-${name}\``
: value === true || arguments.length === 1
? `\`--${name}\``
: value === ""
? `\`--${name}\` without an argument`
: `\`--${name}=${value}\``;
}
module.exports = {
apiDescriptor,
cliDescriptor
};

View File

@ -0,0 +1,153 @@
"use strict";
const leven = require("leven");
const validator = require("./options-validator");
const descriptors = require("./options-descriptor");
function normalizeOptions(options, optionInfos, opts) {
opts = opts || {};
const logger =
opts.logger === false
? { warn() {} }
: opts.logger !== undefined ? opts.logger : console;
const descriptor = opts.descriptor || descriptors.apiDescriptor;
const passThrough = opts.passThrough || [];
const optionInfoMap = optionInfos.reduce(
(reduced, optionInfo) =>
Object.assign(reduced, { [optionInfo.name]: optionInfo }),
{}
);
const normalizedOptions = Object.keys(options).reduce((newOptions, key) => {
const optionInfo = optionInfoMap[key];
let optionName = key;
let optionValue = options[key];
if (!optionInfo) {
if (passThrough === true || passThrough.indexOf(optionName) !== -1) {
newOptions[optionName] = optionValue;
} else {
logger.warn(
createUnknownOptionMessage(
optionName,
optionValue,
optionInfos,
descriptor
)
);
}
return newOptions;
}
if (!optionInfo.deprecated) {
optionValue = normalizeOption(optionValue, optionInfo);
} else if (typeof optionInfo.redirect === "string") {
logger.warn(createRedirectOptionMessage(optionInfo, descriptor));
optionName = optionInfo.redirect;
} else if (optionValue) {
logger.warn(createRedirectOptionMessage(optionInfo, descriptor));
optionValue = optionInfo.redirect.value;
optionName = optionInfo.redirect.option;
}
if (optionInfo.choices) {
const choiceInfo = optionInfo.choices.find(
choice => choice.value === optionValue
);
if (choiceInfo && choiceInfo.deprecated) {
logger.warn(
createRedirectChoiceMessage(optionInfo, choiceInfo, descriptor)
);
optionValue = choiceInfo.redirect;
}
}
if (optionInfo.array && !Array.isArray(optionValue)) {
optionValue = [optionValue];
}
if (optionValue !== optionInfo.default) {
validator.validateOption(optionValue, optionInfoMap[optionName], {
descriptor
});
}
newOptions[optionName] = optionValue;
return newOptions;
}, {});
return normalizedOptions;
}
function normalizeOption(option, optionInfo) {
return optionInfo.type === "int" ? Number(option) : option;
}
function createUnknownOptionMessage(key, value, optionInfos, descriptor) {
const messages = [`Ignored unknown option ${descriptor(key, value)}.`];
const suggestedOptionInfo = optionInfos.find(
optionInfo => leven(optionInfo.name, key) < 3
);
if (suggestedOptionInfo) {
messages.push(`Did you mean ${JSON.stringify(suggestedOptionInfo.name)}?`);
}
return messages.join(" ");
}
function createRedirectOptionMessage(optionInfo, descriptor) {
return `${descriptor(
optionInfo.name
)} is deprecated. Prettier now treats it as ${
typeof optionInfo.redirect === "string"
? descriptor(optionInfo.redirect)
: descriptor(optionInfo.redirect.option, optionInfo.redirect.value)
}.`;
}
function createRedirectChoiceMessage(optionInfo, choiceInfo, descriptor) {
return `${descriptor(
optionInfo.name,
choiceInfo.value
)} is deprecated. Prettier now treats it as ${descriptor(
optionInfo.name,
choiceInfo.redirect
)}.`;
}
function normalizeApiOptions(options, optionInfos, opts) {
return normalizeOptions(
options,
optionInfos,
Object.assign({ descriptor: descriptors.apiDescriptor }, opts)
);
}
function normalizeCliOptions(options, optionInfos, opts) {
const args = options["_"] || [];
const newOptions = normalizeOptions(
Object.keys(options).reduce(
(reduced, key) =>
Object.assign(
reduced,
key.length === 1 // omit alias
? null
: { [key]: options[key] }
),
{}
),
optionInfos,
Object.assign({ descriptor: descriptors.cliDescriptor }, opts)
);
newOptions["_"] = args;
return newOptions;
}
module.exports = {
normalizeApiOptions,
normalizeCliOptions
};

View File

@ -0,0 +1,81 @@
"use strict";
const descriptors = require("./options-descriptor");
function validateOption(value, optionInfo, opts) {
opts = opts || {};
const descriptor = opts.descriptor || descriptors.apiDescriptor;
if (
typeof optionInfo.exception === "function" &&
optionInfo.exception(value)
) {
return;
}
try {
validateOptionType(value, optionInfo);
} catch (error) {
throw new Error(
`Invalid \`${descriptor(optionInfo.name)}\` value. ${
error.message
}, but received \`${JSON.stringify(value)}\`.`
);
}
}
function validateOptionType(value, optionInfo) {
if (optionInfo.array) {
if (!Array.isArray(value)) {
throw new Error(`Expected an array`);
}
value.forEach(v =>
validateOptionType(v, Object.assign({}, optionInfo, { array: false }))
);
} else {
switch (optionInfo.type) {
case "int":
validateIntOption(value);
break;
case "boolean":
validateBooleanOption(value);
break;
case "choice":
validateChoiceOption(value, optionInfo.choices);
break;
}
}
}
function validateBooleanOption(value) {
if (typeof value !== "boolean") {
throw new Error(`Expected a boolean`);
}
}
function validateIntOption(value) {
if (
!(
typeof value === "number" &&
Math.floor(value) === value &&
value >= 0 &&
value !== Infinity
)
) {
throw new Error(`Expected an integer`);
}
}
function validateChoiceOption(value, choiceInfos) {
if (!choiceInfos.some(choiceInfo => choiceInfo.value === value)) {
const choices = choiceInfos
.filter(choiceInfo => !choiceInfo.deprecated)
.map(choiceInfo => JSON.stringify(choiceInfo.value))
.sort();
const head = choices.slice(0, -2);
const tail = choices.slice(-2);
throw new Error(`Expected ${head.concat(tail.join(" or ")).join(", ")}`);
}
}
module.exports = { validateOption };

View File

@ -1,142 +1,82 @@
"use strict";
const path = require("path");
const validate = require("jest-validate").validate;
const deprecatedConfig = require("./deprecated");
const getSupportInfo = require("../common/support").getSupportInfo;
const supportInfo = getSupportInfo(null, { showUnreleased: true });
const normalizer = require("./options-normalizer");
const loadPlugins = require("../common/load-plugins");
const resolveParser = require("./parser").resolveParser;
const getPrinter = require("./get-printer");
const defaults = {
cursorOffset: -1,
rangeStart: 0,
rangeEnd: Infinity,
useTabs: false,
tabWidth: 2,
printWidth: 80,
singleQuote: false,
trailingComma: "none",
bracketSpacing: true,
jsxBracketSameLine: false,
parser: "babylon",
parentParser: "",
insertPragma: false,
requirePragma: false,
semi: true,
proseWrap: "preserve",
arrowParens: "avoid",
plugins: [],
const hiddenDefaults = {
astFormat: "estree",
printer: {},
__inJsTemplate: false
printer: {}
};
const exampleConfig = Object.assign({}, defaults, {
filepath: "path/to/Filename",
printWidth: 80,
originalText: "text"
});
const defaults = supportInfo.options.reduce(
(reduced, optionInfo) =>
Object.assign(reduced, { [optionInfo.name]: optionInfo.default }),
Object.assign({}, hiddenDefaults)
);
// Copy options and fill in default values.
function normalize(options) {
const normalized = Object.assign({}, options || {});
const filepath = normalized.filepath;
function normalize(options, opts) {
opts = opts || {};
normalized.plugins = loadPlugins(normalized.plugins);
const rawOptions = Object.assign({}, options);
rawOptions.plugins = loadPlugins(rawOptions.plugins);
if (
filepath &&
!normalized.parentParser &&
(!normalized.parser || normalized.parser === defaults.parser)
) {
const extension = path.extname(filepath);
const filename = path.basename(filepath).toLowerCase();
const language = getSupportInfo(null, {
plugins: normalized.plugins,
pluginsLoaded: true
}).languages.find(
language =>
typeof language.since === "string" &&
(language.extensions.indexOf(extension) > -1 ||
(language.filenames &&
language.filenames.find(name => name.toLowerCase() === filename)))
);
if (language) {
normalized.parser = language.parsers[0];
if (opts.inferParser !== false) {
if (
rawOptions.filepath &&
(!rawOptions.parser || rawOptions.parser === defaults.parser)
) {
const inferredParser = inferParser(
rawOptions.filepath,
rawOptions.plugins
);
if (inferredParser) {
rawOptions.parser = inferredParser;
}
}
}
if (normalized.parser === "json") {
normalized.trailingComma = "none";
}
/* istanbul ignore if */
if (typeof normalized.trailingComma === "boolean") {
// Support a deprecated boolean type for the trailing comma config
// for a few versions. This code can be removed later.
normalized.trailingComma = "es5";
// eslint-disable-next-line no-console
console.warn(
"Warning: `trailingComma` without any argument is deprecated. " +
'Specify "none", "es5", or "all".'
);
}
/* istanbul ignore if */
if (typeof normalized.proseWrap === "boolean") {
normalized.proseWrap = normalized.proseWrap ? "always" : "never";
// eslint-disable-next-line no-console
console.warn(
"Warning: `proseWrap` with boolean value is deprecated. " +
'Use "always", "never", or "preserve" instead.'
);
}
/* istanbul ignore if */
if (normalized.parser === "postcss") {
normalized.parser = "css";
// eslint-disable-next-line no-console
console.warn(
'Warning: `parser` with value "postcss" is deprecated. ' +
'Use "css", "less" or "scss" instead.'
);
}
const parserBackup = normalized.parser;
if (typeof normalized.parser === "function") {
// Delete the function from the object to pass validation.
delete normalized.parser;
}
validate(normalized, { exampleConfig, deprecatedConfig });
// Restore the option back to a function;
normalized.parser = parserBackup;
// For backward compatibility. Deprecated in 0.0.10
/* istanbul ignore if */
if ("useFlowParser" in normalized) {
normalized.parser = normalized.useFlowParser ? "flow" : "babylon";
delete normalized.useFlowParser;
}
normalized.astFormat = resolveParser(normalized).astFormat;
normalized.printer = getPrinter(normalized);
rawOptions.astFormat = resolveParser(rawOptions).astFormat;
rawOptions.printer = getPrinter(rawOptions);
Object.keys(defaults).forEach(k => {
if (normalized[k] == null) {
normalized[k] = defaults[k];
if (rawOptions[k] == null) {
rawOptions[k] = defaults[k];
}
});
return normalized;
if (rawOptions.parser === "json") {
rawOptions.trailingComma = "none";
}
return normalizer.normalizeApiOptions(
rawOptions,
supportInfo.options,
Object.assign({ passThrough: Object.keys(hiddenDefaults) }, opts)
);
}
module.exports = { normalize, defaults };
function inferParser(filepath, plugins) {
const extension = path.extname(filepath);
const filename = path.basename(filepath).toLowerCase();
const language = getSupportInfo(null, {
plugins,
pluginsLoaded: true
}).languages.find(
language =>
typeof language.since === "string" &&
(language.extensions.indexOf(extension) > -1 ||
(language.filenames &&
language.filenames.find(name => name.toLowerCase() === filename)))
);
return language && language.parsers[0];
}
module.exports = { normalize, defaults, hiddenDefaults };

View File

@ -10,7 +10,7 @@ exports[`boolean flags do not swallow the next argument (stdout) 1`] = `
exports[`boolean flags do not swallow the next argument (write) 1`] = `Array []`;
exports[`deprecated option values are warned (stderr) 1`] = `
"[warn] \`--trailing-comma\` without an argument is deprecated. Prettier now treats it as: \`--trailing-comma=es5\`.
"[warn] \`--trailing-comma\` without an argument is deprecated. Prettier now treats it as \`--trailing-comma=es5\`.
"
`;
@ -22,7 +22,7 @@ exports[`deprecated option values are warned (stdout) 1`] = `
exports[`deprecated option values are warned (write) 1`] = `Array []`;
exports[`deprecated options are warned (stderr) 1`] = `
"[warn] \`--flow-parser\` is deprecated. Use \`--parser flow\` instead.
"[warn] \`--flow-parser\` is deprecated. Prettier now treats it as \`--parser=flow\`.
"
`;
@ -43,7 +43,7 @@ exports[`negated options work (stdout) 1`] = `
exports[`negated options work (write) 1`] = `Array []`;
exports[`unknown negated options are warned (stderr) 1`] = `
"[warn] Ignored unknown option: --no-unknown
"[warn] Ignored unknown option \`--no-unknown\`.
"
`;
@ -55,7 +55,7 @@ exports[`unknown negated options are warned (stdout) 1`] = `
exports[`unknown negated options are warned (write) 1`] = `Array []`;
exports[`unknown options are warned (stderr) 1`] = `
"[warn] Ignored unknown option: --unknown
"[warn] Ignored unknown option \`--unknown\`.
"
`;

View File

@ -1,7 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`show warning with kebab-case option key (stderr) 1`] = `
"[warn] Ignored unknown option \`{ \\"print-width\\": 3 }\`. Did you mean \\"printWidth\\"?
"
`;
exports[`show warning with kebab-case option key (stdout) 1`] = `""`;
exports[`show warning with kebab-case option key (write) 1`] = `Array []`;
exports[`show warning with unknown option (stderr) 1`] = `
"[warn] Ignored unknown option: hello
"[warn] Ignored unknown option \`{ \\"hello\\": \\"world\\" }\`.
"
`;
@ -19,8 +28,7 @@ exports[`throw error with invalid config format (stdout) 1`] = `""`;
exports[`throw error with invalid config format (write) 1`] = `Array []`;
exports[`throw error with invalid config option (int) (stderr) 1`] = `
"[error] Invalid tabWidth value.
[error] Expected an integer, but received: 0.5
"[error] Invalid \`\\"tabWidth\\"\` value. Expected an integer, but received \`0.5\`.
"
`;
@ -29,8 +37,7 @@ exports[`throw error with invalid config option (int) (stdout) 1`] = `""`;
exports[`throw error with invalid config option (int) (write) 1`] = `Array []`;
exports[`throw error with invalid config option (trailingComma) (stderr) 1`] = `
"[error] Invalid option for trailingComma.
[error] Expected \\"none\\", \\"es5\\" or \\"all\\", but received: \\"wow\\"
"[error] Invalid \`\\"trailingComma\\"\` value. Expected \\"all\\", \\"es5\\" or \\"none\\", but received \`\\"wow\\"\`.
"
`;
@ -39,8 +46,7 @@ exports[`throw error with invalid config option (trailingComma) (stdout) 1`] = `
exports[`throw error with invalid config option (trailingComma) (write) 1`] = `Array []`;
exports[`throw error with invalid config precedence option (configPrecedence) (stderr) 1`] = `
"[error] Invalid option for --config-precedence.
[error] Expected \\"cli-override\\", \\"file-override\\" or \\"prefer-file\\", but received: \\"option/configPrecedence\\"
"[error] Invalid \`\`--config-precedence\`\` value. Expected \\"cli-override\\", \\"file-override\\" or \\"prefer-file\\", but received \`\\"option/configPrecedence\\"\`.
"
`;

View File

@ -271,16 +271,16 @@ exports[`show detailed usage with --help parser (stdout) 1`] = `
Valid options:
flow
babylon
typescript
css
less
scss
json
graphql
markdown
vue
flow Flow
babylon JavaScript
typescript TypeScript
css CSS
less Less
scss SCSS
json JSON
graphql GraphQL
markdown Markdown
vue Vue
Default: babylon
"
@ -294,6 +294,8 @@ exports[`show detailed usage with --help plugin (stdout) 1`] = `
"--plugin <path>
Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
Default: []
"
`;
@ -565,6 +567,7 @@ Config options:
--ignore-path <path> Path to a file with patterns describing files to ignore.
Defaults to .prettierignore.
--plugin <path> Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
Defaults to [].
--with-node-modules Process files inside 'node_modules' directory.
Editor options:
@ -634,16 +637,16 @@ exports[`show warning with --help not-found (typo) (stdout) 1`] = `
Valid options:
flow
babylon
typescript
css
less
scss
json
graphql
markdown
vue
flow Flow
babylon JavaScript
typescript TypeScript
css CSS
less Less
scss SCSS
json JSON
graphql GraphQL
markdown Markdown
vue Vue
Default: babylon
"
@ -706,6 +709,7 @@ Config options:
--ignore-path <path> Path to a file with patterns describing files to ignore.
Defaults to .prettierignore.
--plugin <path> Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.
Defaults to [].
--with-node-modules Process files inside 'node_modules' directory.
Editor options:

View File

@ -17,8 +17,8 @@ exports[`API getSupportInfo() with version 0.0.0 -> 1.0.0 1`] = `
+ },
+ \\"parser\\": Object {
+ \\"choices\\": Array [
+ \\"babylon\\",
+ \\"flow\\",
+ \\"babylon\\",
+ ],
+ \\"default\\": \\"babylon\\",
+ \\"type\\": \\"choice\\",
@ -174,8 +174,8 @@ exports[`API getSupportInfo() with version 1.0.0 -> 1.4.0 1`] = `
},
\\"parser\\": Object {
\\"choices\\": Array [
\\"babylon\\",
\\"flow\\",
\\"babylon\\",
+ \\"typescript\\",
+ \\"postcss\\",
],
@ -238,8 +238,8 @@ exports[`API getSupportInfo() with version 1.4.0 -> 1.5.0 1`] = `
\\"JavaScript\\": Array [
@@ -47,10 +53,12 @@
\\"choices\\": Array [
\\"babylon\\",
\\"flow\\",
\\"babylon\\",
\\"typescript\\",
\\"postcss\\",
+ \\"json\\",
@ -287,8 +287,8 @@ exports[`API getSupportInfo() with version 1.5.0 -> 1.7.1 1`] = `
@@ -52,11 +52,13 @@
\\"parser\\": Object {
\\"choices\\": Array [
\\"babylon\\",
\\"flow\\",
\\"babylon\\",
\\"typescript\\",
- \\"postcss\\",
+ \\"css\\",
@ -419,7 +419,27 @@ exports[`API getSupportInfo() with version 1.8.2 -> undefined 1`] = `
\\"type\\": \\"boolean\\",
},
\\"cursorOffset\\": Object {
@@ -80,14 +91,15 @@
@@ -65,14 +76,19 @@
\\"less\\",
\\"scss\\",
\\"json\\",
\\"graphql\\",
\\"markdown\\",
+ \\"vue\\",
],
\\"default\\": \\"babylon\\",
\\"type\\": \\"choice\\",
},
+ \\"plugins\\": Object {
+ \\"default\\": Array [],
+ \\"type\\": \\"path\\",
+ },
\\"printWidth\\": Object {
\\"default\\": 80,
\\"range\\": Object {
\\"end\\": Infinity,
\\"start\\": 0,
@@ -80,14 +96,15 @@
},
\\"type\\": \\"int\\",
},
@ -695,8 +715,8 @@ exports[`CLI --support-info (stdout) 1`] = `
\\"default\\": \\"babylon\\",
\\"description\\": \\"Which parser to use.\\",
\\"choices\\": [
{ \\"value\\": \\"babylon\\", \\"description\\": \\"JavaScript\\" },
{ \\"value\\": \\"flow\\", \\"description\\": \\"Flow\\" },
{ \\"value\\": \\"babylon\\", \\"description\\": \\"JavaScript\\" },
{
\\"value\\": \\"typescript\\",
\\"since\\": \\"1.4.0\\",
@ -707,9 +727,20 @@ exports[`CLI --support-info (stdout) 1`] = `
{ \\"value\\": \\"scss\\", \\"since\\": \\"1.7.1\\", \\"description\\": \\"SCSS\\" },
{ \\"value\\": \\"json\\", \\"since\\": \\"1.5.0\\", \\"description\\": \\"JSON\\" },
{ \\"value\\": \\"graphql\\", \\"since\\": \\"1.5.0\\", \\"description\\": \\"GraphQL\\" },
{ \\"value\\": \\"markdown\\", \\"since\\": \\"1.8.0\\", \\"description\\": \\"Markdown\\" }
{ \\"value\\": \\"markdown\\", \\"since\\": \\"1.8.0\\", \\"description\\": \\"Markdown\\" },
{ \\"value\\": \\"vue\\", \\"since\\": \\"1.10.0\\", \\"description\\": \\"Vue\\" }
]
},
{
\\"name\\": \\"plugins\\",
\\"since\\": \\"1.10.0\\",
\\"type\\": \\"path\\",
\\"array\\": true,
\\"default\\": [],
\\"category\\": \\"Global\\",
\\"description\\":
\\"Add a plugin. Multiple plugins can be passed as separate \`--plugin\`s.\\"
},
{
\\"name\\": \\"printWidth\\",
\\"since\\": \\"0.0.0\\",

View File

@ -232,8 +232,7 @@ function rcYaml() {
exports[`CLI overrides take precedence without --config-precedence (write) 1`] = `Array []`;
exports[`CLI validate options with --config-precedence cli-override (stderr) 1`] = `
"[error] Invalid printWidth value.
[error] Expected an integer, but received: 0.5
"[error] Invalid \`\\"printWidth\\"\` value. Expected an integer, but received \`0.5\`.
"
`;
@ -242,8 +241,7 @@ exports[`CLI validate options with --config-precedence cli-override (stdout) 1`]
exports[`CLI validate options with --config-precedence cli-override (write) 1`] = `Array []`;
exports[`CLI validate options with --config-precedence file-override (stderr) 1`] = `
"[error] Invalid printWidth value.
[error] Expected an integer, but received: 0.5
"[error] Invalid \`\\"printWidth\\"\` value. Expected an integer, but received \`0.5\`.
"
`;
@ -252,8 +250,7 @@ exports[`CLI validate options with --config-precedence file-override (stdout) 1`
exports[`CLI validate options with --config-precedence file-override (write) 1`] = `Array []`;
exports[`CLI validate options with --config-precedence prefer-file (stderr) 1`] = `
"[error] Invalid printWidth value.
[error] Expected an integer, but received: 0.5
"[error] Invalid \`\\"printWidth\\"\` value. Expected an integer, but received \`0.5\`.
"
`;

View File

@ -45,3 +45,9 @@ describe("show warning with unknown option", () => {
status: 0
});
});
describe("show warning with kebab-case option key", () => {
runPrettier("cli/config/invalid", ["--config", "option/kebab-case"]).test({
status: 0
});
});

View File

@ -1,3 +1,3 @@
{
"config-precedence": "invalidValue"
"configPrecedence": "invalidValue"
}

View File

@ -0,0 +1,3 @@
{
"print-width": 3
}

View File

@ -1,3 +1,3 @@
{
"trailing-comma": "wow"
"trailingComma": "wow"
}

View File

@ -2593,10 +2593,6 @@ jest-get-type@21.3.0-beta.15:
version "21.3.0-beta.15"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.3.0-beta.15.tgz#d5a510c32683124576eaba69b66a1ba3c25ed67c"
jest-get-type@^21.0.2:
version "21.0.2"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.0.2.tgz#304e6b816dd33cd1f47aba0597bcad258a509fc6"
jest-get-type@^21.2.0:
version "21.2.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23"
@ -2742,15 +2738,6 @@ jest-util@^21.2.1:
jest-validate "^21.2.1"
mkdirp "^0.5.1"
jest-validate@21.1.0:
version "21.1.0"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.1.0.tgz#39d01115544a758bce49f221a5fcbb24ebdecc65"
dependencies:
chalk "^2.0.1"
jest-get-type "^21.0.2"
leven "^2.1.0"
pretty-format "^21.1.0"
jest-validate@^21.2.1:
version "21.2.1"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7"
@ -3601,13 +3588,6 @@ pretty-format@^20.0.3:
ansi-regex "^2.1.1"
ansi-styles "^3.0.0"
pretty-format@^21.1.0:
version "21.1.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.1.0.tgz#557428254323832ee8b7c971cb613442bea67f61"
dependencies:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
pretty-format@^21.2.1:
version "21.2.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36"