Allow JSX lines to be recombined (#1831)

* Allow JSX lines to be recombined

* Add test case for `<br />` breaking expression

* Fix issue where JSX whitespace could be dropped

* Newlines were incorrectly being preserved when JSX contained text
master
Karl O'Keeffe 2017-06-22 17:19:44 +01:00 committed by Christopher Chedeau
parent 39954f7951
commit 0cc0ebc3bc
17 changed files with 555 additions and 290 deletions

View File

@ -1565,7 +1565,7 @@ function genericPrintNoParens(path, options, print, args) {
if (n.value) {
let res;
if (isStringLiteral(n.value)) {
const value = n.value.extra ? n.value.extra.raw : n.value.raw;
const value = rawText(n.value);
res = '"' + value.slice(1, -1).replace(/"/g, "&quot;") + '"';
} else {
res = path.call(print, "value");
@ -3650,6 +3650,7 @@ const jsxWhitespaceChars = " \n\r\t";
const containsNonJsxWhitespaceRegex = new RegExp(
"[^" + jsxWhitespaceChars + "]"
);
const matchJsxWhitespaceRegex = new RegExp("([" + jsxWhitespaceChars + "]+)");
// Meaningful if it contains non-whitespace characters,
// or it contains whitespace without a new line.
@ -3661,18 +3662,24 @@ function isMeaningfulJSXText(node) {
);
}
// Detect an expression node representing `{" "}`
function isJSXWhitespaceExpression(node) {
return (
node.type === "JSXExpressionContainer" &&
isLiteral(node.expression) &&
node.expression.value === " "
);
}
// JSX Children are strange, mostly for two reasons:
// 1. JSX reads newlines into string values, instead of skipping them like JS
// 2. up to one whitespace between elements within a line is significant,
// but not between lines.
//
// So for one thing, '\n' needs to be parsed out of string literals
// and turned into hardlines (with string boundaries otherwise using softline)
//
// For another, leading, trailing, and lone whitespace all need to
// Leading, trailing, and lone whitespace all need to
// turn themselves into the rather ugly `{' '}` when breaking.
//
// Finally we print JSX using the `fill` doc primitive.
// We print JSX using the `fill` doc primitive.
// This requires that we give it an array of alternating
// content and whitespace elements.
// To ensure this we add dummy `""` content elements as needed.
@ -3683,101 +3690,105 @@ function printJSXChildren(path, options, print, jsxWhitespace) {
// using `map` instead of `each` because it provides `i`
path.map((childPath, i) => {
const child = childPath.getValue();
if (isLiteral(child) && typeof child.value === "string") {
const value = child.raw || child.extra.raw;
if (isLiteral(child)) {
const text = rawText(child);
// Contains a non-whitespace character
if (/[^ \n\r\t]/.test(value)) {
// treat each line of text as its own entity
value.split(/(\r?\n\s*)/).forEach(textLine => {
const newlines = textLine.match(/\n/g);
if (newlines) {
children.push("");
children.push(hardline);
if (isMeaningfulJSXText(child)) {
const words = text.split(matchJsxWhitespaceRegex);
// allow one extra newline
if (newlines.length > 1) {
children.push("");
children.push(hardline);
}
return;
}
if (textLine.length === 0) {
return;
}
const beginSpace = /^[ \n\r\t]+/.test(textLine);
if (beginSpace) {
children.push("");
children.push(jsxWhitespace);
}
const stripped = textLine.replace(/^[ \n\r\t]+|[ \n\r\t]+$/g, "");
// Split text into words separated by "line"s.
stripped.split(/([ \n\r\t]+)/).forEach(word => {
const space = /[ \n\r\t]+/.test(word);
if (space) {
children.push(line);
} else {
children.push(word);
}
});
const endSpace = /[ \n\r\t]+$/.test(textLine);
if (endSpace) {
children.push(jsxWhitespace);
// Starts with whitespace
if (words[0] === "") {
children.push("");
words.shift();
if (/\n/.test(words[0])) {
children.push(softline);
} else {
// Ideally this would be a `softline` to allow a break between
// tags and text.
// Unfortunately Facebook have a custom translation pipeline
// (https://github.com/prettier/prettier/issues/1581#issuecomment-300975032)
// that uses the JSX syntax, but does not follow the React whitespace
// rules.
// Ensuring that we never have a break between tags and text in JSX
// will allow Facebook to adopt Prettier without too much of an
// adverse effect on formatting algorithm.
children.push("");
children.push(jsxWhitespace);
}
words.shift();
}
let endWhitespace;
// Ends with whitespace
if (util.getLast(words) === "") {
words.pop();
endWhitespace = words.pop();
}
// This was whitespace only without a new line.
if (words.length === 0) {
return;
}
words.forEach((word, i) => {
if (i % 2 === 1) {
children.push(line);
} else {
children.push(word);
}
});
} else if (/\n/.test(value)) {
children.push("");
children.push(hardline);
// allow one extra newline
if (value.match(/\n/g).length > 1) {
if (endWhitespace !== undefined) {
if (/\n/.test(endWhitespace)) {
children.push(softline);
} else {
children.push(jsxWhitespace);
}
} else {
// Ideally this would be a `softline` to allow a break between
// tags and text.
// Unfortunately Facebook have a custom translation pipeline
// (https://github.com/prettier/prettier/issues/1581#issuecomment-300975032)
// that uses the JSX syntax, but does not follow the React whitespace
// rules.
// Ensuring that we never have a break between tags and text in JSX
// will allow Facebook to adopt Prettier without too much of an
// adverse effect on formatting algorithm.
children.push("");
}
} else if (/\n/.test(text)) {
// Keep (up to one) blank line between tags/expressions/text.
// Note: We don't keep blank lines between text elements.
if (text.match(/\n/g).length > 1) {
children.push("");
children.push(hardline);
}
} else if (/[ \n\r\t]/.test(value)) {
// whitespace(s)-only without newlines,
// eg; one or more spaces separating two elements
for (let i = 0; i < value.length; ++i) {
// Because fill expects alternating content and whitespace parts
// we need to include an empty content part before each JSX
// whitespace.
children.push("");
children.push(jsxWhitespace);
}
} else {
children.push("");
children.push(jsxWhitespace);
}
} else {
children.push(print(childPath));
// Convert `{" "}` to jsxWhitespace so it can be printed as a standard
// space if needed.
if (isJSXWhitespaceExpression(child)) {
children.push("");
children.push(jsxWhitespace);
return;
}
const printedChild = print(childPath);
children.push(printedChild);
const isBrTag =
child.type === "JSXElement" && child.openingElement.name.name === "br";
if (isBrTag) {
// Forcing a hardline after a `<br />` tag gives much better text
// layout.
children.push(hardline);
return;
}
const next = n.children[i + 1];
const followedByJSXElement = next && !isLiteral(next);
const followedByJSXWhitespace =
next &&
next.type === "JSXExpressionContainer" &&
isLiteral(next.expression) &&
next.expression.value === " ";
if (followedByJSXElement && !followedByJSXWhitespace) {
children.push(softline);
} else {
// Ideally this would be a softline as well.
const followedByMeaningfulText = next && isMeaningfulJSXText(next);
const followedByJSXWhitespace = next && isJSXWhitespaceExpression(next);
if (followedByMeaningfulText || followedByJSXWhitespace) {
// Potentially this could be a softline as well.
// See the comment above about the Facebook translation pipeline as
// to why this is an empty string.
children.push("");
} else {
children.push(softline);
}
}
}, "children");
@ -3830,18 +3841,46 @@ function printJSXElement(path, options, print) {
assert.ok(!n.closingElement);
return openingLines;
}
const containsTag =
n.children.filter(child => child.type === "JSXElement").length > 0;
const containsMultipleExpressions =
n.children.filter(child => child.type === "JSXExpressionContainer").length >
1;
const containsMultipleAttributes = n.openingElement.attributes.length > 1;
// Record any breaks. Should never go from true to false, only false to true.
let forcedBreak = willBreak(openingLines);
let forcedBreak =
willBreak(openingLines) ||
containsTag ||
containsMultipleAttributes ||
containsMultipleExpressions;
const rawJsxWhitespace = options.singleQuote ? "{' '}" : '{" "}';
const jsxWhitespace = ifBreak(concat([rawJsxWhitespace, softline]), " ");
const children = printJSXChildren(path, options, print, jsxWhitespace);
// Remove multiple filler empty strings
// These can occur when a text element is followed by a newline.
const containsText =
n.children.filter(child => isMeaningfulJSXText(child)).length > 0;
// We can end up we multiple whitespace elements with empty string
// content between them.
// We need to remove empty whitespace and softlines before JSX whitespace
// to get the correct output.
for (let i = children.length - 2; i >= 0; i--) {
if (children[i] === "" && children[i + 1] === "") {
const isPairOfEmptyStrings = children[i] === "" && children[i + 1] === "";
const isSoftlineFollowedByJSXWhitespace =
children[i] === softline &&
children[i + 1] === "" &&
children[i + 2] === jsxWhitespace;
const isEmptyFollowedByHardline =
children[i] === "" && children[i + 1] === hardline;
if (
isPairOfEmptyStrings ||
isSoftlineFollowedByJSXWhitespace ||
(isEmptyFollowedByHardline && containsText)
) {
children.splice(i, 2);
}
}
@ -3888,13 +3927,19 @@ function printJSXElement(path, options, print) {
// Also detect whether we will force this element to output over multiple lines.
const multilineChildren = [];
children.forEach((child, i) => {
// Ensure that we display leading, trailing, and solitary whitespace as
// `{" "}` when outputting this element over multiple lines.
// There are a number of situations where we need to ensure we display
// whitespace as `{" "}` when outputting this element over multiple lines.
if (child === jsxWhitespace) {
if (i === 1 && children[i - 1] === "") {
// Leading whitespace
multilineChildren.push(rawJsxWhitespace);
return;
} else if (i === children.length - 1) {
// Trailing whitespace
multilineChildren.push(rawJsxWhitespace);
return;
} else if (children[i - 1] === "" && children[i - 2] === hardline) {
// Whitespace after line break
multilineChildren.push(rawJsxWhitespace);
return;
}
@ -3907,10 +3952,17 @@ function printJSXElement(path, options, print) {
}
});
// If there is text we use `fill` to fit as much onto each line as possible.
// When there is no text (just tags and expressions) we use `group`
// to output each on a separate line.
const content = containsText
? fill(multilineChildren)
: group(concat(multilineChildren), { shouldBreak: true });
const multiLineElem = group(
concat([
openingLines,
indent(concat([hardline, fill(multilineChildren)])),
indent(concat([hardline, content])),
hardline,
closingLines
])
@ -3921,7 +3973,7 @@ function printJSXElement(path, options, print) {
}
return conditionalGroup([
group(concat([openingLines, fill(children), closingLines])),
group(concat([openingLines, concat(children), closingLines])),
multiLineElem
]);
}
@ -4122,7 +4174,7 @@ function adjustClause(node, clause, forceSpace) {
}
function nodeStr(node, options, isFlowOrTypeScriptDirectiveLiteral) {
const raw = node.extra ? node.extra.raw : node.raw;
const raw = rawText(node);
// `rawContent` is the string exactly like it appeared in the input source
// code, with its enclosing quote.
const rawContent = raw.slice(1, -1);

View File

@ -495,9 +495,7 @@ Observable.of(process)
.takeUntil(exit);
// Comments disappear inside of JSX
<div>
{/* Some comment */}
</div>;
<div>{/* Some comment */}</div>;
// Comments in JSX tag are placed in a non optimal way
<div
@ -596,23 +594,15 @@ exports[`jsx.js 1`] = `
<div>{/*<div> Some very v ery very very long line to break line width limit </div>*/}</div>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div>
{/* comment */}
</div>;
<div>{/* comment */}</div>;
<div>
{/* comment */}
</div>;
<div>{/* comment */}</div>;
<div>
{/* comment
*/}
</div>;
<div>{/* comment
*/}</div>;
<div>
{a /* comment
*/}
</div>;
<div>{a /* comment
*/}</div>;
<div>
{
@ -622,13 +612,9 @@ exports[`jsx.js 1`] = `
}
</div>;
<div>
{/* comment */}
</div>;
<div>{/* comment */}</div>;
<div>
{/* comment */}
</div>;
<div>{/* comment */}</div>;
<div>
{

View File

@ -99,8 +99,7 @@ var App = React.createClass({
return (
<div>
{foo(x, y)}
{foo(z, x)} // error, since z: number
{foo(x, y)}{foo(z, x)} // error, since z: number
</div>
);
}
@ -164,8 +163,7 @@ var App = require("App.react");
var app = (
<App y={42}>
{" "}// error, y: number but foo expects string in App.react
Some text.
{" "}// error, y: number but foo expects string in App.react Some text.
</App>
);

View File

@ -865,11 +865,7 @@ var ReactClass = React.createClass({
render: function(): any {
// Any state access here seems to make state any
this.state;
return (
<div>
{this.state.bar.qux}
</div>
);
return <div>{this.state.bar.qux}</div>;
}
});

View File

@ -741,11 +741,7 @@ class Bar extends React.Component {
test: number
};
render() {
return (
<div>
{this.props.test}
</div>
);
return <div>{this.props.test}</div>;
}
}

View File

@ -40,7 +40,9 @@ comp2A = (
);
const comp3 = (
<div style={styles} key="something">Bump to next line without parens</div>
<div style={styles} key="something">
Bump to next line without parens
</div>
);
const comp4 = (

View File

@ -98,73 +98,35 @@ regression_extra_newline_2 = (
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
keep = (
<p>
Welcome to the <strong>Universal React Starter-kyt</strong>.
This starter kyt should serve as the base for an advanced,
server-rendered React app.
Welcome to the <strong>Universal React Starter-kyt</strong>. This starter
kyt should serve as the base for an advanced, server-rendered React app.
</p>
);
newlines_text = (
<div>
hi
there
how
newlines_text = <div>hi there how are you are you fine today?</div>;
are you
are you fine today?
</div>
);
newlines_text_spaced = (
<div>
space above
space below
</div>
);
newlines_text_spaced = <div>space above space below</div>;
newlines_elems_spaced = (
<div>
<span>space above</span>
<span>space below</span>
</div>
);
newlines_mixed = (
<div>
hi
<span>there</span>
how
are <strong>you</strong>
are you fine today?
hi<span>there</span>how are <strong>you</strong>are you fine today?
</div>
);
newlines_elems = (
<div>
<div>
<div />
</div>
hi
<div />
<span />
<Big />
hi<div /><span /><Big />
</div>
);
@ -193,8 +155,6 @@ exports[`windows.js 1`] = `
Text
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div>
Text
</div>;
<div>Text</div>;
`;

View File

@ -123,7 +123,14 @@ break_components = (
<div>
<Foo />
<Bar>
<p>foo<span>bar bar bar</span></p><h1><span><em>yep</em></span></h1>
<p>
foo<span>bar bar bar</span>
</p>
<h1>
<span>
<em>yep</em>
</span>
</h1>
</Bar>
<h2>nope</h2>
</div>
@ -182,7 +189,8 @@ not_broken_end = (
not_broken_begin = (
<div>
<br /> long text long text long text long text long text long text long text
<br />
{" "}long text long text long text long text long text long text long text
long text<link>url</link> long text long text
</div>
);

View File

@ -90,11 +90,21 @@ long_open_long_children = (
Hello world
</BaseForm>
<div>
<div><div><div><div><div>hey hiya how are ya</div></div></div></div></div>
<div>
<div>
<div>
<div>
<div>hey hiya how are ya</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div>
<div><div attr="long" attr2="also long" attr3="gonna break" /></div>
<div>
<div attr="long" attr2="also long" attr3="gonna break" />
</div>
</div>
</div>
<div>

View File

@ -86,7 +86,9 @@ const render2 = ({ styles }) =>
</div>;
const render3 = ({ styles }) =>
<div style={styles} key="something">Bump to next line without parens</div>;
<div style={styles} key="something">
Bump to next line without parens
</div>;
const render4 = ({ styles }) =>
<div style={styles} key="something">
@ -133,20 +135,28 @@ const render6 = ({ styles }) =>
const render7 = () =>
<div>
<span /><span>Dont break each elem onto its own line.</span> <span />
<div /> <div />
<span /><span>Dont break each elem onto its own line.</span> <span /><div />{" "}
<div />
</div>;
const render7A = () =>
<div>
<div /><div /><div />
<div />
<div />
<div />
</div>;
const render7B = () =>
<div>
<span> <span /> Dont break plz</span>
<span><span />Dont break plz</span>
<span>Dont break plz<span /></span>
<span>
{" "}<span /> Dont break plz
</span>
<span>
<span />Dont break plz
</span>
<span>
Dont break plz<span />
</span>
</div>;
const render8 = props => <div>{props.text}</div>;

View File

@ -162,6 +162,107 @@ jsx_around_multiline_element_second_pass = (
After
</div>
);
convert_space_expressions =
<div>{" "}</div>
x =
<div>
<first />
<second />
<third />
<fourth />
<fifth />
<sixth />
</div>
const Abc = () => {
return (
<div>
Please state your
{" "}
<b>name</b>
{" "}
and
{" "}
<b>occupation</b>
{" "}
for the board of directors.
</div>
);
};
x = <div id="moo">Some stuff here</div>
headers_and_paragraphs = (
<div>
<h2>First</h2>
<p>The first paragraph.</p>
<h2>Second</h2>
<p>The second paragraph.</p>
</div>
);
no_text_one_tag_per_line =
<div>
<first /><second />
</div>
with_text_fill_line =
<div>
Text <first /><second />
</div>
line_after_br =
<div>
Text<br />
More text <br />
And more<br />
</div>
line_after_br_2 = <div>A<br />B<br />C</div>
br_followed_by_whitespace = <div><br /> text</div>
dont_preserve_blank_lines_when_jsx_contains_text =
<div>
<div>Zeroth</div>
<div>First</div>
Second
</div>
multiple_expressions =
<div>
{header}
{body}
{footer}
</div>
single_expression_child_tags =
<div>
You currently have <strong>{dashboardStr}</strong> and <strong>{userStr}</strong>
</div>
expression_does_not_break =
<div>texty text text text text text text text text text text text {this.props.type} </div>
// FIXME
br_triggers_expression_break =
<div><br />text text text text text text text text text text text {this.props.type} </div>
jsx_whitespace_after_tag =
<div>
<span a="a" b="b">
{variable}
</span>
{" "}
({variable})
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Wrapping text
x = (
@ -182,8 +283,12 @@ x = (
// Wrapping tags
x = (
<div>
<first>f</first><first>f</first><first>f</first><first>f</first>
<first>f</first><first>f</first>
<first>f</first>
<first>f</first>
<first>f</first>
<first>f</first>
<first>f</first>
<first>f</first>
</div>
);
@ -223,7 +328,7 @@ x = (
x = (
<div>
before {stuff} after {stuff} after {stuff} after {stuff} after {stuff} after{" "}
{stuff} {stuff} {stuff} after {stuff} after
{stuff} {stuff} {stuff} after {stuff} after
</div>
);
@ -241,14 +346,11 @@ function DiffOverview(props) {
<div className="alert alert-info">
<p>
This diff overview is computed against the current list of records in
this collection and the list it contained on <b>
{humanDate(since)}
</b>.
this collection and the list it contained on <b>{humanDate(since)}</b>.
</p>
<p>
<b>Note:</b> <code>last_modified</code> and <code>schema</code> record
metadata
are omitted for easier review.
metadata are omitted for easier review.
</p>
</div>
<Diff source={source} target={target} />
@ -277,23 +379,13 @@ x = (
x = (
<div>
<div>
First
</div>
Second
<div>
Third
</div>
<div>First</div>Second<div>Third</div>
</div>
);
x = (
<div>
First{" "}
<div>
Second
</div>{" "}
Third
First <div>Second</div> Third
</div>
);
@ -320,12 +412,7 @@ no_leading_or_trailing_whitespace = (
facebook_translation_leave_text_around_tag = (
<div>
<span>
First
</span>,
(<span>
Second
</span>)
<span>First</span>, (<span>Second</span>)
</div>
);
@ -366,15 +453,7 @@ solitary_whitespace = (
jsx_whitespace_on_newline = (
<div>
<div>
First
</div>{" "}
<div>
Second
</div>{" "}
<div>
Third
</div>
<div>First</div> <div>Second</div> <div>Third</div>
</div>
);
@ -402,4 +481,121 @@ jsx_around_multiline_element_second_pass = (
</div>
);
convert_space_expressions = <div> </div>;
x = (
<div>
<first />
<second />
<third />
<fourth />
<fifth />
<sixth />
</div>
);
const Abc = () => {
return (
<div>
Please state your <b>name</b> and <b>occupation</b> for the board of
directors.
</div>
);
};
x = <div id="moo">Some stuff here</div>;
headers_and_paragraphs = (
<div>
<h2>First</h2>
<p>The first paragraph.</p>
<h2>Second</h2>
<p>The second paragraph.</p>
</div>
);
no_text_one_tag_per_line = (
<div>
<first />
<second />
</div>
);
with_text_fill_line = (
<div>
Text <first /><second />
</div>
);
line_after_br = (
<div>
Text<br />
More text <br />
And more<br />
</div>
);
line_after_br_2 = (
<div>
A<br />
B<br />
C
</div>
);
br_followed_by_whitespace = (
<div>
<br />
{" "}text
</div>
);
dont_preserve_blank_lines_when_jsx_contains_text = (
<div>
<div>Zeroth</div><div>First</div>Second
</div>
);
multiple_expressions = (
<div>
{header}
{body}
{footer}
</div>
);
single_expression_child_tags = (
<div>
You currently have <strong>{dashboardStr}</strong> and{" "}
<strong>{userStr}</strong>
</div>
);
expression_does_not_break = (
<div>
texty text text text text text text text text text text text{" "}
{this.props.type}{" "}
</div>
);
// FIXME
br_triggers_expression_break = (
<div>
<br />
text text text text text text text text text text text {
this.props.type
}{" "}
</div>
);
jsx_whitespace_after_tag = (
<div>
<span a="a" b="b">
{variable}
</span>{" "}
({variable})
</div>
);
`;

View File

@ -159,3 +159,104 @@ jsx_around_multiline_element_second_pass = (
After
</div>
);
convert_space_expressions =
<div>{" "}</div>
x =
<div>
<first />
<second />
<third />
<fourth />
<fifth />
<sixth />
</div>
const Abc = () => {
return (
<div>
Please state your
{" "}
<b>name</b>
{" "}
and
{" "}
<b>occupation</b>
{" "}
for the board of directors.
</div>
);
};
x = <div id="moo">Some stuff here</div>
headers_and_paragraphs = (
<div>
<h2>First</h2>
<p>The first paragraph.</p>
<h2>Second</h2>
<p>The second paragraph.</p>
</div>
);
no_text_one_tag_per_line =
<div>
<first /><second />
</div>
with_text_fill_line =
<div>
Text <first /><second />
</div>
line_after_br =
<div>
Text<br />
More text <br />
And more<br />
</div>
line_after_br_2 = <div>A<br />B<br />C</div>
br_followed_by_whitespace = <div><br /> text</div>
dont_preserve_blank_lines_when_jsx_contains_text =
<div>
<div>Zeroth</div>
<div>First</div>
Second
</div>
multiple_expressions =
<div>
{header}
{body}
{footer}
</div>
single_expression_child_tags =
<div>
You currently have <strong>{dashboardStr}</strong> and <strong>{userStr}</strong>
</div>
expression_does_not_break =
<div>texty text text text text text text text text text text text {this.props.type} </div>
// FIXME
br_triggers_expression_break =
<div><br />text text text text text text text text text text text {this.props.type} </div>
jsx_whitespace_after_tag =
<div>
<span a="a" b="b">
{variable}
</span>
{" "}
({variable})
</div>

View File

@ -27,20 +27,8 @@ zero_width_space = <div>][</div>
// Treated as whitespace in JSX
spaces = <div>] [</div>;
tabs = <div>] [</div>;
slash_n = (
<div>
]
[
</div>
);
slash_r = (
<div>
]
[
</div>
);
slash_n = <div>] [</div>;
slash_r = <div>] [</div>;
// Not treated as whitespace in JSX
// NOTE: Some of the space characters here won't show up in an editor,

View File

@ -128,13 +128,7 @@ exports[`expression.js 1`] = `
value={option}
/>;
<ParentComponent
prop={
<Child>
test
</Child>
}
/>;
<ParentComponent prop={<Child>test</Child>} />;
`;
@ -148,9 +142,7 @@ const aDiv = (
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const aDiv = (
/* $FlowFixMe */
<div className="foo">
Foo bar
</div>
<div className="foo">Foo bar</div>
);
`;
@ -283,7 +275,8 @@ onClick={() => {
a;
}}
>
{header}{showSort}
{header}
{showSort}
</td>;
<td
@ -359,23 +352,11 @@ const Labels = {
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const Labels = {
label1: (
<fbt>
Label 1
</fbt>
),
label1: <fbt>Label 1</fbt>,
label2: (
<fbt>
Label 2
</fbt>
),
label2: <fbt>Label 2</fbt>,
label3: (
<fbt>
Label 3
</fbt>
)
label3: <fbt>Label 3</fbt>
};
`;

View File

@ -31,14 +31,10 @@ raw_amp = <span>foo & bar</span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
many_nbsp = <div>&nbsp; &nbsp; </div>;
single_nbsp = <div>&nbsp;</div>;
nbsp_with_newline = (
<div>
&nbsp;
</div>
);
nbsp_with_newline = <div>&nbsp;</div>;
many_raw_nbsp = <div>   </div>;
many_raw_spaces = <div> </div>;
many_raw_spaces = <div> </div>;
amp = <span>foo &amp; bar</span>;
raw_amp = <span>foo & bar</span>;
@ -62,14 +58,10 @@ raw_amp = <span>foo & bar</span>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
many_nbsp = <div>&nbsp; &nbsp; </div>;
single_nbsp = <div>&nbsp;</div>;
nbsp_with_newline = (
<div>
&nbsp;
</div>
);
nbsp_with_newline = <div>&nbsp;</div>;
many_raw_nbsp = <div>   </div>;
many_raw_spaces = <div> </div>;
many_raw_spaces = <div> </div>;
amp = <span>foo &amp; bar</span>;
raw_amp = <span>foo & bar</span>;

View File

@ -30,17 +30,9 @@ var example2 = <div>
/*test*/
</div>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var example1 = (
<div>
https://test
</div>
);
var example1 = <div>https://test</div>;
var example2 = (
<div>
/*test*/
</div>
);
var example2 = <div>/*test*/</div>;
`;

View File

@ -38,10 +38,7 @@ const MyCoolList = ({ things }) =>
const MyCoolThing = ({ thingo }) => <li>{thingo}</li>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const MyCoolList = ({ things }) =>
<ul>
{things.map(MyCoolThing)}
</ul>;
const MyCoolList = ({ things }) => <ul>{things.map(MyCoolThing)}</ul>;
const MyCoolThing = ({ thingo }) => <li>{thingo}</li>;