diff --git a/changelog_unreleased/javascript/14633.md b/changelog_unreleased/javascript/14633.md new file mode 100644 index 000000000..4ca1211f0 --- /dev/null +++ b/changelog_unreleased/javascript/14633.md @@ -0,0 +1,16 @@ +#### Improve consistency between curried and non-curried arrow function (#14633 by @seiyab, @fisker) + + +```jsx +// Input +Y(() => a ? b : c); +Y(() => () => a ? b : c); + +// Prettier stable +Y(() => (a ? b : c)); +Y(() => () => a ? b : c); + +// Prettier main +Y(() => (a ? b : c)); +Y(() => () => (a ? b : c)); +``` diff --git a/src/language-js/print/arrow-function.js b/src/language-js/print/arrow-function.js new file mode 100644 index 000000000..8785c5419 --- /dev/null +++ b/src/language-js/print/arrow-function.js @@ -0,0 +1,329 @@ +import { + printDanglingComments, + printCommentsSeparately, +} from "../../main/comments/print.js"; +import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; +import { + line, + softline, + group, + indent, + ifBreak, + join, + indentIfBreak, +} from "../../document/builders.js"; +import { removeLines, willBreak } from "../../document/utils.js"; +import { ArgExpansionBailout } from "../../common/errors.js"; +import { + getFunctionParameters, + hasLeadingOwnLineComment, + isJsxElement, + isTemplateOnItsOwnLine, + shouldPrintComma, + startsWithNoLookaheadToken, + isBinaryish, + hasComment, + isCallLikeExpression, + isArrayOrTupleExpression, + isObjectOrRecordExpression, +} from "../utils/index.js"; +import { locEnd } from "../loc.js"; +import { printFunctionParameters } from "./function-parameters.js"; +import { printReturnType, shouldPrintParamsWithoutParens } from "./function.js"; + +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + +// In order to avoid confusion between +// a => a ? a : a +// a <= a ? a : a +const shouldAddParensIfNotBreakCache = new WeakMap(); +function shouldAddParensIfNotBreak(node) { + if (!shouldAddParensIfNotBreakCache.has(node)) { + shouldAddParensIfNotBreakCache.set( + node, + node.type === "ConditionalExpression" && + !startsWithNoLookaheadToken( + node, + (node) => node.type === "ObjectExpression" + ) + ); + } + return shouldAddParensIfNotBreakCache.get(node); +} + +// We handle sequence expressions as the body of arrows specially, +// so that the required parentheses end up on their own lines. +const shouldAlwaysAddParens = (node) => node.type === "SequenceExpression"; + +function printArrowFunction(path, options, print, args = {}) { + /** @type {Doc[]} */ + const signatureDocs = []; + /** @type {Doc} */ + let bodyDoc; + /** @type {Doc[]} */ + const bodyComments = []; + let shouldBreakChain = false; + const shouldPrintAsChain = + !args.expandLastArg && path.node.body.type === "ArrowFunctionExpression"; + let functionBody; + + (function rec() { + const { node } = path; + const signatureDoc = printArrowFunctionSignature( + path, + options, + print, + args + ); + if (signatureDocs.length === 0) { + signatureDocs.push(signatureDoc); + } else { + const { leading, trailing } = printCommentsSeparately(path, options); + signatureDocs.push([leading, signatureDoc]); + bodyComments.unshift(trailing); + } + + if (shouldPrintAsChain) { + shouldBreakChain ||= + // Always break the chain if: + (node.returnType && getFunctionParameters(node).length > 0) || + node.typeParameters || + getFunctionParameters(node).some( + (param) => param.type !== "Identifier" + ); + } + + if (!shouldPrintAsChain || node.body.type !== "ArrowFunctionExpression") { + bodyDoc = print("body", args); + functionBody = node.body; + } else { + path.call(rec, "body"); + } + })(); + + // We want to always keep these types of nodes on the same line + // as the arrow. + const shouldPutBodyOnSameLine = + !hasLeadingOwnLineComment(options.originalText, functionBody) && + (shouldAlwaysAddParens(functionBody) || + mayBreakAfterShortPrefix(functionBody, bodyDoc, options) || + (!shouldBreakChain && shouldAddParensIfNotBreak(functionBody))); + + const isCallee = path.key === "callee" && isCallLikeExpression(path.parent); + const chainGroupId = Symbol("arrow-chain"); + + const signaturesDoc = printArrowFunctionSignatures(path, args, { + signatureDocs, + shouldBreak: shouldBreakChain, + }); + let shouldBreakSignatures; + let shouldIndentSignatures = false; + if ( + shouldPrintAsChain && + (isCallee || + // isAssignmentRhs + args.assignmentLayout) + ) { + shouldIndentSignatures = true; + shouldBreakSignatures = + args.assignmentLayout === "chain-tail-arrow-chain" || + (isCallee && !shouldPutBodyOnSameLine); + } + + bodyDoc = printArrowFunctionBody(path, options, args, { + bodyDoc, + bodyComments, + functionBody, + shouldPutBodyOnSameLine, + }); + + return group([ + group( + shouldIndentSignatures + ? indent([softline, signaturesDoc]) + : signaturesDoc, + { shouldBreak: shouldBreakSignatures, id: chainGroupId } + ), + " =>", + shouldPrintAsChain + ? indentIfBreak(bodyDoc, { groupId: chainGroupId }) + : group(bodyDoc), + shouldPrintAsChain && isCallee + ? ifBreak(softline, "", { groupId: chainGroupId }) + : "", + ]); +} + +function printArrowFunctionSignature(path, options, print, args) { + const { node } = path; + const parts = []; + + if (node.async) { + parts.push("async "); + } + + if (shouldPrintParamsWithoutParens(path, options)) { + parts.push(print(["params", 0])); + } else { + const expandArg = args.expandLastArg || args.expandFirstArg; + let returnTypeDoc = printReturnType(path, print); + if (expandArg) { + if (willBreak(returnTypeDoc)) { + throw new ArgExpansionBailout(); + } + returnTypeDoc = group(removeLines(returnTypeDoc)); + } + parts.push( + group([ + printFunctionParameters( + path, + print, + options, + expandArg, + /* printTypeParams */ true + ), + returnTypeDoc, + ]) + ); + } + + const dangling = printDanglingComments(path, options, { + filter(comment) { + const nextCharacter = getNextNonSpaceNonCommentCharacterIndex( + options.originalText, + locEnd(comment) + ); + return ( + nextCharacter !== false && + options.originalText.slice(nextCharacter, nextCharacter + 2) === "=>" + ); + }, + }); + if (dangling) { + parts.push(" ", dangling); + } + return parts; +} + +/** + * + * @param {*} functionBody + * @param {*} bodyDoc + * @param {*} options + * @returns {boolean} + */ +function mayBreakAfterShortPrefix(functionBody, bodyDoc, options) { + return ( + isArrayOrTupleExpression(functionBody) || + isObjectOrRecordExpression(functionBody) || + functionBody.type === "ArrowFunctionExpression" || + functionBody.type === "DoExpression" || + functionBody.type === "BlockStatement" || + isJsxElement(functionBody) || + (bodyDoc.label?.hug !== false && + (bodyDoc.label?.embed || + isTemplateOnItsOwnLine(functionBody, options.originalText))) + ); +} + +/** + * @param {AstPath} path + * @param {*} args + * @param {Object} arrowFunctionSignaturesPrintOptions + * @param {Doc[]} arrowFunctionSignaturesPrintOptions.signatureDocs + * @param {boolean} arrowFunctionSignaturesPrintOptions.shouldBreak + * @returns {Doc} + */ +function printArrowFunctionSignatures( + path, + args, + { signatureDocs, shouldBreak } +) { + if (signatureDocs.length === 1) { + return signatureDocs[0]; + } + + const { parent, key } = path; + if ( + (key !== "callee" && isCallLikeExpression(parent)) || + isBinaryish(parent) + ) { + return group( + [ + signatureDocs[0], + " =>", + indent([line, join([" =>", line], signatureDocs.slice(1))]), + ], + { shouldBreak } + ); + } + + if ( + (key === "callee" && isCallLikeExpression(parent)) || + // isAssignmentRhs + args.assignmentLayout + ) { + return group(join([" =>", line], signatureDocs), { shouldBreak }); + } + + return group(indent(join([" =>", line], signatureDocs)), { shouldBreak }); +} + +/** + * @param {AstPath} path + * @param {*} options + * @param {*} args + * @param {Object} arrowFunctionBodyPrintOptions + * @param {Doc} arrowFunctionBodyPrintOptions.bodyDoc + * @param {Doc[]} arrowFunctionBodyPrintOptions.bodyComments + * @param {*} arrowFunctionBodyPrintOptions.functionBody + * @param {boolean} arrowFunctionBodyPrintOptions.shouldPutBodyOnSameLine + */ +function printArrowFunctionBody( + path, + options, + args, + { bodyDoc, bodyComments, functionBody, shouldPutBodyOnSameLine } +) { + const { node, parent } = path; + + const trailingComma = + args.expandLastArg && shouldPrintComma(options, "all") ? ifBreak(",") : ""; + + // if the arrow function is expanded as last argument, we are adding a + // level of indentation and need to add a softline to align the closing ) + // with the opening (, or if it's inside a JSXExpression (e.g. an attribute) + // we should align the expression's closing } with the line with the opening {. + const trailingSpace = + (args.expandLastArg || parent.type === "JSXExpressionContainer") && + !hasComment(node) + ? softline + : ""; + + if (shouldPutBodyOnSameLine && shouldAddParensIfNotBreak(functionBody)) { + return [ + " ", + group([ + ifBreak("", "("), + indent([softline, bodyDoc]), + ifBreak("", ")"), + trailingComma, + trailingSpace, + ]), + bodyComments, + ]; + } + + if (shouldAlwaysAddParens(functionBody)) { + bodyDoc = group(["(", indent([softline, bodyDoc]), softline, ")"]); + } + + return shouldPutBodyOnSameLine + ? [" ", bodyDoc, bodyComments] + : [indent([line, bodyDoc, bodyComments]), trailingComma, trailingSpace]; +} + +export { printArrowFunction }; diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index 2e3ddebda..5f6574ce9 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -1,43 +1,23 @@ -/** @typedef {import("../../document/builders.js").Doc} Doc */ - import assert from "node:assert"; +import { printDanglingComments } from "../../main/comments/print.js"; import { - printDanglingComments, - printCommentsSeparately, -} from "../../main/comments/print.js"; -import getNextNonSpaceNonCommentCharacterIndex from "../../utils/get-next-non-space-non-comment-character-index.js"; -import { - line, softline, group, indent, - dedent, ifBreak, hardline, - join, - indentIfBreak, } from "../../document/builders.js"; -import { removeLines, willBreak } from "../../document/utils.js"; -import { ArgExpansionBailout } from "../../common/errors.js"; import { getFunctionParameters, hasLeadingOwnLineComment, - isJsxElement, - isTemplateOnItsOwnLine, - shouldPrintComma, - startsWithNoLookaheadToken, isBinaryish, hasComment, CommentCheckFlags, - isCallLikeExpression, isCallExpression, getCallArguments, hasNakedLeftSide, getLeftSide, - isArrayOrTupleExpression, - isObjectOrRecordExpression, } from "../utils/index.js"; -import { locEnd } from "../loc.js"; import { printFunctionParameters, shouldGroupFunctionParameters, @@ -47,6 +27,11 @@ import { printPropertyKey } from "./property.js"; import { printFunctionTypeParameters, printDeclareToken } from "./misc.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; +/** + * @typedef {import("../../common/ast-path.js").default} AstPath + * @typedef {import("../../document/builders.js").Doc} Doc + */ + const isMethod = (node) => node.type === "ObjectMethod" || node.type === "ClassMethod" || @@ -198,214 +183,6 @@ function printMethodValue(path, options, print) { return parts; } -function printArrowFunctionSignature(path, options, print, args) { - const { node } = path; - const parts = []; - - if (node.async) { - parts.push("async "); - } - - if (shouldPrintParamsWithoutParens(path, options)) { - parts.push(print(["params", 0])); - } else { - const expandArg = args?.expandLastArg || args?.expandFirstArg; - let returnTypeDoc = printReturnType(path, print); - if (expandArg) { - if (willBreak(returnTypeDoc)) { - throw new ArgExpansionBailout(); - } - returnTypeDoc = group(removeLines(returnTypeDoc)); - } - parts.push( - group([ - printFunctionParameters( - path, - print, - options, - expandArg, - /* printTypeParams */ true - ), - returnTypeDoc, - ]) - ); - } - - const dangling = printDanglingComments(path, options, { - filter(comment) { - const nextCharacter = getNextNonSpaceNonCommentCharacterIndex( - options.originalText, - locEnd(comment) - ); - return ( - nextCharacter !== false && - options.originalText.slice(nextCharacter, nextCharacter + 2) === "=>" - ); - }, - }); - if (dangling) { - parts.push(" ", dangling); - } - return parts; -} - -function printArrowChain( - path, - args, - signatures, - shouldBreak, - bodyDoc, - tailNode -) { - const { parent, key } = path; - const isCallee = isCallLikeExpression(parent) && key === "callee"; - const isAssignmentRhs = Boolean(args?.assignmentLayout); - const shouldPutBodyOnSeparateLine = - tailNode.body.type !== "BlockStatement" && - !isObjectOrRecordExpression(tailNode.body) && - tailNode.body.type !== "SequenceExpression"; - const shouldBreakBeforeChain = - (isCallee && shouldPutBodyOnSeparateLine) || - args?.assignmentLayout === "chain-tail-arrow-chain"; - - const groupId = Symbol("arrow-chain"); - - if ((isCallLikeExpression(parent) && !isCallee) || isBinaryish(parent)) { - signatures = [dedent(signatures[0]), ...signatures.slice(1)]; - } - - // We handle sequence expressions as the body of arrows specially, - // so that the required parentheses end up on their own lines. - if (tailNode.body.type === "SequenceExpression") { - bodyDoc = group(["(", indent([softline, bodyDoc]), softline, ")"]); - } - - return group([ - group( - indent([ - isCallee || isAssignmentRhs ? softline : "", - group(join([" =>", line], signatures), { shouldBreak }), - ]), - { id: groupId, shouldBreak: shouldBreakBeforeChain } - ), - " =>", - indentIfBreak( - shouldPutBodyOnSeparateLine ? indent([line, bodyDoc]) : [" ", bodyDoc], - { groupId } - ), - isCallee ? ifBreak(softline, "", { groupId }) : "", - ]); -} - -function printArrowFunction(path, options, print, args) { - let { node } = path; - /** @type {Doc[]} */ - const signatures = []; - const body = []; - let chainShouldBreak = false; - - (function rec() { - const doc = printArrowFunctionSignature(path, options, print, args); - if (signatures.length === 0) { - signatures.push(doc); - } else { - const { leading, trailing } = printCommentsSeparately(path, options); - signatures.push([leading, doc]); - body.unshift(trailing); - } - - chainShouldBreak = - chainShouldBreak || - // Always break the chain if: - (node.returnType && getFunctionParameters(node).length > 0) || - node.typeParameters || - getFunctionParameters(node).some((param) => param.type !== "Identifier"); - - if (node.body.type !== "ArrowFunctionExpression" || args?.expandLastArg) { - body.unshift(print("body", args)); - } else { - node = node.body; - path.call(rec, "body"); - } - })(); - - if (signatures.length > 1) { - return printArrowChain( - path, - args, - signatures, - chainShouldBreak, - body, - node - ); - } - - const parts = signatures; - parts.push(" =>"); - - // We want to always keep these types of nodes on the same line - // as the arrow. - if ( - !hasLeadingOwnLineComment(options.originalText, node.body) && - (isArrayOrTupleExpression(node.body) || - isObjectOrRecordExpression(node.body) || - node.body.type === "BlockStatement" || - isJsxElement(node.body) || - (body[0].label?.hug !== false && - (body[0].label?.embed || - isTemplateOnItsOwnLine(node.body, options.originalText))) || - node.body.type === "ArrowFunctionExpression" || - node.body.type === "DoExpression") - ) { - return group([...parts, " ", body]); - } - - // We handle sequence expressions as the body of arrows specially, - // so that the required parentheses end up on their own lines. - if (node.body.type === "SequenceExpression") { - return group([ - ...parts, - group([" (", indent([softline, body]), softline, ")"]), - ]); - } - - // if the arrow function is expanded as last argument, we are adding a - // level of indentation and need to add a softline to align the closing ) - // with the opening (, or if it's inside a JSXExpression (e.g. an attribute) - // we should align the expression's closing } with the line with the opening {. - const shouldAddSoftLine = - (args?.expandLastArg || path.parent.type === "JSXExpressionContainer") && - !hasComment(node); - - const printTrailingComma = - args?.expandLastArg && shouldPrintComma(options, "all"); - - // In order to avoid confusion between - // a => a ? a : a - // a <= a ? a : a - const shouldAddParens = - node.body.type === "ConditionalExpression" && - !startsWithNoLookaheadToken( - node.body, - (node) => node.type === "ObjectExpression" - ); - - return group([ - ...parts, - group([ - indent([ - line, - shouldAddParens ? ifBreak("", "(") : "", - body, - shouldAddParens ? ifBreak("", ")") : "", - ]), - shouldAddSoftLine - ? [ifBreak(printTrailingComma ? "," : ""), softline] - : "", - ]), - ]); -} - function canPrintParamsWithoutParens(node) { const parameters = getFunctionParameters(node); return ( @@ -530,10 +307,10 @@ function returnArgumentHasLeadingComment(options, argument) { export { printFunction, - printArrowFunction, printMethod, printReturnStatement, printThrowStatement, printMethodValue, + printReturnType, shouldPrintParamsWithoutParens, }; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index b635ba8c3..6566b52b1 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -65,11 +65,11 @@ import { import { printProperty } from "./print/property.js"; import { printFunction, - printArrowFunction, printMethod, printReturnStatement, printThrowStatement, } from "./print/function.js"; +import { printArrowFunction } from "./print/arrow-function.js"; import { printCallExpression } from "./print/call-expression.js"; import { printVariableDeclarator, diff --git a/tests/format/js/arrows/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/arrows/__snapshots__/jsfmt.spec.js.snap index 033886312..929639741 100644 --- a/tests/format/js/arrows/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/arrows/__snapshots__/jsfmt.spec.js.snap @@ -2334,6 +2334,27 @@ export const bem = block => data={data} /> +func(() => // comment +a); +func(() => () => // comment +a); +func(() => () => () => // comment +a); + +func(() => // comment +a ? b : c); +func(() => () => // comment +a ? b : c); +func(() => () => () => // comment +a ? b : c); + +func(() => // comment +(a , b , c)) +func(() => () => // comment +(a , b , c)) +func(() => () => () => // comment +(a , b , c)) + =====================================output===================================== /** * Curried function that ends with a BEM CSS Selector @@ -2367,6 +2388,59 @@ export const bem = data={data} />; +func( + () => + // comment + a, +); +func( + () => () => + // comment + a, +); +func( + () => () => () => + // comment + a, +); + +func(() => + // comment + a ? b : c, +); +func( + () => () => + // comment + a ? b : c, +); +func( + () => () => () => + // comment + a ? b : c, +); + +func( + () => + ( + // comment + a, b, c + ), +); +func( + () => () => + ( + // comment + a, b, c + ), +); +func( + () => () => () => + ( + // comment + a, b, c + ), +); + ================================================================================ `; @@ -2408,6 +2482,27 @@ export const bem = block => data={data} /> +func(() => // comment +a); +func(() => () => // comment +a); +func(() => () => () => // comment +a); + +func(() => // comment +a ? b : c); +func(() => () => // comment +a ? b : c); +func(() => () => () => // comment +a ? b : c); + +func(() => // comment +(a , b , c)) +func(() => () => // comment +(a , b , c)) +func(() => () => () => // comment +(a , b , c)) + =====================================output===================================== /** * Curried function that ends with a BEM CSS Selector @@ -2441,6 +2536,59 @@ export const bem = data={data} />; +func( + () => + // comment + a, +); +func( + () => () => + // comment + a, +); +func( + () => () => () => + // comment + a, +); + +func(() => + // comment + a ? b : c, +); +func( + () => () => + // comment + a ? b : c, +); +func( + () => () => () => + // comment + a ? b : c, +); + +func( + () => + ( + // comment + a, b, c + ), +); +func( + () => () => + ( + // comment + a, b, c + ), +); +func( + () => () => () => + ( + // comment + a, b, c + ), +); + ================================================================================ `; @@ -3666,6 +3814,370 @@ function f( ================================================================================ `; +exports[`currying-4.js - {"arrowParens":"always"} format 1`] = ` +====================================options===================================== +arrowParens: "always" +parsers: ["babel", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +Y(() => a ? b : c); + +Y(() => () => a ? b : c); + +Y(() => () => () => a ? b : c); + +Y(() => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +const x1 = (() => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x2 = (() => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x3 = (() => () => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +f((a) => (1, 2, 3) /* a */); +f((a) => ( + (b) => (1, 2, 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1, 2, 3) /* c */ + ) /* b */ +) /* a */); + +f((a) => (1 ? 2 : 3) /* a */); +f((a) => ( + (b) => (1 ? 2 : 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1 ? 2 : 3) /* c */ + ) /* b */ +) /* a */); + +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => test, +); +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => (test ? 1 : 2), +); + +=====================================output===================================== +Y(() => (a ? b : c)); + +Y(() => () => (a ? b : c)); + +Y(() => () => () => (a ? b : c)); + +Y(() => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +Y( + () => () => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +Y( + () => () => () => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +const x1 = () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +const x2 = () => () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +const x3 = () => () => () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +f((a) => (1, 2, 3) /* a */); +f((a) => (b) => (1, 2, 3) /* b */ /* a */); +f((a) => (b) => (c) => (1, 2, 3) /* c */ /* b */ /* a */); + +f((a) => (1 ? 2 : 3) /* a */); +f((a) => (b) => (1 ? 2 : 3) /* b */ /* a */); +f((a) => (b) => (c) => (1 ? 2 : 3) /* c */ /* b */ /* a */); + +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => + test, +); +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => + test ? 1 : 2, +); + +================================================================================ +`; + +exports[`currying-4.js - {"arrowParens":"avoid"} format 1`] = ` +====================================options===================================== +arrowParens: "avoid" +parsers: ["babel", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +Y(() => a ? b : c); + +Y(() => () => a ? b : c); + +Y(() => () => () => a ? b : c); + +Y(() => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +const x1 = (() => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x2 = (() => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x3 = (() => () => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +f((a) => (1, 2, 3) /* a */); +f((a) => ( + (b) => (1, 2, 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1, 2, 3) /* c */ + ) /* b */ +) /* a */); + +f((a) => (1 ? 2 : 3) /* a */); +f((a) => ( + (b) => (1 ? 2 : 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1 ? 2 : 3) /* c */ + ) /* b */ +) /* a */); + +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => test, +); +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => (test ? 1 : 2), +); + +=====================================output===================================== +Y(() => (a ? b : c)); + +Y(() => () => (a ? b : c)); + +Y(() => () => () => (a ? b : c)); + +Y(() => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +Y( + () => () => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +Y( + () => () => () => + longlonglonglonglonglonglonglonglonglongCondition + ? "Prettier is an opinionated code formatter." + : "Prettier takes your code and reprints it from scratch by taking the line length into account.", +); + +const x1 = () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +const x2 = () => () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +const x3 = () => () => () => [ + "The", + "green", + "dragon", + "liked", + "to", + "knit", + "sweaters", + "for", + "the", + "fluffy", + "clouds", + "in", + "the", + "sky.", +]; + +f(a => (1, 2, 3) /* a */); +f(a => b => (1, 2, 3) /* b */ /* a */); +f(a => b => c => (1, 2, 3) /* c */ /* b */ /* a */); + +f(a => (1 ? 2 : 3) /* a */); +f(a => b => (1 ? 2 : 3) /* b */ /* a */); +f(a => b => c => (1 ? 2 : 3) /* c */ /* b */ /* a */); + +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => + test, +); +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => + test ? 1 : 2, +); + +================================================================================ +`; + exports[`issue-1389-curry.js - {"arrowParens":"always"} format 1`] = ` ====================================options===================================== arrowParens: "always" @@ -3928,6 +4440,9 @@ foo(a => b, d) foo(a => (0, 1)); foo(a => b => (0, 1)); +() => ({} ? 1 : 2); +() => () => ({} ? 1 : 2); + =====================================output===================================== promise.then( (result) => result, @@ -3956,6 +4471,9 @@ foo((a) => b, d); foo((a) => (0, 1)); foo((a) => (b) => (0, 1)); +() => ({}) ? 1 : 2; +() => () => ({}) ? 1 : 2; + ================================================================================ `; @@ -3985,6 +4503,9 @@ foo(a => b, d) foo(a => (0, 1)); foo(a => b => (0, 1)); +() => ({} ? 1 : 2); +() => () => ({} ? 1 : 2); + =====================================output===================================== promise.then( result => result, @@ -4013,6 +4534,9 @@ foo(a => b, d); foo(a => (0, 1)); foo(a => b => (0, 1)); +() => ({}) ? 1 : 2; +() => () => ({}) ? 1 : 2; + ================================================================================ `; diff --git a/tests/format/js/arrows/comment.js b/tests/format/js/arrows/comment.js index 66b101229..fc0d1a061 100644 --- a/tests/format/js/arrows/comment.js +++ b/tests/format/js/arrows/comment.js @@ -28,3 +28,24 @@ export const bem = block => ) => {info.item.widget.missingProp}} data={data} /> + +func(() => // comment +a); +func(() => () => // comment +a); +func(() => () => () => // comment +a); + +func(() => // comment +a ? b : c); +func(() => () => // comment +a ? b : c); +func(() => () => () => // comment +a ? b : c); + +func(() => // comment +(a , b , c)) +func(() => () => // comment +(a , b , c)) +func(() => () => () => // comment +(a , b , c)) diff --git a/tests/format/js/arrows/currying-4.js b/tests/format/js/arrows/currying-4.js new file mode 100644 index 000000000..7aaaeef0a --- /dev/null +++ b/tests/format/js/arrows/currying-4.js @@ -0,0 +1,60 @@ +Y(() => a ? b : c); + +Y(() => () => a ? b : c); + +Y(() => () => () => a ? b : c); + +Y(() => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +Y(() => () => () => longlonglonglonglonglonglonglonglonglongCondition ? 'Prettier is an opinionated code formatter.' : 'Prettier takes your code and reprints it from scratch by taking the line length into account.') + +const x1 = (() => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x2 = (() => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +const x3 = (() => () => () => ['The', 'green', 'dragon', 'liked', 'to', 'knit', 'sweaters', 'for', 'the', 'fluffy', 'clouds', 'in', 'the', 'sky.']) + +f((a) => (1, 2, 3) /* a */); +f((a) => ( + (b) => (1, 2, 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1, 2, 3) /* c */ + ) /* b */ +) /* a */); + +f((a) => (1 ? 2 : 3) /* a */); +f((a) => ( + (b) => (1 ? 2 : 3) /* b */ +) /* a */); +f((a) => ( + (b) => ( + (c) => (1 ? 2 : 3) /* c */ + ) /* b */ +) /* a */); + +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => test, +); +a( + "", + "", + ({}) => + () => + () => + () => + () => + () => + () => (test ? 1 : 2), +); diff --git a/tests/format/js/arrows/parens.js b/tests/format/js/arrows/parens.js index 33fc3771e..895b76af1 100644 --- a/tests/format/js/arrows/parens.js +++ b/tests/format/js/arrows/parens.js @@ -16,3 +16,6 @@ foo(a => b, d) foo(a => (0, 1)); foo(a => b => (0, 1)); + +() => ({} ? 1 : 2); +() => () => ({} ? 1 : 2); diff --git a/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap index 527acc585..1123ad3db 100644 --- a/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/do/__snapshots__/jsfmt.spec.js.snap @@ -213,6 +213,20 @@ function foo() { } }; +() => ( ) => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; + +a =>b=> c => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; + =====================================output===================================== const envSpecific = { domain: do { @@ -272,5 +286,19 @@ function foo() { } }; +() => () => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; + +(a) => (b) => (c) => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; + ================================================================================ `; diff --git a/tests/format/js/do/do.js b/tests/format/js/do/do.js index e2b497108..8e368c09d 100644 --- a/tests/format/js/do/do.js +++ b/tests/format/js/do/do.js @@ -52,3 +52,17 @@ function foo() { obj[key]; } }; + +() => ( ) => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; + +a =>b=> c => do { + var obj = { foo: "bar", bar: "foo" }; + for (var key in obj) { + obj[key]; + } +}; diff --git a/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap index 1c5e24733..169ad7f3a 100644 --- a/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/jsx/jsx/__snapshots__/jsfmt.spec.js.snap @@ -312,6 +312,374 @@ const TodoList = ({ todos }) => ( ================================================================================ `; +exports[`arrow.js - {"singleQuote":false,"jsxSingleQuote":false} format 1`] = ` +====================================options===================================== +jsxSingleQuote: false +parsers: ["flow", "babel", "typescript"] +printWidth: 80 +singleQuote: false + | printWidth +=====================================input====================================== +() => ; +() => () => ; +() => () => () => ; + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() =>
Long long long long long, very very long text. And more text. Another text.
; +() => () =>
Long long long long long, very very long text. And more text. Another text.
; +() => () => () =>
Long long long long long, very very long text. And more text. Another text.
; + + + {We => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => currying => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + +=====================================output===================================== +() => ( + + + +); +() => () => ( + + + +); +() => () => () => ( + + + +); + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); + + + {(We) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + + {(We) => (love) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + + {(We) => (love) => (currying) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + +================================================================================ +`; + +exports[`arrow.js - {"singleQuote":false,"jsxSingleQuote":true} format 1`] = ` +====================================options===================================== +jsxSingleQuote: true +parsers: ["flow", "babel", "typescript"] +printWidth: 80 +singleQuote: false + | printWidth +=====================================input====================================== +() => ; +() => () => ; +() => () => () => ; + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() =>
Long long long long long, very very long text. And more text. Another text.
; +() => () =>
Long long long long long, very very long text. And more text. Another text.
; +() => () => () =>
Long long long long long, very very long text. And more text. Another text.
; + + + {We => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => currying => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + +=====================================output===================================== +() => ( + + + +); +() => () => ( + + + +); +() => () => () => ( + + + +); + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); + + + {(We) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + + {(We) => (love) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + + {(We) => (love) => (currying) => + "The purple monkey danced with a tambourine made of cheese." + + "The robot chef cooked a cake that tasted like rainbows." + + "The talking pineapple sang a lullaby to the sleepy giraffe." + } +; + +================================================================================ +`; + +exports[`arrow.js - {"singleQuote":true,"jsxSingleQuote":false} format 1`] = ` +====================================options===================================== +jsxSingleQuote: false +parsers: ["flow", "babel", "typescript"] +printWidth: 80 +singleQuote: true + | printWidth +=====================================input====================================== +() => ; +() => () => ; +() => () => () => ; + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() =>
Long long long long long, very very long text. And more text. Another text.
; +() => () =>
Long long long long long, very very long text. And more text. Another text.
; +() => () => () =>
Long long long long long, very very long text. And more text. Another text.
; + + + {We => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => currying => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + +=====================================output===================================== +() => ( + + + +); +() => () => ( + + + +); +() => () => () => ( + + + +); + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); + + + {(We) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + + {(We) => (love) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + + {(We) => (love) => (currying) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + +================================================================================ +`; + +exports[`arrow.js - {"singleQuote":true,"jsxSingleQuote":true} format 1`] = ` +====================================options===================================== +jsxSingleQuote: true +parsers: ["flow", "babel", "typescript"] +printWidth: 80 +singleQuote: true + | printWidth +=====================================input====================================== +() => ; +() => () => ; +() => () => () => ; + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() =>
Long long long long long, very very long text. And more text. Another text.
; +() => () =>
Long long long long long, very very long text. And more text. Another text.
; +() => () => () =>
Long long long long long, very very long text. And more text. Another text.
; + + + {We => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => currying => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + +=====================================output===================================== +() => ( + + + +); +() => () => ( + + + +); +() => () => () => ( + + + +); + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); +() => () => () => ( +
+ Long long long long long, very very long text. And more text. Another text. +
+); + + + {(We) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + + {(We) => (love) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + + {(We) => (love) => (currying) => + 'The purple monkey danced with a tambourine made of cheese.' + + 'The robot chef cooked a cake that tasted like rainbows.' + + 'The talking pineapple sang a lullaby to the sleepy giraffe.' + } +; + +================================================================================ +`; + exports[`attr-comments.js - {"singleQuote":false,"jsxSingleQuote":false} format 1`] = ` ====================================options===================================== jsxSingleQuote: false diff --git a/tests/format/jsx/jsx/arrow.js b/tests/format/jsx/jsx/arrow.js new file mode 100644 index 000000000..25d771dec --- /dev/null +++ b/tests/format/jsx/jsx/arrow.js @@ -0,0 +1,21 @@ +() => ; +() => () => ; +() => () => () => ; + +() =>
Some text here
; +() => () =>
Some text here
; +() => () => () =>
Some text here
; + +() =>
Long long long long long, very very long text. And more text. Another text.
; +() => () =>
Long long long long long, very very long text. And more text. Another text.
; +() => () => () =>
Long long long long long, very very long text. And more text. Another text.
; + + + {We => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +; + + {We => love => currying => 'The purple monkey danced with a tambourine made of cheese.' + 'The robot chef cooked a cake that tasted like rainbows.' + 'The talking pineapple sang a lullaby to the sleepy giraffe.'} +;