Add findDOMNode transform
cpojer committed
Mar 25, 2015 at 01:29 UTC
20004e94d399541da70d08fd12b64dafb498ebce
5 files changed
+217
npm-react-codemod/README.md
+11
@@ -11,6 +11,17 @@ APIs.
11
* Use the `-d` option for a dry-run and use `-p` to print the output
12
for comparison
13
14
+### Included Scripts
15
+
16
+`findDOMNode.js` updates `this.getDOMNode()` or `this.refs.foo.getDOMNode()`
17
+calls inside of `React.createClass` components to `React.findDOMNode(foo)`. Note
18
+that it will only look at code inside of `React.createClass` calls and only
19
+update calls on the component instance or its refs. You can use this script to
20
+update most calls to `getDOMNode` and then manually go through the remaining
21
+calls.
22
+
23
+ * `react-codemod findDOMNode <file>`
24
+
25
### Recast Options
26
27
Options to [recast](https://github.com/benjamn/recast)'s printer can be provided
npm-react-codemod/test/__tests__/transform-tests.js
+3
@@ -34,6 +34,9 @@ function test(transformName, testFileName, options) {
34
35
describe('Transform Tests', () => {
36
37
+ it('transforms the "findDOMNode" tests correctly', () => {
38
+ test('findDOMNode', 'findDOMNode-test');
39
+ });
40
41
42
});
npm-react-codemod/test/findDOMNode-test.js
new
+34
@@ -0,0 +1,34 @@
1
+'use strict';
2
+
3
+var React = require('React');
4
+
5
+var Composer = React.createClass({
6
+ componentWillReceiveProps: function(nextProps) {
7
+ this.getDOMNode();
8
+ return foo(this.refs.input.getDOMNode());
9
+ },
10
+
11
+ foo: function() {
12
+ var ref = 'foo';
13
+ var element = this.refs[ref];
14
+ var domNode = element.getDOMNode();
15
+ },
16
+
17
+ bar: function() {
18
+ var thing = this.refs.foo;
19
+ thing.getDOMNode();
20
+ },
21
+
22
+ foobar: function() {
23
+ passThisOn(this.refs.main.refs.list.getDOMNode());
24
+ }
25
+});
26
+
27
+var SomeDialog = React.createClass({
28
+ render: function() {
29
+ call(this.refs.SomeThing);
30
+ return (
31
+ <div />
32
+ );
33
+ }
34
+});
npm-react-codemod/test/findDOMNode-test.output.js
new
+34
@@ -0,0 +1,34 @@
1
+'use strict';
2
+
3
+var React = require('React');
4
+
5
+var Composer = React.createClass({
6
+ componentWillReceiveProps: function(nextProps) {
7
+ React.findDOMNode(this);
8
+ return foo(React.findDOMNode(this.refs.input));
9
+ },
10
+
11
+ foo: function() {
12
+ var ref = 'foo';
13
+ var element = this.refs[ref];
14
+ var domNode = React.findDOMNode(element);
15
+ },
16
+
17
+ bar: function() {
18
+ var thing = this.refs.foo;
19
+ React.findDOMNode(thing);
20
+ },
21
+
22
+ foobar: function() {
23
+ passThisOn(React.findDOMNode(this.refs.main.refs.list));
24
+ }
25
+});
26
+
27
+var SomeDialog = React.createClass({
28
+ render: function() {
29
+ call(this.refs.SomeThing);
30
+ return (
31
+ <div />
32
+ );
33
+ }
34
+});
npm-react-codemod/transforms/findDOMNode.js
new
+135
@@ -0,0 +1,135 @@
1
+/*eslint-disable no-comma-dangle*/
2
+
3
+'use strict';
4
+
5
+function getDOMNodeToFindDOMNode(file, api, options) {
6
+ const j = api.jscodeshift;
7
+
8
+ require('./utils/array-polyfills');
9
+ const ReactUtils = require('./utils/ReactUtils')(j);
10
+
11
+ const printOptions = options.printOptions || {quote: 'single'};
12
+ const root = j(file.source);
13
+
14
+ const createReactFindDOMNodeCall = arg => j.callExpression(
15
+ j.memberExpression(
16
+ j.identifier('React'),
17
+ j.identifier('findDOMNode'),
18
+ false
19
+ ),
20
+ [arg]
21
+ );
22
+
23
+ const updateRefCall = (path, refName) => {
24
+ j(path)
25
+ .find(j.CallExpression, {
26
+ callee: {
27
+ object: {
28
+ type: 'Identifier',
29
+ name: refName,
30
+ },
31
+ property: {
32
+ type: 'Identifier',
33
+ name: 'getDOMNode',
34
+ },
35
+ },
36
+ })
37
+ .forEach(callPath => j(callPath).replaceWith(
38
+ createReactFindDOMNodeCall(j.identifier(refName))
39
+ ));
40
+ };
41
+
42
+ const updateToFindDOMNode = classPath => {
43
+ var sum = 0;
44
+
45
+ // this.getDOMNode()
46
+ sum += j(classPath)
47
+ .find(j.CallExpression, {
48
+ callee: {
49
+ object: {
50
+ type: 'ThisExpression',
51
+ },
52
+ property: {
53
+ type: 'Identifier',
54
+ name: 'getDOMNode',
55
+ },
56
+ },
57
+ })
58
+ .forEach(path => j(path).replaceWith(
59
+ createReactFindDOMNodeCall(j.thisExpression())
60
+ ))
61
+ .size();
62
+
63
+ // this.refs.xxx.getDOMNode() or this.refs.xxx.refs.yyy.getDOMNode()
64
+ sum += j(classPath)
65
+ .find(j.MemberExpression, {
66
+ object: {
67
+ type: 'MemberExpression',
68
+ object: {
69
+ type: 'MemberExpression',
70
+ object: {
71
+ type: 'ThisExpression',
72
+ },
73
+ property: {
74
+ type: 'Identifier',
75
+ name: 'refs',
76
+ },
77
+ },
78
+ },
79
+ })
80
+ .closest(j.CallExpression)
81
+ .filter(path => (
82
+ path.value.callee.property &&
83
+ path.value.callee.property.type === 'Identifier' &&
84
+ path.value.callee.property.name === 'getDOMNode'
85
+ ))
86
+ .forEach(path => j(path).replaceWith(
87
+ createReactFindDOMNodeCall(path.value.callee.object)
88
+ ))
89
+ .size();
90
+
91
+ // someVariable.getDOMNode() wherre `someVariable = this.refs.xxx`
92
+ sum += j(classPath)
93
+ .findVariableDeclarators()
94
+ .filter(path => {
95
+ const init = path.value.init;
96
+ const value = init && init.object;
97
+ return (
98
+ value &&
99
+ value.type === 'MemberExpression' &&
100
+ value.object &&
101
+ value.object.type === 'ThisExpression' &&
102
+ value.property &&
103
+ value.property.type === 'Identifier' &&
104
+ value.property.name === 'refs' &&
105
+ init.property &&
106
+ init.property.type === 'Identifier'
107
+ );
108
+ })
109
+ .forEach(path => j(path)
110
+ .closest(j.FunctionExpression)
111
+ .forEach(fnPath => updateRefCall(fnPath, path.value.id.name))
112
+ )
113
+ .size();
114
+
115
+ return sum > 0;
116
+ };
117
+
118
+ if (
119
+ options['no-explicit-require'] ||
120
+ ReactUtils.hasReact(root)
121
+ ) {
122
+ const didTransform = ReactUtils
123
+ .findReactCreateClass(root)
124
+ .filter(updateToFindDOMNode)
125
+ .size() > 0;
126
+
127
+ if (didTransform) {
128
+ return root.toSource(printOptions) + '\n';
129
+ }
130
+ }
131
+
132
+ return null;
133
+}
134
+
135
+module.exports = getDOMNodeToFindDOMNode;