Refactor: Move functions unrelated to printing from printer-estree.js to utils.js (#6562)
parent
6d4d6a7599
commit
32dd447271
|
@ -16,7 +16,6 @@ const {
|
||||||
printString,
|
printString,
|
||||||
printNumber,
|
printNumber,
|
||||||
hasIgnoreComment,
|
hasIgnoreComment,
|
||||||
skipWhitespace,
|
|
||||||
hasNodeIgnoreComment,
|
hasNodeIgnoreComment,
|
||||||
getPenultimate,
|
getPenultimate,
|
||||||
startsWithNoLookaheadToken,
|
startsWithNoLookaheadToken,
|
||||||
|
@ -29,7 +28,6 @@ const {
|
||||||
isNextLineEmptyAfterIndex,
|
isNextLineEmptyAfterIndex,
|
||||||
getNextNonSpaceNonCommentCharacterIndex
|
getNextNonSpaceNonCommentCharacterIndex
|
||||||
} = require("../common/util-shared");
|
} = require("../common/util-shared");
|
||||||
const isIdentifierName = require("esutils").keyword.isIdentifierNameES5;
|
|
||||||
const embed = require("./embed");
|
const embed = require("./embed");
|
||||||
const clean = require("./clean");
|
const clean = require("./clean");
|
||||||
const insertPragma = require("./pragma").insertPragma;
|
const insertPragma = require("./pragma").insertPragma;
|
||||||
|
@ -41,12 +39,57 @@ const {
|
||||||
} = require("./html-binding");
|
} = require("./html-binding");
|
||||||
const preprocess = require("./preprocess");
|
const preprocess = require("./preprocess");
|
||||||
const {
|
const {
|
||||||
getLeftSide,
|
classChildNeedsASIProtection,
|
||||||
|
classPropMayCauseASIProblems,
|
||||||
|
conditionalExpressionChainContainsJSX,
|
||||||
|
getFlowVariance,
|
||||||
getLeftSidePathName,
|
getLeftSidePathName,
|
||||||
hasNakedLeftSide,
|
getTypeScriptMappedTypeModifier,
|
||||||
hasNode,
|
hasDanglingComments,
|
||||||
hasFlowAnnotationComment,
|
hasFlowAnnotationComment,
|
||||||
hasFlowShorthandAnnotationComment
|
hasFlowShorthandAnnotationComment,
|
||||||
|
hasLeadingComment,
|
||||||
|
hasLeadingOwnLineComment,
|
||||||
|
hasNakedLeftSide,
|
||||||
|
hasNewlineBetweenOrAfterDecorators,
|
||||||
|
hasNgSideEffect,
|
||||||
|
hasPrettierIgnore,
|
||||||
|
hasTrailingComment,
|
||||||
|
identity,
|
||||||
|
isBinaryish,
|
||||||
|
isCallOrOptionalCallExpression,
|
||||||
|
isEmptyJSXElement,
|
||||||
|
isFlowAnnotationComment,
|
||||||
|
isFunctionCompositionArgs,
|
||||||
|
isFunctionNotation,
|
||||||
|
isFunctionOrArrowExpression,
|
||||||
|
isGetterOrSetter,
|
||||||
|
isJestEachTemplateLiteral,
|
||||||
|
isJSXNode,
|
||||||
|
isJSXWhitespaceExpression,
|
||||||
|
isLastStatement,
|
||||||
|
isLiteral,
|
||||||
|
isLongCurriedCallExpression,
|
||||||
|
isMeaningfulJSXText,
|
||||||
|
isMemberExpressionChain,
|
||||||
|
isMemberish,
|
||||||
|
isNgForOf,
|
||||||
|
isNodeStartingWithDeclare,
|
||||||
|
isNumericLiteral,
|
||||||
|
isObjectType,
|
||||||
|
isObjectTypePropertyAFunction,
|
||||||
|
isSimpleFlowType,
|
||||||
|
isSimpleTemplateLiteral,
|
||||||
|
isStringLiteral,
|
||||||
|
isStringPropSafeToCoerceToIdentifier,
|
||||||
|
isTemplateOnItsOwnLine,
|
||||||
|
isTestCall,
|
||||||
|
isTheOnlyJSXElementInMarkdown,
|
||||||
|
isTypeAnnotationAFunction,
|
||||||
|
matchJsxWhitespaceRegex,
|
||||||
|
needsHardlineAfterDanglingComment,
|
||||||
|
rawText,
|
||||||
|
returnArgumentHasLeadingComment
|
||||||
} = require("./utils");
|
} = require("./utils");
|
||||||
|
|
||||||
const needsQuoteProps = new WeakMap();
|
const needsQuoteProps = new WeakMap();
|
||||||
|
@ -202,17 +245,6 @@ function genericPrint(path, options, printPath, args) {
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasNewlineBetweenOrAfterDecorators(node, options) {
|
|
||||||
return (
|
|
||||||
hasNewlineInRange(
|
|
||||||
options.originalText,
|
|
||||||
options.locStart(node.decorators[0]),
|
|
||||||
options.locEnd(getLast(node.decorators))
|
|
||||||
) ||
|
|
||||||
hasNewline(options.originalText, options.locEnd(getLast(node.decorators)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printDecorators(path, options, print) {
|
function printDecorators(path, options, print) {
|
||||||
const node = path.getValue();
|
const node = path.getValue();
|
||||||
return group(
|
return group(
|
||||||
|
@ -223,40 +255,6 @@ function printDecorators(path, options, print) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPrettierIgnore(path) {
|
|
||||||
return hasIgnoreComment(path) || hasJsxIgnoreComment(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasJsxIgnoreComment(path) {
|
|
||||||
const node = path.getValue();
|
|
||||||
const parent = path.getParentNode();
|
|
||||||
if (!parent || !node || !isJSXNode(node) || !isJSXNode(parent)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup the previous sibling, ignoring any empty JSXText elements
|
|
||||||
const index = parent.children.indexOf(node);
|
|
||||||
let prevSibling = null;
|
|
||||||
for (let i = index; i > 0; i--) {
|
|
||||||
const candidate = parent.children[i - 1];
|
|
||||||
if (candidate.type === "JSXText" && !isMeaningfulJSXText(candidate)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
prevSibling = candidate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
prevSibling &&
|
|
||||||
prevSibling.type === "JSXExpressionContainer" &&
|
|
||||||
prevSibling.expression.type === "JSXEmptyExpression" &&
|
|
||||||
prevSibling.expression.comments &&
|
|
||||||
prevSibling.expression.comments.find(
|
|
||||||
comment => comment.value.trim() === "prettier-ignore"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The following is the shared logic for
|
* The following is the shared logic for
|
||||||
* ternary operators, namely ConditionalExpression
|
* ternary operators, namely ConditionalExpression
|
||||||
|
@ -419,15 +417,6 @@ function printTernaryOperator(path, options, print, operatorOptions) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeScriptMappedTypeModifier(tokenNode, keyword) {
|
|
||||||
if (tokenNode === "+") {
|
|
||||||
return "+" + keyword;
|
|
||||||
} else if (tokenNode === "-") {
|
|
||||||
return "-" + keyword;
|
|
||||||
}
|
|
||||||
return keyword;
|
|
||||||
}
|
|
||||||
|
|
||||||
function printPathNoParens(path, options, print, args) {
|
function printPathNoParens(path, options, print, args) {
|
||||||
const n = path.getValue();
|
const n = path.getValue();
|
||||||
const semi = options.semi ? ";" : "";
|
const semi = options.semi ? ";" : "";
|
||||||
|
@ -3560,30 +3549,6 @@ function printPathNoParens(path, options, print, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNgForOf(node, index, parentNode) {
|
|
||||||
return (
|
|
||||||
node.type === "NGMicrosyntaxKeyedExpression" &&
|
|
||||||
node.key.name === "of" &&
|
|
||||||
index === 1 &&
|
|
||||||
parentNode.body[0].type === "NGMicrosyntaxLet" &&
|
|
||||||
parentNode.body[0].value === null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** identify if an angular expression seems to have side effects */
|
|
||||||
function hasNgSideEffect(path) {
|
|
||||||
return hasNode(path.getValue(), node => {
|
|
||||||
switch (node.type) {
|
|
||||||
case undefined:
|
|
||||||
return false;
|
|
||||||
case "CallExpression":
|
|
||||||
case "OptionalCallExpression":
|
|
||||||
case "AssignmentExpression":
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function printStatementSequence(path, options, print) {
|
function printStatementSequence(path, options, print) {
|
||||||
const printed = [];
|
const printed = [];
|
||||||
|
|
||||||
|
@ -3825,57 +3790,6 @@ function shouldGroupFirstArg(args) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSimpleFlowType(node) {
|
|
||||||
const flowTypeAnnotations = [
|
|
||||||
"AnyTypeAnnotation",
|
|
||||||
"NullLiteralTypeAnnotation",
|
|
||||||
"GenericTypeAnnotation",
|
|
||||||
"ThisTypeAnnotation",
|
|
||||||
"NumberTypeAnnotation",
|
|
||||||
"VoidTypeAnnotation",
|
|
||||||
"EmptyTypeAnnotation",
|
|
||||||
"MixedTypeAnnotation",
|
|
||||||
"BooleanTypeAnnotation",
|
|
||||||
"BooleanLiteralTypeAnnotation",
|
|
||||||
"StringTypeAnnotation"
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
node &&
|
|
||||||
flowTypeAnnotations.indexOf(node.type) !== -1 &&
|
|
||||||
!(node.type === "GenericTypeAnnotation" && node.typeParameters)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isJestEachTemplateLiteral(node, parentNode) {
|
|
||||||
/**
|
|
||||||
* describe.each`table`(name, fn)
|
|
||||||
* describe.only.each`table`(name, fn)
|
|
||||||
* describe.skip.each`table`(name, fn)
|
|
||||||
* test.each`table`(name, fn)
|
|
||||||
* test.only.each`table`(name, fn)
|
|
||||||
* test.skip.each`table`(name, fn)
|
|
||||||
*
|
|
||||||
* Ref: https://github.com/facebook/jest/pull/6102
|
|
||||||
*/
|
|
||||||
const jestEachTriggerRegex = /^[xf]?(describe|it|test)$/;
|
|
||||||
return (
|
|
||||||
parentNode.type === "TaggedTemplateExpression" &&
|
|
||||||
parentNode.quasi === node &&
|
|
||||||
parentNode.tag.type === "MemberExpression" &&
|
|
||||||
parentNode.tag.property.type === "Identifier" &&
|
|
||||||
parentNode.tag.property.name === "each" &&
|
|
||||||
((parentNode.tag.object.type === "Identifier" &&
|
|
||||||
jestEachTriggerRegex.test(parentNode.tag.object.name)) ||
|
|
||||||
(parentNode.tag.object.type === "MemberExpression" &&
|
|
||||||
parentNode.tag.object.property.type === "Identifier" &&
|
|
||||||
(parentNode.tag.object.property.name === "only" ||
|
|
||||||
parentNode.tag.object.property.name === "skip") &&
|
|
||||||
parentNode.tag.object.object.type === "Identifier" &&
|
|
||||||
jestEachTriggerRegex.test(parentNode.tag.object.object.name)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printJestEachTemplateLiteral(node, expressions, options) {
|
function printJestEachTemplateLiteral(node, expressions, options) {
|
||||||
/**
|
/**
|
||||||
* a | b | expected
|
* a | b | expected
|
||||||
|
@ -3966,97 +3880,6 @@ function printJestEachTemplateLiteral(node, expressions, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param node {import("estree").TemplateLiteral} */
|
|
||||||
function isSimpleTemplateLiteral(node) {
|
|
||||||
if (node.expressions.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.expressions.every(expr => {
|
|
||||||
// Disallow comments since printDocToString can't print them here
|
|
||||||
if (expr.comments) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow `x` and `this`
|
|
||||||
if (expr.type === "Identifier" || expr.type === "ThisExpression") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow `a.b.c`, `a.b[c]`, and `this.x.y`
|
|
||||||
if (
|
|
||||||
(expr.type === "MemberExpression" ||
|
|
||||||
expr.type === "OptionalMemberExpression") &&
|
|
||||||
(expr.property.type === "Identifier" || expr.property.type === "Literal")
|
|
||||||
) {
|
|
||||||
let ancestor = expr;
|
|
||||||
while (
|
|
||||||
ancestor.type === "MemberExpression" ||
|
|
||||||
ancestor.type === "OptionalMemberExpression"
|
|
||||||
) {
|
|
||||||
ancestor = ancestor.object;
|
|
||||||
if (ancestor.comments) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
ancestor.type === "Identifier" ||
|
|
||||||
ancestor.type === "ThisExpression"
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logic to check for args with multiple anonymous functions. For instance,
|
|
||||||
// the following call should be split on multiple lines for readability:
|
|
||||||
// source.pipe(map((x) => x + x), filter((x) => x % 2 === 0))
|
|
||||||
function isFunctionCompositionArgs(args) {
|
|
||||||
if (args.length <= 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let count = 0;
|
|
||||||
for (const arg of args) {
|
|
||||||
if (isFunctionOrArrowExpression(arg)) {
|
|
||||||
count += 1;
|
|
||||||
if (count > 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (isCallOrOptionalCallExpression(arg)) {
|
|
||||||
for (const childArg of arg.arguments) {
|
|
||||||
if (isFunctionOrArrowExpression(childArg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logic to determine if a call is a “long curried function call”.
|
|
||||||
// See https://github.com/prettier/prettier/issues/1420.
|
|
||||||
//
|
|
||||||
// `connect(a, b, c)(d)`
|
|
||||||
// In the above call expression, the second call is the parent node and the
|
|
||||||
// first call is the current node.
|
|
||||||
function isLongCurriedCallExpression(path) {
|
|
||||||
const node = path.getValue();
|
|
||||||
const parent = path.getParentNode();
|
|
||||||
return (
|
|
||||||
isCallOrOptionalCallExpression(node) &&
|
|
||||||
isCallOrOptionalCallExpression(parent) &&
|
|
||||||
parent.callee === node &&
|
|
||||||
node.arguments.length > parent.arguments.length &&
|
|
||||||
parent.arguments.length > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printArgumentsList(path, options, print) {
|
function printArgumentsList(path, options, print) {
|
||||||
const node = path.getValue();
|
const node = path.getValue();
|
||||||
const args = node.arguments;
|
const args = node.arguments;
|
||||||
|
@ -4683,27 +4506,6 @@ function printFlowDeclaration(path, parts) {
|
||||||
return concat(parts);
|
return concat(parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFlowVariance(path) {
|
|
||||||
if (!path.variance) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Babel 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:
|
|
||||||
/* istanbul ignore next */
|
|
||||||
return variance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function printTypeScriptModifiers(path, options, print) {
|
function printTypeScriptModifiers(path, options, print) {
|
||||||
const n = path.getValue();
|
const n = path.getValue();
|
||||||
if (!n.modifiers || !n.modifiers.length) {
|
if (!n.modifiers || !n.modifiers.length) {
|
||||||
|
@ -5275,153 +5077,6 @@ function printMemberChain(path, options, print) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCallOrOptionalCallExpression(node) {
|
|
||||||
return (
|
|
||||||
node.type === "CallExpression" || node.type === "OptionalCallExpression"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isJSXNode(node) {
|
|
||||||
return node.type === "JSXElement" || node.type === "JSXFragment";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmptyJSXElement(node) {
|
|
||||||
if (node.children.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node.children.length > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is one text child and does not contain any meaningful text
|
|
||||||
// we can treat the element as empty.
|
|
||||||
const child = node.children[0];
|
|
||||||
return isLiteral(child) && !isMeaningfulJSXText(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only space, newline, carriage return, and tab are treated as whitespace
|
|
||||||
// inside JSX.
|
|
||||||
const jsxWhitespaceChars = " \n\r\t";
|
|
||||||
const containsNonJsxWhitespaceRegex = new RegExp(
|
|
||||||
"[^" + jsxWhitespaceChars + "]"
|
|
||||||
);
|
|
||||||
const matchJsxWhitespaceRegex = new RegExp("([" + jsxWhitespaceChars + "]+)");
|
|
||||||
|
|
||||||
// Meaningful if it contains non-whitespace characters,
|
|
||||||
// or it contains whitespace without a new line.
|
|
||||||
function isMeaningfulJSXText(node) {
|
|
||||||
return (
|
|
||||||
isLiteral(node) &&
|
|
||||||
(containsNonJsxWhitespaceRegex.test(rawText(node)) ||
|
|
||||||
!/\n/.test(rawText(node)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function conditionalExpressionChainContainsJSX(node) {
|
|
||||||
return Boolean(getConditionalChainContents(node).find(isJSXNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have nested conditional expressions, we want to print them in JSX mode
|
|
||||||
// if there's at least one JSXElement somewhere in the tree.
|
|
||||||
//
|
|
||||||
// A conditional expression chain like this should be printed in normal mode,
|
|
||||||
// because there aren't JSXElements anywhere in it:
|
|
||||||
//
|
|
||||||
// isA ? "A" : isB ? "B" : isC ? "C" : "Unknown";
|
|
||||||
//
|
|
||||||
// But a conditional expression chain like this should be printed in JSX mode,
|
|
||||||
// because there is a JSXElement in the last ConditionalExpression:
|
|
||||||
//
|
|
||||||
// isA ? "A" : isB ? "B" : isC ? "C" : <span className="warning">Unknown</span>;
|
|
||||||
//
|
|
||||||
// This type of ConditionalExpression chain is structured like this in the AST:
|
|
||||||
//
|
|
||||||
// ConditionalExpression {
|
|
||||||
// test: ...,
|
|
||||||
// consequent: ...,
|
|
||||||
// alternate: ConditionalExpression {
|
|
||||||
// test: ...,
|
|
||||||
// consequent: ...,
|
|
||||||
// alternate: ConditionalExpression {
|
|
||||||
// test: ...,
|
|
||||||
// consequent: ...,
|
|
||||||
// alternate: ...,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// We want to traverse over that shape and convert it into a flat structure so
|
|
||||||
// that we can find if there's a JSXElement somewhere inside.
|
|
||||||
function getConditionalChainContents(node) {
|
|
||||||
// Given this code:
|
|
||||||
//
|
|
||||||
// // Using a ConditionalExpression as the consequent is uncommon, but should
|
|
||||||
// // be handled.
|
|
||||||
// A ? B : C ? D : E ? F ? G : H : I
|
|
||||||
//
|
|
||||||
// which has this AST:
|
|
||||||
//
|
|
||||||
// ConditionalExpression {
|
|
||||||
// test: Identifier(A),
|
|
||||||
// consequent: Identifier(B),
|
|
||||||
// alternate: ConditionalExpression {
|
|
||||||
// test: Identifier(C),
|
|
||||||
// consequent: Identifier(D),
|
|
||||||
// alternate: ConditionalExpression {
|
|
||||||
// test: Identifier(E),
|
|
||||||
// consequent: ConditionalExpression {
|
|
||||||
// test: Identifier(F),
|
|
||||||
// consequent: Identifier(G),
|
|
||||||
// alternate: Identifier(H),
|
|
||||||
// },
|
|
||||||
// alternate: Identifier(I),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// we should return this Array:
|
|
||||||
//
|
|
||||||
// [
|
|
||||||
// Identifier(A),
|
|
||||||
// Identifier(B),
|
|
||||||
// Identifier(C),
|
|
||||||
// Identifier(D),
|
|
||||||
// Identifier(E),
|
|
||||||
// Identifier(F),
|
|
||||||
// Identifier(G),
|
|
||||||
// Identifier(H),
|
|
||||||
// Identifier(I)
|
|
||||||
// ];
|
|
||||||
//
|
|
||||||
// This loses the information about whether each node was the test,
|
|
||||||
// consequent, or alternate, but we don't care about that here- we are only
|
|
||||||
// flattening this structure to find if there's any JSXElements inside.
|
|
||||||
const nonConditionalExpressions = [];
|
|
||||||
|
|
||||||
function recurse(node) {
|
|
||||||
if (node.type === "ConditionalExpression") {
|
|
||||||
recurse(node.test);
|
|
||||||
recurse(node.consequent);
|
|
||||||
recurse(node.alternate);
|
|
||||||
} else {
|
|
||||||
nonConditionalExpressions.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recurse(node);
|
|
||||||
|
|
||||||
return nonConditionalExpressions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect an expression node representing `{" "}`
|
|
||||||
function isJSXWhitespaceExpression(node) {
|
|
||||||
return (
|
|
||||||
node.type === "JSXExpressionContainer" &&
|
|
||||||
isLiteral(node.expression) &&
|
|
||||||
node.expression.value === " " &&
|
|
||||||
!node.expression.comments
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function separatorNoWhitespace(
|
function separatorNoWhitespace(
|
||||||
isFacebookTranslationTag,
|
isFacebookTranslationTag,
|
||||||
child,
|
child,
|
||||||
|
@ -5852,22 +5507,6 @@ function maybeWrapJSXElementInParens(path, elem) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBinaryish(node) {
|
|
||||||
return (
|
|
||||||
node.type === "BinaryExpression" ||
|
|
||||||
node.type === "LogicalExpression" ||
|
|
||||||
node.type === "NGPipeExpression"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMemberish(node) {
|
|
||||||
return (
|
|
||||||
node.type === "MemberExpression" ||
|
|
||||||
node.type === "OptionalMemberExpression" ||
|
|
||||||
(node.type === "BindExpression" && node.object)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldInlineLogicalExpression(node) {
|
function shouldInlineLogicalExpression(node) {
|
||||||
if (node.type !== "LogicalExpression") {
|
if (node.type !== "LogicalExpression") {
|
||||||
return false;
|
return false;
|
||||||
|
@ -6080,45 +5719,6 @@ function printRegex(node) {
|
||||||
return `/${node.pattern}/${flags}`;
|
return `/${node.pattern}/${flags}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLastStatement(path) {
|
|
||||||
const parent = path.getParentNode();
|
|
||||||
if (!parent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const node = path.getValue();
|
|
||||||
const body = (parent.body || parent.consequent).filter(
|
|
||||||
stmt => stmt.type !== "EmptyStatement"
|
|
||||||
);
|
|
||||||
return body && body[body.length - 1] === node;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasLeadingComment(node) {
|
|
||||||
return node.comments && node.comments.some(comment => comment.leading);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasTrailingComment(node) {
|
|
||||||
return node.comments && node.comments.some(comment => comment.trailing);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasLeadingOwnLineComment(text, node, options) {
|
|
||||||
if (isJSXNode(node)) {
|
|
||||||
return hasNodeIgnoreComment(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res =
|
|
||||||
node.comments &&
|
|
||||||
node.comments.some(
|
|
||||||
comment => comment.leading && hasNewline(text, options.locEnd(comment))
|
|
||||||
);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFlowAnnotationComment(text, typeAnnotation, options) {
|
|
||||||
const start = options.locStart(typeAnnotation);
|
|
||||||
const end = skipWhitespace(text, options.locEnd(typeAnnotation));
|
|
||||||
return text.substr(start, 2) === "/*" && text.substr(end, 2) === "*/";
|
|
||||||
}
|
|
||||||
|
|
||||||
function exprNeedsASIProtection(path, options) {
|
function exprNeedsASIProtection(path, options) {
|
||||||
const node = path.getValue();
|
const node = path.getValue();
|
||||||
|
|
||||||
|
@ -6170,159 +5770,6 @@ function stmtNeedsASIProtection(path, options) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function classPropMayCauseASIProblems(path) {
|
|
||||||
const node = path.getNode();
|
|
||||||
|
|
||||||
if (node.type !== "ClassProperty") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = node.key && node.key.name;
|
|
||||||
|
|
||||||
// this isn't actually possible yet with most parsers available today
|
|
||||||
// so isn't properly tested yet.
|
|
||||||
if (
|
|
||||||
(name === "static" || name === "get" || name === "set") &&
|
|
||||||
!node.value &&
|
|
||||||
!node.typeAnnotation
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function classChildNeedsASIProtection(node) {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
node.static ||
|
|
||||||
node.accessibility // TypeScript
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!node.computed) {
|
|
||||||
const name = node.key && node.key.name;
|
|
||||||
if (name === "in" || name === "instanceof") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (node.type) {
|
|
||||||
case "ClassProperty":
|
|
||||||
case "TSAbstractClassProperty":
|
|
||||||
return node.computed;
|
|
||||||
case "MethodDefinition": // Flow
|
|
||||||
case "TSAbstractMethodDefinition": // TypeScript
|
|
||||||
case "ClassMethod":
|
|
||||||
case "ClassPrivateMethod": {
|
|
||||||
// Babel
|
|
||||||
const isAsync = node.value ? node.value.async : node.async;
|
|
||||||
const isGenerator = node.value ? node.value.generator : node.generator;
|
|
||||||
if (isAsync || node.kind === "get" || node.kind === "set") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (node.computed || isGenerator) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* istanbul ignore next */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, options)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNakedLeftSide(argument)) {
|
|
||||||
let leftMost = argument;
|
|
||||||
let newLeftMost;
|
|
||||||
while ((newLeftMost = getLeftSide(leftMost))) {
|
|
||||||
leftMost = newLeftMost;
|
|
||||||
|
|
||||||
if (hasLeadingOwnLineComment(options.originalText, leftMost, options)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMemberExpressionChain(node) {
|
|
||||||
if (
|
|
||||||
node.type !== "MemberExpression" &&
|
|
||||||
node.type !== "OptionalMemberExpression"
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (node.object.type === "Identifier") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return isMemberExpressionChain(node.object);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack to differentiate between the following two which have the same ast
|
|
||||||
// type T = { method: () => void };
|
|
||||||
// type T = { method(): void };
|
|
||||||
function isObjectTypePropertyAFunction(node, options) {
|
|
||||||
return (
|
|
||||||
(node.type === "ObjectTypeProperty" ||
|
|
||||||
node.type === "ObjectTypeInternalSlot") &&
|
|
||||||
node.value.type === "FunctionTypeAnnotation" &&
|
|
||||||
!node.static &&
|
|
||||||
!isFunctionNotation(node, options)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This is a bad hack and we need a better way to distinguish between
|
|
||||||
// arrow functions and otherwise
|
|
||||||
function isFunctionNotation(node, options) {
|
|
||||||
return isGetterOrSetter(node) || sameLocStart(node, node.value, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isGetterOrSetter(node) {
|
|
||||||
return node.kind === "get" || node.kind === "set";
|
|
||||||
}
|
|
||||||
|
|
||||||
function sameLocStart(nodeA, nodeB, options) {
|
|
||||||
return options.locStart(nodeA) === options.locStart(nodeB);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack to differentiate between the following two which have the same ast
|
|
||||||
// declare function f(a): void;
|
|
||||||
// var f: (a) => void;
|
|
||||||
function isTypeAnnotationAFunction(node, options) {
|
|
||||||
return (
|
|
||||||
(node.type === "TypeAnnotation" || node.type === "TSTypeAnnotation") &&
|
|
||||||
node.typeAnnotation.type === "FunctionTypeAnnotation" &&
|
|
||||||
!node.static &&
|
|
||||||
!sameLocStart(node, node.typeAnnotation, options)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNodeStartingWithDeclare(node, options) {
|
|
||||||
if (!(options.parser === "flow" || options.parser === "typescript")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
options.originalText
|
|
||||||
.slice(0, options.locStart(node))
|
|
||||||
.match(/declare[ \t]*$/) ||
|
|
||||||
options.originalText
|
|
||||||
.slice(node.range[0], node.range[1])
|
|
||||||
.startsWith("declare ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldHugType(node) {
|
function shouldHugType(node) {
|
||||||
if (isSimpleFlowType(node) || isObjectType(node)) {
|
if (isSimpleFlowType(node) || isObjectType(node)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -6381,19 +5828,6 @@ function shouldHugArguments(fun) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateLiteralHasNewLines(template) {
|
|
||||||
return template.quasis.some(quasi => quasi.value.raw.includes("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTemplateOnItsOwnLine(n, text, options) {
|
|
||||||
return (
|
|
||||||
((n.type === "TemplateLiteral" && templateLiteralHasNewLines(n)) ||
|
|
||||||
(n.type === "TaggedTemplateExpression" &&
|
|
||||||
templateLiteralHasNewLines(n.quasi))) &&
|
|
||||||
!hasNewline(text, options.locStart(n), { backwards: true })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printArrayItems(path, options, printPath, print) {
|
function printArrayItems(path, options, printPath, print) {
|
||||||
const printedElements = [];
|
const printedElements = [];
|
||||||
let separatorParts = [];
|
let separatorParts = [];
|
||||||
|
@ -6414,173 +5848,6 @@ function printArrayItems(path, options, printPath, print) {
|
||||||
return concat(printedElements);
|
return concat(printedElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasDanglingComments(node) {
|
|
||||||
return (
|
|
||||||
node.comments &&
|
|
||||||
node.comments.some(comment => !comment.leading && !comment.trailing)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function needsHardlineAfterDanglingComment(node) {
|
|
||||||
if (!node.comments) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const lastDanglingComment = getLast(
|
|
||||||
node.comments.filter(comment => !comment.leading && !comment.trailing)
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
lastDanglingComment && !handleComments.isBlockComment(lastDanglingComment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStringPropSafeToCoerceToIdentifier(node, options) {
|
|
||||||
return (
|
|
||||||
isStringLiteral(node.key) &&
|
|
||||||
isIdentifierName(node.key.value) &&
|
|
||||||
options.parser !== "json" &&
|
|
||||||
!(options.parser === "typescript" && node.type === "ClassProperty")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNumericLiteral(node) {
|
|
||||||
return (
|
|
||||||
node.type === "NumericLiteral" ||
|
|
||||||
(node.type === "Literal" && typeof node.value === "number")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStringLiteral(node) {
|
|
||||||
return (
|
|
||||||
node.type === "StringLiteral" ||
|
|
||||||
(node.type === "Literal" && typeof node.value === "string")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isObjectType(n) {
|
|
||||||
return n.type === "ObjectTypeAnnotation" || n.type === "TSTypeLiteral";
|
|
||||||
}
|
|
||||||
|
|
||||||
const unitTestRe = /^(skip|[fx]?(it|describe|test))$/;
|
|
||||||
|
|
||||||
// eg; `describe("some string", (done) => {})`
|
|
||||||
function isTestCall(n, parent) {
|
|
||||||
if (n.type !== "CallExpression") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (n.arguments.length === 1) {
|
|
||||||
if (isAngularTestWrapper(n) && parent && isTestCall(parent)) {
|
|
||||||
return isFunctionOrArrowExpression(n.arguments[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUnitTestSetUp(n)) {
|
|
||||||
return isAngularTestWrapper(n.arguments[0]);
|
|
||||||
}
|
|
||||||
} else if (n.arguments.length === 2 || n.arguments.length === 3) {
|
|
||||||
if (
|
|
||||||
((n.callee.type === "Identifier" && unitTestRe.test(n.callee.name)) ||
|
|
||||||
isSkipOrOnlyBlock(n)) &&
|
|
||||||
(isTemplateLiteral(n.arguments[0]) || isStringLiteral(n.arguments[0]))
|
|
||||||
) {
|
|
||||||
// it("name", () => { ... }, 2500)
|
|
||||||
if (n.arguments[2] && !isNumericLiteral(n.arguments[2])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
(n.arguments.length === 2
|
|
||||||
? isFunctionOrArrowExpression(n.arguments[1])
|
|
||||||
: isFunctionOrArrowExpressionWithBody(n.arguments[1]) &&
|
|
||||||
n.arguments[1].params.length <= 1) ||
|
|
||||||
isAngularTestWrapper(n.arguments[1])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSkipOrOnlyBlock(node) {
|
|
||||||
return (
|
|
||||||
(node.callee.type === "MemberExpression" ||
|
|
||||||
node.callee.type === "OptionalMemberExpression") &&
|
|
||||||
node.callee.object.type === "Identifier" &&
|
|
||||||
node.callee.property.type === "Identifier" &&
|
|
||||||
unitTestRe.test(node.callee.object.name) &&
|
|
||||||
(node.callee.property.name === "only" ||
|
|
||||||
node.callee.property.name === "skip")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTemplateLiteral(node) {
|
|
||||||
return node.type === "TemplateLiteral";
|
|
||||||
}
|
|
||||||
|
|
||||||
// `inject` is used in AngularJS 1.x, `async` in Angular 2+
|
|
||||||
// example: https://docs.angularjs.org/guide/unit-testing#using-beforeall-
|
|
||||||
function isAngularTestWrapper(node) {
|
|
||||||
return (
|
|
||||||
(node.type === "CallExpression" ||
|
|
||||||
node.type === "OptionalCallExpression") &&
|
|
||||||
node.callee.type === "Identifier" &&
|
|
||||||
(node.callee.name === "async" ||
|
|
||||||
node.callee.name === "inject" ||
|
|
||||||
node.callee.name === "fakeAsync")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFunctionOrArrowExpression(node) {
|
|
||||||
return (
|
|
||||||
node.type === "FunctionExpression" ||
|
|
||||||
node.type === "ArrowFunctionExpression"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFunctionOrArrowExpressionWithBody(node) {
|
|
||||||
return (
|
|
||||||
node.type === "FunctionExpression" ||
|
|
||||||
(node.type === "ArrowFunctionExpression" &&
|
|
||||||
node.body.type === "BlockStatement")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUnitTestSetUp(n) {
|
|
||||||
const unitTestSetUpRe = /^(before|after)(Each|All)$/;
|
|
||||||
return (
|
|
||||||
n.callee.type === "Identifier" &&
|
|
||||||
unitTestSetUpRe.test(n.callee.name) &&
|
|
||||||
n.arguments.length === 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTheOnlyJSXElementInMarkdown(options, path) {
|
|
||||||
if (options.parentParser !== "markdown" && options.parentParser !== "mdx") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const node = path.getNode();
|
|
||||||
|
|
||||||
if (!node.expression || !isJSXNode(node.expression)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = path.getParentNode();
|
|
||||||
|
|
||||||
return parent.type === "Program" && parent.body.length == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function willPrintOwnComments(path /*, options */) {
|
function willPrintOwnComments(path /*, options */) {
|
||||||
const node = path.getValue();
|
const node = path.getValue();
|
||||||
const parent = path.getParentNode();
|
const parent = path.getParentNode();
|
||||||
|
@ -6685,14 +5952,6 @@ function printIndentableBlockComment(comment) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rawText(node) {
|
|
||||||
return node.extra ? node.extra.raw : node.raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
function identity(x) {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
preprocess,
|
preprocess,
|
||||||
print: genericPrint,
|
print: genericPrint,
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const {
|
||||||
|
getLast,
|
||||||
|
hasNewline,
|
||||||
|
hasNewlineInRange,
|
||||||
|
hasIgnoreComment,
|
||||||
|
hasNodeIgnoreComment,
|
||||||
|
skipWhitespace
|
||||||
|
} = require("../common/util");
|
||||||
|
const isIdentifierName = require("esutils").keyword.isIdentifierNameES5;
|
||||||
|
const handleComments = require("./comments");
|
||||||
|
|
||||||
// We match any whitespace except line terminators because
|
// We match any whitespace except line terminators because
|
||||||
// Flow annotation comments cannot be split across lines. For example:
|
// Flow annotation comments cannot be split across lines. For example:
|
||||||
//
|
//
|
||||||
|
@ -107,11 +118,840 @@ function getLeftSidePathName(path, node) {
|
||||||
throw new Error("Unexpected node has no left side", node);
|
throw new Error("Unexpected node has no left side", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNumericLiteral(node) {
|
||||||
|
return (
|
||||||
|
node.type === "NumericLiteral" ||
|
||||||
|
(node.type === "Literal" && typeof node.value === "number")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStringLiteral(node) {
|
||||||
|
return (
|
||||||
|
node.type === "StringLiteral" ||
|
||||||
|
(node.type === "Literal" && typeof node.value === "string")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isObjectType(n) {
|
||||||
|
return n.type === "ObjectTypeAnnotation" || n.type === "TSTypeLiteral";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFunctionOrArrowExpression(node) {
|
||||||
|
return (
|
||||||
|
node.type === "FunctionExpression" ||
|
||||||
|
node.type === "ArrowFunctionExpression"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFunctionOrArrowExpressionWithBody(node) {
|
||||||
|
return (
|
||||||
|
node.type === "FunctionExpression" ||
|
||||||
|
(node.type === "ArrowFunctionExpression" &&
|
||||||
|
node.body.type === "BlockStatement")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTemplateLiteral(node) {
|
||||||
|
return node.type === "TemplateLiteral";
|
||||||
|
}
|
||||||
|
|
||||||
|
// `inject` is used in AngularJS 1.x, `async` in Angular 2+
|
||||||
|
// example: https://docs.angularjs.org/guide/unit-testing#using-beforeall-
|
||||||
|
function isAngularTestWrapper(node) {
|
||||||
|
return (
|
||||||
|
(node.type === "CallExpression" ||
|
||||||
|
node.type === "OptionalCallExpression") &&
|
||||||
|
node.callee.type === "Identifier" &&
|
||||||
|
(node.callee.name === "async" ||
|
||||||
|
node.callee.name === "inject" ||
|
||||||
|
node.callee.name === "fakeAsync")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJSXNode(node) {
|
||||||
|
return node.type === "JSXElement" || node.type === "JSXFragment";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTheOnlyJSXElementInMarkdown(options, path) {
|
||||||
|
if (options.parentParser !== "markdown" && options.parentParser !== "mdx") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = path.getNode();
|
||||||
|
|
||||||
|
if (!node.expression || !isJSXNode(node.expression)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = path.getParentNode();
|
||||||
|
|
||||||
|
return parent.type === "Program" && parent.body.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect an expression node representing `{" "}`
|
||||||
|
function isJSXWhitespaceExpression(node) {
|
||||||
|
return (
|
||||||
|
node.type === "JSXExpressionContainer" &&
|
||||||
|
isLiteral(node.expression) &&
|
||||||
|
node.expression.value === " " &&
|
||||||
|
!node.expression.comments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMemberExpressionChain(node) {
|
||||||
|
if (
|
||||||
|
node.type !== "MemberExpression" &&
|
||||||
|
node.type !== "OptionalMemberExpression"
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (node.object.type === "Identifier") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isMemberExpressionChain(node.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGetterOrSetter(node) {
|
||||||
|
return node.kind === "get" || node.kind === "set";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameLocStart(nodeA, nodeB, options) {
|
||||||
|
return options.locStart(nodeA) === options.locStart(nodeB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a bad hack and we need a better way to distinguish between
|
||||||
|
// arrow functions and otherwise
|
||||||
|
function isFunctionNotation(node, options) {
|
||||||
|
return isGetterOrSetter(node) || sameLocStart(node, node.value, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack to differentiate between the following two which have the same ast
|
||||||
|
// type T = { method: () => void };
|
||||||
|
// type T = { method(): void };
|
||||||
|
function isObjectTypePropertyAFunction(node, options) {
|
||||||
|
return (
|
||||||
|
(node.type === "ObjectTypeProperty" ||
|
||||||
|
node.type === "ObjectTypeInternalSlot") &&
|
||||||
|
node.value.type === "FunctionTypeAnnotation" &&
|
||||||
|
!node.static &&
|
||||||
|
!isFunctionNotation(node, options)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack to differentiate between the following two which have the same ast
|
||||||
|
// declare function f(a): void;
|
||||||
|
// var f: (a) => void;
|
||||||
|
function isTypeAnnotationAFunction(node, options) {
|
||||||
|
return (
|
||||||
|
(node.type === "TypeAnnotation" || node.type === "TSTypeAnnotation") &&
|
||||||
|
node.typeAnnotation.type === "FunctionTypeAnnotation" &&
|
||||||
|
!node.static &&
|
||||||
|
!sameLocStart(node, node.typeAnnotation, options)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBinaryish(node) {
|
||||||
|
return (
|
||||||
|
node.type === "BinaryExpression" ||
|
||||||
|
node.type === "LogicalExpression" ||
|
||||||
|
node.type === "NGPipeExpression"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMemberish(node) {
|
||||||
|
return (
|
||||||
|
node.type === "MemberExpression" ||
|
||||||
|
node.type === "OptionalMemberExpression" ||
|
||||||
|
(node.type === "BindExpression" && node.object)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNodeStartingWithDeclare(node, options) {
|
||||||
|
if (!(options.parser === "flow" || options.parser === "typescript")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
options.originalText
|
||||||
|
.slice(0, options.locStart(node))
|
||||||
|
.match(/declare[ \t]*$/) ||
|
||||||
|
options.originalText
|
||||||
|
.slice(node.range[0], node.range[1])
|
||||||
|
.startsWith("declare ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSimpleFlowType(node) {
|
||||||
|
const flowTypeAnnotations = [
|
||||||
|
"AnyTypeAnnotation",
|
||||||
|
"NullLiteralTypeAnnotation",
|
||||||
|
"GenericTypeAnnotation",
|
||||||
|
"ThisTypeAnnotation",
|
||||||
|
"NumberTypeAnnotation",
|
||||||
|
"VoidTypeAnnotation",
|
||||||
|
"EmptyTypeAnnotation",
|
||||||
|
"MixedTypeAnnotation",
|
||||||
|
"BooleanTypeAnnotation",
|
||||||
|
"BooleanLiteralTypeAnnotation",
|
||||||
|
"StringTypeAnnotation"
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
node &&
|
||||||
|
flowTypeAnnotations.indexOf(node.type) !== -1 &&
|
||||||
|
!(node.type === "GenericTypeAnnotation" && node.typeParameters)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unitTestRe = /^(skip|[fx]?(it|describe|test))$/;
|
||||||
|
|
||||||
|
function isSkipOrOnlyBlock(node) {
|
||||||
|
return (
|
||||||
|
(node.callee.type === "MemberExpression" ||
|
||||||
|
node.callee.type === "OptionalMemberExpression") &&
|
||||||
|
node.callee.object.type === "Identifier" &&
|
||||||
|
node.callee.property.type === "Identifier" &&
|
||||||
|
unitTestRe.test(node.callee.object.name) &&
|
||||||
|
(node.callee.property.name === "only" ||
|
||||||
|
node.callee.property.name === "skip")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUnitTestSetUp(n) {
|
||||||
|
const unitTestSetUpRe = /^(before|after)(Each|All)$/;
|
||||||
|
return (
|
||||||
|
n.callee.type === "Identifier" &&
|
||||||
|
unitTestSetUpRe.test(n.callee.name) &&
|
||||||
|
n.arguments.length === 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eg; `describe("some string", (done) => {})`
|
||||||
|
function isTestCall(n, parent) {
|
||||||
|
if (n.type !== "CallExpression") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (n.arguments.length === 1) {
|
||||||
|
if (isAngularTestWrapper(n) && parent && isTestCall(parent)) {
|
||||||
|
return isFunctionOrArrowExpression(n.arguments[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUnitTestSetUp(n)) {
|
||||||
|
return isAngularTestWrapper(n.arguments[0]);
|
||||||
|
}
|
||||||
|
} else if (n.arguments.length === 2 || n.arguments.length === 3) {
|
||||||
|
if (
|
||||||
|
((n.callee.type === "Identifier" && unitTestRe.test(n.callee.name)) ||
|
||||||
|
isSkipOrOnlyBlock(n)) &&
|
||||||
|
(isTemplateLiteral(n.arguments[0]) || isStringLiteral(n.arguments[0]))
|
||||||
|
) {
|
||||||
|
// it("name", () => { ... }, 2500)
|
||||||
|
if (n.arguments[2] && !isNumericLiteral(n.arguments[2])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(n.arguments.length === 2
|
||||||
|
? isFunctionOrArrowExpression(n.arguments[1])
|
||||||
|
: isFunctionOrArrowExpressionWithBody(n.arguments[1]) &&
|
||||||
|
n.arguments[1].params.length <= 1) ||
|
||||||
|
isAngularTestWrapper(n.arguments[1])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasLeadingComment(node) {
|
||||||
|
return node.comments && node.comments.some(comment => comment.leading);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasTrailingComment(node) {
|
||||||
|
return node.comments && node.comments.some(comment => comment.trailing);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCallOrOptionalCallExpression(node) {
|
||||||
|
return (
|
||||||
|
node.type === "CallExpression" || node.type === "OptionalCallExpression"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDanglingComments(node) {
|
||||||
|
return (
|
||||||
|
node.comments &&
|
||||||
|
node.comments.some(comment => !comment.leading && !comment.trailing)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** identify if an angular expression seems to have side effects */
|
||||||
|
function hasNgSideEffect(path) {
|
||||||
|
return hasNode(path.getValue(), node => {
|
||||||
|
switch (node.type) {
|
||||||
|
case undefined:
|
||||||
|
return false;
|
||||||
|
case "CallExpression":
|
||||||
|
case "OptionalCallExpression":
|
||||||
|
case "AssignmentExpression":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNgForOf(node, index, parentNode) {
|
||||||
|
return (
|
||||||
|
node.type === "NGMicrosyntaxKeyedExpression" &&
|
||||||
|
node.key.name === "of" &&
|
||||||
|
index === 1 &&
|
||||||
|
parentNode.body[0].type === "NGMicrosyntaxLet" &&
|
||||||
|
parentNode.body[0].value === null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param node {import("estree").TemplateLiteral} */
|
||||||
|
function isSimpleTemplateLiteral(node) {
|
||||||
|
if (node.expressions.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.expressions.every(expr => {
|
||||||
|
// Disallow comments since printDocToString can't print them here
|
||||||
|
if (expr.comments) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow `x` and `this`
|
||||||
|
if (expr.type === "Identifier" || expr.type === "ThisExpression") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow `a.b.c`, `a.b[c]`, and `this.x.y`
|
||||||
|
if (
|
||||||
|
(expr.type === "MemberExpression" ||
|
||||||
|
expr.type === "OptionalMemberExpression") &&
|
||||||
|
(expr.property.type === "Identifier" || expr.property.type === "Literal")
|
||||||
|
) {
|
||||||
|
let ancestor = expr;
|
||||||
|
while (
|
||||||
|
ancestor.type === "MemberExpression" ||
|
||||||
|
ancestor.type === "OptionalMemberExpression"
|
||||||
|
) {
|
||||||
|
ancestor = ancestor.object;
|
||||||
|
if (ancestor.comments) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
ancestor.type === "Identifier" ||
|
||||||
|
ancestor.type === "ThisExpression"
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFlowVariance(path) {
|
||||||
|
if (!path.variance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Babel 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:
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return variance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function classPropMayCauseASIProblems(path) {
|
||||||
|
const node = path.getNode();
|
||||||
|
|
||||||
|
if (node.type !== "ClassProperty") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = node.key && node.key.name;
|
||||||
|
|
||||||
|
// this isn't actually possible yet with most parsers available today
|
||||||
|
// so isn't properly tested yet.
|
||||||
|
if (
|
||||||
|
(name === "static" || name === "get" || name === "set") &&
|
||||||
|
!node.value &&
|
||||||
|
!node.typeAnnotation
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function classChildNeedsASIProtection(node) {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.static ||
|
||||||
|
node.accessibility // TypeScript
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.computed) {
|
||||||
|
const name = node.key && node.key.name;
|
||||||
|
if (name === "in" || name === "instanceof") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (node.type) {
|
||||||
|
case "ClassProperty":
|
||||||
|
case "TSAbstractClassProperty":
|
||||||
|
return node.computed;
|
||||||
|
case "MethodDefinition": // Flow
|
||||||
|
case "TSAbstractMethodDefinition": // TypeScript
|
||||||
|
case "ClassMethod":
|
||||||
|
case "ClassPrivateMethod": {
|
||||||
|
// Babel
|
||||||
|
const isAsync = node.value ? node.value.async : node.async;
|
||||||
|
const isGenerator = node.value ? node.value.generator : node.generator;
|
||||||
|
if (isAsync || node.kind === "get" || node.kind === "set") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (node.computed || isGenerator) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* istanbul ignore next */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeScriptMappedTypeModifier(tokenNode, keyword) {
|
||||||
|
if (tokenNode === "+") {
|
||||||
|
return "+" + keyword;
|
||||||
|
} else if (tokenNode === "-") {
|
||||||
|
return "-" + keyword;
|
||||||
|
}
|
||||||
|
return keyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasNewlineBetweenOrAfterDecorators(node, options) {
|
||||||
|
return (
|
||||||
|
hasNewlineInRange(
|
||||||
|
options.originalText,
|
||||||
|
options.locStart(node.decorators[0]),
|
||||||
|
options.locEnd(getLast(node.decorators))
|
||||||
|
) ||
|
||||||
|
hasNewline(options.originalText, options.locEnd(getLast(node.decorators)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only space, newline, carriage return, and tab are treated as whitespace
|
||||||
|
// inside JSX.
|
||||||
|
const jsxWhitespaceChars = " \n\r\t";
|
||||||
|
const matchJsxWhitespaceRegex = new RegExp("([" + jsxWhitespaceChars + "]+)");
|
||||||
|
const containsNonJsxWhitespaceRegex = new RegExp(
|
||||||
|
"[^" + jsxWhitespaceChars + "]"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Meaningful if it contains non-whitespace characters,
|
||||||
|
// or it contains whitespace without a new line.
|
||||||
|
function isMeaningfulJSXText(node) {
|
||||||
|
return (
|
||||||
|
isLiteral(node) &&
|
||||||
|
(containsNonJsxWhitespaceRegex.test(rawText(node)) ||
|
||||||
|
!/\n/.test(rawText(node)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasJsxIgnoreComment(path) {
|
||||||
|
const node = path.getValue();
|
||||||
|
const parent = path.getParentNode();
|
||||||
|
if (!parent || !node || !isJSXNode(node) || !isJSXNode(parent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup the previous sibling, ignoring any empty JSXText elements
|
||||||
|
const index = parent.children.indexOf(node);
|
||||||
|
let prevSibling = null;
|
||||||
|
for (let i = index; i > 0; i--) {
|
||||||
|
const candidate = parent.children[i - 1];
|
||||||
|
if (candidate.type === "JSXText" && !isMeaningfulJSXText(candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prevSibling = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
prevSibling &&
|
||||||
|
prevSibling.type === "JSXExpressionContainer" &&
|
||||||
|
prevSibling.expression.type === "JSXEmptyExpression" &&
|
||||||
|
prevSibling.expression.comments &&
|
||||||
|
prevSibling.expression.comments.find(
|
||||||
|
comment => comment.value.trim() === "prettier-ignore"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyJSXElement(node) {
|
||||||
|
if (node.children.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node.children.length > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is one text child and does not contain any meaningful text
|
||||||
|
// we can treat the element as empty.
|
||||||
|
const child = node.children[0];
|
||||||
|
return isLiteral(child) && !isMeaningfulJSXText(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPrettierIgnore(path) {
|
||||||
|
return hasIgnoreComment(path) || hasJsxIgnoreComment(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLastStatement(path) {
|
||||||
|
const parent = path.getParentNode();
|
||||||
|
if (!parent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const node = path.getValue();
|
||||||
|
const body = (parent.body || parent.consequent).filter(
|
||||||
|
stmt => stmt.type !== "EmptyStatement"
|
||||||
|
);
|
||||||
|
return body && body[body.length - 1] === node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFlowAnnotationComment(text, typeAnnotation, options) {
|
||||||
|
const start = options.locStart(typeAnnotation);
|
||||||
|
const end = skipWhitespace(text, options.locEnd(typeAnnotation));
|
||||||
|
return text.substr(start, 2) === "/*" && text.substr(end, 2) === "*/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasLeadingOwnLineComment(text, node, options) {
|
||||||
|
if (isJSXNode(node)) {
|
||||||
|
return hasNodeIgnoreComment(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res =
|
||||||
|
node.comments &&
|
||||||
|
node.comments.some(
|
||||||
|
comment => comment.leading && hasNewline(text, options.locEnd(comment))
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, options)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNakedLeftSide(argument)) {
|
||||||
|
let leftMost = argument;
|
||||||
|
let newLeftMost;
|
||||||
|
while ((newLeftMost = getLeftSide(leftMost))) {
|
||||||
|
leftMost = newLeftMost;
|
||||||
|
|
||||||
|
if (hasLeadingOwnLineComment(options.originalText, leftMost, options)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStringPropSafeToCoerceToIdentifier(node, options) {
|
||||||
|
return (
|
||||||
|
isStringLiteral(node.key) &&
|
||||||
|
isIdentifierName(node.key.value) &&
|
||||||
|
options.parser !== "json" &&
|
||||||
|
!(options.parser === "typescript" && node.type === "ClassProperty")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJestEachTemplateLiteral(node, parentNode) {
|
||||||
|
/**
|
||||||
|
* describe.each`table`(name, fn)
|
||||||
|
* describe.only.each`table`(name, fn)
|
||||||
|
* describe.skip.each`table`(name, fn)
|
||||||
|
* test.each`table`(name, fn)
|
||||||
|
* test.only.each`table`(name, fn)
|
||||||
|
* test.skip.each`table`(name, fn)
|
||||||
|
*
|
||||||
|
* Ref: https://github.com/facebook/jest/pull/6102
|
||||||
|
*/
|
||||||
|
const jestEachTriggerRegex = /^[xf]?(describe|it|test)$/;
|
||||||
|
return (
|
||||||
|
parentNode.type === "TaggedTemplateExpression" &&
|
||||||
|
parentNode.quasi === node &&
|
||||||
|
parentNode.tag.type === "MemberExpression" &&
|
||||||
|
parentNode.tag.property.type === "Identifier" &&
|
||||||
|
parentNode.tag.property.name === "each" &&
|
||||||
|
((parentNode.tag.object.type === "Identifier" &&
|
||||||
|
jestEachTriggerRegex.test(parentNode.tag.object.name)) ||
|
||||||
|
(parentNode.tag.object.type === "MemberExpression" &&
|
||||||
|
parentNode.tag.object.property.type === "Identifier" &&
|
||||||
|
(parentNode.tag.object.property.name === "only" ||
|
||||||
|
parentNode.tag.object.property.name === "skip") &&
|
||||||
|
parentNode.tag.object.object.type === "Identifier" &&
|
||||||
|
jestEachTriggerRegex.test(parentNode.tag.object.object.name)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function templateLiteralHasNewLines(template) {
|
||||||
|
return template.quasis.some(quasi => quasi.value.raw.includes("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTemplateOnItsOwnLine(n, text, options) {
|
||||||
|
return (
|
||||||
|
((n.type === "TemplateLiteral" && templateLiteralHasNewLines(n)) ||
|
||||||
|
(n.type === "TaggedTemplateExpression" &&
|
||||||
|
templateLiteralHasNewLines(n.quasi))) &&
|
||||||
|
!hasNewline(text, options.locStart(n), { backwards: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function needsHardlineAfterDanglingComment(node) {
|
||||||
|
if (!node.comments) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const lastDanglingComment = getLast(
|
||||||
|
node.comments.filter(comment => !comment.leading && !comment.trailing)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
lastDanglingComment && !handleComments.isBlockComment(lastDanglingComment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have nested conditional expressions, we want to print them in JSX mode
|
||||||
|
// if there's at least one JSXElement somewhere in the tree.
|
||||||
|
//
|
||||||
|
// A conditional expression chain like this should be printed in normal mode,
|
||||||
|
// because there aren't JSXElements anywhere in it:
|
||||||
|
//
|
||||||
|
// isA ? "A" : isB ? "B" : isC ? "C" : "Unknown";
|
||||||
|
//
|
||||||
|
// But a conditional expression chain like this should be printed in JSX mode,
|
||||||
|
// because there is a JSXElement in the last ConditionalExpression:
|
||||||
|
//
|
||||||
|
// isA ? "A" : isB ? "B" : isC ? "C" : <span className="warning">Unknown</span>;
|
||||||
|
//
|
||||||
|
// This type of ConditionalExpression chain is structured like this in the AST:
|
||||||
|
//
|
||||||
|
// ConditionalExpression {
|
||||||
|
// test: ...,
|
||||||
|
// consequent: ...,
|
||||||
|
// alternate: ConditionalExpression {
|
||||||
|
// test: ...,
|
||||||
|
// consequent: ...,
|
||||||
|
// alternate: ConditionalExpression {
|
||||||
|
// test: ...,
|
||||||
|
// consequent: ...,
|
||||||
|
// alternate: ...,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// We want to traverse over that shape and convert it into a flat structure so
|
||||||
|
// that we can find if there's a JSXElement somewhere inside.
|
||||||
|
function getConditionalChainContents(node) {
|
||||||
|
// Given this code:
|
||||||
|
//
|
||||||
|
// // Using a ConditionalExpression as the consequent is uncommon, but should
|
||||||
|
// // be handled.
|
||||||
|
// A ? B : C ? D : E ? F ? G : H : I
|
||||||
|
//
|
||||||
|
// which has this AST:
|
||||||
|
//
|
||||||
|
// ConditionalExpression {
|
||||||
|
// test: Identifier(A),
|
||||||
|
// consequent: Identifier(B),
|
||||||
|
// alternate: ConditionalExpression {
|
||||||
|
// test: Identifier(C),
|
||||||
|
// consequent: Identifier(D),
|
||||||
|
// alternate: ConditionalExpression {
|
||||||
|
// test: Identifier(E),
|
||||||
|
// consequent: ConditionalExpression {
|
||||||
|
// test: Identifier(F),
|
||||||
|
// consequent: Identifier(G),
|
||||||
|
// alternate: Identifier(H),
|
||||||
|
// },
|
||||||
|
// alternate: Identifier(I),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// we should return this Array:
|
||||||
|
//
|
||||||
|
// [
|
||||||
|
// Identifier(A),
|
||||||
|
// Identifier(B),
|
||||||
|
// Identifier(C),
|
||||||
|
// Identifier(D),
|
||||||
|
// Identifier(E),
|
||||||
|
// Identifier(F),
|
||||||
|
// Identifier(G),
|
||||||
|
// Identifier(H),
|
||||||
|
// Identifier(I)
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// This loses the information about whether each node was the test,
|
||||||
|
// consequent, or alternate, but we don't care about that here- we are only
|
||||||
|
// flattening this structure to find if there's any JSXElements inside.
|
||||||
|
const nonConditionalExpressions = [];
|
||||||
|
|
||||||
|
function recurse(node) {
|
||||||
|
if (node.type === "ConditionalExpression") {
|
||||||
|
recurse(node.test);
|
||||||
|
recurse(node.consequent);
|
||||||
|
recurse(node.alternate);
|
||||||
|
} else {
|
||||||
|
nonConditionalExpressions.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recurse(node);
|
||||||
|
|
||||||
|
return nonConditionalExpressions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionalExpressionChainContainsJSX(node) {
|
||||||
|
return Boolean(getConditionalChainContents(node).find(isJSXNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic to check for args with multiple anonymous functions. For instance,
|
||||||
|
// the following call should be split on multiple lines for readability:
|
||||||
|
// source.pipe(map((x) => x + x), filter((x) => x % 2 === 0))
|
||||||
|
function isFunctionCompositionArgs(args) {
|
||||||
|
if (args.length <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let count = 0;
|
||||||
|
for (const arg of args) {
|
||||||
|
if (isFunctionOrArrowExpression(arg)) {
|
||||||
|
count += 1;
|
||||||
|
if (count > 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (isCallOrOptionalCallExpression(arg)) {
|
||||||
|
for (const childArg of arg.arguments) {
|
||||||
|
if (isFunctionOrArrowExpression(childArg)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic to determine if a call is a “long curried function call”.
|
||||||
|
// See https://github.com/prettier/prettier/issues/1420.
|
||||||
|
//
|
||||||
|
// `connect(a, b, c)(d)`
|
||||||
|
// In the above call expression, the second call is the parent node and the
|
||||||
|
// first call is the current node.
|
||||||
|
function isLongCurriedCallExpression(path) {
|
||||||
|
const node = path.getValue();
|
||||||
|
const parent = path.getParentNode();
|
||||||
|
return (
|
||||||
|
isCallOrOptionalCallExpression(node) &&
|
||||||
|
isCallOrOptionalCallExpression(parent) &&
|
||||||
|
parent.callee === node &&
|
||||||
|
node.arguments.length > parent.arguments.length &&
|
||||||
|
parent.arguments.length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rawText(node) {
|
||||||
|
return node.extra ? node.extra.raw : node.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
function identity(x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getLeftSide,
|
classChildNeedsASIProtection,
|
||||||
|
classPropMayCauseASIProblems,
|
||||||
|
conditionalExpressionChainContainsJSX,
|
||||||
|
getFlowVariance,
|
||||||
getLeftSidePathName,
|
getLeftSidePathName,
|
||||||
hasNakedLeftSide,
|
getTypeScriptMappedTypeModifier,
|
||||||
hasNode,
|
hasDanglingComments,
|
||||||
|
hasFlowAnnotationComment,
|
||||||
hasFlowShorthandAnnotationComment,
|
hasFlowShorthandAnnotationComment,
|
||||||
hasFlowAnnotationComment
|
hasLeadingComment,
|
||||||
|
hasLeadingOwnLineComment,
|
||||||
|
hasNakedLeftSide,
|
||||||
|
hasNewlineBetweenOrAfterDecorators,
|
||||||
|
hasNgSideEffect,
|
||||||
|
hasPrettierIgnore,
|
||||||
|
hasTrailingComment,
|
||||||
|
identity,
|
||||||
|
isBinaryish,
|
||||||
|
isCallOrOptionalCallExpression,
|
||||||
|
isEmptyJSXElement,
|
||||||
|
isFlowAnnotationComment,
|
||||||
|
isFunctionCompositionArgs,
|
||||||
|
isFunctionNotation,
|
||||||
|
isFunctionOrArrowExpression,
|
||||||
|
isGetterOrSetter,
|
||||||
|
isJestEachTemplateLiteral,
|
||||||
|
isJSXNode,
|
||||||
|
isJSXWhitespaceExpression,
|
||||||
|
isLastStatement,
|
||||||
|
isLiteral,
|
||||||
|
isLongCurriedCallExpression,
|
||||||
|
isMeaningfulJSXText,
|
||||||
|
isMemberExpressionChain,
|
||||||
|
isMemberish,
|
||||||
|
isNgForOf,
|
||||||
|
isNodeStartingWithDeclare,
|
||||||
|
isNumericLiteral,
|
||||||
|
isObjectType,
|
||||||
|
isObjectTypePropertyAFunction,
|
||||||
|
isSimpleFlowType,
|
||||||
|
isSimpleTemplateLiteral,
|
||||||
|
isStringLiteral,
|
||||||
|
isStringPropSafeToCoerceToIdentifier,
|
||||||
|
isTemplateOnItsOwnLine,
|
||||||
|
isTestCall,
|
||||||
|
isTheOnlyJSXElementInMarkdown,
|
||||||
|
isTypeAnnotationAFunction,
|
||||||
|
matchJsxWhitespaceRegex,
|
||||||
|
needsHardlineAfterDanglingComment,
|
||||||
|
rawText,
|
||||||
|
returnArgumentHasLeadingComment
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue