diff --git a/changelog_unreleased/javascript/18397.md b/changelog_unreleased/javascript/18397.md index 0d8eb39e7..952bd7561 100644 --- a/changelog_unreleased/javascript/18397.md +++ b/changelog_unreleased/javascript/18397.md @@ -1,13 +1,58 @@ -#### Avoid double parentheses around logical expression (#18397 by @fisker) +#### Improve logical not expression print (#18397, #18401 by @fisker) + +- Fixed inner logical expression can be double parenthesized in some case. +- Inline the expression in `if`/`while`/`do..while` condition, to reduce diff when changing condition to negated value. ```jsx // Input -const a = !(x || y /* foo */); +if (!( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +)) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} // Prettier stable -const a = !((x || y) /* foo */); +if ( + !( + // `import("foo")` + ( + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" + ) + ) +) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} // Prettier main -const a = !(x || y /* foo */); +if (!( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +)) { +} else if ( + // `import("foo")` + node.type === "ImportExpression" || + // `type foo = import("foo")` + node.type === "TSImportType" +) { +} ``` + +_This code example above is [a real world case](https://github.com/prettier/prettier/blob/edb53db4b1ec326d3f0f4fb683f56c83beefeba4/src/language-js/print/call-expression.js#L135-L155) right here in the Prettier codebase_ diff --git a/src/language-js/print/array.js b/src/language-js/print/array.js index 9da195471..83653c854 100644 --- a/src/language-js/print/array.js +++ b/src/language-js/print/array.js @@ -22,7 +22,7 @@ import { isSignedNumericLiteral, shouldPrintComma, } from "../utilities/index.js"; -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; /** @import {Doc} from "../../document/index.js" */ diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index bd99a1fc5..e1673f630 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -20,6 +20,7 @@ import { hasLeadingOwnLineComment, isArrayExpression, isBinaryish, + isBooleanTypeCoercion, isCallExpression, isJsxElement, isMemberExpression, @@ -114,7 +115,7 @@ function printBinaryishExpression(path, options, print) { grandparent.type !== "NewExpression") || parent.type === "TemplateLiteral" || (key === "argument" && parent.type === "UnaryExpression") || - isBooleanTypeCoercion(path); + (key === "arguments" && isBooleanTypeCoercion(parent)); const shouldIndentIfInlining = parent.type === "AssignmentExpression" || @@ -395,27 +396,4 @@ function isVueFilterSequenceExpression(path, options) { ); } -/** -@returns {boolean} -*/ -function isBooleanTypeCoercion(path) { - if (path.key !== "arguments") { - return false; - } - - const { parent } = path; - if ( - !( - isCallExpression(parent) && - !parent.optional && - parent.arguments.length === 1 - ) - ) { - return false; - } - - const { callee } = parent; - return callee.type === "Identifier" && callee.name === "Boolean"; -} - export { printBinaryishExpression, shouldInlineLogicalExpression }; diff --git a/src/language-js/print/call-expression.js b/src/language-js/print/call-expression.js index 7e3034d67..d199510e5 100644 --- a/src/language-js/print/call-expression.js +++ b/src/language-js/print/call-expression.js @@ -13,7 +13,7 @@ import { } from "../utilities/index.js"; import printCallArguments from "./call-arguments.js"; import printMemberChain from "./member-chain.js"; -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; /* - `NewExpression` diff --git a/src/language-js/print/class.js b/src/language-js/print/class.js index db49d5650..94253721a 100644 --- a/src/language-js/print/class.js +++ b/src/language-js/print/class.js @@ -29,7 +29,7 @@ import { printDefiniteToken, printOptionalToken, printTypeScriptAccessibilityToken, -} from "./misc.js"; +} from "./miscellaneous.js"; import { printPropertyKey } from "./property.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; diff --git a/src/language-js/print/component.js b/src/language-js/print/component.js index e5f2040c3..0c9c7178d 100644 --- a/src/language-js/print/component.js +++ b/src/language-js/print/component.js @@ -10,7 +10,7 @@ import { printDanglingComments } from "../../main/comments/print.js"; import getNextNonSpaceNonCommentCharacter from "../../utilities/get-next-non-space-non-comment-character.js"; import { locEnd } from "../loc.js"; import { isNextLineEmpty, shouldPrintComma } from "../utilities/index.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/language-js/print/enum.js b/src/language-js/print/enum.js index 0df9fbbe7..10a6ff65d 100644 --- a/src/language-js/print/enum.js +++ b/src/language-js/print/enum.js @@ -1,4 +1,4 @@ -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; import { printObject as printEnumMembers } from "./object.js"; /* diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index 6a9bbb0f3..e93e7a102 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -49,7 +49,7 @@ import { adjustClause, printDefiniteToken, printOptionalToken, -} from "./misc.js"; +} from "./miscellaneous.js"; import { printExportDeclaration, printImportDeclaration, diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index 24230abfb..044756d68 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -37,7 +37,7 @@ import { printIntersectionType } from "./intersection-type.js"; import { printBigInt } from "./literal.js"; import { printFlowMappedTypeProperty } from "./mapped-type.js"; import { printMatch, printMatchCase, printMatchPattern } from "./match.js"; -import { printDeclareToken, printOptionalToken } from "./misc.js"; +import { printDeclareToken, printOptionalToken } from "./miscellaneous.js"; import { printExportDeclaration } from "./module.js"; import { printOpaqueType } from "./opaque-type.js"; import { printPropertyKey } from "./property.js"; diff --git a/src/language-js/print/for-statement.js b/src/language-js/print/for-statement.js index e0a474747..7f3e07fa1 100644 --- a/src/language-js/print/for-statement.js +++ b/src/language-js/print/for-statement.js @@ -6,7 +6,7 @@ import { softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import { adjustClause } from "./misc.js"; +import { adjustClause } from "./miscellaneous.js"; function printForStatement(path, options, print) { const { node } = path; diff --git a/src/language-js/print/function-type.js b/src/language-js/print/function-type.js index 5f1e094c8..b9d698904 100644 --- a/src/language-js/print/function-type.js +++ b/src/language-js/print/function-type.js @@ -6,7 +6,7 @@ import { printFunctionParameters, shouldGroupFunctionParameters, } from "./function-parameters.js"; -import { printAbstractToken } from "./misc.js"; +import { printAbstractToken } from "./miscellaneous.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; /** diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index c0cc75b37..1d67d14cc 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -27,7 +27,7 @@ import { shouldBreakFunctionParameters, shouldGroupFunctionParameters, } from "./function-parameters.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; import { printPropertyKey } from "./property.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; diff --git a/src/language-js/print/hook.js b/src/language-js/print/hook.js index c1bf34283..e37225f75 100644 --- a/src/language-js/print/hook.js +++ b/src/language-js/print/hook.js @@ -4,7 +4,7 @@ import { printFunctionParameters, shouldGroupFunctionParameters, } from "./function-parameters.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; /** * @import AstPath from "../../common/ast-path.js" diff --git a/src/language-js/print/if-statement.js b/src/language-js/print/if-statement.js index 87c282333..9f04b1859 100644 --- a/src/language-js/print/if-statement.js +++ b/src/language-js/print/if-statement.js @@ -1,11 +1,11 @@ -import { group, hardline, indent, softline } from "../../document/index.js"; +import { group, hardline } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; import { CommentCheckFlags, hasComment, needsHardlineAfterDanglingComment, } from "../utilities/index.js"; -import { adjustClause } from "./misc.js"; +import { adjustClause, printIfStatementCondition } from "./miscellaneous.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -17,7 +17,7 @@ function printIfStatement(path, options, print) { const consequent = adjustClause(node.consequent, print("consequent")); const opening = group([ "if (", - group([indent([softline, print("test")]), softline]), + printIfStatementCondition(path, options, print), ")", consequent, ]); diff --git a/src/language-js/print/indexed-access-type.js b/src/language-js/print/indexed-access-type.js index f5037daea..fdc96f750 100644 --- a/src/language-js/print/indexed-access-type.js +++ b/src/language-js/print/indexed-access-type.js @@ -1,4 +1,4 @@ -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; /* - `TSIndexedAccessType`(TypeScript) diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 46dbc3fd4..41a7311ad 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -28,7 +28,7 @@ import { import { printBindExpressionCallee } from "./bind-expression.js"; import printCallArguments from "./call-arguments.js"; import { printMemberLookup } from "./member.js"; -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; /** * @import {Doc} from "../../document/index.js" diff --git a/src/language-js/print/member.js b/src/language-js/print/member.js index 3fbcfa3c0..45773b085 100644 --- a/src/language-js/print/member.js +++ b/src/language-js/print/member.js @@ -5,7 +5,7 @@ import { isMemberExpression, isNumericLiteral, } from "../utilities/index.js"; -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; const isCallExpressionWithArguments = (node) => { if (node.type === "ChainExpression" || node.type === "TSNonNullExpression") { diff --git a/src/language-js/print/method-signature.js b/src/language-js/print/method-signature.js index 3db90cb70..2fe02d3db 100644 --- a/src/language-js/print/method-signature.js +++ b/src/language-js/print/method-signature.js @@ -7,7 +7,7 @@ import { import { printOptionalToken, printTypeScriptAccessibilityToken, -} from "./misc.js"; +} from "./miscellaneous.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; function printMethodSignature(path, options, print) { diff --git a/src/language-js/print/misc.js b/src/language-js/print/miscellaneous.js similarity index 67% rename from src/language-js/print/misc.js rename to src/language-js/print/miscellaneous.js index d3635dbc4..a6a78174f 100644 --- a/src/language-js/print/misc.js +++ b/src/language-js/print/miscellaneous.js @@ -1,4 +1,4 @@ -import { indent, line } from "../../document/index.js"; +import { group, indent, line, softline } from "../../document/index.js"; import { CommentCheckFlags, createTypeCheckFunction, @@ -113,11 +113,65 @@ function printTypeScriptAccessibilityToken(node) { return node.accessibility ? node.accessibility + " " : ""; } +const isLogicalNot = (node) => + node.type === "UnaryExpression" && node.operator === "!"; + +function shouldInlineCondition(node) { + if (hasComment(node)) { + return false; + } + + /* + Main purpose here is to avoid indent level change when switching to negative condition + + The following case: + + ```js + if (!foo({ + bar + })); + ``` + + does look nice, but once `!` is removed, it's hard to known how the `CallExpression` will print + + ```js + if (foo({ + bar + })); + ``` + + so we are not going to inline it. + */ + + // `!(a | b)` and `!!(a | b)`, but not `!!!(a | b)` + if (!isLogicalNot(node)) { + return false; + } + + node = node.argument; + node = isLogicalNot(node) ? node.argument : node; + + return node.type === "LogicalExpression"; +} + +function printIfOrWhileCondition(path, options, print) { + const conditionDoc = print("test"); + + if (shouldInlineCondition(path.node.test)) { + return conditionDoc; + } + + return group([indent([softline, conditionDoc]), softline]); +} + export { adjustClause, printAbstractToken, printDeclareToken, printDefiniteToken, + printIfOrWhileCondition as printDoWhileStatementCondition, + printIfOrWhileCondition as printIfStatementCondition, printOptionalToken, printTypeScriptAccessibilityToken, + printIfOrWhileCondition as printWhileStatementCondition, }; diff --git a/src/language-js/print/module-declaration.js b/src/language-js/print/module-declaration.js index 3ad87fc27..1a7b4d4b0 100644 --- a/src/language-js/print/module-declaration.js +++ b/src/language-js/print/module-declaration.js @@ -1,5 +1,5 @@ import { group } from "../../document/index.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; function printModuleDeclaration(path, options, print) { const { node } = path; diff --git a/src/language-js/print/module.js b/src/language-js/print/module.js index ee4dbc6e7..9e4a0cda5 100644 --- a/src/language-js/print/module.js +++ b/src/language-js/print/module.js @@ -23,7 +23,7 @@ import { shouldPrintComma, } from "../utilities/index.js"; import { printDecoratorsBeforeExport } from "./decorators.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; import { printObject } from "./object.js"; /** diff --git a/src/language-js/print/object.js b/src/language-js/print/object.js index 38ca29de2..ce93bca21 100644 --- a/src/language-js/print/object.js +++ b/src/language-js/print/object.js @@ -23,7 +23,7 @@ import { shouldPrintComma, } from "../utilities/index.js"; import { shouldHugTheOnlyParameter } from "./function-parameters.js"; -import { printOptionalToken } from "./misc.js"; +import { printOptionalToken } from "./miscellaneous.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; /** @import {Doc} from "../../document/index.js" */ diff --git a/src/language-js/print/opaque-type.js b/src/language-js/print/opaque-type.js index 1a756447a..d6d65e794 100644 --- a/src/language-js/print/opaque-type.js +++ b/src/language-js/print/opaque-type.js @@ -1,5 +1,5 @@ import { group, indent, line } from "../../document/index.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; /* - `DeclareOpaqueType`(flow) diff --git a/src/language-js/print/type-alias.js b/src/language-js/print/type-alias.js index 0ecdfbd36..01f26809e 100644 --- a/src/language-js/print/type-alias.js +++ b/src/language-js/print/type-alias.js @@ -1,5 +1,5 @@ import { printAssignment } from "./assignment.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; /* - `DeclareTypeAlias`(flow) diff --git a/src/language-js/print/typescript.js b/src/language-js/print/typescript.js index b1135d2f8..8fdf294fb 100644 --- a/src/language-js/print/typescript.js +++ b/src/language-js/print/typescript.js @@ -29,7 +29,7 @@ import { printMethodSignature } from "./method-signature.js"; import { printOptionalToken, printTypeScriptAccessibilityToken, -} from "./misc.js"; +} from "./miscellaneous.js"; import { printImportKind } from "./module.js"; import { printModuleDeclaration } from "./module-declaration.js"; import { printPropertyKey } from "./property.js"; diff --git a/src/language-js/print/variable-declaration.js b/src/language-js/print/variable-declaration.js index aa58642bc..71978af8e 100644 --- a/src/language-js/print/variable-declaration.js +++ b/src/language-js/print/variable-declaration.js @@ -1,6 +1,6 @@ import { group, hardline, indent, line } from "../../document/index.js"; import { hasComment } from "../utilities/index.js"; -import { printDeclareToken } from "./misc.js"; +import { printDeclareToken } from "./miscellaneous.js"; function printVariableDeclaration(path, options, print) { const { node } = path; diff --git a/src/language-js/print/while-statement.js b/src/language-js/print/while-statement.js index a5d3a9fdb..cb7888876 100644 --- a/src/language-js/print/while-statement.js +++ b/src/language-js/print/while-statement.js @@ -1,10 +1,14 @@ -import { group, hardline, indent, softline } from "../../document/index.js"; -import { adjustClause } from "./misc.js"; +import { group, hardline } from "../../document/index.js"; +import { + adjustClause, + printDoWhileStatementCondition, + printWhileStatementCondition, +} from "./miscellaneous.js"; function printWhileStatement(path, options, print) { return group([ "while (", - group([indent([softline, print("test")]), softline]), + printWhileStatementCondition(path, options, print), ")", adjustClause(path.node.body, print("body")), ]); @@ -19,7 +23,7 @@ function printDoWhileStatement(path, options, print) { doBody, body.type === "BlockStatement" ? " " : hardline, "while (", - group([indent([softline, print("test")]), softline]), + printDoWhileStatementCondition(path, options, print), ")", options.semi ? ";" : "", ]; diff --git a/src/language-js/utilities/index.js b/src/language-js/utilities/index.js index 386bd4466..6800d93b4 100644 --- a/src/language-js/utilities/index.js +++ b/src/language-js/utilities/index.js @@ -1098,6 +1098,19 @@ function shouldUnionTypePrintOwnComments({ key, parent }) { return true; } +/** +@returns {boolean} +*/ +function isBooleanTypeCoercion(node) { + return ( + node.type === "CallExpression" && + !node.optional && + node.arguments.length === 1 && + node.callee.type === "Identifier" && + node.callee.name === "Boolean" + ); +} + export { CommentCheckFlags, createTypeCheckFunction, @@ -1119,6 +1132,7 @@ export { isBinaryish, isBitwiseOperator, isBooleanLiteral, + isBooleanTypeCoercion, isCallExpression, isCallLikeExpression, isConditionalType, diff --git a/tests/format/js/if/condition-break/__snapshots__/format.test.js.snap b/tests/format/js/if/condition-break/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..e638459b3 --- /dev/null +++ b/tests/format/js/if/condition-break/__snapshots__/format.test.js.snap @@ -0,0 +1,450 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`boolean-expression.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +if (Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +while (Boolean( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")); + +do {} while (Boolean( + node.type === "ImportExpression" ?? + node.type === "TSImportType" ?? + node.type === "TSExternalModuleReference")); + +if (!!!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (!Boolean(!!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference"))); + +if ( // comment +Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (Boolean // comment +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (Boolean?. +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + + +=====================================output===================================== +if ( + Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ) +); + +while ( + Boolean( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference", + ) +); + +do {} while ( + Boolean( + node.type === "ImportExpression" ?? + node.type === "TSImportType" ?? + node.type === "TSExternalModuleReference", + ) +); + +if ( + !!!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ) +); + +if ( + !Boolean( + !!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ), + ) +); + +if ( + // comment + Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ) +); + +if ( + Boolean( + // comment + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ) +); + +if ( + Boolean?.( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference", + ) +); + +================================================================================ +`; + +exports[`real-world-cases.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +{{ +if (!doesImmediatelyReturnFunctionExpression({ node: current,returns,})) { + } +if (!validatePredefinedFormat(config, name, node, originalName, modifiers)) {} +}} +if ( + ![ + AST_NODE_TYPES.ArrowFunctionExpression, + AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.FunctionExpression, + ].includes(blockParent.type) + ); + if ( + !allowInGenericTypeArguments + .map(s => s.replaceAll(' ', '')) + .includes(fullyQualifiedName) + ) {} + +if(foo) { +// +}else if ( + !(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char) + ) {} +if (!processDefineProps( + ctx, + node.arguments[0], + declId, + true /* isWithDefaults */, + )) {} +if (processDefineProps( + ctx, + node.arguments[0], + declId, + true /* isWithDefaults */, + )) {} + +=====================================output===================================== +{ + { + if (!doesImmediatelyReturnFunctionExpression({ node: current, returns })) { + } + if ( + !validatePredefinedFormat(config, name, node, originalName, modifiers) + ) { + } + } +} +if ( + ![ + AST_NODE_TYPES.ArrowFunctionExpression, + AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.FunctionExpression, + ].includes(blockParent.type) +); +if ( + !allowInGenericTypeArguments + .map((s) => s.replaceAll(" ", "")) + .includes(fullyQualifiedName) +) { +} + +if (foo) { + // +} else if (!(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char)) { +} +if ( + !processDefineProps(ctx, node.arguments[0], declId, true /* isWithDefaults */) +) { +} +if ( + processDefineProps(ctx, node.arguments[0], declId, true /* isWithDefaults */) +) { +} + +================================================================================ +`; + +exports[`unary-expression.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +if (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +do {} while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + + +if (!!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if(typeof ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + + +if ( // comment +!!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (! // comment +!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (!! // comment +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if(1){ +// +}else {if(! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) do { +// +} while (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) else if (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) while (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) { +// +}} +if(2){ +// +}else {if( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) do { +// +} while ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) else if ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) while ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) { +// +}} + +if (( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") || !( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")) {}else if (!( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") || ( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")){} + +=====================================output===================================== +if (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" +)); + +while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" +)); + +do {} while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" +)); + +if (!!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" +)); + +if ( + typeof ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) +); + +if ( + // comment + !!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) +); + +if (!( + // comment + !( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) +)); + +if (!!( + // comment + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" +)); + +if (1) { + // +} else { + if (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + )) + do { + // + } while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + )); + else if (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + )) + while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + )) { + // + } +} +if (2) { + // +} else { + if ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) + do { + // + } while ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ); + else if ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) + while ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference" + ) { + // + } +} + +if ( + (node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") || + !( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference" + ) +) { +} else if ( + !( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference" + ) || + (node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") +) { +} + +================================================================================ +`; diff --git a/tests/format/js/if/condition-break/boolean-expression.js b/tests/format/js/if/condition-break/boolean-expression.js new file mode 100644 index 000000000..f19f15887 --- /dev/null +++ b/tests/format/js/if/condition-break/boolean-expression.js @@ -0,0 +1,43 @@ +if (Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +while (Boolean( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")); + +do {} while (Boolean( + node.type === "ImportExpression" ?? + node.type === "TSImportType" ?? + node.type === "TSExternalModuleReference")); + +if (!!!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (!Boolean(!!Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference"))); + +if ( // comment +Boolean( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (Boolean // comment +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (Boolean?. +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + diff --git a/tests/format/js/if/condition-break/format.test.js b/tests/format/js/if/condition-break/format.test.js new file mode 100644 index 000000000..67c251cce --- /dev/null +++ b/tests/format/js/if/condition-break/format.test.js @@ -0,0 +1 @@ +runFormatTest(import.meta, ["babel", "flow", "typescript"]); diff --git a/tests/format/js/if/condition-break/real-world-cases.js b/tests/format/js/if/condition-break/real-world-cases.js new file mode 100644 index 000000000..ecc0b355d --- /dev/null +++ b/tests/format/js/if/condition-break/real-world-cases.js @@ -0,0 +1,35 @@ +{{ +if (!doesImmediatelyReturnFunctionExpression({ node: current,returns,})) { + } +if (!validatePredefinedFormat(config, name, node, originalName, modifiers)) {} +}} +if ( + ![ + AST_NODE_TYPES.ArrowFunctionExpression, + AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.FunctionExpression, + ].includes(blockParent.type) + ); + if ( + !allowInGenericTypeArguments + .map(s => s.replaceAll(' ', '')) + .includes(fullyQualifiedName) + ) {} + +if(foo) { +// +}else if ( + !(i === 0 ? validFirstIdentCharRE : validIdentCharRE).test(char) + ) {} +if (!processDefineProps( + ctx, + node.arguments[0], + declId, + true /* isWithDefaults */, + )) {} +if (processDefineProps( + ctx, + node.arguments[0], + declId, + true /* isWithDefaults */, + )) {} diff --git a/tests/format/js/if/condition-break/unary-expression.js b/tests/format/js/if/condition-break/unary-expression.js new file mode 100644 index 000000000..ca6b18d74 --- /dev/null +++ b/tests/format/js/if/condition-break/unary-expression.js @@ -0,0 +1,97 @@ +if (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +do {} while (!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + + +if (!!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if(typeof ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + + +if ( // comment +!!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (! // comment +!( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if (!! // comment +( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")); + +if(1){ +// +}else {if(! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) do { +// +} while (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) else if (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) while (! ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) { +// +}} +if(2){ +// +}else {if( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) do { +// +} while ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) else if ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) while ( ( + node.type === "ImportExpression" || + node.type === "TSImportType" || + node.type === "TSExternalModuleReference")) { +// +}} + +if (( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") || !( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")) {}else if (!( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference") || ( + node.type === "ImportExpression" && + node.type === "TSImportType" && + node.type === "TSExternalModuleReference")){} diff --git a/tests/format/js/logical-expressions/__snapshots__/format.test.js.snap b/tests/format/js/logical-expressions/__snapshots__/format.test.js.snap index b7b2dad53..d7bd916c5 100644 --- a/tests/format/js/logical-expressions/__snapshots__/format.test.js.snap +++ b/tests/format/js/logical-expressions/__snapshots__/format.test.js.snap @@ -118,23 +118,21 @@ const foo = () => ]; // Realworld case from Prettier repo -if ( - !( - // \`import("foo")\` - node.type === "ImportExpression" || - // \`type foo = import("foo")\` - node.type === "TSImportType" || - // \`import type A = require("foo")\` - node.type === "TSExternalModuleReference" || - // \`require("foo")\` - // \`require.resolve("foo")\` - // \`require.resolve.paths("foo")\` - // \`import.meta.resolve("foo")\` - (node.type === "CallExpression" && - !node.optional && - isNodeMatches(node.callee, moduleImportCallees)) - ) -) { +if (!( + // \`import("foo")\` + node.type === "ImportExpression" || + // \`type foo = import("foo")\` + node.type === "TSImportType" || + // \`import type A = require("foo")\` + node.type === "TSExternalModuleReference" || + // \`require("foo")\` + // \`require.resolve("foo")\` + // \`require.resolve.paths("foo")\` + // \`import.meta.resolve("foo")\` + (node.type === "CallExpression" && + !node.optional && + isNodeMatches(node.callee, moduleImportCallees)) +)) { } // Realworld case from Babel repo