Skip to content

JSX whitespace coalescing rules #480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 23, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/core/__tests__/ReactRenderDocument-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('rendering React components at document', function() {
React.renderComponentToString(<Root />, function(markup) {
testDocument = getTestDocument(markup);
var component = React.renderComponent(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');

var componentID = ReactMount.getReactRootID(testDocument);
expect(componentID).toBe(component._rootNodeID);
Expand Down Expand Up @@ -95,13 +95,13 @@ describe('rendering React components at document', function() {
React.renderComponentToString(<Root />, function(markup) {
testDocument = getTestDocument(markup);
React.renderComponent(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');

expect(function() {
React.unmountComponentAtNode(testDocument);
}).toThrow(UNMOUNT_INVARIANT_MESSAGE);

expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');
});
});

Expand Down Expand Up @@ -143,14 +143,14 @@ describe('rendering React components at document', function() {

React.renderComponent(<Component />, testDocument);

expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');

// Reactive update
expect(function() {
React.renderComponent(<Component2 />, testDocument);
}).toThrow(UNMOUNT_INVARIANT_MESSAGE);

expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');
});
});

Expand Down
16 changes: 7 additions & 9 deletions vendor/fbtransform/transforms/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ function visitReactTag(traverse, object, path, state) {

utils.move(object.name.range[1], state);

var childrenToRender = object.children.filter(function(child) {
return !(child.type === Syntax.Literal && !child.value.match(/\S/));
});

// if we don't have any attributes, pass in null
if (object.attributes.length === 0) {
utils.append('null', state);
Expand Down Expand Up @@ -131,16 +127,18 @@ function visitReactTag(traverse, object, path, state) {
}

// filter out whitespace
var childrenToRender = object.children.filter(function(child) {
return !(child.type === Syntax.Literal &&
child.value.match(/^[ \t]*[\r\n][ \t\r\n]*$/));
});

if (childrenToRender.length > 0) {
utils.append(', ', state);

object.children.forEach(function(child) {
if (child.type === Syntax.Literal && !child.value.match(/\S/)) {
return;
}
childrenToRender.forEach(function(child, index) {
utils.catchup(child.range[0], state);

var isLast = child === childrenToRender[childrenToRender.length - 1];
var isLast = index === childrenToRender.length - 1;

if (child.type === Syntax.Literal) {
renderXJSLiteral(child, isLast, state);
Expand Down
157 changes: 52 additions & 105 deletions vendor/fbtransform/transforms/xjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,118 +149,65 @@ var knownTags = {
wbr: true
};

function safeTrim(string) {
return string.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
}

// Replace all trailing whitespace characters with a single space character
function trimWithSingleSpace(string) {
return string.replace(/^[ \t\xA0]{2,}/, ' ').
replace(/[ \t\xA0]{2,}$/, ' ').replace(/^\s+$/, '');
}

/**
* Special handling for multiline string literals
* print lines:
*
* line
* line
*
* as:
*
* "line "+
* "line"
*/
function renderXJSLiteral(object, isLast, state, start, end) {
/** Added blank check filtering and triming*/
var trimmedChildValue = safeTrim(object.value);
var hasFinalNewLine = false;

if (trimmedChildValue) {
// head whitespace
utils.append(object.value.match(/^[\t ]*/)[0], state);
if (start) {
utils.append(start, state);
var lines = object.value.split(/\r\n|\n|\r/);

if (start) {
utils.append(start, state);
}

var lastNonEmptyLine = 0;

lines.forEach(function (line, index) {
if (line.match(/[^ \t]/)) {
lastNonEmptyLine = index;
}

var trimmedChildValueWithSpace = trimWithSingleSpace(object.value);

/**
*/
var initialLines = trimmedChildValue.split(/\r\n|\n|\r/);

var lines = initialLines.filter(function(line) {
return safeTrim(line).length > 0;
});

var hasInitialNewLine = initialLines[0] !== lines[0];
hasFinalNewLine =
initialLines[initialLines.length - 1] !== lines[lines.length - 1];

var numLines = lines.length;
lines.forEach(function (line, ii) {
var lastLine = ii === numLines - 1;
var trimmedLine = safeTrim(line);
if (trimmedLine === '' && !lastLine) {
utils.append(line, state);
} else {
var preString = '';
var postString = '';
var leading = line.match(/^[ \t]*/)[0];

if (ii === 0) {
if (hasInitialNewLine) {
preString = ' ';
leading = '\n' + leading;
}
if (trimmedChildValueWithSpace.substring(0, 1) === ' ') {
// If this is the first line, and the original content starts with
// whitespace, place a single space at the beginning.
preString = ' ';
}
});

lines.forEach(function (line, index) {
var isFirstLine = index === 0;
var isLastLine = index === lines.length - 1;
var isLastNonEmptyLine = index === lastNonEmptyLine;

// replace rendered whitespace tabs with spaces
var trimmedLine = line.replace(/\t/g, ' ');

// trim whitespace touching a newline
if (!isFirstLine) {
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
}
if (!isLastLine) {
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
}

utils.append(line.match(/^[ \t]*/)[0], state);

if (trimmedLine || isLastNonEmptyLine) {
utils.append(
JSON.stringify(trimmedLine) +
(!isLastNonEmptyLine ? "+' '+" : ''),
state);

if (isLastNonEmptyLine) {
if (end) {
utils.append(end, state);
}
if (!lastLine || trimmedChildValueWithSpace.substr(
trimmedChildValueWithSpace.length - 1, 1) === ' ' ||
hasFinalNewLine
) {
// If either not on the last line, or the original content ends with
// whitespace, place a single character at the end.
postString = ' ';
if (!isLast) {
utils.append(',', state);
}

utils.append(
leading +
JSON.stringify(
preString + trimmedLine + postString
) +
(lastLine ? '' : '+') +
line.match(/[ \t]*$/)[0],
state);
}
if (!lastLine) {
utils.append('\n', state);

// only restore tail whitespace if line had literals
if (trimmedLine) {
utils.append(line.match(/[ \t]*$/)[0], state);
}
});
} else {
if (start) {
utils.append(start, state);
}
utils.append('""', state);
}
if (end) {
utils.append(end, state);
}

// add comma before trailing whitespace
if (!isLast) {
utils.append(',', state);
}

// tail whitespace
if (hasFinalNewLine) {
utils.append('\n', state);
}
utils.append(object.value.match(/[ \t]*$/)[0], state);

if (!isLastLine) {
utils.append('\n', state);
}
});

utils.move(object.range[1], state);
}

Expand Down