eslint-plugin-react-hooks-extra 1.8.0 → 1.8.1-next.1

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/README.md CHANGED
@@ -41,7 +41,7 @@ export default [
41
41
 
42
42
  ## Rules
43
43
 
44
- | Rule | Description | 💼 | 💭 | |
44
+ | Rule | Description | 💼 | 💭 | 🪧 |
45
45
  | :----------------------------------------- | :------------------------------------------------------------------------------ | :-: | :-: | :-: |
46
46
  | `ensure-custom-hooks-using-other-hooks` | Warns when custom Hooks that don't use other Hooks. | ✔️ | | |
47
47
  | `ensure-use-callback-has-non-empty-deps` | Warns when `useCallback` is called with empty dependencies array. | 🧐 | | |
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ var R__namespace = /*#__PURE__*/_interopNamespace(R);
30
30
 
31
31
  // package.json
32
32
  var name = "eslint-plugin-react-hooks-extra";
33
- var version = "1.8.0";
33
+ var version = "1.8.1-next.1";
34
34
  var createRule = shared.createRuleForPlugin("hooks-extra");
35
35
  function isFromUseStateCall(context, useStateAlias) {
36
36
  return (topLevelId) => {
@@ -507,6 +507,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
507
507
  const effectFunctionIdentifiers = [];
508
508
  const indirectFunctionCalls = [];
509
509
  const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
510
+ const indirectSetStateCallsAsEFs = /* @__PURE__ */ new Map();
511
+ const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
510
512
  const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
511
513
  const onEffectFunctionEnter = (node) => {
512
514
  tools.MutRef.set(effectFunctionRef, node);
@@ -533,9 +535,9 @@ var no_direct_set_state_in_use_effect_default = createRule({
533
535
  $(getCallKind(node, context, [useStateAlias, useEffectAlias])).with("setState", () => {
534
536
  if (!parentFn) return;
535
537
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
536
- const variableDeclaratorFromHookCall = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
537
- if (tools.O.isSome(variableDeclaratorFromHookCall)) {
538
- const vd = variableDeclaratorFromHookCall.value;
538
+ const maybeVd = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
539
+ if (tools.O.isSome(maybeVd)) {
540
+ const vd = maybeVd.value;
539
541
  const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
540
542
  indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
541
543
  return;
@@ -558,6 +560,38 @@ var no_direct_set_state_in_use_effect_default = createRule({
558
560
  indirectFunctionCalls.push(node);
559
561
  }).otherwise(tools.F.constVoid);
560
562
  },
563
+ Identifier(node) {
564
+ if (!node.parent) return;
565
+ if (!isFromUseStateCall(context, useStateAlias)(node)) return;
566
+ switch (node.parent.type) {
567
+ case ast.NodeType.CallExpression: {
568
+ const [firstArg] = node.parent.arguments;
569
+ if (node !== firstArg) break;
570
+ if (core.isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
571
+ const maybeVd = ast.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
572
+ if (tools.O.isNone(maybeVd)) break;
573
+ const vd = maybeVd.value;
574
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
575
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
576
+ }
577
+ if (core.isReactHookCallWithNameAlias("useEffect", context, useEffectAlias)(node.parent)) {
578
+ const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
579
+ indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
580
+ }
581
+ break;
582
+ }
583
+ case ast.NodeType.ArrowFunctionExpression: {
584
+ const parent = node.parent.parent;
585
+ if (parent?.type !== ast.NodeType.CallExpression) break;
586
+ if (!core.isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
587
+ const maybeVd = ast.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
588
+ if (tools.O.isNone(maybeVd)) break;
589
+ const vd = maybeVd.value;
590
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
591
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
592
+ }
593
+ }
594
+ },
561
595
  "Program:exit"() {
562
596
  const getSetStateCalls = (id, initialScope) => {
563
597
  const node = tools.O.flatMap(_var.findVariable(id, initialScope), _var.getVariableNode(0)).pipe(tools.O.getOrNull);
@@ -567,10 +601,19 @@ var no_direct_set_state_in_use_effect_default = createRule({
567
601
  case ast.NodeType.ArrowFunctionExpression:
568
602
  return indirectSetStateCalls.get(node) ?? [];
569
603
  case ast.NodeType.CallExpression:
570
- return indirectSetStateCallsInHooks.get(node) ?? [];
604
+ return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
571
605
  }
572
606
  return [];
573
607
  };
608
+ for (const [_2, calls] of indirectSetStateCallsAsEFs) {
609
+ for (const call of calls) {
610
+ context.report({
611
+ data: { name: call.name },
612
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
613
+ node: call
614
+ });
615
+ }
616
+ }
574
617
  for (const { callee } of indirectFunctionCalls) {
575
618
  if (!("name" in callee)) continue;
576
619
  const { name: name2 } = callee;
@@ -629,6 +672,8 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
629
672
  const effectFunctionIdentifiers = [];
630
673
  const indirectFunctionCalls = [];
631
674
  const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
675
+ const indirectSetStateCallsAsEFs = /* @__PURE__ */ new Map();
676
+ const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
632
677
  const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
633
678
  const onEffectFunctionEnter = (node) => {
634
679
  tools.MutRef.set(effectFunctionRef, node);
@@ -655,9 +700,9 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
655
700
  $(getCallKind2(node, context, [useStateAlias, useLayoutEffectAlias])).with("setState", () => {
656
701
  if (!parentFn) return;
657
702
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
658
- const variableDeclaratorFromHookCall = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
659
- if (tools.O.isSome(variableDeclaratorFromHookCall)) {
660
- const vd = variableDeclaratorFromHookCall.value;
703
+ const maybeVd = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
704
+ if (tools.O.isSome(maybeVd)) {
705
+ const vd = maybeVd.value;
661
706
  const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
662
707
  indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
663
708
  return;
@@ -680,6 +725,38 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
680
725
  indirectFunctionCalls.push(node);
681
726
  }).otherwise(tools.F.constVoid);
682
727
  },
728
+ Identifier(node) {
729
+ if (!node.parent) return;
730
+ if (!isFromUseStateCall(context, useStateAlias)(node)) return;
731
+ switch (node.parent.type) {
732
+ case ast.NodeType.CallExpression: {
733
+ const [firstArg] = node.parent.arguments;
734
+ if (node !== firstArg) break;
735
+ if (core.isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
736
+ const maybeVd = ast.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
737
+ if (tools.O.isNone(maybeVd)) break;
738
+ const vd = maybeVd.value;
739
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
740
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
741
+ }
742
+ if (core.isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias)(node.parent)) {
743
+ const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
744
+ indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
745
+ }
746
+ break;
747
+ }
748
+ case ast.NodeType.ArrowFunctionExpression: {
749
+ const parent = node.parent.parent;
750
+ if (parent?.type !== ast.NodeType.CallExpression) break;
751
+ if (!core.isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
752
+ const maybeVd = ast.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
753
+ if (tools.O.isNone(maybeVd)) break;
754
+ const vd = maybeVd.value;
755
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
756
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
757
+ }
758
+ }
759
+ },
683
760
  "Program:exit"() {
684
761
  const getSetStateCalls = (id, initialScope) => {
685
762
  const node = tools.O.flatMap(_var.findVariable(id, initialScope), _var.getVariableNode(0)).pipe(tools.O.getOrNull);
@@ -689,10 +766,19 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
689
766
  case ast.NodeType.ArrowFunctionExpression:
690
767
  return indirectSetStateCalls.get(node) ?? [];
691
768
  case ast.NodeType.CallExpression:
692
- return indirectSetStateCallsInHooks.get(node) ?? [];
769
+ return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
693
770
  }
694
771
  return [];
695
772
  };
773
+ for (const [_2, calls] of indirectSetStateCallsAsEFs) {
774
+ for (const call of calls) {
775
+ context.report({
776
+ data: { name: call.name },
777
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
778
+ node: call
779
+ });
780
+ }
781
+ }
696
782
  for (const { callee } of indirectFunctionCalls) {
697
783
  if (!("name" in callee)) continue;
698
784
  const { name: name2 } = callee;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isReactHookName, isReactHookCallWithNameAlias } from '@eslint-react/core';
1
+ import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isReactHookCallWithNameAlias, isUseStateCall, isReactHookName } from '@eslint-react/core';
2
2
  import { createRuleForPlugin, decodeSettings } from '@eslint-react/shared';
3
3
  import { isFunction, NodeType, is, traverseUpGuard, getNestedIdentifiers, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
4
4
  import { F, O, MutRef } from '@eslint-react/tools';
@@ -8,7 +8,7 @@ import * as R from 'remeda';
8
8
 
9
9
  // package.json
10
10
  var name = "eslint-plugin-react-hooks-extra";
11
- var version = "1.8.0";
11
+ var version = "1.8.1-next.1";
12
12
  var createRule = createRuleForPlugin("hooks-extra");
13
13
  function isFromUseStateCall(context, useStateAlias) {
14
14
  return (topLevelId) => {
@@ -485,6 +485,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
485
485
  const effectFunctionIdentifiers = [];
486
486
  const indirectFunctionCalls = [];
487
487
  const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
488
+ const indirectSetStateCallsAsEFs = /* @__PURE__ */ new Map();
489
+ const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
488
490
  const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
489
491
  const onEffectFunctionEnter = (node) => {
490
492
  MutRef.set(effectFunctionRef, node);
@@ -511,9 +513,9 @@ var no_direct_set_state_in_use_effect_default = createRule({
511
513
  $(getCallKind(node, context, [useStateAlias, useEffectAlias])).with("setState", () => {
512
514
  if (!parentFn) return;
513
515
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
514
- const variableDeclaratorFromHookCall = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
515
- if (O.isSome(variableDeclaratorFromHookCall)) {
516
- const vd = variableDeclaratorFromHookCall.value;
516
+ const maybeVd = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
517
+ if (O.isSome(maybeVd)) {
518
+ const vd = maybeVd.value;
517
519
  const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
518
520
  indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
519
521
  return;
@@ -536,6 +538,38 @@ var no_direct_set_state_in_use_effect_default = createRule({
536
538
  indirectFunctionCalls.push(node);
537
539
  }).otherwise(F.constVoid);
538
540
  },
541
+ Identifier(node) {
542
+ if (!node.parent) return;
543
+ if (!isFromUseStateCall(context, useStateAlias)(node)) return;
544
+ switch (node.parent.type) {
545
+ case NodeType.CallExpression: {
546
+ const [firstArg] = node.parent.arguments;
547
+ if (node !== firstArg) break;
548
+ if (isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
549
+ const maybeVd = traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
550
+ if (O.isNone(maybeVd)) break;
551
+ const vd = maybeVd.value;
552
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
553
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
554
+ }
555
+ if (isReactHookCallWithNameAlias("useEffect", context, useEffectAlias)(node.parent)) {
556
+ const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
557
+ indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
558
+ }
559
+ break;
560
+ }
561
+ case NodeType.ArrowFunctionExpression: {
562
+ const parent = node.parent.parent;
563
+ if (parent?.type !== NodeType.CallExpression) break;
564
+ if (!isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
565
+ const maybeVd = traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
566
+ if (O.isNone(maybeVd)) break;
567
+ const vd = maybeVd.value;
568
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
569
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
570
+ }
571
+ }
572
+ },
539
573
  "Program:exit"() {
540
574
  const getSetStateCalls = (id, initialScope) => {
541
575
  const node = O.flatMap(findVariable(id, initialScope), getVariableNode(0)).pipe(O.getOrNull);
@@ -545,10 +579,19 @@ var no_direct_set_state_in_use_effect_default = createRule({
545
579
  case NodeType.ArrowFunctionExpression:
546
580
  return indirectSetStateCalls.get(node) ?? [];
547
581
  case NodeType.CallExpression:
548
- return indirectSetStateCallsInHooks.get(node) ?? [];
582
+ return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
549
583
  }
550
584
  return [];
551
585
  };
586
+ for (const [_2, calls] of indirectSetStateCallsAsEFs) {
587
+ for (const call of calls) {
588
+ context.report({
589
+ data: { name: call.name },
590
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
591
+ node: call
592
+ });
593
+ }
594
+ }
552
595
  for (const { callee } of indirectFunctionCalls) {
553
596
  if (!("name" in callee)) continue;
554
597
  const { name: name2 } = callee;
@@ -607,6 +650,8 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
607
650
  const effectFunctionIdentifiers = [];
608
651
  const indirectFunctionCalls = [];
609
652
  const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
653
+ const indirectSetStateCallsAsEFs = /* @__PURE__ */ new Map();
654
+ const indirectSetStateCallsAsArgs = /* @__PURE__ */ new WeakMap();
610
655
  const indirectSetStateCallsInHooks = /* @__PURE__ */ new WeakMap();
611
656
  const onEffectFunctionEnter = (node) => {
612
657
  MutRef.set(effectFunctionRef, node);
@@ -633,9 +678,9 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
633
678
  $(getCallKind2(node, context, [useStateAlias, useLayoutEffectAlias])).with("setState", () => {
634
679
  if (!parentFn) return;
635
680
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
636
- const variableDeclaratorFromHookCall = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
637
- if (O.isSome(variableDeclaratorFromHookCall)) {
638
- const vd = variableDeclaratorFromHookCall.value;
681
+ const maybeVd = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
682
+ if (O.isSome(maybeVd)) {
683
+ const vd = maybeVd.value;
639
684
  const calls2 = indirectSetStateCallsInHooks.get(vd.init) ?? [];
640
685
  indirectSetStateCallsInHooks.set(vd.init, [...calls2, node]);
641
686
  return;
@@ -658,6 +703,38 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
658
703
  indirectFunctionCalls.push(node);
659
704
  }).otherwise(F.constVoid);
660
705
  },
706
+ Identifier(node) {
707
+ if (!node.parent) return;
708
+ if (!isFromUseStateCall(context, useStateAlias)(node)) return;
709
+ switch (node.parent.type) {
710
+ case NodeType.CallExpression: {
711
+ const [firstArg] = node.parent.arguments;
712
+ if (node !== firstArg) break;
713
+ if (isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
714
+ const maybeVd = traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
715
+ if (O.isNone(maybeVd)) break;
716
+ const vd = maybeVd.value;
717
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
718
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
719
+ }
720
+ if (isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias)(node.parent)) {
721
+ const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
722
+ indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
723
+ }
724
+ break;
725
+ }
726
+ case NodeType.ArrowFunctionExpression: {
727
+ const parent = node.parent.parent;
728
+ if (parent?.type !== NodeType.CallExpression) break;
729
+ if (!isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
730
+ const maybeVd = traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
731
+ if (O.isNone(maybeVd)) break;
732
+ const vd = maybeVd.value;
733
+ const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
734
+ indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
735
+ }
736
+ }
737
+ },
661
738
  "Program:exit"() {
662
739
  const getSetStateCalls = (id, initialScope) => {
663
740
  const node = O.flatMap(findVariable(id, initialScope), getVariableNode(0)).pipe(O.getOrNull);
@@ -667,10 +744,19 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
667
744
  case NodeType.ArrowFunctionExpression:
668
745
  return indirectSetStateCalls.get(node) ?? [];
669
746
  case NodeType.CallExpression:
670
- return indirectSetStateCallsInHooks.get(node) ?? [];
747
+ return indirectSetStateCallsInHooks.get(node) ?? indirectSetStateCallsAsArgs.get(node) ?? [];
671
748
  }
672
749
  return [];
673
750
  };
751
+ for (const [_2, calls] of indirectSetStateCallsAsEFs) {
752
+ for (const call of calls) {
753
+ context.report({
754
+ data: { name: call.name },
755
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
756
+ node: call
757
+ });
758
+ }
759
+ }
674
760
  for (const { callee } of indirectFunctionCalls) {
675
761
  if (!("name" in callee)) continue;
676
762
  const { name: name2 } = callee;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "1.8.0",
3
+ "version": "1.8.1-next.1",
4
4
  "description": "ESLint React's ESLint plugin for React Hooks related rules.",
5
5
  "keywords": [
6
6
  "eslint",
@@ -46,13 +46,13 @@
46
46
  "@typescript-eslint/types": "^8.0.0",
47
47
  "@typescript-eslint/utils": "^8.0.0",
48
48
  "remeda": "^2.6.0",
49
- "@eslint-react/ast": "1.8.0",
50
- "@eslint-react/jsx": "1.8.0",
51
- "@eslint-react/core": "1.8.0",
52
- "@eslint-react/shared": "1.8.0",
53
- "@eslint-react/tools": "1.8.0",
54
- "@eslint-react/types": "1.8.0",
55
- "@eslint-react/var": "1.8.0"
49
+ "@eslint-react/ast": "1.8.1-next.1",
50
+ "@eslint-react/shared": "1.8.1-next.1",
51
+ "@eslint-react/core": "1.8.1-next.1",
52
+ "@eslint-react/jsx": "1.8.1-next.1",
53
+ "@eslint-react/tools": "1.8.1-next.1",
54
+ "@eslint-react/types": "1.8.1-next.1",
55
+ "@eslint-react/var": "1.8.1-next.1"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/react": "^18.3.3",