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.
This commit is contained in:
parent
aae31ae24c
commit
2058ad99be
|
@ -29,23 +29,21 @@ describe('react displayName jsx', function() {
|
|||
|
||||
it('should only inject displayName if missing', function() {
|
||||
var code = [
|
||||
'/** @jsx React.DOM */',
|
||||
'"use strict";',
|
||||
'var Whateva = React.createClass({',
|
||||
' displayName: \'Whateva\',',
|
||||
' render: function() {',
|
||||
' return <div className=\'whateva\'>...whateva.</div>;',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
||||
var result = [
|
||||
'/** @jsx React.DOM */',
|
||||
'"use strict";',
|
||||
'var Whateva = React.createClass({',
|
||||
' displayName: \'Whateva\',',
|
||||
' render: function() {',
|
||||
' return React.DOM.div({className: "whateva"}, "...whateva.");',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
@ -55,19 +53,17 @@ describe('react displayName jsx', function() {
|
|||
|
||||
it('should inject displayName in simple assignment', () => {
|
||||
var code = [
|
||||
'/** @jsx React.DOM */',
|
||||
'var Component = React.createClass({',
|
||||
' render: function() {',
|
||||
' return <div/>;',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
||||
var result = [
|
||||
'/** @jsx React.DOM */',
|
||||
'var Component = React.createClass({displayName: \'Component\',',
|
||||
' render: function() {',
|
||||
' return React.DOM.div(null);',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
@ -77,21 +73,19 @@ describe('react displayName jsx', function() {
|
|||
|
||||
it('should inject displayName in simple assignment without var', () => {
|
||||
var code = [
|
||||
'/** @jsx React.DOM */',
|
||||
'var Component;',
|
||||
'Component = React.createClass({',
|
||||
' render: function() {',
|
||||
' return <div/>;',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
||||
var result = [
|
||||
'/** @jsx React.DOM */',
|
||||
'var Component;',
|
||||
'Component = React.createClass({displayName: \'Component\',',
|
||||
' render: function() {',
|
||||
' return React.DOM.div(null);',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
@ -101,19 +95,17 @@ describe('react displayName jsx', function() {
|
|||
|
||||
it('should inject displayName in property assignment', () => {
|
||||
var code = [
|
||||
'/** @jsx React.DOM */',
|
||||
'exports.Component = React.createClass({',
|
||||
' render: function() {',
|
||||
' return <div/>;',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
||||
var result = [
|
||||
'/** @jsx React.DOM */',
|
||||
'exports.Component = React.createClass({displayName: \'Component\',',
|
||||
' render: function() {',
|
||||
' return React.DOM.div(null);',
|
||||
' return null;',
|
||||
' }',
|
||||
'});'
|
||||
].join('\n');
|
||||
|
@ -123,22 +115,20 @@ describe('react displayName jsx', function() {
|
|||
|
||||
it('should inject displayName in object declaration', () => {
|
||||
var code = [
|
||||
'/** @jsx React.DOM */',
|
||||
'exports = {',
|
||||
' Component: React.createClass({',
|
||||
' render: function() {',
|
||||
' return <div/>;',
|
||||
' return null;',
|
||||
' }',
|
||||
' })',
|
||||
'};'
|
||||
].join('\n');
|
||||
|
||||
var result = [
|
||||
'/** @jsx React.DOM */',
|
||||
'exports = {',
|
||||
' Component: React.createClass({displayName: \'Component\',',
|
||||
' render: function() {',
|
||||
' return React.DOM.div(null);',
|
||||
' return null;',
|
||||
' }',
|
||||
' })',
|
||||
'};'
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
*
|
||||
* @emails jeffmo@fb.com
|
||||
*/
|
||||
|
||||
/*jshint evil:true, unused:false*/
|
||||
|
||||
"use strict";
|
||||
|
||||
require('mock-modules').autoMockOff();
|
||||
|
@ -42,20 +45,19 @@ describe('react jsx', function() {
|
|||
var y = 789012;
|
||||
var z = 345678;
|
||||
|
||||
var HEADER =
|
||||
'/**\n' +
|
||||
' * @jsx React.DOM\n' +
|
||||
' */\n';
|
||||
|
||||
var expectObjectAssign = function(code) {
|
||||
var Component = jest.genMockFunction();
|
||||
var Child = jest.genMockFunction();
|
||||
var objectAssignMock = jest.genMockFunction();
|
||||
Object.assign = objectAssignMock;
|
||||
eval(transform(HEADER + code).code);
|
||||
eval(transform(code).code);
|
||||
return expect(objectAssignMock);
|
||||
}
|
||||
|
||||
var React = {
|
||||
createElement: jest.genMockFunction()
|
||||
};
|
||||
|
||||
it('should convert simple tags', function() {
|
||||
var code = [
|
||||
'/**@jsx React.DOM*/',
|
||||
|
@ -63,7 +65,7 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
var result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = React.DOM.div(null);'
|
||||
'var x = React.createElement(React.DOM.div, null);'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toEqual(result);
|
||||
|
@ -76,7 +78,7 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
var result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = React.DOM.div(null, "text");'
|
||||
'var x = React.createElement(React.DOM.div, null, "text");'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toEqual(result);
|
||||
|
@ -93,10 +95,12 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
var result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = React.DOM.div(null, ',
|
||||
' React.DOM.div(null, React.DOM.br(null)), ',
|
||||
' Component(null, foo, React.DOM.br(null), bar), ',
|
||||
' React.DOM.br(null)',
|
||||
'var x = React.createElement(React.DOM.div, null, ',
|
||||
' React.createElement(React.DOM.div, null, ' +
|
||||
'React.createElement(React.DOM.br, null)), ',
|
||||
' React.createElement(Component, null, foo, ' +
|
||||
'React.createElement(React.DOM.br, null), bar), ',
|
||||
' React.createElement(React.DOM.br, null)',
|
||||
');'
|
||||
].join('\n');
|
||||
|
||||
|
@ -113,8 +117,8 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
var result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = React.DOM.div(null, ',
|
||||
' Component(null)',
|
||||
'var x = React.createElement(React.DOM.div, null, ',
|
||||
' React.createElement(Component, null)',
|
||||
');'
|
||||
].join('\n');
|
||||
|
||||
|
@ -129,7 +133,7 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = React.DOM.div(null, ',
|
||||
'var x = React.createElement(React.DOM.div, null, ',
|
||||
' this.props.children',
|
||||
');'
|
||||
].join('\n');
|
||||
|
@ -144,7 +148,7 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = Composite(null, ',
|
||||
'var x = React.createElement(Composite, null, ',
|
||||
' this.props.children',
|
||||
');'
|
||||
].join('\n');
|
||||
|
@ -159,8 +163,8 @@ describe('react jsx', function() {
|
|||
].join('\n');
|
||||
result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x = Composite(null, ',
|
||||
' Composite2(null)',
|
||||
'var x = React.createElement(Composite, null, ',
|
||||
' React.createElement(Composite2, null)',
|
||||
');'
|
||||
].join('\n');
|
||||
expect(transform(code).code).toEqual(result);
|
||||
|
@ -190,7 +194,7 @@ describe('react jsx', function() {
|
|||
var result = [
|
||||
'/**@jsx React.DOM*/',
|
||||
'var x =',
|
||||
' React.DOM.div({',
|
||||
' React.createElement(React.DOM.div, {',
|
||||
' attr1: ',
|
||||
' "foo" + "bar", ',
|
||||
' ',
|
||||
|
@ -235,14 +239,14 @@ describe('react jsx', function() {
|
|||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'var x = (',
|
||||
' React.DOM.div(null, ',
|
||||
' React.createElement(React.DOM.div, null, ',
|
||||
' /* A comment at the beginning */',
|
||||
' /* A second comment at the beginning */',
|
||||
' React.DOM.span(null',
|
||||
' React.createElement(React.DOM.span, null',
|
||||
' /* A nested comment */',
|
||||
' ), ',
|
||||
' /* A sandwiched comment */',
|
||||
' React.DOM.br(null)',
|
||||
' React.createElement(React.DOM.br, null)',
|
||||
' /* A comment at the end */',
|
||||
' /* A second comment at the end */',
|
||||
' )',
|
||||
|
@ -273,11 +277,11 @@ describe('react jsx', function() {
|
|||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'var x = (',
|
||||
' React.DOM.div({',
|
||||
' React.createElement(React.DOM.div, {',
|
||||
' /* a multi-line',
|
||||
' comment */',
|
||||
' attr1: "foo"}, ',
|
||||
' React.DOM.span({// a double-slash comment',
|
||||
' React.createElement(React.DOM.span, {// a double-slash comment',
|
||||
' attr2: "bar"}',
|
||||
' )',
|
||||
' )',
|
||||
|
@ -298,7 +302,7 @@ describe('react jsx', function() {
|
|||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'React.DOM.div(null, "\u00A0");'
|
||||
'React.createElement(React.DOM.div, null, "\u00A0");'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
|
@ -315,59 +319,29 @@ describe('react jsx', function() {
|
|||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'React.DOM.div(null, "\u00A0 ");'
|
||||
'React.createElement(React.DOM.div, null, "\u00A0 ");'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
});
|
||||
|
||||
it('should handle hasOwnProperty correctly', function() {
|
||||
var code = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'<hasOwnProperty>testing</hasOwnProperty>;'
|
||||
].join('\n');
|
||||
var result = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'hasOwnProperty(null, "testing");'
|
||||
].join('\n');
|
||||
var code = '<hasOwnProperty>testing</hasOwnProperty>;';
|
||||
var result = 'React.createElement(hasOwnProperty, null, "testing");';
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
});
|
||||
|
||||
it('should allow constructor as prop', function() {
|
||||
var code = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'<Component constructor="foo" />;'
|
||||
].join('\n');
|
||||
var result = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'Component({constructor: "foo"});'
|
||||
].join('\n');
|
||||
var code = '<Component constructor="foo" />;';
|
||||
var result = 'React.createElement(Component, {constructor: "foo"});';
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
});
|
||||
|
||||
it('should allow JS namespacing', function() {
|
||||
var code = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'<Namespace.Component />;'
|
||||
].join('\n');
|
||||
var result = [
|
||||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'Namespace.Component(null);'
|
||||
].join('\n');
|
||||
var code = '<Namespace.Component />;';
|
||||
var result = 'React.createElement(Namespace.Component, null);';
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
});
|
||||
|
@ -383,7 +357,7 @@ describe('react jsx', function() {
|
|||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'Namespace.DeepNamespace.Component(null);'
|
||||
'React.createElement(Namespace.DeepNamespace.Component, null);'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
|
@ -401,10 +375,12 @@ describe('react jsx', function() {
|
|||
});
|
||||
|
||||
it('wraps props in Object.assign for spread attributes', function() {
|
||||
var code = HEADER +
|
||||
'<Component { ... x } y\n={2 } z />';
|
||||
var result = HEADER +
|
||||
'Component(Object.assign({}, x , {y: \n2, z: true}))';
|
||||
var code =
|
||||
'<Component { ... x } y\n' +
|
||||
'={2 } z />';
|
||||
var result =
|
||||
'React.createElement(Component, Object.assign({}, x , {y: \n' +
|
||||
'2, z: true}))';
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
});
|
||||
|
@ -420,7 +396,7 @@ describe('react jsx', function() {
|
|||
'/**',
|
||||
' * @jsx React.DOM',
|
||||
' */',
|
||||
'React.DOM[\'font-face\'](null);'
|
||||
'React.createElement(React.DOM[\'font-face\'], null);'
|
||||
].join('\n');
|
||||
|
||||
expect(transform(code).code).toBe(result);
|
||||
|
|
|
@ -69,6 +69,13 @@ function visitReactTag(traverse, object, path, state) {
|
|||
throw new Error('Namespace tags are not supported. ReactJSX is not XML.');
|
||||
}
|
||||
|
||||
var isReact = jsxObjIdent !== 'JSXDOM';
|
||||
|
||||
// We assume that the React runtime is already in scope
|
||||
if (isReact) {
|
||||
utils.append('React.createElement(', state);
|
||||
}
|
||||
|
||||
// Only identifiers can be fallback tags or need quoting. We don't need to
|
||||
// handle quoting for other types.
|
||||
var didAddTag = false;
|
||||
|
@ -106,7 +113,11 @@ function visitReactTag(traverse, object, path, state) {
|
|||
utils.catchup(nameObject.range[1], state);
|
||||
}
|
||||
|
||||
utils.append('(', state);
|
||||
if (isReact) {
|
||||
utils.append(', ', state);
|
||||
} else {
|
||||
utils.append('(', state);
|
||||
}
|
||||
|
||||
var hasAttributes = attributesObject.length;
|
||||
|
||||
|
@ -278,9 +289,7 @@ function visitReactTag(traverse, object, path, state) {
|
|||
}
|
||||
|
||||
visitReactTag.test = function(object, path, state) {
|
||||
// only run react when react @jsx namespace is specified in docblock
|
||||
var jsx = utils.getDocblock(state).jsx;
|
||||
return object.type === Syntax.XJSElement && jsx && jsx.length;
|
||||
return object.type === Syntax.XJSElement;
|
||||
};
|
||||
|
||||
exports.visitorList = [
|
||||
|
|
|
@ -87,19 +87,12 @@ function visitReactDisplayName(traverse, object, path, state) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will only run on @jsx files for now.
|
||||
*/
|
||||
visitReactDisplayName.test = function(object, path, state) {
|
||||
if (utils.getDocblock(state).jsx) {
|
||||
return (
|
||||
object.type === Syntax.AssignmentExpression ||
|
||||
object.type === Syntax.Property ||
|
||||
object.type === Syntax.VariableDeclarator
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
object.type === Syntax.AssignmentExpression ||
|
||||
object.type === Syntax.Property ||
|
||||
object.type === Syntax.VariableDeclarator
|
||||
);
|
||||
};
|
||||
|
||||
exports.visitorList = [
|
||||
|
|
Loading…
Reference in New Issue