Do not codemod classes with invalid fields, fixes #4628
cpojer committed
Sep 3, 2015 at 17:04 UTC
cfe0351b8f95cc212570c9d158ad7cde155258d0
4 files changed
+47
-7
packages/react-codemod/package.json
+3
-3
@@ -11,11 +11,11 @@
11
"test": "jest"
12
},
13
"dependencies": {
14
- "jscodeshift": "^0.3.2"
14
+ "jscodeshift": "^0.3.7"
15
},
16
"devDependencies": {
17
- "babel-jest": "^4.0.0",
18
- "jest-cli": "^0.4.0"
17
+ "babel-jest": "^5.3.0",
18
+ "jest-cli": "^0.5.1"
19
},
20
"jest": {
21
"scriptPreprocessor": "./node_modules/babel-jest",
packages/react-codemod/test/class-test.js
+5
@@ -121,6 +121,11 @@ var MyComponent3 = React.createClass({
121
}
122
});
123
124
+var MyComponent4 = React.createClass({
125
+ foo: callMeMaybe(),
126
+ render: function() {},
127
+});
128
+
129
module.exports = Relay.createContainer(MyComponent, {
130
queries: {
131
me: Relay.graphql`this is not graphql`,
packages/react-codemod/test/class-test.output.js
+5
@@ -132,6 +132,11 @@ MyComponent3.propTypes = {
132
133
MyComponent3.someThing = 10;
134
135
+var MyComponent4 = React.createClass({
136
+ foo: callMeMaybe(),
137
+ render: function() {},
138
+});
139
+
140
module.exports = Relay.createContainer(MyComponent, {
141
queries: {
142
me: Relay.graphql`this is not graphql`,
packages/react-codemod/transforms/class.js
+34
-4
@@ -46,6 +46,8 @@ module.exports = (file, api, options) => {
46
'setProps',
47
];
48
49
+ const STATIC_KEY = 'statics';
50
+
51
const STATIC_KEYS = {
52
childContextTypes: true,
53
contextTypes: true,
@@ -67,8 +69,8 @@ module.exports = (file, api, options) => {
69
const callsDeprecatedAPIs = classPath => {
70
if (checkDeprecatedAPICalls(classPath)) {
71
console.log(
70
- file.path + ': "' + ReactUtils.getComponentName(classPath) + '" ' +
71
- 'skipped because of deprecated API calls. Remove calls to ' +
72
+ file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
73
+ 'was skipped because of deprecated API calls. Remove calls to ' +
74
DEPRECATED_APIS.join(', ') + ' in your React component and re-run ' +
75
'this script.'
76
);
@@ -77,11 +79,38 @@ module.exports = (file, api, options) => {
79
return true;
80
};
81
82
+ const canConvertToClass = classPath => {
83
+ const specPath = ReactUtils.getReactCreateClassSpec(classPath);
84
+ const invalidProperties = specPath.properties.filter(prop => (
85
+ !prop.key.name || (
86
+ !STATIC_KEYS[prop.key.name] &&
87
+ STATIC_KEY != prop.key.name &&
88
+ !filterDefaultPropsField(prop) &&
89
+ !filterGetInitialStateField(prop) &&
90
+ !isFunctionExpression(prop)
91
+ )
92
+ ));
93
+
94
+ if (invalidProperties.length) {
95
+ const invalidText = invalidProperties
96
+ .map(prop => prop.key.name ? prop.key.name : prop.key)
97
+ .join(', ');
98
+ console.log(
99
+ file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
100
+ 'was skipped because of invalid field(s) `' + invalidText + '` on ' +
101
+ 'the React component. Remove any right-hand-side expressions that ' +
102
+ 'are not simple, like: `componentWillUpdate: createWillUpdate()` or ' +
103
+ '`render: foo ? renderA : renderB`.'
104
+ );
105
+ }
106
+ return !invalidProperties.length;
107
+ }
108
+
109
const hasMixins = classPath => {
110
if (ReactUtils.hasMixins(classPath)) {
111
console.log(
83
- file.path + ': "' + ReactUtils.getComponentName(classPath) + '" ' +
84
- ' skipped because of mixins.'
112
+ file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
113
+ 'was skipped because of mixins.'
114
);
115
return false;
116
}
@@ -499,6 +528,7 @@ module.exports = (file, api, options) => {
528
path
529
.filter(hasMixins)
530
.filter(callsDeprecatedAPIs)
531
+ .filter(canConvertToClass)
532
.forEach(classPath => updateToClass(
533
classPath,
534
!options['no-super-class'],