Skip to content

Commit

Permalink
Wrap Stateless JSX Arrow Functions and Assignment in Parens (fixes pa…
Browse files Browse the repository at this point in the history
…rt of #73)
  • Loading branch information
rattrayalex authored and jlongster committed Jan 13, 2017
1 parent 31a07fe commit d91a28e
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 82 deletions.
140 changes: 76 additions & 64 deletions src/printer.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,12 +343,20 @@ function genericPrintNoParens(path, options, print) {

parts.push(" =>");

const body = path.call(print, "body");
const collapsed = concat([ concat(parts), " ", body ]);

if (n.body.type === "JSXElement") {
return group(collapsed);
}

return conditionalGroup([
concat([ concat(parts), " ", path.call(print, "body") ]),
concat([ concat(parts),
indent(options.tabWidth,
concat([line, path.call(print, "body")]))])
])
collapsed,
concat([
concat(parts),
indent(options.tabWidth, concat([ line, body ])),
]),
]);
case "MethodDefinition":
if (n.static) {
parts.push("static ");
Expand Down Expand Up @@ -553,22 +561,8 @@ function genericPrintNoParens(path, options, print) {
case "ReturnStatement":
parts.push("return");

var arg = path.call(print, "argument");

if (n.argument) {
if (
namedTypes.JSXElement && namedTypes.JSXElement.check(n.argument) &&
hasHardLine(arg)
) {
parts.push(
" (",
indent(options.tabWidth, concat([ hardline, arg ])),
hardline,
")"
);
} else {
parts.push(" ", arg);
}
parts.push(" ", path.call(print, "argument"));
}

parts.push(";");
Expand Down Expand Up @@ -1091,50 +1085,7 @@ function genericPrintNoParens(path, options, print) {
"}"
]));
case "JSXElement":
var openingLines = path.call(print, "openingElement");

if (n.openingElement.selfClosing) {
assert.ok(!n.closingElement);

return openingLines;
}

var children = [];

path.map(
function(childPath) {
var child = childPath.getValue();

if (
namedTypes.Literal.check(child) && typeof child.value === "string"
) {
if (/\S/.test(child.value)) {
const beginBreak = child.value.match(/^\s*\n/);
const endBreak = child.value.match(/\n\s*$/);

children.push(
beginBreak ? hardline : "",
child.value.replace(/^\s+|\s+$/g, ""),
endBreak ? hardline : ""
);
} else if (/\n/.test(child.value)) {
children.push(hardline);
}
} else {
children.push(print(childPath));
}
},
"children"
);

var mostChildren = children.slice(0, -1);
var closingLines = path.call(print, "closingElement");
return concat([
openingLines,
indent(options.tabWidth, concat(mostChildren)),
util.getLast(children) || "",
closingLines
]);
return printJSXElement(path, options, print);
case "JSXOpeningElement":
return group(
concat([
Expand Down Expand Up @@ -2053,6 +2004,67 @@ function printMemberChain(node, options, print) {
}
}

function printJSXElement(path, options, print) {
const n = path.getValue();
var openingLines = path.call(print, "openingElement");

let elem;
if (n.openingElement.selfClosing) {
assert.ok(!n.closingElement);

elem = openingLines;
} else {
var children = [];

path.map(
function(childPath) {
var child = childPath.getValue();

if (
namedTypes.Literal.check(child) && typeof child.value === "string"
) {
if (/\S/.test(child.value)) {
const beginBreak = child.value.match(/^\s*\n/);
const endBreak = child.value.match(/\n\s*$/);

children.push(
beginBreak ? hardline : "",
child.value.replace(/^\s+|\s+$/g, ""),
endBreak ? hardline : ""
);
} else if (/\n/.test(child.value)) {
children.push(hardline);
}
} else {
children.push(print(childPath));
}
},
"children"
);

var mostChildren = children.slice(0, -1);
var closingLines = path.call(print, "closingElement");
elem = concat([
openingLines,
indent(options.tabWidth, concat(mostChildren)),
util.getLast(children) || "",
closingLines
]);
}

const parent = path.getParentNode();
if (!parent || parent.type === "JSXElement" || parent.type === "ExpressionStatement") {
return elem;
}

return multilineGroup(concat([
ifBreak("("),
indent(options.tabWidth, concat([ softline, elem ])),
softline,
ifBreak(")"),
]));
}

function adjustClause(clause, options, forceSpace) {
if (isCurlyBracket(clause) || forceSpace) {
return concat([ " ", clause ]);
Expand Down
6 changes: 3 additions & 3 deletions tests/jsx_intrinsics.builtin/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ var c: React.Element<any> = <div not_a_real_attr=\"asdf\" />;
// different attributes.
var d: React.Element<{ doesntmatch: string }> = <div not_a_real_attr=\"asdf\" />;
// No error as long as expectations are consistent, though.
var e: React.Element<{
not_a_real_attr: string
}> = <div not_a_real_attr=\"asdf\" />;
var e: React.Element<{ not_a_real_attr: string }> = (
<div not_a_real_attr=\"asdf\" />
);
"
`;
Expand Down
6 changes: 3 additions & 3 deletions tests/jsx_intrinsics.custom/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ var b: React.Element<{ prop1: string }> = <CustomComponent prop=\"asdf\" />;
<div id={42} />;
// Error: (\`id\` prop) number ~> string
var c: React.Element<{ id: string }> = <div id=\"asdf\" />;
var d: React.Element<{
id: number
}> = <div id=\"asdf\" />; // Error: Props<{id:string}> ~> Props<{id:number}>
var d: React.Element<{ id: number }> = (
<div id=\"asdf\" />
); // Error: Props<{id:string}> ~> Props<{id:number}>
"
`;

Expand Down
127 changes: 115 additions & 12 deletions tests/prettier/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@ function fn() {
"
`;

exports[`test method-chain.js 1`] = `
"method().then(x => x)
[\"abc\"](x => x)
[abc](x => x);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method()
.then(x => x)
[\"abc\"](x => x)
[abc](x => x);
"
`;

exports[`test exports.js 1`] = `
"export { value1, value2 as value2_renamed, value3, value4 as value4_renamed, value5 } from \"exports\";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -38,6 +26,121 @@ export {
"
`;

exports[`test jsx-multiline-assign.js 1`] = `
"const comp1 = (
<div style={styles} key=\"something\">
Keep the wrapping parens.
</div>
);
const comp2 = <div style={styles} key=\"something\">
Create wrapping parens.
</div>;
comp2A = <div style={styles} key=\"something\">
Create wrapping parens.
</div>;
const comp3 = <div style={styles} key=\"something\">Bump to next line without parens</div>;
const comp4 = <div style={styles} key=\"something\">Create wrapping parens and indent <strong>all the things</strong>.</div>;
const comp5 = <div>Keep it on one line.</div>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const comp1 = (
<div style={styles} key=\"something\">
Keep the wrapping parens.
</div>
);
const comp2 = (
<div style={styles} key=\"something\">
Create wrapping parens.
</div>
);
comp2A = (
<div style={styles} key=\"something\">
Create wrapping parens.
</div>
);
const comp3 = (
<div style={styles} key=\"something\">Bump to next line without parens</div>
);
const comp4 = (
<div style={
styles
} key=\"something\">Create wrapping parens and indent<strong>all the things</strong>.</div>
);
const comp5 = <div>Keep it on one line.</div>;
"
`;
exports[`test jsx-stateless-arrow-fn.js 1`] = `
"const render1 = ({ styles }) => (
<div style={styles} key=\"something\">
Keep the wrapping parens. Put each key on its own line.
</div>
);
const render2 = ({ styles }) => <div style={styles} key=\"something\">
Create wrapping parens.
</div>;
const render3 = ({ styles }) => <div style={styles} key=\"something\">Bump to next line without parens</div>;
const render4 = ({ styles }) => <div style={styles} key=\"something\">Create wrapping parens and indent <strong>all the things</strong>.</div>;
const render5 = ({ styles }) => <div>Keep it on one line.</div>;
const notJSX = (aaaaaaaaaaaaaaaaa, bbbbbbbbbbb) => this.someLongCallWithParams(aaaaaa, bbbbbbb).anotherLongCallWithParams(cccccccccccc, dddddddddddddddddddddd)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const render1 = ({ styles }) => (
<div style={styles} key=\"something\">
Keep the wrapping parens. Put each key on its own line.
</div>
);
const render2 = ({ styles }) => (
<div style={styles} key=\"something\">
Create wrapping parens.
</div>
);
const render3 = ({ styles }) => (
<div style={styles} key=\"something\">Bump to next line without parens</div>
);
const render4 = ({ styles }) => (
<div style={
styles
} key=\"something\">Create wrapping parens and indent<strong>all the things</strong>.</div>
);
const render5 = ({ styles }) => <div>Keep it on one line.</div>;
const notJSX = (aaaaaaaaaaaaaaaaa, bbbbbbbbbbb) =>
this
.someLongCallWithParams(aaaaaa, bbbbbbb)
.anotherLongCallWithParams(cccccccccccc, dddddddddddddddddddddd);
"
`;
exports[`test method-chain.js 1`] = `
"method().then(x => x)
[\"abc\"](x => x)
[abc](x => x);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
method()
.then(x => x)
[\"abc\"](x => x)
[abc](x => x);
"
`;
exports[`test optional-type-name.js 1`] = `
"type Foo = (any) => string
Expand Down
19 changes: 19 additions & 0 deletions tests/prettier/jsx-multiline-assign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const comp1 = (
<div style={styles} key="something">
Keep the wrapping parens.
</div>
);

const comp2 = <div style={styles} key="something">
Create wrapping parens.
</div>;

comp2A = <div style={styles} key="something">
Create wrapping parens.
</div>;

const comp3 = <div style={styles} key="something">Bump to next line without parens</div>;

const comp4 = <div style={styles} key="something">Create wrapping parens and indent <strong>all the things</strong>.</div>;

const comp5 = <div>Keep it on one line.</div>;
17 changes: 17 additions & 0 deletions tests/prettier/jsx-stateless-arrow-fn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const render1 = ({ styles }) => (
<div style={styles} key="something">
Keep the wrapping parens. Put each key on its own line.
</div>
);

const render2 = ({ styles }) => <div style={styles} key="something">
Create wrapping parens.
</div>;

const render3 = ({ styles }) => <div style={styles} key="something">Bump to next line without parens</div>;

const render4 = ({ styles }) => <div style={styles} key="something">Create wrapping parens and indent <strong>all the things</strong>.</div>;

const render5 = ({ styles }) => <div>Keep it on one line.</div>;

const notJSX = (aaaaaaaaaaaaaaaaa, bbbbbbbbbbb) => this.someLongCallWithParams(aaaaaa, bbbbbbb).anotherLongCallWithParams(cccccccccccc, dddddddddddddddddddddd)

0 comments on commit d91a28e

Please sign in to comment.