2017-01-10 20:18:22 +03:00
|
|
|
"use strict";
|
2017-01-28 18:50:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const assert = require("assert");
|
|
|
|
const comments = require("./comments");
|
|
|
|
const FastPath = require("./fast-path");
|
|
|
|
const util = require("./util");
|
|
|
|
const isIdentifierName = require("esutils").keyword.isIdentifierNameES6;
|
|
|
|
|
|
|
|
const docBuilders = require("./doc-builders");
|
|
|
|
const concat = docBuilders.concat;
|
|
|
|
const join = docBuilders.join;
|
|
|
|
const line = docBuilders.line;
|
|
|
|
const hardline = docBuilders.hardline;
|
|
|
|
const softline = docBuilders.softline;
|
|
|
|
const literalline = docBuilders.literalline;
|
|
|
|
const group = docBuilders.group;
|
|
|
|
const indent = docBuilders.indent;
|
|
|
|
const align = docBuilders.align;
|
|
|
|
const conditionalGroup = docBuilders.conditionalGroup;
|
|
|
|
const fill = docBuilders.fill;
|
|
|
|
const ifBreak = docBuilders.ifBreak;
|
|
|
|
const breakParent = docBuilders.breakParent;
|
|
|
|
const lineSuffixBoundary = docBuilders.lineSuffixBoundary;
|
Add `--range-start` and `--range-end` options to format only parts of the input (#1609)
* Add `--range-start` and `--range-end` options to format only parts of the input
These options default to `0` and `Infinity`, respectively, so that the
entire input is formatted by default. However, if either option is
specified such that a node lies completely outside the resulting range,
the node will be treated as if it has a `// prettier-ignore` comment.
Related to https://github.com/prettier/prettier/pull/1577#issuecomment-300551179
Related to https://github.com/prettier/prettier/issues/1324
Related to https://github.com/prettier/prettier/issues/593
* printer: Extract hasPrettierIgnoreComment() helper
* Move isOutsideRange() to util
* Don't throw errors about comments outside range "not printing"
* Remove unnecessary check from isOutsideRange()
* Make --range-end exclusive
This lets it use the conventional way of specifying ranges in strings.
Note that if the rangeEnd in the tests is changed to 158, it will fail,
but it wouldn't have failed before this change.
* Change range formatting approach
NOTE: This doesn't pass its test yet. Note that since we're reading the
indentation from the first line, it is expected not to change. However,
a semicolon is added, and the lines outside the range are not changed.
The new approach is roughly:
* Require that the range exactly covers an integer number of lines of the input
* Detect the indentation of the line the range starts on
* Format the range's substring using `printAstToDoc`
* Add enough `indent`s to the doc to restore the detected indentation
* Format the doc to a string with `printDocToString`
* Prepend/append the original input before/after the range
See https://github.com/prettier/prettier/pull/1609#issuecomment-301582273
---
Given `tests/range/range.js`, run the following:
prettier tests/range/range.js --range-start 165 --range-end 246
See the range's text with:
dd if=tests/range/range.js ibs=1 skip=165 count=81 2>/dev/null
* Don't use default function parameters
Node v4 doesn't support them. See
http://node.green/#ES2015-syntax-default-function-parameters
* Hackily fix indentation of range formatting
See
https://github.com/prettier/prettier/pull/1609#issuecomment-301625368
Also update the snapshot to reflect that the indentation actually should
decrease by one space, since there were 13 spaces in the input and we
round down after dividing by tabWidth.
* Revert "printer: Extract hasPrettierIgnoreComment() helper"
See https://github.com/prettier/prettier/pull/1609#discussion_r116804853
This reverts commit 62bf068ca98f69d4a7fd0ae188b3554d409eee8d.
* Test automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Fix automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Propagate breaks after adding an indentation-triggering hardline
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116805581
* Extract getAlignmentSize(), use instead of countIndents()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Extract addAlignmentToDoc(), use instead of addIndentsToDoc()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Document that --range-start and --range-end include the entire line
* Fix rangeStart calculation
Before, it was incorrectly resulting in 1 when the originally provided
value was 0
* Extract formatRange() helper function
* Move getAlignmentSize() from printer to util
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636241
* Move addAlignmentToDoc() from printer to doc-builders
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636251
2017-05-21 20:14:13 +03:00
|
|
|
const addAlignmentToDoc = docBuilders.addAlignmentToDoc;
|
2017-05-19 23:00:38 +03:00
|
|
|
|
|
|
|
const docUtils = require("./doc-utils");
|
|
|
|
const willBreak = docUtils.willBreak;
|
|
|
|
const isLineNext = docUtils.isLineNext;
|
|
|
|
const isEmpty = docUtils.isEmpty;
|
|
|
|
|
2017-02-18 06:44:55 +03:00
|
|
|
function shouldPrintComma(options, level) {
|
|
|
|
level = level || "es5";
|
|
|
|
|
2017-02-23 20:57:51 +03:00
|
|
|
switch (options.trailingComma) {
|
2017-02-18 06:44:55 +03:00
|
|
|
case "all":
|
2017-02-23 20:57:51 +03:00
|
|
|
if (level === "all") {
|
2017-02-18 06:44:55 +03:00
|
|
|
return true;
|
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
// fallthrough
|
2017-02-18 06:44:55 +03:00
|
|
|
case "es5":
|
2017-02-23 20:57:51 +03:00
|
|
|
if (level === "es5") {
|
2017-02-18 06:44:55 +03:00
|
|
|
return true;
|
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
// fallthrough
|
2017-02-18 06:44:55 +03:00
|
|
|
case "none":
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-05 17:28:38 +03:00
|
|
|
function getPrintFunction(options) {
|
|
|
|
switch (options.parser) {
|
2017-06-05 22:34:08 +03:00
|
|
|
case "graphql":
|
|
|
|
return require("./printer-graphql");
|
2017-06-05 17:28:38 +03:00
|
|
|
case "postcss":
|
|
|
|
return require("./printer-postcss");
|
|
|
|
default:
|
|
|
|
return genericPrintNoParens;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-11 18:50:49 +03:00
|
|
|
function genericPrint(path, options, printPath, args) {
|
2016-12-23 21:38:10 +03:00
|
|
|
assert.ok(path instanceof FastPath);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const node = path.getValue();
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-02-14 05:58:50 +03:00
|
|
|
// Escape hatch
|
2017-02-16 06:56:11 +03:00
|
|
|
if (
|
2017-04-18 17:51:50 +03:00
|
|
|
node &&
|
2017-02-16 06:56:11 +03:00
|
|
|
node.comments &&
|
|
|
|
node.comments.length > 0 &&
|
2017-04-05 20:51:20 +03:00
|
|
|
node.comments.some(comment => comment.value.trim() === "prettier-ignore")
|
2017-02-16 06:56:11 +03:00
|
|
|
) {
|
|
|
|
return options.originalText.slice(util.locStart(node), util.locEnd(node));
|
2017-02-14 05:58:50 +03:00
|
|
|
}
|
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
let needsParens = false;
|
2017-06-05 17:28:38 +03:00
|
|
|
const linesWithoutParens = getPrintFunction(options)(
|
2017-05-21 21:00:16 +03:00
|
|
|
path,
|
|
|
|
options,
|
|
|
|
printPath,
|
|
|
|
args
|
|
|
|
);
|
2017-04-18 17:51:50 +03:00
|
|
|
|
|
|
|
if (!node || isEmpty(linesWithoutParens)) {
|
|
|
|
return linesWithoutParens;
|
|
|
|
}
|
|
|
|
|
2017-06-07 22:40:47 +03:00
|
|
|
const decorators = [];
|
2016-12-31 07:03:22 +03:00
|
|
|
if (
|
2017-01-19 02:31:46 +03:00
|
|
|
node.decorators &&
|
2017-02-16 06:56:11 +03:00
|
|
|
node.decorators.length > 0 &&
|
|
|
|
// If the parent node is an export declaration, it will be
|
|
|
|
// responsible for printing node.decorators.
|
|
|
|
!util.getParentExportDeclaration(path)
|
2016-12-31 07:03:22 +03:00
|
|
|
) {
|
2017-05-23 22:33:54 +03:00
|
|
|
let separator = hardline;
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(decoratorPath => {
|
2017-05-23 22:33:54 +03:00
|
|
|
let prefix = "@";
|
|
|
|
let decorator = decoratorPath.getValue();
|
|
|
|
if (decorator.expression) {
|
|
|
|
decorator = decorator.expression;
|
|
|
|
prefix = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
node.decorators.length === 1 &&
|
2017-06-02 17:38:44 +03:00
|
|
|
node.type !== "ClassDeclaration" &&
|
|
|
|
node.type !== "MethodDefinition" &&
|
2017-06-03 15:24:24 +03:00
|
|
|
node.type !== "ClassMethod" &&
|
2017-05-23 22:33:54 +03:00
|
|
|
(decorator.type === "Identifier" ||
|
2017-06-02 17:38:44 +03:00
|
|
|
decorator.type === "MemberExpression" ||
|
|
|
|
(decorator.type === "CallExpression" &&
|
2017-06-03 00:40:39 +03:00
|
|
|
(decorator.arguments.length === 0 ||
|
|
|
|
(decorator.arguments.length === 1 &&
|
|
|
|
(isStringLiteral(decorator.arguments[0]) ||
|
|
|
|
decorator.arguments[0].type === "Identifier" ||
|
|
|
|
decorator.arguments[0].type === "MemberExpression")))))
|
2017-05-23 22:33:54 +03:00
|
|
|
) {
|
2017-06-07 22:40:47 +03:00
|
|
|
separator = line;
|
2017-05-23 22:33:54 +03:00
|
|
|
}
|
|
|
|
|
2017-06-07 22:40:47 +03:00
|
|
|
decorators.push(prefix, printPath(decoratorPath), separator);
|
2017-04-12 20:16:11 +03:00
|
|
|
}, "decorators");
|
2017-01-09 20:09:04 +03:00
|
|
|
} else if (
|
2017-01-19 02:31:46 +03:00
|
|
|
util.isExportDeclaration(node) &&
|
2017-02-16 06:56:11 +03:00
|
|
|
node.declaration &&
|
|
|
|
node.declaration.decorators
|
2017-01-09 20:09:04 +03:00
|
|
|
) {
|
|
|
|
// Export declarations are responsible for printing any decorators
|
|
|
|
// that logically apply to node.declaration.
|
|
|
|
path.each(
|
2017-05-21 22:11:09 +03:00
|
|
|
decoratorPath => {
|
2017-05-23 22:33:54 +03:00
|
|
|
const decorator = decoratorPath.getValue();
|
|
|
|
const prefix = decorator.type === "Decorator" ||
|
|
|
|
decorator.type === "TSDecorator"
|
|
|
|
? ""
|
|
|
|
: "@";
|
2017-06-07 22:40:47 +03:00
|
|
|
decorators.push(prefix, printPath(decoratorPath), hardline);
|
2017-01-09 20:09:04 +03:00
|
|
|
},
|
|
|
|
"declaration",
|
|
|
|
"decorators"
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Nodes with decorators can't have parentheses, so we can avoid
|
|
|
|
// computing path.needsParens() except in this case.
|
|
|
|
needsParens = path.needsParens();
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
if (node.type) {
|
|
|
|
// HACK: ASI prevention in no-semi mode relies on knowledge of whether
|
|
|
|
// or not a paren has been inserted (see `exprNeedsASIProtection()`).
|
|
|
|
// For now, we're just passing that information by mutating the AST here,
|
|
|
|
// but it would be nice to find a cleaner way to do this.
|
|
|
|
node.needsParens = needsParens;
|
|
|
|
}
|
|
|
|
|
2017-06-07 22:40:47 +03:00
|
|
|
const parts = [];
|
2016-12-23 21:38:10 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.unshift("(");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(linesWithoutParens);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.push(")");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-06-07 22:40:47 +03:00
|
|
|
if (decorators.length > 0) {
|
|
|
|
return group(concat(decorators.concat(parts)));
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2017-04-11 18:50:49 +03:00
|
|
|
function genericPrintNoParens(path, options, print, args) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const n = path.getValue();
|
2017-04-12 20:16:11 +03:00
|
|
|
const semi = options.semi ? ";" : "";
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (!n) {
|
2017-01-20 00:20:03 +03:00
|
|
|
return "";
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (typeof n === "string") {
|
2017-01-20 00:20:03 +03:00
|
|
|
return n;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
let parts = [];
|
2016-12-23 21:38:10 +03:00
|
|
|
switch (n.type) {
|
2017-01-13 23:03:53 +03:00
|
|
|
case "File":
|
|
|
|
return path.call(print, "program");
|
|
|
|
case "Program":
|
|
|
|
// Babel 6
|
|
|
|
if (n.directives) {
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(childPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
parts.push(print(childPath), semi, hardline);
|
|
|
|
if (
|
|
|
|
util.isNextLineEmpty(options.originalText, childPath.getValue())
|
|
|
|
) {
|
|
|
|
parts.push(hardline);
|
|
|
|
}
|
|
|
|
}, "directives");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
2017-05-21 22:11:09 +03:00
|
|
|
path.call(bodyPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
}, "body")
|
2017-01-13 23:03:53 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-28 00:09:15 +03:00
|
|
|
parts.push(
|
2017-02-14 04:08:58 +03:00
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true)
|
2017-01-28 00:09:15 +03:00
|
|
|
);
|
2017-02-07 18:39:04 +03:00
|
|
|
|
|
|
|
// Only force a trailing newline if there were any contents.
|
|
|
|
if (n.body.length || n.comments) {
|
|
|
|
parts.push(hardline);
|
|
|
|
}
|
2017-01-11 17:39:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
// Babel extension.
|
|
|
|
case "Noop":
|
|
|
|
case "EmptyStatement":
|
2017-01-20 00:20:03 +03:00
|
|
|
return "";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ExpressionStatement":
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
// Detect Flow-parsed directives
|
|
|
|
if (n.directive) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat([nodeStr(n.expression, options, true), semi]);
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
}
|
2017-04-11 23:50:47 +03:00
|
|
|
return concat([path.call(print, "expression"), semi]); // Babel extension.
|
2017-01-28 18:50:22 +03:00
|
|
|
case "ParenthesizedExpression":
|
|
|
|
return concat(["(", path.call(print, "expression"), ")"]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "AssignmentExpression":
|
2017-03-15 19:45:40 +03:00
|
|
|
return printAssignment(
|
2017-04-14 03:09:00 +03:00
|
|
|
n.left,
|
2017-03-15 19:45:40 +03:00
|
|
|
path.call(print, "left"),
|
2017-05-25 04:19:15 +03:00
|
|
|
concat([" ", n.operator]),
|
2017-03-15 19:45:40 +03:00
|
|
|
n.right,
|
|
|
|
path.call(print, "right"),
|
|
|
|
options
|
2017-01-13 23:03:53 +03:00
|
|
|
);
|
|
|
|
case "BinaryExpression":
|
2017-01-19 01:01:17 +03:00
|
|
|
case "LogicalExpression": {
|
2017-02-09 18:36:05 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-05-06 07:44:50 +03:00
|
|
|
const parentParent = path.getParentNode(1);
|
2017-04-19 20:43:48 +03:00
|
|
|
const isInsideParenthesis =
|
|
|
|
n !== parent.body &&
|
|
|
|
(parent.type === "IfStatement" ||
|
|
|
|
parent.type === "WhileStatement" ||
|
|
|
|
parent.type === "DoStatement");
|
2017-02-09 18:36:05 +03:00
|
|
|
|
2017-04-19 20:43:48 +03:00
|
|
|
const parts = printBinaryishExpressions(
|
|
|
|
path,
|
|
|
|
print,
|
|
|
|
options,
|
|
|
|
/* isNested */ false,
|
|
|
|
isInsideParenthesis
|
|
|
|
);
|
|
|
|
|
|
|
|
// if (
|
|
|
|
// this.hasPlugin("dynamicImports") && this.lookahead().type === tt.parenLeft
|
|
|
|
// ) {
|
|
|
|
//
|
|
|
|
// looks super weird, we want to break the children if the parent breaks
|
|
|
|
//
|
|
|
|
// if (
|
|
|
|
// this.hasPlugin("dynamicImports") &&
|
|
|
|
// this.lookahead().type === tt.parenLeft
|
|
|
|
// ) {
|
|
|
|
if (isInsideParenthesis) {
|
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2017-05-26 02:26:34 +03:00
|
|
|
if (parent.type === "UnaryExpression") {
|
|
|
|
return group(
|
|
|
|
concat([indent(concat([softline, concat(parts)])), softline])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-19 20:43:48 +03:00
|
|
|
// Avoid indenting sub-expressions in assignment/return/etc statements.
|
2017-02-15 17:41:06 +03:00
|
|
|
if (
|
2017-03-15 19:45:40 +03:00
|
|
|
parent.type === "AssignmentExpression" ||
|
|
|
|
parent.type === "VariableDeclarator" ||
|
2017-02-15 17:41:06 +03:00
|
|
|
shouldInlineLogicalExpression(n) ||
|
2017-03-19 05:35:09 +03:00
|
|
|
parent.type === "ReturnStatement" ||
|
2017-05-06 07:44:50 +03:00
|
|
|
(parent.type === "JSXExpressionContainer" &&
|
|
|
|
parentParent.type === "JSXAttribute") ||
|
2017-04-19 20:43:48 +03:00
|
|
|
(n === parent.body && parent.type === "ArrowFunctionExpression") ||
|
|
|
|
(n !== parent.body && parent.type === "ForStatement")
|
2017-02-09 18:36:05 +03:00
|
|
|
) {
|
|
|
|
return group(concat(parts));
|
|
|
|
}
|
|
|
|
|
|
|
|
const rest = concat(parts.slice(1));
|
2017-01-19 01:01:17 +03:00
|
|
|
|
2017-02-03 19:50:51 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
2017-01-19 02:31:46 +03:00
|
|
|
// Don't include the initial expression in the indentation
|
|
|
|
// level. The first item is guaranteed to be the first
|
|
|
|
// left-most expression.
|
|
|
|
parts.length > 0 ? parts[0] : "",
|
2017-04-07 05:49:37 +03:00
|
|
|
indent(rest)
|
2017-02-03 19:50:51 +03:00
|
|
|
])
|
|
|
|
);
|
2017-01-19 01:01:17 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "AssignmentPattern":
|
|
|
|
return concat([
|
2017-01-09 20:09:04 +03:00
|
|
|
path.call(print, "left"),
|
2017-01-13 23:03:53 +03:00
|
|
|
" = ",
|
2017-01-09 20:09:04 +03:00
|
|
|
path.call(print, "right")
|
2017-01-13 23:03:53 +03:00
|
|
|
]);
|
2017-05-07 18:09:52 +03:00
|
|
|
case "TSTypeAssertionExpression":
|
|
|
|
return concat([
|
|
|
|
"<",
|
|
|
|
path.call(print, "typeAnnotation"),
|
|
|
|
">",
|
|
|
|
path.call(print, "expression")
|
|
|
|
]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MemberExpression": {
|
2017-04-11 21:37:35 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-04-14 03:09:00 +03:00
|
|
|
let firstNonMemberParent;
|
|
|
|
let i = 0;
|
|
|
|
do {
|
|
|
|
firstNonMemberParent = path.getParentNode(i);
|
|
|
|
i++;
|
|
|
|
} while (
|
2017-05-21 21:00:16 +03:00
|
|
|
firstNonMemberParent && firstNonMemberParent.type === "MemberExpression"
|
2017-04-14 03:09:00 +03:00
|
|
|
);
|
|
|
|
|
2017-04-11 21:37:35 +03:00
|
|
|
const shouldInline =
|
2017-05-21 21:00:16 +03:00
|
|
|
(firstNonMemberParent &&
|
|
|
|
((firstNonMemberParent.type === "VariableDeclarator" &&
|
2017-04-14 04:31:21 +03:00
|
|
|
firstNonMemberParent.id.type !== "Identifier") ||
|
2017-05-21 21:00:16 +03:00
|
|
|
(firstNonMemberParent.type === "AssignmentExpression" &&
|
|
|
|
firstNonMemberParent.left.type !== "Identifier"))) ||
|
2017-04-11 21:37:35 +03:00
|
|
|
n.computed ||
|
|
|
|
(n.object.type === "Identifier" &&
|
|
|
|
n.property.type === "Identifier" &&
|
|
|
|
parent.type !== "MemberExpression");
|
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "object"),
|
2017-04-11 21:37:35 +03:00
|
|
|
shouldInline
|
|
|
|
? printMemberLookup(path, options, print)
|
|
|
|
: group(
|
|
|
|
indent(
|
|
|
|
concat([softline, printMemberLookup(path, options, print)])
|
|
|
|
)
|
|
|
|
)
|
2017-01-09 20:09:04 +03:00
|
|
|
]);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MetaProperty":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "meta"),
|
|
|
|
".",
|
|
|
|
path.call(print, "property")
|
|
|
|
]);
|
|
|
|
case "BindExpression":
|
|
|
|
if (n.object) {
|
|
|
|
parts.push(path.call(print, "object"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("::", path.call(print, "callee"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "Path":
|
|
|
|
return join(".", n.body);
|
2017-05-19 23:00:38 +03:00
|
|
|
case "Identifier": {
|
2017-05-21 21:00:16 +03:00
|
|
|
const parentNode = path.getParentNode();
|
2017-05-19 23:00:38 +03:00
|
|
|
const isFunctionDeclarationIdentifier =
|
2017-05-21 21:00:16 +03:00
|
|
|
parentNode.type === "DeclareFunction" && parentNode.id === n;
|
2017-04-25 19:48:56 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
n.name,
|
|
|
|
n.optional ? "?" : "",
|
2017-05-21 21:00:16 +03:00
|
|
|
n.typeAnnotation && !isFunctionDeclarationIdentifier ? ": " : "",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "SpreadElement":
|
|
|
|
case "SpreadElementPattern":
|
|
|
|
case "RestProperty":
|
2017-05-26 05:04:15 +03:00
|
|
|
case "ExperimentalRestProperty":
|
2017-05-27 06:19:17 +03:00
|
|
|
case "ExperimentalSpreadProperty":
|
2017-01-13 23:03:53 +03:00
|
|
|
case "SpreadProperty":
|
|
|
|
case "SpreadPropertyPattern":
|
|
|
|
case "RestElement":
|
2017-04-05 00:52:16 +03:00
|
|
|
case "ObjectTypeSpreadProperty":
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
"...",
|
|
|
|
path.call(print, "argument"),
|
2017-04-23 04:09:53 +03:00
|
|
|
n.typeAnnotation ? ": " : "",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
case "FunctionDeclaration":
|
|
|
|
case "FunctionExpression":
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSNamespaceFunctionDeclaration":
|
2017-04-27 19:37:42 +03:00
|
|
|
if (isNodeStartingWithDeclare(n, options)) {
|
|
|
|
parts.push("declare ");
|
|
|
|
}
|
|
|
|
parts.push(printFunctionDeclaration(path, print, options));
|
2017-06-07 21:51:17 +03:00
|
|
|
if (!n.body) {
|
2017-06-03 00:49:37 +03:00
|
|
|
parts.push(semi);
|
|
|
|
}
|
2017-04-27 19:37:42 +03:00
|
|
|
return concat(parts);
|
2017-04-11 18:50:49 +03:00
|
|
|
case "ArrowFunctionExpression": {
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.async) {
|
|
|
|
parts.push("async ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-06 05:44:26 +03:00
|
|
|
parts.push(printFunctionTypeParameters(path, options, print));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
if (canPrintParamsWithoutParens(n)) {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "params", 0));
|
|
|
|
} else {
|
|
|
|
parts.push(
|
2017-01-24 02:47:11 +03:00
|
|
|
group(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
2017-04-13 06:24:50 +03:00
|
|
|
printFunctionParams(
|
|
|
|
path,
|
|
|
|
print,
|
|
|
|
options,
|
|
|
|
args && (args.expandLastArg || args.expandFirstArg)
|
|
|
|
),
|
2017-01-13 23:03:53 +03:00
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" =>");
|
2017-01-11 00:46:33 +03:00
|
|
|
|
2017-05-25 04:19:07 +03:00
|
|
|
const body = path.call(bodyPath => print(bodyPath, args), "body");
|
2017-01-28 18:50:22 +03:00
|
|
|
const collapsed = concat([concat(parts), " ", body]);
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-01-24 21:54:01 +03:00
|
|
|
// We want to always keep these types of nodes on the same line
|
|
|
|
// as the arrow.
|
2017-01-15 08:04:50 +03:00
|
|
|
if (
|
2017-05-22 16:40:19 +03:00
|
|
|
!hasLeadingOwnLineComment(options.originalText, n.body) &&
|
|
|
|
(n.body.type === "ArrayExpression" ||
|
|
|
|
n.body.type === "ObjectExpression" ||
|
|
|
|
n.body.type === "BlockStatement" ||
|
2017-05-26 22:29:00 +03:00
|
|
|
n.body.type === "SequenceExpression" ||
|
2017-05-22 16:40:19 +03:00
|
|
|
isTemplateOnItsOwnLine(n.body, options.originalText) ||
|
|
|
|
n.body.type === "ArrowFunctionExpression")
|
2017-01-15 08:04:50 +03:00
|
|
|
) {
|
2017-01-13 23:03:53 +03:00
|
|
|
return group(collapsed);
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-04-11 18:50:49 +03:00
|
|
|
// if the arrow function is expanded as last argument, we are adding a
|
|
|
|
// level of indentation and need to add a softline to align the closing )
|
|
|
|
// with the opening (.
|
|
|
|
const shouldAddSoftLine = args && args.expandLastArg;
|
|
|
|
|
2017-05-02 00:32:52 +03:00
|
|
|
// In order to avoid confusion between
|
|
|
|
// a => a ? a : a
|
|
|
|
// a <= a ? a : a
|
|
|
|
const shouldAddParens =
|
|
|
|
n.body.type === "ConditionalExpression" &&
|
|
|
|
!util.startsWithNoLookaheadToken(
|
|
|
|
n.body,
|
|
|
|
/* forbidFunctionAndClass */ false
|
|
|
|
);
|
|
|
|
|
2017-01-24 21:54:01 +03:00
|
|
|
return group(
|
2017-03-09 20:08:12 +03:00
|
|
|
concat([
|
|
|
|
concat(parts),
|
2017-04-11 18:50:49 +03:00
|
|
|
group(
|
|
|
|
concat([
|
2017-05-02 00:32:52 +03:00
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
line,
|
|
|
|
shouldAddParens ? ifBreak("", "(") : "",
|
|
|
|
body,
|
|
|
|
shouldAddParens ? ifBreak("", ")") : ""
|
|
|
|
])
|
|
|
|
),
|
2017-04-11 18:50:49 +03:00
|
|
|
shouldAddSoftLine
|
|
|
|
? concat([
|
|
|
|
ifBreak(shouldPrintComma(options, "all") ? "," : ""),
|
|
|
|
softline
|
|
|
|
])
|
2017-04-12 20:16:11 +03:00
|
|
|
: ""
|
2017-04-11 18:50:49 +03:00
|
|
|
])
|
2017-04-12 20:16:11 +03:00
|
|
|
)
|
2017-03-09 20:08:12 +03:00
|
|
|
])
|
2017-01-24 21:54:01 +03:00
|
|
|
);
|
2017-04-11 18:50:49 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MethodDefinition":
|
2017-04-26 19:25:58 +03:00
|
|
|
case "TSAbstractMethodDefinition":
|
|
|
|
if (n.accessibility) {
|
|
|
|
parts.push(n.accessibility + " ");
|
|
|
|
}
|
2017-06-02 17:16:11 +03:00
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2017-04-26 19:25:58 +03:00
|
|
|
if (n.type === "TSAbstractMethodDefinition") {
|
|
|
|
parts.push("abstract ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(printMethod(path, options, print));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "YieldExpression":
|
|
|
|
parts.push("yield");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.delegate) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
|
|
|
if (n.argument) {
|
|
|
|
parts.push(" ", path.call(print, "argument"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "AwaitExpression":
|
|
|
|
parts.push("await");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.all) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
|
|
|
if (n.argument) {
|
|
|
|
parts.push(" ", path.call(print, "argument"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ModuleDeclaration":
|
|
|
|
parts.push("module", path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.source) {
|
|
|
|
assert.ok(!n.body);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("from", path.call(print, "source"));
|
|
|
|
} else {
|
|
|
|
parts.push(path.call(print, "body"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return join(" ", parts);
|
|
|
|
case "ImportSpecifier":
|
|
|
|
if (n.imported) {
|
2017-02-15 23:56:34 +03:00
|
|
|
if (n.importKind) {
|
|
|
|
parts.push(path.call(print, "importKind"), " ");
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "imported"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.local && n.local.name !== n.imported.name) {
|
|
|
|
parts.push(" as ", path.call(print, "local"));
|
|
|
|
}
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.name) {
|
|
|
|
parts.push(" as ", path.call(print, "name"));
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
parts.push(path.call(print, "local"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.exported && n.exported.name !== n.local.name) {
|
|
|
|
parts.push(" as ", path.call(print, "exported"));
|
|
|
|
}
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.name) {
|
|
|
|
parts.push(" as ", path.call(print, "name"));
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportBatchSpecifier":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "*";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ImportNamespaceSpecifier":
|
|
|
|
parts.push("* as ");
|
|
|
|
|
|
|
|
if (n.local) {
|
|
|
|
parts.push(path.call(print, "local"));
|
|
|
|
} else if (n.id) {
|
|
|
|
parts.push(path.call(print, "id"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ImportDefaultSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
return path.call(print, "local");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return path.call(print, "id");
|
|
|
|
case "ExportDeclaration":
|
|
|
|
case "ExportDefaultDeclaration":
|
|
|
|
case "ExportNamedDeclaration":
|
|
|
|
return printExportDeclaration(path, options, print);
|
|
|
|
case "ExportAllDeclaration":
|
|
|
|
parts.push("export *");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.exported) {
|
|
|
|
parts.push(" as ", path.call(print, "exported"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(" from ", path.call(print, "source"), semi);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ExportNamespaceSpecifier":
|
|
|
|
case "ExportDefaultSpecifier":
|
|
|
|
return path.call(print, "exported");
|
2017-05-21 22:11:09 +03:00
|
|
|
case "ImportDeclaration": {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("import ");
|
|
|
|
|
|
|
|
if (n.importKind && n.importKind !== "value") {
|
|
|
|
parts.push(n.importKind + " ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const standalones = [];
|
|
|
|
const grouped = [];
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.specifiers && n.specifiers.length > 0) {
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(specifierPath => {
|
2017-05-19 23:00:38 +03:00
|
|
|
const value = specifierPath.getValue();
|
2017-04-12 20:16:11 +03:00
|
|
|
if (
|
2017-05-26 21:45:24 +03:00
|
|
|
value.type === "ImportDefaultSpecifier" ||
|
|
|
|
value.type === "ImportNamespaceSpecifier"
|
2017-04-12 20:16:11 +03:00
|
|
|
) {
|
|
|
|
standalones.push(print(specifierPath));
|
|
|
|
} else {
|
|
|
|
grouped.push(print(specifierPath));
|
|
|
|
}
|
|
|
|
}, "specifiers");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (standalones.length > 0) {
|
2017-01-22 23:34:00 +03:00
|
|
|
parts.push(join(", ", standalones));
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (standalones.length > 0 && grouped.length > 0) {
|
|
|
|
parts.push(", ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-26 02:27:04 +03:00
|
|
|
if (
|
|
|
|
grouped.length === 1 &&
|
|
|
|
n.specifiers &&
|
|
|
|
!n.specifiers.some(node => node.comments)
|
|
|
|
) {
|
|
|
|
parts.push(
|
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
options.bracketSpacing ? " " : "",
|
|
|
|
concat(grouped),
|
|
|
|
options.bracketSpacing ? " " : "",
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
);
|
|
|
|
} else if (grouped.length >= 1) {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
2017-01-24 21:54:01 +03:00
|
|
|
group(
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
2017-01-28 18:50:22 +03:00
|
|
|
join(concat([",", line]), grouped)
|
2017-01-13 23:03:53 +03:00
|
|
|
])
|
|
|
|
),
|
2017-02-18 06:44:55 +03:00
|
|
|
ifBreak(shouldPrintComma(options) ? "," : ""),
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
options.bracketSpacing ? line : softline,
|
2017-01-13 23:03:53 +03:00
|
|
|
"}"
|
Break multiline imports (#167)
Follows the same pattern as https://github.com/jlongster/prettier/pull/156/files
```js
echo "import { aaaaaaaa, bbbbbb, cccccc, dddddddd, eeeeeee, fffffffff, ggggggggg } from 'vjeux';" | ./bin/prettier.js --stdin
import {
aaaaaaaa,
bbbbbb,
cccccc,
dddddddd,
eeeeeee,
fffffffff,
ggggggggg
} from "vjeux";
```
2017-01-13 20:59:33 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-05-26 02:27:04 +03:00
|
|
|
parts.push(" ", "from ");
|
2017-03-06 06:08:42 +03:00
|
|
|
} else if (n.importKind && n.importKind === "type") {
|
|
|
|
parts.push("{} from ");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-26 02:27:04 +03:00
|
|
|
parts.push(path.call(print, "source"), semi);
|
2017-02-04 00:19:14 +03:00
|
|
|
|
2017-05-26 02:27:04 +03:00
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-25 18:33:48 +03:00
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
case "Import":
|
2017-01-25 18:33:48 +03:00
|
|
|
return "import";
|
2017-01-24 20:37:01 +03:00
|
|
|
case "BlockStatement": {
|
2017-05-21 22:11:09 +03:00
|
|
|
const naked = path.call(bodyPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
}, "body");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-13 06:25:58 +03:00
|
|
|
const hasContent = n.body.find(node => node.type !== "EmptyStatement");
|
2017-01-15 07:39:41 +03:00
|
|
|
const hasDirectives = n.directives && n.directives.length > 0;
|
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-02-23 18:29:05 +03:00
|
|
|
const parentParent = path.getParentNode(1);
|
2017-01-28 18:50:22 +03:00
|
|
|
if (
|
|
|
|
!hasContent &&
|
2017-02-16 06:56:11 +03:00
|
|
|
!hasDirectives &&
|
|
|
|
!n.comments &&
|
|
|
|
(parent.type === "ArrowFunctionExpression" ||
|
|
|
|
parent.type === "FunctionExpression" ||
|
|
|
|
parent.type === "FunctionDeclaration" ||
|
|
|
|
parent.type === "ObjectMethod" ||
|
2017-02-23 18:29:05 +03:00
|
|
|
parent.type === "ClassMethod" ||
|
2017-05-30 19:39:10 +03:00
|
|
|
parent.type === "ForStatement" ||
|
|
|
|
parent.type === "WhileStatement" ||
|
|
|
|
parent.type === "DoWhileStatement" ||
|
2017-02-23 18:29:05 +03:00
|
|
|
(parent.type === "CatchClause" && !parentParent.finalizer))
|
2017-01-28 18:50:22 +03:00
|
|
|
) {
|
2017-01-24 20:37:01 +03:00
|
|
|
return "{}";
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("{");
|
|
|
|
|
|
|
|
// Babel 6
|
2017-01-15 07:39:41 +03:00
|
|
|
if (hasDirectives) {
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(childPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
parts.push(indent(concat([hardline, print(childPath), semi])));
|
|
|
|
}, "directives");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:39:41 +03:00
|
|
|
if (hasContent) {
|
2017-04-07 05:49:37 +03:00
|
|
|
parts.push(indent(concat([hardline, naked])));
|
2017-01-19 22:46:37 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-28 00:09:15 +03:00
|
|
|
parts.push(comments.printDanglingComments(path, options));
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(hardline, "}");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-01-24 20:37:01 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ReturnStatement":
|
|
|
|
parts.push("return");
|
|
|
|
|
2017-03-09 04:06:18 +03:00
|
|
|
if (n.argument) {
|
|
|
|
if (returnArgumentHasLeadingComment(options, n.argument)) {
|
|
|
|
parts.push(
|
|
|
|
concat([
|
|
|
|
" (",
|
2017-04-12 20:16:11 +03:00
|
|
|
indent(concat([softline, path.call(print, "argument")])),
|
2017-03-09 04:06:18 +03:00
|
|
|
line,
|
|
|
|
")"
|
|
|
|
])
|
|
|
|
);
|
2017-03-19 05:35:09 +03:00
|
|
|
} else if (
|
2017-04-12 20:16:11 +03:00
|
|
|
n.argument.type === "LogicalExpression" ||
|
|
|
|
n.argument.type === "BinaryExpression"
|
2017-03-19 05:35:09 +03:00
|
|
|
) {
|
|
|
|
parts.push(
|
2017-04-12 20:16:11 +03:00
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
ifBreak(" (", " "),
|
|
|
|
indent(concat([softline, path.call(print, "argument")])),
|
|
|
|
softline,
|
|
|
|
ifBreak(")")
|
|
|
|
])
|
|
|
|
)
|
2017-03-19 05:35:09 +03:00
|
|
|
);
|
2017-03-09 04:06:18 +03:00
|
|
|
} else {
|
|
|
|
parts.push(" ", path.call(print, "argument"));
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-02 00:41:18 +03:00
|
|
|
if (hasDanglingComments(n)) {
|
2017-04-10 20:46:25 +03:00
|
|
|
parts.push(
|
|
|
|
" ",
|
2017-04-12 20:16:11 +03:00
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true)
|
2017-04-10 20:46:25 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "CallExpression": {
|
2017-03-04 00:48:29 +03:00
|
|
|
if (
|
|
|
|
// We want to keep require calls as a unit
|
2017-03-04 02:39:37 +03:00
|
|
|
(n.callee.type === "Identifier" && n.callee.name === "require") ||
|
2017-05-02 00:27:00 +03:00
|
|
|
// Template literals as single arguments
|
2017-05-04 00:35:58 +03:00
|
|
|
(n.arguments.length === 1 &&
|
|
|
|
isTemplateOnItsOwnLine(n.arguments[0], options.originalText)) ||
|
2017-03-09 00:24:28 +03:00
|
|
|
// Keep test declarations on a single line
|
|
|
|
// e.g. `it('long name', () => {`
|
2017-03-04 02:39:37 +03:00
|
|
|
(n.callee.type === "Identifier" &&
|
2017-03-09 00:24:28 +03:00
|
|
|
(n.callee.name === "it" ||
|
|
|
|
n.callee.name === "test" ||
|
|
|
|
n.callee.name === "describe") &&
|
2017-03-04 00:48:29 +03:00
|
|
|
n.arguments.length === 2 &&
|
2017-03-04 02:39:37 +03:00
|
|
|
(n.arguments[0].type === "StringLiteral" ||
|
2017-03-07 19:36:03 +03:00
|
|
|
n.arguments[0].type === "TemplateLiteral" ||
|
2017-03-04 02:39:37 +03:00
|
|
|
(n.arguments[0].type === "Literal" &&
|
|
|
|
typeof n.arguments[0].value === "string")) &&
|
|
|
|
(n.arguments[1].type === "FunctionExpression" ||
|
|
|
|
n.arguments[1].type === "ArrowFunctionExpression") &&
|
2017-03-04 00:48:29 +03:00
|
|
|
n.arguments[1].params.length <= 1)
|
|
|
|
) {
|
2017-03-03 06:21:02 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "callee"),
|
2017-03-19 18:06:52 +03:00
|
|
|
path.call(print, "typeParameters"),
|
2017-03-03 06:21:02 +03:00
|
|
|
concat(["(", join(", ", path.map(print, "arguments")), ")"])
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2017-03-04 00:48:29 +03:00
|
|
|
// We detect calls on member lookups and possibly print them in a
|
|
|
|
// special chain format. See `printMemberChain` for more info.
|
|
|
|
if (n.callee.type === "MemberExpression") {
|
|
|
|
return printMemberChain(path, options, print);
|
|
|
|
}
|
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "callee"),
|
2017-05-06 05:44:26 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
2017-01-09 20:09:04 +03:00
|
|
|
printArgumentsList(path, options, print)
|
|
|
|
]);
|
|
|
|
}
|
2017-05-11 00:11:50 +03:00
|
|
|
case "TSInterfaceDeclaration":
|
|
|
|
parts.push(
|
2017-05-27 06:19:17 +03:00
|
|
|
n.abstract ? "abstract " : "",
|
2017-05-11 00:11:50 +03:00
|
|
|
printTypeScriptModifiers(path, options, print),
|
|
|
|
"interface ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
n.typeParameters ? path.call(print, "typeParameters") : "",
|
|
|
|
" "
|
|
|
|
);
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-13 03:13:38 +03:00
|
|
|
if (n.heritage.length) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push("extends ", join(", ", path.map(print, "heritage")), " ");
|
2017-05-11 00:11:50 +03:00
|
|
|
}
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-11 00:11:50 +03:00
|
|
|
parts.push(path.call(print, "body"));
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-11 00:11:50 +03:00
|
|
|
return concat(parts);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ObjectExpression":
|
|
|
|
case "ObjectPattern":
|
|
|
|
case "ObjectTypeAnnotation":
|
2017-06-03 01:32:51 +03:00
|
|
|
case "TSInterfaceBody":
|
2017-05-02 05:12:53 +03:00
|
|
|
case "TSTypeLiteral": {
|
2017-05-19 23:00:38 +03:00
|
|
|
const isTypeAnnotation = n.type === "ObjectTypeAnnotation";
|
2017-06-03 15:18:59 +03:00
|
|
|
const shouldBreak =
|
|
|
|
n.type !== "ObjectPattern" &&
|
|
|
|
util.hasNewlineInRange(
|
|
|
|
options.originalText,
|
|
|
|
util.locStart(n),
|
|
|
|
util.locEnd(n)
|
|
|
|
);
|
2017-06-03 01:32:51 +03:00
|
|
|
const separator = n.type === "TSInterfaceBody" ||
|
|
|
|
n.type === "TSTypeLiteral"
|
2017-06-08 21:47:49 +03:00
|
|
|
? ifBreak(semi, ";")
|
2017-06-03 01:32:51 +03:00
|
|
|
: ",";
|
2017-05-19 23:00:38 +03:00
|
|
|
const fields = [];
|
|
|
|
const leftBrace = n.exact ? "{|" : "{";
|
|
|
|
const rightBrace = n.exact ? "|}" : "}";
|
|
|
|
const parent = path.getParentNode(0);
|
2017-05-26 02:26:40 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
let propertiesField;
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
if (n.type === "TSTypeLiteral") {
|
2017-05-11 00:11:50 +03:00
|
|
|
propertiesField = "members";
|
|
|
|
} else if (n.type === "TSInterfaceBody") {
|
|
|
|
propertiesField = "body";
|
|
|
|
} else {
|
|
|
|
propertiesField = "properties";
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
if (isTypeAnnotation) {
|
|
|
|
fields.push("indexers", "callProperties");
|
|
|
|
}
|
2017-03-19 18:06:52 +03:00
|
|
|
fields.push(propertiesField);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-02 00:42:52 +03:00
|
|
|
// Unfortunately, things are grouped together in the ast can be
|
|
|
|
// interleaved in the source code. So we need to reorder them before
|
|
|
|
// printing them.
|
|
|
|
const propsAndLoc = [];
|
2017-05-21 22:11:09 +03:00
|
|
|
fields.forEach(field => {
|
|
|
|
path.each(childPath => {
|
2017-05-02 00:42:52 +03:00
|
|
|
const node = childPath.getValue();
|
|
|
|
propsAndLoc.push({
|
|
|
|
node: node,
|
|
|
|
printed: print(childPath),
|
|
|
|
loc: util.locStart(node)
|
|
|
|
});
|
|
|
|
}, field);
|
|
|
|
});
|
2017-04-12 20:16:11 +03:00
|
|
|
|
2017-05-02 00:42:52 +03:00
|
|
|
let separatorParts = [];
|
2017-05-21 21:00:16 +03:00
|
|
|
const props = propsAndLoc.sort((a, b) => a.loc - b.loc).map(prop => {
|
|
|
|
const result = concat(separatorParts.concat(group(prop.printed)));
|
|
|
|
separatorParts = [separator, line];
|
|
|
|
if (util.isNextLineEmpty(options.originalText, prop.node)) {
|
|
|
|
separatorParts.push(hardline);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
});
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
const lastElem = util.getLast(n[propertiesField]);
|
2017-04-13 17:10:08 +03:00
|
|
|
|
2017-06-02 21:48:43 +03:00
|
|
|
const canHaveTrailingSeparator = !(
|
|
|
|
lastElem &&
|
|
|
|
(lastElem.type === "RestProperty" || lastElem.type === "RestElement")
|
|
|
|
);
|
2017-01-30 18:07:03 +03:00
|
|
|
|
2017-05-02 00:49:03 +03:00
|
|
|
let content;
|
|
|
|
if (props.length === 0 && !n.typeAnnotation) {
|
2017-05-02 00:41:18 +03:00
|
|
|
if (!hasDanglingComments(n)) {
|
2017-05-13 03:13:38 +03:00
|
|
|
return concat([leftBrace, rightBrace]);
|
2017-05-02 00:41:18 +03:00
|
|
|
}
|
|
|
|
|
2017-05-02 00:49:03 +03:00
|
|
|
content = group(
|
2017-01-31 17:52:05 +03:00
|
|
|
concat([
|
2017-04-03 20:24:36 +03:00
|
|
|
leftBrace,
|
2017-01-31 17:52:05 +03:00
|
|
|
comments.printDanglingComments(path, options),
|
|
|
|
softline,
|
2017-04-03 20:24:36 +03:00
|
|
|
rightBrace
|
2017-01-31 17:52:05 +03:00
|
|
|
])
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
2017-05-02 00:49:03 +03:00
|
|
|
content = concat([
|
|
|
|
leftBrace,
|
|
|
|
indent(
|
2017-05-26 02:26:40 +03:00
|
|
|
concat([options.bracketSpacing ? line : softline, concat(props)])
|
2017-05-02 00:49:03 +03:00
|
|
|
),
|
|
|
|
ifBreak(
|
2017-05-07 17:39:21 +03:00
|
|
|
canHaveTrailingSeparator &&
|
|
|
|
(separator !== "," || shouldPrintComma(options))
|
2017-05-21 21:00:16 +03:00
|
|
|
? separator
|
|
|
|
: ""
|
2017-05-02 00:49:03 +03:00
|
|
|
),
|
2017-05-26 02:26:40 +03:00
|
|
|
concat([options.bracketSpacing ? line : softline, rightBrace]),
|
2017-05-02 00:49:03 +03:00
|
|
|
n.typeAnnotation ? ": " : "",
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we inline the object as first argument of the parent, we don't want
|
|
|
|
// to create another group so that the object breaks before the return
|
|
|
|
// type
|
2017-05-02 05:12:53 +03:00
|
|
|
const parentParentParent = path.getParentNode(2);
|
2017-05-02 00:49:03 +03:00
|
|
|
if (
|
2017-05-02 05:12:53 +03:00
|
|
|
(n.type === "ObjectPattern" &&
|
|
|
|
parent &&
|
|
|
|
shouldHugArguments(parent) &&
|
|
|
|
parent.params[0] === n) ||
|
2017-05-26 02:26:40 +03:00
|
|
|
(shouldHugType(n) &&
|
2017-05-02 05:12:53 +03:00
|
|
|
parentParentParent &&
|
|
|
|
shouldHugArguments(parentParentParent) &&
|
|
|
|
parentParentParent.params[0].typeAnnotation.typeAnnotation === n)
|
2017-05-02 00:49:03 +03:00
|
|
|
) {
|
|
|
|
return content;
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
|
|
|
|
2017-05-02 00:49:03 +03:00
|
|
|
return group(content, { shouldBreak });
|
2017-05-02 05:12:53 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "PropertyPattern":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "key"),
|
|
|
|
": ",
|
|
|
|
path.call(print, "pattern")
|
|
|
|
]);
|
|
|
|
// Babel 6
|
2017-01-28 18:50:22 +03:00
|
|
|
case "ObjectProperty": // Non-standard AST node type.
|
|
|
|
case "Property":
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.method || n.kind === "get" || n.kind === "set") {
|
|
|
|
return printMethod(path, options, print);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n.shorthand) {
|
|
|
|
parts.push(path.call(print, "value"));
|
|
|
|
} else {
|
2017-05-25 04:19:15 +03:00
|
|
|
let printedLeft;
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.computed) {
|
2017-05-25 04:19:15 +03:00
|
|
|
printedLeft = concat(["[", path.call(print, "key"), "]"]);
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
2017-05-25 04:19:15 +03:00
|
|
|
printedLeft = printPropertyKey(path, options, print);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2017-05-25 04:19:15 +03:00
|
|
|
parts.push(
|
|
|
|
printAssignment(
|
|
|
|
n.key,
|
|
|
|
printedLeft,
|
|
|
|
":",
|
|
|
|
n.value,
|
|
|
|
path.call(print, "value"),
|
|
|
|
options
|
|
|
|
)
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
|
|
|
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(parts); // Babel 6
|
|
|
|
case "ClassMethod":
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
|
|
|
|
|
|
|
parts = parts.concat(printObjectMethod(path, options, print));
|
|
|
|
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(parts); // Babel 6
|
|
|
|
case "ObjectMethod":
|
2017-01-13 23:03:53 +03:00
|
|
|
return printObjectMethod(path, options, print);
|
2017-05-03 02:03:23 +03:00
|
|
|
case "TSDecorator":
|
2017-01-13 23:03:53 +03:00
|
|
|
case "Decorator":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["@", path.call(print, "expression")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ArrayExpression":
|
2017-01-23 07:52:30 +03:00
|
|
|
case "ArrayPattern":
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.elements.length === 0) {
|
2017-05-02 00:41:18 +03:00
|
|
|
if (!hasDanglingComments(n)) {
|
|
|
|
parts.push("[]");
|
|
|
|
} else {
|
|
|
|
parts.push(
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"[",
|
|
|
|
comments.printDanglingComments(path, options),
|
|
|
|
softline,
|
|
|
|
"]"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
2017-01-23 02:20:47 +03:00
|
|
|
const lastElem = util.getLast(n.elements);
|
2017-06-02 21:48:43 +03:00
|
|
|
const canHaveTrailingComma = !(
|
|
|
|
lastElem && lastElem.type === "RestElement"
|
|
|
|
);
|
2017-01-23 02:20:47 +03:00
|
|
|
|
2017-01-16 20:53:39 +03:00
|
|
|
// JavaScript allows you to have empty elements in an array which
|
|
|
|
// changes its length based on the number of commas. The algorithm
|
|
|
|
// is that if the last argument is null, we need to force insert
|
|
|
|
// a comma to ensure JavaScript recognizes it.
|
|
|
|
// [,].length === 1
|
|
|
|
// [1,].length === 1
|
|
|
|
// [1,,].length === 2
|
|
|
|
//
|
|
|
|
// Note that util.getLast returns null if the array is empty, but
|
|
|
|
// we already check for an empty array just above so we are safe
|
2017-04-12 20:16:11 +03:00
|
|
|
const needsForcedTrailingComma =
|
|
|
|
canHaveTrailingComma && lastElem === null;
|
2017-01-16 20:53:39 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
2017-01-24 21:54:01 +03:00
|
|
|
group(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
|
|
|
"[",
|
|
|
|
indent(
|
2017-03-15 19:35:22 +03:00
|
|
|
concat([
|
|
|
|
softline,
|
|
|
|
printArrayItems(path, options, "elements", print)
|
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
),
|
2017-01-16 20:53:39 +03:00
|
|
|
needsForcedTrailingComma ? "," : "",
|
2017-01-23 02:20:47 +03:00
|
|
|
ifBreak(
|
|
|
|
canHaveTrailingComma &&
|
2017-01-23 20:49:46 +03:00
|
|
|
!needsForcedTrailingComma &&
|
2017-02-18 06:44:55 +03:00
|
|
|
shouldPrintComma(options)
|
2017-01-23 20:49:46 +03:00
|
|
|
? ","
|
|
|
|
: ""
|
2017-01-23 02:20:47 +03:00
|
|
|
),
|
2017-02-23 20:57:51 +03:00
|
|
|
comments.printDanglingComments(
|
|
|
|
path,
|
|
|
|
options,
|
|
|
|
/* sameIndent */ true
|
|
|
|
),
|
2017-01-24 22:38:12 +03:00
|
|
|
softline,
|
2017-01-13 23:03:53 +03:00
|
|
|
"]"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.typeAnnotation) {
|
|
|
|
parts.push(": ", path.call(print, "typeAnnotation"));
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
2017-05-26 22:29:00 +03:00
|
|
|
case "SequenceExpression": {
|
|
|
|
const parent = path.getParentNode();
|
|
|
|
const shouldInline =
|
|
|
|
parent.type === "ReturnStatement" ||
|
|
|
|
parent.type === "ForStatement" ||
|
|
|
|
parent.type === "ExpressionStatement";
|
|
|
|
|
|
|
|
if (shouldInline) {
|
|
|
|
return join(", ", path.map(print, "expressions"));
|
|
|
|
}
|
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
softline,
|
|
|
|
join(concat([",", line]), path.map(print, "expressions"))
|
|
|
|
])
|
|
|
|
),
|
|
|
|
softline
|
|
|
|
])
|
|
|
|
);
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ThisExpression":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "this";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "Super":
|
2017-04-19 20:24:13 +03:00
|
|
|
return "super";
|
2017-05-21 22:11:09 +03:00
|
|
|
case "NullLiteral": // Babel 6 Literal split
|
2017-04-19 20:24:13 +03:00
|
|
|
return "null";
|
2017-05-21 22:11:09 +03:00
|
|
|
case "RegExpLiteral": // Babel 6 Literal split
|
2017-04-19 20:24:13 +03:00
|
|
|
return printRegex(n);
|
2017-05-21 22:11:09 +03:00
|
|
|
case "NumericLiteral": // Babel 6 Literal split
|
2017-01-30 20:36:23 +03:00
|
|
|
return printNumber(n.extra.raw);
|
2017-05-21 22:11:09 +03:00
|
|
|
case "BooleanLiteral": // Babel 6 Literal split
|
|
|
|
case "StringLiteral": // Babel 6 Literal split
|
2017-01-13 23:03:53 +03:00
|
|
|
case "Literal":
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.regex) {
|
|
|
|
return printRegex(n.regex);
|
|
|
|
}
|
2017-05-06 07:39:11 +03:00
|
|
|
if (typeof n.value === "number") {
|
|
|
|
return printNumber(n.raw);
|
|
|
|
}
|
2017-05-03 03:06:25 +03:00
|
|
|
if (typeof n.value !== "string") {
|
|
|
|
return "" + n.value;
|
|
|
|
}
|
2017-01-28 18:50:22 +03:00
|
|
|
return nodeStr(n, options); // Babel 6
|
|
|
|
case "Directive":
|
|
|
|
return path.call(print, "value"); // Babel 6
|
|
|
|
case "DirectiveLiteral":
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ModuleSpecifier":
|
|
|
|
if (n.local) {
|
|
|
|
throw new Error("The ESTree ModuleSpecifier type should be abstract");
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Esprima ModuleSpecifier type is just a string-valued
|
|
|
|
// Literal identifying the imported-from module.
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "UnaryExpression":
|
|
|
|
parts.push(n.operator);
|
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (/[a-z]$/.test(n.operator)) {
|
|
|
|
parts.push(" ");
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
parts.push(path.call(print, "argument"));
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "UpdateExpression":
|
|
|
|
parts.push(path.call(print, "argument"), n.operator);
|
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.prefix) {
|
|
|
|
parts.reverse();
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
2017-05-30 20:08:06 +03:00
|
|
|
case "ConditionalExpression": {
|
|
|
|
const parent = path.getParentNode();
|
|
|
|
const printed = concat([
|
|
|
|
line,
|
|
|
|
"? ",
|
|
|
|
n.consequent.type === "ConditionalExpression" ? ifBreak("", "(") : "",
|
|
|
|
align(2, path.call(print, "consequent")),
|
|
|
|
n.consequent.type === "ConditionalExpression" ? ifBreak("", ")") : "",
|
|
|
|
line,
|
|
|
|
": ",
|
|
|
|
align(2, path.call(print, "alternate"))
|
|
|
|
]);
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "test"),
|
2017-05-30 20:08:06 +03:00
|
|
|
parent.type === "ConditionalExpression" ? printed : indent(printed)
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
|
|
|
);
|
2017-05-30 20:08:06 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NewExpression":
|
2017-05-06 05:44:26 +03:00
|
|
|
parts.push(
|
|
|
|
"new ",
|
|
|
|
path.call(print, "callee"),
|
|
|
|
printFunctionTypeParameters(path, options, print)
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-06 05:44:26 +03:00
|
|
|
if (n.arguments) {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(printArgumentsList(path, options, print));
|
2017-01-11 08:48:49 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-05-19 23:00:38 +03:00
|
|
|
case "VariableDeclaration": {
|
2017-05-21 22:11:09 +03:00
|
|
|
const printed = path.map(childPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
return print(childPath);
|
|
|
|
}, "declarations");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-06-03 01:00:16 +03:00
|
|
|
// We generally want to terminate all variable declarations with a
|
|
|
|
// semicolon, except when they in the () part of for loops.
|
|
|
|
const parentNode = path.getParentNode();
|
|
|
|
|
|
|
|
const isParentForLoop =
|
|
|
|
parentNode.type === "ForStatement" ||
|
|
|
|
parentNode.type === "ForInStatement" ||
|
|
|
|
parentNode.type === "ForOfStatement" ||
|
|
|
|
parentNode.type === "ForAwaitStatement";
|
|
|
|
|
2017-05-26 02:26:26 +03:00
|
|
|
const hasValue = n.declarations.some(decl => decl.init);
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts = [
|
2017-04-27 19:37:42 +03:00
|
|
|
isNodeStartingWithDeclare(n, options) ? "declare " : "",
|
2017-01-13 23:03:53 +03:00
|
|
|
n.kind,
|
2017-05-21 18:03:38 +03:00
|
|
|
printed.length ? concat([" ", printed[0]]) : "",
|
2017-05-26 02:26:26 +03:00
|
|
|
indent(
|
|
|
|
concat(
|
|
|
|
printed
|
|
|
|
.slice(1)
|
2017-06-03 01:00:16 +03:00
|
|
|
.map(p =>
|
|
|
|
concat([",", hasValue && !isParentForLoop ? hardline : line, p])
|
|
|
|
)
|
2017-05-26 02:26:26 +03:00
|
|
|
)
|
|
|
|
)
|
2017-01-13 23:03:53 +03:00
|
|
|
];
|
|
|
|
|
2017-01-22 23:34:14 +03:00
|
|
|
if (!(isParentForLoop && parentNode.body !== n)) {
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
|
|
|
|
2017-01-24 21:54:01 +03:00
|
|
|
return group(concat(parts));
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "VariableDeclarator":
|
2017-03-15 19:45:40 +03:00
|
|
|
return printAssignment(
|
2017-04-14 03:09:00 +03:00
|
|
|
n.id,
|
2017-04-26 00:08:30 +03:00
|
|
|
concat([path.call(print, "id"), path.call(print, "typeParameters")]),
|
2017-05-25 04:19:15 +03:00
|
|
|
" =",
|
2017-03-15 19:45:40 +03:00
|
|
|
n.init,
|
|
|
|
n.init && path.call(print, "init"),
|
|
|
|
options
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "WithStatement":
|
2017-05-21 21:00:16 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"with (",
|
|
|
|
path.call(print, "object"),
|
|
|
|
")",
|
|
|
|
adjustClause(n.body, path.call(print, "body"))
|
|
|
|
])
|
|
|
|
);
|
2017-05-21 22:11:09 +03:00
|
|
|
case "IfStatement": {
|
2017-04-13 06:25:44 +03:00
|
|
|
const con = adjustClause(n.consequent, path.call(print, "consequent"));
|
2017-04-12 20:16:11 +03:00
|
|
|
const opening = group(
|
|
|
|
concat([
|
|
|
|
"if (",
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
indent(concat([softline, path.call(print, "test")])),
|
|
|
|
softline
|
|
|
|
])
|
2017-03-18 00:02:35 +03:00
|
|
|
),
|
2017-04-12 20:16:11 +03:00
|
|
|
")",
|
|
|
|
con
|
|
|
|
])
|
|
|
|
);
|
2017-03-18 00:02:35 +03:00
|
|
|
|
|
|
|
parts.push(opening);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.alternate) {
|
2017-04-13 06:25:44 +03:00
|
|
|
if (n.consequent.type === "BlockStatement") {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(" else");
|
|
|
|
} else {
|
2017-03-18 00:02:35 +03:00
|
|
|
parts.push(hardline, "else");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
2017-04-12 20:16:11 +03:00
|
|
|
group(
|
|
|
|
adjustClause(
|
2017-04-13 06:25:44 +03:00
|
|
|
n.alternate,
|
2017-04-12 20:16:11 +03:00
|
|
|
path.call(print, "alternate"),
|
|
|
|
n.alternate.type === "IfStatement"
|
|
|
|
)
|
|
|
|
)
|
2017-01-13 23:03:53 +03:00
|
|
|
);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-03-18 00:02:35 +03:00
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ForStatement": {
|
2017-04-13 06:25:44 +03:00
|
|
|
const body = adjustClause(n.body, path.call(print, "body"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-02-20 18:15:46 +03:00
|
|
|
// We want to keep dangling comments above the loop to stay consistent.
|
|
|
|
// Any comment positioned between the for statement and the parentheses
|
|
|
|
// is going to be printed before the statement.
|
2017-02-23 20:57:51 +03:00
|
|
|
const dangling = comments.printDanglingComments(
|
|
|
|
path,
|
|
|
|
options,
|
|
|
|
/* sameLine */ true
|
|
|
|
);
|
2017-02-20 17:17:10 +03:00
|
|
|
const printedComments = dangling ? concat([dangling, softline]) : "";
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (!n.init && !n.test && !n.update) {
|
2017-05-04 01:50:48 +03:00
|
|
|
return concat([printedComments, group(concat(["for (;;)", body]))]);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
2017-02-20 17:17:10 +03:00
|
|
|
printedComments,
|
2017-05-21 21:00:16 +03:00
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"for (",
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
softline,
|
|
|
|
path.call(print, "init"),
|
|
|
|
";",
|
|
|
|
line,
|
|
|
|
path.call(print, "test"),
|
|
|
|
";",
|
|
|
|
line,
|
|
|
|
path.call(print, "update")
|
|
|
|
])
|
|
|
|
),
|
|
|
|
softline
|
|
|
|
])
|
|
|
|
),
|
|
|
|
")",
|
|
|
|
body
|
|
|
|
])
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
case "WhileStatement":
|
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"while (",
|
2017-05-04 01:50:48 +03:00
|
|
|
group(
|
|
|
|
concat([
|
2017-05-21 21:00:16 +03:00
|
|
|
indent(concat([softline, path.call(print, "test")])),
|
2017-05-04 01:50:48 +03:00
|
|
|
softline
|
|
|
|
])
|
|
|
|
),
|
|
|
|
")",
|
2017-05-21 21:00:16 +03:00
|
|
|
adjustClause(n.body, path.call(print, "body"))
|
|
|
|
])
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ForInStatement":
|
|
|
|
// Note: esprima can't actually parse "for each (".
|
2017-05-21 21:00:16 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
n.each ? "for each (" : "for (",
|
|
|
|
path.call(print, "left"),
|
|
|
|
" in ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
")",
|
|
|
|
adjustClause(n.body, path.call(print, "body"))
|
|
|
|
])
|
|
|
|
);
|
2017-03-22 19:23:57 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ForOfStatement":
|
2017-05-21 22:11:09 +03:00
|
|
|
case "ForAwaitStatement": {
|
2017-04-05 23:28:02 +03:00
|
|
|
// Babylon 7 removed ForAwaitStatement in favor of ForOfStatement
|
2017-03-22 19:23:57 +03:00
|
|
|
// with `"await": true`:
|
|
|
|
// https://github.com/estree/estree/pull/138
|
2017-04-12 20:16:11 +03:00
|
|
|
const isAwait = n.type === "ForAwaitStatement" || n.await;
|
2017-03-22 19:23:57 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"for",
|
|
|
|
isAwait ? " await" : "",
|
|
|
|
" (",
|
|
|
|
path.call(print, "left"),
|
|
|
|
" of ",
|
|
|
|
path.call(print, "right"),
|
|
|
|
")",
|
|
|
|
adjustClause(n.body, path.call(print, "body"))
|
|
|
|
])
|
|
|
|
);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-03-22 19:23:57 +03:00
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
case "DoWhileStatement": {
|
2017-05-19 23:00:38 +03:00
|
|
|
const clause = adjustClause(n.body, path.call(print, "body"));
|
|
|
|
const doBody = group(concat(["do", clause]));
|
|
|
|
parts = [doBody];
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-13 06:25:44 +03:00
|
|
|
if (n.body.type === "BlockStatement") {
|
|
|
|
parts.push(" ");
|
|
|
|
} else {
|
|
|
|
parts.push(hardline);
|
|
|
|
}
|
2017-04-21 18:03:53 +03:00
|
|
|
parts.push("while (");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(
|
|
|
|
group(concat([indent(softline), path.call(print, "test"), softline])),
|
|
|
|
")",
|
|
|
|
semi
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
|
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DoExpression":
|
2017-02-27 18:49:08 +03:00
|
|
|
return concat(["do ", path.call(print, "body")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "BreakStatement":
|
|
|
|
parts.push("break");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.label) {
|
|
|
|
parts.push(" ", path.call(print, "label"));
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ContinueStatement":
|
|
|
|
parts.push("continue");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.label) {
|
|
|
|
parts.push(" ", path.call(print, "label"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "LabeledStatement":
|
2017-01-22 23:34:27 +03:00
|
|
|
if (n.body.type === "EmptyStatement") {
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat([path.call(print, "label"), ":;"]);
|
2017-01-22 23:34:27 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "label"),
|
2017-03-04 00:46:40 +03:00
|
|
|
": ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "body")
|
|
|
|
]);
|
|
|
|
case "TryStatement":
|
|
|
|
parts.push("try ", path.call(print, "block"));
|
|
|
|
|
|
|
|
if (n.handler) {
|
|
|
|
parts.push(" ", path.call(print, "handler"));
|
|
|
|
} else if (n.handlers) {
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(handlerPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
parts.push(" ", print(handlerPath));
|
|
|
|
}, "handlers");
|
2017-01-05 20:10:14 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.finalizer) {
|
|
|
|
parts.push(" finally ", path.call(print, "finalizer"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "CatchClause":
|
|
|
|
parts.push("catch (", path.call(print, "param"));
|
|
|
|
|
2017-05-25 01:57:05 +03:00
|
|
|
if (n.guard) {
|
2017-01-13 23:03:53 +03:00
|
|
|
// Note: esprima does not recognize conditional catch clauses.
|
|
|
|
parts.push(" if ", path.call(print, "guard"));
|
2017-05-25 01:57:05 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(") ", path.call(print, "body"));
|
2017-01-13 21:00:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ThrowStatement":
|
2017-04-11 23:50:47 +03:00
|
|
|
return concat(["throw ", path.call(print, "argument"), semi]);
|
2017-01-13 23:03:53 +03:00
|
|
|
// Note: ignoring n.lexical because it has no printing consequences.
|
|
|
|
case "SwitchStatement":
|
2017-01-13 21:00:32 +03:00
|
|
|
return concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"switch (",
|
|
|
|
path.call(print, "discriminant"),
|
|
|
|
") {",
|
2017-01-23 20:49:46 +03:00
|
|
|
n.cases.length > 0
|
2017-05-24 23:20:05 +03:00
|
|
|
? indent(
|
|
|
|
concat([
|
|
|
|
hardline,
|
|
|
|
join(
|
|
|
|
hardline,
|
|
|
|
path.map(casePath => {
|
|
|
|
const caseNode = casePath.getValue();
|
|
|
|
return concat([
|
|
|
|
casePath.call(print),
|
|
|
|
n.cases.indexOf(caseNode) !== n.cases.length - 1 &&
|
|
|
|
util.isNextLineEmpty(options.originalText, caseNode)
|
|
|
|
? hardline
|
|
|
|
: ""
|
|
|
|
]);
|
|
|
|
}, "cases")
|
|
|
|
)
|
|
|
|
])
|
|
|
|
)
|
2017-01-23 20:49:46 +03:00
|
|
|
: "",
|
2017-01-13 23:03:53 +03:00
|
|
|
hardline,
|
|
|
|
"}"
|
2017-01-13 21:00:32 +03:00
|
|
|
]);
|
2017-05-21 22:11:09 +03:00
|
|
|
case "SwitchCase": {
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.test) {
|
|
|
|
parts.push("case ", path.call(print, "test"), ":");
|
|
|
|
} else {
|
|
|
|
parts.push("default:");
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-05-24 23:20:05 +03:00
|
|
|
const consequent = n.consequent.filter(
|
|
|
|
node => node.type !== "EmptyStatement"
|
|
|
|
);
|
2017-04-06 02:27:14 +03:00
|
|
|
|
2017-05-24 23:20:05 +03:00
|
|
|
if (consequent.length > 0) {
|
2017-03-17 18:08:36 +03:00
|
|
|
const cons = path.call(consequentPath => {
|
2017-06-07 00:58:20 +03:00
|
|
|
return printStatementSequence(consequentPath, options, print);
|
2017-03-17 18:08:36 +03:00
|
|
|
}, "consequent");
|
2017-05-23 23:58:43 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
2017-05-23 23:58:43 +03:00
|
|
|
consequent.length === 1 && consequent[0].type === "BlockStatement"
|
2017-01-28 18:50:22 +03:00
|
|
|
? concat([" ", cons])
|
2017-04-07 05:49:37 +03:00
|
|
|
: indent(concat([hardline, cons]))
|
2017-01-13 23:03:53 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
// JSX extensions below.
|
|
|
|
case "DebuggerStatement":
|
2017-04-11 23:50:47 +03:00
|
|
|
return concat(["debugger", semi]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXAttribute":
|
|
|
|
parts.push(path.call(print, "name"));
|
|
|
|
|
|
|
|
if (n.value) {
|
|
|
|
let res;
|
2017-05-29 17:31:24 +03:00
|
|
|
if (isStringLiteral(n.value)) {
|
2017-04-08 17:55:50 +03:00
|
|
|
const value = n.value.extra ? n.value.extra.raw : n.value.raw;
|
2017-05-03 18:47:15 +03:00
|
|
|
res = '"' + value.slice(1, -1).replace(/"/g, """) + '"';
|
2017-01-13 23:03:53 +03:00
|
|
|
} else {
|
|
|
|
res = path.call(print, "value");
|
|
|
|
}
|
|
|
|
parts.push("=", res);
|
|
|
|
}
|
2017-01-13 21:00:32 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "JSXIdentifier":
|
2017-05-06 16:19:33 +03:00
|
|
|
// Can be removed when this is fixed:
|
2017-06-03 00:27:27 +03:00
|
|
|
// https://github.com/eslint/typescript-eslint-parser/issues/307
|
|
|
|
if (!n.name) {
|
|
|
|
return "this";
|
2017-05-06 16:19:33 +03:00
|
|
|
}
|
2017-01-20 00:20:03 +03:00
|
|
|
return "" + n.name;
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXNamespacedName":
|
|
|
|
return join(":", [
|
|
|
|
path.call(print, "namespace"),
|
|
|
|
path.call(print, "name")
|
|
|
|
]);
|
|
|
|
case "JSXMemberExpression":
|
|
|
|
return join(".", [
|
|
|
|
path.call(print, "object"),
|
|
|
|
path.call(print, "property")
|
|
|
|
]);
|
2017-05-22 00:16:24 +03:00
|
|
|
case "TSQualifiedName":
|
|
|
|
return join(".", [path.call(print, "left"), path.call(print, "right")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXSpreadAttribute":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["{...", path.call(print, "argument"), "}"]);
|
2017-01-26 22:51:08 +03:00
|
|
|
case "JSXExpressionContainer": {
|
|
|
|
const parent = path.getParentNode(0);
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
const shouldInline =
|
|
|
|
n.expression.type === "ArrayExpression" ||
|
2017-01-26 22:51:08 +03:00
|
|
|
n.expression.type === "ObjectExpression" ||
|
|
|
|
n.expression.type === "ArrowFunctionExpression" ||
|
|
|
|
n.expression.type === "CallExpression" ||
|
|
|
|
n.expression.type === "FunctionExpression" ||
|
2017-02-05 05:37:18 +03:00
|
|
|
n.expression.type === "JSXEmptyExpression" ||
|
2017-04-11 19:33:02 +03:00
|
|
|
n.expression.type === "TemplateLiteral" ||
|
|
|
|
n.expression.type === "TaggedTemplateExpression" ||
|
2017-02-23 20:57:51 +03:00
|
|
|
(parent.type === "JSXElement" &&
|
2017-01-28 18:50:22 +03:00
|
|
|
(n.expression.type === "ConditionalExpression" ||
|
2017-03-09 18:59:53 +03:00
|
|
|
isBinaryish(n.expression)));
|
2017-01-26 22:51:08 +03:00
|
|
|
|
|
|
|
if (shouldInline) {
|
2017-02-23 20:57:51 +03:00
|
|
|
return group(
|
|
|
|
concat(["{", path.call(print, "expression"), lineSuffixBoundary, "}"])
|
|
|
|
);
|
2017-01-19 20:37:02 +03:00
|
|
|
}
|
|
|
|
|
2017-01-24 02:47:11 +03:00
|
|
|
return group(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
|
|
|
"{",
|
2017-04-12 20:16:11 +03:00
|
|
|
indent(concat([softline, path.call(print, "expression")])),
|
2017-01-13 23:03:53 +03:00
|
|
|
softline,
|
2017-02-23 20:26:26 +03:00
|
|
|
lineSuffixBoundary,
|
2017-01-13 23:03:53 +03:00
|
|
|
"}"
|
|
|
|
])
|
|
|
|
);
|
2017-01-26 22:51:08 +03:00
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
case "JSXElement": {
|
2017-05-24 23:19:47 +03:00
|
|
|
const elem = comments.printComments(
|
|
|
|
path,
|
|
|
|
() => printJSXElement(path, options, print),
|
|
|
|
options
|
|
|
|
);
|
2017-05-26 23:01:03 +03:00
|
|
|
return maybeWrapJSXElementInParens(path, elem);
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
2017-01-27 01:22:56 +03:00
|
|
|
case "JSXOpeningElement": {
|
|
|
|
const n = path.getValue();
|
|
|
|
|
|
|
|
// don't break up opening elements with a single long text attribute
|
2017-01-28 18:50:22 +03:00
|
|
|
if (
|
|
|
|
n.attributes.length === 1 &&
|
2017-02-16 06:56:11 +03:00
|
|
|
n.attributes[0].value &&
|
2017-05-29 17:31:24 +03:00
|
|
|
isStringLiteral(n.attributes[0].value)
|
2017-01-27 01:22:56 +03:00
|
|
|
) {
|
2017-01-28 18:50:22 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"<",
|
|
|
|
path.call(print, "name"),
|
|
|
|
" ",
|
|
|
|
concat(path.map(print, "attributes")),
|
|
|
|
n.selfClosing ? " />" : ">"
|
|
|
|
])
|
|
|
|
);
|
2017-01-27 01:22:56 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return group(
|
2017-01-09 20:09:04 +03:00
|
|
|
concat([
|
2017-01-13 23:03:53 +03:00
|
|
|
"<",
|
|
|
|
path.call(print, "name"),
|
2017-01-24 21:54:01 +03:00
|
|
|
concat([
|
|
|
|
indent(
|
|
|
|
concat(
|
2017-01-28 18:50:22 +03:00
|
|
|
path.map(attr => concat([line, print(attr)]), "attributes")
|
2017-01-24 21:54:01 +03:00
|
|
|
)
|
|
|
|
),
|
2017-02-16 06:56:11 +03:00
|
|
|
n.selfClosing ? line : options.jsxBracketSameLine ? ">" : softline
|
2017-01-24 21:54:01 +03:00
|
|
|
]),
|
2017-02-16 06:56:11 +03:00
|
|
|
n.selfClosing ? "/>" : options.jsxBracketSameLine ? "" : ">"
|
2017-01-09 20:09:04 +03:00
|
|
|
])
|
|
|
|
);
|
2017-01-27 01:22:56 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXClosingElement":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["</", path.call(print, "name"), ">"]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "JSXText":
|
|
|
|
throw new Error("JSXTest should be handled by JSXElement");
|
2017-05-21 22:11:09 +03:00
|
|
|
case "JSXEmptyExpression": {
|
2017-05-21 21:00:16 +03:00
|
|
|
const requiresHardline =
|
|
|
|
n.comments && !n.comments.every(util.isBlockComment);
|
2017-03-11 05:23:11 +03:00
|
|
|
|
|
|
|
return concat([
|
|
|
|
comments.printDanglingComments(
|
|
|
|
path,
|
|
|
|
options,
|
2017-03-16 23:26:44 +03:00
|
|
|
/* sameIndent */ !requiresHardline
|
2017-03-11 05:23:11 +03:00
|
|
|
),
|
|
|
|
requiresHardline ? hardline : ""
|
|
|
|
]);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-06-08 19:25:28 +03:00
|
|
|
case "Keyword": {
|
|
|
|
return n.name;
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "TypeAnnotatedIdentifier":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "annotation"),
|
|
|
|
" ",
|
|
|
|
path.call(print, "identifier")
|
|
|
|
]);
|
|
|
|
case "ClassBody":
|
2017-02-04 00:56:06 +03:00
|
|
|
if (!n.comments && n.body.length === 0) {
|
|
|
|
return "{}";
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-02-03 19:50:51 +03:00
|
|
|
return concat([
|
|
|
|
"{",
|
2017-02-09 18:44:56 +03:00
|
|
|
n.body.length > 0
|
|
|
|
? indent(
|
|
|
|
concat([
|
|
|
|
hardline,
|
2017-05-21 22:11:09 +03:00
|
|
|
path.call(bodyPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
}, "body")
|
2017-02-09 18:44:56 +03:00
|
|
|
])
|
2017-01-13 23:03:53 +03:00
|
|
|
)
|
2017-02-09 18:44:56 +03:00
|
|
|
: comments.printDanglingComments(path, options),
|
2017-02-03 19:50:51 +03:00
|
|
|
hardline,
|
|
|
|
"}"
|
|
|
|
]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ClassPropertyDefinition":
|
|
|
|
parts.push("static ", path.call(print, "definition"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-26 21:45:24 +03:00
|
|
|
if (
|
|
|
|
n.definition.type !== "MethodDefinition" &&
|
|
|
|
n.definition.type !== "TSAbstractMethodDefinition"
|
|
|
|
) {
|
2017-05-03 03:06:25 +03:00
|
|
|
parts.push(semi);
|
|
|
|
}
|
2017-01-09 20:07:39 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "ClassProperty":
|
2017-05-19 23:00:38 +03:00
|
|
|
case "TSAbstractClassProperty": {
|
2017-05-26 23:01:03 +03:00
|
|
|
const variance = getFlowVariance(n);
|
2017-05-03 03:06:25 +03:00
|
|
|
if (variance) {
|
|
|
|
parts.push(variance);
|
|
|
|
}
|
|
|
|
if (n.accessibility) {
|
|
|
|
parts.push(n.accessibility + " ");
|
|
|
|
}
|
2017-06-03 00:27:19 +03:00
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.type === "TSAbstractClassProperty") {
|
|
|
|
parts.push("abstract ");
|
|
|
|
}
|
2017-06-02 18:09:24 +03:00
|
|
|
if (n.readonly) {
|
|
|
|
parts.push("readonly ");
|
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.computed) {
|
2017-05-01 03:41:19 +03:00
|
|
|
parts.push("[", path.call(print, "key"), "]");
|
2017-01-11 22:37:53 +03:00
|
|
|
} else {
|
2017-05-01 03:41:19 +03:00
|
|
|
parts.push(printPropertyKey(path, options, print));
|
2017-01-11 22:37:53 +03:00
|
|
|
}
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.typeAnnotation) {
|
|
|
|
parts.push(": ", path.call(print, "typeAnnotation"));
|
|
|
|
}
|
|
|
|
if (n.value) {
|
2017-05-24 03:13:40 +03:00
|
|
|
parts.push(
|
|
|
|
" =",
|
|
|
|
printAssignmentRight(
|
|
|
|
n.value,
|
|
|
|
path.call(print, "value"),
|
|
|
|
false, // canBreak
|
|
|
|
options
|
|
|
|
)
|
|
|
|
);
|
2017-05-03 03:06:25 +03:00
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ClassDeclaration":
|
|
|
|
case "ClassExpression":
|
2017-04-26 19:25:58 +03:00
|
|
|
case "TSAbstractClassDeclaration":
|
2017-04-27 19:37:42 +03:00
|
|
|
if (isNodeStartingWithDeclare(n, options)) {
|
|
|
|
parts.push("declare ");
|
|
|
|
}
|
|
|
|
parts.push(concat(printClass(path, options, print)));
|
|
|
|
return concat(parts);
|
2017-05-13 03:13:38 +03:00
|
|
|
case "TSInterfaceHeritage":
|
2017-05-20 13:15:50 +03:00
|
|
|
parts.push(path.call(print, "id"));
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-20 13:15:50 +03:00
|
|
|
if (n.typeParameters) {
|
|
|
|
parts.push(path.call(print, "typeParameters"));
|
|
|
|
}
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-20 13:15:50 +03:00
|
|
|
return concat(parts);
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSHeritageClause":
|
|
|
|
return join(", ", path.map(print, "types"));
|
|
|
|
case "TSExpressionWithTypeArguments":
|
2017-05-06 16:17:48 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "expression"),
|
|
|
|
printTypeParameters(path, options, print, "typeArguments")
|
|
|
|
]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "TemplateElement":
|
2017-05-02 00:39:03 +03:00
|
|
|
return join(literalline, n.value.raw.split(/\r?\n/g));
|
2017-05-21 22:11:09 +03:00
|
|
|
case "TemplateLiteral": {
|
2017-06-06 18:28:13 +03:00
|
|
|
const parent = path.getParentNode();
|
|
|
|
const parentParent = path.getParentNode(1);
|
|
|
|
const isCSS =
|
|
|
|
n.quasis &&
|
|
|
|
n.quasis.length === 1 &&
|
|
|
|
parent.type === "JSXExpressionContainer" &&
|
|
|
|
parentParent.type === "JSXElement" &&
|
|
|
|
parentParent.openingElement.name.name === "style" &&
|
|
|
|
parentParent.openingElement.attributes.some(
|
|
|
|
attribute => attribute.name.name === "jsx"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isCSS) {
|
|
|
|
const parseCss = eval("require")("./parser-postcss");
|
|
|
|
const newOptions = Object.assign({}, options, { parser: "postcss" });
|
|
|
|
const text = n.quasis[0].value.raw;
|
|
|
|
try {
|
|
|
|
const ast = parseCss(text, newOptions);
|
|
|
|
let subtree = printAstToDoc(ast, newOptions);
|
|
|
|
|
|
|
|
// HACK remove ending hardline
|
|
|
|
assert.ok(
|
|
|
|
subtree.type === "concat" &&
|
|
|
|
subtree.parts[0].type === "concat" &&
|
|
|
|
subtree.parts[0].parts.length === 2 &&
|
|
|
|
subtree.parts[0].parts[1] === hardline
|
|
|
|
);
|
|
|
|
subtree = subtree.parts[0].parts[0];
|
|
|
|
|
|
|
|
parts.push("`", indent(concat([line, subtree])), line, "`");
|
|
|
|
return group(concat(parts));
|
|
|
|
} catch (error) {
|
|
|
|
// If CSS parsing (or printing) failed
|
|
|
|
// we give up and just print the TemplateElement as usual
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const expressions = path.map(print, "expressions");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("`");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(childPath => {
|
2017-05-19 23:00:38 +03:00
|
|
|
const i = childPath.getName();
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
parts.push(print(childPath));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
if (i < expressions.length) {
|
2017-05-02 00:28:11 +03:00
|
|
|
// For a template literal of the following form:
|
|
|
|
// `someQuery {
|
|
|
|
// ${call({
|
|
|
|
// a,
|
|
|
|
// b,
|
|
|
|
// })}
|
|
|
|
// }`
|
|
|
|
// the expression is on its own line (there is a \n in the previous
|
|
|
|
// quasi literal), therefore we want to indent the JavaScript
|
|
|
|
// expression inside at the beginning of ${ instead of the beginning
|
|
|
|
// of the `.
|
|
|
|
let size = 0;
|
|
|
|
const value = childPath.getValue().value.raw;
|
2017-05-21 21:00:16 +03:00
|
|
|
const index = value.lastIndexOf("\n");
|
2017-05-02 00:28:11 +03:00
|
|
|
const tabWidth = options.tabWidth;
|
|
|
|
if (index !== -1) {
|
2017-05-24 23:19:34 +03:00
|
|
|
size = util.getAlignmentSize(
|
|
|
|
// All the leading whitespaces
|
|
|
|
value.slice(index + 1).match(/^[ \t]*/)[0],
|
|
|
|
tabWidth
|
|
|
|
);
|
2017-05-02 00:28:11 +03:00
|
|
|
}
|
|
|
|
|
2017-05-22 19:00:01 +03:00
|
|
|
const aligned = addAlignmentToDoc(expressions[i], size, tabWidth);
|
2017-05-02 00:28:11 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push("${", aligned, lineSuffixBoundary, "}");
|
2017-04-12 20:16:11 +03:00
|
|
|
}
|
|
|
|
}, "quasis");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push("`");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
// These types are unprintable because they serve as abstract
|
|
|
|
// supertypes for other (printable) types.
|
|
|
|
case "TaggedTemplateExpression":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat([path.call(print, "tag"), path.call(print, "quasi")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "Node":
|
|
|
|
case "Printable":
|
|
|
|
case "SourceLocation":
|
|
|
|
case "Position":
|
|
|
|
case "Statement":
|
|
|
|
case "Function":
|
|
|
|
case "Pattern":
|
|
|
|
case "Expression":
|
|
|
|
case "Declaration":
|
|
|
|
case "Specifier":
|
|
|
|
case "NamedSpecifier":
|
|
|
|
case "Comment":
|
2017-01-28 18:50:22 +03:00
|
|
|
case "MemberTypeAnnotation": // Flow
|
|
|
|
case "Type":
|
2017-01-13 23:03:53 +03:00
|
|
|
throw new Error("unprintable type: " + JSON.stringify(n.type));
|
|
|
|
// Type Annotations for Facebook Flow, typically stripped out or
|
|
|
|
// transformed away before printing.
|
|
|
|
case "TypeAnnotation":
|
|
|
|
if (n.typeAnnotation) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return path.call(print, "typeAnnotation");
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return "";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSTupleType":
|
2017-05-21 22:11:09 +03:00
|
|
|
case "TupleTypeAnnotation": {
|
|
|
|
const typesField = n.type === "TSTupleType" ? "elementTypes" : "types";
|
2017-03-15 19:35:22 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"[",
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
softline,
|
2017-03-19 18:06:52 +03:00
|
|
|
printArrayItems(path, options, typesField, print)
|
2017-03-15 19:35:22 +03:00
|
|
|
])
|
|
|
|
),
|
2017-06-02 17:16:41 +03:00
|
|
|
// TypeScript doesn't support trailing commas in tuple types
|
|
|
|
n.type === "TSTupleType"
|
|
|
|
? ""
|
|
|
|
: ifBreak(shouldPrintComma(options) ? "," : ""),
|
2017-04-12 20:16:11 +03:00
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true),
|
2017-03-15 19:35:22 +03:00
|
|
|
softline,
|
|
|
|
"]"
|
|
|
|
])
|
|
|
|
);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-03-15 19:35:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ExistsTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "*";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "EmptyTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "empty";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "AnyTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "any";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "MixedTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "mixed";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ArrayTypeAnnotation":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat([path.call(print, "elementType"), "[]"]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "BooleanTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "boolean";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "BooleanLiteralTypeAnnotation":
|
|
|
|
return "" + n.value;
|
|
|
|
case "DeclareClass":
|
2017-01-26 00:36:55 +03:00
|
|
|
return printFlowDeclaration(path, printClass(path, options, print));
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DeclareFunction":
|
2017-03-19 18:06:52 +03:00
|
|
|
// For TypeScript the DeclareFunction node shares the AST
|
|
|
|
// structure with FunctionDeclaration
|
|
|
|
if (n.params) {
|
|
|
|
return concat([
|
|
|
|
"declare ",
|
2017-04-12 20:16:11 +03:00
|
|
|
printFunctionDeclaration(path, print, options)
|
|
|
|
]);
|
2017-03-19 18:06:52 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"function ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
n.predicate ? " " : "",
|
|
|
|
path.call(print, "predicate"),
|
2017-04-11 23:50:47 +03:00
|
|
|
semi
|
2017-01-13 23:03:53 +03:00
|
|
|
]);
|
|
|
|
case "DeclareModule":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"module ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
" ",
|
|
|
|
path.call(print, "body")
|
|
|
|
]);
|
|
|
|
case "DeclareModuleExports":
|
|
|
|
return printFlowDeclaration(path, [
|
|
|
|
"module.exports",
|
2017-04-23 04:09:53 +03:00
|
|
|
": ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "typeAnnotation"),
|
2017-04-11 23:50:47 +03:00
|
|
|
semi
|
2017-01-13 23:03:53 +03:00
|
|
|
]);
|
|
|
|
case "DeclareVariable":
|
2017-04-11 23:50:47 +03:00
|
|
|
return printFlowDeclaration(path, ["var ", path.call(print, "id"), semi]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DeclareExportAllDeclaration":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["declare export * from ", path.call(print, "source")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DeclareExportDeclaration":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["declare ", printExportDeclaration(path, options, print)]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "FunctionTypeAnnotation":
|
2017-05-19 23:00:38 +03:00
|
|
|
case "TSFunctionType": {
|
2017-01-13 23:03:53 +03:00
|
|
|
// FunctionTypeAnnotation is ambiguous:
|
|
|
|
// declare function foo(a: B): void; OR
|
|
|
|
// var A: (a: B) => void;
|
2017-05-19 23:00:38 +03:00
|
|
|
const parent = path.getParentNode(0);
|
|
|
|
const parentParent = path.getParentNode(1);
|
2017-05-26 21:45:24 +03:00
|
|
|
const parentParentParent = path.getParentNode(2);
|
2017-05-19 23:00:38 +03:00
|
|
|
let isArrowFunctionTypeAnnotation =
|
2017-04-12 20:16:11 +03:00
|
|
|
n.type === "TSFunctionType" ||
|
2017-06-02 21:48:43 +03:00
|
|
|
!(
|
|
|
|
(parent.type === "ObjectTypeProperty" &&
|
|
|
|
!getFlowVariance(parent) &&
|
|
|
|
!parent.optional &&
|
|
|
|
util.locStart(parent) === util.locStart(n)) ||
|
2017-05-26 21:45:24 +03:00
|
|
|
parent.type === "ObjectTypeCallProperty" ||
|
2017-06-02 21:48:43 +03:00
|
|
|
(parentParentParent && parentParentParent.type === "DeclareFunction")
|
|
|
|
);
|
2017-04-12 20:16:11 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
let needsColon =
|
2017-05-26 21:45:24 +03:00
|
|
|
isArrowFunctionTypeAnnotation && parent.type === "TypeAnnotation";
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-04-10 20:51:31 +03:00
|
|
|
// Sadly we can't put it inside of FastPath::needsColon because we are
|
|
|
|
// printing ":" as part of the expression and it would put parenthesis
|
|
|
|
// around :(
|
2017-04-12 20:16:11 +03:00
|
|
|
const needsParens =
|
|
|
|
needsColon &&
|
2017-04-10 20:51:31 +03:00
|
|
|
isArrowFunctionTypeAnnotation &&
|
|
|
|
parent.type === "TypeAnnotation" &&
|
|
|
|
parentParent.type === "ArrowFunctionExpression";
|
|
|
|
|
2017-02-05 05:23:37 +03:00
|
|
|
if (isObjectTypePropertyAFunction(parent)) {
|
|
|
|
isArrowFunctionTypeAnnotation = true;
|
|
|
|
needsColon = true;
|
|
|
|
}
|
|
|
|
|
2017-04-10 20:51:31 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.push("(");
|
|
|
|
}
|
2017-04-24 03:33:00 +03:00
|
|
|
|
2017-05-06 05:44:26 +03:00
|
|
|
parts.push(
|
|
|
|
printFunctionTypeParameters(path, options, print),
|
|
|
|
printFunctionParams(path, print, options)
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
// The returnType is not wrapped in a TypeAnnotation, so the colon
|
|
|
|
// needs to be added separately.
|
2017-03-19 18:06:52 +03:00
|
|
|
if (n.returnType || n.predicate || n.typeAnnotation) {
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(
|
|
|
|
isArrowFunctionTypeAnnotation ? " => " : ": ",
|
|
|
|
path.call(print, "returnType"),
|
2017-03-19 18:06:52 +03:00
|
|
|
path.call(print, "predicate"),
|
|
|
|
path.call(print, "typeAnnotation")
|
2017-01-13 23:03:53 +03:00
|
|
|
);
|
|
|
|
}
|
2017-04-10 20:51:31 +03:00
|
|
|
if (needsParens) {
|
|
|
|
parts.push(")");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-03-20 19:35:30 +03:00
|
|
|
return group(concat(parts));
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "FunctionTypeParam":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "name"),
|
|
|
|
n.optional ? "?" : "",
|
|
|
|
n.name ? ": " : "",
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
|
|
|
case "GenericTypeAnnotation":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters")
|
|
|
|
]);
|
|
|
|
case "DeclareInterface":
|
2017-01-14 07:15:30 +03:00
|
|
|
case "InterfaceDeclaration": {
|
2017-02-13 20:17:20 +03:00
|
|
|
if (
|
|
|
|
n.type === "DeclareInterface" ||
|
2017-04-27 19:37:42 +03:00
|
|
|
isNodeStartingWithDeclare(n, options)
|
2017-02-13 20:17:20 +03:00
|
|
|
) {
|
2017-01-14 07:15:30 +03:00
|
|
|
parts.push("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(
|
2017-01-20 00:20:03 +03:00
|
|
|
"interface ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "id"),
|
2017-04-05 00:25:17 +03:00
|
|
|
path.call(print, "typeParameters")
|
2016-12-23 21:38:10 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n["extends"].length > 0) {
|
2017-04-05 00:25:17 +03:00
|
|
|
parts.push(
|
|
|
|
group(
|
|
|
|
indent(
|
2017-04-12 20:16:11 +03:00
|
|
|
concat([line, "extends ", join(", ", path.map(print, "extends"))])
|
2017-04-05 00:25:17 +03:00
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-05 00:25:17 +03:00
|
|
|
parts.push(" ");
|
2017-01-14 07:15:30 +03:00
|
|
|
parts.push(path.call(print, "body"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-05 00:25:17 +03:00
|
|
|
return group(concat(parts));
|
2017-01-14 07:15:30 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ClassImplements":
|
|
|
|
case "InterfaceExtends":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters")
|
|
|
|
]);
|
2017-04-23 20:27:40 +03:00
|
|
|
case "TSIntersectionType":
|
2017-03-22 20:01:34 +03:00
|
|
|
case "IntersectionTypeAnnotation": {
|
|
|
|
const types = path.map(print, "types");
|
|
|
|
const result = [];
|
|
|
|
for (let i = 0; i < types.length; ++i) {
|
|
|
|
if (i === 0) {
|
|
|
|
result.push(types[i]);
|
2017-06-03 01:40:33 +03:00
|
|
|
} else if (!isObjectType(n.types[i - 1]) && !isObjectType(n.types[i])) {
|
2017-04-10 20:50:38 +03:00
|
|
|
// If no object is involved, go to the next line if it breaks
|
2017-04-07 05:49:37 +03:00
|
|
|
result.push(indent(concat([" &", line, types[i]])));
|
2017-04-10 20:50:38 +03:00
|
|
|
} else {
|
|
|
|
// If you go from object to non-object or vis-versa, then inline it
|
|
|
|
result.push(" & ", i > 1 ? indent(types[i]) : types[i]);
|
2017-03-22 20:01:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return group(concat(result));
|
|
|
|
}
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSUnionType":
|
2017-01-13 23:03:53 +03:00
|
|
|
case "UnionTypeAnnotation": {
|
2017-01-27 05:41:50 +03:00
|
|
|
// single-line variation
|
|
|
|
// A | B | C
|
|
|
|
|
|
|
|
// multi-line variation
|
|
|
|
// | A
|
|
|
|
// | B
|
|
|
|
// | C
|
2017-02-20 18:18:39 +03:00
|
|
|
|
2017-03-04 00:54:45 +03:00
|
|
|
const parent = path.getParentNode();
|
|
|
|
// If there's a leading comment, the parent is doing the indentation
|
2017-05-21 21:00:16 +03:00
|
|
|
const shouldIndent =
|
2017-05-05 05:06:30 +03:00
|
|
|
parent.type !== "TypeParameterInstantiation" &&
|
2017-05-26 02:49:25 +03:00
|
|
|
parent.type !== "GenericTypeAnnotation" &&
|
2017-06-02 21:48:43 +03:00
|
|
|
!(
|
|
|
|
(parent.type === "TypeAlias" ||
|
|
|
|
parent.type === "VariableDeclarator") &&
|
|
|
|
hasLeadingOwnLineComment(options.originalText, n)
|
|
|
|
);
|
2017-03-04 00:54:45 +03:00
|
|
|
|
2017-05-26 02:26:40 +03:00
|
|
|
// {
|
|
|
|
// a: string
|
|
|
|
// } | null | void
|
|
|
|
// should be inlined and not be printed in the multi-line variant
|
|
|
|
const shouldHug = shouldHugType(n);
|
|
|
|
|
|
|
|
// We want to align the children but without its comment, so it looks like
|
|
|
|
// | child1
|
|
|
|
// // comment
|
|
|
|
// | child2
|
|
|
|
const printed = path.map(typePath => {
|
|
|
|
let printedType = typePath.call(print);
|
|
|
|
if (!shouldHug && shouldIndent) {
|
|
|
|
printedType = align(2, printedType);
|
|
|
|
}
|
|
|
|
return comments.printComments(typePath, () => printedType, options);
|
|
|
|
}, "types");
|
|
|
|
|
|
|
|
if (shouldHug) {
|
|
|
|
return join(" | ", printed);
|
|
|
|
}
|
|
|
|
|
2017-04-07 05:49:37 +03:00
|
|
|
const code = concat([
|
|
|
|
ifBreak(concat([shouldIndent ? line : "", "| "])),
|
2017-05-26 02:26:40 +03:00
|
|
|
join(concat([line, "| "]), printed)
|
2017-04-07 05:49:37 +03:00
|
|
|
]);
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
return group(shouldIndent ? indent(code) : code);
|
2016-12-31 07:03:22 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NullableTypeAnnotation":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["?", path.call(print, "typeAnnotation")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NullLiteralTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "null";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ThisTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "this";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NumberTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "number";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "ObjectTypeCallProperty":
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "value"));
|
|
|
|
|
|
|
|
return concat(parts);
|
2017-05-19 23:00:38 +03:00
|
|
|
case "ObjectTypeIndexer": {
|
2017-05-26 23:01:03 +03:00
|
|
|
const variance = getFlowVariance(n);
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
2017-03-22 19:37:26 +03:00
|
|
|
variance || "",
|
2017-01-13 23:03:53 +03:00
|
|
|
"[",
|
|
|
|
path.call(print, "id"),
|
|
|
|
n.id ? ": " : "",
|
|
|
|
path.call(print, "key"),
|
|
|
|
"]: ",
|
|
|
|
path.call(print, "value")
|
|
|
|
]);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
|
|
|
case "ObjectTypeProperty": {
|
2017-05-26 23:01:03 +03:00
|
|
|
const variance = getFlowVariance(n);
|
2017-02-05 05:23:37 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat([
|
|
|
|
n.static ? "static " : "",
|
2017-05-12 02:29:31 +03:00
|
|
|
isGetterOrSetter(n) ? n.kind + " " : "",
|
2017-03-22 19:37:26 +03:00
|
|
|
variance || "",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "key"),
|
|
|
|
n.optional ? "?" : "",
|
2017-05-12 02:29:31 +03:00
|
|
|
isFunctionNotation(n) ? "" : ": ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "value")
|
|
|
|
]);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "QualifiedTypeIdentifier":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "qualification"),
|
|
|
|
".",
|
|
|
|
path.call(print, "id")
|
|
|
|
]);
|
|
|
|
case "StringLiteralTypeAnnotation":
|
2017-01-20 00:20:03 +03:00
|
|
|
return nodeStr(n, options);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "NumberLiteralTypeAnnotation":
|
|
|
|
assert.strictEqual(typeof n.value, "number");
|
|
|
|
|
2017-04-05 23:22:23 +03:00
|
|
|
if (n.extra != null) {
|
|
|
|
return printNumber(n.extra.raw);
|
|
|
|
}
|
2017-06-06 01:12:59 +03:00
|
|
|
return printNumber(n.raw);
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
case "StringTypeAnnotation":
|
2017-01-20 21:12:37 +03:00
|
|
|
return "string";
|
2017-01-13 23:03:53 +03:00
|
|
|
case "DeclareTypeAlias":
|
|
|
|
case "TypeAlias": {
|
2017-02-13 20:17:20 +03:00
|
|
|
if (
|
|
|
|
n.type === "DeclareTypeAlias" ||
|
2017-04-27 19:37:42 +03:00
|
|
|
isNodeStartingWithDeclare(n, options)
|
2017-02-13 20:17:20 +03:00
|
|
|
) {
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
const canBreak = n.right.type === "StringLiteralTypeAnnotation";
|
2017-04-27 21:21:42 +03:00
|
|
|
|
|
|
|
const printed = printAssignmentRight(
|
|
|
|
n.right,
|
|
|
|
path.call(print, "right"),
|
|
|
|
canBreak,
|
|
|
|
options
|
|
|
|
);
|
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push(
|
2016-12-23 21:38:10 +03:00
|
|
|
"type ",
|
|
|
|
path.call(print, "id"),
|
|
|
|
path.call(print, "typeParameters"),
|
2017-03-04 00:54:45 +03:00
|
|
|
" =",
|
2017-04-27 21:21:42 +03:00
|
|
|
printed,
|
2017-04-11 23:50:47 +03:00
|
|
|
semi
|
2016-12-30 08:01:44 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-27 21:21:42 +03:00
|
|
|
return group(concat(parts));
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "TypeCastExpression":
|
|
|
|
return concat([
|
|
|
|
"(",
|
|
|
|
path.call(print, "expression"),
|
2017-04-23 04:09:53 +03:00
|
|
|
": ",
|
2017-01-13 23:03:53 +03:00
|
|
|
path.call(print, "typeAnnotation"),
|
|
|
|
")"
|
|
|
|
]);
|
|
|
|
case "TypeParameterDeclaration":
|
2017-05-03 18:01:45 +03:00
|
|
|
case "TypeParameterInstantiation":
|
|
|
|
return printTypeParameters(path, options, print, "params");
|
2017-05-19 23:00:38 +03:00
|
|
|
case "TypeParameter": {
|
2017-05-26 23:01:03 +03:00
|
|
|
const variance = getFlowVariance(n);
|
2017-01-13 23:03:53 +03:00
|
|
|
|
2017-03-22 19:37:26 +03:00
|
|
|
if (variance) {
|
|
|
|
parts.push(variance);
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "name"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n.bound) {
|
2017-04-23 04:09:53 +03:00
|
|
|
parts.push(": ");
|
2017-01-13 23:03:53 +03:00
|
|
|
parts.push(path.call(print, "bound"));
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
if (n.constraint) {
|
|
|
|
parts.push(" extends ", path.call(print, "constraint"));
|
|
|
|
}
|
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (n["default"]) {
|
2017-05-02 00:39:15 +03:00
|
|
|
parts.push(" = ", path.call(print, "default"));
|
2017-01-13 23:03:53 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
return concat(parts);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-01-13 23:03:53 +03:00
|
|
|
case "TypeofTypeAnnotation":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["typeof ", path.call(print, "argument")]);
|
2017-01-13 23:03:53 +03:00
|
|
|
case "VoidTypeAnnotation":
|
|
|
|
return "void";
|
|
|
|
case "NullTypeAnnotation":
|
|
|
|
return "null";
|
|
|
|
case "InferredPredicate":
|
|
|
|
return "%checks";
|
|
|
|
// Unhandled types below. If encountered, nodes of these types should
|
|
|
|
// be either left alone or desugared into AST types that are fully
|
|
|
|
// supported by the pretty-printer.
|
|
|
|
case "DeclaredPredicate":
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat(["%checks(", path.call(print, "value"), ")"]);
|
2017-05-06 20:30:59 +03:00
|
|
|
case "TSAbstractKeyword":
|
|
|
|
return "abstract";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSAnyKeyword":
|
|
|
|
return "any";
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSAsyncKeyword":
|
|
|
|
return "async";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSBooleanKeyword":
|
|
|
|
return "boolean";
|
2017-05-06 20:30:59 +03:00
|
|
|
case "TSConstKeyword":
|
|
|
|
return "const";
|
|
|
|
case "TSDeclareKeyword":
|
2017-05-21 21:00:16 +03:00
|
|
|
return "declare";
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSExportKeyword":
|
|
|
|
return "export";
|
2017-05-06 20:30:59 +03:00
|
|
|
case "TSNeverKeyword":
|
|
|
|
return "never";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSNumberKeyword":
|
|
|
|
return "number";
|
|
|
|
case "TSObjectKeyword":
|
|
|
|
return "object";
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSProtectedKeyword":
|
|
|
|
return "protected";
|
|
|
|
case "TSPrivateKeyword":
|
|
|
|
return "private";
|
|
|
|
case "TSPublicKeyword":
|
|
|
|
return "public";
|
2017-05-06 16:18:23 +03:00
|
|
|
case "TSReadonlyKeyword":
|
|
|
|
return "readonly";
|
2017-05-06 20:30:59 +03:00
|
|
|
case "TSSymbolKeyword":
|
|
|
|
return "symbol";
|
2017-05-03 03:06:25 +03:00
|
|
|
case "TSStaticKeyword":
|
|
|
|
return "static";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSStringKeyword":
|
|
|
|
return "string";
|
2017-05-06 20:30:59 +03:00
|
|
|
case "TSUndefinedKeyword":
|
|
|
|
return "undefined";
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSVoidKeyword":
|
|
|
|
return "void";
|
|
|
|
case "TSAsExpression":
|
|
|
|
return concat([
|
2017-04-05 23:28:02 +03:00
|
|
|
path.call(print, "expression"),
|
|
|
|
" as ",
|
2017-04-12 20:16:11 +03:00
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
2017-03-19 18:06:52 +03:00
|
|
|
case "TSArrayType":
|
|
|
|
return concat([path.call(print, "elementType"), "[]"]);
|
2017-05-21 22:11:09 +03:00
|
|
|
case "TSPropertySignature": {
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.accessibility) {
|
|
|
|
parts.push(n.accessibility + " ");
|
|
|
|
}
|
|
|
|
if (n.export) {
|
|
|
|
parts.push("export ");
|
|
|
|
}
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2017-06-02 18:09:24 +03:00
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.readonly) {
|
|
|
|
parts.push("readonly ");
|
|
|
|
}
|
2017-05-06 18:00:26 +03:00
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.computed) {
|
2017-05-06 18:00:26 +03:00
|
|
|
parts.push("[");
|
|
|
|
}
|
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
parts.push(path.call(print, "key"));
|
2017-05-06 18:00:26 +03:00
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.computed) {
|
2017-05-06 18:00:26 +03:00
|
|
|
parts.push("]");
|
|
|
|
}
|
2017-05-04 00:35:58 +03:00
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.optional) {
|
2017-05-07 08:34:47 +03:00
|
|
|
parts.push("?");
|
|
|
|
}
|
|
|
|
|
2017-05-03 02:03:51 +03:00
|
|
|
if (n.typeAnnotation) {
|
|
|
|
parts.push(": ");
|
|
|
|
parts.push(path.call(print, "typeAnnotation"));
|
|
|
|
}
|
2017-03-19 18:06:52 +03:00
|
|
|
|
2017-05-07 08:34:47 +03:00
|
|
|
// This isn't valid semantically, but it's in the AST so we can print it.
|
|
|
|
if (n.initializer) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(" = ", path.call(print, "initializer"));
|
2017-05-07 08:34:47 +03:00
|
|
|
}
|
|
|
|
|
2017-04-26 19:25:58 +03:00
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-04-26 19:25:58 +03:00
|
|
|
case "TSParameterProperty":
|
|
|
|
if (n.accessibility) {
|
|
|
|
parts.push(n.accessibility + " ");
|
|
|
|
}
|
2017-05-28 23:18:29 +03:00
|
|
|
if (n.export) {
|
|
|
|
parts.push("export ");
|
|
|
|
}
|
|
|
|
if (n.static) {
|
|
|
|
parts.push("static ");
|
|
|
|
}
|
2017-05-29 16:26:53 +03:00
|
|
|
if (n.readonly) {
|
2017-04-26 19:25:58 +03:00
|
|
|
parts.push("readonly ");
|
|
|
|
}
|
|
|
|
|
|
|
|
parts.push(path.call(print, "parameter"));
|
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
return concat(parts);
|
|
|
|
case "TSTypeReference":
|
|
|
|
return concat([
|
2017-05-03 18:01:45 +03:00
|
|
|
path.call(print, "typeName"),
|
2017-05-20 13:15:50 +03:00
|
|
|
printTypeParameters(path, options, print, "typeParameters")
|
2017-03-19 18:06:52 +03:00
|
|
|
]);
|
|
|
|
case "TSTypeQuery":
|
|
|
|
return concat(["typeof ", path.call(print, "exprName")]);
|
|
|
|
case "TSParenthesizedType":
|
|
|
|
return concat(["(", path.call(print, "typeAnnotation"), ")"]);
|
2017-05-28 23:18:29 +03:00
|
|
|
case "TSIndexSignature": {
|
2017-06-04 17:21:33 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-05-28 23:18:29 +03:00
|
|
|
let printedParams = [];
|
|
|
|
if (n.params) {
|
|
|
|
printedParams = path.map(print, "params");
|
|
|
|
}
|
|
|
|
if (n.parameters) {
|
|
|
|
printedParams = path.map(print, "parameters");
|
|
|
|
}
|
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
return concat([
|
2017-05-28 23:18:29 +03:00
|
|
|
n.accessibility ? concat([n.accessibility, " "]) : "",
|
|
|
|
n.export ? "export " : "",
|
|
|
|
n.static ? "static " : "",
|
|
|
|
n.readonly ? "readonly " : "",
|
2017-04-05 23:28:02 +03:00
|
|
|
"[",
|
2017-05-28 23:18:29 +03:00
|
|
|
path.call(print, "index"),
|
2017-03-19 18:06:52 +03:00
|
|
|
// This should only contain a single element, however TypeScript parses
|
|
|
|
// it using parseDelimitedList that uses commas as delimiter.
|
2017-05-28 23:18:29 +03:00
|
|
|
join(", ", printedParams),
|
2017-03-19 18:06:52 +03:00
|
|
|
"]: ",
|
2017-06-04 17:21:33 +03:00
|
|
|
path.call(print, "typeAnnotation"),
|
|
|
|
parent.type === "ClassBody" ? semi : ""
|
2017-03-19 18:06:52 +03:00
|
|
|
]);
|
2017-05-28 23:18:29 +03:00
|
|
|
}
|
2017-05-26 05:04:15 +03:00
|
|
|
case "TSTypePredicate":
|
2017-05-21 18:03:38 +03:00
|
|
|
return concat([
|
|
|
|
path.call(print, "parameterName"),
|
|
|
|
" is ",
|
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
]);
|
2017-04-20 01:41:18 +03:00
|
|
|
case "TSNonNullExpression":
|
|
|
|
return concat([path.call(print, "expression"), "!"]);
|
|
|
|
case "TSThisType":
|
|
|
|
return "this";
|
2017-04-21 04:01:34 +03:00
|
|
|
case "TSLastTypeNode":
|
2017-05-21 21:00:16 +03:00
|
|
|
return path.call(print, "literal");
|
2017-04-21 04:01:34 +03:00
|
|
|
case "TSIndexedAccessType":
|
|
|
|
return concat([
|
|
|
|
path.call(print, "objectType"),
|
|
|
|
"[",
|
|
|
|
path.call(print, "indexType"),
|
|
|
|
"]"
|
2017-05-21 21:00:16 +03:00
|
|
|
]);
|
2017-05-03 18:01:45 +03:00
|
|
|
case "TSConstructSignature":
|
2017-04-21 17:57:26 +03:00
|
|
|
case "TSConstructorType":
|
2017-05-21 22:11:09 +03:00
|
|
|
case "TSCallSignature": {
|
2017-05-03 18:01:45 +03:00
|
|
|
if (n.type !== "TSCallSignature") {
|
|
|
|
parts.push("new ");
|
|
|
|
}
|
2017-05-19 23:00:38 +03:00
|
|
|
const isType = n.type === "TSConstructorType";
|
2017-05-20 13:15:50 +03:00
|
|
|
|
|
|
|
if (n.typeParameters) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(printTypeParameters(path, options, print, "typeParameters"));
|
2017-05-20 13:15:50 +03:00
|
|
|
}
|
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
const params = n.params
|
|
|
|
? path.map(print, "params")
|
|
|
|
: path.map(print, "parameters");
|
|
|
|
parts.push("(", join(", ", params), ")");
|
2017-05-03 18:01:45 +03:00
|
|
|
if (n.typeAnnotation) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(isType ? " => " : ": ", path.call(print, "typeAnnotation"));
|
2017-05-03 18:01:45 +03:00
|
|
|
}
|
|
|
|
return concat(parts);
|
2017-05-21 22:11:09 +03:00
|
|
|
}
|
2017-04-23 18:25:25 +03:00
|
|
|
case "TSTypeOperator":
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat(["keyof ", path.call(print, "typeAnnotation")]);
|
2017-04-23 20:16:25 +03:00
|
|
|
case "TSMappedType":
|
2017-05-06 16:18:23 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
2017-05-06 20:30:59 +03:00
|
|
|
n.readonlyToken
|
|
|
|
? concat([path.call(print, "readonlyToken"), " "])
|
|
|
|
: "",
|
2017-05-06 16:18:23 +03:00
|
|
|
printTypeScriptModifiers(path, options, print),
|
|
|
|
"[",
|
|
|
|
path.call(print, "typeParameter"),
|
2017-05-06 20:30:59 +03:00
|
|
|
"]",
|
2017-05-07 08:34:47 +03:00
|
|
|
n.questionToken ? "?" : "",
|
2017-05-06 20:30:59 +03:00
|
|
|
": ",
|
2017-05-06 16:18:23 +03:00
|
|
|
path.call(print, "typeAnnotation")
|
|
|
|
])
|
|
|
|
),
|
2017-05-21 21:00:16 +03:00
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true),
|
2017-05-06 16:18:23 +03:00
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
);
|
2017-04-23 20:16:25 +03:00
|
|
|
case "TSTypeParameter":
|
2017-05-03 18:01:45 +03:00
|
|
|
parts.push(path.call(print, "name"));
|
2017-04-25 19:48:56 +03:00
|
|
|
|
2017-04-23 20:16:25 +03:00
|
|
|
if (n.constraint) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(" in ", path.call(print, "constraint"));
|
2017-04-23 20:16:25 +03:00
|
|
|
}
|
2017-04-25 19:48:56 +03:00
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat(parts);
|
2017-04-26 00:15:33 +03:00
|
|
|
case "TSMethodSignature":
|
2017-05-01 19:25:49 +03:00
|
|
|
parts.push(
|
2017-05-29 02:05:07 +03:00
|
|
|
n.accessibility ? concat([n.accessibility, " "]) : "",
|
|
|
|
n.export ? "export " : "",
|
|
|
|
n.static ? "static " : "",
|
|
|
|
n.readonly ? "readonly " : "",
|
|
|
|
n.computed ? "[" : "",
|
2017-05-28 23:18:29 +03:00
|
|
|
path.call(print, "key"),
|
2017-05-29 02:05:07 +03:00
|
|
|
n.computed ? "]" : "",
|
2017-05-28 23:18:29 +03:00
|
|
|
n.optional ? "?" : "",
|
2017-05-20 13:15:50 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
2017-05-06 07:11:13 +03:00
|
|
|
printFunctionParams(path, print, options)
|
2017-05-21 21:00:16 +03:00
|
|
|
);
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-01 19:25:49 +03:00
|
|
|
if (n.typeAnnotation) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(": ", path.call(print, "typeAnnotation"));
|
2017-05-01 19:25:49 +03:00
|
|
|
}
|
2017-06-08 00:20:55 +03:00
|
|
|
return group(concat(parts));
|
2017-05-01 19:25:49 +03:00
|
|
|
case "TSNamespaceExportDeclaration":
|
|
|
|
if (n.declaration) {
|
2017-06-02 18:12:03 +03:00
|
|
|
// Temporary fix until https://github.com/eslint/typescript-eslint-parser/issues/263
|
|
|
|
const isDefault = options.originalText
|
|
|
|
.slice(util.locStart(n), util.locStart(n.declaration))
|
|
|
|
.match(/\bdefault\b/);
|
|
|
|
parts.push(
|
|
|
|
"export ",
|
|
|
|
isDefault ? "default " : "",
|
|
|
|
path.call(print, "declaration")
|
|
|
|
);
|
2017-05-01 19:25:49 +03:00
|
|
|
} else {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push("export as namespace ", path.call(print, "name"));
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-01 19:25:49 +03:00
|
|
|
if (options.semi) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(";");
|
2017-05-01 19:25:49 +03:00
|
|
|
}
|
|
|
|
}
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-07 06:28:49 +03:00
|
|
|
return group(concat(parts));
|
2017-05-01 19:25:49 +03:00
|
|
|
case "TSEnumDeclaration":
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.modifiers) {
|
|
|
|
parts.push(printTypeScriptModifiers(path, options, print));
|
|
|
|
}
|
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push("enum ", path.call(print, "name"), " ");
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-01 19:25:49 +03:00
|
|
|
if (n.members.length === 0) {
|
|
|
|
parts.push(
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
comments.printDanglingComments(path, options),
|
|
|
|
softline,
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
parts.push(
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
concat([
|
2017-06-03 01:09:55 +03:00
|
|
|
hardline,
|
2017-06-03 15:20:36 +03:00
|
|
|
printArrayItems(path, options, "members", print),
|
|
|
|
shouldPrintComma(options, "es5") ? "," : ""
|
2017-05-01 19:25:49 +03:00
|
|
|
])
|
|
|
|
),
|
|
|
|
comments.printDanglingComments(
|
|
|
|
path,
|
|
|
|
options,
|
|
|
|
/* sameIndent */ true
|
|
|
|
),
|
2017-06-03 01:09:55 +03:00
|
|
|
hardline,
|
2017-05-01 19:25:49 +03:00
|
|
|
"}"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
case "TSEnumMember":
|
2017-05-06 05:44:26 +03:00
|
|
|
parts.push(path.call(print, "name"));
|
|
|
|
if (n.initializer) {
|
|
|
|
parts.push(" = ", path.call(print, "initializer"));
|
|
|
|
}
|
|
|
|
return concat(parts);
|
2017-05-01 19:25:49 +03:00
|
|
|
case "TSImportEqualsDeclaration":
|
|
|
|
parts.push(
|
2017-05-03 03:06:25 +03:00
|
|
|
printTypeScriptModifiers(path, options, print),
|
2017-05-01 19:25:49 +03:00
|
|
|
"import ",
|
|
|
|
path.call(print, "name"),
|
|
|
|
" = ",
|
|
|
|
path.call(print, "moduleReference")
|
2017-05-21 21:00:16 +03:00
|
|
|
);
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-01 19:25:49 +03:00
|
|
|
if (options.semi) {
|
2017-05-21 21:00:16 +03:00
|
|
|
parts.push(";");
|
2017-05-01 19:25:49 +03:00
|
|
|
}
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
return group(concat(parts));
|
2017-05-01 19:25:49 +03:00
|
|
|
case "TSExternalModuleReference":
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat(["require(", path.call(print, "expression"), ")"]);
|
2017-05-19 23:00:38 +03:00
|
|
|
case "TSModuleDeclaration": {
|
|
|
|
const parent = path.getParentNode();
|
2017-05-26 21:45:24 +03:00
|
|
|
const isExternalModule = isLiteral(n.name);
|
2017-05-19 23:00:38 +03:00
|
|
|
const parentIsDeclaration = parent.type === "TSModuleDeclaration";
|
2017-05-24 03:13:47 +03:00
|
|
|
const bodyIsDeclaration = n.body && n.body.type === "TSModuleDeclaration";
|
2017-05-07 13:44:41 +03:00
|
|
|
|
2017-05-06 07:14:07 +03:00
|
|
|
if (parentIsDeclaration) {
|
|
|
|
parts.push(".");
|
|
|
|
} else {
|
2017-05-07 13:44:41 +03:00
|
|
|
parts.push(printTypeScriptModifiers(path, options, print));
|
|
|
|
|
|
|
|
// Global declaration looks like this:
|
|
|
|
// declare global { ... }
|
2017-05-21 21:00:16 +03:00
|
|
|
const isGlobalDeclaration =
|
|
|
|
n.name.type === "Identifier" &&
|
2017-05-07 13:44:41 +03:00
|
|
|
n.name.name === "global" &&
|
2017-05-21 21:00:16 +03:00
|
|
|
n.modifiers &&
|
|
|
|
n.modifiers.some(modifier => modifier.type === "TSDeclareKeyword");
|
2017-05-07 13:44:41 +03:00
|
|
|
|
|
|
|
if (!isGlobalDeclaration) {
|
|
|
|
parts.push(isExternalModule ? "module " : "namespace ");
|
|
|
|
}
|
2017-05-06 07:14:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
parts.push(path.call(print, "name"));
|
|
|
|
|
|
|
|
if (bodyIsDeclaration) {
|
|
|
|
parts.push(path.call(print, "body"));
|
2017-05-24 03:13:47 +03:00
|
|
|
} else if (n.body) {
|
2017-05-06 07:14:07 +03:00
|
|
|
parts.push(
|
|
|
|
" {",
|
2017-05-27 07:30:42 +03:00
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
line,
|
|
|
|
path.call(
|
|
|
|
bodyPath =>
|
|
|
|
comments.printDanglingComments(bodyPath, options, true),
|
|
|
|
"body"
|
|
|
|
),
|
|
|
|
group(path.call(print, "body"))
|
|
|
|
])
|
|
|
|
),
|
2017-05-06 07:14:07 +03:00
|
|
|
line,
|
|
|
|
"}"
|
2017-05-07 13:44:41 +03:00
|
|
|
);
|
2017-05-24 03:13:47 +03:00
|
|
|
} else {
|
|
|
|
parts.push(semi);
|
2017-05-07 13:44:41 +03:00
|
|
|
}
|
2017-05-02 00:42:52 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
return concat(parts);
|
2017-05-19 23:00:38 +03:00
|
|
|
}
|
2017-05-01 19:25:49 +03:00
|
|
|
case "TSModuleBlock":
|
2017-05-21 22:11:09 +03:00
|
|
|
return path.call(bodyPath => {
|
2017-05-06 05:44:26 +03:00
|
|
|
return printStatementSequence(bodyPath, options, print);
|
|
|
|
}, "body");
|
2017-05-24 20:52:12 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
default:
|
|
|
|
throw new Error("unknown type: " + JSON.stringify(n.type));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function printStatementSequence(path, options, print) {
|
2017-05-21 22:11:09 +03:00
|
|
|
const printed = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
const bodyNode = path.getNode();
|
|
|
|
const isClass = bodyNode.type === "ClassBody";
|
|
|
|
|
|
|
|
path.map((stmtPath, i) => {
|
2017-05-19 23:00:38 +03:00
|
|
|
const stmt = stmtPath.getValue();
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
// Just in case the AST has been modified to contain falsy
|
|
|
|
// "statements," it's safer simply to skip them.
|
|
|
|
if (!stmt) {
|
|
|
|
return;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
// Skip printing EmptyStatement nodes to avoid leaving stray
|
|
|
|
// semicolons lying around.
|
|
|
|
if (stmt.type === "EmptyStatement") {
|
|
|
|
return;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
const stmtPrinted = print(stmtPath);
|
2017-01-10 05:49:06 +03:00
|
|
|
const text = options.originalText;
|
2017-01-05 06:27:25 +03:00
|
|
|
const parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
// in no-semi mode, prepend statement with semicolon if it might break ASI
|
|
|
|
if (!options.semi && !isClass && stmtNeedsASIProtection(stmtPath)) {
|
2017-05-21 21:00:16 +03:00
|
|
|
if (stmt.comments && stmt.comments.some(comment => comment.leading)) {
|
2017-04-14 04:33:46 +03:00
|
|
|
// Note: stmtNeedsASIProtection requires stmtPath to already be printed
|
|
|
|
// as it reads needsParens which is mutated on the instance
|
|
|
|
parts.push(print(stmtPath, { needsSemi: true }));
|
|
|
|
} else {
|
|
|
|
parts.push(";", stmtPrinted);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parts.push(stmtPrinted);
|
2017-04-11 23:50:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!options.semi && isClass) {
|
|
|
|
if (classPropMayCauseASIProblems(stmtPath)) {
|
|
|
|
parts.push(";");
|
|
|
|
} else if (stmt.type === "ClassProperty") {
|
|
|
|
const nextChild = bodyNode.body[i + 1];
|
|
|
|
if (classChildNeedsASIProtection(nextChild)) {
|
|
|
|
parts.push(";");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-30 20:10:54 +03:00
|
|
|
if (util.isNextLineEmpty(text, stmt) && !isLastStatement(stmtPath)) {
|
2017-01-05 06:27:25 +03:00
|
|
|
parts.push(hardline);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-05 06:27:25 +03:00
|
|
|
printed.push(concat(parts));
|
|
|
|
});
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:01:07 +03:00
|
|
|
return join(hardline, printed);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 20:37:24 +03:00
|
|
|
function printPropertyKey(path, options, print) {
|
2017-01-23 05:41:31 +03:00
|
|
|
const node = path.getNode();
|
|
|
|
const key = node.key;
|
|
|
|
|
2017-05-29 17:31:24 +03:00
|
|
|
if (isStringLiteral(key) && isIdentifierName(key.value) && !node.computed) {
|
2017-01-11 20:31:34 +03:00
|
|
|
// 'a' -> a
|
2017-04-05 23:10:10 +03:00
|
|
|
return path.call(
|
2017-04-25 19:48:56 +03:00
|
|
|
keyPath => comments.printComments(keyPath, () => key.value, options),
|
2017-04-05 23:10:10 +03:00
|
|
|
"key"
|
|
|
|
);
|
2017-01-11 08:48:49 +03:00
|
|
|
}
|
|
|
|
return path.call(print, "key");
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function printMethod(path, options, print) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const node = path.getNode();
|
|
|
|
const semi = options.semi ? ";" : "";
|
|
|
|
const kind = node.kind;
|
|
|
|
const parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.type === "ObjectMethod" || node.type === "ClassMethod") {
|
|
|
|
node.value = node;
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.value.async) {
|
|
|
|
parts.push("async ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (!kind || kind === "init" || kind === "method" || kind === "constructor") {
|
|
|
|
if (node.value.generator) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert.ok(kind === "get" || kind === "set");
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(kind, " ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
let key = printPropertyKey(path, options, print);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (node.computed) {
|
2017-01-28 18:50:22 +03:00
|
|
|
key = concat(["[", key, "]"]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-02-03 19:50:51 +03:00
|
|
|
parts.push(
|
|
|
|
key,
|
2017-05-21 21:00:16 +03:00
|
|
|
concat(
|
|
|
|
path.call(
|
|
|
|
valuePath => [
|
|
|
|
printFunctionTypeParameters(valuePath, options, print),
|
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
printFunctionParams(valuePath, print, options),
|
|
|
|
printReturnType(valuePath, print)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
],
|
|
|
|
"value"
|
|
|
|
)
|
|
|
|
)
|
2017-02-03 19:50:51 +03:00
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-26 19:25:58 +03:00
|
|
|
if (!node.value.body || node.value.body.length === 0) {
|
|
|
|
parts.push(semi);
|
|
|
|
} else {
|
|
|
|
parts.push(" ", path.call(print, "value", "body"));
|
|
|
|
}
|
|
|
|
|
2017-01-09 21:47:02 +03:00
|
|
|
return concat(parts);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-03-18 21:20:07 +03:00
|
|
|
function couldGroupArg(arg) {
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
(arg.type === "ObjectExpression" && arg.properties.length > 0) ||
|
|
|
|
(arg.type === "ArrayExpression" && arg.elements.length > 0) ||
|
|
|
|
arg.type === "FunctionExpression" ||
|
|
|
|
(arg.type === "ArrowFunctionExpression" &&
|
|
|
|
(arg.body.type === "BlockStatement" ||
|
|
|
|
arg.body.type === "ArrowFunctionExpression" ||
|
|
|
|
arg.body.type === "ObjectExpression" ||
|
|
|
|
arg.body.type === "ArrayExpression" ||
|
|
|
|
arg.body.type === "CallExpression" ||
|
|
|
|
arg.body.type === "JSXElement"))
|
|
|
|
);
|
2017-03-18 21:20:07 +03:00
|
|
|
}
|
|
|
|
|
2017-03-03 06:29:53 +03:00
|
|
|
function shouldGroupLastArg(args) {
|
2017-02-25 20:56:13 +03:00
|
|
|
const lastArg = util.getLast(args);
|
2017-03-04 02:39:37 +03:00
|
|
|
const penultimateArg = util.getPenultimate(args);
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
(!lastArg.comments || !lastArg.comments.length) &&
|
2017-03-18 21:20:07 +03:00
|
|
|
couldGroupArg(lastArg) &&
|
2017-03-01 20:37:02 +03:00
|
|
|
// If the last two arguments are of the same type,
|
|
|
|
// disable last element expansion.
|
2017-04-12 20:16:11 +03:00
|
|
|
(!penultimateArg || penultimateArg.type !== lastArg.type)
|
|
|
|
);
|
2017-03-03 06:29:53 +03:00
|
|
|
}
|
2017-01-04 23:24:10 +03:00
|
|
|
|
2017-03-18 21:20:07 +03:00
|
|
|
function shouldGroupFirstArg(args) {
|
|
|
|
if (args.length !== 2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const firstArg = args[0];
|
|
|
|
const secondArg = args[1];
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
(!firstArg.comments || !firstArg.comments.length) &&
|
|
|
|
(firstArg.type === "FunctionExpression" ||
|
|
|
|
(firstArg.type === "ArrowFunctionExpression" &&
|
|
|
|
firstArg.body.type === "BlockStatement")) &&
|
|
|
|
!couldGroupArg(secondArg)
|
|
|
|
);
|
2017-03-18 21:20:07 +03:00
|
|
|
}
|
|
|
|
|
2017-03-03 06:29:53 +03:00
|
|
|
function printArgumentsList(path, options, print) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const printed = path.map(print, "arguments");
|
2017-03-03 06:29:53 +03:00
|
|
|
if (printed.length === 0) {
|
2017-03-09 18:55:18 +03:00
|
|
|
return concat([
|
|
|
|
"(",
|
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true),
|
|
|
|
")"
|
|
|
|
]);
|
2017-03-03 06:29:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const args = path.getValue().arguments;
|
|
|
|
// This is just an optimization; I think we could return the
|
|
|
|
// conditional group for all function calls, but it's more expensive
|
|
|
|
// so only do it for specific forms.
|
2017-03-18 21:20:07 +03:00
|
|
|
const shouldGroupFirst = shouldGroupFirstArg(args);
|
2017-04-13 06:24:50 +03:00
|
|
|
const shouldGroupLast = shouldGroupLastArg(args);
|
|
|
|
if (shouldGroupFirst || shouldGroupLast) {
|
2017-03-18 21:20:07 +03:00
|
|
|
const shouldBreak = shouldGroupFirst
|
|
|
|
? printed.slice(1).some(willBreak)
|
|
|
|
: printed.slice(0, -1).some(willBreak);
|
2017-04-11 18:50:49 +03:00
|
|
|
|
2017-04-13 06:24:50 +03:00
|
|
|
// We want to print the last argument with a special flag
|
|
|
|
let printedExpanded;
|
|
|
|
let i = 0;
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(argPath => {
|
2017-04-13 06:24:50 +03:00
|
|
|
if (shouldGroupFirst && i === 0) {
|
2017-05-21 21:00:16 +03:00
|
|
|
printedExpanded = [
|
|
|
|
argPath.call(p => print(p, { expandFirstArg: true }))
|
|
|
|
].concat(printed.slice(1));
|
2017-04-13 06:24:50 +03:00
|
|
|
}
|
|
|
|
if (shouldGroupLast && i === args.length - 1) {
|
|
|
|
printedExpanded = printed
|
|
|
|
.slice(0, -1)
|
|
|
|
.concat(argPath.call(p => print(p, { expandLastArg: true })));
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}, "arguments");
|
2017-04-11 18:50:49 +03:00
|
|
|
|
2017-02-04 00:15:21 +03:00
|
|
|
return concat([
|
|
|
|
printed.some(willBreak) ? breakParent : "",
|
|
|
|
conditionalGroup(
|
|
|
|
[
|
2017-04-13 06:24:50 +03:00
|
|
|
concat(["(", join(concat([", "]), printedExpanded), ")"]),
|
2017-03-18 21:20:07 +03:00
|
|
|
shouldGroupFirst
|
|
|
|
? concat([
|
|
|
|
"(",
|
2017-04-13 06:24:50 +03:00
|
|
|
group(printedExpanded[0], { shouldBreak: true }),
|
2017-03-18 21:20:07 +03:00
|
|
|
printed.length > 1 ? ", " : "",
|
|
|
|
join(concat([",", line]), printed.slice(1)),
|
|
|
|
")"
|
|
|
|
])
|
|
|
|
: concat([
|
|
|
|
"(",
|
|
|
|
join(concat([",", line]), printed.slice(0, -1)),
|
|
|
|
printed.length > 1 ? ", " : "",
|
2017-04-13 06:24:50 +03:00
|
|
|
group(util.getLast(printedExpanded), {
|
2017-04-12 20:16:11 +03:00
|
|
|
shouldBreak: true
|
|
|
|
}),
|
2017-03-18 21:20:07 +03:00
|
|
|
")"
|
|
|
|
]),
|
2017-02-04 00:15:21 +03:00
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
"(",
|
2017-04-12 20:16:11 +03:00
|
|
|
indent(concat([line, join(concat([",", line]), printed)])),
|
2017-02-18 06:44:55 +03:00
|
|
|
shouldPrintComma(options, "all") ? "," : "",
|
2017-02-04 00:15:21 +03:00
|
|
|
line,
|
|
|
|
")"
|
|
|
|
]),
|
|
|
|
{ shouldBreak: true }
|
|
|
|
)
|
|
|
|
],
|
|
|
|
{ shouldBreak }
|
|
|
|
)
|
|
|
|
]);
|
2017-01-04 23:24:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-24 21:54:01 +03:00
|
|
|
return group(
|
2017-01-04 23:24:10 +03:00
|
|
|
concat([
|
|
|
|
"(",
|
2017-04-12 20:16:11 +03:00
|
|
|
indent(concat([softline, join(concat([",", line]), printed)])),
|
2017-02-18 06:44:55 +03:00
|
|
|
ifBreak(shouldPrintComma(options, "all") ? "," : ""),
|
2017-01-04 23:24:10 +03:00
|
|
|
softline,
|
|
|
|
")"
|
2017-01-31 19:45:47 +03:00
|
|
|
]),
|
|
|
|
{ shouldBreak: printed.some(willBreak) }
|
2017-01-04 23:24:10 +03:00
|
|
|
);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-05-06 05:44:26 +03:00
|
|
|
function printFunctionTypeParameters(path, options, print) {
|
|
|
|
const fun = path.getValue();
|
2017-05-21 21:00:16 +03:00
|
|
|
const paramsFieldIsArray = Array.isArray(fun["typeParameters"]);
|
2017-05-21 18:03:38 +03:00
|
|
|
|
2017-05-13 01:33:03 +03:00
|
|
|
if (fun.typeParameters) {
|
2017-05-20 13:15:50 +03:00
|
|
|
// for TSFunctionType typeParameters is an array
|
|
|
|
// for FunctionTypeAnnotation it's a single node
|
|
|
|
if (paramsFieldIsArray) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat("<", join(", ", path.map(print, "typeParameters")), ">");
|
2017-05-20 13:15:50 +03:00
|
|
|
}
|
2017-06-06 01:12:59 +03:00
|
|
|
return path.call(print, "typeParameters");
|
2017-05-06 05:44:26 +03:00
|
|
|
}
|
2017-06-06 01:12:59 +03:00
|
|
|
return "";
|
2017-05-06 05:44:26 +03:00
|
|
|
}
|
|
|
|
|
2017-04-13 06:24:50 +03:00
|
|
|
function printFunctionParams(path, print, options, expandArg) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const fun = path.getValue();
|
2017-05-28 23:18:29 +03:00
|
|
|
const paramsField = fun.parameters ? "parameters" : "params";
|
2017-05-06 07:11:13 +03:00
|
|
|
|
2017-05-28 23:18:29 +03:00
|
|
|
let printed = [];
|
|
|
|
if (fun[paramsField]) {
|
|
|
|
printed = path.map(print, paramsField);
|
|
|
|
}
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (fun.defaults) {
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(defExprPath => {
|
2017-05-19 23:00:38 +03:00
|
|
|
const i = defExprPath.getName();
|
|
|
|
const p = printed[i];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
if (p && defExprPath.getValue()) {
|
|
|
|
printed[i] = concat([p, " = ", print(defExprPath)]);
|
|
|
|
}
|
|
|
|
}, "defaults");
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (fun.rest) {
|
2017-01-28 18:50:22 +03:00
|
|
|
printed.push(concat(["...", path.call(print, "rest")]));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-15 07:55:38 +03:00
|
|
|
if (printed.length === 0) {
|
2017-02-24 18:26:30 +03:00
|
|
|
return concat([
|
|
|
|
"(",
|
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true),
|
|
|
|
")"
|
2017-03-01 20:37:02 +03:00
|
|
|
]);
|
2017-01-15 07:55:38 +03:00
|
|
|
}
|
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
const lastParam = util.getLast(fun[paramsField]);
|
2017-01-18 20:42:20 +03:00
|
|
|
|
2017-03-18 21:20:07 +03:00
|
|
|
// If the parent is a call with the first/last argument expansion and this is the
|
|
|
|
// params of the first/last argument, we dont want the arguments to break and instead
|
2017-03-03 06:29:53 +03:00
|
|
|
// want the whole expression to be on a new line.
|
|
|
|
//
|
|
|
|
// Good: Bad:
|
|
|
|
// verylongcall( verylongcall((
|
|
|
|
// (a, b) => { a,
|
|
|
|
// } b,
|
|
|
|
// }) ) => {
|
|
|
|
// })
|
2017-04-13 06:24:50 +03:00
|
|
|
if (expandArg) {
|
2017-04-18 18:40:08 +03:00
|
|
|
return group(concat(["(", join(", ", printed.map(removeLines)), ")"]));
|
2017-03-03 06:29:53 +03:00
|
|
|
}
|
|
|
|
|
2017-03-22 19:58:33 +03:00
|
|
|
// Single object destructuring should hug
|
|
|
|
//
|
|
|
|
// function({
|
|
|
|
// a,
|
|
|
|
// b,
|
|
|
|
// c
|
|
|
|
// }) {}
|
2017-05-02 05:12:53 +03:00
|
|
|
if (shouldHugArguments(fun)) {
|
2017-03-22 19:58:33 +03:00
|
|
|
return concat(["(", join(", ", printed), ")"]);
|
|
|
|
}
|
|
|
|
|
2017-04-13 06:24:50 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-04-14 03:54:20 +03:00
|
|
|
|
|
|
|
const flowTypeAnnotations = [
|
|
|
|
"AnyTypeAnnotation",
|
|
|
|
"NullLiteralTypeAnnotation",
|
|
|
|
"GenericTypeAnnotation",
|
|
|
|
"ThisTypeAnnotation",
|
|
|
|
"NumberTypeAnnotation",
|
|
|
|
"VoidTypeAnnotation",
|
|
|
|
"NullTypeAnnotation",
|
|
|
|
"EmptyTypeAnnotation",
|
|
|
|
"MixedTypeAnnotation",
|
|
|
|
"BooleanTypeAnnotation",
|
|
|
|
"BooleanLiteralTypeAnnotation",
|
|
|
|
"StringTypeAnnotation"
|
|
|
|
];
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
const isFlowShorthandWithOneArg =
|
|
|
|
(isObjectTypePropertyAFunction(parent) ||
|
|
|
|
isTypeAnnotationAFunction(parent) ||
|
2017-04-24 23:58:30 +03:00
|
|
|
parent.type === "TypeAlias" ||
|
|
|
|
parent.type === "UnionTypeAnnotation" ||
|
2017-05-26 02:49:25 +03:00
|
|
|
parent.type === "TSUnionType" ||
|
2017-04-24 23:58:30 +03:00
|
|
|
parent.type === "IntersectionTypeAnnotation" ||
|
|
|
|
(parent.type === "FunctionTypeAnnotation" &&
|
|
|
|
parent.returnType === fun)) &&
|
2017-04-12 20:16:11 +03:00
|
|
|
fun[paramsField].length === 1 &&
|
|
|
|
fun[paramsField][0].name === null &&
|
2017-04-14 03:54:20 +03:00
|
|
|
fun[paramsField][0].typeAnnotation &&
|
2017-05-21 21:00:16 +03:00
|
|
|
flowTypeAnnotations.indexOf(fun[paramsField][0].typeAnnotation.type) !==
|
|
|
|
-1 &&
|
2017-06-02 21:48:43 +03:00
|
|
|
!(
|
|
|
|
fun[paramsField][0].typeAnnotation.type === "GenericTypeAnnotation" &&
|
|
|
|
fun[paramsField][0].typeAnnotation.typeParameters
|
|
|
|
) &&
|
2017-04-12 20:16:11 +03:00
|
|
|
!fun.rest;
|
2017-03-16 20:32:59 +03:00
|
|
|
|
2017-05-02 00:39:46 +03:00
|
|
|
if (isFlowShorthandWithOneArg) {
|
|
|
|
return concat(printed);
|
|
|
|
}
|
|
|
|
|
2017-04-21 00:46:41 +03:00
|
|
|
const canHaveTrailingComma =
|
2017-05-21 21:00:16 +03:00
|
|
|
!(lastParam && lastParam.type === "RestElement") && !fun.rest;
|
2017-04-21 00:46:41 +03:00
|
|
|
|
2017-01-05 00:52:49 +03:00
|
|
|
return concat([
|
2017-05-02 00:39:46 +03:00
|
|
|
"(",
|
2017-04-12 20:16:11 +03:00
|
|
|
indent(concat([softline, join(concat([",", line]), printed)])),
|
2017-02-23 20:57:51 +03:00
|
|
|
ifBreak(
|
|
|
|
canHaveTrailingComma && shouldPrintComma(options, "all") ? "," : ""
|
|
|
|
),
|
2017-01-05 00:52:49 +03:00
|
|
|
softline,
|
2017-05-02 00:39:46 +03:00
|
|
|
")"
|
2017-01-05 00:52:49 +03:00
|
|
|
]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
function canPrintParamsWithoutParens(node) {
|
|
|
|
return (
|
|
|
|
node.params.length === 1 &&
|
|
|
|
!node.rest &&
|
|
|
|
node.params[0].type === "Identifier" &&
|
|
|
|
!node.params[0].typeAnnotation &&
|
2017-05-15 01:58:06 +03:00
|
|
|
!util.hasBlockComments(node.params[0]) &&
|
2017-04-11 23:50:47 +03:00
|
|
|
!node.params[0].optional &&
|
|
|
|
!node.predicate &&
|
|
|
|
!node.returnType
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-03-19 18:06:52 +03:00
|
|
|
function printFunctionDeclaration(path, print, options) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const n = path.getValue();
|
|
|
|
const parts = [];
|
2017-03-19 18:06:52 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.async) {
|
|
|
|
parts.push("async ");
|
|
|
|
}
|
2017-03-19 18:06:52 +03:00
|
|
|
|
|
|
|
parts.push("function");
|
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (n.generator) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
2017-03-19 18:06:52 +03:00
|
|
|
if (n.id) {
|
|
|
|
parts.push(" ", path.call(print, "id"));
|
|
|
|
}
|
|
|
|
|
|
|
|
parts.push(
|
2017-05-06 05:44:26 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
2017-03-19 18:06:52 +03:00
|
|
|
group(
|
|
|
|
concat([
|
|
|
|
printFunctionParams(path, print, options),
|
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
),
|
2017-05-24 02:30:35 +03:00
|
|
|
n.body ? " " : "",
|
2017-03-19 18:06:52 +03:00
|
|
|
path.call(print, "body")
|
|
|
|
);
|
|
|
|
|
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function printObjectMethod(path, options, print) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const objMethod = path.getValue();
|
|
|
|
const parts = [];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
if (objMethod.async) {
|
|
|
|
parts.push("async ");
|
|
|
|
}
|
|
|
|
if (objMethod.generator) {
|
|
|
|
parts.push("*");
|
|
|
|
}
|
2016-12-31 07:03:22 +03:00
|
|
|
if (
|
2017-05-21 21:00:16 +03:00
|
|
|
objMethod.method ||
|
|
|
|
objMethod.kind === "get" ||
|
|
|
|
objMethod.kind === "set"
|
2016-12-31 07:03:22 +03:00
|
|
|
) {
|
2016-12-23 21:38:10 +03:00
|
|
|
return printMethod(path, options, print);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-19 23:00:38 +03:00
|
|
|
const key = printPropertyKey(path, options, print);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (objMethod.computed) {
|
|
|
|
parts.push("[", key, "]");
|
|
|
|
} else {
|
|
|
|
parts.push(key);
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
parts.push(
|
2017-05-06 05:44:26 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
2017-01-24 21:54:01 +03:00
|
|
|
group(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
|
|
|
printFunctionParams(path, print, options),
|
|
|
|
printReturnType(path, print)
|
|
|
|
])
|
|
|
|
),
|
2016-12-23 21:38:10 +03:00
|
|
|
" ",
|
|
|
|
path.call(print, "body")
|
|
|
|
);
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-09 21:47:02 +03:00
|
|
|
return concat(parts);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
2016-12-30 21:32:43 +03:00
|
|
|
function printReturnType(path, print) {
|
|
|
|
const n = path.getValue();
|
2017-01-28 18:50:22 +03:00
|
|
|
const parts = [path.call(print, "returnType")];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2017-04-23 04:09:53 +03:00
|
|
|
// prepend colon to TypeScript type annotation
|
|
|
|
if (n.returnType && n.returnType.typeAnnotation) {
|
2017-04-27 19:37:42 +03:00
|
|
|
parts.unshift(": ");
|
2017-04-23 04:09:53 +03:00
|
|
|
}
|
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (n.predicate) {
|
2016-12-31 07:10:22 +03:00
|
|
|
// The return type will already add the colon, but otherwise we
|
|
|
|
// need to do it ourselves
|
2017-01-09 20:09:04 +03:00
|
|
|
parts.push(n.returnType ? " " : ": ", path.call(print, "predicate"));
|
2016-12-30 21:32:43 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 21:32:43 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
function printExportDeclaration(path, options, print) {
|
2017-03-16 02:29:15 +03:00
|
|
|
const decl = path.getValue();
|
2017-04-11 23:50:47 +03:00
|
|
|
const semi = options.semi ? ";" : "";
|
2017-05-21 22:11:09 +03:00
|
|
|
const parts = ["export "];
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (decl["default"] || decl.type === "ExportDefaultDeclaration") {
|
2017-06-02 18:32:40 +03:00
|
|
|
// Temp fix, delete after https://github.com/eslint/typescript-eslint-parser/issues/304
|
|
|
|
if (
|
|
|
|
decl.declaration &&
|
|
|
|
/=/.test(
|
|
|
|
options.originalText.slice(
|
|
|
|
util.locStart(decl),
|
|
|
|
util.locStart(decl.declaration)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
parts.push("= ");
|
|
|
|
} else {
|
|
|
|
parts.push("default ");
|
|
|
|
}
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-02-23 20:57:51 +03:00
|
|
|
parts.push(
|
|
|
|
comments.printDanglingComments(path, options, /* sameIndent */ true)
|
|
|
|
);
|
2017-02-20 01:39:09 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (decl.declaration) {
|
|
|
|
parts.push(path.call(print, "declaration"));
|
2017-01-11 15:43:27 +03:00
|
|
|
|
2017-01-13 23:03:53 +03:00
|
|
|
if (
|
|
|
|
decl.type === "ExportDefaultDeclaration" &&
|
2017-02-16 06:56:11 +03:00
|
|
|
(decl.declaration.type !== "ClassDeclaration" &&
|
2017-06-03 15:19:39 +03:00
|
|
|
decl.declaration.type !== "FunctionDeclaration" &&
|
|
|
|
decl.declaration.type !== "TSAbstractClassDeclaration")
|
2017-01-13 23:03:53 +03:00
|
|
|
) {
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2017-01-11 15:43:27 +03:00
|
|
|
}
|
2017-01-16 19:58:23 +03:00
|
|
|
} else {
|
|
|
|
if (decl.specifiers && decl.specifiers.length > 0) {
|
|
|
|
if (
|
|
|
|
decl.specifiers.length === 1 &&
|
2017-02-16 06:56:11 +03:00
|
|
|
decl.specifiers[0].type === "ExportBatchSpecifier"
|
2017-01-16 19:58:23 +03:00
|
|
|
) {
|
|
|
|
parts.push("*");
|
|
|
|
} else {
|
2017-05-21 22:11:09 +03:00
|
|
|
const specifiers = [];
|
|
|
|
const defaultSpecifiers = [];
|
|
|
|
const namespaceSpecifiers = [];
|
2017-03-16 02:29:15 +03:00
|
|
|
|
|
|
|
path.map(specifierPath => {
|
|
|
|
const specifierType = path.getValue().type;
|
|
|
|
if (specifierType === "ExportSpecifier") {
|
|
|
|
specifiers.push(print(specifierPath));
|
|
|
|
} else if (specifierType === "ExportDefaultSpecifier") {
|
|
|
|
defaultSpecifiers.push(print(specifierPath));
|
|
|
|
} else if (specifierType === "ExportNamespaceSpecifier") {
|
|
|
|
namespaceSpecifiers.push(concat(["* as ", print(specifierPath)]));
|
|
|
|
}
|
|
|
|
}, "specifiers");
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
const isNamespaceFollowed =
|
|
|
|
namespaceSpecifiers.length !== 0 &&
|
2017-03-16 02:29:15 +03:00
|
|
|
(specifiers.length !== 0 || defaultSpecifiers.length !== 0);
|
2017-04-12 20:16:11 +03:00
|
|
|
const isDefaultFollowed =
|
|
|
|
defaultSpecifiers.length !== 0 && specifiers.length !== 0;
|
2017-03-16 02:29:15 +03:00
|
|
|
|
2017-01-16 19:58:23 +03:00
|
|
|
parts.push(
|
|
|
|
decl.exportKind === "type" ? "type " : "",
|
2017-03-16 02:29:15 +03:00
|
|
|
concat(namespaceSpecifiers),
|
|
|
|
concat([isNamespaceFollowed ? ", " : ""]),
|
|
|
|
concat(defaultSpecifiers),
|
|
|
|
concat([isDefaultFollowed ? ", " : ""]),
|
|
|
|
specifiers.length !== 0
|
|
|
|
? group(
|
2017-04-12 20:16:11 +03:00
|
|
|
concat([
|
|
|
|
"{",
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
join(concat([",", line]), specifiers)
|
|
|
|
])
|
|
|
|
),
|
|
|
|
ifBreak(shouldPrintComma(options) ? "," : ""),
|
|
|
|
options.bracketSpacing ? line : softline,
|
|
|
|
"}"
|
|
|
|
])
|
|
|
|
)
|
|
|
|
: ""
|
2017-01-16 19:58:23 +03:00
|
|
|
);
|
|
|
|
}
|
2017-01-09 20:09:04 +03:00
|
|
|
} else {
|
2017-01-16 19:58:23 +03:00
|
|
|
parts.push("{}");
|
2017-01-09 20:09:04 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-01-09 20:09:04 +03:00
|
|
|
if (decl.source) {
|
|
|
|
parts.push(" from ", path.call(print, "source"));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2017-01-11 15:43:27 +03:00
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
parts.push(semi);
|
2017-01-09 20:09:04 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
|
|
|
function printFlowDeclaration(path, parts) {
|
2017-05-19 23:00:38 +03:00
|
|
|
const parentExportDecl = util.getParentExportDeclaration(path);
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
if (parentExportDecl) {
|
2016-12-31 07:03:22 +03:00
|
|
|
assert.strictEqual(parentExportDecl.type, "DeclareExportDeclaration");
|
2016-12-23 21:38:10 +03:00
|
|
|
} else {
|
|
|
|
// If the parent node has type DeclareExportDeclaration, then it
|
|
|
|
// will be responsible for printing the "declare" token. Otherwise
|
|
|
|
// it needs to be printed with this non-exported declaration node.
|
|
|
|
parts.unshift("declare ");
|
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-23 21:38:10 +03:00
|
|
|
return concat(parts);
|
|
|
|
}
|
|
|
|
|
2017-04-25 19:48:56 +03:00
|
|
|
function getFlowVariance(path) {
|
2017-03-22 19:37:26 +03:00
|
|
|
if (!path.variance) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Babylon 7.0 currently uses variance node type, and flow should
|
|
|
|
// follow suit soon:
|
|
|
|
// https://github.com/babel/babel/issues/4722
|
|
|
|
const variance = path.variance.kind || path.variance;
|
|
|
|
|
|
|
|
switch (variance) {
|
|
|
|
case "plus":
|
|
|
|
return "+";
|
|
|
|
|
|
|
|
case "minus":
|
|
|
|
return "-";
|
|
|
|
|
|
|
|
default:
|
|
|
|
return variance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-03 03:06:25 +03:00
|
|
|
function printTypeScriptModifiers(path, options, print) {
|
|
|
|
const n = path.getValue();
|
|
|
|
if (!n.modifiers || !n.modifiers.length) {
|
|
|
|
return "";
|
|
|
|
}
|
2017-05-21 21:00:16 +03:00
|
|
|
return concat([join(" ", path.map(print, "modifiers")), " "]);
|
2017-05-03 03:06:25 +03:00
|
|
|
}
|
|
|
|
|
2017-05-03 18:01:45 +03:00
|
|
|
function printTypeParameters(path, options, print, paramsKey) {
|
2017-05-13 01:33:03 +03:00
|
|
|
const n = path.getValue();
|
2017-05-03 18:01:45 +03:00
|
|
|
|
2017-05-13 01:33:03 +03:00
|
|
|
if (!n[paramsKey]) {
|
|
|
|
return "";
|
|
|
|
}
|
2017-05-03 18:01:45 +03:00
|
|
|
|
2017-05-20 13:15:50 +03:00
|
|
|
// for TypeParameterDeclaration typeParameters is a single node
|
|
|
|
if (!Array.isArray(n[paramsKey])) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return path.call(print, paramsKey);
|
2017-05-20 13:15:50 +03:00
|
|
|
}
|
|
|
|
|
2017-05-13 01:33:03 +03:00
|
|
|
const shouldInline =
|
|
|
|
n[paramsKey].length === 1 &&
|
2017-05-26 02:26:40 +03:00
|
|
|
(shouldHugType(n[paramsKey][0]) ||
|
2017-05-26 02:49:25 +03:00
|
|
|
(n[paramsKey][0].type === "GenericTypeAnnotation" &&
|
|
|
|
shouldHugType(n[paramsKey][0].id)) ||
|
2017-05-13 01:33:03 +03:00
|
|
|
n[paramsKey][0].type === "NullableTypeAnnotation");
|
2017-05-03 18:01:45 +03:00
|
|
|
|
2017-05-13 01:33:03 +03:00
|
|
|
if (shouldInline) {
|
|
|
|
return concat(["<", join(", ", path.map(print, paramsKey)), ">"]);
|
|
|
|
}
|
2017-05-03 18:01:45 +03:00
|
|
|
|
2017-05-13 01:33:03 +03:00
|
|
|
return group(
|
|
|
|
concat([
|
|
|
|
"<",
|
|
|
|
indent(
|
|
|
|
concat([
|
|
|
|
softline,
|
|
|
|
join(concat([",", line]), path.map(print, paramsKey))
|
|
|
|
])
|
|
|
|
),
|
2017-05-31 19:22:00 +03:00
|
|
|
ifBreak(
|
|
|
|
options.parser !== "typescript" && shouldPrintComma(options, "all")
|
|
|
|
? ","
|
|
|
|
: ""
|
|
|
|
),
|
2017-05-13 01:33:03 +03:00
|
|
|
softline,
|
|
|
|
">"
|
|
|
|
])
|
|
|
|
);
|
2017-05-03 18:01:45 +03:00
|
|
|
}
|
|
|
|
|
2017-01-26 00:36:55 +03:00
|
|
|
function printClass(path, options, print) {
|
2016-12-30 08:01:44 +03:00
|
|
|
const n = path.getValue();
|
2017-04-26 19:25:58 +03:00
|
|
|
const parts = [];
|
|
|
|
|
|
|
|
if (n.accessibility) {
|
|
|
|
parts.push(n.accessibility + " ");
|
|
|
|
}
|
|
|
|
if (n.type === "TSAbstractClassDeclaration") {
|
|
|
|
parts.push("abstract ");
|
|
|
|
}
|
|
|
|
|
|
|
|
parts.push("class");
|
2016-12-31 22:38:58 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
if (n.id) {
|
2017-05-23 22:33:10 +03:00
|
|
|
parts.push(" ", path.call(print, "id"));
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-05-23 22:33:10 +03:00
|
|
|
parts.push(path.call(print, "typeParameters"));
|
|
|
|
|
2017-01-26 00:36:55 +03:00
|
|
|
const partsGroup = [];
|
2016-12-30 08:01:44 +03:00
|
|
|
if (n.superClass) {
|
2017-05-26 02:26:56 +03:00
|
|
|
parts.push(
|
|
|
|
" extends ",
|
2017-01-09 20:09:04 +03:00
|
|
|
path.call(print, "superClass"),
|
|
|
|
path.call(print, "superTypeParameters")
|
|
|
|
);
|
|
|
|
} else if (n.extends && n.extends.length > 0) {
|
2017-05-26 02:26:56 +03:00
|
|
|
parts.push(" extends ", join(", ", path.map(print, "extends")));
|
2017-01-09 20:09:04 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-31 07:03:22 +03:00
|
|
|
if (n["implements"] && n["implements"].length > 0) {
|
2017-01-28 18:50:22 +03:00
|
|
|
partsGroup.push(
|
|
|
|
line,
|
|
|
|
"implements ",
|
2017-05-30 19:45:07 +03:00
|
|
|
group(indent(join(concat([",", line]), path.map(print, "implements"))))
|
2017-01-28 18:50:22 +03:00
|
|
|
);
|
2017-01-26 00:36:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (partsGroup.length > 0) {
|
2017-04-07 05:49:37 +03:00
|
|
|
parts.push(group(indent(concat(partsGroup))));
|
2016-12-30 08:01:44 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
parts.push(" ", path.call(print, "body"));
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2016-12-30 08:01:44 +03:00
|
|
|
return parts;
|
|
|
|
}
|
|
|
|
|
2017-01-26 22:58:47 +03:00
|
|
|
function printMemberLookup(path, options, print) {
|
2017-01-05 23:26:45 +03:00
|
|
|
const property = path.call(print, "property");
|
|
|
|
const n = path.getValue();
|
2017-01-26 22:58:47 +03:00
|
|
|
|
2017-05-02 00:41:34 +03:00
|
|
|
if (!n.computed) {
|
|
|
|
return concat([".", property]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
2017-05-04 21:20:52 +03:00
|
|
|
!n.property ||
|
2017-05-02 00:41:34 +03:00
|
|
|
(n.property.type === "Literal" && typeof n.property.value === "number") ||
|
2017-05-21 21:00:16 +03:00
|
|
|
n.property.type === "NumericLiteral"
|
2017-05-02 00:41:34 +03:00
|
|
|
) {
|
|
|
|
return concat(["[", property, "]"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return group(
|
2017-05-21 21:00:16 +03:00
|
|
|
concat(["[", indent(concat([softline, property])), softline, "]"])
|
2017-01-28 18:50:22 +03:00
|
|
|
);
|
2017-01-05 23:26:45 +03:00
|
|
|
}
|
|
|
|
|
2017-01-08 22:44:44 +03:00
|
|
|
// We detect calls on member expressions specially to format a
|
|
|
|
// comman pattern better. The pattern we are looking for is this:
|
|
|
|
//
|
|
|
|
// arr
|
|
|
|
// .map(x => x + 1)
|
|
|
|
// .filter(x => x > 10)
|
|
|
|
// .some(x => x % 2)
|
|
|
|
//
|
2017-01-31 23:32:42 +03:00
|
|
|
// The way it is structured in the AST is via a nested sequence of
|
|
|
|
// MemberExpression and CallExpression. We need to traverse the AST
|
|
|
|
// and make groups out of it to print it in the desired way.
|
|
|
|
function printMemberChain(path, options, print) {
|
|
|
|
// The first phase is to linearize the AST by traversing it down.
|
|
|
|
//
|
|
|
|
// a().b()
|
|
|
|
// has the following AST structure:
|
|
|
|
// CallExpression(MemberExpression(CallExpression(Identifier)))
|
|
|
|
// and we transform it into
|
|
|
|
// [Identifier, CallExpression, MemberExpression, CallExpression]
|
|
|
|
const printedNodes = [];
|
|
|
|
|
|
|
|
function rec(path) {
|
|
|
|
const node = path.getValue();
|
|
|
|
if (node.type === "CallExpression") {
|
|
|
|
printedNodes.unshift({
|
|
|
|
node: node,
|
2017-02-05 05:31:54 +03:00
|
|
|
printed: comments.printComments(
|
|
|
|
path,
|
2017-05-21 21:00:16 +03:00
|
|
|
() =>
|
|
|
|
concat([
|
2017-05-06 05:44:26 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
|
|
|
printArgumentsList(path, options, print)
|
2017-05-21 21:00:16 +03:00
|
|
|
]),
|
2017-02-05 05:31:54 +03:00
|
|
|
options
|
|
|
|
)
|
2017-01-31 23:32:42 +03:00
|
|
|
});
|
|
|
|
path.call(callee => rec(callee), "callee");
|
|
|
|
} else if (node.type === "MemberExpression") {
|
|
|
|
printedNodes.unshift({
|
|
|
|
node: node,
|
2017-02-03 19:50:51 +03:00
|
|
|
printed: comments.printComments(
|
|
|
|
path,
|
2017-04-25 19:48:56 +03:00
|
|
|
() => printMemberLookup(path, options, print),
|
2017-02-03 19:50:51 +03:00
|
|
|
options
|
|
|
|
)
|
2017-01-31 23:32:42 +03:00
|
|
|
});
|
|
|
|
path.call(object => rec(object), "object");
|
|
|
|
} else {
|
|
|
|
printedNodes.unshift({
|
|
|
|
node: node,
|
|
|
|
printed: path.call(print)
|
|
|
|
});
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
|
|
|
}
|
2017-02-05 05:31:54 +03:00
|
|
|
// Note: the comments of the root node have already been printed, so we
|
|
|
|
// need to extract this first call without printing them as they would
|
|
|
|
// if handled inside of the recursive call.
|
|
|
|
printedNodes.unshift({
|
|
|
|
node: path.getValue(),
|
2017-05-06 05:44:26 +03:00
|
|
|
printed: concat([
|
2017-05-21 21:00:16 +03:00
|
|
|
printFunctionTypeParameters(path, options, print),
|
|
|
|
printArgumentsList(path, options, print)
|
2017-05-06 05:44:26 +03:00
|
|
|
])
|
2017-02-05 05:31:54 +03:00
|
|
|
});
|
|
|
|
path.call(callee => rec(callee), "callee");
|
2017-01-31 23:32:42 +03:00
|
|
|
|
|
|
|
// Once we have a linear list of printed nodes, we want to create groups out
|
|
|
|
// of it.
|
|
|
|
//
|
|
|
|
// a().b.c().d().e
|
|
|
|
// will be grouped as
|
|
|
|
// [
|
|
|
|
// [Identifier, CallExpression],
|
|
|
|
// [MemberExpression, MemberExpression, CallExpression],
|
|
|
|
// [MemberExpression, CallExpression],
|
|
|
|
// [MemberExpression],
|
|
|
|
// ]
|
|
|
|
// so that we can print it as
|
|
|
|
// a()
|
|
|
|
// .b.c()
|
|
|
|
// .d()
|
|
|
|
// .e
|
|
|
|
|
2017-02-04 00:18:54 +03:00
|
|
|
// The first group is the first node followed by
|
|
|
|
// - as many CallExpression as possible
|
|
|
|
// < fn()()() >.something()
|
|
|
|
// - then, as many MemberExpression as possible but the last one
|
|
|
|
// < this.items >.something()
|
2017-05-19 23:00:38 +03:00
|
|
|
const groups = [];
|
|
|
|
let currentGroup = [printedNodes[0]];
|
|
|
|
let i = 1;
|
2017-01-31 23:32:42 +03:00
|
|
|
for (; i < printedNodes.length; ++i) {
|
|
|
|
if (printedNodes[i].node.type === "CallExpression") {
|
|
|
|
currentGroup.push(printedNodes[i]);
|
2017-01-09 20:09:04 +03:00
|
|
|
} else {
|
2017-01-31 23:32:42 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-02-04 00:18:54 +03:00
|
|
|
for (; i + 1 < printedNodes.length; ++i) {
|
2017-02-09 18:44:56 +03:00
|
|
|
if (
|
|
|
|
printedNodes[i].node.type === "MemberExpression" &&
|
2017-02-16 06:56:11 +03:00
|
|
|
printedNodes[i + 1].node.type === "MemberExpression"
|
2017-02-09 18:44:56 +03:00
|
|
|
) {
|
2017-02-04 00:18:54 +03:00
|
|
|
currentGroup.push(printedNodes[i]);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-01-31 23:32:42 +03:00
|
|
|
groups.push(currentGroup);
|
|
|
|
currentGroup = [];
|
|
|
|
|
|
|
|
// Then, each following group is a sequence of MemberExpression followed by
|
|
|
|
// a sequence of CallExpression. To compute it, we keep adding things to the
|
|
|
|
// group until we has seen a CallExpression in the past and reach a
|
|
|
|
// MemberExpression
|
2017-05-19 23:00:38 +03:00
|
|
|
let hasSeenCallExpression = false;
|
2017-01-31 23:32:42 +03:00
|
|
|
for (; i < printedNodes.length; ++i) {
|
2017-02-03 19:50:51 +03:00
|
|
|
if (
|
2017-05-21 21:00:16 +03:00
|
|
|
hasSeenCallExpression &&
|
|
|
|
printedNodes[i].node.type === "MemberExpression"
|
2017-02-03 19:50:51 +03:00
|
|
|
) {
|
2017-02-23 18:34:52 +03:00
|
|
|
// [0] should be appended at the end of the group instead of the
|
|
|
|
// beginning of the next one
|
|
|
|
if (printedNodes[i].node.computed) {
|
|
|
|
currentGroup.push(printedNodes[i]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-01-31 23:32:42 +03:00
|
|
|
groups.push(currentGroup);
|
|
|
|
currentGroup = [];
|
|
|
|
hasSeenCallExpression = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (printedNodes[i].node.type === "CallExpression") {
|
|
|
|
hasSeenCallExpression = true;
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
2017-01-31 23:32:42 +03:00
|
|
|
currentGroup.push(printedNodes[i]);
|
2017-05-23 23:58:52 +03:00
|
|
|
|
|
|
|
if (
|
|
|
|
printedNodes[i].node.comments &&
|
|
|
|
printedNodes[i].node.comments.some(comment => comment.trailing)
|
|
|
|
) {
|
|
|
|
groups.push(currentGroup);
|
|
|
|
currentGroup = [];
|
|
|
|
hasSeenCallExpression = false;
|
|
|
|
}
|
2017-01-31 23:32:42 +03:00
|
|
|
}
|
|
|
|
if (currentGroup.length > 0) {
|
|
|
|
groups.push(currentGroup);
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
2017-01-31 23:32:42 +03:00
|
|
|
|
|
|
|
// There are cases like Object.keys(), Observable.of(), _.values() where
|
|
|
|
// they are the subject of all the chained calls and therefore should
|
|
|
|
// be kept on the same line:
|
|
|
|
//
|
|
|
|
// Object.keys(items)
|
|
|
|
// .filter(x => x)
|
|
|
|
// .map(x => x)
|
|
|
|
//
|
|
|
|
// In order to detect those cases, we use an heuristic: if the first
|
|
|
|
// node is just an identifier with the name starting with a capital
|
2017-02-23 17:41:44 +03:00
|
|
|
// letter, just a sequence of _$ or this. The rationale is that they are
|
2017-01-31 23:32:42 +03:00
|
|
|
// likely to be factories.
|
2017-04-12 20:16:11 +03:00
|
|
|
const shouldMerge =
|
2017-04-13 03:26:58 +03:00
|
|
|
groups.length >= 2 &&
|
|
|
|
!groups[1][0].node.comments &&
|
2017-04-12 20:16:11 +03:00
|
|
|
groups[0].length === 1 &&
|
2017-02-23 17:41:44 +03:00
|
|
|
(groups[0][0].node.type === "ThisExpression" ||
|
2017-02-23 20:57:51 +03:00
|
|
|
(groups[0][0].node.type === "Identifier" &&
|
2017-04-13 03:26:58 +03:00
|
|
|
groups[0][0].node.name.match(/(^[A-Z])|^[_$]+$/)));
|
2017-01-31 23:32:42 +03:00
|
|
|
|
|
|
|
function printGroup(printedGroup) {
|
|
|
|
return concat(printedGroup.map(tuple => tuple.printed));
|
|
|
|
}
|
|
|
|
|
2017-02-28 18:55:32 +03:00
|
|
|
function printIndentedGroup(groups) {
|
2017-05-24 02:31:10 +03:00
|
|
|
if (groups.length === 0) {
|
|
|
|
return "";
|
|
|
|
}
|
2017-02-13 18:07:33 +03:00
|
|
|
return indent(
|
2017-02-28 18:55:32 +03:00
|
|
|
group(concat([hardline, join(hardline, groups.map(printGroup))]))
|
2017-02-13 18:07:33 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-01-31 23:32:42 +03:00
|
|
|
const printedGroups = groups.map(printGroup);
|
|
|
|
const oneLine = concat(printedGroups);
|
2017-05-24 02:31:10 +03:00
|
|
|
|
2017-06-08 01:20:11 +03:00
|
|
|
const cutoff = shouldMerge ? 3 : 2;
|
2017-05-24 02:31:10 +03:00
|
|
|
const flatGroups = groups
|
2017-06-08 01:20:11 +03:00
|
|
|
.slice(0, cutoff)
|
2017-05-24 03:13:01 +03:00
|
|
|
.reduce((res, group) => res.concat(group), []);
|
2017-05-24 02:31:10 +03:00
|
|
|
|
2017-03-20 19:32:49 +03:00
|
|
|
const hasComment =
|
2017-06-03 01:50:43 +03:00
|
|
|
flatGroups.slice(1, -1).some(node => hasLeadingComment(node.node)) ||
|
2017-06-08 01:20:11 +03:00
|
|
|
flatGroups.slice(0, -1).some(node => hasTrailingComment(node.node)) ||
|
|
|
|
(groups[cutoff] && hasLeadingComment(groups[cutoff][0].node));
|
2017-01-31 23:32:42 +03:00
|
|
|
|
|
|
|
// If we only have a single `.`, we shouldn't do anything fancy and just
|
|
|
|
// render everything concatenated together.
|
2017-03-01 20:37:02 +03:00
|
|
|
if (
|
2017-06-08 01:20:11 +03:00
|
|
|
groups.length <= cutoff &&
|
2017-03-01 20:37:02 +03:00
|
|
|
!hasComment &&
|
|
|
|
// (a || b).map() should be break before .map() instead of ||
|
|
|
|
groups[0][0].node.type !== "LogicalExpression"
|
|
|
|
) {
|
2017-01-31 23:32:42 +03:00
|
|
|
return group(oneLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
const expanded = concat([
|
|
|
|
printGroup(groups[0]),
|
2017-02-28 18:55:32 +03:00
|
|
|
shouldMerge ? concat(groups.slice(1, 2).map(printGroup)) : "",
|
|
|
|
printIndentedGroup(groups.slice(shouldMerge ? 2 : 1))
|
2017-01-31 23:32:42 +03:00
|
|
|
]);
|
|
|
|
|
2017-02-13 20:05:18 +03:00
|
|
|
// If there's a comment, we don't want to print in one line.
|
|
|
|
if (hasComment) {
|
|
|
|
return group(expanded);
|
|
|
|
}
|
|
|
|
|
2017-01-31 23:32:42 +03:00
|
|
|
// If any group but the last one has a hard line, we want to force expand
|
|
|
|
// it. If the last group is a function it's okay to inline if it fits.
|
|
|
|
if (printedGroups.slice(0, -1).some(willBreak)) {
|
|
|
|
return group(expanded);
|
|
|
|
}
|
|
|
|
|
2017-04-12 19:26:27 +03:00
|
|
|
return concat([
|
|
|
|
// We only need to check `oneLine` because if `expanded` is chosen
|
|
|
|
// that means that the parent group has already been broken
|
|
|
|
// naturally
|
|
|
|
willBreak(oneLine) ? breakParent : "",
|
|
|
|
conditionalGroup([oneLine, expanded])
|
2017-04-12 20:16:11 +03:00
|
|
|
]);
|
2017-01-08 22:44:44 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 00:25:20 +03:00
|
|
|
function isEmptyJSXElement(node) {
|
2017-05-03 03:06:25 +03:00
|
|
|
if (node.children.length === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (node.children.length > 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
// if there is one child but it's just a newline, treat as empty
|
|
|
|
const value = node.children[0].value;
|
|
|
|
if (!/\S/.test(value) && /\n/.test(value)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-06-06 01:12:59 +03:00
|
|
|
return false;
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// JSX Children are strange, mostly for two reasons:
|
|
|
|
// 1. JSX reads newlines into string values, instead of skipping them like JS
|
|
|
|
// 2. up to one whitespace between elements within a line is significant,
|
|
|
|
// but not between lines.
|
|
|
|
//
|
|
|
|
// So for one thing, '\n' needs to be parsed out of string literals
|
|
|
|
// and turned into hardlines (with string boundaries otherwise using softline)
|
|
|
|
//
|
|
|
|
// For another, leading, trailing, and lone whitespace all need to
|
|
|
|
// turn themselves into the rather ugly `{' '}` when breaking.
|
2017-05-31 19:42:36 +03:00
|
|
|
//
|
|
|
|
// Finally we print JSX using the `fill` doc primitive.
|
|
|
|
// This requires that we give it an array of alternating
|
|
|
|
// content and whitespace elements.
|
|
|
|
// To ensure this we add dummy `""` content elements as needed.
|
2017-02-07 21:47:34 +03:00
|
|
|
function printJSXChildren(path, options, print, jsxWhitespace) {
|
2017-01-19 00:25:20 +03:00
|
|
|
const n = path.getValue();
|
|
|
|
const children = [];
|
|
|
|
|
|
|
|
// using `map` instead of `each` because it provides `i`
|
2017-05-21 22:11:09 +03:00
|
|
|
path.map((childPath, i) => {
|
2017-04-12 20:16:11 +03:00
|
|
|
const child = childPath.getValue();
|
2017-05-26 21:45:24 +03:00
|
|
|
if (isLiteral(child) && typeof child.value === "string") {
|
2017-05-03 18:47:15 +03:00
|
|
|
const value = child.raw || child.extra.raw;
|
2017-04-12 20:16:11 +03:00
|
|
|
|
2017-05-23 19:09:29 +03:00
|
|
|
// Contains a non-whitespace character
|
|
|
|
if (/[^ \n\r\t]/.test(value)) {
|
2017-04-12 20:16:11 +03:00
|
|
|
// treat each line of text as its own entity
|
2017-05-11 02:13:21 +03:00
|
|
|
value.split(/(\r?\n\s*)/).forEach(textLine => {
|
|
|
|
const newlines = textLine.match(/\n/g);
|
2017-04-12 20:16:11 +03:00
|
|
|
if (newlines) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(hardline);
|
2017-01-27 22:20:01 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
// allow one extra newline
|
|
|
|
if (newlines.length > 1) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(hardline);
|
2017-01-27 22:20:01 +03:00
|
|
|
}
|
2017-04-12 20:16:11 +03:00
|
|
|
return;
|
|
|
|
}
|
2017-01-27 22:20:01 +03:00
|
|
|
|
2017-05-11 02:13:21 +03:00
|
|
|
if (textLine.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-23 19:09:29 +03:00
|
|
|
const beginSpace = /^[ \n\r\t]+/.test(textLine);
|
2017-04-12 20:16:11 +03:00
|
|
|
if (beginSpace) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
|
|
|
children.push(jsxWhitespace);
|
|
|
|
}
|
|
|
|
|
|
|
|
const stripped = textLine.replace(/^[ \n\r\t]+|[ \n\r\t]+$/g, "");
|
|
|
|
// Split text into words separated by "line"s.
|
|
|
|
stripped.split(/([ \n\r\t]+)/).forEach(word => {
|
|
|
|
const space = /[ \n\r\t]+/.test(word);
|
|
|
|
if (space) {
|
|
|
|
children.push(line);
|
|
|
|
} else {
|
|
|
|
children.push(word);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const endSpace = /[ \n\r\t]+$/.test(textLine);
|
|
|
|
if (endSpace) {
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(jsxWhitespace);
|
2017-05-11 02:13:21 +03:00
|
|
|
} else {
|
2017-05-23 21:56:21 +03:00
|
|
|
// Ideally this would be a `softline` to allow a break between
|
|
|
|
// tags and text.
|
|
|
|
// Unfortunately Facebook have a custom translation pipeline
|
|
|
|
// (https://github.com/prettier/prettier/issues/1581#issuecomment-300975032)
|
|
|
|
// that uses the JSX syntax, but does not follow the React whitespace
|
|
|
|
// rules.
|
|
|
|
// Ensuring that we never have a break between tags and text in JSX
|
|
|
|
// will allow Facebook to adopt Prettier without too much of an
|
|
|
|
// adverse effect on formatting algorithm.
|
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (/\n/.test(value)) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(hardline);
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
// allow one extra newline
|
|
|
|
if (value.match(/\n/g).length > 1) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(hardline);
|
2017-01-19 02:31:46 +03:00
|
|
|
}
|
2017-05-23 19:09:29 +03:00
|
|
|
} else if (/[ \n\r\t]/.test(value)) {
|
2017-05-03 18:47:15 +03:00
|
|
|
// whitespace(s)-only without newlines,
|
|
|
|
// eg; one or more spaces separating two elements
|
|
|
|
for (let i = 0; i < value.length; ++i) {
|
2017-05-11 02:13:21 +03:00
|
|
|
// Because fill expects alternating content and whitespace parts
|
2017-05-31 19:42:36 +03:00
|
|
|
// we need to include an empty content part before each JSX
|
2017-05-11 02:13:21 +03:00
|
|
|
// whitespace.
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
|
|
|
children.push(jsxWhitespace);
|
2017-05-03 18:47:15 +03:00
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
2017-04-12 20:16:11 +03:00
|
|
|
} else {
|
|
|
|
children.push(print(childPath));
|
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
const next = n.children[i + 1];
|
2017-05-26 21:45:24 +03:00
|
|
|
const followedByJSXElement = next && !isLiteral(next);
|
2017-06-05 23:52:54 +03:00
|
|
|
const followedByJSXWhitespace =
|
|
|
|
next &&
|
|
|
|
next.type === "JSXExpressionContainer" &&
|
|
|
|
isLiteral(next.expression) &&
|
|
|
|
next.expression.value === " ";
|
|
|
|
|
|
|
|
if (followedByJSXElement && !followedByJSXWhitespace) {
|
2017-04-12 20:16:11 +03:00
|
|
|
children.push(softline);
|
2017-05-31 19:42:36 +03:00
|
|
|
} else {
|
|
|
|
// Ideally this would be a softline as well.
|
|
|
|
// See the comment above about the Facebook translation pipeline as
|
|
|
|
// to why this is an empty string.
|
|
|
|
children.push("");
|
2017-04-12 20:16:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}, "children");
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSX expands children from the inside-out, instead of the outside-in.
|
|
|
|
// This is both to break children before attributes,
|
|
|
|
// and to ensure that when children break, their parents do as well.
|
|
|
|
//
|
|
|
|
// Any element that is written without any newlines and fits on a single line
|
|
|
|
// is left that way.
|
|
|
|
// Not only that, any user-written-line containing multiple JSX siblings
|
|
|
|
// should also be kept on one line if possible,
|
|
|
|
// so each user-written-line is wrapped in its own group.
|
|
|
|
//
|
|
|
|
// Elements that contain newlines or don't fit on a single line (recursively)
|
|
|
|
// are fully-split, using hardline and shouldBreak: true.
|
|
|
|
//
|
|
|
|
// To support that case properly, all leading and trailing spaces
|
|
|
|
// are stripped from the list of children, and replaced with a single hardline.
|
2017-01-12 02:15:12 +03:00
|
|
|
function printJSXElement(path, options, print) {
|
|
|
|
const n = path.getValue();
|
|
|
|
|
2017-01-24 02:47:11 +03:00
|
|
|
// Turn <div></div> into <div />
|
2017-01-19 00:25:20 +03:00
|
|
|
if (isEmptyJSXElement(n)) {
|
|
|
|
n.openingElement.selfClosing = true;
|
|
|
|
delete n.closingElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
const openingLines = path.call(print, "openingElement");
|
2017-04-11 19:33:02 +03:00
|
|
|
const closingLines = path.call(print, "closingElement");
|
|
|
|
|
|
|
|
if (
|
|
|
|
n.children.length === 1 &&
|
|
|
|
n.children[0].type === "JSXExpressionContainer" &&
|
|
|
|
(n.children[0].expression.type === "TemplateLiteral" ||
|
|
|
|
n.children[0].expression.type === "TaggedTemplateExpression")
|
|
|
|
) {
|
|
|
|
return concat([
|
|
|
|
openingLines,
|
|
|
|
concat(path.map(print, "children")),
|
|
|
|
closingLines
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no children, just print the opening element
|
2017-01-12 02:15:12 +03:00
|
|
|
if (n.openingElement.selfClosing) {
|
|
|
|
assert.ok(!n.closingElement);
|
2017-01-19 00:25:20 +03:00
|
|
|
return openingLines;
|
|
|
|
}
|
2017-03-08 04:19:05 +03:00
|
|
|
// Record any breaks. Should never go from true to false, only false to true.
|
|
|
|
let forcedBreak = willBreak(openingLines);
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-05-11 02:13:21 +03:00
|
|
|
const rawJsxWhitespace = options.singleQuote ? "{' '}" : '{" "}';
|
2017-06-05 23:52:54 +03:00
|
|
|
const jsxWhitespace = ifBreak(concat([rawJsxWhitespace, softline]), " ");
|
2017-05-11 02:13:21 +03:00
|
|
|
|
2017-02-07 21:47:34 +03:00
|
|
|
const children = printJSXChildren(path, options, print, jsxWhitespace);
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-05-31 19:42:36 +03:00
|
|
|
// Remove multiple filler empty strings
|
|
|
|
// These can occur when a text element is followed by a newline.
|
|
|
|
for (let i = children.length - 2; i >= 0; i--) {
|
|
|
|
if (children[i] === "" && children[i + 1] === "") {
|
|
|
|
children.splice(i, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trim trailing lines (or empty strings), recording if there was a hardline
|
2017-01-27 22:20:01 +03:00
|
|
|
let numTrailingHard = 0;
|
2017-05-31 19:42:36 +03:00
|
|
|
while (
|
|
|
|
children.length &&
|
|
|
|
(isLineNext(util.getLast(children)) || isEmpty(util.getLast(children)))
|
|
|
|
) {
|
2017-01-24 02:47:11 +03:00
|
|
|
if (willBreak(util.getLast(children))) {
|
2017-01-27 22:20:01 +03:00
|
|
|
++numTrailingHard;
|
2017-01-24 02:47:11 +03:00
|
|
|
forcedBreak = true;
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
children.pop();
|
|
|
|
}
|
2017-01-27 22:20:01 +03:00
|
|
|
// allow one extra newline
|
|
|
|
if (numTrailingHard > 1) {
|
2017-05-31 19:42:36 +03:00
|
|
|
children.push("");
|
2017-01-27 22:20:01 +03:00
|
|
|
children.push(hardline);
|
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-05-23 21:56:21 +03:00
|
|
|
// Trim leading lines (or empty strings), recording if there was a hardline
|
2017-01-27 22:20:01 +03:00
|
|
|
let numLeadingHard = 0;
|
2017-05-31 19:42:36 +03:00
|
|
|
while (
|
|
|
|
children.length &&
|
|
|
|
(isLineNext(children[0]) || isEmpty(children[0])) &&
|
|
|
|
(isLineNext(children[1]) || isEmpty(children[1]))
|
|
|
|
) {
|
|
|
|
if (willBreak(children[0]) || willBreak(children[1])) {
|
2017-01-27 22:20:01 +03:00
|
|
|
++numLeadingHard;
|
2017-01-24 02:47:11 +03:00
|
|
|
forcedBreak = true;
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
children.shift();
|
2017-05-31 19:42:36 +03:00
|
|
|
children.shift();
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
2017-01-27 22:20:01 +03:00
|
|
|
// allow one extra newline
|
|
|
|
if (numLeadingHard > 1) {
|
|
|
|
children.unshift(hardline);
|
2017-05-31 19:42:36 +03:00
|
|
|
children.unshift("");
|
2017-01-27 22:20:01 +03:00
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-05-11 02:13:21 +03:00
|
|
|
// Tweak how we format children if outputting this element over multiple lines.
|
|
|
|
// Also detect whether we will force this element to output over multiple lines.
|
2017-05-21 22:11:09 +03:00
|
|
|
const multilineChildren = [];
|
2017-02-07 21:47:34 +03:00
|
|
|
children.forEach((child, i) => {
|
2017-05-11 02:13:21 +03:00
|
|
|
// Ensure that we display leading, trailing, and solitary whitespace as
|
|
|
|
// `{" "}` when outputting this element over multiple lines.
|
2017-02-07 21:47:34 +03:00
|
|
|
if (child === jsxWhitespace) {
|
2017-05-31 19:42:36 +03:00
|
|
|
if (i === 1 && children[i - 1] === "") {
|
2017-05-31 20:16:50 +03:00
|
|
|
multilineChildren.push(rawJsxWhitespace);
|
2017-02-07 21:47:34 +03:00
|
|
|
return;
|
|
|
|
} else if (i === children.length - 1) {
|
2017-06-05 23:52:54 +03:00
|
|
|
multilineChildren.push(rawJsxWhitespace);
|
2017-05-31 20:16:50 +03:00
|
|
|
return;
|
2017-02-07 21:47:34 +03:00
|
|
|
}
|
2017-01-24 02:47:11 +03:00
|
|
|
}
|
2017-01-12 02:15:12 +03:00
|
|
|
|
2017-05-11 02:13:21 +03:00
|
|
|
multilineChildren.push(child);
|
2017-01-24 02:47:11 +03:00
|
|
|
|
2017-05-11 02:13:21 +03:00
|
|
|
if (willBreak(child)) {
|
|
|
|
forcedBreak = true;
|
2017-02-07 21:47:34 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-01-19 02:31:46 +03:00
|
|
|
const multiLineElem = group(
|
|
|
|
concat([
|
|
|
|
openingLines,
|
2017-05-11 02:13:21 +03:00
|
|
|
indent(concat([hardline, fill(multilineChildren)])),
|
2017-01-19 02:31:46 +03:00
|
|
|
hardline,
|
|
|
|
closingLines
|
|
|
|
])
|
|
|
|
);
|
2017-01-19 00:25:20 +03:00
|
|
|
|
2017-01-24 02:47:11 +03:00
|
|
|
if (forcedBreak) {
|
|
|
|
return multiLineElem;
|
2017-01-12 02:15:12 +03:00
|
|
|
}
|
|
|
|
|
2017-01-24 02:47:11 +03:00
|
|
|
return conditionalGroup([
|
2017-05-11 02:13:21 +03:00
|
|
|
group(concat([openingLines, fill(children), closingLines])),
|
2017-01-24 02:47:11 +03:00
|
|
|
multiLineElem
|
|
|
|
]);
|
2017-01-19 00:25:20 +03:00
|
|
|
}
|
|
|
|
|
2017-04-25 19:48:56 +03:00
|
|
|
function maybeWrapJSXElementInParens(path, elem) {
|
2017-01-12 02:15:12 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-05-03 03:06:25 +03:00
|
|
|
if (!parent) {
|
|
|
|
return elem;
|
|
|
|
}
|
2017-01-19 00:25:20 +03:00
|
|
|
|
|
|
|
const NO_WRAP_PARENTS = {
|
2017-03-05 19:56:07 +03:00
|
|
|
ArrayExpression: true,
|
2017-01-19 02:31:46 +03:00
|
|
|
JSXElement: true,
|
2017-03-01 23:05:57 +03:00
|
|
|
JSXExpressionContainer: true,
|
2017-01-19 02:31:46 +03:00
|
|
|
ExpressionStatement: true,
|
|
|
|
CallExpression: true,
|
2017-01-26 22:51:08 +03:00
|
|
|
ConditionalExpression: true,
|
2017-05-26 02:26:49 +03:00
|
|
|
LogicalExpression: true,
|
|
|
|
ArrowFunctionExpression: true
|
2017-01-19 00:25:20 +03:00
|
|
|
};
|
|
|
|
if (NO_WRAP_PARENTS[parent.type]) {
|
2017-01-12 02:15:12 +03:00
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
2017-01-24 21:54:01 +03:00
|
|
|
return group(
|
2017-01-13 23:03:53 +03:00
|
|
|
concat([
|
|
|
|
ifBreak("("),
|
2017-04-07 05:49:37 +03:00
|
|
|
indent(concat([softline, elem])),
|
2017-01-13 23:03:53 +03:00
|
|
|
softline,
|
|
|
|
ifBreak(")")
|
|
|
|
])
|
|
|
|
);
|
2017-01-12 02:15:12 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 01:01:17 +03:00
|
|
|
function isBinaryish(node) {
|
|
|
|
return node.type === "BinaryExpression" || node.type === "LogicalExpression";
|
|
|
|
}
|
|
|
|
|
2017-02-15 17:41:06 +03:00
|
|
|
function shouldInlineLogicalExpression(node) {
|
2017-05-12 17:48:03 +03:00
|
|
|
if (node.type !== "LogicalExpression") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
if (
|
|
|
|
node.right.type === "ObjectExpression" &&
|
|
|
|
node.right.properties.length !== 0
|
|
|
|
) {
|
2017-05-12 17:48:03 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
if (
|
|
|
|
node.right.type === "ArrayExpression" &&
|
|
|
|
node.right.elements.length !== 0
|
|
|
|
) {
|
2017-05-12 17:48:03 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2017-02-15 17:41:06 +03:00
|
|
|
}
|
|
|
|
|
2017-01-19 01:01:17 +03:00
|
|
|
// For binary expressions to be consistent, we need to group
|
|
|
|
// subsequent operators with the same precedence level under a single
|
|
|
|
// group. Otherwise they will be nested such that some of them break
|
|
|
|
// onto new lines but not all. Operators with the same precedence
|
|
|
|
// level should either all break or not. Because we group them by
|
|
|
|
// precedence level and the AST is structured based on precedence
|
|
|
|
// level, things are naturally broken up correctly, i.e. `&&` is
|
|
|
|
// broken before `+`.
|
2017-05-21 21:00:16 +03:00
|
|
|
function printBinaryishExpressions(
|
|
|
|
path,
|
|
|
|
print,
|
|
|
|
options,
|
|
|
|
isNested,
|
|
|
|
isInsideParenthesis
|
|
|
|
) {
|
2017-03-22 00:49:08 +03:00
|
|
|
let parts = [];
|
2017-05-21 22:11:09 +03:00
|
|
|
const node = path.getValue();
|
2017-01-19 01:01:17 +03:00
|
|
|
|
|
|
|
// We treat BinaryExpression and LogicalExpression nodes the same.
|
2017-01-19 02:31:46 +03:00
|
|
|
if (isBinaryish(node)) {
|
2017-01-19 01:01:17 +03:00
|
|
|
// Put all operators with the same precedence level in the same
|
|
|
|
// group. The reason we only need to do this with the `left`
|
|
|
|
// expression is because given an expression like `1 + 2 - 3`, it
|
|
|
|
// is always parsed like `((1 + 2) - 3)`, meaning the `left` side
|
|
|
|
// is where the rest of the expression will exist. Binary
|
|
|
|
// expressions on the right side mean they have a difference
|
|
|
|
// precedence level and should be treated as a separate group, so
|
2017-03-09 00:11:53 +03:00
|
|
|
// print them normally. (This doesn't hold for the `**` operator,
|
|
|
|
// which is unique in that it is right-associative.)
|
2017-01-19 02:31:46 +03:00
|
|
|
if (
|
|
|
|
util.getPrecedence(node.left.operator) ===
|
2017-05-21 21:00:16 +03:00
|
|
|
util.getPrecedence(node.operator) &&
|
|
|
|
node.operator !== "**"
|
2017-01-19 02:31:46 +03:00
|
|
|
) {
|
2017-03-22 00:49:08 +03:00
|
|
|
// Flatten them out by recursively calling this function.
|
2017-04-12 20:16:11 +03:00
|
|
|
parts = parts.concat(
|
|
|
|
path.call(
|
|
|
|
left =>
|
|
|
|
printBinaryishExpressions(
|
|
|
|
left,
|
|
|
|
print,
|
|
|
|
options,
|
2017-04-19 20:43:48 +03:00
|
|
|
/* isNested */ true,
|
|
|
|
isInsideParenthesis
|
2017-04-12 20:16:11 +03:00
|
|
|
),
|
|
|
|
"left"
|
|
|
|
)
|
|
|
|
);
|
2017-01-19 02:31:46 +03:00
|
|
|
} else {
|
2017-01-19 01:01:17 +03:00
|
|
|
parts.push(path.call(print, "left"));
|
|
|
|
}
|
|
|
|
|
2017-02-15 17:41:06 +03:00
|
|
|
const right = concat([
|
|
|
|
node.operator,
|
|
|
|
shouldInlineLogicalExpression(node) ? " " : line,
|
|
|
|
path.call(print, "right")
|
|
|
|
]);
|
2017-02-09 18:44:03 +03:00
|
|
|
|
2017-03-03 06:45:26 +03:00
|
|
|
// If there's only a single binary expression, we want to create a group
|
|
|
|
// in order to avoid having a small right part like -1 be on its own line.
|
2017-02-09 18:44:03 +03:00
|
|
|
const parent = path.getParentNode();
|
2017-04-12 20:16:11 +03:00
|
|
|
const shouldGroup =
|
2017-04-19 23:18:01 +03:00
|
|
|
!(isInsideParenthesis && node.type === "LogicalExpression") &&
|
2017-04-12 20:16:11 +03:00
|
|
|
parent.type !== node.type &&
|
2017-03-03 06:45:26 +03:00
|
|
|
node.left.type !== node.type &&
|
|
|
|
node.right.type !== node.type;
|
2017-02-09 18:44:03 +03:00
|
|
|
|
|
|
|
parts.push(" ", shouldGroup ? group(right) : right);
|
2017-01-27 22:03:44 +03:00
|
|
|
|
|
|
|
// The root comments are already printed, but we need to manually print
|
|
|
|
// the other ones since we don't call the normal print on BinaryExpression,
|
|
|
|
// only for the left and right parts
|
|
|
|
if (isNested && node.comments) {
|
2017-04-25 19:48:56 +03:00
|
|
|
parts = comments.printComments(path, () => concat(parts), options);
|
2017-01-27 22:03:44 +03:00
|
|
|
}
|
2017-01-19 02:31:46 +03:00
|
|
|
} else {
|
2017-01-19 01:01:17 +03:00
|
|
|
// Our stopping case. Simply print the node normally.
|
|
|
|
parts.push(path.call(print));
|
|
|
|
}
|
|
|
|
|
|
|
|
return parts;
|
|
|
|
}
|
|
|
|
|
2017-04-27 21:21:42 +03:00
|
|
|
function printAssignmentRight(rightNode, printedRight, canBreak, options) {
|
|
|
|
if (hasLeadingOwnLineComment(options.originalText, rightNode)) {
|
|
|
|
return indent(concat([hardline, printedRight]));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canBreak) {
|
|
|
|
return indent(concat([line, printedRight]));
|
|
|
|
}
|
|
|
|
|
|
|
|
return concat([" ", printedRight]);
|
|
|
|
}
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
function printAssignment(
|
2017-04-14 03:09:00 +03:00
|
|
|
leftNode,
|
2017-04-12 20:16:11 +03:00
|
|
|
printedLeft,
|
|
|
|
operator,
|
|
|
|
rightNode,
|
|
|
|
printedRight,
|
|
|
|
options
|
|
|
|
) {
|
2017-03-15 19:45:40 +03:00
|
|
|
if (!rightNode) {
|
|
|
|
return printedLeft;
|
|
|
|
}
|
|
|
|
|
2017-05-21 21:00:16 +03:00
|
|
|
const canBreak =
|
2017-04-07 20:51:02 +03:00
|
|
|
(isBinaryish(rightNode) && !shouldInlineLogicalExpression(rightNode)) ||
|
2017-05-29 17:31:24 +03:00
|
|
|
((leftNode.type === "Identifier" ||
|
|
|
|
isStringLiteral(leftNode) ||
|
|
|
|
leftNode.type === "MemberExpression") &&
|
|
|
|
(isStringLiteral(rightNode) || isMemberExpressionChain(rightNode)));
|
2017-04-27 21:21:42 +03:00
|
|
|
|
|
|
|
const printed = printAssignmentRight(
|
|
|
|
rightNode,
|
|
|
|
printedRight,
|
|
|
|
canBreak,
|
|
|
|
options
|
|
|
|
);
|
2017-03-15 19:45:40 +03:00
|
|
|
|
2017-05-25 04:19:15 +03:00
|
|
|
return group(concat([printedLeft, operator, printed]));
|
2017-03-15 19:45:40 +03:00
|
|
|
}
|
|
|
|
|
2017-04-13 06:25:44 +03:00
|
|
|
function adjustClause(node, clause, forceSpace) {
|
|
|
|
if (node.type === "EmptyStatement") {
|
2017-01-16 20:54:39 +03:00
|
|
|
return ";";
|
|
|
|
}
|
|
|
|
|
2017-04-13 06:25:44 +03:00
|
|
|
if (node.type === "BlockStatement" || forceSpace) {
|
2017-01-28 18:50:22 +03:00
|
|
|
return concat([" ", clause]);
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:10:22 +03:00
|
|
|
|
2017-04-07 05:49:37 +03:00
|
|
|
return indent(concat([line, clause]));
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
|
|
|
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
function nodeStr(node, options, isFlowDirectiveLiteral) {
|
2017-01-22 23:32:43 +03:00
|
|
|
const raw = node.extra ? node.extra.raw : node.raw;
|
|
|
|
// `rawContent` is the string exactly like it appeared in the input source
|
|
|
|
// code, with its enclosing quote.
|
|
|
|
const rawContent = raw.slice(1, -1);
|
2017-01-12 07:36:47 +03:00
|
|
|
|
2017-01-22 23:32:43 +03:00
|
|
|
const double = { quote: '"', regex: /"/g };
|
|
|
|
const single = { quote: "'", regex: /'/g };
|
2017-01-12 07:36:47 +03:00
|
|
|
|
2017-01-22 23:32:43 +03:00
|
|
|
const preferred = options.singleQuote ? single : double;
|
|
|
|
const alternate = preferred === single ? double : single;
|
|
|
|
|
|
|
|
let shouldUseAlternateQuote = false;
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
const isDirectiveLiteral =
|
|
|
|
isFlowDirectiveLiteral || node.type === "DirectiveLiteral";
|
|
|
|
let canChangeDirectiveQuotes = false;
|
2017-01-18 00:47:20 +03:00
|
|
|
|
2017-01-22 23:32:43 +03:00
|
|
|
// If `rawContent` contains at least one of the quote preferred for enclosing
|
|
|
|
// the string, we might want to enclose with the alternate quote instead, to
|
|
|
|
// minimize the number of escaped quotes.
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
// Also check for the alternate quote, to determine if we're allowed to swap
|
|
|
|
// the quotes on a DirectiveLiteral.
|
|
|
|
if (
|
|
|
|
rawContent.includes(preferred.quote) ||
|
|
|
|
rawContent.includes(alternate.quote)
|
|
|
|
) {
|
2017-01-22 23:32:43 +03:00
|
|
|
const numPreferredQuotes = (rawContent.match(preferred.regex) || []).length;
|
|
|
|
const numAlternateQuotes = (rawContent.match(alternate.regex) || []).length;
|
|
|
|
|
|
|
|
shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes;
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
} else {
|
|
|
|
canChangeDirectiveQuotes = true;
|
2017-01-18 00:47:20 +03:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:32:43 +03:00
|
|
|
const enclosingQuote = shouldUseAlternateQuote
|
|
|
|
? alternate.quote
|
|
|
|
: preferred.quote;
|
|
|
|
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
// Directives are exact code unit sequences, which means that you can't
|
|
|
|
// change the escape sequences they use.
|
|
|
|
// See https://github.com/prettier/prettier/issues/1555
|
|
|
|
// and https://tc39.github.io/ecma262/#directive-prologue
|
|
|
|
if (isDirectiveLiteral) {
|
|
|
|
if (canChangeDirectiveQuotes) {
|
|
|
|
return enclosingQuote + rawContent + enclosingQuote;
|
|
|
|
}
|
2017-06-06 01:12:59 +03:00
|
|
|
return raw;
|
Preserve code unit sequence of directive literals (#1571)
* Print directive literals verbatim
This addresses https://github.com/prettier/prettier/issues/1555,
but doesn't seem to pass the AST_COMPARE=1 tests:
AST_COMPARE=1 npm test -- tests/quotes -t strings
However, running `prettier --debug-check` on the relevant file *does*
work:
prettier tests/quotes/strings.js --debug-check
* Change directive literal quotes if it doesn't contain quotes
This addresses https://github.com/prettier/prettier/pull/1560#discussion_r115396257
From https://github.com/prettier/prettier/issues/1555#issue-227206837:
> It's okay to change the type of quotation marks used, but only if
doing so does not require changing any characters within the directive.
* Don't change directive literal quotes if it contains a backslash
This passes the `--debug-check` tests again:
prettier tests/quotes/strings.js --debug-check
* Try to add regression test for escaped directive literals
This seems not to work, despite the following command having the correct
output:
echo "'\''" | prettier
You can use the following to get an idea of how flow/typescript parse
this:
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'flow'}), null, 2)"
node -p "JSON.stringify(require('./src/parser').parse('\\'\\\\\'\\'', {parser: 'typescript'}), null, 2)"
* WIP Disable Flow/Typescript for ./tests/directives
We don't yet handle escaped directives for them, but Babylon works.
(similar to https://github.com/prettier/prettier/pull/602/commits/90bf93713c78a6a6b3f55e52d7be172ece9b56df#diff-0de18284f37da79ab8af4e4690919abaR1)
* Revert "WIP Disable Flow/Typescript for ./tests/directives"
This reverts commit 2aba6231271f6985a395c31e3df9323e8f3da115.
* Prevent test strings from being parsed as directives
See https://github.com/prettier/prettier/pull/1560#issue-227225960
* Add more escaped directive tests
* Infer DirectiveLiterals from Flow parser
* Don't test TypeScript on directives
See https://github.com/prettier/prettier/pull/1560#issuecomment-300296221
* fixup! Infer DirectiveLiterals from Flow parser
* Don't fake objects that look like a DirectiveLiteral
Instead, add a flag to nodeStr() that deals with the Flow node
accordingly. See https://github.com/prettier/prettier/pull/1560#discussion_r115605758
* Print preferred quotes around escaped DirectiveLiteral when it doesn't contain quotes
See https://github.com/prettier/prettier/pull/1560#discussion_r115606122
* Simplify `canChangeDirectiveQuotes` logic
* Add directive test with unnecessarily escaped non-quote character
* Fix boolean logic error
I thought that this would result in the following if-block executing, as
needed to pass the test case in the previous commit. However, it appears
that it's not actually needed to pass the test case, since `makeString`
doesn't unescape unnecessarily escaped non-quote characters.
Nevertheless, I think we should leave that if-block (`if (canChangeDirectiveQuotes)`)
there, in case `makeString` is updated.
See https://github.com/prettier/prettier/pull/1571#discussion_r115658398
* Make isFlowDirectiveLiteral a separate argument to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115810988
* Simplify isFlowDirectiveLiteral logic by passing n.expression to nodeStr()
See https://github.com/prettier/prettier/pull/1571#discussion_r115811216
2017-05-10 22:15:27 +03:00
|
|
|
}
|
|
|
|
|
2017-01-22 23:32:43 +03:00
|
|
|
// It might sound unnecessary to use `makeString` even if `node.raw` already
|
|
|
|
// is enclosed with `enclosingQuote`, but it isn't. `node.raw` could contain
|
|
|
|
// unnecessary escapes (such as in `"\'"`). Always using `makeString` makes
|
|
|
|
// sure that we consistently output the minimum amount of escaped quotes.
|
|
|
|
return makeString(rawContent, enclosingQuote);
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeString(rawContent, enclosingQuote) {
|
|
|
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
|
|
|
|
|
|
|
// Matches _any_ escape and unescaped quotes (both single and double).
|
|
|
|
const regex = /\\([\s\S])|(['"])/g;
|
|
|
|
|
|
|
|
// Escape and unescape single and double quotes as needed to be able to
|
|
|
|
// enclose `rawContent` with `enclosingQuote`.
|
|
|
|
const newContent = rawContent.replace(regex, (match, escaped, quote) => {
|
|
|
|
// If we matched an escape, and the escaped character is a quote of the
|
|
|
|
// other type than we intend to enclose the string with, there's no need for
|
|
|
|
// it to be escaped, so return it _without_ the backslash.
|
|
|
|
if (escaped === otherQuote) {
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we matched an unescaped quote and it is of the _same_ type as we
|
|
|
|
// intend to enclose the string with, it must be escaped, so return it with
|
|
|
|
// a backslash.
|
|
|
|
if (quote === enclosingQuote) {
|
|
|
|
return "\\" + quote;
|
|
|
|
}
|
|
|
|
|
2017-05-11 02:02:49 +03:00
|
|
|
if (quote) {
|
|
|
|
return quote;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unescape any unnecessarily escaped character.
|
|
|
|
// Adapted from https://github.com/eslint/eslint/blob/de0b4ad7bd820ade41b1f606008bea68683dc11a/lib/rules/no-useless-escape.js#L27
|
|
|
|
return /^[^\\nrvtbfux\r\n\u2028\u2029"'0-7]$/.test(escaped)
|
|
|
|
? escaped
|
|
|
|
: "\\" + escaped;
|
2017-01-22 23:32:43 +03:00
|
|
|
});
|
|
|
|
|
2017-02-02 20:54:10 +03:00
|
|
|
return enclosingQuote + newContent + enclosingQuote;
|
2016-12-23 21:38:10 +03:00
|
|
|
}
|
2016-12-31 07:01:07 +03:00
|
|
|
|
2017-04-19 20:24:13 +03:00
|
|
|
function printRegex(node) {
|
2017-05-21 21:00:16 +03:00
|
|
|
const flags = node.flags.split("").sort().join("");
|
2017-04-19 20:24:13 +03:00
|
|
|
return `/${node.pattern}/${flags}`;
|
|
|
|
}
|
|
|
|
|
2017-01-30 20:36:23 +03:00
|
|
|
function printNumber(rawNumber) {
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
rawNumber
|
|
|
|
.toLowerCase()
|
|
|
|
// Remove unnecessary plus and zeroes from scientific notation.
|
|
|
|
.replace(/^([\d.]+e)(?:\+|(-))?0*(\d)/, "$1$2$3")
|
|
|
|
// Remove unnecessary scientific notation (1e0).
|
|
|
|
.replace(/^([\d.]+)e[+-]?0+$/, "$1")
|
|
|
|
// Make sure numbers always start with a digit.
|
|
|
|
.replace(/^\./, "0.")
|
2017-05-27 20:46:45 +03:00
|
|
|
// Remove extraneous trailing decimal zeroes.
|
|
|
|
.replace(/(\.\d+?)0+(?=e|$)/, "$1")
|
2017-04-12 20:16:11 +03:00
|
|
|
// Remove trailing dot.
|
|
|
|
.replace(/\.(?=e|$)/, "")
|
|
|
|
);
|
2017-01-30 20:36:23 +03:00
|
|
|
}
|
|
|
|
|
2016-12-31 07:01:07 +03:00
|
|
|
function isLastStatement(path) {
|
|
|
|
const parent = path.getParentNode();
|
2017-05-03 03:06:25 +03:00
|
|
|
if (!parent) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-12-31 07:01:07 +03:00
|
|
|
const node = path.getValue();
|
2017-06-07 00:58:20 +03:00
|
|
|
const body = (parent.body || parent.consequent)
|
|
|
|
.filter(stmt => stmt.type !== "EmptyStatement");
|
2016-12-31 07:01:07 +03:00
|
|
|
return body && body[body.length - 1] === node;
|
|
|
|
}
|
2017-01-19 23:43:10 +03:00
|
|
|
|
2017-05-24 02:31:10 +03:00
|
|
|
function hasLeadingComment(node) {
|
|
|
|
return node.comments && node.comments.some(comment => comment.leading);
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasTrailingComment(node) {
|
|
|
|
return node.comments && node.comments.some(comment => comment.trailing);
|
|
|
|
}
|
|
|
|
|
2017-02-28 18:00:28 +03:00
|
|
|
function hasLeadingOwnLineComment(text, node) {
|
2017-05-24 23:19:47 +03:00
|
|
|
if (node.type === "JSXElement") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
const res =
|
|
|
|
node.comments &&
|
2017-03-01 20:37:02 +03:00
|
|
|
node.comments.some(
|
2017-04-12 20:16:11 +03:00
|
|
|
comment => comment.leading && util.hasNewline(text, util.locEnd(comment))
|
2017-02-28 18:00:28 +03:00
|
|
|
);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
function hasNakedLeftSide(node) {
|
|
|
|
return (
|
|
|
|
node.type === "AssignmentExpression" ||
|
|
|
|
node.type === "BinaryExpression" ||
|
|
|
|
node.type === "LogicalExpression" ||
|
|
|
|
node.type === "ConditionalExpression" ||
|
|
|
|
node.type === "CallExpression" ||
|
|
|
|
node.type === "MemberExpression" ||
|
|
|
|
node.type === "SequenceExpression" ||
|
2017-04-28 00:00:53 +03:00
|
|
|
node.type === "TaggedTemplateExpression" ||
|
|
|
|
(node.type === "UpdateExpression" && !node.prefix)
|
2017-04-11 23:50:47 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getLeftSide(node) {
|
|
|
|
if (node.expressions) {
|
|
|
|
return node.expressions[0];
|
|
|
|
}
|
2017-05-21 21:00:16 +03:00
|
|
|
return (
|
|
|
|
node.left ||
|
|
|
|
node.test ||
|
|
|
|
node.callee ||
|
|
|
|
node.object ||
|
|
|
|
node.tag ||
|
2017-06-03 02:08:45 +03:00
|
|
|
node.argument ||
|
|
|
|
node.expression
|
2017-05-21 21:00:16 +03:00
|
|
|
);
|
2017-04-11 23:50:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function exprNeedsASIProtection(node) {
|
|
|
|
// HACK: node.needsParens is added in `genericPrint()` for the sole purpose
|
|
|
|
// of being used here. It'd be preferable to find a cleaner way to do this.
|
2017-04-12 20:16:11 +03:00
|
|
|
const maybeASIProblem =
|
|
|
|
node.needsParens ||
|
2017-04-11 23:50:47 +03:00
|
|
|
node.type === "ParenthesizedExpression" ||
|
|
|
|
node.type === "TypeCastExpression" ||
|
|
|
|
(node.type === "ArrowFunctionExpression" &&
|
|
|
|
!canPrintParamsWithoutParens(node)) ||
|
|
|
|
node.type === "ArrayExpression" ||
|
|
|
|
node.type === "ArrayPattern" ||
|
|
|
|
(node.type === "UnaryExpression" &&
|
|
|
|
node.prefix &&
|
|
|
|
(node.operator === "+" || node.operator === "-")) ||
|
|
|
|
node.type === "TemplateLiteral" ||
|
|
|
|
node.type === "TemplateElement" ||
|
|
|
|
node.type === "JSXElement" ||
|
2017-04-28 00:00:04 +03:00
|
|
|
node.type === "BindExpression" ||
|
2017-04-11 23:50:47 +03:00
|
|
|
node.type === "RegExpLiteral" ||
|
|
|
|
(node.type === "Literal" && node.pattern) ||
|
|
|
|
(node.type === "Literal" && node.regex);
|
|
|
|
|
|
|
|
if (maybeASIProblem) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hasNakedLeftSide(node)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return exprNeedsASIProtection(getLeftSide(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
function stmtNeedsASIProtection(path) {
|
2017-05-03 03:06:25 +03:00
|
|
|
if (!path) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-11 23:50:47 +03:00
|
|
|
const node = path.getNode();
|
|
|
|
|
|
|
|
if (node.type !== "ExpressionStatement") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return exprNeedsASIProtection(node.expression);
|
|
|
|
}
|
|
|
|
|
|
|
|
function classPropMayCauseASIProblems(path) {
|
|
|
|
const node = path.getNode();
|
|
|
|
|
|
|
|
if (node.type !== "ClassProperty") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const name = node.key && node.key.name;
|
|
|
|
if (!name) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this isn't actually possible yet with most parsers available today
|
|
|
|
// so isn't properly tested yet.
|
2017-06-07 21:51:25 +03:00
|
|
|
if (
|
|
|
|
(name === "static" || name === "get" || name === "set") &&
|
|
|
|
!node.typeAnnotation
|
|
|
|
) {
|
2017-04-11 23:50:47 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function classChildNeedsASIProtection(node) {
|
2017-05-03 03:06:25 +03:00
|
|
|
if (!node) {
|
|
|
|
return;
|
|
|
|
}
|
2017-04-11 23:50:47 +03:00
|
|
|
|
2017-06-02 20:52:11 +03:00
|
|
|
if (!node.computed) {
|
|
|
|
const name = node.key && node.key.name;
|
2017-06-02 21:48:43 +03:00
|
|
|
if (name === "in" || name === "instanceof") {
|
|
|
|
return true;
|
|
|
|
}
|
2017-06-02 20:52:11 +03:00
|
|
|
}
|
2017-04-11 23:50:47 +03:00
|
|
|
switch (node.type) {
|
|
|
|
case "ClassProperty":
|
2017-05-01 03:41:19 +03:00
|
|
|
case "TSAbstractClassProperty":
|
2017-04-11 23:50:47 +03:00
|
|
|
return node.computed;
|
2017-05-21 22:11:09 +03:00
|
|
|
case "MethodDefinition": // Flow
|
|
|
|
case "TSAbstractMethodDefinition": // TypeScript
|
2017-04-11 23:50:47 +03:00
|
|
|
case "ClassMethod": {
|
2017-05-21 22:11:09 +03:00
|
|
|
// Babylon
|
2017-04-12 20:16:11 +03:00
|
|
|
const isAsync = node.value ? node.value.async : node.async;
|
|
|
|
const isGenerator = node.value ? node.value.generator : node.generator;
|
|
|
|
if (
|
2017-05-21 21:00:16 +03:00
|
|
|
isAsync ||
|
|
|
|
node.static ||
|
|
|
|
node.kind === "get" ||
|
|
|
|
node.kind === "set"
|
2017-04-12 20:16:11 +03:00
|
|
|
) {
|
2017-04-11 23:50:47 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (node.computed || isGenerator) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-05-21 22:11:09 +03:00
|
|
|
return false;
|
2017-04-11 23:50:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 04:06:18 +03:00
|
|
|
// This recurses the return argument, looking for the first token
|
|
|
|
// (the leftmost leaf node) and, if it (or its parents) has any
|
|
|
|
// leadingComments, returns true (so it can be wrapped in parens).
|
|
|
|
function returnArgumentHasLeadingComment(options, argument) {
|
|
|
|
if (hasLeadingOwnLineComment(options.originalText, argument)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-11 23:50:47 +03:00
|
|
|
if (hasNakedLeftSide(argument)) {
|
2017-03-09 04:06:18 +03:00
|
|
|
let leftMost = argument;
|
|
|
|
let newLeftMost;
|
2017-04-12 20:16:11 +03:00
|
|
|
while ((newLeftMost = getLeftSide(leftMost))) {
|
2017-03-09 04:06:18 +03:00
|
|
|
leftMost = newLeftMost;
|
|
|
|
|
|
|
|
if (hasLeadingOwnLineComment(options.originalText, leftMost)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:37:46 +03:00
|
|
|
function isMemberExpressionChain(node) {
|
|
|
|
if (node.type !== "MemberExpression") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (node.object.type === "Identifier") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return isMemberExpressionChain(node.object);
|
|
|
|
}
|
|
|
|
|
2017-02-05 05:23:37 +03:00
|
|
|
// Hack to differentiate between the following two which have the same ast
|
|
|
|
// type T = { method: () => void };
|
|
|
|
// type T = { method(): void };
|
|
|
|
function isObjectTypePropertyAFunction(node) {
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
node.type === "ObjectTypeProperty" &&
|
2017-02-05 05:23:37 +03:00
|
|
|
node.value.type === "FunctionTypeAnnotation" &&
|
|
|
|
!node.static &&
|
2017-05-12 02:29:31 +03:00
|
|
|
!isFunctionNotation(node)
|
2017-04-12 20:16:11 +03:00
|
|
|
);
|
2017-02-05 05:23:37 +03:00
|
|
|
}
|
|
|
|
|
2017-05-12 02:29:31 +03:00
|
|
|
// TODO: This is a bad hack and we need a better way to distinguish between
|
|
|
|
// arrow functions and otherwise
|
|
|
|
function isFunctionNotation(node) {
|
|
|
|
return isGetterOrSetter(node) || sameLocStart(node, node.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isGetterOrSetter(node) {
|
|
|
|
return node.kind === "get" || node.kind === "set";
|
|
|
|
}
|
|
|
|
|
|
|
|
function sameLocStart(nodeA, nodeB) {
|
|
|
|
return util.locStart(nodeA) === util.locStart(nodeB);
|
|
|
|
}
|
|
|
|
|
2017-03-16 20:32:59 +03:00
|
|
|
// Hack to differentiate between the following two which have the same ast
|
|
|
|
// declare function f(a): void;
|
|
|
|
// var f: (a) => void;
|
|
|
|
function isTypeAnnotationAFunction(node) {
|
2017-04-12 20:16:11 +03:00
|
|
|
return (
|
|
|
|
node.type === "TypeAnnotation" &&
|
2017-03-16 20:32:59 +03:00
|
|
|
node.typeAnnotation.type === "FunctionTypeAnnotation" &&
|
|
|
|
!node.static &&
|
2017-05-12 02:29:31 +03:00
|
|
|
!sameLocStart(node, node.typeAnnotation)
|
2017-04-12 20:16:11 +03:00
|
|
|
);
|
2017-03-16 20:32:59 +03:00
|
|
|
}
|
|
|
|
|
2017-04-27 19:37:42 +03:00
|
|
|
function isNodeStartingWithDeclare(node, options) {
|
|
|
|
if (!(options.parser === "flow" || options.parser === "typescript")) {
|
2017-02-13 20:17:20 +03:00
|
|
|
return false;
|
|
|
|
}
|
2017-04-27 19:37:42 +03:00
|
|
|
return (
|
|
|
|
options.originalText.slice(0, util.locStart(node)).match(/declare\s*$/) ||
|
|
|
|
options.originalText
|
|
|
|
.slice(node.range[0], node.range[1])
|
|
|
|
.startsWith("declare ")
|
|
|
|
);
|
2017-02-13 20:17:20 +03:00
|
|
|
}
|
|
|
|
|
2017-05-26 02:26:40 +03:00
|
|
|
function shouldHugType(node) {
|
2017-06-07 21:51:32 +03:00
|
|
|
if (node.type === "ObjectTypeAnnotation" || node.type === "TSTypeLiteral") {
|
2017-05-26 02:26:40 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-26 02:49:25 +03:00
|
|
|
if (node.type === "UnionTypeAnnotation" || node.type === "TSUnionType") {
|
2017-06-07 21:51:32 +03:00
|
|
|
const voidCount = node.types.filter(
|
2017-05-26 02:26:40 +03:00
|
|
|
n =>
|
|
|
|
n.type === "VoidTypeAnnotation" ||
|
2017-05-26 02:49:25 +03:00
|
|
|
n.type === "TSVoidKeyword" ||
|
|
|
|
n.type === "NullLiteralTypeAnnotation" ||
|
|
|
|
(n.type === "Literal" && n.value === null)
|
2017-05-26 02:26:40 +03:00
|
|
|
).length;
|
|
|
|
|
2017-06-07 21:51:32 +03:00
|
|
|
const objectCount = node.types.filter(
|
|
|
|
n =>
|
|
|
|
n.type === "ObjectTypeAnnotation" ||
|
|
|
|
n.type === "TSTypeLiteral" ||
|
|
|
|
// This is a bit aggressive but captures Array<{x}>
|
|
|
|
n.type === "GenericTypeAnnotation" ||
|
|
|
|
n.type === "TSTypeReference"
|
|
|
|
).length;
|
|
|
|
|
|
|
|
if (node.types.length - 1 === voidCount && objectCount > 0) {
|
2017-05-26 02:26:40 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-02 05:12:53 +03:00
|
|
|
function shouldHugArguments(fun) {
|
|
|
|
return (
|
|
|
|
fun &&
|
|
|
|
fun.params &&
|
|
|
|
fun.params.length === 1 &&
|
|
|
|
!fun.params[0].comments &&
|
|
|
|
(fun.params[0].type === "ObjectPattern" ||
|
|
|
|
(fun.params[0].type === "Identifier" &&
|
|
|
|
fun.params[0].typeAnnotation &&
|
|
|
|
fun.params[0].typeAnnotation.type === "TypeAnnotation" &&
|
2017-05-26 02:26:40 +03:00
|
|
|
shouldHugType(fun.params[0].typeAnnotation.typeAnnotation)) ||
|
2017-05-21 21:00:16 +03:00
|
|
|
(fun.params[0].type === "FunctionTypeParam" &&
|
2017-05-26 02:26:40 +03:00
|
|
|
shouldHugType(fun.params[0].typeAnnotation))) &&
|
2017-05-02 05:12:53 +03:00
|
|
|
!fun.rest
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-04 00:35:58 +03:00
|
|
|
function templateLiteralHasNewLines(template) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return template.quasis.some(quasi => quasi.value.raw.includes("\n"));
|
2017-05-04 00:35:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function isTemplateOnItsOwnLine(n, text) {
|
|
|
|
return (
|
2017-05-21 21:00:16 +03:00
|
|
|
((n.type === "TemplateLiteral" && templateLiteralHasNewLines(n)) ||
|
|
|
|
(n.type === "TaggedTemplateExpression" &&
|
|
|
|
templateLiteralHasNewLines(n.quasi))) &&
|
|
|
|
!util.hasNewline(text, util.locStart(n), { backwards: true })
|
2017-05-04 00:35:58 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-03-15 19:35:22 +03:00
|
|
|
function printArrayItems(path, options, printPath, print) {
|
|
|
|
const printedElements = [];
|
|
|
|
let separatorParts = [];
|
|
|
|
|
2017-05-21 22:11:09 +03:00
|
|
|
path.each(childPath => {
|
2017-04-12 20:16:11 +03:00
|
|
|
printedElements.push(concat(separatorParts));
|
|
|
|
printedElements.push(group(print(childPath)));
|
2017-03-15 19:35:22 +03:00
|
|
|
|
2017-04-12 20:16:11 +03:00
|
|
|
separatorParts = [",", line];
|
|
|
|
if (
|
|
|
|
childPath.getValue() &&
|
|
|
|
util.isNextLineEmpty(options.originalText, childPath.getValue())
|
|
|
|
) {
|
|
|
|
separatorParts.push(softline);
|
|
|
|
}
|
|
|
|
}, printPath);
|
2017-03-15 19:35:22 +03:00
|
|
|
|
|
|
|
return concat(printedElements);
|
|
|
|
}
|
|
|
|
|
2017-05-02 00:41:18 +03:00
|
|
|
function hasDanglingComments(node) {
|
2017-05-21 21:00:16 +03:00
|
|
|
return (
|
|
|
|
node.comments &&
|
|
|
|
node.comments.some(comment => !comment.leading && !comment.trailing)
|
|
|
|
);
|
2017-05-02 00:41:18 +03:00
|
|
|
}
|
|
|
|
|
2017-05-26 21:45:24 +03:00
|
|
|
function isLiteral(node) {
|
|
|
|
return (
|
|
|
|
node.type === "BooleanLiteral" ||
|
|
|
|
node.type === "DirectiveLiteral" ||
|
|
|
|
node.type === "Literal" ||
|
|
|
|
node.type === "NullLiteral" ||
|
|
|
|
node.type === "NumericLiteral" ||
|
|
|
|
node.type === "RegExpLiteral" ||
|
|
|
|
node.type === "StringLiteral" ||
|
|
|
|
node.type === "TemplateLiteral" ||
|
|
|
|
node.type === "TSTypeLiteral" ||
|
|
|
|
node.type === "JSXText"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:31:24 +03:00
|
|
|
function isStringLiteral(node) {
|
|
|
|
return (
|
|
|
|
node.type === "StringLiteral" ||
|
|
|
|
(node.type === "Literal" && typeof node.value === "string")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-18 18:40:08 +03:00
|
|
|
function removeLines(doc) {
|
|
|
|
// Force this doc into flat mode by statically converting all
|
|
|
|
// lines into spaces (or soft lines into nothing). Hard lines
|
|
|
|
// should still output because there's too great of a chance
|
|
|
|
// of breaking existing assumptions otherwise.
|
|
|
|
return docUtils.mapDoc(doc, d => {
|
|
|
|
if (d.type === "line" && !d.hard) {
|
|
|
|
return d.soft ? "" : " ";
|
|
|
|
} else if (d.type === "if-break") {
|
|
|
|
return d.flatContents || "";
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-03 01:40:33 +03:00
|
|
|
function isObjectType(n) {
|
|
|
|
return n.type === "ObjectTypeAnnotation" || n.type === "TSTypeLiteral";
|
|
|
|
}
|
|
|
|
|
Add `--range-start` and `--range-end` options to format only parts of the input (#1609)
* Add `--range-start` and `--range-end` options to format only parts of the input
These options default to `0` and `Infinity`, respectively, so that the
entire input is formatted by default. However, if either option is
specified such that a node lies completely outside the resulting range,
the node will be treated as if it has a `// prettier-ignore` comment.
Related to https://github.com/prettier/prettier/pull/1577#issuecomment-300551179
Related to https://github.com/prettier/prettier/issues/1324
Related to https://github.com/prettier/prettier/issues/593
* printer: Extract hasPrettierIgnoreComment() helper
* Move isOutsideRange() to util
* Don't throw errors about comments outside range "not printing"
* Remove unnecessary check from isOutsideRange()
* Make --range-end exclusive
This lets it use the conventional way of specifying ranges in strings.
Note that if the rangeEnd in the tests is changed to 158, it will fail,
but it wouldn't have failed before this change.
* Change range formatting approach
NOTE: This doesn't pass its test yet. Note that since we're reading the
indentation from the first line, it is expected not to change. However,
a semicolon is added, and the lines outside the range are not changed.
The new approach is roughly:
* Require that the range exactly covers an integer number of lines of the input
* Detect the indentation of the line the range starts on
* Format the range's substring using `printAstToDoc`
* Add enough `indent`s to the doc to restore the detected indentation
* Format the doc to a string with `printDocToString`
* Prepend/append the original input before/after the range
See https://github.com/prettier/prettier/pull/1609#issuecomment-301582273
---
Given `tests/range/range.js`, run the following:
prettier tests/range/range.js --range-start 165 --range-end 246
See the range's text with:
dd if=tests/range/range.js ibs=1 skip=165 count=81 2>/dev/null
* Don't use default function parameters
Node v4 doesn't support them. See
http://node.green/#ES2015-syntax-default-function-parameters
* Hackily fix indentation of range formatting
See
https://github.com/prettier/prettier/pull/1609#issuecomment-301625368
Also update the snapshot to reflect that the indentation actually should
decrease by one space, since there were 13 spaces in the input and we
round down after dividing by tabWidth.
* Revert "printer: Extract hasPrettierIgnoreComment() helper"
See https://github.com/prettier/prettier/pull/1609#discussion_r116804853
This reverts commit 62bf068ca98f69d4a7fd0ae188b3554d409eee8d.
* Test automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Fix automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Propagate breaks after adding an indentation-triggering hardline
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116805581
* Extract getAlignmentSize(), use instead of countIndents()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Extract addAlignmentToDoc(), use instead of addIndentsToDoc()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Document that --range-start and --range-end include the entire line
* Fix rangeStart calculation
Before, it was incorrectly resulting in 1 when the originally provided
value was 0
* Extract formatRange() helper function
* Move getAlignmentSize() from printer to util
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636241
* Move addAlignmentToDoc() from printer to doc-builders
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636251
2017-05-21 20:14:13 +03:00
|
|
|
function printAstToDoc(ast, options, addAlignmentSize) {
|
|
|
|
addAlignmentSize = addAlignmentSize || 0;
|
|
|
|
|
2017-04-11 18:50:49 +03:00
|
|
|
function printGenerically(path, args) {
|
2017-05-24 23:19:47 +03:00
|
|
|
const node = path.getValue();
|
2017-05-26 02:26:40 +03:00
|
|
|
const parent = path.getParentNode(0);
|
2017-05-24 23:19:47 +03:00
|
|
|
// We let JSXElement print its comments itself because it adds () around
|
2017-05-26 02:26:40 +03:00
|
|
|
// UnionTypeAnnotation has to align the child without the comments
|
|
|
|
if (
|
|
|
|
(node && node.type === "JSXElement") ||
|
2017-05-26 02:49:25 +03:00
|
|
|
(parent &&
|
|
|
|
(parent.type === "UnionTypeAnnotation" ||
|
|
|
|
parent.type === "TSUnionType"))
|
2017-05-26 02:26:40 +03:00
|
|
|
) {
|
2017-05-24 23:19:47 +03:00
|
|
|
return genericPrint(path, options, printGenerically, args);
|
|
|
|
}
|
|
|
|
|
2017-01-19 23:43:10 +03:00
|
|
|
return comments.printComments(
|
|
|
|
path,
|
2017-04-11 18:50:49 +03:00
|
|
|
p => genericPrint(p, options, printGenerically, args),
|
2017-04-14 04:33:46 +03:00
|
|
|
options,
|
|
|
|
args && args.needsSemi
|
2017-01-19 23:43:10 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-26 20:59:12 +03:00
|
|
|
let doc = printGenerically(new FastPath(ast));
|
Add `--range-start` and `--range-end` options to format only parts of the input (#1609)
* Add `--range-start` and `--range-end` options to format only parts of the input
These options default to `0` and `Infinity`, respectively, so that the
entire input is formatted by default. However, if either option is
specified such that a node lies completely outside the resulting range,
the node will be treated as if it has a `// prettier-ignore` comment.
Related to https://github.com/prettier/prettier/pull/1577#issuecomment-300551179
Related to https://github.com/prettier/prettier/issues/1324
Related to https://github.com/prettier/prettier/issues/593
* printer: Extract hasPrettierIgnoreComment() helper
* Move isOutsideRange() to util
* Don't throw errors about comments outside range "not printing"
* Remove unnecessary check from isOutsideRange()
* Make --range-end exclusive
This lets it use the conventional way of specifying ranges in strings.
Note that if the rangeEnd in the tests is changed to 158, it will fail,
but it wouldn't have failed before this change.
* Change range formatting approach
NOTE: This doesn't pass its test yet. Note that since we're reading the
indentation from the first line, it is expected not to change. However,
a semicolon is added, and the lines outside the range are not changed.
The new approach is roughly:
* Require that the range exactly covers an integer number of lines of the input
* Detect the indentation of the line the range starts on
* Format the range's substring using `printAstToDoc`
* Add enough `indent`s to the doc to restore the detected indentation
* Format the doc to a string with `printDocToString`
* Prepend/append the original input before/after the range
See https://github.com/prettier/prettier/pull/1609#issuecomment-301582273
---
Given `tests/range/range.js`, run the following:
prettier tests/range/range.js --range-start 165 --range-end 246
See the range's text with:
dd if=tests/range/range.js ibs=1 skip=165 count=81 2>/dev/null
* Don't use default function parameters
Node v4 doesn't support them. See
http://node.green/#ES2015-syntax-default-function-parameters
* Hackily fix indentation of range formatting
See
https://github.com/prettier/prettier/pull/1609#issuecomment-301625368
Also update the snapshot to reflect that the indentation actually should
decrease by one space, since there were 13 spaces in the input and we
round down after dividing by tabWidth.
* Revert "printer: Extract hasPrettierIgnoreComment() helper"
See https://github.com/prettier/prettier/pull/1609#discussion_r116804853
This reverts commit 62bf068ca98f69d4a7fd0ae188b3554d409eee8d.
* Test automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Fix automatically using the beginning of the rangeStart line and same for the end
See https://github.com/prettier/prettier/pull/1609#issuecomment-301862076
* Propagate breaks after adding an indentation-triggering hardline
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116805581
* Extract getAlignmentSize(), use instead of countIndents()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Extract addAlignmentToDoc(), use instead of addIndentsToDoc()
See https://github.com/prettier/prettier/pull/1609/files/c1a61ebde8be73414c0a54bde3f323ac24295715#r116804694
* Document that --range-start and --range-end include the entire line
* Fix rangeStart calculation
Before, it was incorrectly resulting in 1 when the originally provided
value was 0
* Extract formatRange() helper function
* Move getAlignmentSize() from printer to util
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636241
* Move addAlignmentToDoc() from printer to doc-builders
This addresses https://github.com/prettier/prettier/pull/1609#discussion_r117636251
2017-05-21 20:14:13 +03:00
|
|
|
if (addAlignmentSize > 0) {
|
|
|
|
// Add a hardline to make the indents take effect
|
|
|
|
// It should be removed in index.js format()
|
|
|
|
doc = addAlignmentToDoc(
|
|
|
|
removeLines(concat([hardline, doc])),
|
|
|
|
addAlignmentSize,
|
|
|
|
options.tabWidth
|
|
|
|
);
|
|
|
|
}
|
2017-01-24 02:47:11 +03:00
|
|
|
docUtils.propagateBreaks(doc);
|
|
|
|
return doc;
|
2017-01-19 23:43:10 +03:00
|
|
|
}
|
|
|
|
|
2017-01-20 21:12:37 +03:00
|
|
|
module.exports = { printAstToDoc };
|