@samitouri / QOS-React-2 / commits / 989caa870d

Support export default in the class codemod.

cpojer committed Aug 13, 2015 at 19:08 UTC 989caa870d614458aee2f40fb08b2c1229a04248
5 files changed +67 -48
packages/react-codemod/README.md
+4 -4
@@ -25,8 +25,8 @@ calls.
25 * `jscodeshift -t react/packages/react-codemod/transforms/findDOMNode.js <file>`
26
27 `pure-render-mixin` removes `PureRenderMixin` and inlines
28 -`shouldComponentUpdate` so that the ES6 class transform can pick up the React
29 -component and turn it into an ES6 class. NOTE: This currently only works if you
28 +`shouldComponentUpdate` so that the ES2015 class transform can pick up the React
29 +component and turn it into an ES2015 class. NOTE: This currently only works if you
30 are using the master version (>0.13.1) of React as it is using
31 `React.addons.shallowCompare`
32
@@ -36,7 +36,7 @@ are using the master version (>0.13.1) of React as it is using
36 namespaced name for the mixin. `mixins: [React.addons.PureRenderMixin]` will
37 not currently work.
38
39 -`class` transforms `React.createClass` calls into ES6 classes.
39 +`class` transforms `React.createClass` calls into ES2015 classes.
40
41 * `jscodeshift -t react/packages/react-codemod/transforms/class.js <file>`
42 * If `--no-super-class=true` is specified it will not extend
@@ -49,7 +49,7 @@ All scripts take an option `--no-explicit-require=true` if you don't have a
49 `require('React')` statement in your code files and if you access React as a
50 global.
51
52 -### Explanation of the ES6 class transform
52 +### Explanation of the ES2015 class transform
53
54 * Ignore components with calls to deprecated APIs. This is very defensive, if
55 the script finds any identifiers called `isMounted`, `getDOMNode`,
packages/react-codemod/test/export-default-class-test.js
+4
@@ -9,6 +9,10 @@ export default React.createClass({
9 };
10 },
11
12 + propTypes: {
13 + foo: React.PropTypes.string,
14 + },
15 +
16 render: function() {
17 return <div />;
18 }
packages/react-codemod/test/export-default-class-test.output.js
+5 -2
@@ -11,8 +11,11 @@ export default class extends React.Component {
11 };
12 }
13
14 + static propTypes = {
15 + foo: React.PropTypes.string,
16 + };
17 +
18 render() {
19 return <div />;
20 }
17 -};
18 -
21 +}
packages/react-codemod/transforms/class.js
+42 -25
@@ -10,7 +10,7 @@
10
11 'use strict';
12
13 -function updateReactCreateClassToES6(file, api, options) {
13 +module.exports = (file, api, options) => {
14 const j = api.jscodeshift;
15
16 require('./utils/array-polyfills');
@@ -341,9 +341,9 @@ function updateReactCreateClassToES6(file, api, options) {
341 ];
342 };
343
344 - const createES6Class = (
344 + const createESClass = (
345 name,
346 - functions,
346 + properties,
347 getInitialState,
348 autobindFunctions,
349 comments,
@@ -358,7 +358,7 @@ function updateReactCreateClassToES6(file, api, options) {
358 autobindFunctions,
359 shouldAddSuperClass
360 ),
361 - functions.map(createMethodDefinition)
361 + properties
362 )
363 ),
364 shouldAddSuperClass ? j.memberExpression(
@@ -384,6 +384,17 @@ function updateReactCreateClassToES6(file, api, options) {
384 const createStaticAssignmentExpressions = (name, statics) =>
385 statics.map(staticProperty => createStaticAssignment(name, staticProperty));
386
387 + const createStaticClassProperty = staticProperty =>
388 + withComments(j.classProperty(
389 + j.identifier(staticProperty.key.name),
390 + staticProperty.value,
391 + null,
392 + true
393 + ), staticProperty);
394 +
395 + const createStaticClassProperties = statics =>
396 + statics.map(createStaticClassProperty);
397 +
398 const hasSingleReturnStatement = value => (
399 value.type === 'FunctionExpression' &&
400 value.body &&
@@ -434,7 +445,7 @@ function updateReactCreateClassToES6(file, api, options) {
445 false
446 );
447
437 - const updateToES6Class = (classPath, shouldExtend, isModuleExports) => {
448 + const updateToClass = (classPath, shouldExtend, type) => {
449 const specPath = ReactUtils.getReactCreateClassSpec(classPath);
450 const name = ReactUtils.getComponentName(classPath);
451 const statics = collectStatics(specPath);
@@ -448,16 +459,19 @@ function updateReactCreateClassToES6(file, api, options) {
459 name ? j.identifier(name) : createModuleExportsMemberExpression();
460
461 var path;
451 - if (isModuleExports) {
462 + if (type == 'moduleExports' || type == 'exportDefault') {
463 path = ReactUtils.findReactCreateClassCallExpression(classPath);
464 } else {
465 path = j(classPath).closest(j.VariableDeclaration);
466 }
467
468 + const properties =
469 + (type == 'exportDefault') ? createStaticClassProperties(statics) : [];
470 +
471 path.replaceWith(
458 - createES6Class(
472 + createESClass(
473 name,
460 - functions,
474 + properties.concat(functions.map(createMethodDefinition)),
475 getInitialState,
476 autobindFunctions,
477 comments,
@@ -465,43 +479,46 @@ function updateReactCreateClassToES6(file, api, options) {
479 )
480 );
481
468 - const staticAssignments =
469 - createStaticAssignmentExpressions(staticName, statics);
470 - if (isModuleExports) {
471 - const body = root.get().value.body;
472 - body.push.apply(body, staticAssignments);
473 - } else {
474 - staticAssignments
475 - .forEach(expression => path = path.insertAfter(expression));
482 + if (type == 'moduleExports' || type == 'var') {
483 + const staticAssignments = createStaticAssignmentExpressions(
484 + staticName,
485 + statics
486 + );
487 + if (type == 'moduleExports') {
488 + root.get().value.program.body.push(...staticAssignments);
489 + } else {
490 + path.insertAfter(staticAssignments.reverse());
491 + }
492 }
493 };
494
495 if (
496 options['no-explicit-require'] || ReactUtils.hasReact(root)
497 ) {
482 - const apply = (path, isModuleExports) =>
498 + const apply = (path, type) =>
499 path
500 .filter(hasMixins)
501 .filter(callsDeprecatedAPIs)
486 - .forEach(classPath => updateToES6Class(
502 + .forEach(classPath => updateToClass(
503 classPath,
504 !options['no-super-class'],
489 - isModuleExports
505 + type
506 ));
507
508 const didTransform = (
493 - apply(ReactUtils.findReactCreateClass(root), false).size() +
494 - apply(ReactUtils.findReactCreateClassModuleExports(root), true).size() +
495 - apply(ReactUtils.findReactCreateClassExportDefault(root), false).size()
509 + apply(ReactUtils.findReactCreateClass(root), 'var')
510 + .size() +
511 + apply(ReactUtils.findReactCreateClassModuleExports(root), 'moduleExports')
512 + .size() +
513 + apply(ReactUtils.findReactCreateClassExportDefault(root), 'exportDefault')
514 + .size()
515 ) > 0;
516
517 if (didTransform) {
518 return root.toSource(printOptions);
519 }
501 -
520 +
521 }
522
523 return null;
524 }
506 -
507 -module.exports = updateReactCreateClassToES6;
packages/react-codemod/transforms/utils/ReactUtils.js
+12 -17
@@ -34,9 +34,9 @@ module.exports = function(j) {
34 source: {
35 type: 'Literal',
36 }
37 - }).filter((importDeclarator) => {
38 - return importDeclarator.value.source.value === module;
39 - }).size() === 1
37 + })
38 + .filter(declarator => declarator.value.source.value === module)
39 + .size() === 1
40
41 const hasReact = path => (
42 hasModule(path, 'React') ||
@@ -56,19 +56,13 @@ module.exports = function(j) {
56 .findVariableDeclarators()
57 .filter(decl => findReactCreateClassCallExpression(decl).size() > 0);
58
59 - const findReactCreateClassExportDefault = path => {
60 - var collection = [];
61 - path
62 - .find(j.ExportDefaultDeclaration, {
63 - type: 'ExportDefaultDeclaration',
64 - declaration: {
65 - type: 'CallExpression',
66 - callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION
67 - }
68 - })
69 - .forEach((p) => collection.push(p.value.declaration))
70 - return j(collection);
71 - }
59 + const findReactCreateClassExportDefault = path =>
60 + path.find(j.ExportDefaultDeclaration, {
61 + declaration: {
62 + type: 'CallExpression',
63 + callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION
64 + }
65 + });
66
67 const findReactCreateClassModuleExports = path =>
68 path
@@ -129,7 +123,8 @@ module.exports = function(j) {
123 // ---------------------------------------------------------------------------
124 // Others
125 const getReactCreateClassSpec = classPath => {
132 - const spec = (classPath.value.init || classPath.value.right || classPath.value).arguments[0]
126 + var {value} = classPath;
127 + const spec = (value.init || value.right || value.declaration).arguments[0];
128 if (spec.type === 'ObjectExpression' && Array.isArray(spec.properties)) {
129 return spec;
130 }