From f126c2e38d87a7eec23ac1700dc255d5cbab8cea Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Thu, 31 Oct 2013 20:50:42 +0100 Subject: [PATCH 01/11] improved whitespace collapsing rules --- .../__tests__/ReactRenderDocument-test.js | 12 +- vendor/fbtransform/transforms/react.js | 5 +- vendor/fbtransform/transforms/xjs.js | 158 ++++++------------ 3 files changed, 62 insertions(+), 113 deletions(-) diff --git a/src/core/__tests__/ReactRenderDocument-test.js b/src/core/__tests__/ReactRenderDocument-test.js index dd5250811ca..2409e74ab7e 100644 --- a/src/core/__tests__/ReactRenderDocument-test.js +++ b/src/core/__tests__/ReactRenderDocument-test.js @@ -62,7 +62,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, 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); @@ -91,7 +91,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); var unmounted = React.unmountComponentAtNode(testDocument); expect(unmounted).toBe(true); @@ -152,12 +152,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, 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'); }); @@ -200,12 +200,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); // Reactive update React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Goodbye world '); + expect(testDocument.body.innerHTML).toBe('Goodbye world'); }); diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index 094632ac244..a50079590c8 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -74,7 +74,7 @@ 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\xA0]*[\r\n][ \t\xA0\r\n]*$/)); }); // if we don't have any attributes, pass in null @@ -140,9 +140,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]; diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index b04c4e50b17..24b8e760b83 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -147,118 +147,70 @@ 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 - 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/); + var trimmedChildValue = + object.value.replace(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*/, function(match) { + // trim all whitespace except newlines from the start of the string, only if it also contains a newline + return match.replace(/[ \t\xA0]+/g, ''); + }). + replace(/[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/, function(match) { + // trim all whitespace except newlines from the end of the string, only if it also contains a newline + return match.replace(/[ \t\xA0]+/g, ''); + }). + replace(/([^ \t\xA0\r\n])([ \t\xA0]*[\r\n][ \t\xA0\r\n]*)(?=[^ \t\xA0\r\n])/g, function(_, start, whitespace) { + // collapse all whitespace, that doesn't touch either the start or end of the string, into a single space + return start + ' ' + whitespace.replace(/[ \t\xA0]+/g, ''); + }). + // collapse all whitespace to a single space + replace(/[ \t\xA0]+/g, ' '); + + //append(object.value.match(/^[ \t]*/)[0], state); + if (start) { + 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) { + append(lines[ii].match(/^[ \t\xA0]*/)[0], state); } - if (!lastLine) { - append('\n', state); + + append( + JSON.stringify(line) + + (ii >= lastNonEmptyLine ? '' : '+'), + state); + } + + // insert just after the last literal + if (!isLast && ii == lastNonEmptyLine) { + if (end) { + append(end, state); } - }); - } else { - if (start) { - append(start, state); + append(',', 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); + + //if (!isEmptyLine || ii > 0) { + append(lines[ii].match(/[ \t\xA0]*$/)[0], state); + //} + + if (!isLastLine) { + append('\n', state); + } + }); + move(object.range[1], state); } From a4f8ad1bb0ef6bd7dff3483471fbfb9815538a5d Mon Sep 17 00:00:00 2001 From: JeffMo Date: Thu, 31 Oct 2013 13:45:16 -0700 Subject: [PATCH 02/11] Switch utility function calls -> namespaced calls --- vendor/fbtransform/transforms/xjs.js | 38 +++++++++++++--------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index b04c4e50b17..3e1452a4e9a 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -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, @@ -176,9 +174,9 @@ function renderXJSLiteral(object, isLast, state, start, end) { if (trimmedChildValue) { // head whitespace - append(object.value.match(/^[\t ]*/)[0], state); + utils.append(object.value.match(/^[\t ]*/)[0], state); if (start) { - append(start, state); + utils.append(start, state); } var trimmedChildValueWithSpace = trimWithSingleSpace(object.value); @@ -200,7 +198,7 @@ function renderXJSLiteral(object, isLast, state, start, end) { var lastLine = ii === numLines - 1; var trimmedLine = safeTrim(line); if (trimmedLine === '' && !lastLine) { - append(line, state); + utils.append(line, state); } else { var preString = ''; var postString = ''; @@ -226,7 +224,7 @@ function renderXJSLiteral(object, isLast, state, start, end) { postString = ' '; } - append( + utils.append( leading + JSON.stringify( preString + trimmedLine + postString @@ -236,45 +234,45 @@ function renderXJSLiteral(object, isLast, state, start, end) { state); } if (!lastLine) { - append('\n', state); + utils.append('\n', state); } }); } else { if (start) { - append(start, state); + utils.append(start, state); } - append('""', state); + utils.append('""', state); } if (end) { - append(end, state); + utils.append(end, state); } // add comma before trailing whitespace if (!isLast) { - append(',', state); + utils.append(',', state); } // tail whitespace if (hasFinalNewLine) { - append('\n', state); + utils.append('\n', state); } - append(object.value.match(/[ \t]*$/)[0], state); - move(object.range[1], state); + utils.append(object.value.match(/[ \t]*$/)[0], 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; } From 0f941c6ab1f2c1b9b05dc369a73e78e1d7b7a721 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Thu, 31 Oct 2013 22:24:42 +0100 Subject: [PATCH 03/11] fixed line lengths, all under 80 except one slightly longer regex --- vendor/fbtransform/transforms/react.js | 3 ++- vendor/fbtransform/transforms/xjs.js | 17 +++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index a50079590c8..7bf35961197 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -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(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/)); + return !(child.type === Syntax.Literal && + child.value.match(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/)); }); // if we don't have any attributes, pass in null diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 24b8e760b83..142413e0c3b 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -150,17 +150,22 @@ var knownTags = { function renderXJSLiteral(object, isLast, state, start, end) { var trimmedChildValue = object.value.replace(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*/, function(match) { - // trim all whitespace except newlines from the start of the string, only if it also contains a newline + // trim all whitespace except newlines from the start of the string + // but only if it also contains a newline return match.replace(/[ \t\xA0]+/g, ''); }). replace(/[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/, function(match) { - // trim all whitespace except newlines from the end of the string, only if it also contains a newline + // trim all whitespace except newlines from the end of the string + // but only if it also contains a newline return match.replace(/[ \t\xA0]+/g, ''); }). - replace(/([^ \t\xA0\r\n])([ \t\xA0]*[\r\n][ \t\xA0\r\n]*)(?=[^ \t\xA0\r\n])/g, function(_, start, whitespace) { - // collapse all whitespace, that doesn't touch either the start or end of the string, into a single space - return start + ' ' + whitespace.replace(/[ \t\xA0]+/g, ''); - }). + replace(/([^ \t\xA0\r\n])([ \t\xA0]*[\r\n][ \t\xA0\r\n]*)(?=[^ \t\xA0\r\n])/g, + function(_, start, whitespace) { + // collapse all whitespace, that doesn't touch either the start or + // the end of the string, into a single space + return start + ' ' + whitespace.replace(/[ \t\xA0]+/g, ''); + } + ). // collapse all whitespace to a single space replace(/[ \t\xA0]+/g, ' '); From 0dc011c40c5929023e59daafbd94c0d17b0b7d9b Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Thu, 31 Oct 2013 15:24:58 -0700 Subject: [PATCH 04/11] Better `getUnboundedScrollPosition` for windows Instead of using browser sniffing, `getUnboundedScrollPosition` can do better and not have to depend on the `getDocumentScrollElement` module. --- .../core/dom/getDocumentScrollElement.js | 27 ------------------- .../core/dom/getUnboundedScrollPosition.js | 7 ++--- 2 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 src/vendor/core/dom/getDocumentScrollElement.js diff --git a/src/vendor/core/dom/getDocumentScrollElement.js b/src/vendor/core/dom/getDocumentScrollElement.js deleted file mode 100644 index ec289c3ba6d..00000000000 --- a/src/vendor/core/dom/getDocumentScrollElement.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @providesModule getDocumentScrollElement - * @typechecks - */ - -"use strict"; - -// TODO: Replace this with a UserAgent module. -var isWebkit = navigator.userAgent.indexOf('AppleWebKit') > -1; - -/** - * Gets the element with the document scroll properties such as `scrollLeft` and - * `scrollHeight`. This may differ across different browsers. - * - * NOTE: The return value can be null if the DOM is not yet ready. - * - * @param {?DOMDocument} doc Defaults to current document. - * @return {?DOMElement} - */ -function getDocumentScrollElement(doc) { - doc = doc || document; - return !isWebkit && doc.compatMode === 'CSS1Compat' ? - doc.documentElement : - doc.body; -} - -module.exports = getDocumentScrollElement; diff --git a/src/vendor/core/dom/getUnboundedScrollPosition.js b/src/vendor/core/dom/getUnboundedScrollPosition.js index b0e32bd53a0..872465bbc15 100644 --- a/src/vendor/core/dom/getUnboundedScrollPosition.js +++ b/src/vendor/core/dom/getUnboundedScrollPosition.js @@ -5,8 +5,6 @@ "use strict"; -var getDocumentScrollElement = require('getDocumentScrollElement'); - /** * Gets the scroll position of the supplied element or window. * @@ -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, From 0cb57e32470504633fe6afa42393f4eb292572c6 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Fri, 1 Nov 2013 00:09:01 +0100 Subject: [PATCH 05/11] cleaned up the code a bit added explanation of the rules and annotated the regexps removed \xA0 from being treated as regular whitespace (or should it?) --- vendor/fbtransform/transforms/react.js | 2 +- vendor/fbtransform/transforms/xjs.js | 41 ++++++++++++++------------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index 7bf35961197..7bef3e097c8 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -75,7 +75,7 @@ function visitReactTag(traverse, object, path, state) { var childrenToRender = object.children.filter(function(child) { return !(child.type === Syntax.Literal && - child.value.match(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/)); + child.value.match(/^[ \t]*[\r\n][ \t\r\n]*$/)); }); // if we don't have any attributes, pass in null diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 142413e0c3b..852f4f4a3bb 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -147,27 +147,30 @@ var knownTags = { wbr: true }; +/** + * #1 Any whitespace sequence which CONTAIN A NEWLINE and TOUCHES + * the start or end of the string is removed completely + * + * #2 Any whitespace sequence which CONTAIN A NEWLINE but DOES NOT TOUCH + * the start or end of the string is collapsed to a single space + * + * #3 Any whitespace sequence which DOES NOT CONTAIN A NEWLINE + * is collapsed to a single space + */ + function renderXJSLiteral(object, isLast, state, start, end) { + var removeWhitespace = function(str) { + return str.replace(/[ \t]+/g, ''); + }; + var trimmedChildValue = - object.value.replace(/^[ \t\xA0]*[\r\n][ \t\xA0\r\n]*/, function(match) { - // trim all whitespace except newlines from the start of the string - // but only if it also contains a newline - return match.replace(/[ \t\xA0]+/g, ''); - }). - replace(/[ \t\xA0]*[\r\n][ \t\xA0\r\n]*$/, function(match) { - // trim all whitespace except newlines from the end of the string - // but only if it also contains a newline - return match.replace(/[ \t\xA0]+/g, ''); - }). - replace(/([^ \t\xA0\r\n])([ \t\xA0]*[\r\n][ \t\xA0\r\n]*)(?=[^ \t\xA0\r\n])/g, - function(_, start, whitespace) { - // collapse all whitespace, that doesn't touch either the start or - // the end of the string, into a single space - return start + ' ' + whitespace.replace(/[ \t\xA0]+/g, ''); - } - ). - // collapse all whitespace to a single space - replace(/[ \t\xA0]+/g, ' '); + 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); + }). + replace(/[ \t]+/g, ' '); // #3 //append(object.value.match(/^[ \t]*/)[0], state); if (start) { From 0fa3767c979aa66f5e593f85499efd66233e06f9 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Fri, 1 Nov 2013 00:25:00 +0100 Subject: [PATCH 06/11] moved helper function outside of renderXJSLiteral --- vendor/fbtransform/transforms/xjs.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 852f4f4a3bb..b331ce9db32 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -158,11 +158,11 @@ var knownTags = { * is collapsed to a single space */ +function removeWhitespace(str) { + return str.replace(/[ \t]+/g, ''); +}; + function renderXJSLiteral(object, isLast, state, start, end) { - var removeWhitespace = function(str) { - return str.replace(/[ \t]+/g, ''); - }; - var trimmedChildValue = object.value.replace(/^[ \t]*[\r\n][ \t\r\n]*/, removeWhitespace). // #1 replace(/[ \t]*[\r\n][ \t\r\n]*$/, removeWhitespace). // #1 From 1d1182b00c58865db500f8fb49085c01919e6959 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Fri, 1 Nov 2013 12:35:12 +0100 Subject: [PATCH 07/11] updated "S \n S" rule to produce "S\nS" instead --- vendor/fbtransform/transforms/xjs.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index b331ce9db32..ce8052d757b 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -152,10 +152,10 @@ var knownTags = { * the start or end of the string is removed completely * * #2 Any whitespace sequence which CONTAIN A NEWLINE but DOES NOT TOUCH - * the start or end of the string is collapsed to a single space + * the start or end of the string is replaced with a single newline * * #3 Any whitespace sequence which DOES NOT CONTAIN A NEWLINE - * is collapsed to a single space + * is replace with a single space */ function removeWhitespace(str) { @@ -195,6 +195,10 @@ function renderXJSLiteral(object, isLast, state, start, end) { if (!isEmptyLine) { append(lines[ii].match(/^[ \t\xA0]*/)[0], state); } + if (!isLastLine) { + // we temporarily appended a space for #2, replace with a newline + line = line.replace(/[ \t]$/g, '\n'); + } append( JSON.stringify(line) + From aedf580a333c42f5654d70359518974c3fdac7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=E2=80=99Shannessy?= Date: Fri, 1 Nov 2013 15:29:10 -0700 Subject: [PATCH 08/11] [docs] Clarify when `getInitialState` is called. --- docs/docs/ref-03-component-specs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/ref-03-component-specs.md b/docs/docs/ref-03-component-specs.md index 0a4a63c825b..393faea3d67 100644 --- a/docs/docs/ref-03-component-specs.md +++ b/docs/docs/ref-03-component-specs.md @@ -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 From 844156c02c6223605ad1457d814cdb7586a41531 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Fri, 1 Nov 2013 23:50:54 +0100 Subject: [PATCH 09/11] improved whitespace collapsing rules fixed line lengths, all under 80 except one slightly longer regex cleaned up the code a bit added explanation of the rules and annotated the regexps removed \xA0 from being treated as regular whitespace (or should it?) moved helper function outside of renderXJSLiteral updated "S \n S" rule to produce "S\nS" instead --- .../__tests__/ReactRenderDocument-test.js | 12 +- vendor/fbtransform/transforms/react.js | 6 +- vendor/fbtransform/transforms/xjs.js | 162 +++++++----------- 3 files changed, 71 insertions(+), 109 deletions(-) diff --git a/src/core/__tests__/ReactRenderDocument-test.js b/src/core/__tests__/ReactRenderDocument-test.js index dd5250811ca..2409e74ab7e 100644 --- a/src/core/__tests__/ReactRenderDocument-test.js +++ b/src/core/__tests__/ReactRenderDocument-test.js @@ -62,7 +62,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, 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); @@ -91,7 +91,7 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); var unmounted = React.unmountComponentAtNode(testDocument); expect(unmounted).toBe(true); @@ -152,12 +152,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; var component = React.renderComponent(, 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'); }); @@ -200,12 +200,12 @@ describe('rendering React components at document', function() { ReactMount.allowFullPageRender = true; React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); // Reactive update React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Goodbye world '); + expect(testDocument.body.innerHTML).toBe('Goodbye world'); }); diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index 094632ac244..7bef3e097c8 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -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 @@ -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]; diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 3e1452a4e9a..05765dc3c43 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -145,118 +145,82 @@ 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 + * #1 Any whitespace sequence which CONTAIN A NEWLINE and TOUCHES + * the start or end of the string is removed completely * - * as: + * #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 * - * "line "+ - * "line" + * #3 Any whitespace sequence which DOES NOT CONTAIN A NEWLINE + * is replace with a single space */ -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 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); + }). + replace(/[ \t]+/g, ' '); // #3 + + //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) { - utils.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]*$/); - utils.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) { - utils.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) { - 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); + 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); } From 3f1bdf91ec422a87ec70575fa36b1f92dc67d0a4 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Sun, 3 Nov 2013 11:28:14 +0100 Subject: [PATCH 10/11] removed rule 3, no longer collapsing whitespace sequences without a newline to a single space --- vendor/fbtransform/transforms/xjs.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 05765dc3c43..481a9ba4203 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -151,9 +151,6 @@ var knownTags = { * * #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 - * - * #3 Any whitespace sequence which DOES NOT CONTAIN A NEWLINE - * is replace with a single space */ function removeWhitespace(str) { @@ -167,8 +164,7 @@ function renderXJSLiteral(object, isLast, state, start, end) { replace(/([^ \t\r\n])([ \t]*[\r\n][ \t\r\n]*)(?=[^ \t\r\n])/g, // #2 function(_, leading, match) { return leading + ' ' + removeWhitespace(match); - }). - replace(/[ \t]+/g, ' '); // #3 + }); //utils.append(object.value.match(/^[ \t]*/)[0], state); if (start) { From 79bede19411d6f88947f9ade4b1609789bfaff69 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Sun, 3 Nov 2013 13:45:12 +0100 Subject: [PATCH 11/11] strict type-equality "fix" --- vendor/fbtransform/transforms/xjs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index 481a9ba4203..8d420612524 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -201,7 +201,7 @@ function renderXJSLiteral(object, isLast, state, start, end) { } // insert just after the last literal - if (!isLast && ii == lastNonEmptyLine) { + if (!isLast && ii === lastNonEmptyLine) { if (end) { utils.append(end, state); }