From c16b5659a0323572b2cd404f6356c7b38e38b038 Mon Sep 17 00:00:00 2001 From: Andreas Svensson Date: Wed, 4 Dec 2013 22:55:45 +0100 Subject: [PATCH] Implement stricter whitespace rules --- .../__tests__/ReactRenderDocument-test.js | 10 +- vendor/fbtransform/transforms/react.js | 16 +- vendor/fbtransform/transforms/xjs.js | 157 ++++++------------ 3 files changed, 64 insertions(+), 119 deletions(-) diff --git a/src/core/__tests__/ReactRenderDocument-test.js b/src/core/__tests__/ReactRenderDocument-test.js index 350c0356b7b..04e3d30525f 100644 --- a/src/core/__tests__/ReactRenderDocument-test.js +++ b/src/core/__tests__/ReactRenderDocument-test.js @@ -67,7 +67,7 @@ describe('rendering React components at document', function() { React.renderComponentToString(, function(markup) { testDocument = getTestDocument(markup); 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); @@ -95,13 +95,13 @@ describe('rendering React components at document', function() { React.renderComponentToString(, function(markup) { testDocument = getTestDocument(markup); React.renderComponent(, 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'); }); }); @@ -143,14 +143,14 @@ describe('rendering React components at document', function() { React.renderComponent(, testDocument); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); // Reactive update expect(function() { React.renderComponent(, testDocument); }).toThrow(UNMOUNT_INVARIANT_MESSAGE); - expect(testDocument.body.innerHTML).toBe(' Hello world '); + expect(testDocument.body.innerHTML).toBe('Hello world'); }); }); diff --git a/vendor/fbtransform/transforms/react.js b/vendor/fbtransform/transforms/react.js index af5aa7dbedc..0c217c7c718 100644 --- a/vendor/fbtransform/transforms/react.js +++ b/vendor/fbtransform/transforms/react.js @@ -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); @@ -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); diff --git a/vendor/fbtransform/transforms/xjs.js b/vendor/fbtransform/transforms/xjs.js index faa9fb40f33..97b4f0432c1 100644 --- a/vendor/fbtransform/transforms/xjs.js +++ b/vendor/fbtransform/transforms/xjs.js @@ -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); }