2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2017-04-12 23:41:51 +03:00
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
const fs = require("fs");
|
2017-05-08 15:57:19 +03:00
|
|
|
const extname = require("path").extname;
|
2017-10-02 18:58:55 +03:00
|
|
|
const prettier = require("./require_prettier");
|
2017-06-08 01:48:34 +03:00
|
|
|
const parser = require("../src/parser");
|
2017-05-21 18:13:11 +03:00
|
|
|
const massageAST = require("../src/clean-ast.js").massageAST;
|
2016-12-23 22:31:38 +03:00
|
|
|
|
2017-05-09 20:32:01 +03:00
|
|
|
const AST_COMPARE = process.env["AST_COMPARE"];
|
2017-03-25 18:10:17 +03:00
|
|
|
const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false;
|
2017-04-13 17:08:55 +03:00
|
|
|
const ALL_PARSERS = process.env["ALL_PARSERS"]
|
|
|
|
? JSON.parse(process.env["ALL_PARSERS"])
|
2017-06-05 22:34:08 +03:00
|
|
|
: ["flow", "graphql", "babylon", "typescript"];
|
2016-12-30 08:01:44 +03:00
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
function run_spec(dirname, options, additionalParsers) {
|
2016-12-23 22:31:38 +03:00
|
|
|
fs.readdirSync(dirname).forEach(filename => {
|
2017-06-11 06:10:34 +03:00
|
|
|
const path = dirname + "/" + filename;
|
2017-06-05 22:34:08 +03:00
|
|
|
if (
|
2017-06-11 06:10:34 +03:00
|
|
|
extname(filename) !== ".snap" &&
|
|
|
|
fs.lstatSync(path).isFile() &&
|
2017-06-05 22:34:08 +03:00
|
|
|
filename !== "jsfmt.spec.js"
|
|
|
|
) {
|
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
|
|
|
let rangeStart = 0;
|
|
|
|
let rangeEnd = Infinity;
|
|
|
|
const source = read(path)
|
|
|
|
.replace(/\r\n/g, "\n")
|
|
|
|
.replace("<<<PRETTIER_RANGE_START>>>", (match, offset) => {
|
|
|
|
rangeStart = offset;
|
|
|
|
return "";
|
|
|
|
})
|
|
|
|
.replace("<<<PRETTIER_RANGE_END>>>", (match, offset) => {
|
|
|
|
rangeEnd = offset;
|
|
|
|
return "";
|
|
|
|
});
|
2016-12-23 22:31:38 +03:00
|
|
|
|
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
|
|
|
const mergedOptions = Object.assign(mergeDefaultOptions(options || {}), {
|
|
|
|
rangeStart: rangeStart,
|
|
|
|
rangeEnd: rangeEnd
|
|
|
|
});
|
2017-05-09 06:23:51 +03:00
|
|
|
const output = prettyprint(source, path, mergedOptions);
|
2017-10-08 10:04:40 +03:00
|
|
|
test(`${filename} - ${mergedOptions.parser}-verify`, () => {
|
2017-05-09 06:23:51 +03:00
|
|
|
expect(raw(source + "~".repeat(80) + "\n" + output)).toMatchSnapshot(
|
|
|
|
filename
|
|
|
|
);
|
|
|
|
});
|
2017-03-25 18:10:17 +03:00
|
|
|
|
2017-05-09 06:23:51 +03:00
|
|
|
getParsersToVerify(
|
|
|
|
mergedOptions.parser,
|
|
|
|
additionalParsers || []
|
|
|
|
).forEach(parserName => {
|
|
|
|
test(`${filename} - ${parserName}-verify`, () => {
|
|
|
|
const verifyOptions = Object.assign(mergedOptions, {
|
|
|
|
parser: parserName
|
2017-03-25 18:10:17 +03:00
|
|
|
});
|
2017-05-09 06:23:51 +03:00
|
|
|
const verifyOutput = prettyprint(source, path, verifyOptions);
|
|
|
|
expect(output).toEqual(verifyOutput);
|
2016-12-23 22:31:38 +03:00
|
|
|
});
|
2017-05-09 06:23:51 +03:00
|
|
|
});
|
2016-12-23 22:31:38 +03:00
|
|
|
|
2017-05-09 20:32:01 +03:00
|
|
|
if (AST_COMPARE) {
|
2017-05-09 04:16:35 +03:00
|
|
|
const ast = parse(source, mergedOptions);
|
|
|
|
const astMassaged = massageAST(ast);
|
|
|
|
let ppastMassaged;
|
2016-12-27 21:28:04 +03:00
|
|
|
let pperr = null;
|
|
|
|
try {
|
2017-05-21 18:13:11 +03:00
|
|
|
const ppast = parse(
|
|
|
|
prettyprint(source, path, mergedOptions),
|
|
|
|
mergedOptions
|
|
|
|
);
|
2017-05-09 04:16:35 +03:00
|
|
|
ppastMassaged = massageAST(ppast);
|
2017-03-25 18:10:17 +03:00
|
|
|
} catch (e) {
|
2016-12-27 21:28:04 +03:00
|
|
|
pperr = e.stack;
|
|
|
|
}
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
test(path + " parse", () => {
|
2016-12-27 21:28:04 +03:00
|
|
|
expect(pperr).toBe(null);
|
2017-05-09 04:16:35 +03:00
|
|
|
expect(ppastMassaged).toBeDefined();
|
|
|
|
if (!ast.errors || ast.errors.length === 0) {
|
|
|
|
expect(astMassaged).toEqual(ppastMassaged);
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-23 22:31:38 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
global.run_spec = run_spec;
|
|
|
|
|
|
|
|
function stripLocation(ast) {
|
|
|
|
if (Array.isArray(ast)) {
|
|
|
|
return ast.map(e => stripLocation(e));
|
|
|
|
}
|
2017-03-25 18:10:17 +03:00
|
|
|
if (typeof ast === "object") {
|
2016-12-23 22:31:38 +03:00
|
|
|
const newObj = {};
|
2017-05-21 22:11:09 +03:00
|
|
|
for (const key in ast) {
|
2017-03-25 18:10:17 +03:00
|
|
|
if (
|
2017-05-21 18:13:11 +03:00
|
|
|
key === "loc" ||
|
|
|
|
key === "range" ||
|
|
|
|
key === "raw" ||
|
2017-06-10 05:45:39 +03:00
|
|
|
key === "comments" ||
|
|
|
|
key === "parent" ||
|
|
|
|
key === "prev"
|
2017-03-25 18:10:17 +03:00
|
|
|
) {
|
2016-12-23 22:31:38 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
newObj[key] = stripLocation(ast[key]);
|
|
|
|
}
|
|
|
|
return newObj;
|
|
|
|
}
|
|
|
|
return ast;
|
|
|
|
}
|
|
|
|
|
2017-05-09 04:16:35 +03:00
|
|
|
function parse(string, opts) {
|
|
|
|
return stripLocation(parser.parse(string, opts));
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 09:29:59 +03:00
|
|
|
function prettyprint(src, filename, options) {
|
2017-03-25 18:10:17 +03:00
|
|
|
return prettier.format(
|
|
|
|
src,
|
|
|
|
Object.assign(
|
|
|
|
{
|
2017-06-02 00:40:49 +03:00
|
|
|
filepath: filename
|
2017-03-25 18:10:17 +03:00
|
|
|
},
|
|
|
|
options
|
|
|
|
)
|
|
|
|
);
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function read(filename) {
|
2017-03-25 18:10:17 +03:00
|
|
|
return fs.readFileSync(filename, "utf8");
|
|
|
|
}
|
|
|
|
|
2017-04-12 23:41:51 +03:00
|
|
|
/**
|
|
|
|
* Wraps a string in a marker object that is used by `./raw-serializer.js` to
|
|
|
|
* directly print that string in a snapshot without escaping all double quotes.
|
|
|
|
* Backticks will still be escaped.
|
|
|
|
*/
|
|
|
|
function raw(string) {
|
|
|
|
if (typeof string !== "string") {
|
|
|
|
throw new Error("Raw snapshots have to be strings.");
|
|
|
|
}
|
|
|
|
return { [Symbol.for("raw")]: string };
|
|
|
|
}
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
function mergeDefaultOptions(parserConfig) {
|
|
|
|
return Object.assign(
|
|
|
|
{
|
|
|
|
parser: "flow",
|
|
|
|
printWidth: 80
|
|
|
|
},
|
|
|
|
parserConfig
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getParsersToVerify(parser, additionalParsers) {
|
|
|
|
if (VERIFY_ALL_PARSERS) {
|
2017-05-09 03:27:39 +03:00
|
|
|
return ALL_PARSERS.splice(ALL_PARSERS.indexOf(parser), 1);
|
2017-03-25 18:10:17 +03:00
|
|
|
}
|
|
|
|
return additionalParsers;
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|