2016-11-29 20:14:10 +03:00
|
|
|
#!/usr/bin/env node
|
2017-01-18 01:16:40 +03:00
|
|
|
|
2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2017-01-28 18:50:22 +03:00
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
const chalk = require("chalk");
|
|
|
|
const dashify = require("dashify");
|
2016-11-29 20:14:10 +03:00
|
|
|
const fs = require("fs");
|
2017-06-15 02:57:29 +03:00
|
|
|
const getStream = require("get-stream");
|
2017-07-04 18:55:06 +03:00
|
|
|
const globby = require("globby");
|
2016-12-31 07:01:07 +03:00
|
|
|
const minimist = require("minimist");
|
2017-07-10 15:26:36 +03:00
|
|
|
const path = require("path");
|
2017-02-15 00:44:14 +03:00
|
|
|
const readline = require("readline");
|
2016-11-29 20:14:10 +03:00
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
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, {
|
2017-01-13 23:03:53 +03:00
|
|
|
boolean: [
|
|
|
|
"write",
|
|
|
|
"stdin",
|
2017-01-24 00:29:57 +03:00
|
|
|
// 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.
|
2017-01-23 20:10:51 +03:00
|
|
|
"color",
|
2017-03-03 06:47:36 +03:00
|
|
|
"list-different",
|
2017-01-26 21:13:32 +03:00
|
|
|
"help",
|
2017-01-20 01:35:12 +03:00
|
|
|
"version",
|
Add debugging support for doc IR (#347)
This PR adds two things:
`--debug-print-doc` command that prints the formatted doc
```js
echo "<div><</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
groupConditional(
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
[
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
indent(2, groupBreak([ hardline(), "<" ])),
hardline(),
"</",
"div",
">"
])
]
),
";",
hardline()
];
```
The ability to view the IR in real time on the browser display:
![image](https://cloud.githubusercontent.com/assets/197597/22134741/4f172f20-de7e-11e6-84bc-5f813976dc19.png)
The way it works is pretty cool, we take the doc IR and print a valid JavaScript string out of it, that we then send to prettier in order to make it look good :)
2017-01-20 20:54:32 +03:00
|
|
|
"debug-print-doc",
|
2017-02-07 18:04:44 +03:00
|
|
|
"debug-check",
|
2017-07-10 15:26:36 +03:00
|
|
|
"with-node-modules"
|
2017-01-20 01:35:12 +03:00
|
|
|
],
|
2017-05-21 21:00:16 +03:00
|
|
|
string: [
|
2017-06-02 01:52:29 +03:00
|
|
|
"cursor-offset",
|
2017-05-21 21:00:16 +03:00
|
|
|
"range-start",
|
2017-05-31 22:26:22 +03:00
|
|
|
"range-end",
|
2017-07-10 15:26:36 +03:00
|
|
|
"stdin-filepath",
|
|
|
|
"config",
|
|
|
|
"resolve-config"
|
2017-05-21 21:00:16 +03:00
|
|
|
],
|
|
|
|
default: {
|
2017-07-10 15:26:36 +03:00
|
|
|
color: true
|
|
|
|
},
|
|
|
|
alias: {
|
|
|
|
help: "h",
|
|
|
|
version: "v",
|
|
|
|
"list-different": "l"
|
2017-05-21 21:00:16 +03:00
|
|
|
},
|
2017-01-21 20:11:34 +03:00
|
|
|
unknown: param => {
|
|
|
|
if (param.startsWith("-")) {
|
2017-07-11 12:52:47 +03:00
|
|
|
const paramName = param.replace(/--(no-)?/, "");
|
2017-07-10 15:26:36 +03:00
|
|
|
if (
|
|
|
|
booleanOptionNames.indexOf(paramName) === -1 &&
|
|
|
|
stringOptionNames.indexOf(paramName) === -1
|
|
|
|
) {
|
|
|
|
console.warn("Ignored unknown option: " + param + "\n");
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-21 20:11:34 +03:00
|
|
|
}
|
|
|
|
}
|
2016-12-31 07:01:07 +03:00
|
|
|
});
|
|
|
|
|
2017-01-18 23:45:44 +03:00
|
|
|
if (argv["version"]) {
|
2017-01-22 03:42:13 +03:00
|
|
|
console.log(prettier.version);
|
2017-01-18 23:45:44 +03:00
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
|
2017-01-23 18:37:57 +03:00
|
|
|
const filepatterns = argv["_"];
|
2017-01-10 23:45:04 +03:00
|
|
|
const write = argv["write"];
|
2017-02-23 20:57:51 +03:00
|
|
|
const stdin = argv["stdin"] || (!filepatterns.length && !process.stdin.isTTY);
|
2017-05-24 00:19:04 +03:00
|
|
|
const ignoreNodeModules = argv["with-node-modules"] === false;
|
2017-07-04 18:55:06 +03:00
|
|
|
const ignoreNodeModulesGlobs = ["!**/node_modules/**", "!./node_modules/**"];
|
2017-05-24 00:19:04 +03:00
|
|
|
const globOptions = {
|
2017-06-01 22:27:56 +03:00
|
|
|
dot: true
|
2017-05-24 00:19:04 +03:00
|
|
|
};
|
2016-11-29 20:14:10 +03:00
|
|
|
|
2017-05-09 12:51:04 +03:00
|
|
|
if (write && argv["debug-check"]) {
|
|
|
|
console.error("Cannot use --write and --debug-check together.");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
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;
|
2017-01-26 21:13:32 +03:00
|
|
|
|
|
|
|
if (value === undefined) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2017-01-20 01:35:12 +03:00
|
|
|
// For backward compatibility. Deprecated in 0.0.10
|
|
|
|
if (argv["flow-parser"]) {
|
2017-01-21 20:23:29 +03:00
|
|
|
console.warn("`--flow-parser` is deprecated. Use `--parser flow` instead.");
|
2017-01-20 01:35:12 +03:00
|
|
|
return "flow";
|
|
|
|
}
|
|
|
|
|
2017-06-12 09:51:12 +03:00
|
|
|
return value;
|
2017-01-20 01:35:12 +03:00
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
function getIntOption(argv, optionName) {
|
2017-01-26 21:13:32 +03:00
|
|
|
const value = argv[optionName];
|
|
|
|
|
|
|
|
if (value === undefined) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (/^\d+$/.test(value)) {
|
|
|
|
return Number(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
console.error(
|
|
|
|
"Invalid --" +
|
|
|
|
optionName +
|
|
|
|
" value. Expected an integer, but received: " +
|
|
|
|
JSON.stringify(value)
|
|
|
|
);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
function getTrailingComma(argv) {
|
2017-02-18 06:44:55 +03:00
|
|
|
switch (argv["trailing-comma"]) {
|
|
|
|
case undefined:
|
|
|
|
case "none":
|
|
|
|
return "none";
|
|
|
|
case "":
|
2017-02-23 20:57:51 +03:00
|
|
|
console.warn(
|
|
|
|
"Warning: `--trailing-comma` was used without an argument. This is deprecated. " +
|
|
|
|
'Specify "none", "es5", or "all".'
|
|
|
|
);
|
2017-05-21 22:11:09 +03:00
|
|
|
return "es5";
|
2017-02-18 06:44:55 +03:00
|
|
|
case "es5":
|
|
|
|
return "es5";
|
|
|
|
case "all":
|
|
|
|
return "all";
|
|
|
|
default:
|
|
|
|
throw new Error("Invalid option for --trailing-comma");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
function dashifyObject(object) {
|
|
|
|
return Object.keys(object || {}).reduce((output, key) => {
|
|
|
|
output[dashify(key)] = object[key];
|
|
|
|
return output;
|
|
|
|
}, {});
|
|
|
|
}
|
Add debugging support for doc IR (#347)
This PR adds two things:
`--debug-print-doc` command that prints the formatted doc
```js
echo "<div><</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
groupConditional(
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
[
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
indent(2, groupBreak([ hardline(), "<" ])),
hardline(),
"</",
"div",
">"
])
]
),
";",
hardline()
];
```
The ability to view the IR in real time on the browser display:
![image](https://cloud.githubusercontent.com/assets/197597/22134741/4f172f20-de7e-11e6-84bc-5f813976dc19.png)
The way it works is pretty cool, we take the doc IR and print a valid JavaScript string out of it, that we then send to prettier in order to make it look good :)
2017-01-20 20:54:32 +03:00
|
|
|
|
2017-07-05 23:54:46 +03:00
|
|
|
function diff(a, b) {
|
|
|
|
return require("diff").createTwoFilesPatch("", "", a, b, "", "", {
|
|
|
|
context: 2
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-01 00:42:04 +03:00
|
|
|
function format(input, opt) {
|
Add debugging support for doc IR (#347)
This PR adds two things:
`--debug-print-doc` command that prints the formatted doc
```js
echo "<div><</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
groupConditional(
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
[
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
indent(2, groupBreak([ hardline(), "<" ])),
hardline(),
"</",
"div",
">"
])
]
),
";",
hardline()
];
```
The ability to view the IR in real time on the browser display:
![image](https://cloud.githubusercontent.com/assets/197597/22134741/4f172f20-de7e-11e6-84bc-5f813976dc19.png)
The way it works is pretty cool, we take the doc IR and print a valid JavaScript string out of it, that we then send to prettier in order to make it look good :)
2017-01-20 20:54:32 +03:00
|
|
|
if (argv["debug-print-doc"]) {
|
2017-06-01 00:42:04 +03:00
|
|
|
const doc = prettier.__debug.printToDoc(input, opt);
|
2017-07-05 19:25:51 +03:00
|
|
|
return { formatted: prettier.__debug.formatDoc(doc) };
|
Add debugging support for doc IR (#347)
This PR adds two things:
`--debug-print-doc` command that prints the formatted doc
```js
echo "<div><</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
groupConditional(
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
[
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
indent(2, groupBreak([ hardline(), "<" ])),
hardline(),
"</",
"div",
">"
])
]
),
";",
hardline()
];
```
The ability to view the IR in real time on the browser display:
![image](https://cloud.githubusercontent.com/assets/197597/22134741/4f172f20-de7e-11e6-84bc-5f813976dc19.png)
The way it works is pretty cool, we take the doc IR and print a valid JavaScript string out of it, that we then send to prettier in order to make it look good :)
2017-01-20 20:54:32 +03:00
|
|
|
}
|
2017-02-07 18:04:44 +03:00
|
|
|
|
|
|
|
if (argv["debug-check"]) {
|
2017-06-01 00:42:04 +03:00
|
|
|
const pp = prettier.format(input, opt);
|
|
|
|
const pppp = prettier.format(pp, opt);
|
2017-02-07 18:04:44 +03:00
|
|
|
if (pp !== pppp) {
|
2017-05-14 13:26:37 +03:00
|
|
|
throw "prettier(input) !== prettier(prettier(input))\n" + diff(pp, pppp);
|
2017-04-22 02:09:33 +03:00
|
|
|
} else {
|
2017-06-01 00:42:04 +03:00
|
|
|
const ast = cleanAST(prettier.__debug.parse(input, opt));
|
|
|
|
const past = cleanAST(prettier.__debug.parse(pp, opt));
|
2017-04-22 02:09:33 +03:00
|
|
|
|
|
|
|
if (ast !== past) {
|
2017-05-14 13:26:37 +03:00
|
|
|
const MAX_AST_SIZE = 2097152; // 2MB
|
2017-07-08 15:07:39 +03:00
|
|
|
const astDiff =
|
|
|
|
ast.length > MAX_AST_SIZE || past.length > MAX_AST_SIZE
|
|
|
|
? "AST diff too large to render"
|
|
|
|
: diff(ast, past);
|
2017-05-21 21:00:16 +03:00
|
|
|
throw "ast(input) !== ast(prettier(input))\n" +
|
|
|
|
astDiff +
|
|
|
|
"\n" +
|
|
|
|
diff(input, pp);
|
2017-04-22 02:09:33 +03:00
|
|
|
}
|
2017-02-07 18:04:44 +03:00
|
|
|
}
|
2017-06-07 02:10:24 +03:00
|
|
|
return { formatted: opt.filepath || "(stdin)\n" };
|
2017-02-07 18:04:44 +03:00
|
|
|
}
|
|
|
|
|
2017-06-02 01:52:29 +03:00
|
|
|
return prettier.formatWithCursor(input, opt);
|
2017-01-11 18:57:16 +03:00
|
|
|
}
|
2017-01-10 07:03:35 +03:00
|
|
|
|
2017-01-26 21:13:32 +03:00
|
|
|
function handleError(filename, e) {
|
|
|
|
const isParseError = Boolean(e && e.loc);
|
|
|
|
const isValidationError = /Validation Error/.test(e && e.message);
|
|
|
|
|
|
|
|
// For parse errors and validation errors, we only want to show the error
|
|
|
|
// message formatted in a nice way. `String(e)` takes care of that. Other
|
|
|
|
// (unexpected) errors are passed as-is as a separate argument to
|
|
|
|
// `console.error`. That includes the stack trace (if any), and shows a nice
|
|
|
|
// `util.inspect` of throws things that aren't `Error` objects. (The Flow
|
|
|
|
// parser has mistakenly thrown arrays sometimes.)
|
|
|
|
if (isParseError) {
|
|
|
|
console.error(filename + ": " + String(e));
|
|
|
|
} else if (isValidationError) {
|
|
|
|
console.error(String(e));
|
|
|
|
// If validation fails for one file, it will fail for all of them.
|
|
|
|
process.exit(1);
|
|
|
|
} else {
|
2017-05-28 23:54:54 +03:00
|
|
|
console.error(filename + ":", e.stack || e);
|
2017-01-26 21:13:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't exit the process if one file failed
|
|
|
|
process.exitCode = 2;
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
if (
|
|
|
|
argv["help"] ||
|
|
|
|
(!filepatterns.length && !stdin && !argv["resolve-config"])
|
|
|
|
) {
|
2017-02-18 06:44:55 +03:00
|
|
|
console.log(
|
|
|
|
"Usage: prettier [opts] [filename ...]\n\n" +
|
|
|
|
"Available options:\n" +
|
|
|
|
" --write Edit the file in-place. (Beware!)\n" +
|
2017-04-19 19:04:47 +03:00
|
|
|
" --list-different or -l Print filenames of files that are different from Prettier formatting.\n" +
|
2017-07-10 15:26:36 +03:00
|
|
|
" --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" +
|
2017-02-18 06:44:55 +03:00
|
|
|
" --stdin Read input from stdin.\n" +
|
2017-05-31 22:26:22 +03:00
|
|
|
" --stdin-filepath Path to the file used to read from stdin.\n" +
|
2017-02-18 06:44:55 +03:00
|
|
|
" --print-width <int> Specify the length of line that the printer will wrap on. Defaults to 80.\n" +
|
|
|
|
" --tab-width <int> Specify the number of spaces per indentation-level. Defaults to 2.\n" +
|
2017-04-19 19:04:47 +03:00
|
|
|
" --use-tabs Indent lines with tabs instead of spaces.\n" +
|
|
|
|
" --no-semi Do not print semicolons, except at the beginning of lines which may need them.\n" +
|
|
|
|
" --single-quote Use single quotes instead of double quotes.\n" +
|
|
|
|
" --no-bracket-spacing Do not print spaces between brackets.\n" +
|
|
|
|
" --jsx-bracket-same-line Put > on the last line instead of at a new line.\n" +
|
2017-02-18 06:44:55 +03:00
|
|
|
" --trailing-comma <none|es5|all>\n" +
|
|
|
|
" Print trailing commas wherever possible. Defaults to none.\n" +
|
2017-06-17 23:36:42 +03:00
|
|
|
" --parser <flow|babylon|typescript|postcss|json>\n" +
|
2017-06-02 23:42:20 +03:00
|
|
|
" Specify which parse to use. Defaults to babylon.\n" +
|
2017-06-02 01:52:29 +03:00
|
|
|
" --cursor-offset <int> Print (to stderr) where a cursor at the given position would move to after formatting.\n" +
|
|
|
|
" This option cannot be used with --range-start and --range-end\n" +
|
Find nearest node when formatting range (#1659)
* Move range extension code into helper functions
* Add findNodeByOffset() helper
This was adapted from https://github.com/prettier/prettier/pull/1637/commits/cbc1929c64db558b4e444500bca3d2ce1d550359
* Test extending formatted range to entire node
* Fix extending formatted range to entire node
* Fix style errors
* Add run_file test function
This makes it possible to use different options on a per-file basis,
which is useful for things like range formatting tests.
* Test extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Fix extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Test that external indentation is left alone when formatting a range
* Preserve external indentation when formatting a range
* Dedupe range formatting traversal callbacks
* Simplify range formatting traversal using ast-types
See https://github.com/prettier/prettier/pull/1659#issuecomment-302974798
* Make range formatting traversal more efficient
There's less unnecessary parsing now.
* Fix style errors
* Add test where range expanding fails
* Fix test where range expanding fails
This makes sure that the range contains the entirety of the nodes
containing each of the range's endpoints.
* Add test for expanding range to beginning of line
* Pass test for expanding range to beginning of line
This makes it so that indentation before the range is added to the
formatted range.
* Don't parse/stringify AST to detect pre-range indentation
See https://github.com/prettier/prettier/pull/1659#discussion_r117790671
* When formatting a range, find closest statement rather than parsing
The `isStatement` implementation came from `docs/prettier.min.js`.
See https://github.com/prettier/prettier/pull/1659#issuecomment-303154770
* Add test for range-formatting a FunctionDeclaration's argument object
* Include FunctionDeclaration when searching for nearest node to range-format
From the spec, a Program is a series of SourceElements, each of which is
either a Statement or a FunctionDeclaration. See
https://www.ecma-international.org/ecma-262/5.1/#sec-A.5
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659#discussion_r117810096
* Add tests with multiple statements
See https://github.com/prettier/prettier/pull/1659#discussion_r117810753
* Remove unnecessary arguments from findNodeByOffset()
* Contract format range to ensure it starts/ends on nodes
* Specify test ranges in the fixtures
See https://github.com/prettier/prettier/pull/1659#discussion_r117811186
* Remove unnecessary comments from range fixtures
* Remove run_file test function
It's no longer used. This essentially reverts
8241216e68f2e0da997a4f558b03658d642c89a2
* Update range formatting docs
Clarify that the range expands to the nearest statement, and not to the
end of the line.
* Don't overwrite test options when detecting range
Now that multiple files share the same object again, we shouldn't be
re-assigning to it.
* Reuse already-read fixtures for AST_COMPARE=1 tests
* Remove `run_file` global from test eslintrc
* Undo package.json churn
`yarn` reformatted it before, but the whitespace visually sets off the
comment, so let's put it back how it was before.
See https://github.com/prettier/prettier/pull/1659#discussion_r117864655
* Remove misleading comments from isSourceElement
See https://github.com/prettier/prettier/pull/1659#discussion_r117865196
* Loop backwards through string instead of reversing it
See https://github.com/prettier/prettier/pull/1659#discussion_r117865759
* Don't recompute indent string when formatting range
See https://github.com/prettier/prettier/pull/1659#discussion_r117867268
* Rename findNodeByOffset to findNodeAtOffset
"Find x by y" is the common usage for finding an `x` by a key `y`.
However, since "by" has positional meaning, let's use "at" instead.
See https://github.com/prettier/prettier/pull/1659#discussion_r117865121
* Always trimRight() in formatRange and explain why
See https://github.com/prettier/prettier/pull/1659#discussion_r117864635
* Test formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Fix formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659/files/e52db5e9f9ec4af599f658da03739e206dd4578c#r117878763
* Add test demonstrating range formatting indent detection
* Detect alignment from text on line before range, but don't reformat it
This avoids reformatting non-indentation that happens to precede the
range on the same line, while still correctly indenting the range based
on it.
See https://github.com/prettier/prettier/pull/1659#discussion_r117881430
2017-05-23 17:43:58 +03:00
|
|
|
" --range-start <int> Format code starting at a given character offset.\n" +
|
|
|
|
" The range will extend backwards to the start of the first line containing the selected statement.\n" +
|
2017-06-02 01:52:29 +03:00
|
|
|
" This option cannot be used with --cursor-offset.\n" +
|
Find nearest node when formatting range (#1659)
* Move range extension code into helper functions
* Add findNodeByOffset() helper
This was adapted from https://github.com/prettier/prettier/pull/1637/commits/cbc1929c64db558b4e444500bca3d2ce1d550359
* Test extending formatted range to entire node
* Fix extending formatted range to entire node
* Fix style errors
* Add run_file test function
This makes it possible to use different options on a per-file basis,
which is useful for things like range formatting tests.
* Test extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Fix extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Test that external indentation is left alone when formatting a range
* Preserve external indentation when formatting a range
* Dedupe range formatting traversal callbacks
* Simplify range formatting traversal using ast-types
See https://github.com/prettier/prettier/pull/1659#issuecomment-302974798
* Make range formatting traversal more efficient
There's less unnecessary parsing now.
* Fix style errors
* Add test where range expanding fails
* Fix test where range expanding fails
This makes sure that the range contains the entirety of the nodes
containing each of the range's endpoints.
* Add test for expanding range to beginning of line
* Pass test for expanding range to beginning of line
This makes it so that indentation before the range is added to the
formatted range.
* Don't parse/stringify AST to detect pre-range indentation
See https://github.com/prettier/prettier/pull/1659#discussion_r117790671
* When formatting a range, find closest statement rather than parsing
The `isStatement` implementation came from `docs/prettier.min.js`.
See https://github.com/prettier/prettier/pull/1659#issuecomment-303154770
* Add test for range-formatting a FunctionDeclaration's argument object
* Include FunctionDeclaration when searching for nearest node to range-format
From the spec, a Program is a series of SourceElements, each of which is
either a Statement or a FunctionDeclaration. See
https://www.ecma-international.org/ecma-262/5.1/#sec-A.5
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659#discussion_r117810096
* Add tests with multiple statements
See https://github.com/prettier/prettier/pull/1659#discussion_r117810753
* Remove unnecessary arguments from findNodeByOffset()
* Contract format range to ensure it starts/ends on nodes
* Specify test ranges in the fixtures
See https://github.com/prettier/prettier/pull/1659#discussion_r117811186
* Remove unnecessary comments from range fixtures
* Remove run_file test function
It's no longer used. This essentially reverts
8241216e68f2e0da997a4f558b03658d642c89a2
* Update range formatting docs
Clarify that the range expands to the nearest statement, and not to the
end of the line.
* Don't overwrite test options when detecting range
Now that multiple files share the same object again, we shouldn't be
re-assigning to it.
* Reuse already-read fixtures for AST_COMPARE=1 tests
* Remove `run_file` global from test eslintrc
* Undo package.json churn
`yarn` reformatted it before, but the whitespace visually sets off the
comment, so let's put it back how it was before.
See https://github.com/prettier/prettier/pull/1659#discussion_r117864655
* Remove misleading comments from isSourceElement
See https://github.com/prettier/prettier/pull/1659#discussion_r117865196
* Loop backwards through string instead of reversing it
See https://github.com/prettier/prettier/pull/1659#discussion_r117865759
* Don't recompute indent string when formatting range
See https://github.com/prettier/prettier/pull/1659#discussion_r117867268
* Rename findNodeByOffset to findNodeAtOffset
"Find x by y" is the common usage for finding an `x` by a key `y`.
However, since "by" has positional meaning, let's use "at" instead.
See https://github.com/prettier/prettier/pull/1659#discussion_r117865121
* Always trimRight() in formatRange and explain why
See https://github.com/prettier/prettier/pull/1659#discussion_r117864635
* Test formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Fix formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659/files/e52db5e9f9ec4af599f658da03739e206dd4578c#r117878763
* Add test demonstrating range formatting indent detection
* Detect alignment from text on line before range, but don't reformat it
This avoids reformatting non-indentation that happens to precede the
range on the same line, while still correctly indenting the range based
on it.
See https://github.com/prettier/prettier/pull/1659#discussion_r117881430
2017-05-23 17:43:58 +03:00
|
|
|
" Defaults to 0.\n" +
|
|
|
|
" --range-end <int> Format code ending at a given character offset (exclusive).\n" +
|
|
|
|
" The range will extend forwards to the end of the selected statement.\n" +
|
2017-06-02 01:52:29 +03:00
|
|
|
" This option cannot be used with --cursor-offset.\n" +
|
Find nearest node when formatting range (#1659)
* Move range extension code into helper functions
* Add findNodeByOffset() helper
This was adapted from https://github.com/prettier/prettier/pull/1637/commits/cbc1929c64db558b4e444500bca3d2ce1d550359
* Test extending formatted range to entire node
* Fix extending formatted range to entire node
* Fix style errors
* Add run_file test function
This makes it possible to use different options on a per-file basis,
which is useful for things like range formatting tests.
* Test extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Fix extending the format range to nearest parseable node
This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.
* Test that external indentation is left alone when formatting a range
* Preserve external indentation when formatting a range
* Dedupe range formatting traversal callbacks
* Simplify range formatting traversal using ast-types
See https://github.com/prettier/prettier/pull/1659#issuecomment-302974798
* Make range formatting traversal more efficient
There's less unnecessary parsing now.
* Fix style errors
* Add test where range expanding fails
* Fix test where range expanding fails
This makes sure that the range contains the entirety of the nodes
containing each of the range's endpoints.
* Add test for expanding range to beginning of line
* Pass test for expanding range to beginning of line
This makes it so that indentation before the range is added to the
formatted range.
* Don't parse/stringify AST to detect pre-range indentation
See https://github.com/prettier/prettier/pull/1659#discussion_r117790671
* When formatting a range, find closest statement rather than parsing
The `isStatement` implementation came from `docs/prettier.min.js`.
See https://github.com/prettier/prettier/pull/1659#issuecomment-303154770
* Add test for range-formatting a FunctionDeclaration's argument object
* Include FunctionDeclaration when searching for nearest node to range-format
From the spec, a Program is a series of SourceElements, each of which is
either a Statement or a FunctionDeclaration. See
https://www.ecma-international.org/ecma-262/5.1/#sec-A.5
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659#discussion_r117810096
* Add tests with multiple statements
See https://github.com/prettier/prettier/pull/1659#discussion_r117810753
* Remove unnecessary arguments from findNodeByOffset()
* Contract format range to ensure it starts/ends on nodes
* Specify test ranges in the fixtures
See https://github.com/prettier/prettier/pull/1659#discussion_r117811186
* Remove unnecessary comments from range fixtures
* Remove run_file test function
It's no longer used. This essentially reverts
8241216e68f2e0da997a4f558b03658d642c89a2
* Update range formatting docs
Clarify that the range expands to the nearest statement, and not to the
end of the line.
* Don't overwrite test options when detecting range
Now that multiple files share the same object again, we shouldn't be
re-assigning to it.
* Reuse already-read fixtures for AST_COMPARE=1 tests
* Remove `run_file` global from test eslintrc
* Undo package.json churn
`yarn` reformatted it before, but the whitespace visually sets off the
comment, so let's put it back how it was before.
See https://github.com/prettier/prettier/pull/1659#discussion_r117864655
* Remove misleading comments from isSourceElement
See https://github.com/prettier/prettier/pull/1659#discussion_r117865196
* Loop backwards through string instead of reversing it
See https://github.com/prettier/prettier/pull/1659#discussion_r117865759
* Don't recompute indent string when formatting range
See https://github.com/prettier/prettier/pull/1659#discussion_r117867268
* Rename findNodeByOffset to findNodeAtOffset
"Find x by y" is the common usage for finding an `x` by a key `y`.
However, since "by" has positional meaning, let's use "at" instead.
See https://github.com/prettier/prettier/pull/1659#discussion_r117865121
* Always trimRight() in formatRange and explain why
See https://github.com/prettier/prettier/pull/1659#discussion_r117864635
* Test formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Fix formatting a range that crosses AST levels
See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688
* Remove unnecessary try-catch
See https://github.com/prettier/prettier/pull/1659/files/e52db5e9f9ec4af599f658da03739e206dd4578c#r117878763
* Add test demonstrating range formatting indent detection
* Detect alignment from text on line before range, but don't reformat it
This avoids reformatting non-indentation that happens to precede the
range on the same line, while still correctly indenting the range based
on it.
See https://github.com/prettier/prettier/pull/1659#discussion_r117881430
2017-05-23 17:43:58 +03:00
|
|
|
" Defaults to Infinity.\n" +
|
2017-04-19 19:04:47 +03:00
|
|
|
" --no-color Do not colorize error messages.\n" +
|
2017-05-24 13:25:45 +03:00
|
|
|
" --with-node-modules Process files inside `node_modules` directory.\n" +
|
2017-04-19 19:04:47 +03:00
|
|
|
" --version or -v Print Prettier version.\n" +
|
|
|
|
"\n"
|
2017-02-18 06:44:55 +03:00
|
|
|
);
|
|
|
|
process.exit(argv["help"] ? 0 : 1);
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
if (argv["resolve-config"]) {
|
|
|
|
resolveConfig(argv["resolve-config"]);
|
|
|
|
} else if (stdin) {
|
2017-06-15 02:57:29 +03:00
|
|
|
getStream(process.stdin).then(input => {
|
2017-07-10 15:26:36 +03:00
|
|
|
getOptionsForFile(process.cwd()).then(options => {
|
|
|
|
if (listDifferent(input, options, "(stdin)")) {
|
|
|
|
return;
|
|
|
|
}
|
2017-07-04 21:41:46 +03:00
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
try {
|
|
|
|
writeOutput(format(input, options), options);
|
|
|
|
} catch (e) {
|
|
|
|
handleError("stdin", e);
|
|
|
|
}
|
|
|
|
});
|
2017-01-11 18:57:16 +03:00
|
|
|
});
|
|
|
|
} else {
|
2017-07-10 15:26:36 +03:00
|
|
|
eachFilename(filepatterns, (filename, options) => {
|
2017-06-07 02:10:24 +03:00
|
|
|
if (write) {
|
2017-03-20 20:14:33 +03:00
|
|
|
// Don't use `console.log` here since we need to replace this line.
|
|
|
|
process.stdout.write(filename);
|
|
|
|
}
|
2016-12-31 07:01:07 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
let input;
|
|
|
|
try {
|
|
|
|
input = fs.readFileSync(filename, "utf8");
|
2017-04-12 20:16:11 +03:00
|
|
|
} catch (e) {
|
2017-03-20 20:14:33 +03:00
|
|
|
// Add newline to split errors from filename line.
|
|
|
|
process.stdout.write("\n");
|
|
|
|
|
2017-04-14 01:49:53 +03:00
|
|
|
console.error("Unable to read file: " + filename + "\n" + e);
|
2017-03-20 20:14:33 +03:00
|
|
|
// Don't exit the process if one file failed
|
|
|
|
process.exitCode = 2;
|
|
|
|
return;
|
|
|
|
}
|
2017-02-07 22:03:48 +03:00
|
|
|
|
2017-07-04 21:41:46 +03:00
|
|
|
listDifferent(input, options, filename);
|
2017-04-03 19:54:10 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
const start = Date.now();
|
2017-01-11 18:57:16 +03:00
|
|
|
|
2017-06-02 01:52:29 +03:00
|
|
|
let result;
|
2017-03-20 20:14:33 +03:00
|
|
|
let output;
|
2017-02-07 22:03:48 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
try {
|
2017-06-02 01:52:29 +03:00
|
|
|
result = format(
|
2017-06-01 00:42:04 +03:00
|
|
|
input,
|
|
|
|
Object.assign({}, options, { filepath: filename })
|
|
|
|
);
|
2017-06-02 01:52:29 +03:00
|
|
|
output = result.formatted;
|
2017-03-20 20:14:33 +03:00
|
|
|
} catch (e) {
|
|
|
|
// Add newline to split errors from filename line.
|
|
|
|
process.stdout.write("\n");
|
2017-02-07 22:03:48 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
handleError(filename, e);
|
|
|
|
return;
|
|
|
|
}
|
2017-02-07 22:03:48 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
if (write) {
|
|
|
|
// Remove previously printed filename to log it with duration.
|
|
|
|
readline.clearLine(process.stdout, 0);
|
|
|
|
readline.cursorTo(process.stdout, 0, null);
|
2017-01-11 18:57:16 +03:00
|
|
|
|
2017-03-20 20:14:33 +03:00
|
|
|
// Don't write the file if it won't change in order not to invalidate
|
|
|
|
// mtime based caches.
|
|
|
|
if (output === input) {
|
2017-05-21 18:12:00 +03:00
|
|
|
if (!argv["list-different"]) {
|
|
|
|
console.log(chalk.grey("%s %dms"), filename, Date.now() - start);
|
|
|
|
}
|
2017-01-11 18:57:16 +03:00
|
|
|
} else {
|
2017-05-21 18:12:00 +03:00
|
|
|
if (argv["list-different"]) {
|
|
|
|
console.log(filename);
|
|
|
|
} else {
|
|
|
|
console.log("%s %dms", filename, Date.now() - start);
|
|
|
|
}
|
2017-03-20 20:14:33 +03:00
|
|
|
|
2017-04-16 00:06:20 +03:00
|
|
|
try {
|
|
|
|
fs.writeFileSync(filename, output, "utf8");
|
|
|
|
} catch (err) {
|
|
|
|
console.error("Unable to write file: " + filename + "\n" + err);
|
|
|
|
// Don't exit the process if one file failed
|
|
|
|
process.exitCode = 2;
|
|
|
|
}
|
2017-01-11 18:57:16 +03:00
|
|
|
}
|
2017-03-20 20:14:33 +03:00
|
|
|
} else if (argv["debug-check"]) {
|
|
|
|
if (output) {
|
|
|
|
console.log(output);
|
2017-05-09 12:51:04 +03:00
|
|
|
} else {
|
|
|
|
process.exitCode = 2;
|
2017-03-20 20:14:33 +03:00
|
|
|
}
|
2017-05-21 18:12:00 +03:00
|
|
|
} else if (!argv["list-different"]) {
|
2017-07-10 15:26:36 +03:00
|
|
|
writeOutput(result, options);
|
2017-03-20 20:14:33 +03:00
|
|
|
}
|
2017-01-10 23:45:04 +03:00
|
|
|
});
|
2017-01-11 18:57:16 +03:00
|
|
|
}
|
2017-01-23 18:37:57 +03:00
|
|
|
|
2017-07-04 21:41:46 +03:00
|
|
|
function listDifferent(input, options, filename) {
|
|
|
|
if (!argv["list-different"]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
options = Object.assign({}, options, { filepath: filename });
|
|
|
|
|
|
|
|
if (!prettier.check(input, options)) {
|
|
|
|
if (!write) {
|
|
|
|
console.log(filename);
|
|
|
|
}
|
|
|
|
process.exitCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-10 15:26:36 +03:00
|
|
|
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) {
|
2017-06-02 01:52:29 +03:00
|
|
|
// Don't use `console.log` here since it adds an extra newline at the end.
|
|
|
|
process.stdout.write(result.formatted);
|
|
|
|
|
|
|
|
if (options.cursorOffset) {
|
|
|
|
process.stderr.write(result.cursorOffset + "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-23 18:37:57 +03:00
|
|
|
function eachFilename(patterns, callback) {
|
2017-07-04 18:55:06 +03:00
|
|
|
if (ignoreNodeModules) {
|
|
|
|
patterns = patterns.concat(ignoreNodeModulesGlobs);
|
|
|
|
}
|
2017-04-17 07:12:49 +03:00
|
|
|
|
2017-07-04 18:55:06 +03:00
|
|
|
return globby(patterns, globOptions)
|
|
|
|
.then(filePaths => {
|
|
|
|
if (filePaths.length === 0) {
|
|
|
|
console.error(
|
|
|
|
"No matching files. Patterns tried: " + patterns.join(" ")
|
|
|
|
);
|
2017-01-23 18:37:57 +03:00
|
|
|
process.exitCode = 2;
|
|
|
|
return;
|
|
|
|
}
|
2017-07-10 15:26:36 +03:00
|
|
|
// Use map series to ensure idempotency
|
|
|
|
mapSeries(filePaths, filePath => {
|
|
|
|
return getOptionsForFile(filePath).then(options =>
|
|
|
|
callback(filePath, options)
|
|
|
|
);
|
2017-01-23 18:37:57 +03:00
|
|
|
});
|
2017-07-04 18:55:06 +03:00
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
console.error(
|
|
|
|
"Unable to expand glob patterns: " + patterns.join(" ") + "\n" + err
|
|
|
|
);
|
|
|
|
// Don't exit the process if one pattern failed
|
|
|
|
process.exitCode = 2;
|
2017-01-23 18:37:57 +03:00
|
|
|
});
|
2017-05-24 00:19:04 +03:00
|
|
|
}
|
2017-07-10 15:26:36 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|