@samitouri / QOS-React-2 / commits / 2058ad99be

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 = [