diff --git a/src/printer.js b/src/printer.js
index 8e8acbae..aed562ad 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -462,6 +462,7 @@ function genericPrintNoParens(path, options, print, args) {
(n.body.type === "ArrayExpression" ||
n.body.type === "ObjectExpression" ||
n.body.type === "BlockStatement" ||
+ n.body.type === "JSXElement" ||
isTemplateOnItsOwnLine(n.body, options.originalText) ||
n.body.type === "ArrowFunctionExpression")
) {
@@ -1224,21 +1225,81 @@ function genericPrintNoParens(path, options, print, args) {
return concat(parts);
case "ConditionalExpression": {
const parent = path.getParentNode();
- const printed = concat([
- line,
- "? ",
- n.consequent.type === "ConditionalExpression" ? ifBreak("", "(") : "",
- align(2, path.call(print, "consequent")),
- n.consequent.type === "ConditionalExpression" ? ifBreak("", ")") : "",
- line,
- ": ",
- align(2, path.call(print, "alternate"))
- ]);
+ let forceNoIndent = false;
+ let firstNonConditionalParent;
+ let i = 0;
+ do {
+ firstNonConditionalParent = path.getParentNode(i);
+ i++;
+ } while (
+ firstNonConditionalParent &&
+ firstNonConditionalParent.type === "ConditionalExpression"
+ );
+
+ // Conditional expressions are intentionally formatted differently when
+ // they appear within JSX or contain JSX. Instead of:
+ //
+ // test
+ // ? consequent
+ // : alternate;
+ //
+ // They look like:
+ //
+ // test ? (
+ // consequent
+ // ) : (
+ // alternate
+ // )
+ //
+ if (
+ n.consequent.type === "JSXElement" ||
+ n.alternate.type === "JSXElement" ||
+ parent.type === "JSXExpressionContainer" ||
+ firstNonConditionalParent.type === "JSXExpressionContainer"
+ ) {
+ forceNoIndent = true;
+
+ // Even though they don't need parens, we wrap (almost) everything in
+ // parens when using ?: within JSX, because the parens are analagous to
+ // curly braces in an if statement.
+ const wrap = node =>
+ concat(["(", indent(concat([hardline, node])), hardline, ")"]);
+
+ const NO_WRAP_TYPES = {
+ ConditionalExpression: true,
+ // JSXElements are always wrapped when they're the child of a ConditionalExpression.
+ JSXElement: true
+ };
+
+ parts.push(
+ " ? ",
+ NO_WRAP_TYPES[n.consequent.type]
+ ? path.call(print, "consequent")
+ : wrap(path.call(print, "consequent")),
+ " : ",
+ NO_WRAP_TYPES[n.alternate.type]
+ ? path.call(print, "alternate")
+ : wrap(path.call(print, "alternate"))
+ );
+ } else {
+ parts.push(
+ line,
+ "? ",
+ n.consequent.type === "ConditionalExpression" ? ifBreak("", "(") : "",
+ align(2, path.call(print, "consequent")),
+ n.consequent.type === "ConditionalExpression" ? ifBreak("", ")") : "",
+ line,
+ ": ",
+ align(2, path.call(print, "alternate"))
+ );
+ }
return group(
concat([
path.call(print, "test"),
- parent.type === "ConditionalExpression" ? printed : indent(printed)
+ parent.type === "ConditionalExpression" || forceNoIndent
+ ? concat(parts)
+ : indent(concat(parts))
])
);
}
@@ -4018,15 +4079,20 @@ function maybeWrapJSXElementInParens(path, elem) {
JSXElement: true,
JSXExpressionContainer: true,
ExpressionStatement: true,
- CallExpression: true,
- ConditionalExpression: true,
- LogicalExpression: true,
- ArrowFunctionExpression: true
+ CallExpression: true
};
if (NO_WRAP_PARENTS[parent.type]) {
return elem;
}
+ const ALWAYS_WRAP_PARENTS = {
+ LogicalExpression: true,
+ ConditionalExpression: true
+ };
+ if (ALWAYS_WRAP_PARENTS[parent.type]) {
+ return concat(["(", indent(concat([hardline, elem])), hardline, ")"]);
+ }
+
return group(
concat([
ifBreak("("),
@@ -4060,6 +4126,10 @@ function shouldInlineLogicalExpression(node) {
return true;
}
+ if (node.right.type === "JSXElement") {
+ return true;
+ }
+
return false;
}
diff --git a/tests/jsx-stateless-arrow-fn/__snapshots__/jsfmt.spec.js.snap b/tests/jsx-stateless-arrow-fn/__snapshots__/jsfmt.spec.js.snap
index 8ab45ff1..c88f5ef2 100644
--- a/tests/jsx-stateless-arrow-fn/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/jsx-stateless-arrow-fn/__snapshots__/jsfmt.spec.js.snap
@@ -11,7 +11,7 @@ const render2 = ({ styles }) =>
Create wrapping parens.
;
-const render3 = ({ styles }) => Bump to next line without parens
;
+const render3 = ({ styles }) => Create wrapping parens.
;
const render4 = ({ styles }) => Create wrapping parens and indent all the things.
;
@@ -75,29 +75,33 @@ const renderTernary = (props) =>
{props.showTheOtherThing ? I am here!!
: null}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-const render1 = ({ styles }) =>
+const render1 = ({ styles }) => (
Keep the wrapping parens. Put each key on its own line.
-
;
+
+);
-const render2 = ({ styles }) =>
+const render2 = ({ styles }) => (
Create wrapping parens.
-
;
+
+);
-const render3 = ({ styles }) =>
+const render3 = ({ styles }) => (
- Bump to next line without parens
-
;
+ Create wrapping parens.
+
+);
-const render4 = ({ styles }) =>
+const render4 = ({ styles }) => (
Create wrapping parens and indent all the things.
-
;
+
+);
const render5 = ({ styles }) => Keep it on one line.
;
-const render6 = ({ styles }) =>
+const render6 = ({ styles }) => (
- ;
+
+);
-const render7 = () =>
+const render7 = () => (
Dont break each elem onto its own line.
-
;
+
+);
-const render7A = () =>
+const render7A = () => (
;
+
+);
-const render7B = () =>
+const render7B = () => (
{" "}
@@ -159,15 +166,18 @@ const render7B = () =>
Dont break plz
-
;
+
+);
const render8 = props => {props.text}
;
-const render9 = props =>
- {props.looooooooooooooooooooooooooooooong_text}
;
-const render10 = props =>
+const render9 = props => (
+ {props.looooooooooooooooooooooooooooooong_text}
+);
+const render10 = props => (
{props.even_looooooooooooooooooooooooooooooooooooooooooonger_contents}
-
;
+
+);
const notJSX = (aaaaaaaaaaaaaaaaa, bbbbbbbbbbb) =>
this.someLongCallWithParams(aaaaaa, bbbbbbb).anotherLongCallWithParams(
@@ -186,7 +196,7 @@ React.render(
document.querySelector("#react-root")
);
-const renderTernary = props =>
+const renderTernary = props => (
size="large"
submitLabel="Sign in with Google"
>
- {props.showTheThing
- ?
- Hello world
-
- : "hello " + "howdy! "}
- {props.showTheThing
- ?
- Hello world
-
- : null}
- {props.showTheThing
- ? null
- :
- Hello world
- }
- {props.showTheOtherThing ? I am here
: }
- {props.showTheOtherThing ? I am here!!
: null}
- ;
+ {props.showTheThing ? (
+
+ Hello world
+
+ ) : (
+ "hello " + "howdy! "
+ )}
+ {props.showTheThing ? (
+
+ Hello world
+
+ ) : (
+ null
+ )}
+ {props.showTheThing ? (
+ null
+ ) : (
+
+ Hello world
+
+ )}
+ {props.showTheOtherThing ? (
+ I am here
+ ) : (
+
+ )}
+ {props.showTheOtherThing ? (
+ I am here!!
+ ) : (
+ null
+ )}
+
+);
`;
diff --git a/tests/jsx-stateless-arrow-fn/test.js b/tests/jsx-stateless-arrow-fn/test.js
index 20bde80b..2099e5bc 100644
--- a/tests/jsx-stateless-arrow-fn/test.js
+++ b/tests/jsx-stateless-arrow-fn/test.js
@@ -8,7 +8,7 @@ const render2 = ({ styles }) =>
Create wrapping parens.
;
-const render3 = ({ styles }) => Bump to next line without parens
;
+const render3 = ({ styles }) => Create wrapping parens.
;
const render4 = ({ styles }) => Create wrapping parens and indent all the things.
;
diff --git a/tests/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
index d89b311e..b747d29d 100644
--- a/tests/jsx/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/jsx/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`conditional-expression.js 1`] = `
+
+ {isCool ? "cool" : "very nice"}
+
;
+
+// should get formatted JSX style
+const a = something ? "string" : ;
+
+// should not get formatted JSX style
+const a = something ? "string" : "other string";
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ "why"
+ ) : yes ? (
+ "it"
+ ) : is ? (
+ "!"
+ ) : (
+ ":)"
+ )}
+
;
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ why
+ ) : yes ? (
+ it
+ ) : is ? (
+ !
+ ) : (
+ :)
+ )}
+
;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ {isCool ? (
+ "cool"
+ ) : (
+ "very nice"
+ )}
+
;
+
+// should get formatted JSX style
+const a = something ? (
+ "string"
+) : (
+
+);
+
+// should not get formatted JSX style
+const a = something ? "string" : "other string";
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ "why"
+ ) : yes ? (
+ "it"
+ ) : is ? (
+ "!"
+ ) : (
+ ":)"
+ )}
+
;
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ why
+ ) : yes ? (
+ it
+ ) : is ? (
+ !
+ ) : (
+ :)
+ )}
+
;
+
+`;
+
exports[`expression.js 1`] = `
;
- {() =>
-
-
- }
-;
-
-
- {items.map(item =>
+ {() => (
)}
;
+
+ {items.map(item => (
+
+
+
+ ))}
+;
+
{function() {
return (
@@ -193,32 +273,35 @@ exports[`hug.js 1`] = `
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- {__DEV__
- ? this.renderDevApp()
- :
- {routes.map(route =>
-
- )}
-
}
-
;
-
-
- {__DEV__ &&
+ {__DEV__ ? (
+ this.renderDevApp()
+ ) : (
- {routes.map(route =>
+ {routes.map(route => (
- )}
-
}
+ ))}
+
+ )}
+;
+
+
+ {__DEV__ && (
+
+ {routes.map(route => (
+
+ ))}
+
+ )}
;
@@ -228,6 +311,58 @@ exports[`hug.js 1`] = `
`;
+exports[`logical-expression.js 1`] = `
+
+ {a || "b"}
+
;
+
+
+ {a && "b"}
+
;
+
+
+ {a || }
+
;
+
+
+ {a && }
+
;
+
+
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
{a || "b"}
;
+
+
{a && "b"}
;
+
+
+ {a || (
+
+ )}
+
;
+
+
+ {a && (
+
+ )}
+
;
+
+
;
+
+`;
+
exports[`object-property.js 1`] = `
const tabs = [
{
@@ -330,6 +465,111 @@ exports[`quotes.js 1`] = `
`;
+exports[`return-statement.js 1`] = `
+const NonBreakingArrowExpression = () =>
;
+
+const BreakingArrowExpression = () =>
;
+
+const NonBreakingArrowExpressionWBody = () => {
+ return (
+
+ );
+};
+
+const BreakingArrowExpressionWBody = () => {
+ return
+};
+
+const NonBreakingFunction = function() {
+ return (
+
+ );
+};
+
+const BreakingFunction = function() {
+ return
+};
+
+class NonBreakingClass extends React.component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+class BreakingClass extends React.component {
+ render() {
+ return
;
+ }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const NonBreakingArrowExpression = () =>
;
+
+const BreakingArrowExpression = () => (
+
+);
+
+const NonBreakingArrowExpressionWBody = () => {
+ return
;
+};
+
+const BreakingArrowExpressionWBody = () => {
+ return (
+
+ );
+};
+
+const NonBreakingFunction = function() {
+ return
;
+};
+
+const BreakingFunction = function() {
+ return (
+
+ );
+};
+
+class NonBreakingClass extends React.component {
+ render() {
+ return
;
+ }
+}
+
+class BreakingClass extends React.component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+`;
+
exports[`spacing.js 1`] = `
const Labels = {
label1: (
diff --git a/tests/jsx/conditional-expression.js b/tests/jsx/conditional-expression.js
new file mode 100644
index 00000000..1ae25181
--- /dev/null
+++ b/tests/jsx/conditional-expression.js
@@ -0,0 +1,33 @@
+
+ {isCool ? "cool" : "very nice"}
+
;
+
+// should get formatted JSX style
+const a = something ? "string" :
;
+
+// should not get formatted JSX style
+const a = something ? "string" : "other string";
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ "why"
+ ) : yes ? (
+ "it"
+ ) : is ? (
+ "!"
+ ) : (
+ ":)"
+ )}
+
;
+
+
+ {reallyLongChainOfConditionalExpressions ? (
+ why
+ ) : yes ? (
+ it
+ ) : is ? (
+ !
+ ) : (
+ :)
+ )}
+
;
diff --git a/tests/jsx/logical-expression.js b/tests/jsx/logical-expression.js
new file mode 100644
index 00000000..07ec8bda
--- /dev/null
+++ b/tests/jsx/logical-expression.js
@@ -0,0 +1,23 @@
+
+ {a || "b"}
+
;
+
+
+ {a && "b"}
+
;
+
+
+ {a || }
+
;
+
+
+ {a && }
+
;
+
+
;
\ No newline at end of file
diff --git a/tests/jsx/return-statement.js b/tests/jsx/return-statement.js
new file mode 100644
index 00000000..8afae7ce
--- /dev/null
+++ b/tests/jsx/return-statement.js
@@ -0,0 +1,53 @@
+const NonBreakingArrowExpression = () =>
;
+
+const BreakingArrowExpression = () =>
;
+
+const NonBreakingArrowExpressionWBody = () => {
+ return (
+
+ );
+};
+
+const BreakingArrowExpressionWBody = () => {
+ return
+};
+
+const NonBreakingFunction = function() {
+ return (
+
+ );
+};
+
+const BreakingFunction = function() {
+ return
+};
+
+class NonBreakingClass extends React.component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+class BreakingClass extends React.component {
+ render() {
+ return
;
+ }
+}
diff --git a/tests/last_argument_expansion/__snapshots__/jsfmt.spec.js.snap b/tests/last_argument_expansion/__snapshots__/jsfmt.spec.js.snap
index 990e245e..a2d888b2 100644
--- a/tests/last_argument_expansion/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/last_argument_expansion/__snapshots__/jsfmt.spec.js.snap
@@ -109,19 +109,21 @@ true
]
});
-true
- ? test({
- a: 1
- })
- :
;
+true ? (
+ test({
+ a: 1
+ })
+) : (
+
+);
`;
@@ -282,11 +284,11 @@ const els = items.map(item => (
));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-const els = items.map(item =>
+const els = items.map(item => (
{children}
-);
+));
`;
diff --git a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
index f7d4023b..9c958c8d 100644
--- a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap
@@ -17,9 +17,11 @@ room = room.map((row, rowIndex) => (
const funnelSnapshotCard =
(report === MY_OVERVIEW && !ReportGK.xar_metrics_active_capitol_v2) ||
(report === COMPANY_OVERVIEW &&
- !ReportGK.xar_metrics_active_capitol_v2_company_metrics)
- ?
- : null;
+ !ReportGK.xar_metrics_active_capitol_v2_company_metrics) ? (
+
+ ) : (
+ null
+ );
room = room.map((row, rowIndex) =>
row.map(
@@ -52,9 +54,11 @@ room = room.map((row, rowIndex) => (
const funnelSnapshotCard =
(report === MY_OVERVIEW && !ReportGK.xar_metrics_active_capitol_v2) ||
(report === COMPANY_OVERVIEW &&
- !ReportGK.xar_metrics_active_capitol_v2_company_metrics)
- ?
- : null;
+ !ReportGK.xar_metrics_active_capitol_v2_company_metrics) ? (
+
+ ) : (
+ null
+ );
room = room.map((row, rowIndex) =>
row.map(