-
Notifications
You must be signed in to change notification settings - Fork 321
/
this-dispatch-to-return.js
101 lines (85 loc) · 3.23 KB
/
this-dispatch-to-return.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
101
const isDispatch = path => (
path.value.type === 'CallExpression' &&
path.value.callee.type === 'MemberExpression' &&
// path.value.callee.object.type === 'ThisExpression' && // commented out so we support var self = this; self.dispatch();
path.value.callee.property.type === 'Identifier' &&
path.value.callee.property.name === 'dispatch'
)
const isThisActions = path => (
path.value.type === 'MemberExpression' &&
path.value.object.type === 'MemberExpression' &&
path.value.object.property.type === 'Identifier' &&
path.value.object.property.name === 'actions'
)
const updateDispatchToReturn = j => (p) => {
j(p).replaceWith(j.returnStatement(p.value.arguments[0] || null))
}
const updateDispatchToCall = j => (p) => {
j(p).replaceWith(j.callExpression(j.identifier('dispatch'), p.value.arguments))
}
const updateToJustThis = j => (p) => {
j(p).replaceWith(j.memberExpression(p.value.object.object, p.value.property))
}
const findDispatches = (j, p) => {
return j(p).find(j.CallExpression).filter(isDispatch)
}
const findThisActionReferences = (j, p) => {
return j(p).find(j.MemberExpression).filter(isThisActions)
}
const replaceFunction = (j, p) => {
j(p).replaceWith(j.functionExpression(
null,
p.value.params,
j.blockStatement([
j.returnStatement(
j.functionExpression(
null,
[j.identifier('dispatch')],
j.blockStatement(p.value.body.body)
)
)
])
))
}
module.exports = (file, api) => {
const j = api.jscodeshift
const root = j(file.source)
root.find(j.FunctionExpression).forEach((p) => {
// ignore constructors
if (p.parent.value.type === 'MethodDefinition' && p.parent.value.kind === 'constructor') {
return
}
// find all dispatches that are inside the function
const dispatches = findDispatches(j, p).size()
const withinParent = findDispatches(j, p).filter(x => x.parent.parent.parent.value === p.value).size()
if (withinParent === 0 && dispatches > 0) {
replaceFunction(j, p)
findDispatches(j, p).forEach(updateDispatchToCall(j))
} else if (dispatches === 0) {
const hasReturn = j(p).find(j.ReturnStatement).size() > 0
if (hasReturn) {
console.warn('Could not transform function because it returned', 'at line', p.parent.value.loc.start.line)
} else {
console.warn('This function does not dispatch?', 'at line', p.parent.value.loc.start.line)
}
// if there are multiple dispatches happening then we'll need to return a
// dispatch function and update this.dispatch to a dispatch call
} else if (dispatches > 1) {
replaceFunction(j, p)
findDispatches(j, p).forEach(updateDispatchToCall(j))
// if there's a single dispatch then it's ok to return to dispatch
} else {
// if its the only statement within the function
if (p.value.body.body.length === 1) {
findDispatches(j, p).forEach(updateDispatchToReturn(j))
// otherwise lets run the function
} else {
replaceFunction(j, p)
findDispatches(j, p).forEach(updateDispatchToCall(j))
}
}
// Also find any mentions to `this.actions`
findThisActionReferences(j, p).forEach(updateToJustThis(j))
})
return root.toSource({ quote: 'single' })
}