Skip to content

Commit c136e9c

Browse files
author
Keyan Zhang
committed
added react-addons-pure-render-mixin support
1 parent f70dad2 commit c136e9c

12 files changed

+157
-83
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var React = require('React');
2+
var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
3+
4+
var ComponentWithOnlyPureRenderMixin = React.createClass({
5+
mixins: [ReactComponentWithPureRenderMixin],
6+
7+
getInitialState: function() {
8+
return {
9+
counter: this.props.initialNumber + 1,
10+
};
11+
},
12+
13+
render: function() {
14+
return (
15+
<div>{this.state.counter}</div>
16+
);
17+
},
18+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var React = require('React');
2+
3+
class ComponentWithOnlyPureRenderMixin extends React.PureComponent {
4+
constructor(props, context) {
5+
super(props, context);
6+
7+
this.state = {
8+
counter: props.initialNumber + 1,
9+
};
10+
}
11+
12+
render() {
13+
return (
14+
<div>{this.state.counter}</div>
15+
);
16+
}
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'React';
2+
import WhateverYouCallIt from 'react-addons-pure-render-mixin';
3+
4+
var ComponentWithOnlyPureRenderMixin = React.createClass({
5+
mixins: [WhateverYouCallIt],
6+
7+
getInitialState: function() {
8+
return {
9+
counter: this.props.initialNumber + 1,
10+
};
11+
},
12+
13+
render: function() {
14+
return (
15+
<div>{this.state.counter}</div>
16+
);
17+
},
18+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'React';
2+
3+
class ComponentWithOnlyPureRenderMixin extends React.PureComponent {
4+
constructor(props, context) {
5+
super(props, context);
6+
7+
this.state = {
8+
counter: props.initialNumber + 1,
9+
};
10+
}
11+
12+
render() {
13+
return (
14+
<div>{this.state.counter}</div>
15+
);
16+
}
17+
}

transforms/__testfixtures__/class-pure-mixin3.input.js

Whitespace-only changes.

transforms/__testfixtures__/class-pure-mixin3.output.js

Whitespace-only changes.

transforms/__testfixtures__/class-test2.input.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,6 @@ module.exports = React.createClass({
4747
},
4848
});
4949

50-
var ComponentWithOnlyPureRenderMixin = React.createClass({
51-
mixins: [ReactComponentWithPureRenderMixin],
52-
53-
getInitialState: function() {
54-
return {
55-
counter: this.props.initialNumber + 1,
56-
};
57-
},
58-
59-
render: function() {
60-
return (
61-
<div>{this.state.counter}</div>
62-
);
63-
},
64-
});
65-
6650
var ComponentWithInconvertibleMixins = React.createClass({
6751
mixins: [ReactComponentWithPureRenderMixin, FooBarMixin],
6852

transforms/__testfixtures__/class-test2.output.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,6 @@ module.exports = class extends React.Component {
4848
}
4949
};
5050

51-
class ComponentWithOnlyPureRenderMixin extends React.PureComponent {
52-
constructor(props, context) {
53-
super(props, context);
54-
55-
this.state = {
56-
counter: props.initialNumber + 1,
57-
};
58-
}
59-
60-
render() {
61-
return (
62-
<div>{this.state.counter}</div>
63-
);
64-
}
65-
}
66-
6751
var ComponentWithInconvertibleMixins = React.createClass({
6852
mixins: [ReactComponentWithPureRenderMixin, FooBarMixin],
6953

transforms/__testfixtures__/export-default-property-initializer.input.js

Lines changed: 0 additions & 21 deletions
This file was deleted.

transforms/__testfixtures__/export-default-property-initializer.output.js

Lines changed: 0 additions & 19 deletions
This file was deleted.

transforms/__tests__/class-test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
'use strict';
1212

1313
const defineTest = require('jscodeshift/dist/testUtils').defineTest;
14+
15+
const pureMixinAlternativeOption = {
16+
'mixin-module-name': 'ReactComponentWithPureRenderMixin',
17+
};
18+
1419
defineTest(__dirname, 'class');
15-
defineTest(__dirname, 'class', null, 'class-test2');
20+
defineTest(__dirname, 'class', pureMixinAlternativeOption, 'class-test2');
1621
defineTest(__dirname, 'class', null, 'export-default-class');
22+
defineTest(__dirname, 'class', pureMixinAlternativeOption, 'class-pure-mixin1');
23+
defineTest(__dirname, 'class', null, 'class-pure-mixin2');

transforms/class.js

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ module.exports = (file, api, options) => {
4545
'setProps',
4646
];
4747

48+
const PURE_MIXIN_MODULE_NAME = options['mixin-module-name'] ||
49+
'react-addons-pure-render-mixin';
50+
4851
const STATIC_KEY = 'statics';
4952

5053
const STATIC_KEYS = {
@@ -108,15 +111,11 @@ module.exports = (file, api, options) => {
108111
return !invalidProperties.length;
109112
};
110113

111-
const hasInconvertibleMixins = classPath => {
114+
const areMixinsConvertible = (mixinIdentifierNames, classPath) => {
112115
if (
113116
ReactUtils.hasMixins(classPath) &&
114-
!ReactUtils.hasSpecificMixins(classPath, ['ReactComponentWithPureRenderMixin'])
117+
!ReactUtils.hasSpecificMixins(classPath, mixinIdentifierNames)
115118
) {
116-
console.log(
117-
file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
118-
'was skipped because of inconvertible mixins.'
119-
);
120119
return false;
121120
}
122121
return true;
@@ -246,6 +245,49 @@ module.exports = (file, api, options) => {
246245
return Object.keys(autobindNames);
247246
};
248247

248+
const findRequirePathAndBinding = (moduleName) => {
249+
let result = null;
250+
251+
const requireStatement = root.find(j.VariableDeclarator, {
252+
init: {
253+
type: 'CallExpression',
254+
callee: {
255+
type: 'Identifier',
256+
name: 'require',
257+
},
258+
arguments: [{
259+
value: moduleName,
260+
}],
261+
},
262+
});
263+
264+
const importStatement = root.find(j.ImportDeclaration, {
265+
source: {
266+
value: moduleName,
267+
},
268+
});
269+
270+
if (importStatement.size() !== 0) {
271+
importStatement.forEach(path => {
272+
result = {
273+
path,
274+
binding: path.value.specifiers[0].local.name,
275+
};
276+
});
277+
} else if (requireStatement.size() !== 0) {
278+
requireStatement.forEach(path => {
279+
result = {
280+
path,
281+
binding: path.value.id.name,
282+
};
283+
});
284+
}
285+
286+
return result;
287+
};
288+
289+
const pureRenderMixinPathAndBinding = findRequirePathAndBinding(PURE_MIXIN_MODULE_NAME);
290+
249291
// ---------------------------------------------------------------------------
250292
// Boom!
251293
const createMethodDefinition = fn =>
@@ -501,9 +543,10 @@ module.exports = (file, api, options) => {
501543

502544
const staticProperties = createStaticClassProperties(statics);
503545
const baseClassName =
504-
ReactUtils.hasSpecificMixins(classPath, ['ReactComponentWithPureRenderMixin']) ?
505-
'PureComponent' :
506-
'Component';
546+
pureRenderMixinPathAndBinding &&
547+
ReactUtils.hasSpecificMixins(classPath, [pureRenderMixinPathAndBinding.binding]) ?
548+
'PureComponent' :
549+
'Component';
507550

508551
path.replaceWith(
509552
createESClass(
@@ -521,9 +564,29 @@ module.exports = (file, api, options) => {
521564
if (
522565
options['explicit-require'] === false || ReactUtils.hasReact(root)
523566
) {
567+
// no mixins found on the classPath -> true
568+
// pure mixin identifier not found -> (has mixins) -> false
569+
// found pure mixin identifier ->
570+
// class mixins only contain the identifier -> true
571+
// otherwise -> false
572+
const mixinsFilter = (classPath) => {
573+
if (!ReactUtils.hasMixins(classPath)) {
574+
return true;
575+
} else if (pureRenderMixinPathAndBinding) {
576+
if (areMixinsConvertible([pureRenderMixinPathAndBinding.binding], classPath)) {
577+
return true;
578+
}
579+
}
580+
console.log(
581+
file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
582+
'was skipped because of inconvertible mixins.'
583+
);
584+
return false;
585+
};
586+
524587
const apply = (path, type) =>
525588
path
526-
.filter(hasInconvertibleMixins)
589+
.filter(mixinsFilter)
527590
.filter(callsDeprecatedAPIs)
528591
.filter(canConvertToClass)
529592
.forEach(classPath => updateToClass(classPath, type));
@@ -538,6 +601,12 @@ module.exports = (file, api, options) => {
538601
) > 0;
539602

540603
if (didTransform) {
604+
// prune removed requires
605+
if (pureRenderMixinPathAndBinding) {
606+
// FIXME check the scope before removing stuff
607+
j(pureRenderMixinPathAndBinding.path).remove();
608+
}
609+
541610
return root.toSource(printOptions);
542611
}
543612

0 commit comments

Comments
 (0)