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 }) => (
{" "} hello
- ; + +); -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 && +
+
+
+
} +
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
{a || "b"}
; + +
{a && "b"}
; + +
+ {a || ( + + )} +
; + +
+ {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 = () =>
+
+ bla bla bla +
+
; + +const NonBreakingArrowExpressionWBody = () => { + return ( +
+ ); +}; + +const BreakingArrowExpressionWBody = () => { + return
+
+ bla bla bla +
+
+}; + +const NonBreakingFunction = function() { + return ( +
+ ); +}; + +const BreakingFunction = function() { + return
+
+ bla bla bla +
+
+}; + +class NonBreakingClass extends React.component { + render() { + return ( +
+ ); + } +} + +class BreakingClass extends React.component { + render() { + return
+
+ bla bla bla +
+
; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const NonBreakingArrowExpression = () =>
; + +const BreakingArrowExpression = () => ( +
+
bla bla bla
+
+); + +const NonBreakingArrowExpressionWBody = () => { + return
; +}; + +const BreakingArrowExpressionWBody = () => { + return ( +
+
bla bla bla
+
+ ); +}; + +const NonBreakingFunction = function() { + return
; +}; + +const BreakingFunction = function() { + return ( +
+
bla bla bla
+
+ ); +}; + +class NonBreakingClass extends React.component { + render() { + return
; + } +} + +class BreakingClass extends React.component { + render() { + return ( +
+
bla bla bla
+
+ ); + } +} + +`; + 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 && } +
; + +
+ {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 = () =>
+
+ bla bla bla +
+
; + +const NonBreakingArrowExpressionWBody = () => { + return ( +
+ ); +}; + +const BreakingArrowExpressionWBody = () => { + return
+
+ bla bla bla +
+
+}; + +const NonBreakingFunction = function() { + return ( +
+ ); +}; + +const BreakingFunction = function() { + return
+
+ bla bla bla +
+
+}; + +class NonBreakingClass extends React.component { + render() { + return ( +
+ ); + } +} + +class BreakingClass extends React.component { + render() { + return
+
+ bla bla bla +
+
; + } +} 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(