From 3337fc1509b7ebc5a9e61e867dec7463cb69c6a0 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 23 Nov 2020 18:59:31 +0800 Subject: [PATCH] Refactor `ConditionalExpression` and `TSConditionalType` print (#9753) --- src/language-js/print/ternary.js | 138 ++++++++++++++++-------------- src/language-js/printer-estree.js | 30 +------ 2 files changed, 77 insertions(+), 91 deletions(-) diff --git a/src/language-js/print/ternary.js b/src/language-js/print/ternary.js index 604ea79de..580074850 100644 --- a/src/language-js/print/ternary.js +++ b/src/language-js/print/ternary.js @@ -23,15 +23,6 @@ const { * @typedef {import("../../common/fast-path")} FastPath * * @typedef {any} Options - Prettier options (TBD ...) - * - * @typedef {Object} OperatorOptions - * @property {() => Array} beforeParts - Parts to print before the `?`. - * @property {(breakClosingParen: boolean) => Array} 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 }; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index c7ce91726..078df4222 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -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")]);