Refactor ConditionalExpression and TSConditionalType print (#9753)

This commit is contained in:
fisker Cheung authored and GitHub committed 2020-11-23 18:59:31 +08:00
1 parent 895a177b0d
commit 3337fc1509
2 files changed
+77 -91

No files matched your search

+74 -64
View File
@@ -23,15 +23,6 @@ const {
* @typedef {import("../../common/fast-path")} FastPath
*
* @typedef {any} Options - Prettier options (TBD ...)
*
* @typedef {Object} OperatorOptions
* @property {() => Array<string | Doc>} beforeParts - Parts to print before the `?`.
* @property {(breakClosingParen: boolean) => Array<string | Doc>} afterParts - Parts to print after the conditional expression.
* @property {boolean} shouldCheckJsx - Whether to check for and print in JSX mode.
* @property {string} conditionalNodeType - The type of the conditional expression node, ie "ConditionalExpression" or "TSConditionalType".
* @property {string} consequentNodePropertyName - The property at which the consequent node can be found on the main node, eg "consequent".
* @property {string} alternateNodePropertyName - The property at which the alternate node can be found on the main node, eg "alternate".
* @property {string[]} testNodePropertyNames - The properties at which the test nodes can be found on the main node, eg "test".
*/
// If we have nested conditional expressions, we want to print them in JSX mode
@@ -129,6 +120,40 @@ function conditionalExpressionChainContainsJSX(node) {
return getConditionalChainContents(node).some(isJSXNode);
}
function printTernaryTest(path, options, print) {
const node = path.getValue();
const isConditionalExpression = node.type === "ConditionalExpression";
const alternateNodePropertyName = isConditionalExpression
? "alternate"
: "falseType";
const parent = path.getParentNode();
const printed = isConditionalExpression
? path.call(print, "test")
: concat([
path.call(print, "checkType"),
" ",
"extends",
" ",
path.call(print, "extendsType"),
]);
/**
* a
* ? b
* : multiline
* test
* node
* ^^ align(2)
* ? d
* : e
*/
if (parent.type === node.type && parent[alternateNodePropertyName] === node) {
return align(2, printed);
}
return printed;
}
/**
* The following is the shared logic for
* ternary operators, namely ConditionalExpression
@@ -136,13 +161,22 @@ function conditionalExpressionChainContainsJSX(node) {
* @param {FastPath} path - The path to the ConditionalExpression/TSConditionalType node.
* @param {Options} options - Prettier options
* @param {Function} print - Print function to call recursively
* @param {OperatorOptions} operatorOptions
* @returns {Doc}
*/
function printTernaryOperator(path, options, print, operatorOptions) {
function printTernary(path, options, print) {
const node = path.getValue();
const consequentNode = node[operatorOptions.consequentNodePropertyName];
const alternateNode = node[operatorOptions.alternateNodePropertyName];
const isConditionalExpression = node.type === "ConditionalExpression";
const consequentNodePropertyName = isConditionalExpression
? "consequent"
: "trueType";
const alternateNodePropertyName = isConditionalExpression
? "alternate"
: "falseType";
const testNodePropertyNames = isConditionalExpression
? ["test"]
: ["checkType", "extendsType"];
const consequentNode = node[consequentNodePropertyName];
const alternateNode = node[alternateNodePropertyName];
const parts = [];
// We print a ConditionalExpression in either "JSX mode" or "normal mode".
@@ -150,10 +184,9 @@ function printTernaryOperator(path, options, print, operatorOptions) {
let jsxMode = false;
const parent = path.getParentNode();
const isParentTest =
parent.type === operatorOptions.conditionalNodeType &&
operatorOptions.testNodePropertyNames.some((prop) => parent[prop] === node);
let forceNoIndent =
parent.type === operatorOptions.conditionalNodeType && !isParentTest;
parent.type === node.type &&
testNodePropertyNames.some((prop) => parent[prop] === node);
let forceNoIndent = parent.type === node.type && !isParentTest;
// Find the outermost non-ConditionalExpression parent, and the outermost
// ConditionalExpression parent. We'll use these to determine if we should
@@ -167,8 +200,8 @@ function printTernaryOperator(path, options, print, operatorOptions) {
i++;
} while (
currentParent &&
currentParent.type === operatorOptions.conditionalNodeType &&
operatorOptions.testNodePropertyNames.every(
currentParent.type === node.type &&
testNodePropertyNames.every(
(prop) => currentParent[prop] !== previousParent
)
);
@@ -176,8 +209,8 @@ function printTernaryOperator(path, options, print, operatorOptions) {
const lastConditionalParent = previousParent;
if (
operatorOptions.shouldCheckJsx &&
(isJSXNode(node[operatorOptions.testNodePropertyNames[0]]) ||
isConditionalExpression &&
(isJSXNode(node[testNodePropertyNames[0]]) ||
isJSXNode(consequentNode) ||
isJSXNode(alternateNode) ||
conditionalExpressionChainContainsJSX(lastConditionalParent))
@@ -208,35 +241,30 @@ function printTernaryOperator(path, options, print, operatorOptions) {
parts.push(
" ? ",
isNil(consequentNode)
? path.call(print, operatorOptions.consequentNodePropertyName)
: wrap(path.call(print, operatorOptions.consequentNodePropertyName)),
? path.call(print, consequentNodePropertyName)
: wrap(path.call(print, consequentNodePropertyName)),
" : ",
alternateNode.type === operatorOptions.conditionalNodeType ||
isNil(alternateNode)
? path.call(print, operatorOptions.alternateNodePropertyName)
: wrap(path.call(print, operatorOptions.alternateNodePropertyName))
alternateNode.type === node.type || isNil(alternateNode)
? path.call(print, alternateNodePropertyName)
: wrap(path.call(print, alternateNodePropertyName))
);
} else {
// normal mode
const part = concat([
line,
"? ",
consequentNode.type === operatorOptions.conditionalNodeType
? ifBreak("", "(")
: "",
align(2, path.call(print, operatorOptions.consequentNodePropertyName)),
consequentNode.type === operatorOptions.conditionalNodeType
? ifBreak("", ")")
: "",
consequentNode.type === node.type ? ifBreak("", "(") : "",
align(2, path.call(print, consequentNodePropertyName)),
consequentNode.type === node.type ? ifBreak("", ")") : "",
line,
": ",
alternateNode.type === operatorOptions.conditionalNodeType
? path.call(print, operatorOptions.alternateNodePropertyName)
: align(2, path.call(print, operatorOptions.alternateNodePropertyName)),
alternateNode.type === node.type
? path.call(print, alternateNodePropertyName)
: align(2, path.call(print, alternateNodePropertyName)),
]);
parts.push(
parent.type !== operatorOptions.conditionalNodeType ||
parent[operatorOptions.alternateNodePropertyName] === node ||
parent.type !== node.type ||
parent[alternateNodePropertyName] === node ||
isParentTest
? part
: options.useTabs
@@ -249,9 +277,7 @@ function printTernaryOperator(path, options, print, operatorOptions) {
// break if any of them break. That means we should only group around the
// outer-most ConditionalExpression.
const comments = flat([
...operatorOptions.testNodePropertyNames.map(
(propertyName) => node[propertyName].comments
),
...testNodePropertyNames.map((propertyName) => node[propertyName].comments),
consequentNode.comments,
alternateNode.comments,
]).filter(Boolean);
@@ -284,27 +310,11 @@ function printTernaryOperator(path, options, print, operatorOptions) {
!parent.computed;
const result = maybeGroup(
concat(
[].concat(
((testDoc) =>
/**
* a
* ? b
* : multiline
* test
* node
* ^^ align(2)
* ? d
* : e
*/
parent.type === operatorOptions.conditionalNodeType &&
parent[operatorOptions.alternateNodePropertyName] === node
? align(2, testDoc)
: testDoc)(concat(operatorOptions.beforeParts())),
forceNoIndent ? concat(parts) : indent(concat(parts)),
operatorOptions.afterParts(breakClosingParen)
)
)
concat([
printTernaryTest(path, options, print),
forceNoIndent ? concat(parts) : indent(concat(parts)),
isConditionalExpression && breakClosingParen ? softline : "",
])
);
return isParentTest
@@ -312,4 +322,4 @@ function printTernaryOperator(path, options, print, operatorOptions) {
: result;
}
module.exports = printTernaryOperator;
module.exports = { printTernary };
+3 -27
View File
@@ -81,7 +81,7 @@ const {
printExportAllDeclaration,
printModuleSpecifier,
} = require("./print/module");
const printTernaryOperator = require("./print/ternary");
const { printTernary } = require("./print/ternary");
const { printFunctionParameters } = require("./print/function-parameters");
const { printTemplateLiteral } = require("./print/template-literal");
const { printArray, printArrayItems } = require("./print/array");
@@ -646,15 +646,8 @@ function printPathNoParens(path, options, print, args) {
return concat(parts);
case "ConditionalExpression":
return printTernaryOperator(path, options, print, {
beforeParts: () => [path.call(print, "test")],
afterParts: (breakClosingParen) => [breakClosingParen ? softline : ""],
shouldCheckJsx: true,
conditionalNodeType: "ConditionalExpression",
consequentNodePropertyName: "consequent",
alternateNodePropertyName: "alternate",
testNodePropertyNames: ["test"],
});
case "TSConditionalType":
return printTernary(path, options, print);
case "VariableDeclaration": {
const printed = path.map((childPath) => {
return print(childPath);
@@ -2038,23 +2031,6 @@ function printPathNoParens(path, options, print, args) {
case "TSPrivateIdentifier":
return n.escapedText;
case "TSConditionalType":
return printTernaryOperator(path, options, print, {
beforeParts: () => [
path.call(print, "checkType"),
" ",
"extends",
" ",
path.call(print, "extendsType"),
],
afterParts: () => [],
shouldCheckJsx: false,
conditionalNodeType: "TSConditionalType",
consequentNodePropertyName: "trueType",
alternateNodePropertyName: "falseType",
testNodePropertyNames: ["checkType", "extendsType"],
});
case "TSInferType":
return concat(["infer", " ", path.call(print, "typeParameter")]);