@@ -86,154 +86,6 @@ export default {
86
86
return result ;
87
87
} ;
88
88
}
89
-
90
- return {
91
- CallExpression : visitCallExpression ,
92
- } ;
93
-
94
- function visitCallExpression ( node ) {
95
- const callbackIndex = getReactiveHookCallbackIndex ( node . callee , options ) ;
96
- if ( callbackIndex === - 1 ) {
97
- // Not a React Hook call that needs deps.
98
- return ;
99
- }
100
- const callback = node . arguments [ callbackIndex ] ;
101
- const reactiveHook = node . callee ;
102
- const reactiveHookName = getNodeWithoutReactNamespace ( reactiveHook ) . name ;
103
- const declaredDependenciesNode = node . arguments [ callbackIndex + 1 ] ;
104
- const isEffect = / E f f e c t ( $ | [ ^ a - z ] ) / g. test ( reactiveHookName ) ;
105
-
106
- // Check the declared dependencies for this reactive hook. If there is no
107
- // second argument then the reactive callback will re-run on every render.
108
- // So no need to check for dependency inclusion.
109
- if ( ! declaredDependenciesNode && ! isEffect ) {
110
- // These are only used for optimization.
111
- if (
112
- reactiveHookName === 'useMemo' ||
113
- reactiveHookName === 'useCallback'
114
- ) {
115
- // TODO: Can this have a suggestion?
116
- reportProblem ( {
117
- node : reactiveHook ,
118
- message :
119
- `React Hook ${ reactiveHookName } does nothing when called with ` +
120
- `only one argument. Did you forget to pass an array of ` +
121
- `dependencies?` ,
122
- } ) ;
123
- }
124
- return ;
125
- }
126
-
127
- switch ( callback . type ) {
128
- case 'FunctionExpression' :
129
- case 'ArrowFunctionExpression' :
130
- visitFunctionWithDependencies (
131
- callback ,
132
- declaredDependenciesNode ,
133
- reactiveHook ,
134
- reactiveHookName ,
135
- isEffect ,
136
- ) ;
137
- return ; // Handled
138
- case 'Identifier' :
139
- if ( ! declaredDependenciesNode ) {
140
- // No deps, no problems.
141
- return ; // Handled
142
- }
143
- // The function passed as a callback is not written inline.
144
- // But perhaps it's in the dependencies array?
145
- if (
146
- declaredDependenciesNode . elements &&
147
- declaredDependenciesNode . elements . some (
148
- el => el && el . type === 'Identifier' && el . name === callback . name ,
149
- )
150
- ) {
151
- // If it's already in the list of deps, we don't care because
152
- // this is valid regardless.
153
- return ; // Handled
154
- }
155
- // We'll do our best effort to find it, complain otherwise.
156
- const variable = context . getScope ( ) . set . get ( callback . name ) ;
157
- if ( variable == null || variable . defs == null ) {
158
- // If it's not in scope, we don't care.
159
- return ; // Handled
160
- }
161
- // The function passed as a callback is not written inline.
162
- // But it's defined somewhere in the render scope.
163
- // We'll do our best effort to find and check it, complain otherwise.
164
- const def = variable . defs [ 0 ] ;
165
- if ( ! def || ! def . node ) {
166
- break ; // Unhandled
167
- }
168
- if ( def . type !== 'Variable' && def . type !== 'FunctionName' ) {
169
- // Parameter or an unusual pattern. Bail out.
170
- break ; // Unhandled
171
- }
172
- switch ( def . node . type ) {
173
- case 'FunctionDeclaration' :
174
- // useEffect(() => { ... }, []);
175
- visitFunctionWithDependencies (
176
- def . node ,
177
- declaredDependenciesNode ,
178
- reactiveHook ,
179
- reactiveHookName ,
180
- isEffect ,
181
- ) ;
182
- return ; // Handled
183
- case 'VariableDeclarator' :
184
- const init = def . node . init ;
185
- if ( ! init ) {
186
- break ; // Unhandled
187
- }
188
- switch ( init . type ) {
189
- // const effectBody = () => {...};
190
- // useEffect(effectBody, []);
191
- case 'ArrowFunctionExpression' :
192
- case 'FunctionExpression' :
193
- // We can inspect this function as if it were inline.
194
- visitFunctionWithDependencies (
195
- init ,
196
- declaredDependenciesNode ,
197
- reactiveHook ,
198
- reactiveHookName ,
199
- isEffect ,
200
- ) ;
201
- return ; // Handled
202
- }
203
- break ; // Unhandled
204
- }
205
- break ; // Unhandled
206
- default :
207
- // useEffect(generateEffectBody(), []);
208
- reportProblem ( {
209
- node : reactiveHook ,
210
- message :
211
- `React Hook ${ reactiveHookName } received a function whose dependencies ` +
212
- `are unknown. Pass an inline function instead.` ,
213
- } ) ;
214
- return ; // Handled
215
- }
216
-
217
- // Something unusual. Fall back to suggesting to add the body itself as a dep.
218
- reportProblem ( {
219
- node : reactiveHook ,
220
- message :
221
- `React Hook ${ reactiveHookName } has a missing dependency: '${ callback . name } '. ` +
222
- `Either include it or remove the dependency array.` ,
223
- suggest : [
224
- {
225
- desc : `Update the dependencies array to be: [${ callback . name } ]` ,
226
- fix ( fixer ) {
227
- return fixer . replaceText (
228
- declaredDependenciesNode ,
229
- `[${ callback . name } ]` ,
230
- ) ;
231
- } ,
232
- } ,
233
- ] ,
234
- } ) ;
235
- }
236
-
237
89
/**
238
90
* Visitor for both function expressions and arrow function expressions.
239
91
*/
@@ -1251,6 +1103,153 @@ export default {
1251
1103
] ,
1252
1104
} ) ;
1253
1105
}
1106
+
1107
+ function visitCallExpression ( node ) {
1108
+ const callbackIndex = getReactiveHookCallbackIndex ( node . callee , options ) ;
1109
+ if ( callbackIndex === - 1 ) {
1110
+ // Not a React Hook call that needs deps.
1111
+ return ;
1112
+ }
1113
+ const callback = node . arguments [ callbackIndex ] ;
1114
+ const reactiveHook = node . callee ;
1115
+ const reactiveHookName = getNodeWithoutReactNamespace ( reactiveHook ) . name ;
1116
+ const declaredDependenciesNode = node . arguments [ callbackIndex + 1 ] ;
1117
+ const isEffect = / E f f e c t ( $ | [ ^ a - z ] ) / g. test ( reactiveHookName ) ;
1118
+
1119
+ // Check the declared dependencies for this reactive hook. If there is no
1120
+ // second argument then the reactive callback will re-run on every render.
1121
+ // So no need to check for dependency inclusion.
1122
+ if ( ! declaredDependenciesNode && ! isEffect ) {
1123
+ // These are only used for optimization.
1124
+ if (
1125
+ reactiveHookName === 'useMemo' ||
1126
+ reactiveHookName === 'useCallback'
1127
+ ) {
1128
+ // TODO: Can this have a suggestion?
1129
+ reportProblem ( {
1130
+ node : reactiveHook ,
1131
+ message :
1132
+ `React Hook ${ reactiveHookName } does nothing when called with ` +
1133
+ `only one argument. Did you forget to pass an array of ` +
1134
+ `dependencies?` ,
1135
+ } ) ;
1136
+ }
1137
+ return ;
1138
+ }
1139
+
1140
+ switch ( callback . type ) {
1141
+ case 'FunctionExpression' :
1142
+ case 'ArrowFunctionExpression' :
1143
+ visitFunctionWithDependencies (
1144
+ callback ,
1145
+ declaredDependenciesNode ,
1146
+ reactiveHook ,
1147
+ reactiveHookName ,
1148
+ isEffect ,
1149
+ ) ;
1150
+ return ; // Handled
1151
+ case 'Identifier' :
1152
+ if ( ! declaredDependenciesNode ) {
1153
+ // No deps, no problems.
1154
+ return ; // Handled
1155
+ }
1156
+ // The function passed as a callback is not written inline.
1157
+ // But perhaps it's in the dependencies array?
1158
+ if (
1159
+ declaredDependenciesNode . elements &&
1160
+ declaredDependenciesNode . elements . some (
1161
+ el => el && el . type === 'Identifier' && el . name === callback . name ,
1162
+ )
1163
+ ) {
1164
+ // If it's already in the list of deps, we don't care because
1165
+ // this is valid regardless.
1166
+ return ; // Handled
1167
+ }
1168
+ // We'll do our best effort to find it, complain otherwise.
1169
+ const variable = context . getScope ( ) . set . get ( callback . name ) ;
1170
+ if ( variable == null || variable . defs == null ) {
1171
+ // If it's not in scope, we don't care.
1172
+ return ; // Handled
1173
+ }
1174
+ // The function passed as a callback is not written inline.
1175
+ // But it's defined somewhere in the render scope.
1176
+ // We'll do our best effort to find and check it, complain otherwise.
1177
+ const def = variable . defs [ 0 ] ;
1178
+ if ( ! def || ! def . node ) {
1179
+ break ; // Unhandled
1180
+ }
1181
+ if ( def . type !== 'Variable' && def . type !== 'FunctionName' ) {
1182
+ // Parameter or an unusual pattern. Bail out.
1183
+ break ; // Unhandled
1184
+ }
1185
+ switch ( def . node . type ) {
1186
+ case 'FunctionDeclaration' :
1187
+ // useEffect(() => { ... }, []);
1188
+ visitFunctionWithDependencies (
1189
+ def . node ,
1190
+ declaredDependenciesNode ,
1191
+ reactiveHook ,
1192
+ reactiveHookName ,
1193
+ isEffect ,
1194
+ ) ;
1195
+ return ; // Handled
1196
+ case 'VariableDeclarator' :
1197
+ const init = def . node . init ;
1198
+ if ( ! init ) {
1199
+ break ; // Unhandled
1200
+ }
1201
+ switch ( init . type ) {
1202
+ // const effectBody = () => {...};
1203
+ // useEffect(effectBody, []);
1204
+ case 'ArrowFunctionExpression' :
1205
+ case 'FunctionExpression' :
1206
+ // We can inspect this function as if it were inline.
1207
+ visitFunctionWithDependencies (
1208
+ init ,
1209
+ declaredDependenciesNode ,
1210
+ reactiveHook ,
1211
+ reactiveHookName ,
1212
+ isEffect ,
1213
+ ) ;
1214
+ return ; // Handled
1215
+ }
1216
+ break ; // Unhandled
1217
+ }
1218
+ break ; // Unhandled
1219
+ default :
1220
+ // useEffect(generateEffectBody(), []);
1221
+ reportProblem ( {
1222
+ node : reactiveHook ,
1223
+ message :
1224
+ `React Hook ${ reactiveHookName } received a function whose dependencies ` +
1225
+ `are unknown. Pass an inline function instead.` ,
1226
+ } ) ;
1227
+ return ; // Handled
1228
+ }
1229
+
1230
+ // Something unusual. Fall back to suggesting to add the body itself as a dep.
1231
+ reportProblem ( {
1232
+ node : reactiveHook ,
1233
+ message :
1234
+ `React Hook ${ reactiveHookName } has a missing dependency: '${ callback . name } '. ` +
1235
+ `Either include it or remove the dependency array.` ,
1236
+ suggest : [
1237
+ {
1238
+ desc : `Update the dependencies array to be: [${ callback . name } ]` ,
1239
+ fix ( fixer ) {
1240
+ return fixer . replaceText (
1241
+ declaredDependenciesNode ,
1242
+ `[${ callback . name } ]` ,
1243
+ ) ;
1244
+ } ,
1245
+ } ,
1246
+ ] ,
1247
+ } ) ;
1248
+ }
1249
+
1250
+ return {
1251
+ CallExpression : visitCallExpression ,
1252
+ } ;
1254
1253
} ,
1255
1254
} ;
1256
1255
0 commit comments