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 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.2";
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
- Identifier(node) {
480
- const isInUseEffectCall = tools.MutRef.get(useEffectCallRef) !== null;
481
- const parentFn = tools.MutList.tail(functionStack)?.[0];
482
- const useEffectCall = tools.MutRef.get(useEffectCallRef);
483
- const isEffectFunction2 = useEffectCall && tools.O.isSome(ast.traverseUp(useEffectCall, (n2) => n2 === parentFn));
484
- if (isFromUseStateCall(node) && isInUseEffectCall && isEffectFunction2) {
485
- context.report({
486
- node,
487
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT"
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
- indirectSetStateCalls.set(parentFn, [...indirectSetStateCalls.get(parentFn) ?? [], node]);
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
- node,
505
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT"
505
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
506
+ node
506
507
  });
507
508
  }).with("useEffect", () => {
508
- onUseEffectCallEnter(node);
509
- }).with("useState", () => {
510
- }).with("then", () => {
509
+ tools.MutRef.set(useEffectCallRef, node);
511
510
  }).with("other", () => {
512
- indirectFunctionCalls.push(node);
513
- }).exhaustive();
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
- Identifier(node) {
654
- const isInUseLayoutEffectCall = tools.MutRef.get(useLayoutEffectCallRef) !== null;
655
- const parentFn = tools.MutList.tail(functionStack)?.[0];
656
- const useLayoutEffectCall = tools.MutRef.get(useLayoutEffectCallRef);
657
- const isEffectFunction2 = useLayoutEffectCall && tools.O.isSome(ast.traverseUp(useLayoutEffectCall, (n2) => n2 === parentFn));
658
- if (isFromUseStateCall(node) && isInUseLayoutEffectCall && isEffectFunction2) {
659
- context.report({
660
- node,
661
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT"
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
- indirectSetStateCalls.set(parentFn, [...indirectSetStateCalls.get(parentFn) ?? [], node]);
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
- node,
679
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT"
672
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
673
+ node
680
674
  });
681
675
  }).with("useLayoutEffect", () => {
682
- onUseLayoutEffectCallEnter(node);
683
- }).with("useState", () => {
684
- }).with("then", () => {
676
+ tools.MutRef.set(useLayoutEffectCallRef, node);
685
677
  }).with("other", () => {
686
- indirectFunctionCalls.push(node);
687
- }).exhaustive();
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.2";
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
- Identifier(node) {
478
- const isInUseEffectCall = MutRef.get(useEffectCallRef) !== null;
479
- const parentFn = MutList.tail(functionStack)?.[0];
480
- const useEffectCall = MutRef.get(useEffectCallRef);
481
- const isEffectFunction2 = useEffectCall && O.isSome(traverseUp(useEffectCall, (n2) => n2 === parentFn));
482
- if (isFromUseStateCall(node) && isInUseEffectCall && isEffectFunction2) {
483
- context.report({
484
- node,
485
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT"
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
- indirectSetStateCalls.set(parentFn, [...indirectSetStateCalls.get(parentFn) ?? [], node]);
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
- node,
503
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT"
503
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
504
+ node
504
505
  });
505
506
  }).with("useEffect", () => {
506
- onUseEffectCallEnter(node);
507
- }).with("useState", () => {
508
- }).with("then", () => {
507
+ MutRef.set(useEffectCallRef, node);
509
508
  }).with("other", () => {
510
- indirectFunctionCalls.push(node);
511
- }).exhaustive();
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
- Identifier(node) {
652
- const isInUseLayoutEffectCall = MutRef.get(useLayoutEffectCallRef) !== null;
653
- const parentFn = MutList.tail(functionStack)?.[0];
654
- const useLayoutEffectCall = MutRef.get(useLayoutEffectCallRef);
655
- const isEffectFunction2 = useLayoutEffectCall && O.isSome(traverseUp(useLayoutEffectCall, (n2) => n2 === parentFn));
656
- if (isFromUseStateCall(node) && isInUseLayoutEffectCall && isEffectFunction2) {
657
- context.report({
658
- node,
659
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT"
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
- indirectSetStateCalls.set(parentFn, [...indirectSetStateCalls.get(parentFn) ?? [], node]);
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
- node,
677
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT"
670
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
671
+ node
678
672
  });
679
673
  }).with("useLayoutEffect", () => {
680
- onUseLayoutEffectCallEnter(node);
681
- }).with("useState", () => {
682
- }).with("then", () => {
674
+ MutRef.set(useLayoutEffectCallRef, node);
683
675
  }).with("other", () => {
684
- indirectFunctionCalls.push(node);
685
- }).exhaustive();
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.2",
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.2",
43
- "@eslint-react/core": "1.5.28-beta.2",
44
- "@eslint-react/tools": "1.5.28-beta.2",
45
- "@eslint-react/shared": "1.5.28-beta.2",
46
- "@eslint-react/types": "1.5.28-beta.2",
47
- "@eslint-react/var": "1.5.28-beta.2",
48
- "@eslint-react/jsx": "1.5.28-beta.2"
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",