Sync out latest JSX transforms
The biggest change: instead of calling functions directly, we now use React.createElement to wrap. The other big change: this removes the need for the @jsx pragma. Instead we'll parse everything, assume React is in scope, and only look for the actual XJS parts in the AST (as opposed to only runing the transform when @jsx was specified in the docblock). Note: this is actually a series of commits internally and should have been syned out in pieces over the past several weeks.
Paul O’Shannessy committed
Aug 20, 2014 at 00:24 UTC
2058ad99be59c986a534160f4897d27a1d455a01
4 files changed
+72
-104
vendor/fbtransform/transforms/__tests__/react-displayName-test.js
+10
-20
@@ -29,23 +29,21 @@ describe('react displayName jsx', function() {
29
30
it('should only inject displayName if missing', function() {
31
var code = [
32
- '/** @jsx React.DOM */',
32
'"use strict";',
33
'var Whateva = React.createClass({',
34
' displayName: \'Whateva\',',
35
' render: function() {',
37
- ' return <div className=\'whateva\'>...whateva.</div>;',
36
+ ' return null;',
37
' }',
38
'});'
39
].join('\n');
40
41
var result = [
43
- '/** @jsx React.DOM */',
42
'"use strict";',
43
'var Whateva = React.createClass({',
44
' displayName: \'Whateva\',',
45
' render: function() {',
48
- ' return React.DOM.div({className: "whateva"}, "...whateva.");',
46
+ ' return null;',
47
' }',
48
'});'
49
].join('\n');
@@ -55,19 +53,17 @@ describe('react displayName jsx', function() {
53
54
it('should inject displayName in simple assignment', () => {
55
var code = [
58
- '/** @jsx React.DOM */',
56
'var Component = React.createClass({',
57
' render: function() {',
61
- ' return <div/>;',
58
+ ' return null;',
59
' }',
60
'});'
61
].join('\n');
62
63
var result = [
67
- '/** @jsx React.DOM */',
64
'var Component = React.createClass({displayName: \'Component\',',
65
' render: function() {',
70
- ' return React.DOM.div(null);',
66
+ ' return null;',
67
' }',
68
'});'
69
].join('\n');
@@ -77,21 +73,19 @@ describe('react displayName jsx', function() {
73
74
it('should inject displayName in simple assignment without var', () => {
75
var code = [
80
- '/** @jsx React.DOM */',
76
'var Component;',
77
'Component = React.createClass({',
78
' render: function() {',
84
- ' return <div/>;',
79
+ ' return null;',
80
' }',
81
'});'
82
].join('\n');
83
84
var result = [
90
- '/** @jsx React.DOM */',
85
'var Component;',
86
'Component = React.createClass({displayName: \'Component\',',
87
' render: function() {',
94
- ' return React.DOM.div(null);',
88
+ ' return null;',
89
' }',
90
'});'
91
].join('\n');
@@ -101,19 +95,17 @@ describe('react displayName jsx', function() {
95
96
it('should inject displayName in property assignment', () => {
97
var code = [
104
- '/** @jsx React.DOM */',
98
'exports.Component = React.createClass({',
99
' render: function() {',
107
- ' return <div/>;',
100
+ ' return null;',
101
' }',
102
'});'
103
].join('\n');
104
105
var result = [
113
- '/** @jsx React.DOM */',
106
'exports.Component = React.createClass({displayName: \'Component\',',
107
' render: function() {',
116
- ' return React.DOM.div(null);',
108
+ ' return null;',
109
' }',
110
'});'
111
].join('\n');
@@ -123,22 +115,20 @@ describe('react displayName jsx', function() {
115
116
it('should inject displayName in object declaration', () => {
117
var code = [
126
- '/** @jsx React.DOM */',
118
'exports = {',
119
' Component: React.createClass({',
120
' render: function() {',
130
- ' return <div/>;',
121
+ ' return null;',
122
' }',
123
' })',
124
'};'
125
].join('\n');
126
127
var result = [
137
- '/** @jsx React.DOM */',
128
'exports = {',
129
' Component: React.createClass({displayName: \'Component\',',
130
' render: function() {',
141
- ' return React.DOM.div(null);',
131
+ ' return null;',
132
' }',
133
' })',
134
'};'
vendor/fbtransform/transforms/__tests__/react-test.js
+44
-68
@@ -15,6 +15,9 @@
15
*
16
* @emails jeffmo@fb.com
17
*/
18
+
19
+/*jshint evil:true, unused:false*/
20
+
21
"use strict";
22
23
require('mock-modules').autoMockOff();
@@ -42,20 +45,19 @@ describe('react jsx', function() {
45
var y = 789012;
46
var z = 345678;
47
45
- var HEADER =
46
- '/**\n' +
47
- ' * @jsx React.DOM\n' +
48
- ' */\n';
49
-
48
var expectObjectAssign = function(code) {
49
var Component = jest.genMockFunction();
50
var Child = jest.genMockFunction();
51
var objectAssignMock = jest.genMockFunction();
52
Object.assign = objectAssignMock;
55
- eval(transform(HEADER + code).code);
53
+ eval(transform(code).code);
54
return expect(objectAssignMock);
55
}
56
57
+ var React = {
58
+ createElement: jest.genMockFunction()
59
+ };
60
+
61
it('should convert simple tags', function() {
62
var code = [
63
'/**@jsx React.DOM*/',
@@ -63,7 +65,7 @@ describe('react jsx', function() {
65
].join('\n');
66
var result = [
67
'/**@jsx React.DOM*/',
66
- 'var x = React.DOM.div(null);'
68
+ 'var x = React.createElement(React.DOM.div, null);'
69
].join('\n');
70
71
expect(transform(code).code).toEqual(result);
@@ -76,7 +78,7 @@ describe('react jsx', function() {
78
].join('\n');
79
var result = [
80
'/**@jsx React.DOM*/',
79
- 'var x = React.DOM.div(null, "text");'
81
+ 'var x = React.createElement(React.DOM.div, null, "text");'
82
].join('\n');
83
84
expect(transform(code).code).toEqual(result);
@@ -93,10 +95,12 @@ describe('react jsx', function() {
95
].join('\n');
96
var result = [
97
'/**@jsx React.DOM*/',
96
- 'var x = React.DOM.div(null, ',
97
- ' React.DOM.div(null, React.DOM.br(null)), ',
98
- ' Component(null, foo, React.DOM.br(null), bar), ',
99
- ' React.DOM.br(null)',
98
+ 'var x = React.createElement(React.DOM.div, null, ',
99
+ ' React.createElement(React.DOM.div, null, ' +
100
+ 'React.createElement(React.DOM.br, null)), ',
101
+ ' React.createElement(Component, null, foo, ' +
102
+ 'React.createElement(React.DOM.br, null), bar), ',
103
+ ' React.createElement(React.DOM.br, null)',
104
');'
105
].join('\n');
106
@@ -113,8 +117,8 @@ describe('react jsx', function() {
117
].join('\n');
118
var result = [
119
'/**@jsx React.DOM*/',
116
- 'var x = React.DOM.div(null, ',
117
- ' Component(null)',
120
+ 'var x = React.createElement(React.DOM.div, null, ',
121
+ ' React.createElement(Component, null)',
122
');'
123
].join('\n');
124
@@ -129,7 +133,7 @@ describe('react jsx', function() {
133
].join('\n');
134
result = [
135
'/**@jsx React.DOM*/',
132
- 'var x = React.DOM.div(null, ',
136
+ 'var x = React.createElement(React.DOM.div, null, ',
137
' this.props.children',
138
');'
139
].join('\n');
@@ -144,7 +148,7 @@ describe('react jsx', function() {
148
].join('\n');
149
result = [
150
'/**@jsx React.DOM*/',
147
- 'var x = Composite(null, ',
151
+ 'var x = React.createElement(Composite, null, ',
152
' this.props.children',
153
');'
154
].join('\n');
@@ -159,8 +163,8 @@ describe('react jsx', function() {
163
].join('\n');
164
result = [
165
'/**@jsx React.DOM*/',
162
- 'var x = Composite(null, ',
163
- ' Composite2(null)',
166
+ 'var x = React.createElement(Composite, null, ',
167
+ ' React.createElement(Composite2, null)',
168
');'
169
].join('\n');
170
expect(transform(code).code).toEqual(result);
@@ -190,7 +194,7 @@ describe('react jsx', function() {
194
var result = [
195
'/**@jsx React.DOM*/',
196
'var x =',
193
- ' React.DOM.div({',
197
+ ' React.createElement(React.DOM.div, {',
198
' attr1: ',
199
' "foo" + "bar", ',
200
' ',
@@ -235,14 +239,14 @@ describe('react jsx', function() {
239
' * @jsx React.DOM',
240
' */',
241
'var x = (',
238
- ' React.DOM.div(null, ',
242
+ ' React.createElement(React.DOM.div, null, ',
243
' /* A comment at the beginning */',
244
' /* A second comment at the beginning */',
241
- ' React.DOM.span(null',
245
+ ' React.createElement(React.DOM.span, null',
246
' /* A nested comment */',
247
' ), ',
248
' /* A sandwiched comment */',
245
- ' React.DOM.br(null)',
249
+ ' React.createElement(React.DOM.br, null)',
250
' /* A comment at the end */',
251
' /* A second comment at the end */',
252
' )',
@@ -273,11 +277,11 @@ describe('react jsx', function() {
277
' * @jsx React.DOM',
278
' */',
279
'var x = (',
276
- ' React.DOM.div({',
280
+ ' React.createElement(React.DOM.div, {',
281
' /* a multi-line',
282
' comment */',
283
' attr1: "foo"}, ',
280
- ' React.DOM.span({// a double-slash comment',
284
+ ' React.createElement(React.DOM.span, {// a double-slash comment',
285
' attr2: "bar"}',
286
' )',
287
' )',
@@ -298,7 +302,7 @@ describe('react jsx', function() {
302
'/**',
303
' * @jsx React.DOM',
304
' */',
301
- 'React.DOM.div(null, "\u00A0");'
305
+ 'React.createElement(React.DOM.div, null, "\u00A0");'
306
].join('\n');
307
308
expect(transform(code).code).toBe(result);
@@ -315,59 +319,29 @@ describe('react jsx', function() {
319
'/**',
320
' * @jsx React.DOM',
321
' */',
318
- 'React.DOM.div(null, "\u00A0 ");'
322
+ 'React.createElement(React.DOM.div, null, "\u00A0 ");'
323
].join('\n');
324
325
expect(transform(code).code).toBe(result);
326
});
327
328
it('should handle hasOwnProperty correctly', function() {
325
- var code = [
326
- '/**',
327
- ' * @jsx React.DOM',
328
- ' */',
329
- '<hasOwnProperty>testing</hasOwnProperty>;'
330
- ].join('\n');
331
- var result = [
332
- '/**',
333
- ' * @jsx React.DOM',
334
- ' */',
335
- 'hasOwnProperty(null, "testing");'
336
- ].join('\n');
329
+ var code = '<hasOwnProperty>testing</hasOwnProperty>;';
330
+ var result = 'React.createElement(hasOwnProperty, null, "testing");';
331
332
expect(transform(code).code).toBe(result);
333
});
334
335
it('should allow constructor as prop', function() {
342
- var code = [
343
- '/**',
344
- ' * @jsx React.DOM',
345
- ' */',
346
- '<Component constructor="foo" />;'
347
- ].join('\n');
348
- var result = [
349
- '/**',
350
- ' * @jsx React.DOM',
351
- ' */',
352
- 'Component({constructor: "foo"});'
353
- ].join('\n');
336
+ var code = '<Component constructor="foo" />;';
337
+ var result = 'React.createElement(Component, {constructor: "foo"});';
338
339
expect(transform(code).code).toBe(result);
340
});
341
342
it('should allow JS namespacing', function() {
359
- var code = [
360
- '/**',
361
- ' * @jsx React.DOM',
362
- ' */',
363
- '<Namespace.Component />;'
364
- ].join('\n');
365
- var result = [
366
- '/**',
367
- ' * @jsx React.DOM',
368
- ' */',
369
- 'Namespace.Component(null);'
370
- ].join('\n');
343
+ var code = '<Namespace.Component />;';
344
+ var result = 'React.createElement(Namespace.Component, null);';
345
346
expect(transform(code).code).toBe(result);
347
});
@@ -383,7 +357,7 @@ describe('react jsx', function() {
357
'/**',
358
' * @jsx React.DOM',
359
' */',
386
- 'Namespace.DeepNamespace.Component(null);'
360
+ 'React.createElement(Namespace.DeepNamespace.Component, null);'
361
].join('\n');
362
363
expect(transform(code).code).toBe(result);
@@ -401,10 +375,12 @@ describe('react jsx', function() {
375
});
376
377
it('wraps props in Object.assign for spread attributes', function() {
404
- var code = HEADER +
405
- '<Component { ... x } y\n={2 } z />';
406
- var result = HEADER +
407
- 'Component(Object.assign({}, x , {y: \n2, z: true}))';
378
+ var code =
379
+ '<Component { ... x } y\n' +
380
+ '={2 } z />';
381
+ var result =
382
+ 'React.createElement(Component, Object.assign({}, x , {y: \n' +
383
+ '2, z: true}))';
384
385
expect(transform(code).code).toBe(result);
386
});
@@ -420,7 +396,7 @@ describe('react jsx', function() {
396
'/**',
397
' * @jsx React.DOM',
398
' */',
423
- 'React.DOM[\'font-face\'](null);'
399
+ 'React.createElement(React.DOM[\'font-face\'], null);'
400
].join('\n');
401
402
expect(transform(code).code).toBe(result);
vendor/fbtransform/transforms/react.js
+13
-4
@@ -69,6 +69,13 @@ function visitReactTag(traverse, object, path, state) {
69
throw new Error('Namespace tags are not supported. ReactJSX is not XML.');
70
}
71
72
+ var isReact = jsxObjIdent !== 'JSXDOM';
73
+
74
+ // We assume that the React runtime is already in scope
75
+ if (isReact) {
76
+ utils.append('React.createElement(', state);
77
+ }
78
+
79
// Only identifiers can be fallback tags or need quoting. We don't need to
80
// handle quoting for other types.
81
var didAddTag = false;
@@ -106,7 +113,11 @@ function visitReactTag(traverse, object, path, state) {
113
utils.catchup(nameObject.range[1], state);
114
}
115
109
- utils.append('(', state);
116
+ if (isReact) {
117
+ utils.append(', ', state);
118
+ } else {
119
+ utils.append('(', state);
120
+ }
121
122
var hasAttributes = attributesObject.length;
123
@@ -278,9 +289,7 @@ function visitReactTag(traverse, object, path, state) {
289
}
290
291
visitReactTag.test = function(object, path, state) {
281
- // only run react when react @jsx namespace is specified in docblock
282
- var jsx = utils.getDocblock(state).jsx;
283
- return object.type === Syntax.XJSElement && jsx && jsx.length;
292
+ return object.type === Syntax.XJSElement;
293
};
294
295
exports.visitorList = [
vendor/fbtransform/transforms/reactDisplayName.js
+5
-12
@@ -87,19 +87,12 @@ function visitReactDisplayName(traverse, object, path, state) {
87
}
88
}
89
90
-/**
91
- * Will only run on @jsx files for now.
92
- */
90
visitReactDisplayName.test = function(object, path, state) {
94
- if (utils.getDocblock(state).jsx) {
95
- return (
96
- object.type === Syntax.AssignmentExpression ||
97
- object.type === Syntax.Property ||
98
- object.type === Syntax.VariableDeclarator
99
- );
100
- } else {
101
- return false;
102
- }
91
+ return (
92
+ object.type === Syntax.AssignmentExpression ||
93
+ object.type === Syntax.Property ||
94
+ object.type === Syntax.VariableDeclarator
95
+ );
96
};
97
98
exports.visitorList = [