|
1 | 1 | import * as t from '@babel/types' |
2 | 2 |
|
3 | 3 | interface ToModifyVariableI { |
4 | | - raw: string |
5 | | - simplified: string |
| 4 | + raw: string |
| 5 | + simplified: string |
6 | 6 | } |
7 | 7 |
|
8 | 8 | export default function () { |
9 | | - const toMod: ToModifyVariableI[] = [] |
10 | | - return { |
11 | | - visitor: { |
12 | | - FunctionDeclaration(path: any) { |
13 | | - transformToStateByScope(path, toMod) |
14 | | - }, |
15 | | - ArrowFunctionExpression(path: any) { |
16 | | - transformToStateByScope(path, toMod) |
17 | | - }, |
18 | | - }, |
19 | | - } |
| 9 | + const toMod: ToModifyVariableI[] = [] |
| 10 | + return { |
| 11 | + visitor: { |
| 12 | + FunctionDeclaration(path: any) { |
| 13 | + transformToStateByScope(path, toMod) |
| 14 | + }, |
| 15 | + ArrowFunctionExpression(path: any) { |
| 16 | + transformToStateByScope(path, toMod) |
| 17 | + }, |
| 18 | + }, |
| 19 | + } |
20 | 20 | } |
21 | 21 |
|
22 | 22 | function transformToStateByScope(path: any, toMod: ToModifyVariableI[]) { |
23 | | - // TODO: check if the returned values are of the form `React.createElement` |
24 | | - // NOTE: can avoid the above one since custom hooks won't be able to use this |
25 | | - |
26 | | - Object.keys(path.scope.bindings).forEach((binding) => { |
27 | | - if (/^\$/.test(binding)) { |
28 | | - // add to list of identifiers to compare and replace |
29 | | - // (not using scope replace to avoid shadow variables being replaced) |
30 | | - const normName = normalizeName(binding) |
31 | | - toMod.push({ |
32 | | - raw: binding, |
33 | | - simplified: normName, |
34 | | - }) |
35 | | - } |
36 | | - }) |
37 | | - |
38 | | - // nested traverse to avoid replacing bindings of anything other than what's in this |
39 | | - // function. To prevent creating state hooks outside a function |
40 | | - path.traverse({ |
41 | | - Identifier({node}: {node: t.Identifier}) { |
42 | | - if (isReactiveIdentifier(node.name, toMod)) { |
43 | | - node.name = normalizeName(node.name) |
44 | | - } |
45 | | - }, |
46 | | - VariableDeclaration({node}: {node: t.VariableDeclaration}) { |
47 | | - transformReactiveDeclarations(node, toMod) |
48 | | - }, |
49 | | - ExpressionStatement({node}: {node: t.ExpressionStatement}) { |
50 | | - transformAssignmentExpression(node, toMod) |
51 | | - }, |
52 | | - }) |
| 23 | + // TODO: check if the returned values are of the form `React.createElement` |
| 24 | + // NOTE: can avoid the above one since custom hooks won't be able to use this |
| 25 | + |
| 26 | + Object.keys(path.scope.bindings).forEach((binding) => { |
| 27 | + if (/^\$/.test(binding)) { |
| 28 | + // add to list of identifiers to compare and replace |
| 29 | + // (not using scope replace to avoid shadow variables being replaced) |
| 30 | + const normName = normalizeName(binding) |
| 31 | + toMod.push({ |
| 32 | + raw: binding, |
| 33 | + simplified: normName, |
| 34 | + }) |
| 35 | + } |
| 36 | + }) |
| 37 | + |
| 38 | + // nested traverse to avoid replacing bindings of anything other than what's in this |
| 39 | + // function. To prevent creating state hooks outside a function |
| 40 | + path.traverse({ |
| 41 | + Identifier({node}: {node: t.Identifier}) { |
| 42 | + if (isReactiveIdentifier(node.name, toMod)) { |
| 43 | + node.name = normalizeName(node.name) |
| 44 | + } |
| 45 | + }, |
| 46 | + VariableDeclaration({node}: {node: t.VariableDeclaration}) { |
| 47 | + transformReactiveDeclarations(node, toMod) |
| 48 | + }, |
| 49 | + ExpressionStatement({node}: {node: t.ExpressionStatement}) { |
| 50 | + transformAssignmentExpression(node, toMod) |
| 51 | + }, |
| 52 | + }) |
53 | 53 | } |
54 | 54 |
|
55 | 55 | function transformReactiveDeclarations( |
56 | | - node: t.VariableDeclaration, |
57 | | - toMod: ToModifyVariableI[] |
| 56 | + node: t.VariableDeclaration, |
| 57 | + toMod: ToModifyVariableI[] |
58 | 58 | ) { |
59 | | - for (let i = 0; i < node.declarations.length; i += 1) { |
60 | | - const declaration = node.declarations[i] |
61 | | - |
62 | | - if ( |
63 | | - !( |
64 | | - t.isIdentifier(declaration.id) && |
65 | | - isReactiveIdentifier(declaration.id.name, toMod) |
66 | | - ) |
67 | | - ) { |
68 | | - continue |
69 | | - } |
70 | | - |
71 | | - // change to const if it's `let` by any chance |
72 | | - node.kind = 'const' |
73 | | - |
74 | | - const normName = normalizeName(declaration.id.name) |
75 | | - const setterName = getSetterName(normName) |
76 | | - |
77 | | - // convert to `const [x,setX] = React.useState()` |
78 | | - node.declarations[i] = t.variableDeclarator( |
79 | | - t.arrayPattern([t.identifier(normName), t.identifier(setterName)]), |
80 | | - t.callExpression( |
81 | | - t.memberExpression(t.identifier('React'), t.identifier('useState')), |
82 | | - declaration.init ? [declaration.init] : [] |
83 | | - ) |
84 | | - ) |
85 | | - } |
| 59 | + for (let i = 0; i < node.declarations.length; i += 1) { |
| 60 | + const declaration = node.declarations[i] |
| 61 | + |
| 62 | + if ( |
| 63 | + !( |
| 64 | + t.isIdentifier(declaration.id) && |
| 65 | + isReactiveIdentifier(declaration.id.name, toMod) |
| 66 | + ) |
| 67 | + ) { |
| 68 | + continue |
| 69 | + } |
| 70 | + |
| 71 | + // change to const if it's `let` by any chance |
| 72 | + node.kind = 'const' |
| 73 | + |
| 74 | + const normName = normalizeName(declaration.id.name) |
| 75 | + const setterName = getSetterName(normName) |
| 76 | + |
| 77 | + // convert to `const [x,setX] = React.useState()` |
| 78 | + node.declarations[i] = t.variableDeclarator( |
| 79 | + t.arrayPattern([t.identifier(normName), t.identifier(setterName)]), |
| 80 | + t.callExpression( |
| 81 | + t.memberExpression(t.identifier('React'), t.identifier('useState')), |
| 82 | + declaration.init ? [declaration.init] : [] |
| 83 | + ) |
| 84 | + ) |
| 85 | + } |
86 | 86 | } |
87 | 87 |
|
88 | 88 | function transformAssignmentExpression( |
89 | | - node: t.ExpressionStatement, |
90 | | - toMod: ToModifyVariableI[] |
| 89 | + node: t.ExpressionStatement, |
| 90 | + toMod: ToModifyVariableI[] |
91 | 91 | ) { |
92 | | - if (!t.isAssignmentExpression(node.expression)) { |
93 | | - return |
94 | | - } |
95 | | - //HACK: forced to assignment expression for now, will need to switch to a `switch` |
96 | | - // statement when working with both Assignment(=,+=,-=,etc) and Update expressions(++,--,**,etc) |
97 | | - const expression: t.AssignmentExpression = node.expression |
98 | | - |
99 | | - if (!t.isIdentifier(expression.left)) { |
100 | | - return |
101 | | - } |
102 | | - |
103 | | - if (!isReactiveIdentifier(expression.left.name, toMod)) { |
104 | | - return |
105 | | - } |
106 | | - |
107 | | - const normName = normalizeName(expression.left.name) |
108 | | - const setterName = getSetterName(normName) |
109 | | - |
110 | | - let callArgs: t.Expression[] |
111 | | - |
112 | | - switch (expression.operator) { |
113 | | - case '=': { |
114 | | - callArgs = [{...expression.right}] |
115 | | - break |
116 | | - } |
117 | | - |
118 | | - case '+=': { |
119 | | - callArgs = [ |
120 | | - t.binaryExpression('+', t.identifier(normName), expression.right), |
121 | | - ] |
122 | | - break |
123 | | - } |
124 | | - |
125 | | - case '-=': { |
126 | | - callArgs = [ |
127 | | - t.binaryExpression('-', t.identifier(normName), expression.right), |
128 | | - ] |
129 | | - break |
130 | | - } |
131 | | - |
132 | | - case '/=': { |
133 | | - callArgs = [ |
134 | | - t.binaryExpression('/', t.identifier(normName), expression.right), |
135 | | - ] |
136 | | - break |
137 | | - } |
138 | | - |
139 | | - case '*=': { |
140 | | - callArgs = [ |
141 | | - t.binaryExpression('*', t.identifier(normName), expression.right), |
142 | | - ] |
143 | | - break |
144 | | - } |
145 | | - default: { |
146 | | - callArgs = [] |
147 | | - } |
148 | | - } |
149 | | - |
150 | | - node.expression = t.callExpression(t.identifier(setterName), callArgs) |
| 92 | + if (!t.isAssignmentExpression(node.expression)) { |
| 93 | + return |
| 94 | + } |
| 95 | + //HACK: forced to assignment expression for now, will need to switch to a `switch` |
| 96 | + // statement when working with both Assignment(=,+=,-=,etc) and Update expressions(++,--,**,etc) |
| 97 | + const expression: t.AssignmentExpression = node.expression |
| 98 | + |
| 99 | + if (!t.isIdentifier(expression.left)) { |
| 100 | + return |
| 101 | + } |
| 102 | + |
| 103 | + if (!isReactiveIdentifier(expression.left.name, toMod)) { |
| 104 | + return |
| 105 | + } |
| 106 | + |
| 107 | + const normName = normalizeName(expression.left.name) |
| 108 | + const setterName = getSetterName(normName) |
| 109 | + |
| 110 | + let callArgs: t.Expression[] |
| 111 | + |
| 112 | + switch (expression.operator) { |
| 113 | + case '=': { |
| 114 | + callArgs = [{...expression.right}] |
| 115 | + break |
| 116 | + } |
| 117 | + |
| 118 | + case '+=': { |
| 119 | + callArgs = [ |
| 120 | + t.binaryExpression('+', t.identifier(normName), expression.right), |
| 121 | + ] |
| 122 | + break |
| 123 | + } |
| 124 | + |
| 125 | + case '-=': { |
| 126 | + callArgs = [ |
| 127 | + t.binaryExpression('-', t.identifier(normName), expression.right), |
| 128 | + ] |
| 129 | + break |
| 130 | + } |
| 131 | + |
| 132 | + case '/=': { |
| 133 | + callArgs = [ |
| 134 | + t.binaryExpression('/', t.identifier(normName), expression.right), |
| 135 | + ] |
| 136 | + break |
| 137 | + } |
| 138 | + |
| 139 | + case '*=': { |
| 140 | + callArgs = [ |
| 141 | + t.binaryExpression('*', t.identifier(normName), expression.right), |
| 142 | + ] |
| 143 | + break |
| 144 | + } |
| 145 | + default: { |
| 146 | + callArgs = [] |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + node.expression = t.callExpression(t.identifier(setterName), callArgs) |
151 | 151 | } |
152 | 152 |
|
153 | 153 | function isReactiveIdentifier(idName: string, modMap: ToModifyVariableI[]) { |
154 | | - return ( |
155 | | - modMap.findIndex((x) => x.raw === idName || x.simplified === idName) > -1 |
156 | | - ) |
| 154 | + return ( |
| 155 | + modMap.findIndex((x) => x.raw === idName || x.simplified === idName) > -1 |
| 156 | + ) |
157 | 157 | } |
158 | 158 |
|
159 | 159 | function getSetterName(normalizedName: string) { |
160 | | - return ( |
161 | | - 'set' + normalizedName.charAt(0).toUpperCase() + normalizedName.slice(1) |
162 | | - ) |
| 160 | + return ( |
| 161 | + 'set' + normalizedName.charAt(0).toUpperCase() + normalizedName.slice(1) |
| 162 | + ) |
163 | 163 | } |
164 | 164 |
|
165 | 165 | function normalizeName(n: string) { |
166 | | - return n.replace(/\$/, '') |
| 166 | + return n.replace(/\$/, '') |
167 | 167 | } |
0 commit comments