eslint-plugin-react-hooks-extra 1.5.28 → 1.5.29-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 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";
12
+ var version = "1.5.29-beta.0";
13
13
  var createRule = shared.createRuleForPlugin("hooks-extra");
14
14
 
15
15
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -246,7 +246,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
246
246
  },
247
247
  name: RULE_NAME2,
248
248
  create(context) {
249
- const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useCallback ?? [];
249
+ const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useCallback ?? [];
250
250
  return {
251
251
  CallExpression(node) {
252
252
  if (!core.isReactHookCall(node)) return;
@@ -269,7 +269,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
269
269
  $(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
270
270
  return tools.F.pipe(
271
271
  _var.findVariable(n2.name, initialScope),
272
- tools.O.flatMap(_var.getVariableInit(0)),
272
+ tools.O.flatMap(_var.getVariableNode(0)),
273
273
  tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
274
274
  );
275
275
  }).otherwise(tools.O.none),
@@ -292,7 +292,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
292
292
  }).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
293
293
  return tools.F.pipe(
294
294
  _var.findVariable(n2.name, initialScope),
295
- tools.O.flatMap(_var.getVariableInit(0)),
295
+ tools.O.flatMap(_var.getVariableNode(0)),
296
296
  tools.O.filter(ast.isFunction)
297
297
  );
298
298
  }).otherwise(tools.O.none),
@@ -324,7 +324,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
324
324
  },
325
325
  name: RULE_NAME3,
326
326
  create(context) {
327
- const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useMemo ?? [];
327
+ const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useMemo ?? [];
328
328
  return {
329
329
  CallExpression(node) {
330
330
  if (!core.isReactHookCall(node)) return;
@@ -347,7 +347,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
347
347
  $(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
348
348
  return tools.F.pipe(
349
349
  _var.findVariable(n2.name, initialScope),
350
- tools.O.flatMap(_var.getVariableInit(0)),
350
+ tools.O.flatMap(_var.getVariableNode(0)),
351
351
  tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
352
352
  );
353
353
  }).otherwise(tools.O.none),
@@ -370,7 +370,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
370
370
  }).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
371
371
  return tools.F.pipe(
372
372
  _var.findVariable(n2.name, initialScope),
373
- tools.O.flatMap(_var.getVariableInit(0)),
373
+ tools.O.flatMap(_var.getVariableNode(0)),
374
374
  tools.O.filter(ast.isFunction)
375
375
  );
376
376
  }).otherwise(tools.O.none),
@@ -402,8 +402,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
402
402
  },
403
403
  name: RULE_NAME4,
404
404
  create(context) {
405
- const settings = shared.getESLintReactSettings(context.settings);
406
- const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
405
+ const settings = shared.parseESLintSettings(context.settings)["react-x"];
406
+ const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings?.additionalHooks ?? {};
407
407
  function isUseEffectCallWithAlias(node) {
408
408
  return core.isUseEffectCall(node, context) || useEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
409
409
  }
@@ -422,7 +422,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
422
422
  object: { type: ast.NodeType.Identifier }
423
423
  }, (n2) => tools.O.some(n2.object.name)).otherwise(tools.O.none),
424
424
  tools.O.flatMap(_var.findVariable(context.sourceCode.getScope(id))),
425
- tools.O.flatMap(_var.getVariableInit(0)),
425
+ tools.O.flatMap(_var.getVariableNode(0)),
426
426
  tools.O.filter(ast.is(ast.NodeType.CallExpression)),
427
427
  tools.O.exists(isUseStateCallWithAlias)
428
428
  );
@@ -466,24 +466,27 @@ var no_direct_set_state_in_use_effect_default = createRule({
466
466
  function getFunctionKind(node) {
467
467
  return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
468
468
  }
469
- const useEffectCallRef = tools.MutRef.make(null);
470
469
  const functionStack = tools.MutList.make();
471
470
  const effectFunctionRef = tools.MutRef.make(null);
472
- const cleanUpFunctionRef = tools.MutRef.make(null);
471
+ const effectFunctionIdentifiers = tools.MutRef.make(tools.Chunk.empty());
473
472
  const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
474
- const indirectSetStateCalls = /* @__PURE__ */ new Map();
473
+ const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
474
+ const onEffectFunctionEnter = (node) => {
475
+ tools.MutRef.set(effectFunctionRef, node);
476
+ };
477
+ const onEffectFunctionExit = (node) => {
478
+ tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
479
+ };
475
480
  return {
476
481
  ":function"(node) {
477
482
  const functionKind = getFunctionKind(node);
478
483
  tools.MutList.append(functionStack, [node, functionKind]);
479
484
  $(functionKind).with("effect", () => {
480
- tools.MutRef.set(effectFunctionRef, node);
481
- }).with("cleanup", () => {
482
- tools.MutRef.set(cleanUpFunctionRef, node);
485
+ onEffectFunctionEnter(node);
483
486
  }).otherwise(tools.F.constVoid);
484
487
  },
485
488
  ":function:exit"(node) {
486
- tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
489
+ onEffectFunctionExit(node);
487
490
  tools.MutList.pop(functionStack);
488
491
  },
489
492
  CallExpression(node) {
@@ -503,35 +506,43 @@ var no_direct_set_state_in_use_effect_default = createRule({
503
506
  node
504
507
  });
505
508
  }).with("useEffect", () => {
506
- tools.MutRef.set(useEffectCallRef, node);
509
+ if (node.arguments.every(ast.isFunction)) return;
510
+ const identifiers = ast.getNestedIdentifiers(node);
511
+ tools.MutRef.update(effectFunctionIdentifiers, tools.Chunk.appendAll(tools.Chunk.unsafeFromArray(identifiers)));
507
512
  }).with("other", () => {
508
513
  const isInEffectFunction = effectFn === parentFn;
509
514
  if (!isInEffectFunction) return;
510
515
  tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
511
516
  }).otherwise(tools.F.constVoid);
512
517
  },
513
- "CallExpression:exit"(node) {
514
- if (tools.MutRef.get(useEffectCallRef) === node) {
515
- tools.MutRef.set(useEffectCallRef, null);
516
- }
517
- },
518
518
  "Program:exit"() {
519
- for (const call of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
520
- if (!("name" in call.callee)) continue;
521
- const { name: name2 } = call.callee;
522
- const setStateCalls = tools.F.pipe(
523
- _var.findVariable(name2, context.sourceCode.getScope(call)),
524
- tools.O.flatMap(_var.getVariableInit(0)),
519
+ const getSetStateCalls = (id, initialScope) => {
520
+ return tools.F.pipe(
521
+ _var.findVariable(id, initialScope),
522
+ tools.O.flatMap(_var.getVariableNode(0)),
525
523
  tools.O.filter(ast.isFunction),
526
- tools.O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
524
+ tools.O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
527
525
  tools.O.map(tools.Chunk.toReadonlyArray),
528
526
  tools.O.getOrElse(() => [])
529
527
  );
528
+ };
529
+ for (const { callee } of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
530
+ if (!("name" in callee)) continue;
531
+ const { name: name2 } = callee;
532
+ const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
533
+ for (const setStateCall of setStateCalls) {
534
+ context.report({
535
+ data: { name: name2 },
536
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
537
+ node: setStateCall
538
+ });
539
+ }
540
+ }
541
+ for (const id of tools.Chunk.toReadonlyArray(tools.MutRef.get(effectFunctionIdentifiers))) {
542
+ const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
530
543
  for (const setStateCall of setStateCalls) {
531
544
  context.report({
532
- data: {
533
- name: name2
534
- },
545
+ data: { name: id.name },
535
546
  messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
536
547
  node: setStateCall
537
548
  });
@@ -556,7 +567,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
556
567
  },
557
568
  name: RULE_NAME5,
558
569
  create(context) {
559
- const settings = shared.getESLintReactSettings(context.settings);
570
+ const settings = shared.parseESLintSettings(context.settings)["react-x"] ?? {};
560
571
  const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
561
572
  function isUseLayoutEffectCallWithAlias(node) {
562
573
  return core.isUseLayoutEffectCall(node, context) || useLayoutEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
@@ -576,7 +587,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
576
587
  object: { type: ast.NodeType.Identifier }
577
588
  }, (n2) => tools.O.some(n2.object.name)).otherwise(tools.O.none),
578
589
  tools.O.flatMap(_var.findVariable(context.sourceCode.getScope(id))),
579
- tools.O.flatMap(_var.getVariableInit(0)),
590
+ tools.O.flatMap(_var.getVariableNode(0)),
580
591
  tools.O.filter(ast.is(ast.NodeType.CallExpression)),
581
592
  tools.O.exists(isUseStateCallWithAlias)
582
593
  );
@@ -620,24 +631,27 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
620
631
  function getFunctionKind(node) {
621
632
  return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
622
633
  }
623
- const useLayoutEffectCallRef = tools.MutRef.make(null);
624
634
  const functionStack = tools.MutList.make();
625
635
  const effectFunctionRef = tools.MutRef.make(null);
626
- const cleanUpFunctionRef = tools.MutRef.make(null);
636
+ const effectFunctionIdentifiers = tools.MutRef.make(tools.Chunk.empty());
627
637
  const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
628
- const indirectSetStateCalls = /* @__PURE__ */ new Map();
638
+ const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
639
+ const onEffectFunctionEnter = (node) => {
640
+ tools.MutRef.set(effectFunctionRef, node);
641
+ };
642
+ const onEffectFunctionExit = (node) => {
643
+ tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
644
+ };
629
645
  return {
630
646
  ":function"(node) {
631
647
  const functionKind = getFunctionKind(node);
632
648
  tools.MutList.append(functionStack, [node, functionKind]);
633
649
  $(functionKind).with("effect", () => {
634
- tools.MutRef.set(effectFunctionRef, node);
635
- }).with("cleanup", () => {
636
- tools.MutRef.set(cleanUpFunctionRef, node);
650
+ onEffectFunctionEnter(node);
637
651
  }).otherwise(tools.F.constVoid);
638
652
  },
639
653
  ":function:exit"(node) {
640
- tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
654
+ onEffectFunctionExit(node);
641
655
  tools.MutList.pop(functionStack);
642
656
  },
643
657
  CallExpression(node) {
@@ -657,35 +671,43 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
657
671
  node
658
672
  });
659
673
  }).with("useLayoutEffect", () => {
660
- tools.MutRef.set(useLayoutEffectCallRef, node);
674
+ if (node.arguments.every(ast.isFunction)) return;
675
+ const identifiers = ast.getNestedIdentifiers(node);
676
+ tools.MutRef.update(effectFunctionIdentifiers, tools.Chunk.appendAll(tools.Chunk.unsafeFromArray(identifiers)));
661
677
  }).with("other", () => {
662
678
  const isInEffectFunction = effectFn === parentFn;
663
679
  if (!isInEffectFunction) return;
664
680
  tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
665
681
  }).otherwise(tools.F.constVoid);
666
682
  },
667
- "CallExpression:exit"(node) {
668
- if (tools.MutRef.get(useLayoutEffectCallRef) === node) {
669
- tools.MutRef.set(useLayoutEffectCallRef, null);
670
- }
671
- },
672
683
  "Program:exit"() {
673
- for (const call of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
674
- if (!("name" in call.callee)) continue;
675
- const { name: name2 } = call.callee;
676
- const setStateCalls = tools.F.pipe(
677
- _var.findVariable(name2, context.sourceCode.getScope(call)),
678
- tools.O.flatMap(_var.getVariableInit(0)),
684
+ const getSetStateCalls = (id, initialScope) => {
685
+ return tools.F.pipe(
686
+ _var.findVariable(id, initialScope),
687
+ tools.O.flatMap(_var.getVariableNode(0)),
679
688
  tools.O.filter(ast.isFunction),
680
- tools.O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
689
+ tools.O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
681
690
  tools.O.map(tools.Chunk.toReadonlyArray),
682
691
  tools.O.getOrElse(() => [])
683
692
  );
693
+ };
694
+ for (const { callee } of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
695
+ if (!("name" in callee)) continue;
696
+ const { name: name2 } = callee;
697
+ const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
698
+ for (const setStateCall of setStateCalls) {
699
+ context.report({
700
+ data: { name: name2 },
701
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
702
+ node: setStateCall
703
+ });
704
+ }
705
+ }
706
+ for (const id of tools.Chunk.toReadonlyArray(tools.MutRef.get(effectFunctionIdentifiers))) {
707
+ const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
684
708
  for (const setStateCall of setStateCalls) {
685
709
  context.report({
686
- data: {
687
- name: name2
688
- },
710
+ data: { name: id.name },
689
711
  messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
690
712
  node: setStateCall
691
713
  });
@@ -711,7 +733,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
711
733
  },
712
734
  name: RULE_NAME6,
713
735
  create(context) {
714
- const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
736
+ const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useState ?? [];
715
737
  return {
716
738
  CallExpression(node) {
717
739
  if (!core.isReactHookCall(node)) return;
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
- import { createRuleForPlugin, getESLintReactSettings } from '@eslint-react/shared';
3
- import { isFunction, NodeType, is, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
4
- import { F, O, MutRef, MutList, Chunk } from '@eslint-react/tools';
5
- import { findVariable, getVariableInit } from '@eslint-react/var';
2
+ import { createRuleForPlugin, parseESLintSettings } from '@eslint-react/shared';
3
+ import { isFunction, NodeType, is, getNestedIdentifiers, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
4
+ import { F, O, MutList, MutRef, Chunk } from '@eslint-react/tools';
5
+ import { findVariable, getVariableNode } 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";
10
+ var version = "1.5.29-beta.0";
11
11
  var createRule = createRuleForPlugin("hooks-extra");
12
12
 
13
13
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -244,7 +244,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
244
244
  },
245
245
  name: RULE_NAME2,
246
246
  create(context) {
247
- const alias = getESLintReactSettings(context.settings).additionalHooks?.useCallback ?? [];
247
+ const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useCallback ?? [];
248
248
  return {
249
249
  CallExpression(node) {
250
250
  if (!isReactHookCall(node)) return;
@@ -267,7 +267,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
267
267
  $(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
268
268
  return F.pipe(
269
269
  findVariable(n2.name, initialScope),
270
- O.flatMap(getVariableInit(0)),
270
+ O.flatMap(getVariableNode(0)),
271
271
  O.filter(is(NodeType.ArrayExpression))
272
272
  );
273
273
  }).otherwise(O.none),
@@ -290,7 +290,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
290
290
  }).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
291
291
  return F.pipe(
292
292
  findVariable(n2.name, initialScope),
293
- O.flatMap(getVariableInit(0)),
293
+ O.flatMap(getVariableNode(0)),
294
294
  O.filter(isFunction)
295
295
  );
296
296
  }).otherwise(O.none),
@@ -322,7 +322,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
322
322
  },
323
323
  name: RULE_NAME3,
324
324
  create(context) {
325
- const alias = getESLintReactSettings(context.settings).additionalHooks?.useMemo ?? [];
325
+ const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useMemo ?? [];
326
326
  return {
327
327
  CallExpression(node) {
328
328
  if (!isReactHookCall(node)) return;
@@ -345,7 +345,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
345
345
  $(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
346
346
  return F.pipe(
347
347
  findVariable(n2.name, initialScope),
348
- O.flatMap(getVariableInit(0)),
348
+ O.flatMap(getVariableNode(0)),
349
349
  O.filter(is(NodeType.ArrayExpression))
350
350
  );
351
351
  }).otherwise(O.none),
@@ -368,7 +368,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
368
368
  }).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
369
369
  return F.pipe(
370
370
  findVariable(n2.name, initialScope),
371
- O.flatMap(getVariableInit(0)),
371
+ O.flatMap(getVariableNode(0)),
372
372
  O.filter(isFunction)
373
373
  );
374
374
  }).otherwise(O.none),
@@ -400,8 +400,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
400
400
  },
401
401
  name: RULE_NAME4,
402
402
  create(context) {
403
- const settings = getESLintReactSettings(context.settings);
404
- const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
403
+ const settings = parseESLintSettings(context.settings)["react-x"];
404
+ const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings?.additionalHooks ?? {};
405
405
  function isUseEffectCallWithAlias(node) {
406
406
  return isUseEffectCall(node, context) || useEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
407
407
  }
@@ -420,7 +420,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
420
420
  object: { type: NodeType.Identifier }
421
421
  }, (n2) => O.some(n2.object.name)).otherwise(O.none),
422
422
  O.flatMap(findVariable(context.sourceCode.getScope(id))),
423
- O.flatMap(getVariableInit(0)),
423
+ O.flatMap(getVariableNode(0)),
424
424
  O.filter(is(NodeType.CallExpression)),
425
425
  O.exists(isUseStateCallWithAlias)
426
426
  );
@@ -464,24 +464,27 @@ var no_direct_set_state_in_use_effect_default = createRule({
464
464
  function getFunctionKind(node) {
465
465
  return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
466
466
  }
467
- const useEffectCallRef = MutRef.make(null);
468
467
  const functionStack = MutList.make();
469
468
  const effectFunctionRef = MutRef.make(null);
470
- const cleanUpFunctionRef = MutRef.make(null);
469
+ const effectFunctionIdentifiers = MutRef.make(Chunk.empty());
471
470
  const indirectFunctionCalls = MutRef.make(Chunk.empty());
472
- const indirectSetStateCalls = /* @__PURE__ */ new Map();
471
+ const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
472
+ const onEffectFunctionEnter = (node) => {
473
+ MutRef.set(effectFunctionRef, node);
474
+ };
475
+ const onEffectFunctionExit = (node) => {
476
+ MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
477
+ };
473
478
  return {
474
479
  ":function"(node) {
475
480
  const functionKind = getFunctionKind(node);
476
481
  MutList.append(functionStack, [node, functionKind]);
477
482
  $(functionKind).with("effect", () => {
478
- MutRef.set(effectFunctionRef, node);
479
- }).with("cleanup", () => {
480
- MutRef.set(cleanUpFunctionRef, node);
483
+ onEffectFunctionEnter(node);
481
484
  }).otherwise(F.constVoid);
482
485
  },
483
486
  ":function:exit"(node) {
484
- MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
487
+ onEffectFunctionExit(node);
485
488
  MutList.pop(functionStack);
486
489
  },
487
490
  CallExpression(node) {
@@ -501,35 +504,43 @@ var no_direct_set_state_in_use_effect_default = createRule({
501
504
  node
502
505
  });
503
506
  }).with("useEffect", () => {
504
- MutRef.set(useEffectCallRef, node);
507
+ if (node.arguments.every(isFunction)) return;
508
+ const identifiers = getNestedIdentifiers(node);
509
+ MutRef.update(effectFunctionIdentifiers, Chunk.appendAll(Chunk.unsafeFromArray(identifiers)));
505
510
  }).with("other", () => {
506
511
  const isInEffectFunction = effectFn === parentFn;
507
512
  if (!isInEffectFunction) return;
508
513
  MutRef.update(indirectFunctionCalls, Chunk.append(node));
509
514
  }).otherwise(F.constVoid);
510
515
  },
511
- "CallExpression:exit"(node) {
512
- if (MutRef.get(useEffectCallRef) === node) {
513
- MutRef.set(useEffectCallRef, null);
514
- }
515
- },
516
516
  "Program:exit"() {
517
- for (const call of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
518
- if (!("name" in call.callee)) continue;
519
- const { name: name2 } = call.callee;
520
- const setStateCalls = F.pipe(
521
- findVariable(name2, context.sourceCode.getScope(call)),
522
- O.flatMap(getVariableInit(0)),
517
+ const getSetStateCalls = (id, initialScope) => {
518
+ return F.pipe(
519
+ findVariable(id, initialScope),
520
+ O.flatMap(getVariableNode(0)),
523
521
  O.filter(isFunction),
524
- O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
522
+ O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
525
523
  O.map(Chunk.toReadonlyArray),
526
524
  O.getOrElse(() => [])
527
525
  );
526
+ };
527
+ for (const { callee } of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
528
+ if (!("name" in callee)) continue;
529
+ const { name: name2 } = callee;
530
+ const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
531
+ for (const setStateCall of setStateCalls) {
532
+ context.report({
533
+ data: { name: name2 },
534
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
535
+ node: setStateCall
536
+ });
537
+ }
538
+ }
539
+ for (const id of Chunk.toReadonlyArray(MutRef.get(effectFunctionIdentifiers))) {
540
+ const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
528
541
  for (const setStateCall of setStateCalls) {
529
542
  context.report({
530
- data: {
531
- name: name2
532
- },
543
+ data: { name: id.name },
533
544
  messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
534
545
  node: setStateCall
535
546
  });
@@ -554,7 +565,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
554
565
  },
555
566
  name: RULE_NAME5,
556
567
  create(context) {
557
- const settings = getESLintReactSettings(context.settings);
568
+ const settings = parseESLintSettings(context.settings)["react-x"] ?? {};
558
569
  const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
559
570
  function isUseLayoutEffectCallWithAlias(node) {
560
571
  return isUseLayoutEffectCall(node, context) || useLayoutEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
@@ -574,7 +585,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
574
585
  object: { type: NodeType.Identifier }
575
586
  }, (n2) => O.some(n2.object.name)).otherwise(O.none),
576
587
  O.flatMap(findVariable(context.sourceCode.getScope(id))),
577
- O.flatMap(getVariableInit(0)),
588
+ O.flatMap(getVariableNode(0)),
578
589
  O.filter(is(NodeType.CallExpression)),
579
590
  O.exists(isUseStateCallWithAlias)
580
591
  );
@@ -618,24 +629,27 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
618
629
  function getFunctionKind(node) {
619
630
  return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
620
631
  }
621
- const useLayoutEffectCallRef = MutRef.make(null);
622
632
  const functionStack = MutList.make();
623
633
  const effectFunctionRef = MutRef.make(null);
624
- const cleanUpFunctionRef = MutRef.make(null);
634
+ const effectFunctionIdentifiers = MutRef.make(Chunk.empty());
625
635
  const indirectFunctionCalls = MutRef.make(Chunk.empty());
626
- const indirectSetStateCalls = /* @__PURE__ */ new Map();
636
+ const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
637
+ const onEffectFunctionEnter = (node) => {
638
+ MutRef.set(effectFunctionRef, node);
639
+ };
640
+ const onEffectFunctionExit = (node) => {
641
+ MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
642
+ };
627
643
  return {
628
644
  ":function"(node) {
629
645
  const functionKind = getFunctionKind(node);
630
646
  MutList.append(functionStack, [node, functionKind]);
631
647
  $(functionKind).with("effect", () => {
632
- MutRef.set(effectFunctionRef, node);
633
- }).with("cleanup", () => {
634
- MutRef.set(cleanUpFunctionRef, node);
648
+ onEffectFunctionEnter(node);
635
649
  }).otherwise(F.constVoid);
636
650
  },
637
651
  ":function:exit"(node) {
638
- MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
652
+ onEffectFunctionExit(node);
639
653
  MutList.pop(functionStack);
640
654
  },
641
655
  CallExpression(node) {
@@ -655,35 +669,43 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
655
669
  node
656
670
  });
657
671
  }).with("useLayoutEffect", () => {
658
- MutRef.set(useLayoutEffectCallRef, node);
672
+ if (node.arguments.every(isFunction)) return;
673
+ const identifiers = getNestedIdentifiers(node);
674
+ MutRef.update(effectFunctionIdentifiers, Chunk.appendAll(Chunk.unsafeFromArray(identifiers)));
659
675
  }).with("other", () => {
660
676
  const isInEffectFunction = effectFn === parentFn;
661
677
  if (!isInEffectFunction) return;
662
678
  MutRef.update(indirectFunctionCalls, Chunk.append(node));
663
679
  }).otherwise(F.constVoid);
664
680
  },
665
- "CallExpression:exit"(node) {
666
- if (MutRef.get(useLayoutEffectCallRef) === node) {
667
- MutRef.set(useLayoutEffectCallRef, null);
668
- }
669
- },
670
681
  "Program:exit"() {
671
- for (const call of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
672
- if (!("name" in call.callee)) continue;
673
- const { name: name2 } = call.callee;
674
- const setStateCalls = F.pipe(
675
- findVariable(name2, context.sourceCode.getScope(call)),
676
- O.flatMap(getVariableInit(0)),
682
+ const getSetStateCalls = (id, initialScope) => {
683
+ return F.pipe(
684
+ findVariable(id, initialScope),
685
+ O.flatMap(getVariableNode(0)),
677
686
  O.filter(isFunction),
678
- O.flatMapNullable((init) => indirectSetStateCalls.get(init)),
687
+ O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
679
688
  O.map(Chunk.toReadonlyArray),
680
689
  O.getOrElse(() => [])
681
690
  );
691
+ };
692
+ for (const { callee } of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
693
+ if (!("name" in callee)) continue;
694
+ const { name: name2 } = callee;
695
+ const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
696
+ for (const setStateCall of setStateCalls) {
697
+ context.report({
698
+ data: { name: name2 },
699
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
700
+ node: setStateCall
701
+ });
702
+ }
703
+ }
704
+ for (const id of Chunk.toReadonlyArray(MutRef.get(effectFunctionIdentifiers))) {
705
+ const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
682
706
  for (const setStateCall of setStateCalls) {
683
707
  context.report({
684
- data: {
685
- name: name2
686
- },
708
+ data: { name: id.name },
687
709
  messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
688
710
  node: setStateCall
689
711
  });
@@ -709,7 +731,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
709
731
  },
710
732
  name: RULE_NAME6,
711
733
  create(context) {
712
- const alias = getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
734
+ const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useState ?? [];
713
735
  return {
714
736
  CallExpression(node) {
715
737
  if (!isReactHookCall(node)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "1.5.28",
3
+ "version": "1.5.29-beta.0",
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,17 +39,17 @@
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/jsx": "1.5.28",
43
- "@eslint-react/core": "1.5.28",
44
- "@eslint-react/shared": "1.5.28",
45
- "@eslint-react/types": "1.5.28",
46
- "@eslint-react/ast": "1.5.28",
47
- "@eslint-react/var": "1.5.28",
48
- "@eslint-react/tools": "1.5.28"
42
+ "@eslint-react/ast": "1.5.29-beta.0",
43
+ "@eslint-react/core": "1.5.29-beta.0",
44
+ "@eslint-react/shared": "1.5.29-beta.0",
45
+ "@eslint-react/tools": "1.5.29-beta.0",
46
+ "@eslint-react/types": "1.5.29-beta.0",
47
+ "@eslint-react/var": "1.5.29-beta.0",
48
+ "@eslint-react/jsx": "1.5.29-beta.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "string-ts": "2.2.0",
52
- "tsup": "8.2.0"
52
+ "tsup": "8.2.1"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "eslint": "^8.57.0 || ^9.0.0",