eslint-plugin-react-hooks-extra 1.5.28-beta.2 → 1.5.28-beta.4
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 +73 -87
- package/dist/index.mjs +74 -88
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var astUtils = require('@typescript-eslint/utils/ast-utils');
|
|
|
9
9
|
|
|
10
10
|
// package.json
|
|
11
11
|
var name = "eslint-plugin-react-hooks-extra";
|
|
12
|
-
var version = "1.5.28-beta.
|
|
12
|
+
var version = "1.5.28-beta.4";
|
|
13
13
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
14
14
|
|
|
15
15
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -467,29 +467,29 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
467
467
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
|
|
468
468
|
}
|
|
469
469
|
const useEffectCallRef = tools.MutRef.make(null);
|
|
470
|
-
const callStack = tools.MutList.make();
|
|
471
470
|
const functionStack = tools.MutList.make();
|
|
472
471
|
const effectFunctionRef = tools.MutRef.make(null);
|
|
473
472
|
const cleanUpFunctionRef = tools.MutRef.make(null);
|
|
474
|
-
const indirectFunctionCalls =
|
|
473
|
+
const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
|
|
475
474
|
const indirectSetStateCalls = /* @__PURE__ */ new Map();
|
|
476
|
-
const onUseEffectCallEnter = (node) => void tools.MutRef.set(useEffectCallRef, node);
|
|
477
|
-
const onUseEffectCallExit = () => void tools.MutRef.set(useEffectCallRef, null);
|
|
478
475
|
return {
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
476
|
+
":function"(node) {
|
|
477
|
+
const functionKind = getFunctionKind(node);
|
|
478
|
+
tools.MutList.append(functionStack, [node, functionKind]);
|
|
479
|
+
$(functionKind).with("effect", () => {
|
|
480
|
+
tools.MutRef.set(effectFunctionRef, node);
|
|
481
|
+
}).with("cleanup", () => {
|
|
482
|
+
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
483
|
+
}).otherwise(tools.F.constVoid);
|
|
484
|
+
},
|
|
485
|
+
":function:exit"(node) {
|
|
486
|
+
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
487
|
+
if (effectFn === node) {
|
|
488
|
+
tools.MutRef.set(effectFunctionRef, null);
|
|
489
489
|
}
|
|
490
|
+
tools.MutList.pop(functionStack);
|
|
490
491
|
},
|
|
491
492
|
CallExpression(node) {
|
|
492
|
-
tools.MutList.append(callStack, node);
|
|
493
493
|
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
494
494
|
const [parentFn, parentFnKind] = tools.MutList.tail(functionStack) ?? [];
|
|
495
495
|
if (parentFn?.async) return;
|
|
@@ -497,47 +497,39 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
497
497
|
$(callKind).with("setState", () => {
|
|
498
498
|
if (!parentFn) return;
|
|
499
499
|
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
500
|
-
|
|
500
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? tools.Chunk.empty();
|
|
501
|
+
indirectSetStateCalls.set(parentFn, tools.Chunk.append(calls, node));
|
|
501
502
|
return;
|
|
502
503
|
}
|
|
503
504
|
context.report({
|
|
504
|
-
|
|
505
|
-
|
|
505
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
506
|
+
node
|
|
506
507
|
});
|
|
507
508
|
}).with("useEffect", () => {
|
|
508
|
-
|
|
509
|
-
}).with("useState", () => {
|
|
510
|
-
}).with("then", () => {
|
|
509
|
+
tools.MutRef.set(useEffectCallRef, node);
|
|
511
510
|
}).with("other", () => {
|
|
512
|
-
indirectFunctionCalls.
|
|
513
|
-
}).
|
|
514
|
-
},
|
|
515
|
-
":function"(node) {
|
|
516
|
-
const functionKind = getFunctionKind(node);
|
|
517
|
-
tools.MutList.append(functionStack, [node, functionKind]);
|
|
518
|
-
$(functionKind).with("effect", () => {
|
|
519
|
-
tools.MutRef.set(effectFunctionRef, node);
|
|
520
|
-
}).with("cleanup", () => {
|
|
521
|
-
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
522
|
-
}).otherwise(() => {
|
|
523
|
-
});
|
|
524
|
-
},
|
|
525
|
-
":function:exit"(node) {
|
|
526
|
-
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
527
|
-
if (effectFn === node) {
|
|
528
|
-
tools.MutRef.set(effectFunctionRef, null);
|
|
529
|
-
}
|
|
530
|
-
tools.MutList.pop(functionStack);
|
|
511
|
+
tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
|
|
512
|
+
}).otherwise(tools.F.constVoid);
|
|
531
513
|
},
|
|
532
514
|
"CallExpression:exit"(node) {
|
|
533
|
-
tools.MutList.pop(callStack);
|
|
534
515
|
if (tools.MutRef.get(useEffectCallRef) === node) {
|
|
535
|
-
onUseEffectCallExit();
|
|
536
516
|
tools.MutRef.set(useEffectCallRef, null);
|
|
537
517
|
}
|
|
538
518
|
},
|
|
519
|
+
Identifier(node) {
|
|
520
|
+
const isInUseEffectCall = tools.MutRef.get(useEffectCallRef) !== null;
|
|
521
|
+
const parentFn = tools.MutList.tail(functionStack)?.[0];
|
|
522
|
+
const useEffectCall = tools.MutRef.get(useEffectCallRef);
|
|
523
|
+
const isEffectFunction2 = useEffectCall && tools.O.isSome(ast.traverseUp(useEffectCall, (n2) => n2 === parentFn));
|
|
524
|
+
if (isFromUseStateCall(node) && isInUseEffectCall && isEffectFunction2) {
|
|
525
|
+
context.report({
|
|
526
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
527
|
+
node
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
},
|
|
539
531
|
"Program:exit"() {
|
|
540
|
-
for (const call of indirectFunctionCalls) {
|
|
532
|
+
for (const call of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
|
|
541
533
|
if (!("name" in call.callee)) continue;
|
|
542
534
|
const { name: name2 } = call.callee;
|
|
543
535
|
const setStateCalls = tools.F.pipe(
|
|
@@ -545,6 +537,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
545
537
|
tools.O.flatMap(_var.getVariableInit(0)),
|
|
546
538
|
tools.O.filter(ast.isFunction),
|
|
547
539
|
tools.O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
|
|
540
|
+
tools.O.map(tools.Chunk.toReadonlyArray),
|
|
548
541
|
tools.O.getOrElse(() => [])
|
|
549
542
|
);
|
|
550
543
|
for (const setStateCall of setStateCalls) {
|
|
@@ -641,29 +634,29 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
641
634
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
|
|
642
635
|
}
|
|
643
636
|
const useLayoutEffectCallRef = tools.MutRef.make(null);
|
|
644
|
-
const callStack = tools.MutList.make();
|
|
645
637
|
const functionStack = tools.MutList.make();
|
|
646
638
|
const effectFunctionRef = tools.MutRef.make(null);
|
|
647
639
|
const cleanUpFunctionRef = tools.MutRef.make(null);
|
|
648
|
-
const indirectFunctionCalls =
|
|
640
|
+
const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
|
|
649
641
|
const indirectSetStateCalls = /* @__PURE__ */ new Map();
|
|
650
|
-
const onUseLayoutEffectCallEnter = (node) => void tools.MutRef.set(useLayoutEffectCallRef, node);
|
|
651
|
-
const onUseLayoutEffectCallExit = () => void tools.MutRef.set(useLayoutEffectCallRef, null);
|
|
652
642
|
return {
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
643
|
+
":function"(node) {
|
|
644
|
+
const functionKind = getFunctionKind(node);
|
|
645
|
+
tools.MutList.append(functionStack, [node, functionKind]);
|
|
646
|
+
$(functionKind).with("effect", () => {
|
|
647
|
+
tools.MutRef.set(effectFunctionRef, node);
|
|
648
|
+
}).with("cleanup", () => {
|
|
649
|
+
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
650
|
+
}).otherwise(tools.F.constVoid);
|
|
651
|
+
},
|
|
652
|
+
":function:exit"(node) {
|
|
653
|
+
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
654
|
+
if (effectFn === node) {
|
|
655
|
+
tools.MutRef.set(effectFunctionRef, null);
|
|
663
656
|
}
|
|
657
|
+
tools.MutList.pop(functionStack);
|
|
664
658
|
},
|
|
665
659
|
CallExpression(node) {
|
|
666
|
-
tools.MutList.append(callStack, node);
|
|
667
660
|
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
668
661
|
const [parentFn, parentFnKind] = tools.MutList.tail(functionStack) ?? [];
|
|
669
662
|
if (parentFn?.async) return;
|
|
@@ -671,47 +664,39 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
671
664
|
$(callKind).with("setState", () => {
|
|
672
665
|
if (!parentFn) return;
|
|
673
666
|
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
674
|
-
|
|
667
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? tools.Chunk.empty();
|
|
668
|
+
indirectSetStateCalls.set(parentFn, tools.Chunk.append(calls, node));
|
|
675
669
|
return;
|
|
676
670
|
}
|
|
677
671
|
context.report({
|
|
678
|
-
|
|
679
|
-
|
|
672
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
673
|
+
node
|
|
680
674
|
});
|
|
681
675
|
}).with("useLayoutEffect", () => {
|
|
682
|
-
|
|
683
|
-
}).with("useState", () => {
|
|
684
|
-
}).with("then", () => {
|
|
676
|
+
tools.MutRef.set(useLayoutEffectCallRef, node);
|
|
685
677
|
}).with("other", () => {
|
|
686
|
-
indirectFunctionCalls.
|
|
687
|
-
}).
|
|
688
|
-
},
|
|
689
|
-
":function"(node) {
|
|
690
|
-
const functionKind = getFunctionKind(node);
|
|
691
|
-
tools.MutList.append(functionStack, [node, functionKind]);
|
|
692
|
-
$(functionKind).with("effect", () => {
|
|
693
|
-
tools.MutRef.set(effectFunctionRef, node);
|
|
694
|
-
}).with("cleanup", () => {
|
|
695
|
-
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
696
|
-
}).otherwise(() => {
|
|
697
|
-
});
|
|
698
|
-
},
|
|
699
|
-
":function:exit"(node) {
|
|
700
|
-
const effectFn = tools.MutRef.get(effectFunctionRef);
|
|
701
|
-
if (effectFn === node) {
|
|
702
|
-
tools.MutRef.set(effectFunctionRef, null);
|
|
703
|
-
}
|
|
704
|
-
tools.MutList.pop(functionStack);
|
|
678
|
+
tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
|
|
679
|
+
}).otherwise(tools.F.constVoid);
|
|
705
680
|
},
|
|
706
681
|
"CallExpression:exit"(node) {
|
|
707
|
-
tools.MutList.pop(callStack);
|
|
708
682
|
if (tools.MutRef.get(useLayoutEffectCallRef) === node) {
|
|
709
|
-
onUseLayoutEffectCallExit();
|
|
710
683
|
tools.MutRef.set(useLayoutEffectCallRef, null);
|
|
711
684
|
}
|
|
712
685
|
},
|
|
686
|
+
Identifier(node) {
|
|
687
|
+
const isInUseLayoutEffectCall = tools.MutRef.get(useLayoutEffectCallRef) !== null;
|
|
688
|
+
const parentFn = tools.MutList.tail(functionStack)?.[0];
|
|
689
|
+
const useLayoutEffectCall = tools.MutRef.get(useLayoutEffectCallRef);
|
|
690
|
+
const isEffectFunction2 = useLayoutEffectCall && tools.O.isSome(ast.traverseUp(useLayoutEffectCall, (n2) => n2 === parentFn));
|
|
691
|
+
if (isFromUseStateCall(node) && isInUseLayoutEffectCall && isEffectFunction2) {
|
|
692
|
+
context.report({
|
|
693
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
694
|
+
node
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
},
|
|
713
698
|
"Program:exit"() {
|
|
714
|
-
for (const call of indirectFunctionCalls) {
|
|
699
|
+
for (const call of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
|
|
715
700
|
if (!("name" in call.callee)) continue;
|
|
716
701
|
const { name: name2 } = call.callee;
|
|
717
702
|
const setStateCalls = tools.F.pipe(
|
|
@@ -719,6 +704,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
719
704
|
tools.O.flatMap(_var.getVariableInit(0)),
|
|
720
705
|
tools.O.filter(ast.isFunction),
|
|
721
706
|
tools.O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
|
|
707
|
+
tools.O.map(tools.Chunk.toReadonlyArray),
|
|
722
708
|
tools.O.getOrElse(() => [])
|
|
723
709
|
);
|
|
724
710
|
for (const setStateCall of setStateCalls) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall, isUseLayoutEffectCall } from '@eslint-react/core';
|
|
2
2
|
import { createRuleForPlugin, getESLintReactSettings } from '@eslint-react/shared';
|
|
3
3
|
import { isFunction, NodeType, is, traverseUp, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
|
|
4
|
-
import { F, O, MutRef, MutList } from '@eslint-react/tools';
|
|
4
|
+
import { F, O, MutRef, MutList, Chunk } from '@eslint-react/tools';
|
|
5
5
|
import { findVariable, getVariableInit } from '@eslint-react/var';
|
|
6
6
|
import { getStaticValue } from '@typescript-eslint/utils/ast-utils';
|
|
7
7
|
|
|
8
8
|
// package.json
|
|
9
9
|
var name = "eslint-plugin-react-hooks-extra";
|
|
10
|
-
var version = "1.5.28-beta.
|
|
10
|
+
var version = "1.5.28-beta.4";
|
|
11
11
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
12
12
|
|
|
13
13
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -465,29 +465,29 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
465
465
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
|
|
466
466
|
}
|
|
467
467
|
const useEffectCallRef = MutRef.make(null);
|
|
468
|
-
const callStack = MutList.make();
|
|
469
468
|
const functionStack = MutList.make();
|
|
470
469
|
const effectFunctionRef = MutRef.make(null);
|
|
471
470
|
const cleanUpFunctionRef = MutRef.make(null);
|
|
472
|
-
const indirectFunctionCalls =
|
|
471
|
+
const indirectFunctionCalls = MutRef.make(Chunk.empty());
|
|
473
472
|
const indirectSetStateCalls = /* @__PURE__ */ new Map();
|
|
474
|
-
const onUseEffectCallEnter = (node) => void MutRef.set(useEffectCallRef, node);
|
|
475
|
-
const onUseEffectCallExit = () => void MutRef.set(useEffectCallRef, null);
|
|
476
473
|
return {
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
474
|
+
":function"(node) {
|
|
475
|
+
const functionKind = getFunctionKind(node);
|
|
476
|
+
MutList.append(functionStack, [node, functionKind]);
|
|
477
|
+
$(functionKind).with("effect", () => {
|
|
478
|
+
MutRef.set(effectFunctionRef, node);
|
|
479
|
+
}).with("cleanup", () => {
|
|
480
|
+
MutRef.set(cleanUpFunctionRef, node);
|
|
481
|
+
}).otherwise(F.constVoid);
|
|
482
|
+
},
|
|
483
|
+
":function:exit"(node) {
|
|
484
|
+
const effectFn = MutRef.get(effectFunctionRef);
|
|
485
|
+
if (effectFn === node) {
|
|
486
|
+
MutRef.set(effectFunctionRef, null);
|
|
487
487
|
}
|
|
488
|
+
MutList.pop(functionStack);
|
|
488
489
|
},
|
|
489
490
|
CallExpression(node) {
|
|
490
|
-
MutList.append(callStack, node);
|
|
491
491
|
const effectFn = MutRef.get(effectFunctionRef);
|
|
492
492
|
const [parentFn, parentFnKind] = MutList.tail(functionStack) ?? [];
|
|
493
493
|
if (parentFn?.async) return;
|
|
@@ -495,47 +495,39 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
495
495
|
$(callKind).with("setState", () => {
|
|
496
496
|
if (!parentFn) return;
|
|
497
497
|
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
498
|
-
|
|
498
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? Chunk.empty();
|
|
499
|
+
indirectSetStateCalls.set(parentFn, Chunk.append(calls, node));
|
|
499
500
|
return;
|
|
500
501
|
}
|
|
501
502
|
context.report({
|
|
502
|
-
|
|
503
|
-
|
|
503
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
504
|
+
node
|
|
504
505
|
});
|
|
505
506
|
}).with("useEffect", () => {
|
|
506
|
-
|
|
507
|
-
}).with("useState", () => {
|
|
508
|
-
}).with("then", () => {
|
|
507
|
+
MutRef.set(useEffectCallRef, node);
|
|
509
508
|
}).with("other", () => {
|
|
510
|
-
indirectFunctionCalls.
|
|
511
|
-
}).
|
|
512
|
-
},
|
|
513
|
-
":function"(node) {
|
|
514
|
-
const functionKind = getFunctionKind(node);
|
|
515
|
-
MutList.append(functionStack, [node, functionKind]);
|
|
516
|
-
$(functionKind).with("effect", () => {
|
|
517
|
-
MutRef.set(effectFunctionRef, node);
|
|
518
|
-
}).with("cleanup", () => {
|
|
519
|
-
MutRef.set(cleanUpFunctionRef, node);
|
|
520
|
-
}).otherwise(() => {
|
|
521
|
-
});
|
|
522
|
-
},
|
|
523
|
-
":function:exit"(node) {
|
|
524
|
-
const effectFn = MutRef.get(effectFunctionRef);
|
|
525
|
-
if (effectFn === node) {
|
|
526
|
-
MutRef.set(effectFunctionRef, null);
|
|
527
|
-
}
|
|
528
|
-
MutList.pop(functionStack);
|
|
509
|
+
MutRef.update(indirectFunctionCalls, Chunk.append(node));
|
|
510
|
+
}).otherwise(F.constVoid);
|
|
529
511
|
},
|
|
530
512
|
"CallExpression:exit"(node) {
|
|
531
|
-
MutList.pop(callStack);
|
|
532
513
|
if (MutRef.get(useEffectCallRef) === node) {
|
|
533
|
-
onUseEffectCallExit();
|
|
534
514
|
MutRef.set(useEffectCallRef, null);
|
|
535
515
|
}
|
|
536
516
|
},
|
|
517
|
+
Identifier(node) {
|
|
518
|
+
const isInUseEffectCall = MutRef.get(useEffectCallRef) !== null;
|
|
519
|
+
const parentFn = MutList.tail(functionStack)?.[0];
|
|
520
|
+
const useEffectCall = MutRef.get(useEffectCallRef);
|
|
521
|
+
const isEffectFunction2 = useEffectCall && O.isSome(traverseUp(useEffectCall, (n2) => n2 === parentFn));
|
|
522
|
+
if (isFromUseStateCall(node) && isInUseEffectCall && isEffectFunction2) {
|
|
523
|
+
context.report({
|
|
524
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
525
|
+
node
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
},
|
|
537
529
|
"Program:exit"() {
|
|
538
|
-
for (const call of indirectFunctionCalls) {
|
|
530
|
+
for (const call of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
|
|
539
531
|
if (!("name" in call.callee)) continue;
|
|
540
532
|
const { name: name2 } = call.callee;
|
|
541
533
|
const setStateCalls = F.pipe(
|
|
@@ -543,6 +535,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
543
535
|
O.flatMap(getVariableInit(0)),
|
|
544
536
|
O.filter(isFunction),
|
|
545
537
|
O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
|
|
538
|
+
O.map(Chunk.toReadonlyArray),
|
|
546
539
|
O.getOrElse(() => [])
|
|
547
540
|
);
|
|
548
541
|
for (const setStateCall of setStateCalls) {
|
|
@@ -639,29 +632,29 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
639
632
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
|
|
640
633
|
}
|
|
641
634
|
const useLayoutEffectCallRef = MutRef.make(null);
|
|
642
|
-
const callStack = MutList.make();
|
|
643
635
|
const functionStack = MutList.make();
|
|
644
636
|
const effectFunctionRef = MutRef.make(null);
|
|
645
637
|
const cleanUpFunctionRef = MutRef.make(null);
|
|
646
|
-
const indirectFunctionCalls =
|
|
638
|
+
const indirectFunctionCalls = MutRef.make(Chunk.empty());
|
|
647
639
|
const indirectSetStateCalls = /* @__PURE__ */ new Map();
|
|
648
|
-
const onUseLayoutEffectCallEnter = (node) => void MutRef.set(useLayoutEffectCallRef, node);
|
|
649
|
-
const onUseLayoutEffectCallExit = () => void MutRef.set(useLayoutEffectCallRef, null);
|
|
650
640
|
return {
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
641
|
+
":function"(node) {
|
|
642
|
+
const functionKind = getFunctionKind(node);
|
|
643
|
+
MutList.append(functionStack, [node, functionKind]);
|
|
644
|
+
$(functionKind).with("effect", () => {
|
|
645
|
+
MutRef.set(effectFunctionRef, node);
|
|
646
|
+
}).with("cleanup", () => {
|
|
647
|
+
MutRef.set(cleanUpFunctionRef, node);
|
|
648
|
+
}).otherwise(F.constVoid);
|
|
649
|
+
},
|
|
650
|
+
":function:exit"(node) {
|
|
651
|
+
const effectFn = MutRef.get(effectFunctionRef);
|
|
652
|
+
if (effectFn === node) {
|
|
653
|
+
MutRef.set(effectFunctionRef, null);
|
|
661
654
|
}
|
|
655
|
+
MutList.pop(functionStack);
|
|
662
656
|
},
|
|
663
657
|
CallExpression(node) {
|
|
664
|
-
MutList.append(callStack, node);
|
|
665
658
|
const effectFn = MutRef.get(effectFunctionRef);
|
|
666
659
|
const [parentFn, parentFnKind] = MutList.tail(functionStack) ?? [];
|
|
667
660
|
if (parentFn?.async) return;
|
|
@@ -669,47 +662,39 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
669
662
|
$(callKind).with("setState", () => {
|
|
670
663
|
if (!parentFn) return;
|
|
671
664
|
if (parentFn !== effectFn && parentFnKind !== "immediate") {
|
|
672
|
-
|
|
665
|
+
const calls = indirectSetStateCalls.get(parentFn) ?? Chunk.empty();
|
|
666
|
+
indirectSetStateCalls.set(parentFn, Chunk.append(calls, node));
|
|
673
667
|
return;
|
|
674
668
|
}
|
|
675
669
|
context.report({
|
|
676
|
-
|
|
677
|
-
|
|
670
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
671
|
+
node
|
|
678
672
|
});
|
|
679
673
|
}).with("useLayoutEffect", () => {
|
|
680
|
-
|
|
681
|
-
}).with("useState", () => {
|
|
682
|
-
}).with("then", () => {
|
|
674
|
+
MutRef.set(useLayoutEffectCallRef, node);
|
|
683
675
|
}).with("other", () => {
|
|
684
|
-
indirectFunctionCalls.
|
|
685
|
-
}).
|
|
686
|
-
},
|
|
687
|
-
":function"(node) {
|
|
688
|
-
const functionKind = getFunctionKind(node);
|
|
689
|
-
MutList.append(functionStack, [node, functionKind]);
|
|
690
|
-
$(functionKind).with("effect", () => {
|
|
691
|
-
MutRef.set(effectFunctionRef, node);
|
|
692
|
-
}).with("cleanup", () => {
|
|
693
|
-
MutRef.set(cleanUpFunctionRef, node);
|
|
694
|
-
}).otherwise(() => {
|
|
695
|
-
});
|
|
696
|
-
},
|
|
697
|
-
":function:exit"(node) {
|
|
698
|
-
const effectFn = MutRef.get(effectFunctionRef);
|
|
699
|
-
if (effectFn === node) {
|
|
700
|
-
MutRef.set(effectFunctionRef, null);
|
|
701
|
-
}
|
|
702
|
-
MutList.pop(functionStack);
|
|
676
|
+
MutRef.update(indirectFunctionCalls, Chunk.append(node));
|
|
677
|
+
}).otherwise(F.constVoid);
|
|
703
678
|
},
|
|
704
679
|
"CallExpression:exit"(node) {
|
|
705
|
-
MutList.pop(callStack);
|
|
706
680
|
if (MutRef.get(useLayoutEffectCallRef) === node) {
|
|
707
|
-
onUseLayoutEffectCallExit();
|
|
708
681
|
MutRef.set(useLayoutEffectCallRef, null);
|
|
709
682
|
}
|
|
710
683
|
},
|
|
684
|
+
Identifier(node) {
|
|
685
|
+
const isInUseLayoutEffectCall = MutRef.get(useLayoutEffectCallRef) !== null;
|
|
686
|
+
const parentFn = MutList.tail(functionStack)?.[0];
|
|
687
|
+
const useLayoutEffectCall = MutRef.get(useLayoutEffectCallRef);
|
|
688
|
+
const isEffectFunction2 = useLayoutEffectCall && O.isSome(traverseUp(useLayoutEffectCall, (n2) => n2 === parentFn));
|
|
689
|
+
if (isFromUseStateCall(node) && isInUseLayoutEffectCall && isEffectFunction2) {
|
|
690
|
+
context.report({
|
|
691
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
692
|
+
node
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
},
|
|
711
696
|
"Program:exit"() {
|
|
712
|
-
for (const call of indirectFunctionCalls) {
|
|
697
|
+
for (const call of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
|
|
713
698
|
if (!("name" in call.callee)) continue;
|
|
714
699
|
const { name: name2 } = call.callee;
|
|
715
700
|
const setStateCalls = F.pipe(
|
|
@@ -717,6 +702,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
717
702
|
O.flatMap(getVariableInit(0)),
|
|
718
703
|
O.filter(isFunction),
|
|
719
704
|
O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
|
|
705
|
+
O.map(Chunk.toReadonlyArray),
|
|
720
706
|
O.getOrElse(() => [])
|
|
721
707
|
);
|
|
722
708
|
for (const setStateCall of setStateCalls) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "1.5.28-beta.
|
|
3
|
+
"version": "1.5.28-beta.4",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for React Hooks related rules.",
|
|
5
5
|
"homepage": "https://github.com/rel1cx/eslint-react",
|
|
6
6
|
"bugs": {
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"@typescript-eslint/type-utils": "^7.16.1",
|
|
40
40
|
"@typescript-eslint/types": "^7.16.1",
|
|
41
41
|
"@typescript-eslint/utils": "^7.16.1",
|
|
42
|
-
"@eslint-react/ast": "1.5.28-beta.
|
|
43
|
-
"@eslint-react/core": "1.5.28-beta.
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/
|
|
47
|
-
"@eslint-react/var": "1.5.28-beta.
|
|
48
|
-
"@eslint-react/
|
|
42
|
+
"@eslint-react/ast": "1.5.28-beta.4",
|
|
43
|
+
"@eslint-react/core": "1.5.28-beta.4",
|
|
44
|
+
"@eslint-react/shared": "1.5.28-beta.4",
|
|
45
|
+
"@eslint-react/jsx": "1.5.28-beta.4",
|
|
46
|
+
"@eslint-react/tools": "1.5.28-beta.4",
|
|
47
|
+
"@eslint-react/var": "1.5.28-beta.4",
|
|
48
|
+
"@eslint-react/types": "1.5.28-beta.4"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"string-ts": "2.2.0",
|