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;
|
2018-09-01 07:11:19 +03:00
|
|
|
const raw = require("jest-snapshot-serializer-raw").wrap;
|
2016-12-23 22:31:38 +03:00
|
|
|
|
2017-05-09 20:32:01 +03:00
|
|
|
const AST_COMPARE = process.env["AST_COMPARE"];
|
2018-06-12 21:05:40 +03:00
|
|
|
const TEST_STANDALONE = process.env["TEST_STANDALONE"];
|
|
|
|
|
|
|
|
const prettier = !TEST_STANDALONE
|
|
|
|
? require("prettier/local")
|
|
|
|
: require("prettier/standalone");
|
2016-12-30 08:01:44 +03:00
|
|
|
|
2017-11-30 09:31:52 +03:00
|
|
|
function run_spec(dirname, parsers, options) {
|
|
|
|
/* instabul ignore if */
|
|
|
|
if (!parsers || !parsers.length) {
|
|
|
|
throw new Error(`No parsers were specified for ${dirname}`);
|
|
|
|
}
|
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
fs.readdirSync(dirname).forEach(filename => {
|
2018-06-12 21:05:40 +03:00
|
|
|
// We need to have a skipped test with the same name of the snapshots,
|
|
|
|
// so Jest doesn't mark them as obsolete.
|
|
|
|
if (TEST_STANDALONE && parsers.some(skipStandalone)) {
|
2018-07-04 22:54:39 +03:00
|
|
|
parsers.forEach(parser =>
|
|
|
|
test.skip(`${filename} - ${parser}-verify`, () => {})
|
|
|
|
);
|
2018-06-12 21:05:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
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-10-24 07:19:27 +03:00
|
|
|
filename[0] !== "." &&
|
2017-06-05 22:34:08 +03:00
|
|
|
filename !== "jsfmt.spec.js"
|
|
|
|
) {
|
2018-11-25 11:21:14 +03:00
|
|
|
let rangeStart;
|
|
|
|
let rangeEnd;
|
2018-05-01 15:42:59 +03:00
|
|
|
let cursorOffset;
|
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 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
|
|
|
|
2018-05-01 15:42:59 +03:00
|
|
|
const input = source.replace("<|>", (match, offset) => {
|
|
|
|
cursorOffset = offset;
|
|
|
|
return "";
|
|
|
|
});
|
|
|
|
|
2018-11-25 11:21:14 +03:00
|
|
|
const baseOptions = Object.assign(mergeDefaultOptions(options || {}), {
|
2018-05-01 15:42:59 +03:00
|
|
|
rangeStart,
|
|
|
|
rangeEnd,
|
|
|
|
cursorOffset
|
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
|
|
|
});
|
2018-11-25 11:21:14 +03:00
|
|
|
const mainOptions = Object.assign({}, baseOptions, {
|
|
|
|
parser: parsers[0]
|
|
|
|
});
|
|
|
|
const output = prettyprint(input, path, mainOptions);
|
|
|
|
test(filename, () => {
|
2017-12-29 00:25:11 +03:00
|
|
|
expect(
|
2018-11-25 11:21:14 +03:00
|
|
|
raw(
|
|
|
|
createSnapshot(
|
|
|
|
source,
|
|
|
|
output,
|
|
|
|
Object.assign({}, baseOptions, { parsers })
|
|
|
|
)
|
|
|
|
)
|
2018-07-03 09:06:29 +03:00
|
|
|
).toMatchSnapshot();
|
2017-05-09 06:23:51 +03:00
|
|
|
});
|
2017-03-25 18:10:17 +03:00
|
|
|
|
2018-05-01 15:42:59 +03:00
|
|
|
parsers.slice(1).forEach(parser => {
|
2018-11-25 11:21:14 +03:00
|
|
|
const verifyOptions = Object.assign({}, mainOptions, { parser });
|
2018-05-01 15:42:59 +03:00
|
|
|
test(`${filename} - ${parser}-verify`, () => {
|
|
|
|
const verifyOutput = prettyprint(input, path, verifyOptions);
|
2017-12-29 00:25:11 +03:00
|
|
|
expect(output).toEqual(verifyOutput);
|
|
|
|
});
|
|
|
|
});
|
2016-12-23 22:31:38 +03:00
|
|
|
|
2017-05-09 20:32:01 +03:00
|
|
|
if (AST_COMPARE) {
|
2018-07-04 22:54:39 +03:00
|
|
|
test(`${path} parse`, () => {
|
2018-11-25 11:21:14 +03:00
|
|
|
const compareOptions = Object.assign({}, mainOptions);
|
2018-07-04 22:54:39 +03:00
|
|
|
delete compareOptions.cursorOffset;
|
|
|
|
const astMassaged = parse(input, compareOptions);
|
|
|
|
let ppastMassaged = undefined;
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
ppastMassaged = parse(
|
2018-11-06 17:47:13 +03:00
|
|
|
prettyprint(input, path, compareOptions)
|
|
|
|
// \r has been replaced with /*CR*/ to test presence of CR in jest snapshots;
|
|
|
|
// reverting this to get the right AST
|
|
|
|
.replace(/\/\*CR\*\//g, "\r"),
|
2018-07-04 22:54:39 +03:00
|
|
|
compareOptions
|
|
|
|
);
|
|
|
|
}).not.toThrow();
|
2016-12-27 21:28:04 +03:00
|
|
|
|
2017-05-09 04:16:35 +03:00
|
|
|
expect(ppastMassaged).toBeDefined();
|
2018-04-25 19:29:35 +03:00
|
|
|
if (!astMassaged.errors || astMassaged.errors.length === 0) {
|
2017-05-09 04:16:35 +03:00
|
|
|
expect(astMassaged).toEqual(ppastMassaged);
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-23 22:31:38 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2017-12-29 00:25:11 +03:00
|
|
|
|
2016-12-23 22:31:38 +03:00
|
|
|
global.run_spec = run_spec;
|
|
|
|
|
2017-05-09 04:16:35 +03:00
|
|
|
function parse(string, opts) {
|
2018-05-14 15:52:27 +03:00
|
|
|
return prettier.__debug.parse(string, opts, /* massage */ true).ast;
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 09:29:59 +03:00
|
|
|
function prettyprint(src, filename, options) {
|
2018-05-01 15:42:59 +03:00
|
|
|
const result = prettier.formatWithCursor(
|
2017-03-25 18:10:17 +03:00
|
|
|
src,
|
|
|
|
Object.assign(
|
|
|
|
{
|
2017-06-02 00:40:49 +03:00
|
|
|
filepath: filename
|
2017-03-25 18:10:17 +03:00
|
|
|
},
|
|
|
|
options
|
|
|
|
)
|
|
|
|
);
|
2018-05-01 15:42:59 +03:00
|
|
|
if (options.cursorOffset >= 0) {
|
|
|
|
result.formatted =
|
|
|
|
result.formatted.slice(0, result.cursorOffset) +
|
|
|
|
"<|>" +
|
|
|
|
result.formatted.slice(result.cursorOffset);
|
|
|
|
}
|
2018-11-06 17:47:13 +03:00
|
|
|
|
|
|
|
// \r is trimmed from jest snapshots by default;
|
|
|
|
// manually replacing this character with /*CR*/ to test its true presence
|
|
|
|
return result.formatted.replace(/\r/g, "/*CR*/");
|
2016-12-23 22:31:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function read(filename) {
|
2017-03-25 18:10:17 +03:00
|
|
|
return fs.readFileSync(filename, "utf8");
|
|
|
|
}
|
|
|
|
|
2018-09-19 04:19:18 +03:00
|
|
|
function skipStandalone(/* parser */) {
|
|
|
|
return false;
|
2018-06-12 21:05:40 +03:00
|
|
|
}
|
|
|
|
|
2017-03-25 18:10:17 +03:00
|
|
|
function mergeDefaultOptions(parserConfig) {
|
|
|
|
return Object.assign(
|
|
|
|
{
|
|
|
|
printWidth: 80
|
|
|
|
},
|
|
|
|
parserConfig
|
|
|
|
);
|
|
|
|
}
|
2018-11-25 11:21:14 +03:00
|
|
|
|
|
|
|
function createSnapshot(input, output, options) {
|
|
|
|
const separatorWidth = 80;
|
|
|
|
const printWidthIndicator =
|
|
|
|
options.printWidth > 0 && Number.isFinite(options.printWidth)
|
|
|
|
? " ".repeat(options.printWidth) + "| printWidth"
|
|
|
|
: [];
|
|
|
|
return []
|
|
|
|
.concat(
|
|
|
|
printSeparator(separatorWidth, "options"),
|
|
|
|
printOptions(
|
|
|
|
omit(
|
|
|
|
options,
|
|
|
|
k => k === "rangeStart" || k === "rangeEnd" || k === "cursorOffset"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
printWidthIndicator,
|
|
|
|
printSeparator(separatorWidth, "input"),
|
|
|
|
input,
|
|
|
|
printSeparator(separatorWidth, "output"),
|
|
|
|
output,
|
|
|
|
printSeparator(separatorWidth)
|
|
|
|
)
|
|
|
|
.join("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
function printSeparator(width, description) {
|
|
|
|
description = description || "";
|
|
|
|
const leftLength = Math.floor((width - description.length) / 2);
|
|
|
|
const rightLength = width - leftLength - description.length;
|
|
|
|
return "=".repeat(leftLength) + description + "=".repeat(rightLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
function printOptions(options) {
|
|
|
|
const keys = Object.keys(options).sort();
|
|
|
|
return keys.map(key => `${key}: ${stringify(options[key])}`).join("\n");
|
|
|
|
function stringify(value) {
|
|
|
|
return value === Infinity
|
|
|
|
? "Infinity"
|
|
|
|
: Array.isArray(value)
|
|
|
|
? `[${value.map(v => JSON.stringify(v)).join(", ")}]`
|
|
|
|
: JSON.stringify(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function omit(obj, fn) {
|
|
|
|
return Object.keys(obj).reduce((reduced, key) => {
|
|
|
|
const value = obj[key];
|
|
|
|
if (!fn(key, value)) {
|
|
|
|
reduced[key] = value;
|
|
|
|
}
|
|
|
|
return reduced;
|
|
|
|
}, {});
|
|
|
|
}
|