Skip to content

Improved whitespace collapsing rules #459

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

Closed
wants to merge 12 commits into from
Closed
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
2 changes: 1 addition & 1 deletion docs/docs/ref-03-component-specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The `render()` function should be *pure*, meaning that it does not modify compon
object getInitialState()
```

Invoked once when the component is mounted. The return value will be used as the initial value of `this.state`.
Invoked once before the component is mounted. The return value will be used as the initial value of `this.state`.


### getDefaultProps
Expand Down
12 changes: 6 additions & 6 deletions src/core/__tests__/ReactRenderDocument-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('rendering React components at document', function() {

ReactMount.allowFullPageRender = true;
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 @@ -91,7 +91,7 @@ describe('rendering React components at document', function() {

ReactMount.allowFullPageRender = true;
React.renderComponent(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');

var unmounted = React.unmountComponentAtNode(testDocument);
expect(unmounted).toBe(true);
Expand Down Expand Up @@ -152,12 +152,12 @@ describe('rendering React components at document', function() {

ReactMount.allowFullPageRender = true;
var component = React.renderComponent(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe(' Hello world ');
expect(testDocument.body.innerHTML).toBe('Hello world');

// Reactive update via state transition
component.toggle();

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

});

Expand Down Expand Up @@ -200,12 +200,12 @@ describe('rendering React components at document', function() {
ReactMount.allowFullPageRender = true;
React.renderComponent(<Component />, testDocument);

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

// Reactive update
React.renderComponent(<Component2 />, testDocument);

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

});

Expand Down
27 changes: 0 additions & 27 deletions src/vendor/core/dom/getDocumentScrollElement.js

This file was deleted.

7 changes: 4 additions & 3 deletions src/vendor/core/dom/getUnboundedScrollPosition.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

"use strict";

var getDocumentScrollElement = require('getDocumentScrollElement');

/**
* Gets the scroll position of the supplied element or window.
*
Expand All @@ -19,7 +17,10 @@ var getDocumentScrollElement = require('getDocumentScrollElement');
*/
function getUnboundedScrollPosition(scrollable) {
if (scrollable === window) {
return getUnboundedScrollPosition(getDocumentScrollElement());
return {
x: document.documentElement.scrollLeft || document.body.scrollLeft,
y: document.documentElement.scrollTop || document.body.scrollTop
};
}
return {
x: scrollable.scrollLeft,
Expand Down
6 changes: 2 additions & 4 deletions vendor/fbtransform/transforms/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ function visitReactTag(traverse, object, path, state) {
move(object.name.range[1], state);

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

// if we don't have any attributes, pass in null
Expand Down Expand Up @@ -140,9 +141,6 @@ function visitReactTag(traverse, object, path, state) {
append(', ', state);

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

var isLast = child === childrenToRender[childrenToRender.length - 1];
Expand Down
176 changes: 67 additions & 109 deletions vendor/fbtransform/transforms/xjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
*/
/*global exports:true*/
"use strict";
var append = require('jstransform/src/utils').append;
var catchup = require('jstransform/src/utils').catchup;
var move = require('jstransform/src/utils').move;
var Syntax = require('esprima-fb').Syntax;
var utils = require('jstransform/src/utils');

var knownTags = {
a: true,
Expand Down Expand Up @@ -147,134 +145,94 @@ 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:
* #1 Any whitespace sequence which CONTAIN A NEWLINE and TOUCHES
* the start or end of the string is removed completely
*
* "line "+
* "line"
* #2 Any whitespace sequence which CONTAIN A NEWLINE but DOES NOT TOUCH
* the start or end of the string is replaced with a single newline
*/
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
append(object.value.match(/^[\t ]*/)[0], state);
if (start) {
append(start, state);
}

var trimmedChildValueWithSpace = trimWithSingleSpace(object.value);

/**
*/
var initialLines = trimmedChildValue.split(/\r\n|\n|\r/);
function removeWhitespace(str) {
return str.replace(/[ \t]+/g, '');
};

function renderXJSLiteral(object, isLast, state, start, end) {
var trimmedChildValue =
object.value.replace(/^[ \t]*[\r\n][ \t\r\n]*/, removeWhitespace). // #1
replace(/[ \t]*[\r\n][ \t\r\n]*$/, removeWhitespace). // #1
replace(/([^ \t\r\n])([ \t]*[\r\n][ \t\r\n]*)(?=[^ \t\r\n])/g, // #2
function(_, leading, match) {
return leading + ' ' + removeWhitespace(match);
});

//utils.append(object.value.match(/^[ \t]*/)[0], state);
if (start) {
utils.append(start, state);
}

var lines = initialLines.filter(function(line) {
return safeTrim(line).length > 0;
});
var trimmedLines = trimmedChildValue.split(/\r\n|\n|\r/);
var lines = object.value.split(/\r\n|\n|\r/);

var hasInitialNewLine = initialLines[0] !== lines[0];
hasFinalNewLine =
initialLines[initialLines.length - 1] !== lines[lines.length - 1];
var lastNonEmptyLine = -1;
trimmedLines.forEach(function (line, ii) {
if (line) lastNonEmptyLine = ii;
});

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

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 = ' ';
}
}
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 = ' ';
}
var isEmptyLine = !!lines[ii].match(/^[ \t\xA0]*$/);

append(
leading +
JSON.stringify(
preString + trimmedLine + postString
) +
(lastLine ? '' : '+') +
line.match(/[ \t]*$/)[0],
state);
if (line) {
if (!isEmptyLine) {
utils.append(lines[ii].match(/^[ \t\xA0]*/)[0], state);
}
if (!lastLine) {
append('\n', state);
if (!isLastLine) {
// we temporarily appended a space for #2, replace with a newline
line = line.replace(/[ \t]$/g, '\n');
}
});
} else {
if (start) {
append(start, state);
}
append('""', state);
}
if (end) {
append(end, state);
}

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

// tail whitespace
if (hasFinalNewLine) {
append('\n', state);
}
append(object.value.match(/[ \t]*$/)[0], state);
move(object.range[1], state);
utils.append(
JSON.stringify(line) +
(ii >= lastNonEmptyLine ? '' : '+'),
state);
}

// insert just after the last literal
if (!isLast && ii === lastNonEmptyLine) {
if (end) {
utils.append(end, state);
}
utils.append(',', state);
}

//if (!isEmptyLine || ii > 0) {
utils.append(lines[ii].match(/[ \t\xA0]*$/)[0], state);
//}

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

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

function renderXJSExpressionContainer(traverse, object, isLast, path, state) {
// Plus 1 to skip `{`.
move(object.range[0] + 1, state);
utils.move(object.range[0] + 1, state);
traverse(object.expression, path, state);
if (!isLast && object.expression.type !== Syntax.XJSEmptyExpression) {
// If we need to append a comma, make sure to do so after the expression.
catchup(object.expression.range[1], state);
append(',', state);
utils.catchup(object.expression.range[1], state);
utils.append(',', state);
}

// Minus 1 to skip `}`.
catchup(object.range[1] - 1, state);
move(object.range[1], state);
utils.catchup(object.range[1] - 1, state);
utils.move(object.range[1], state);
return false;
}

Expand Down