eslint-plugin-react-hooks-extra 1.23.3-next.8 → 1.24.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +140 -136
- package/dist/index.mjs +140 -136
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var AST = require('@eslint-react/ast');
|
|
4
4
|
var core = require('@eslint-react/core');
|
|
5
5
|
var eff = require('@eslint-react/eff');
|
|
6
6
|
var shared = require('@eslint-react/shared');
|
|
@@ -26,22 +26,23 @@ function _interopNamespace(e) {
|
|
|
26
26
|
return Object.freeze(n);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
var
|
|
29
|
+
var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
|
|
30
30
|
var VAR5__namespace = /*#__PURE__*/_interopNamespace(VAR5);
|
|
31
31
|
|
|
32
32
|
// package.json
|
|
33
33
|
var name = "eslint-plugin-react-hooks-extra";
|
|
34
|
-
var version = "1.
|
|
34
|
+
var version = "1.24.0-beta.0";
|
|
35
35
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
36
|
-
function isFromHookCall(name2, context, settings, predicate = eff.
|
|
36
|
+
function isFromHookCall(name2, context, settings, predicate = eff.returnTrue) {
|
|
37
37
|
const hookAlias = settings.additionalHooks?.[name2] ?? [];
|
|
38
|
-
return (topLevelId) =>
|
|
39
|
-
VAR5__namespace.findVariable(topLevelId, context.sourceCode.getScope(topLevelId))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
return (topLevelId) => {
|
|
39
|
+
const variable = VAR5__namespace.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
|
|
40
|
+
const variableNode = VAR5__namespace.getVariableNode(variable, 0);
|
|
41
|
+
if (variableNode == null) return false;
|
|
42
|
+
if (variableNode.type !== types.AST_NODE_TYPES.CallExpression) return false;
|
|
43
|
+
if (!core.isReactHookCallWithNameAlias(name2, context, hookAlias)(variableNode)) return false;
|
|
44
|
+
return predicate(topLevelId, variableNode);
|
|
45
|
+
};
|
|
45
46
|
}
|
|
46
47
|
function isFromUseStateCall(context, settings) {
|
|
47
48
|
const predicate = (topLevelId, call) => {
|
|
@@ -72,8 +73,13 @@ function isSetFunctionCall(context, settings) {
|
|
|
72
73
|
if (!isAt || index == null) {
|
|
73
74
|
return false;
|
|
74
75
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
76
|
+
const indexScope = context.sourceCode.getScope(node);
|
|
77
|
+
const indexValue = VAR5__namespace.toResolved({
|
|
78
|
+
kind: "lazy",
|
|
79
|
+
node: index,
|
|
80
|
+
initialScope: indexScope
|
|
81
|
+
}).value;
|
|
82
|
+
return indexValue === 1 && isIdFromUseStateCall(callee.object);
|
|
77
83
|
}
|
|
78
84
|
// const [data, setData] = useState();
|
|
79
85
|
// setData();
|
|
@@ -86,8 +92,14 @@ function isSetFunctionCall(context, settings) {
|
|
|
86
92
|
if (!("name" in node.callee.object)) {
|
|
87
93
|
return false;
|
|
88
94
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
95
|
+
const property = node.callee.property;
|
|
96
|
+
const propertyScope = context.sourceCode.getScope(node);
|
|
97
|
+
const propertyValue = VAR5__namespace.toResolved({
|
|
98
|
+
kind: "lazy",
|
|
99
|
+
node: property,
|
|
100
|
+
initialScope: propertyScope
|
|
101
|
+
}).value;
|
|
102
|
+
return propertyValue === 1 && isIdFromUseStateCall(node.callee.object);
|
|
91
103
|
}
|
|
92
104
|
default: {
|
|
93
105
|
return false;
|
|
@@ -148,8 +160,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
148
160
|
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
149
161
|
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
150
162
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
151
|
-
const
|
|
152
|
-
const setupFunctionRef = { current:
|
|
163
|
+
const functionEntries = [];
|
|
164
|
+
const setupFunctionRef = { current: null };
|
|
153
165
|
const setupFunctionIdentifiers = [];
|
|
154
166
|
const indFunctionCalls = [];
|
|
155
167
|
const indSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
@@ -157,10 +169,12 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
157
169
|
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
158
170
|
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new WeakMap();
|
|
159
171
|
const onSetupFunctionEnter = (node) => {
|
|
160
|
-
setupFunctionRef.current =
|
|
172
|
+
setupFunctionRef.current = node;
|
|
161
173
|
};
|
|
162
174
|
const onSetupFunctionExit = (node) => {
|
|
163
|
-
|
|
175
|
+
if (setupFunctionRef.current === node) {
|
|
176
|
+
setupFunctionRef.current = null;
|
|
177
|
+
}
|
|
164
178
|
};
|
|
165
179
|
function isSetupFunction(node) {
|
|
166
180
|
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
@@ -169,26 +183,26 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
169
183
|
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
170
184
|
}
|
|
171
185
|
function getFunctionKind(node) {
|
|
172
|
-
return tsPattern.match(node).when(isSetupFunction, () => "setup").when(
|
|
186
|
+
return tsPattern.match(node).when(isSetupFunction, () => "setup").when(AST__namespace.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
173
187
|
}
|
|
174
188
|
return {
|
|
175
189
|
":function"(node) {
|
|
176
190
|
const kind = getFunctionKind(node);
|
|
177
|
-
|
|
191
|
+
functionEntries.push({ kind, node });
|
|
178
192
|
if (kind === "setup") {
|
|
179
193
|
onSetupFunctionEnter(node);
|
|
180
194
|
}
|
|
181
195
|
},
|
|
182
196
|
":function:exit"(node) {
|
|
183
|
-
const { kind } =
|
|
197
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
184
198
|
if (kind === "setup") {
|
|
185
199
|
onSetupFunctionExit(node);
|
|
186
200
|
}
|
|
187
|
-
|
|
201
|
+
functionEntries.pop();
|
|
188
202
|
},
|
|
189
203
|
CallExpression(node) {
|
|
190
|
-
const setupFunction =
|
|
191
|
-
const pEntry =
|
|
204
|
+
const setupFunction = setupFunctionRef.current;
|
|
205
|
+
const pEntry = functionEntries.at(-1);
|
|
192
206
|
if (pEntry?.node.async) {
|
|
193
207
|
return;
|
|
194
208
|
}
|
|
@@ -208,28 +222,28 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
208
222
|
return;
|
|
209
223
|
}
|
|
210
224
|
default: {
|
|
211
|
-
const
|
|
212
|
-
if (eff.
|
|
225
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(node, isVariableDeclaratorFromHookCall);
|
|
226
|
+
if (variableDeclarator === eff._) {
|
|
213
227
|
const calls = indSetStateCalls.get(pEntry.node) ?? [];
|
|
214
228
|
indSetStateCalls.set(pEntry.node, [...calls, node]);
|
|
215
229
|
return;
|
|
216
230
|
}
|
|
217
|
-
const
|
|
218
|
-
const prevs = indSetStateCallsInUseMemoOrCallback.get(
|
|
219
|
-
indSetStateCallsInUseMemoOrCallback.set(
|
|
231
|
+
const init = variableDeclarator.init;
|
|
232
|
+
const prevs = indSetStateCallsInUseMemoOrCallback.get(init) ?? [];
|
|
233
|
+
indSetStateCallsInUseMemoOrCallback.set(init, [...prevs, node]);
|
|
220
234
|
}
|
|
221
235
|
}
|
|
222
236
|
}).with("useEffect", () => {
|
|
223
|
-
if (
|
|
237
|
+
if (AST__namespace.isFunction(node.arguments.at(0))) {
|
|
224
238
|
return;
|
|
225
239
|
}
|
|
226
|
-
setupFunctionIdentifiers.push(...
|
|
240
|
+
setupFunctionIdentifiers.push(...AST__namespace.getNestedIdentifiers(node));
|
|
227
241
|
}).with("other", () => {
|
|
228
242
|
if (pEntry?.node !== setupFunction) {
|
|
229
243
|
return;
|
|
230
244
|
}
|
|
231
245
|
indFunctionCalls.push(node);
|
|
232
|
-
}).otherwise(eff.
|
|
246
|
+
}).otherwise(() => eff._);
|
|
233
247
|
},
|
|
234
248
|
Identifier(node) {
|
|
235
249
|
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
@@ -247,11 +261,10 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
247
261
|
if (!isUseMemoCall2(parent)) {
|
|
248
262
|
break;
|
|
249
263
|
}
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
264
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall);
|
|
265
|
+
if (variableDeclarator == null) {
|
|
252
266
|
break;
|
|
253
267
|
}
|
|
254
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
255
268
|
const calls = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? [];
|
|
256
269
|
indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...calls, node]);
|
|
257
270
|
break;
|
|
@@ -261,11 +274,10 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
261
274
|
break;
|
|
262
275
|
}
|
|
263
276
|
if (isUseCallbackCall2(node.parent)) {
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
277
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
278
|
+
if (variableDeclarator == null) {
|
|
266
279
|
break;
|
|
267
280
|
}
|
|
268
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
269
281
|
const prevs = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? [];
|
|
270
282
|
indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...prevs, node]);
|
|
271
283
|
}
|
|
@@ -279,7 +291,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
279
291
|
},
|
|
280
292
|
"Program:exit"() {
|
|
281
293
|
const getSetStateCalls = (id, initialScope) => {
|
|
282
|
-
const node =
|
|
294
|
+
const node = VAR5__namespace.getVariableNode(VAR5__namespace.findVariable(id, initialScope), 0);
|
|
283
295
|
switch (node?.type) {
|
|
284
296
|
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
285
297
|
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
@@ -290,7 +302,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
290
302
|
}
|
|
291
303
|
return [];
|
|
292
304
|
};
|
|
293
|
-
for (const [
|
|
305
|
+
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
294
306
|
for (const call of calls) {
|
|
295
307
|
context.report({
|
|
296
308
|
messageId: "noDirectSetStateInUseEffect",
|
|
@@ -310,7 +322,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
310
322
|
messageId: "noDirectSetStateInUseEffect",
|
|
311
323
|
node: setStateCall,
|
|
312
324
|
data: {
|
|
313
|
-
name:
|
|
325
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
314
326
|
}
|
|
315
327
|
});
|
|
316
328
|
}
|
|
@@ -322,7 +334,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
322
334
|
messageId: "noDirectSetStateInUseEffect",
|
|
323
335
|
node: setStateCall,
|
|
324
336
|
data: {
|
|
325
|
-
name:
|
|
337
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
326
338
|
}
|
|
327
339
|
});
|
|
328
340
|
}
|
|
@@ -365,8 +377,8 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
365
377
|
const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
366
378
|
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
367
379
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
368
|
-
const
|
|
369
|
-
const setupFunctionRef = { current:
|
|
380
|
+
const functionEntries = [];
|
|
381
|
+
const setupFunctionRef = { current: null };
|
|
370
382
|
const setupFunctionIdentifiers = [];
|
|
371
383
|
const indFunctionCalls = [];
|
|
372
384
|
const indSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
@@ -374,10 +386,12 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
374
386
|
const indSetStateCallsInUseLayoutEffectSetup = /* @__PURE__ */ new Map();
|
|
375
387
|
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new WeakMap();
|
|
376
388
|
const onSetupFunctionEnter = (node) => {
|
|
377
|
-
setupFunctionRef.current =
|
|
389
|
+
setupFunctionRef.current = node;
|
|
378
390
|
};
|
|
379
391
|
const onSetupFunctionExit = (node) => {
|
|
380
|
-
|
|
392
|
+
if (setupFunctionRef.current === node) {
|
|
393
|
+
setupFunctionRef.current = null;
|
|
394
|
+
}
|
|
381
395
|
};
|
|
382
396
|
function isSetupFunction(node) {
|
|
383
397
|
return node.parent?.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseLayoutEffectLikeCall(node.parent);
|
|
@@ -386,26 +400,26 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
386
400
|
return tsPattern.match(node).when(isUseStateCall2, () => "useState").when(isUseLayoutEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
387
401
|
}
|
|
388
402
|
function getFunctionKind(node) {
|
|
389
|
-
return tsPattern.match(node).when(isSetupFunction, () => "setup").when(
|
|
403
|
+
return tsPattern.match(node).when(isSetupFunction, () => "setup").when(AST__namespace.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
390
404
|
}
|
|
391
405
|
return {
|
|
392
406
|
":function"(node) {
|
|
393
407
|
const kind = getFunctionKind(node);
|
|
394
|
-
|
|
408
|
+
functionEntries.push({ kind, node });
|
|
395
409
|
if (kind === "setup") {
|
|
396
410
|
onSetupFunctionEnter(node);
|
|
397
411
|
}
|
|
398
412
|
},
|
|
399
413
|
":function:exit"(node) {
|
|
400
|
-
const { kind } =
|
|
414
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
401
415
|
if (kind === "setup") {
|
|
402
416
|
onSetupFunctionExit(node);
|
|
403
417
|
}
|
|
404
|
-
|
|
418
|
+
functionEntries.pop();
|
|
405
419
|
},
|
|
406
420
|
CallExpression(node) {
|
|
407
|
-
const setupFunction =
|
|
408
|
-
const pEntry =
|
|
421
|
+
const setupFunction = setupFunctionRef.current;
|
|
422
|
+
const pEntry = functionEntries.at(-1);
|
|
409
423
|
if (pEntry?.node.async) {
|
|
410
424
|
return;
|
|
411
425
|
}
|
|
@@ -425,28 +439,28 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
425
439
|
return;
|
|
426
440
|
}
|
|
427
441
|
default: {
|
|
428
|
-
const
|
|
429
|
-
if (eff.
|
|
442
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(node, isVariableDeclaratorFromHookCall);
|
|
443
|
+
if (variableDeclarator === eff._) {
|
|
430
444
|
const calls = indSetStateCalls.get(pEntry.node) ?? [];
|
|
431
445
|
indSetStateCalls.set(pEntry.node, [...calls, node]);
|
|
432
446
|
return;
|
|
433
447
|
}
|
|
434
|
-
const
|
|
435
|
-
const prevs = indSetStateCallsInUseMemoOrCallback.get(
|
|
436
|
-
indSetStateCallsInUseMemoOrCallback.set(
|
|
448
|
+
const init = variableDeclarator.init;
|
|
449
|
+
const prevs = indSetStateCallsInUseMemoOrCallback.get(init) ?? [];
|
|
450
|
+
indSetStateCallsInUseMemoOrCallback.set(init, [...prevs, node]);
|
|
437
451
|
}
|
|
438
452
|
}
|
|
439
453
|
}).with("useLayoutEffect", () => {
|
|
440
|
-
if (
|
|
454
|
+
if (AST__namespace.isFunction(node.arguments.at(0))) {
|
|
441
455
|
return;
|
|
442
456
|
}
|
|
443
|
-
setupFunctionIdentifiers.push(...
|
|
457
|
+
setupFunctionIdentifiers.push(...AST__namespace.getNestedIdentifiers(node));
|
|
444
458
|
}).with("other", () => {
|
|
445
459
|
if (pEntry?.node !== setupFunction) {
|
|
446
460
|
return;
|
|
447
461
|
}
|
|
448
462
|
indFunctionCalls.push(node);
|
|
449
|
-
}).otherwise(eff.
|
|
463
|
+
}).otherwise(() => eff._);
|
|
450
464
|
},
|
|
451
465
|
Identifier(node) {
|
|
452
466
|
if (node.parent.type === types.AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
@@ -464,11 +478,10 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
464
478
|
if (!isUseMemoCall2(parent)) {
|
|
465
479
|
break;
|
|
466
480
|
}
|
|
467
|
-
const
|
|
468
|
-
if (
|
|
481
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall);
|
|
482
|
+
if (variableDeclarator == null) {
|
|
469
483
|
break;
|
|
470
484
|
}
|
|
471
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
472
485
|
const calls = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? [];
|
|
473
486
|
indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...calls, node]);
|
|
474
487
|
break;
|
|
@@ -478,11 +491,10 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
478
491
|
break;
|
|
479
492
|
}
|
|
480
493
|
if (isUseCallbackCall2(node.parent)) {
|
|
481
|
-
const
|
|
482
|
-
if (
|
|
494
|
+
const variableDeclarator = AST__namespace.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
495
|
+
if (variableDeclarator == null) {
|
|
483
496
|
break;
|
|
484
497
|
}
|
|
485
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
486
498
|
const prevs = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? [];
|
|
487
499
|
indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...prevs, node]);
|
|
488
500
|
}
|
|
@@ -496,7 +508,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
496
508
|
},
|
|
497
509
|
"Program:exit"() {
|
|
498
510
|
const getSetStateCalls = (id, initialScope) => {
|
|
499
|
-
const node =
|
|
511
|
+
const node = VAR5__namespace.getVariableNode(VAR5__namespace.findVariable(id, initialScope), 0);
|
|
500
512
|
switch (node?.type) {
|
|
501
513
|
case types.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
502
514
|
case types.AST_NODE_TYPES.FunctionDeclaration:
|
|
@@ -507,7 +519,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
507
519
|
}
|
|
508
520
|
return [];
|
|
509
521
|
};
|
|
510
|
-
for (const [
|
|
522
|
+
for (const [, calls] of indSetStateCallsInUseLayoutEffectSetup) {
|
|
511
523
|
for (const call of calls) {
|
|
512
524
|
context.report({
|
|
513
525
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
@@ -527,7 +539,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
527
539
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
528
540
|
node: setStateCall,
|
|
529
541
|
data: {
|
|
530
|
-
name:
|
|
542
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
531
543
|
}
|
|
532
544
|
});
|
|
533
545
|
}
|
|
@@ -539,7 +551,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
539
551
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
540
552
|
node: setStateCall,
|
|
541
553
|
data: {
|
|
542
|
-
name:
|
|
554
|
+
name: AST__namespace.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
543
555
|
}
|
|
544
556
|
});
|
|
545
557
|
}
|
|
@@ -582,43 +594,41 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
582
594
|
}
|
|
583
595
|
const scope = context.sourceCode.getScope(node);
|
|
584
596
|
const component = scope.block;
|
|
585
|
-
if (!
|
|
597
|
+
if (!AST__namespace.isFunction(component)) {
|
|
586
598
|
return;
|
|
587
599
|
}
|
|
588
600
|
const [arg0, arg1] = node.arguments;
|
|
589
601
|
if (arg0 == null || arg1 == null) {
|
|
590
602
|
return;
|
|
591
603
|
}
|
|
592
|
-
const hasEmptyDeps =
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
eff.O.exists((x) => x.elements.length === 0)
|
|
601
|
-
);
|
|
604
|
+
const hasEmptyDeps = tsPattern.match(arg1).with({ type: types.AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
605
|
+
const variable = VAR5__namespace.findVariable(n.name, initialScope);
|
|
606
|
+
const variableNode = VAR5__namespace.getVariableNode(variable, 0);
|
|
607
|
+
if (variableNode?.type !== types.AST_NODE_TYPES.ArrayExpression) {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
return variableNode.elements.length === 0;
|
|
611
|
+
}).otherwise(() => false);
|
|
602
612
|
if (!hasEmptyDeps) {
|
|
603
613
|
return;
|
|
604
614
|
}
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
);
|
|
615
|
+
const arg0Node = tsPattern.match(arg0).with({ type: types.AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
616
|
+
if (n.body.type === types.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
617
|
+
return n.body;
|
|
618
|
+
}
|
|
619
|
+
return n;
|
|
620
|
+
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, eff.identity).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
621
|
+
const variable = VAR5__namespace.findVariable(n.name, initialScope);
|
|
622
|
+
const variableNode = VAR5__namespace.getVariableNode(variable, 0);
|
|
623
|
+
if (variableNode?.type !== types.AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== types.AST_NODE_TYPES.FunctionExpression) {
|
|
624
|
+
return eff._;
|
|
625
|
+
}
|
|
626
|
+
return variableNode;
|
|
627
|
+
}).otherwise(() => eff._);
|
|
628
|
+
if (arg0Node === eff._) return;
|
|
629
|
+
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
630
|
+
const arg0NodeReferences = VAR5__namespace.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
631
|
+
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
622
632
|
if (!isReferencedToComponentScope) {
|
|
623
633
|
context.report({
|
|
624
634
|
messageId: "noUnnecessaryUseCallback",
|
|
@@ -663,51 +673,45 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
663
673
|
}
|
|
664
674
|
const scope = context.sourceCode.getScope(node);
|
|
665
675
|
const component = scope.block;
|
|
666
|
-
if (!
|
|
676
|
+
if (!AST__namespace.isFunction(component)) {
|
|
667
677
|
return;
|
|
668
678
|
}
|
|
669
679
|
const [arg0, arg1] = node.arguments;
|
|
670
680
|
if (arg0 == null || arg1 == null) {
|
|
671
681
|
return;
|
|
672
682
|
}
|
|
673
|
-
const hasCallInArg0 =
|
|
674
|
-
eff.O.some(arg0),
|
|
675
|
-
eff.O.filter((n) => AST2__namespace.isFunction(n)),
|
|
676
|
-
eff.O.exists((n) => [...AST2__namespace.getNestedCallExpressions(n.body), ...AST2__namespace.getNestedNewExpressions(n.body)].length > 0)
|
|
677
|
-
);
|
|
683
|
+
const hasCallInArg0 = AST__namespace.isFunction(arg0) && [...AST__namespace.getNestedCallExpressions(arg0.body), ...AST__namespace.getNestedNewExpressions(arg0.body)].length > 0;
|
|
678
684
|
if (hasCallInArg0) {
|
|
679
685
|
return;
|
|
680
686
|
}
|
|
681
|
-
const hasEmptyDeps =
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
eff.O.exists((x) => x.elements.length === 0)
|
|
690
|
-
);
|
|
687
|
+
const hasEmptyDeps = tsPattern.match(arg1).with({ type: types.AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
688
|
+
const variable = VAR5__namespace.findVariable(n.name, initialScope);
|
|
689
|
+
const variableNode = VAR5__namespace.getVariableNode(variable, 0);
|
|
690
|
+
if (variableNode?.type !== types.AST_NODE_TYPES.ArrayExpression) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
return variableNode.elements.length === 0;
|
|
694
|
+
}).otherwise(() => false);
|
|
691
695
|
if (!hasEmptyDeps) {
|
|
692
696
|
return;
|
|
693
697
|
}
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
);
|
|
698
|
+
const arg0Node = tsPattern.match(arg0).with({ type: types.AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
699
|
+
if (n.body.type === types.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
700
|
+
return n.body;
|
|
701
|
+
}
|
|
702
|
+
return n;
|
|
703
|
+
}).with({ type: types.AST_NODE_TYPES.FunctionExpression }, eff.identity).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => {
|
|
704
|
+
const variable = VAR5__namespace.findVariable(n.name, initialScope);
|
|
705
|
+
const variableNode = VAR5__namespace.getVariableNode(variable, 0);
|
|
706
|
+
if (variableNode?.type !== types.AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== types.AST_NODE_TYPES.FunctionExpression) {
|
|
707
|
+
return eff._;
|
|
708
|
+
}
|
|
709
|
+
return variableNode;
|
|
710
|
+
}).otherwise(() => eff._);
|
|
711
|
+
if (arg0Node === eff._) return;
|
|
712
|
+
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
713
|
+
const arg0NodeReferences = VAR5__namespace.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
714
|
+
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
711
715
|
if (!isReferencedToComponentScope) {
|
|
712
716
|
context.report({
|
|
713
717
|
messageId: "noUnnecessaryUseMemo",
|
|
@@ -746,7 +750,7 @@ var no_useless_custom_hooks_default = createRule({
|
|
|
746
750
|
"Program:exit"(node) {
|
|
747
751
|
const allHooks = ctx.getAllHooks(node);
|
|
748
752
|
for (const { name: name2, node: node2, hookCalls } of allHooks.values()) {
|
|
749
|
-
if (
|
|
753
|
+
if (AST__namespace.isEmptyFunction(node2)) {
|
|
750
754
|
continue;
|
|
751
755
|
}
|
|
752
756
|
if (hookCalls.length > 0) {
|
|
@@ -759,7 +763,7 @@ var no_useless_custom_hooks_default = createRule({
|
|
|
759
763
|
messageId: "noUselessCustomHooks",
|
|
760
764
|
node: node2,
|
|
761
765
|
data: {
|
|
762
|
-
name: name2
|
|
766
|
+
name: name2
|
|
763
767
|
}
|
|
764
768
|
});
|
|
765
769
|
}
|
|
@@ -803,11 +807,11 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
803
807
|
if (useStateInput == null) {
|
|
804
808
|
return;
|
|
805
809
|
}
|
|
806
|
-
const nestedCallExpressions =
|
|
810
|
+
const nestedCallExpressions = AST__namespace.getNestedCallExpressions(useStateInput);
|
|
807
811
|
const hasFunctionCall = nestedCallExpressions.some((n) => {
|
|
808
812
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
809
813
|
});
|
|
810
|
-
const hasNewCall =
|
|
814
|
+
const hasNewCall = AST__namespace.getNestedNewExpressions(useStateInput).some((n) => {
|
|
811
815
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
812
816
|
});
|
|
813
817
|
if (!hasFunctionCall && !hasNewCall) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as AST from '@eslint-react/ast';
|
|
2
2
|
import { isReactHookCallWithNameAlias, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, useHookCollector, isUseStateCall, isReactHookName } from '@eslint-react/core';
|
|
3
|
-
import {
|
|
3
|
+
import { _, identity, returnTrue } from '@eslint-react/eff';
|
|
4
4
|
import { createRuleForPlugin, getSettingsFromContext } from '@eslint-react/shared';
|
|
5
5
|
import * as VAR5 from '@eslint-react/var';
|
|
6
6
|
import { AST_NODE_TYPES } from '@typescript-eslint/types';
|
|
@@ -8,17 +8,18 @@ import { match } from 'ts-pattern';
|
|
|
8
8
|
|
|
9
9
|
// package.json
|
|
10
10
|
var name = "eslint-plugin-react-hooks-extra";
|
|
11
|
-
var version = "1.
|
|
11
|
+
var version = "1.24.0-beta.0";
|
|
12
12
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
13
|
-
function isFromHookCall(name2, context, settings, predicate =
|
|
13
|
+
function isFromHookCall(name2, context, settings, predicate = returnTrue) {
|
|
14
14
|
const hookAlias = settings.additionalHooks?.[name2] ?? [];
|
|
15
|
-
return (topLevelId) =>
|
|
16
|
-
VAR5.findVariable(topLevelId, context.sourceCode.getScope(topLevelId))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
return (topLevelId) => {
|
|
16
|
+
const variable = VAR5.findVariable(topLevelId, context.sourceCode.getScope(topLevelId));
|
|
17
|
+
const variableNode = VAR5.getVariableNode(variable, 0);
|
|
18
|
+
if (variableNode == null) return false;
|
|
19
|
+
if (variableNode.type !== AST_NODE_TYPES.CallExpression) return false;
|
|
20
|
+
if (!isReactHookCallWithNameAlias(name2, context, hookAlias)(variableNode)) return false;
|
|
21
|
+
return predicate(topLevelId, variableNode);
|
|
22
|
+
};
|
|
22
23
|
}
|
|
23
24
|
function isFromUseStateCall(context, settings) {
|
|
24
25
|
const predicate = (topLevelId, call) => {
|
|
@@ -49,8 +50,13 @@ function isSetFunctionCall(context, settings) {
|
|
|
49
50
|
if (!isAt || index == null) {
|
|
50
51
|
return false;
|
|
51
52
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
53
|
+
const indexScope = context.sourceCode.getScope(node);
|
|
54
|
+
const indexValue = VAR5.toResolved({
|
|
55
|
+
kind: "lazy",
|
|
56
|
+
node: index,
|
|
57
|
+
initialScope: indexScope
|
|
58
|
+
}).value;
|
|
59
|
+
return indexValue === 1 && isIdFromUseStateCall(callee.object);
|
|
54
60
|
}
|
|
55
61
|
// const [data, setData] = useState();
|
|
56
62
|
// setData();
|
|
@@ -63,8 +69,14 @@ function isSetFunctionCall(context, settings) {
|
|
|
63
69
|
if (!("name" in node.callee.object)) {
|
|
64
70
|
return false;
|
|
65
71
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
72
|
+
const property = node.callee.property;
|
|
73
|
+
const propertyScope = context.sourceCode.getScope(node);
|
|
74
|
+
const propertyValue = VAR5.toResolved({
|
|
75
|
+
kind: "lazy",
|
|
76
|
+
node: property,
|
|
77
|
+
initialScope: propertyScope
|
|
78
|
+
}).value;
|
|
79
|
+
return propertyValue === 1 && isIdFromUseStateCall(node.callee.object);
|
|
68
80
|
}
|
|
69
81
|
default: {
|
|
70
82
|
return false;
|
|
@@ -125,8 +137,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
125
137
|
const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
126
138
|
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
127
139
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
128
|
-
const
|
|
129
|
-
const setupFunctionRef = { current:
|
|
140
|
+
const functionEntries = [];
|
|
141
|
+
const setupFunctionRef = { current: null };
|
|
130
142
|
const setupFunctionIdentifiers = [];
|
|
131
143
|
const indFunctionCalls = [];
|
|
132
144
|
const indSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
@@ -134,10 +146,12 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
134
146
|
const indSetStateCallsInUseEffectSetup = /* @__PURE__ */ new Map();
|
|
135
147
|
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new WeakMap();
|
|
136
148
|
const onSetupFunctionEnter = (node) => {
|
|
137
|
-
setupFunctionRef.current =
|
|
149
|
+
setupFunctionRef.current = node;
|
|
138
150
|
};
|
|
139
151
|
const onSetupFunctionExit = (node) => {
|
|
140
|
-
|
|
152
|
+
if (setupFunctionRef.current === node) {
|
|
153
|
+
setupFunctionRef.current = null;
|
|
154
|
+
}
|
|
141
155
|
};
|
|
142
156
|
function isSetupFunction(node) {
|
|
143
157
|
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseEffectLikeCall(node.parent);
|
|
@@ -146,26 +160,26 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
146
160
|
return match(node).when(isUseStateCall2, () => "useState").when(isUseEffectLikeCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
147
161
|
}
|
|
148
162
|
function getFunctionKind(node) {
|
|
149
|
-
return match(node).when(isSetupFunction, () => "setup").when(
|
|
163
|
+
return match(node).when(isSetupFunction, () => "setup").when(AST.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
150
164
|
}
|
|
151
165
|
return {
|
|
152
166
|
":function"(node) {
|
|
153
167
|
const kind = getFunctionKind(node);
|
|
154
|
-
|
|
168
|
+
functionEntries.push({ kind, node });
|
|
155
169
|
if (kind === "setup") {
|
|
156
170
|
onSetupFunctionEnter(node);
|
|
157
171
|
}
|
|
158
172
|
},
|
|
159
173
|
":function:exit"(node) {
|
|
160
|
-
const { kind } =
|
|
174
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
161
175
|
if (kind === "setup") {
|
|
162
176
|
onSetupFunctionExit(node);
|
|
163
177
|
}
|
|
164
|
-
|
|
178
|
+
functionEntries.pop();
|
|
165
179
|
},
|
|
166
180
|
CallExpression(node) {
|
|
167
|
-
const setupFunction =
|
|
168
|
-
const pEntry =
|
|
181
|
+
const setupFunction = setupFunctionRef.current;
|
|
182
|
+
const pEntry = functionEntries.at(-1);
|
|
169
183
|
if (pEntry?.node.async) {
|
|
170
184
|
return;
|
|
171
185
|
}
|
|
@@ -185,28 +199,28 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
185
199
|
return;
|
|
186
200
|
}
|
|
187
201
|
default: {
|
|
188
|
-
const
|
|
189
|
-
if (
|
|
202
|
+
const variableDeclarator = AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall);
|
|
203
|
+
if (variableDeclarator === _) {
|
|
190
204
|
const calls = indSetStateCalls.get(pEntry.node) ?? [];
|
|
191
205
|
indSetStateCalls.set(pEntry.node, [...calls, node]);
|
|
192
206
|
return;
|
|
193
207
|
}
|
|
194
|
-
const
|
|
195
|
-
const prevs = indSetStateCallsInUseMemoOrCallback.get(
|
|
196
|
-
indSetStateCallsInUseMemoOrCallback.set(
|
|
208
|
+
const init = variableDeclarator.init;
|
|
209
|
+
const prevs = indSetStateCallsInUseMemoOrCallback.get(init) ?? [];
|
|
210
|
+
indSetStateCallsInUseMemoOrCallback.set(init, [...prevs, node]);
|
|
197
211
|
}
|
|
198
212
|
}
|
|
199
213
|
}).with("useEffect", () => {
|
|
200
|
-
if (
|
|
214
|
+
if (AST.isFunction(node.arguments.at(0))) {
|
|
201
215
|
return;
|
|
202
216
|
}
|
|
203
|
-
setupFunctionIdentifiers.push(...
|
|
217
|
+
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
204
218
|
}).with("other", () => {
|
|
205
219
|
if (pEntry?.node !== setupFunction) {
|
|
206
220
|
return;
|
|
207
221
|
}
|
|
208
222
|
indFunctionCalls.push(node);
|
|
209
|
-
}).otherwise(
|
|
223
|
+
}).otherwise(() => _);
|
|
210
224
|
},
|
|
211
225
|
Identifier(node) {
|
|
212
226
|
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
@@ -224,11 +238,10 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
224
238
|
if (!isUseMemoCall2(parent)) {
|
|
225
239
|
break;
|
|
226
240
|
}
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
241
|
+
const variableDeclarator = AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall);
|
|
242
|
+
if (variableDeclarator == null) {
|
|
229
243
|
break;
|
|
230
244
|
}
|
|
231
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
232
245
|
const calls = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? [];
|
|
233
246
|
indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...calls, node]);
|
|
234
247
|
break;
|
|
@@ -238,11 +251,10 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
238
251
|
break;
|
|
239
252
|
}
|
|
240
253
|
if (isUseCallbackCall2(node.parent)) {
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
254
|
+
const variableDeclarator = AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
255
|
+
if (variableDeclarator == null) {
|
|
243
256
|
break;
|
|
244
257
|
}
|
|
245
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
246
258
|
const prevs = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? [];
|
|
247
259
|
indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...prevs, node]);
|
|
248
260
|
}
|
|
@@ -256,7 +268,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
256
268
|
},
|
|
257
269
|
"Program:exit"() {
|
|
258
270
|
const getSetStateCalls = (id, initialScope) => {
|
|
259
|
-
const node =
|
|
271
|
+
const node = VAR5.getVariableNode(VAR5.findVariable(id, initialScope), 0);
|
|
260
272
|
switch (node?.type) {
|
|
261
273
|
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
262
274
|
case AST_NODE_TYPES.FunctionDeclaration:
|
|
@@ -267,7 +279,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
267
279
|
}
|
|
268
280
|
return [];
|
|
269
281
|
};
|
|
270
|
-
for (const [
|
|
282
|
+
for (const [, calls] of indSetStateCallsInUseEffectSetup) {
|
|
271
283
|
for (const call of calls) {
|
|
272
284
|
context.report({
|
|
273
285
|
messageId: "noDirectSetStateInUseEffect",
|
|
@@ -287,7 +299,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
287
299
|
messageId: "noDirectSetStateInUseEffect",
|
|
288
300
|
node: setStateCall,
|
|
289
301
|
data: {
|
|
290
|
-
name:
|
|
302
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
291
303
|
}
|
|
292
304
|
});
|
|
293
305
|
}
|
|
@@ -299,7 +311,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
299
311
|
messageId: "noDirectSetStateInUseEffect",
|
|
300
312
|
node: setStateCall,
|
|
301
313
|
data: {
|
|
302
|
-
name:
|
|
314
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
303
315
|
}
|
|
304
316
|
});
|
|
305
317
|
}
|
|
@@ -342,8 +354,8 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
342
354
|
const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
|
|
343
355
|
const isSetStateCall = isSetFunctionCall(context, settings);
|
|
344
356
|
const isIdFromUseStateCall = isFromUseStateCall(context, settings);
|
|
345
|
-
const
|
|
346
|
-
const setupFunctionRef = { current:
|
|
357
|
+
const functionEntries = [];
|
|
358
|
+
const setupFunctionRef = { current: null };
|
|
347
359
|
const setupFunctionIdentifiers = [];
|
|
348
360
|
const indFunctionCalls = [];
|
|
349
361
|
const indSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
@@ -351,10 +363,12 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
351
363
|
const indSetStateCallsInUseLayoutEffectSetup = /* @__PURE__ */ new Map();
|
|
352
364
|
const indSetStateCallsInUseMemoOrCallback = /* @__PURE__ */ new WeakMap();
|
|
353
365
|
const onSetupFunctionEnter = (node) => {
|
|
354
|
-
setupFunctionRef.current =
|
|
366
|
+
setupFunctionRef.current = node;
|
|
355
367
|
};
|
|
356
368
|
const onSetupFunctionExit = (node) => {
|
|
357
|
-
|
|
369
|
+
if (setupFunctionRef.current === node) {
|
|
370
|
+
setupFunctionRef.current = null;
|
|
371
|
+
}
|
|
358
372
|
};
|
|
359
373
|
function isSetupFunction(node) {
|
|
360
374
|
return node.parent?.type === AST_NODE_TYPES.CallExpression && node.parent.callee !== node && isUseLayoutEffectLikeCall(node.parent);
|
|
@@ -363,26 +377,26 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
363
377
|
return match(node).when(isUseStateCall2, () => "useState").when(isUseLayoutEffectLikeCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
|
|
364
378
|
}
|
|
365
379
|
function getFunctionKind(node) {
|
|
366
|
-
return match(node).when(isSetupFunction, () => "setup").when(
|
|
380
|
+
return match(node).when(isSetupFunction, () => "setup").when(AST.isFunctionOfImmediatelyInvoked, () => "immediate").otherwise(() => "other");
|
|
367
381
|
}
|
|
368
382
|
return {
|
|
369
383
|
":function"(node) {
|
|
370
384
|
const kind = getFunctionKind(node);
|
|
371
|
-
|
|
385
|
+
functionEntries.push({ kind, node });
|
|
372
386
|
if (kind === "setup") {
|
|
373
387
|
onSetupFunctionEnter(node);
|
|
374
388
|
}
|
|
375
389
|
},
|
|
376
390
|
":function:exit"(node) {
|
|
377
|
-
const { kind } =
|
|
391
|
+
const { kind } = functionEntries.at(-1) ?? {};
|
|
378
392
|
if (kind === "setup") {
|
|
379
393
|
onSetupFunctionExit(node);
|
|
380
394
|
}
|
|
381
|
-
|
|
395
|
+
functionEntries.pop();
|
|
382
396
|
},
|
|
383
397
|
CallExpression(node) {
|
|
384
|
-
const setupFunction =
|
|
385
|
-
const pEntry =
|
|
398
|
+
const setupFunction = setupFunctionRef.current;
|
|
399
|
+
const pEntry = functionEntries.at(-1);
|
|
386
400
|
if (pEntry?.node.async) {
|
|
387
401
|
return;
|
|
388
402
|
}
|
|
@@ -402,28 +416,28 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
402
416
|
return;
|
|
403
417
|
}
|
|
404
418
|
default: {
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
419
|
+
const variableDeclarator = AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall);
|
|
420
|
+
if (variableDeclarator === _) {
|
|
407
421
|
const calls = indSetStateCalls.get(pEntry.node) ?? [];
|
|
408
422
|
indSetStateCalls.set(pEntry.node, [...calls, node]);
|
|
409
423
|
return;
|
|
410
424
|
}
|
|
411
|
-
const
|
|
412
|
-
const prevs = indSetStateCallsInUseMemoOrCallback.get(
|
|
413
|
-
indSetStateCallsInUseMemoOrCallback.set(
|
|
425
|
+
const init = variableDeclarator.init;
|
|
426
|
+
const prevs = indSetStateCallsInUseMemoOrCallback.get(init) ?? [];
|
|
427
|
+
indSetStateCallsInUseMemoOrCallback.set(init, [...prevs, node]);
|
|
414
428
|
}
|
|
415
429
|
}
|
|
416
430
|
}).with("useLayoutEffect", () => {
|
|
417
|
-
if (
|
|
431
|
+
if (AST.isFunction(node.arguments.at(0))) {
|
|
418
432
|
return;
|
|
419
433
|
}
|
|
420
|
-
setupFunctionIdentifiers.push(...
|
|
434
|
+
setupFunctionIdentifiers.push(...AST.getNestedIdentifiers(node));
|
|
421
435
|
}).with("other", () => {
|
|
422
436
|
if (pEntry?.node !== setupFunction) {
|
|
423
437
|
return;
|
|
424
438
|
}
|
|
425
439
|
indFunctionCalls.push(node);
|
|
426
|
-
}).otherwise(
|
|
440
|
+
}).otherwise(() => _);
|
|
427
441
|
},
|
|
428
442
|
Identifier(node) {
|
|
429
443
|
if (node.parent.type === AST_NODE_TYPES.CallExpression && node.parent.callee === node) {
|
|
@@ -441,11 +455,10 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
441
455
|
if (!isUseMemoCall2(parent)) {
|
|
442
456
|
break;
|
|
443
457
|
}
|
|
444
|
-
const
|
|
445
|
-
if (
|
|
458
|
+
const variableDeclarator = AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall);
|
|
459
|
+
if (variableDeclarator == null) {
|
|
446
460
|
break;
|
|
447
461
|
}
|
|
448
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
449
462
|
const calls = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? [];
|
|
450
463
|
indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...calls, node]);
|
|
451
464
|
break;
|
|
@@ -455,11 +468,10 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
455
468
|
break;
|
|
456
469
|
}
|
|
457
470
|
if (isUseCallbackCall2(node.parent)) {
|
|
458
|
-
const
|
|
459
|
-
if (
|
|
471
|
+
const variableDeclarator = AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall);
|
|
472
|
+
if (variableDeclarator == null) {
|
|
460
473
|
break;
|
|
461
474
|
}
|
|
462
|
-
const variableDeclarator = mbVariableDeclarator.value;
|
|
463
475
|
const prevs = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? [];
|
|
464
476
|
indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...prevs, node]);
|
|
465
477
|
}
|
|
@@ -473,7 +485,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
473
485
|
},
|
|
474
486
|
"Program:exit"() {
|
|
475
487
|
const getSetStateCalls = (id, initialScope) => {
|
|
476
|
-
const node =
|
|
488
|
+
const node = VAR5.getVariableNode(VAR5.findVariable(id, initialScope), 0);
|
|
477
489
|
switch (node?.type) {
|
|
478
490
|
case AST_NODE_TYPES.ArrowFunctionExpression:
|
|
479
491
|
case AST_NODE_TYPES.FunctionDeclaration:
|
|
@@ -484,7 +496,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
484
496
|
}
|
|
485
497
|
return [];
|
|
486
498
|
};
|
|
487
|
-
for (const [
|
|
499
|
+
for (const [, calls] of indSetStateCallsInUseLayoutEffectSetup) {
|
|
488
500
|
for (const call of calls) {
|
|
489
501
|
context.report({
|
|
490
502
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
@@ -504,7 +516,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
504
516
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
505
517
|
node: setStateCall,
|
|
506
518
|
data: {
|
|
507
|
-
name:
|
|
519
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
508
520
|
}
|
|
509
521
|
});
|
|
510
522
|
}
|
|
@@ -516,7 +528,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
516
528
|
messageId: "noDirectSetStateInUseLayoutEffect",
|
|
517
529
|
node: setStateCall,
|
|
518
530
|
data: {
|
|
519
|
-
name:
|
|
531
|
+
name: AST.toReadableNodeName(setStateCall, (n) => context.sourceCode.getText(n))
|
|
520
532
|
}
|
|
521
533
|
});
|
|
522
534
|
}
|
|
@@ -559,43 +571,41 @@ var no_unnecessary_use_callback_default = createRule({
|
|
|
559
571
|
}
|
|
560
572
|
const scope = context.sourceCode.getScope(node);
|
|
561
573
|
const component = scope.block;
|
|
562
|
-
if (!
|
|
574
|
+
if (!AST.isFunction(component)) {
|
|
563
575
|
return;
|
|
564
576
|
}
|
|
565
577
|
const [arg0, arg1] = node.arguments;
|
|
566
578
|
if (arg0 == null || arg1 == null) {
|
|
567
579
|
return;
|
|
568
580
|
}
|
|
569
|
-
const hasEmptyDeps =
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
O.exists((x) => x.elements.length === 0)
|
|
578
|
-
);
|
|
581
|
+
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
582
|
+
const variable = VAR5.findVariable(n.name, initialScope);
|
|
583
|
+
const variableNode = VAR5.getVariableNode(variable, 0);
|
|
584
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
return variableNode.elements.length === 0;
|
|
588
|
+
}).otherwise(() => false);
|
|
579
589
|
if (!hasEmptyDeps) {
|
|
580
590
|
return;
|
|
581
591
|
}
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
);
|
|
592
|
+
const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
593
|
+
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
594
|
+
return n.body;
|
|
595
|
+
}
|
|
596
|
+
return n;
|
|
597
|
+
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
598
|
+
const variable = VAR5.findVariable(n.name, initialScope);
|
|
599
|
+
const variableNode = VAR5.getVariableNode(variable, 0);
|
|
600
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
601
|
+
return _;
|
|
602
|
+
}
|
|
603
|
+
return variableNode;
|
|
604
|
+
}).otherwise(() => _);
|
|
605
|
+
if (arg0Node === _) return;
|
|
606
|
+
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
607
|
+
const arg0NodeReferences = VAR5.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
608
|
+
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
599
609
|
if (!isReferencedToComponentScope) {
|
|
600
610
|
context.report({
|
|
601
611
|
messageId: "noUnnecessaryUseCallback",
|
|
@@ -640,51 +650,45 @@ var no_unnecessary_use_memo_default = createRule({
|
|
|
640
650
|
}
|
|
641
651
|
const scope = context.sourceCode.getScope(node);
|
|
642
652
|
const component = scope.block;
|
|
643
|
-
if (!
|
|
653
|
+
if (!AST.isFunction(component)) {
|
|
644
654
|
return;
|
|
645
655
|
}
|
|
646
656
|
const [arg0, arg1] = node.arguments;
|
|
647
657
|
if (arg0 == null || arg1 == null) {
|
|
648
658
|
return;
|
|
649
659
|
}
|
|
650
|
-
const hasCallInArg0 =
|
|
651
|
-
O.some(arg0),
|
|
652
|
-
O.filter((n) => AST2.isFunction(n)),
|
|
653
|
-
O.exists((n) => [...AST2.getNestedCallExpressions(n.body), ...AST2.getNestedNewExpressions(n.body)].length > 0)
|
|
654
|
-
);
|
|
660
|
+
const hasCallInArg0 = AST.isFunction(arg0) && [...AST.getNestedCallExpressions(arg0.body), ...AST.getNestedNewExpressions(arg0.body)].length > 0;
|
|
655
661
|
if (hasCallInArg0) {
|
|
656
662
|
return;
|
|
657
663
|
}
|
|
658
|
-
const hasEmptyDeps =
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
O.exists((x) => x.elements.length === 0)
|
|
667
|
-
);
|
|
664
|
+
const hasEmptyDeps = match(arg1).with({ type: AST_NODE_TYPES.ArrayExpression }, (n) => n.elements.length === 0).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
665
|
+
const variable = VAR5.findVariable(n.name, initialScope);
|
|
666
|
+
const variableNode = VAR5.getVariableNode(variable, 0);
|
|
667
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
return variableNode.elements.length === 0;
|
|
671
|
+
}).otherwise(() => false);
|
|
668
672
|
if (!hasEmptyDeps) {
|
|
669
673
|
return;
|
|
670
674
|
}
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
);
|
|
675
|
+
const arg0Node = match(arg0).with({ type: AST_NODE_TYPES.ArrowFunctionExpression }, (n) => {
|
|
676
|
+
if (n.body.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
677
|
+
return n.body;
|
|
678
|
+
}
|
|
679
|
+
return n;
|
|
680
|
+
}).with({ type: AST_NODE_TYPES.FunctionExpression }, identity).with({ type: AST_NODE_TYPES.Identifier }, (n) => {
|
|
681
|
+
const variable = VAR5.findVariable(n.name, initialScope);
|
|
682
|
+
const variableNode = VAR5.getVariableNode(variable, 0);
|
|
683
|
+
if (variableNode?.type !== AST_NODE_TYPES.ArrowFunctionExpression && variableNode?.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
684
|
+
return _;
|
|
685
|
+
}
|
|
686
|
+
return variableNode;
|
|
687
|
+
}).otherwise(() => _);
|
|
688
|
+
if (arg0Node === _) return;
|
|
689
|
+
const arg0NodeScope = context.sourceCode.getScope(arg0Node);
|
|
690
|
+
const arg0NodeReferences = VAR5.getChidScopes(arg0NodeScope).flatMap((x) => x.references);
|
|
691
|
+
const isReferencedToComponentScope = arg0NodeReferences.some((x) => x.resolved?.scope.block === component);
|
|
688
692
|
if (!isReferencedToComponentScope) {
|
|
689
693
|
context.report({
|
|
690
694
|
messageId: "noUnnecessaryUseMemo",
|
|
@@ -723,7 +727,7 @@ var no_useless_custom_hooks_default = createRule({
|
|
|
723
727
|
"Program:exit"(node) {
|
|
724
728
|
const allHooks = ctx.getAllHooks(node);
|
|
725
729
|
for (const { name: name2, node: node2, hookCalls } of allHooks.values()) {
|
|
726
|
-
if (
|
|
730
|
+
if (AST.isEmptyFunction(node2)) {
|
|
727
731
|
continue;
|
|
728
732
|
}
|
|
729
733
|
if (hookCalls.length > 0) {
|
|
@@ -736,7 +740,7 @@ var no_useless_custom_hooks_default = createRule({
|
|
|
736
740
|
messageId: "noUselessCustomHooks",
|
|
737
741
|
node: node2,
|
|
738
742
|
data: {
|
|
739
|
-
name: name2
|
|
743
|
+
name: name2
|
|
740
744
|
}
|
|
741
745
|
});
|
|
742
746
|
}
|
|
@@ -780,11 +784,11 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
780
784
|
if (useStateInput == null) {
|
|
781
785
|
return;
|
|
782
786
|
}
|
|
783
|
-
const nestedCallExpressions =
|
|
787
|
+
const nestedCallExpressions = AST.getNestedCallExpressions(useStateInput);
|
|
784
788
|
const hasFunctionCall = nestedCallExpressions.some((n) => {
|
|
785
789
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
786
790
|
});
|
|
787
|
-
const hasNewCall =
|
|
791
|
+
const hasNewCall = AST.getNestedNewExpressions(useStateInput).some((n) => {
|
|
788
792
|
return "name" in n.callee && !ALLOW_LIST.includes(n.callee.name);
|
|
789
793
|
});
|
|
790
794
|
if (!hasFunctionCall && !hasNewCall) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0-beta.0",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Hooks related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"eslint-plugin",
|
|
12
12
|
"eslint-plugin-react-hooks-extra"
|
|
13
13
|
],
|
|
14
|
-
"homepage": "https://github.com/
|
|
14
|
+
"homepage": "https://github.com/Rel1cx/eslint-react",
|
|
15
15
|
"bugs": {
|
|
16
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/Rel1cx/eslint-react/issues"
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/
|
|
20
|
+
"url": "git+https://github.com/Rel1cx/eslint-react.git",
|
|
21
21
|
"directory": "packages/plugins/eslint-plugin-react-hooks-extra"
|
|
22
22
|
},
|
|
23
23
|
"license": "MIT",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"@typescript-eslint/utils": "^8.19.1",
|
|
49
49
|
"string-ts": "^2.2.0",
|
|
50
50
|
"ts-pattern": "^5.6.0",
|
|
51
|
-
"@eslint-react/ast": "1.
|
|
52
|
-
"@eslint-react/
|
|
53
|
-
"@eslint-react/
|
|
54
|
-
"@eslint-react/shared": "1.
|
|
55
|
-
"@eslint-react/
|
|
56
|
-
"@eslint-react/
|
|
57
|
-
"@eslint-react/
|
|
51
|
+
"@eslint-react/ast": "1.24.0-beta.0",
|
|
52
|
+
"@eslint-react/core": "1.24.0-beta.0",
|
|
53
|
+
"@eslint-react/jsx": "1.24.0-beta.0",
|
|
54
|
+
"@eslint-react/shared": "1.24.0-beta.0",
|
|
55
|
+
"@eslint-react/var": "1.24.0-beta.0",
|
|
56
|
+
"@eslint-react/eff": "1.24.0-beta.0",
|
|
57
|
+
"@eslint-react/types": "1.24.0-beta.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/react": "^19.0.4",
|