-
Notifications
You must be signed in to change notification settings - Fork 0
/
static_property_placement.js
100 lines (86 loc) · 3.01 KB
/
static_property_placement.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// jscodeshift frontend/javascripts/**/components/**/*.jsx -t script/jscodeshift/static_property_placement.js
const staticPropertyNames = [
'childContextTypes',
'contextTypes',
'contextType',
'defaultProps',
'displayName',
'propTypes',
];
const isReactStaticProperty = node =>
node.type === 'ClassProperty' && node.static && staticPropertyNames.includes(node.key.name);
const getClassIdentifier = classPath => {
if (classPath.parent.value.type === 'AssignmentExpression') {
return classPath.parent.value.left;
}
return classPath.value.id;
};
const assignmentExpressionFromProperty = (j, classPath, classPropertyNode) =>
j.expressionStatement(
j.assignmentExpression(
'=',
j.memberExpression(
getClassIdentifier(classPath),
j.identifier(classPropertyNode.key.name),
false,
),
classPropertyNode.value,
),
);
const findScopeBodyArray = path => {
let body = path.scope.node.body;
if (body.type === 'BlockStatement') body = body.body;
return body;
};
const findBodyIndexOfDeclaration = (j, classPath, bodyArray) =>
bodyArray.findIndex(bodyChild => {
const pathFind = j(bodyChild)
.find(j.ClassBody)
.map(p => p.parent)
.filter(bodyPath => {
return bodyPath.node === classPath.node;
});
return pathFind.size() !== 0;
});
const appendNodesAfterClassDeclaration = (j, path, nodesToAdd) => {
let body = findScopeBodyArray(path);
const insertIndex = findBodyIndexOfDeclaration(j, path, body);
// console.log('scopeIndex', insertIndex);
body.splice(insertIndex + 1, 0, ...nodesToAdd);
};
const copyReactStaticsOutsideOfClass = (j, path) => {
// Take the react static properties and place them as assignments after the class declaration
const staticProperties = path.value.body.body.filter(isReactStaticProperty);
if (staticProperties.length === 0) return;
// console.log(
// reactClassName,
// 'has react static properties ',
// staticProperties.map(p => p.key.name),
// );
// create assignment expressions from the properties
const expressions = staticProperties.map(n => assignmentExpressionFromProperty(j, path, n));
// append the expressions after the class
appendNodesAfterClassDeclaration(j, path, expressions);
};
export default function transformer(file, api) {
const j = api.jscodeshift;
let root = j(file.source);
root
.find(j.ClassBody)
// filter for classes that are React Components
.filter(path => {
const { superClass } = path.parent.value;
return (
superClass &&
(superClass.name === 'Component' ||
(superClass.object && superClass.object.name === 'React'))
);
})
// copy the react static properties to assignments outside the class
.forEach(path => copyReactStaticsOutsideOfClass(j, path.parent))
// now use collection functions to remove those properties inside the class
.find(j.ClassProperty)
.filter(p => isReactStaticProperty(p.node))
.remove();
return root.toSource({ quote: 'single' });
}