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======================================
+() =>