Skip to content

Commit db04043

Browse files
Jeff MorrisonJeff Morrison
authored andcommitted
Merge pull request facebook#480 from syranide/whitespace
JSX whitespace coalescing rules
2 parents 38491c7 + c16b565 commit db04043

File tree

3 files changed

+64
-119
lines changed

3 files changed

+64
-119
lines changed

src/core/__tests__/ReactRenderDocument-test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('rendering React components at document', function() {
6767
React.renderComponentToString(<Root />, function(markup) {
6868
testDocument = getTestDocument(markup);
6969
var component = React.renderComponent(<Root />, testDocument);
70-
expect(testDocument.body.innerHTML).toBe(' Hello world ');
70+
expect(testDocument.body.innerHTML).toBe('Hello world');
7171

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

100100
expect(function() {
101101
React.unmountComponentAtNode(testDocument);
102102
}).toThrow(UNMOUNT_INVARIANT_MESSAGE);
103103

104-
expect(testDocument.body.innerHTML).toBe(' Hello world ');
104+
expect(testDocument.body.innerHTML).toBe('Hello world');
105105
});
106106
});
107107

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

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

146-
expect(testDocument.body.innerHTML).toBe(' Hello world ');
146+
expect(testDocument.body.innerHTML).toBe('Hello world');
147147

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

153-
expect(testDocument.body.innerHTML).toBe(' Hello world ');
153+
expect(testDocument.body.innerHTML).toBe('Hello world');
154154
});
155155
});
156156

vendor/fbtransform/transforms/react.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ function visitReactTag(traverse, object, path, state) {
6868

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

71-
var childrenToRender = object.children.filter(function(child) {
72-
return !(child.type === Syntax.Literal && !child.value.match(/\S/));
73-
});
74-
7571
// if we don't have any attributes, pass in null
7672
if (object.attributes.length === 0) {
7773
utils.append('null', state);
@@ -131,16 +127,18 @@ function visitReactTag(traverse, object, path, state) {
131127
}
132128

133129
// filter out whitespace
130+
var childrenToRender = object.children.filter(function(child) {
131+
return !(child.type === Syntax.Literal &&
132+
child.value.match(/^[ \t]*[\r\n][ \t\r\n]*$/));
133+
});
134+
134135
if (childrenToRender.length > 0) {
135136
utils.append(', ', state);
136137

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

143-
var isLast = child === childrenToRender[childrenToRender.length - 1];
141+
var isLast = index === childrenToRender.length - 1;
144142

145143
if (child.type === Syntax.Literal) {
146144
renderXJSLiteral(child, isLast, state);

vendor/fbtransform/transforms/xjs.js

Lines changed: 52 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -149,118 +149,65 @@ var knownTags = {
149149
wbr: true
150150
};
151151

152-
function safeTrim(string) {
153-
return string.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
154-
}
155-
156-
// Replace all trailing whitespace characters with a single space character
157-
function trimWithSingleSpace(string) {
158-
return string.replace(/^[ \t\xA0]{2,}/, ' ').
159-
replace(/[ \t\xA0]{2,}$/, ' ').replace(/^\s+$/, '');
160-
}
161-
162-
/**
163-
* Special handling for multiline string literals
164-
* print lines:
165-
*
166-
* line
167-
* line
168-
*
169-
* as:
170-
*
171-
* "line "+
172-
* "line"
173-
*/
174152
function renderXJSLiteral(object, isLast, state, start, end) {
175-
/** Added blank check filtering and triming*/
176-
var trimmedChildValue = safeTrim(object.value);
177-
var hasFinalNewLine = false;
178-
179-
if (trimmedChildValue) {
180-
// head whitespace
181-
utils.append(object.value.match(/^[\t ]*/)[0], state);
182-
if (start) {
183-
utils.append(start, state);
153+
var lines = object.value.split(/\r\n|\n|\r/);
154+
155+
if (start) {
156+
utils.append(start, state);
157+
}
158+
159+
var lastNonEmptyLine = 0;
160+
161+
lines.forEach(function (line, index) {
162+
if (line.match(/[^ \t]/)) {
163+
lastNonEmptyLine = index;
184164
}
185-
186-
var trimmedChildValueWithSpace = trimWithSingleSpace(object.value);
187-
188-
/**
189-
*/
190-
var initialLines = trimmedChildValue.split(/\r\n|\n|\r/);
191-
192-
var lines = initialLines.filter(function(line) {
193-
return safeTrim(line).length > 0;
194-
});
195-
196-
var hasInitialNewLine = initialLines[0] !== lines[0];
197-
hasFinalNewLine =
198-
initialLines[initialLines.length - 1] !== lines[lines.length - 1];
199-
200-
var numLines = lines.length;
201-
lines.forEach(function (line, ii) {
202-
var lastLine = ii === numLines - 1;
203-
var trimmedLine = safeTrim(line);
204-
if (trimmedLine === '' && !lastLine) {
205-
utils.append(line, state);
206-
} else {
207-
var preString = '';
208-
var postString = '';
209-
var leading = line.match(/^[ \t]*/)[0];
210-
211-
if (ii === 0) {
212-
if (hasInitialNewLine) {
213-
preString = ' ';
214-
leading = '\n' + leading;
215-
}
216-
if (trimmedChildValueWithSpace.substring(0, 1) === ' ') {
217-
// If this is the first line, and the original content starts with
218-
// whitespace, place a single space at the beginning.
219-
preString = ' ';
220-
}
165+
});
166+
167+
lines.forEach(function (line, index) {
168+
var isFirstLine = index === 0;
169+
var isLastLine = index === lines.length - 1;
170+
var isLastNonEmptyLine = index === lastNonEmptyLine;
171+
172+
// replace rendered whitespace tabs with spaces
173+
var trimmedLine = line.replace(/\t/g, ' ');
174+
175+
// trim whitespace touching a newline
176+
if (!isFirstLine) {
177+
trimmedLine = trimmedLine.replace(/^[ ]+/, '');
178+
}
179+
if (!isLastLine) {
180+
trimmedLine = trimmedLine.replace(/[ ]+$/, '');
181+
}
182+
183+
utils.append(line.match(/^[ \t]*/)[0], state);
184+
185+
if (trimmedLine || isLastNonEmptyLine) {
186+
utils.append(
187+
JSON.stringify(trimmedLine) +
188+
(!isLastNonEmptyLine ? "+' '+" : ''),
189+
state);
190+
191+
if (isLastNonEmptyLine) {
192+
if (end) {
193+
utils.append(end, state);
221194
}
222-
if (!lastLine || trimmedChildValueWithSpace.substr(
223-
trimmedChildValueWithSpace.length - 1, 1) === ' ' ||
224-
hasFinalNewLine
225-
) {
226-
// If either not on the last line, or the original content ends with
227-
// whitespace, place a single character at the end.
228-
postString = ' ';
195+
if (!isLast) {
196+
utils.append(',', state);
229197
}
230-
231-
utils.append(
232-
leading +
233-
JSON.stringify(
234-
preString + trimmedLine + postString
235-
) +
236-
(lastLine ? '' : '+') +
237-
line.match(/[ \t]*$/)[0],
238-
state);
239198
}
240-
if (!lastLine) {
241-
utils.append('\n', state);
199+
200+
// only restore tail whitespace if line had literals
201+
if (trimmedLine) {
202+
utils.append(line.match(/[ \t]*$/)[0], state);
242203
}
243-
});
244-
} else {
245-
if (start) {
246-
utils.append(start, state);
247204
}
248-
utils.append('""', state);
249-
}
250-
if (end) {
251-
utils.append(end, state);
252-
}
253-
254-
// add comma before trailing whitespace
255-
if (!isLast) {
256-
utils.append(',', state);
257-
}
258-
259-
// tail whitespace
260-
if (hasFinalNewLine) {
261-
utils.append('\n', state);
262-
}
263-
utils.append(object.value.match(/[ \t]*$/)[0], state);
205+
206+
if (!isLastLine) {
207+
utils.append('\n', state);
208+
}
209+
});
210+
264211
utils.move(object.range[1], state);
265212
}
266213

0 commit comments

Comments
 (0)