Implement stricter whitespace rules
Andreas Svensson committed
Dec 4, 2013 at 22:55 UTC
c16b5659a0323572b2cd404f6356c7b38e38b038
3 files changed
+64
-119
src/core/__tests__/ReactRenderDocument-test.js
+5
-5
@@ -67,7 +67,7 @@ describe('rendering React components at document', function() {
67
React.renderComponentToString(<Root />, function(markup) {
68
testDocument = getTestDocument(markup);
69
var component = React.renderComponent(<Root />, testDocument);
70
- expect(testDocument.body.innerHTML).toBe(' Hello world ');
70
+ expect(testDocument.body.innerHTML).toBe('Hello world');
71
72
var componentID = ReactMount.getReactRootID(testDocument);
73
expect(componentID).toBe(component._rootNodeID);
@@ -95,13 +95,13 @@ describe('rendering React components at document', function() {
95
React.renderComponentToString(<Root />, function(markup) {
96
testDocument = getTestDocument(markup);
97
React.renderComponent(<Root />, testDocument);
98
- expect(testDocument.body.innerHTML).toBe(' Hello world ');
98
+ expect(testDocument.body.innerHTML).toBe('Hello world');
99
100
expect(function() {
101
React.unmountComponentAtNode(testDocument);
102
}).toThrow(UNMOUNT_INVARIANT_MESSAGE);
103
104
- expect(testDocument.body.innerHTML).toBe(' Hello world ');
104
+ expect(testDocument.body.innerHTML).toBe('Hello world');
105
});
106
});
107
@@ -143,14 +143,14 @@ describe('rendering React components at document', function() {
143
144
React.renderComponent(<Component />, testDocument);
145
146
- expect(testDocument.body.innerHTML).toBe(' Hello world ');
146
+ expect(testDocument.body.innerHTML).toBe('Hello world');
147
148
// Reactive update
149
expect(function() {
150
React.renderComponent(<Component2 />, testDocument);
151
}).toThrow(UNMOUNT_INVARIANT_MESSAGE);
152
153
- expect(testDocument.body.innerHTML).toBe(' Hello world ');
153
+ expect(testDocument.body.innerHTML).toBe('Hello world');
154
});
155
});
156
vendor/fbtransform/transforms/react.js
+7
-9
@@ -68,10 +68,6 @@ function visitReactTag(traverse, object, path, state) {
68
69
utils.move(object.name.range[1], state);
70
71
- var childrenToRender = object.children.filter(function(child) {
72
- return !(child.type === Syntax.Literal && !child.value.match(/\S/));
73
- });
74
-
71
// if we don't have any attributes, pass in null
72
if (object.attributes.length === 0) {
73
utils.append('null', state);
@@ -131,16 +127,18 @@ function visitReactTag(traverse, object, path, state) {
127
}
128
129
// 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
+
135
if (childrenToRender.length > 0) {
136
utils.append(', ', state);
137
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) {
139
utils.catchup(child.range[0], state);
140
143
- var isLast = child === childrenToRender[childrenToRender.length - 1];
141
+ var isLast = index === childrenToRender.length - 1;
142
143
if (child.type === Syntax.Literal) {
144
renderXJSLiteral(child, isLast, state);
vendor/fbtransform/transforms/xjs.js
+52
-105
@@ -149,118 +149,65 @@ var knownTags = {
149
wbr: true
150
};
151
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
- */
152
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;
164
}
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);
194
}
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);
197
}
230
-
231
- utils.append(
232
- leading +
233
- JSON.stringify(
234
- preString + trimmedLine + postString
235
- ) +
236
- (lastLine ? '' : '+') +
237
- line.match(/[ \t]*$/)[0],
238
- state);
198
}
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);
203
}
243
- });
244
- } else {
245
- if (start) {
246
- utils.append(start, state);
204
}
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
+
211
utils.move(object.range[1], state);
212
}
213