Implement cosmiconfig for workspace configuration (#2434)
* Implement cosmiconfig
* Add resolveOptions API and extname support
* Add --resolve-config and --config, rename resolveOptions to resolveConfig
* Move color to top-level CLI options
* Fix unknown param warning
* Change from {} to null when no config is found
* Change override API to emulate eslint
* Add test for eslint-style overrides
* Delete overrides from resolveConfig
This commit is contained in:
1 parent
3728749d92
commit
dcccfed366
21 files changed
+525
-64
No files matched your search
+152
-60
@@ -2,24 +2,41 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const chalk = require("chalk");
|
||||
const dashify = require("dashify");
|
||||
const fs = require("fs");
|
||||
const getStream = require("get-stream");
|
||||
const globby = require("globby");
|
||||
const chalk = require("chalk");
|
||||
const minimist = require("minimist");
|
||||
const path = require("path");
|
||||
const readline = require("readline");
|
||||
const prettier = eval("require")("../index");
|
||||
const cleanAST = require("../src/clean-ast.js").cleanAST;
|
||||
|
||||
const argv = minimist(process.argv.slice(2), {
|
||||
const prettier = eval("require")("../index");
|
||||
const cleanAST = require("../src/clean-ast").cleanAST;
|
||||
const resolver = require("../src/resolve-config");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const booleanOptionNames = [
|
||||
"use-tabs",
|
||||
"semi",
|
||||
"single-quote",
|
||||
"bracket-spacing",
|
||||
"jsx-bracket-same-line",
|
||||
// Deprecated in 0.0.10
|
||||
"flow-parser"
|
||||
];
|
||||
const stringOptionNames = [
|
||||
"print-width",
|
||||
"tab-width",
|
||||
"parser",
|
||||
"trailing-comma"
|
||||
];
|
||||
|
||||
const argv = minimist(args, {
|
||||
boolean: [
|
||||
"write",
|
||||
"stdin",
|
||||
"use-tabs",
|
||||
"semi",
|
||||
"single-quote",
|
||||
"bracket-spacing",
|
||||
"jsx-bracket-same-line",
|
||||
// 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.
|
||||
@@ -30,31 +47,34 @@ const argv = minimist(process.argv.slice(2), {
|
||||
"version",
|
||||
"debug-print-doc",
|
||||
"debug-check",
|
||||
"with-node-modules",
|
||||
// Deprecated in 0.0.10
|
||||
"flow-parser"
|
||||
"with-node-modules"
|
||||
],
|
||||
string: [
|
||||
"print-width",
|
||||
"tab-width",
|
||||
"parser",
|
||||
"trailing-comma",
|
||||
"cursor-offset",
|
||||
"range-start",
|
||||
"range-end",
|
||||
"stdin-filepath"
|
||||
"stdin-filepath",
|
||||
"config",
|
||||
"resolve-config"
|
||||
],
|
||||
default: {
|
||||
semi: true,
|
||||
color: true,
|
||||
"bracket-spacing": true,
|
||||
parser: "babylon"
|
||||
color: true
|
||||
},
|
||||
alias: {
|
||||
help: "h",
|
||||
version: "v",
|
||||
"list-different": "l"
|
||||
},
|
||||
alias: { help: "h", version: "v", "list-different": "l" },
|
||||
unknown: param => {
|
||||
if (param.startsWith("-")) {
|
||||
console.warn("Ignored unknown option: " + param + "\n");
|
||||
return false;
|
||||
const paramName = param.substring(2);
|
||||
if (
|
||||
booleanOptionNames.indexOf(paramName) === -1 &&
|
||||
stringOptionNames.indexOf(paramName) === -1
|
||||
) {
|
||||
console.warn("Ignored unknown option: " + param + "\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -78,9 +98,56 @@ if (write && argv["debug-check"]) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getParserOption() {
|
||||
const optionName = "parser";
|
||||
const value = argv[optionName];
|
||||
if (argv["resolve-config"] && filepatterns.length) {
|
||||
console.error("Cannot use --resolve-config with multiple files");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getOptionsForFile(filePath) {
|
||||
return resolver
|
||||
.resolveConfig(filePath, { configFile: argv["config"] })
|
||||
.then(options => {
|
||||
const parsedArgs = minimist(args, {
|
||||
boolean: booleanOptionNames,
|
||||
string: stringOptionNames,
|
||||
default: Object.assign(
|
||||
{
|
||||
semi: true,
|
||||
"bracket-spacing": true,
|
||||
parser: "babylon"
|
||||
},
|
||||
dashifyObject(options)
|
||||
)
|
||||
});
|
||||
|
||||
return getOptions(Object.assign({}, argv, parsedArgs));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Invalid configuration file:", error.toString());
|
||||
process.exit(2);
|
||||
});
|
||||
}
|
||||
|
||||
function getOptions(argv) {
|
||||
return {
|
||||
cursorOffset: getIntOption(argv, "cursor-offset"),
|
||||
rangeStart: getIntOption(argv, "range-start"),
|
||||
rangeEnd: getIntOption(argv, "range-end"),
|
||||
useTabs: argv["use-tabs"],
|
||||
semi: argv["semi"],
|
||||
printWidth: getIntOption(argv, "print-width"),
|
||||
tabWidth: getIntOption(argv, "tab-width"),
|
||||
bracketSpacing: argv["bracket-spacing"],
|
||||
singleQuote: argv["single-quote"],
|
||||
jsxBracketSameLine: argv["jsx-bracket-same-line"],
|
||||
filepath: argv["stdin-filepath"],
|
||||
trailingComma: getTrailingComma(argv),
|
||||
parser: getParserOption(argv)
|
||||
};
|
||||
}
|
||||
|
||||
function getParserOption(argv) {
|
||||
const value = argv.parser;
|
||||
|
||||
if (value === undefined) {
|
||||
return value;
|
||||
@@ -95,7 +162,7 @@ function getParserOption() {
|
||||
return value;
|
||||
}
|
||||
|
||||
function getIntOption(optionName) {
|
||||
function getIntOption(argv, optionName) {
|
||||
const value = argv[optionName];
|
||||
|
||||
if (value === undefined) {
|
||||
@@ -115,7 +182,7 @@ function getIntOption(optionName) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getTrailingComma() {
|
||||
function getTrailingComma(argv) {
|
||||
switch (argv["trailing-comma"]) {
|
||||
case undefined:
|
||||
case "none":
|
||||
@@ -135,21 +202,12 @@ function getTrailingComma() {
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
cursorOffset: getIntOption("cursor-offset"),
|
||||
rangeStart: getIntOption("range-start"),
|
||||
rangeEnd: getIntOption("range-end"),
|
||||
useTabs: argv["use-tabs"],
|
||||
semi: argv["semi"],
|
||||
printWidth: getIntOption("print-width"),
|
||||
tabWidth: getIntOption("tab-width"),
|
||||
bracketSpacing: argv["bracket-spacing"],
|
||||
singleQuote: argv["single-quote"],
|
||||
jsxBracketSameLine: argv["jsx-bracket-same-line"],
|
||||
filepath: argv["stdin-filepath"],
|
||||
trailingComma: getTrailingComma(),
|
||||
parser: getParserOption()
|
||||
};
|
||||
function dashifyObject(object) {
|
||||
return Object.keys(object || {}).reduce((output, key) => {
|
||||
output[dashify(key)] = object[key];
|
||||
return output;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function diff(a, b) {
|
||||
return require("diff").createTwoFilesPatch("", "", a, b, "", "", {
|
||||
@@ -214,12 +272,17 @@ function handleError(filename, e) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
|
||||
if (argv["help"] || (!filepatterns.length && !stdin)) {
|
||||
if (
|
||||
argv["help"] ||
|
||||
(!filepatterns.length && !stdin && !argv["resolve-config"])
|
||||
) {
|
||||
console.log(
|
||||
"Usage: prettier [opts] [filename ...]\n\n" +
|
||||
"Available options:\n" +
|
||||
" --write Edit the file in-place. (Beware!)\n" +
|
||||
" --list-different or -l Print filenames of files that are different from Prettier formatting.\n" +
|
||||
" --config Path to a prettier configuration file (.prettierrc, package.json, prettier.config.js).\n" +
|
||||
" --resolve-config <path> Resolve the path to a configuration file for a given input file.\n" +
|
||||
" --stdin Read input from stdin.\n" +
|
||||
" --stdin-filepath Path to the file used to read from stdin.\n" +
|
||||
" --print-width <int> Specify the length of line that the printer will wrap on. Defaults to 80.\n" +
|
||||
@@ -251,20 +314,24 @@ if (argv["help"] || (!filepatterns.length && !stdin)) {
|
||||
process.exit(argv["help"] ? 0 : 1);
|
||||
}
|
||||
|
||||
if (stdin) {
|
||||
if (argv["resolve-config"]) {
|
||||
resolveConfig(argv["resolve-config"]);
|
||||
} else if (stdin) {
|
||||
getStream(process.stdin).then(input => {
|
||||
if (listDifferent(input, options, "(stdin)")) {
|
||||
return;
|
||||
}
|
||||
getOptionsForFile(process.cwd()).then(options => {
|
||||
if (listDifferent(input, options, "(stdin)")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
writeOutput(format(input, options));
|
||||
} catch (e) {
|
||||
handleError("stdin", e);
|
||||
}
|
||||
try {
|
||||
writeOutput(format(input, options), options);
|
||||
} catch (e) {
|
||||
handleError("stdin", e);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
eachFilename(filepatterns, filename => {
|
||||
eachFilename(filepatterns, (filename, options) => {
|
||||
if (write) {
|
||||
// Don't use `console.log` here since we need to replace this line.
|
||||
process.stdout.write(filename);
|
||||
@@ -337,7 +404,7 @@ if (stdin) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
} else if (!argv["list-different"]) {
|
||||
writeOutput(result);
|
||||
writeOutput(result, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -359,7 +426,17 @@ function listDifferent(input, options, filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function writeOutput(result) {
|
||||
function resolveConfig(filePath) {
|
||||
resolver.resolveConfigFile(filePath).then(configFile => {
|
||||
if (configFile) {
|
||||
console.log(path.relative(process.cwd(), configFile));
|
||||
} else {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeOutput(result, options) {
|
||||
// Don't use `console.log` here since it adds an extra newline at the end.
|
||||
process.stdout.write(result.formatted);
|
||||
|
||||
@@ -382,9 +459,11 @@ function eachFilename(patterns, callback) {
|
||||
process.exitCode = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
filePaths.forEach(filePath => {
|
||||
return callback(filePath);
|
||||
// Use map series to ensure idempotency
|
||||
mapSeries(filePaths, filePath => {
|
||||
return getOptionsForFile(filePath).then(options =>
|
||||
callback(filePath, options)
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -395,3 +474,16 @@ function eachFilename(patterns, callback) {
|
||||
process.exitCode = 2;
|
||||
});
|
||||
}
|
||||
|
||||
function mapSeries(array, iteratee) {
|
||||
let current = Promise.resolve();
|
||||
|
||||
const promises = array.map((item, i) => {
|
||||
current = current.then(() => {
|
||||
return iteratee(item, i, array);
|
||||
});
|
||||
return current;
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
Reference in new issue
Block a user