eslint-plugin-react-hooks-extra 1.8.2 → 1.8.3-beta.2

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.d.mts CHANGED
@@ -5,12 +5,12 @@ declare const meta: {
5
5
  readonly version: string;
6
6
  };
7
7
  declare const rules: {
8
- readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
9
- readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
10
- readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
11
- readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_EFFECT", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
12
- readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
13
- readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
8
+ readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureCustomHooksUsingOtherHooks", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
9
+ readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureUseCallbackHasNonEmptyDeps", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
10
+ readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureUseMemoHasNonEmptyDeps", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
11
+ readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
12
+ readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
13
+ readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
14
14
  };
15
15
 
16
16
  export { meta, rules };
package/dist/index.d.ts CHANGED
@@ -5,12 +5,12 @@ declare const meta: {
5
5
  readonly version: string;
6
6
  };
7
7
  declare const rules: {
8
- readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
9
- readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
10
- readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
11
- readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_EFFECT", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
12
- readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
13
- readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
8
+ readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureCustomHooksUsingOtherHooks", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
9
+ readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureUseCallbackHasNonEmptyDeps", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
10
+ readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ensureUseMemoHasNonEmptyDeps", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
11
+ readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
12
+ readonly "no-direct-set-state-in-use-layout-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"noDirectSetStateInUseLayoutEffect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
13
+ readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"preferUseStateLazyInitialization", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
14
14
  };
15
15
 
16
16
  export { meta, rules };
package/dist/index.js CHANGED
@@ -30,21 +30,25 @@ 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.2";
33
+ var version = "1.8.3-beta.2";
34
34
  var createRule = shared.createRuleForPlugin("hooks-extra");
35
- function isFromUseStateCall(context, useStateAlias) {
36
- return (topLevelId) => {
37
- const useStateCall = tools.F.pipe(
38
- _var.findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
39
- tools.O.flatMap(_var.getVariableNode(0)),
40
- tools.O.filter(ast.is(ast.NodeType.CallExpression)),
41
- tools.O.filter(core.isReactHookCallWithNameAlias("useState", context, useStateAlias))
42
- );
43
- if (tools.O.isNone(useStateCall)) return false;
44
- const { parent } = useStateCall.value;
35
+ function isFromHookCall(name2, context, settings, predicate = tools.F.constTrue) {
36
+ const hookAlias = settings.additionalHooks?.[name2] ?? [];
37
+ return (topLevelId) => tools.F.pipe(
38
+ _var.findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
39
+ tools.O.flatMap(_var.getVariableNode(0)),
40
+ tools.O.filter(ast.is(ast.NodeType.CallExpression)),
41
+ tools.O.filter(core.isReactHookCallWithNameAlias(name2, context, hookAlias)),
42
+ tools.O.exists((call) => predicate(topLevelId, call))
43
+ );
44
+ }
45
+ function isFromUseStateCall(context, settings) {
46
+ const predicate = (topLevelId, call) => {
47
+ const { parent } = call;
45
48
  if (!("id" in parent && parent.id?.type === ast.NodeType.ArrayPattern)) return true;
46
49
  return parent.id.elements.findIndex((e2) => e2?.type === ast.NodeType.Identifier && e2.name === topLevelId.name) === 1;
47
50
  };
51
+ return isFromHookCall("useState", context, settings, predicate);
48
52
  }
49
53
 
50
54
  // ../../../node_modules/.pnpm/ts-pattern@5.2.0/node_modules/ts-pattern/dist/index.js
@@ -230,18 +234,19 @@ var z = class _z {
230
234
  }
231
235
  };
232
236
 
233
- // src/utils/is-set-state-call.ts
234
- function isSetStateCall(context, useStateAlias) {
237
+ // src/utils/is-set-function-call.ts
238
+ function isSetFunctionCall(context, settings) {
239
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
235
240
  return (node) => {
236
241
  switch (node.callee.type) {
237
242
  case ast.NodeType.Identifier: {
238
- return isFromUseStateCall(context, useStateAlias)(node.callee);
243
+ return isIdFromUseStateCall(node.callee);
239
244
  }
240
245
  case ast.NodeType.MemberExpression: {
241
246
  if (!("name" in node.callee.object)) return false;
242
247
  const initialScope = context.sourceCode.getScope(node);
243
248
  const property = astUtils.getStaticValue(node.callee.property, initialScope);
244
- if (property?.value === 1) return isFromUseStateCall(context, useStateAlias)(node.callee.object);
249
+ if (property?.value === 1) return isIdFromUseStateCall(node.callee.object);
245
250
  return false;
246
251
  }
247
252
  case ast.NodeType.CallExpression: {
@@ -259,7 +264,7 @@ function isSetStateCall(context, useStateAlias) {
259
264
  if (!isAt || !index) return false;
260
265
  const initialScope = context.sourceCode.getScope(node);
261
266
  const value = astUtils.getStaticValue(index, initialScope);
262
- if (value?.value === 1) return isFromUseStateCall(context, useStateAlias)(callee.object);
267
+ if (value?.value === 1) return isIdFromUseStateCall(callee.object);
263
268
  return false;
264
269
  }
265
270
  default: {
@@ -294,7 +299,7 @@ var ensure_custom_hooks_using_other_hooks_default = createRule({
294
299
  description: "enforce custom hooks using other hooks"
295
300
  },
296
301
  messages: {
297
- ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS: "A custom hook '{{name}}' should use other hooks."
302
+ ensureCustomHooksUsingOtherHooks: "A custom hook '{{name}}' should use other hooks."
298
303
  },
299
304
  schema: []
300
305
  },
@@ -311,7 +316,7 @@ var ensure_custom_hooks_using_other_hooks_default = createRule({
311
316
  data: {
312
317
  name: name2.value
313
318
  },
314
- messageId: "ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS",
319
+ messageId: "ensureCustomHooksUsingOtherHooks",
315
320
  node: node2
316
321
  });
317
322
  }
@@ -328,7 +333,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
328
333
  description: "enforce 'useCallback' has non-empty dependencies array"
329
334
  },
330
335
  messages: {
331
- ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An 'useCallback' should have a non-empty dependencies array."
336
+ ensureUseCallbackHasNonEmptyDeps: "An 'useCallback' should have a non-empty dependencies array."
332
337
  },
333
338
  schema: []
334
339
  },
@@ -348,7 +353,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
348
353
  const [cb, deps] = node.arguments;
349
354
  if (!deps) {
350
355
  context.report({
351
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
356
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
352
357
  node
353
358
  });
354
359
  return;
@@ -366,7 +371,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
366
371
  if (!hasEmptyDeps) return;
367
372
  if (!cb) {
368
373
  context.report({
369
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
374
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
370
375
  node
371
376
  });
372
377
  return;
@@ -390,7 +395,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
390
395
  );
391
396
  if (isReferencedToComponentScope) return;
392
397
  context.report({
393
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
398
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
394
399
  node
395
400
  });
396
401
  }
@@ -406,7 +411,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
406
411
  description: "enforce 'useMemo' has non-empty dependencies array"
407
412
  },
408
413
  messages: {
409
- ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An 'useMemo' should have a non-empty dependencies array."
414
+ ensureUseMemoHasNonEmptyDeps: "An 'useMemo' should have a non-empty dependencies array."
410
415
  },
411
416
  schema: []
412
417
  },
@@ -426,7 +431,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
426
431
  const [cb, deps] = node.arguments;
427
432
  if (!deps) {
428
433
  context.report({
429
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
434
+ messageId: "ensureUseMemoHasNonEmptyDeps",
430
435
  node
431
436
  });
432
437
  return;
@@ -444,7 +449,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
444
449
  if (!hasEmptyDeps) return;
445
450
  if (!cb) {
446
451
  context.report({
447
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
452
+ messageId: "ensureUseMemoHasNonEmptyDeps",
448
453
  node
449
454
  });
450
455
  return;
@@ -468,7 +473,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
468
473
  );
469
474
  if (isReferencedToComponentScope) return;
470
475
  context.report({
471
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
476
+ messageId: "ensureUseMemoHasNonEmptyDeps",
472
477
  node
473
478
  });
474
479
  }
@@ -477,16 +482,6 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
477
482
  defaultOptions: []
478
483
  });
479
484
  var RULE_NAME4 = "no-direct-set-state-in-use-effect";
480
- function isEffectFunction(context, useSomeEffectAlias) {
481
- return (node) => node.parent?.type === ast.NodeType.CallExpression && node.parent.callee !== node && core.isReactHookCallWithNameAlias("useEffect", context, useSomeEffectAlias)(node.parent);
482
- }
483
- function getCallKind(node, context, alias) {
484
- const [useStateAlias, useEffectAlias] = alias;
485
- return $(node).when(core.isReactHookCallWithNameAlias("useState", context, useStateAlias), () => "useState").when(core.isReactHookCallWithNameAlias("useEffect", context, useEffectAlias), () => "useEffect").when(isSetStateCall(context, useStateAlias), () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
486
- }
487
- function getFunctionKind(node, context, useEffectAlias) {
488
- return $(node).when(isEffectFunction(context, useEffectAlias), () => "effect").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
489
- }
490
485
  var no_direct_set_state_in_use_effect_default = createRule({
491
486
  meta: {
492
487
  type: "problem",
@@ -494,14 +489,20 @@ var no_direct_set_state_in_use_effect_default = createRule({
494
489
  description: "disallow direct calls to the 'set' function of 'useState' in 'useEffect'"
495
490
  },
496
491
  messages: {
497
- NO_DIRECT_SET_STATE_IN_USE_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
492
+ noDirectSetStateInUseEffect: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
498
493
  },
499
494
  schema: []
500
495
  },
501
496
  name: RULE_NAME4,
502
497
  create(context) {
503
498
  const settings = shared.decodeSettings(context.settings);
504
- const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
499
+ const additionalHooks = settings.additionalHooks ?? {};
500
+ const isUseEffectCall = core.isReactHookCallWithNameAlias("useEffect", context, additionalHooks.useEffect ?? []);
501
+ const isUseStateCall2 = core.isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
502
+ const isUseMemoCall2 = core.isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
503
+ const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
504
+ const isSetStateCall = isSetFunctionCall(context, settings);
505
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
505
506
  const functionStack = [];
506
507
  const effectFunctionRef = tools.MutRef.make(null);
507
508
  const effectFunctionIdentifiers = [];
@@ -516,9 +517,18 @@ var no_direct_set_state_in_use_effect_default = createRule({
516
517
  const onEffectFunctionExit = (node) => {
517
518
  tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
518
519
  };
520
+ function isEffectFunction(node) {
521
+ return node.parent?.type === ast.NodeType.CallExpression && node.parent.callee !== node && isUseEffectCall(node.parent);
522
+ }
523
+ function getCallKind(node) {
524
+ return $(node).when(isUseStateCall2, () => "useState").when(isUseEffectCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
525
+ }
526
+ function getFunctionKind(node) {
527
+ return $(node).when(isEffectFunction, () => "effect").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
528
+ }
519
529
  return {
520
530
  ":function"(node) {
521
- const functionKind = getFunctionKind(node, context, useEffectAlias);
531
+ const functionKind = getFunctionKind(node);
522
532
  functionStack.push([node, functionKind]);
523
533
  $(functionKind).with("effect", () => {
524
534
  onEffectFunctionEnter(node);
@@ -532,7 +542,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
532
542
  const effectFn = tools.MutRef.get(effectFunctionRef);
533
543
  const [parentFn, parentFnKind] = R__namespace.last(functionStack) ?? [];
534
544
  if (parentFn?.async) return;
535
- $(getCallKind(node, context, [useStateAlias, useEffectAlias])).with("setState", () => {
545
+ $(getCallKind(node)).with("setState", () => {
536
546
  if (!parentFn) return;
537
547
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
538
548
  const maybeVd = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
@@ -547,7 +557,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
547
557
  return;
548
558
  }
549
559
  context.report({
550
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
560
+ messageId: "noDirectSetStateInUseEffect",
551
561
  node
552
562
  });
553
563
  }).with("useEffect", () => {
@@ -563,19 +573,20 @@ var no_direct_set_state_in_use_effect_default = createRule({
563
573
  },
564
574
  Identifier(node) {
565
575
  if (!node.parent) return;
566
- if (!isFromUseStateCall(context, useStateAlias)(node)) return;
576
+ if (node.parent.type === ast.NodeType.CallExpression && node.parent.callee === node) return;
577
+ if (!isIdFromUseStateCall(node)) return;
567
578
  switch (node.parent.type) {
568
579
  case ast.NodeType.CallExpression: {
569
580
  const [firstArg] = node.parent.arguments;
570
581
  if (node !== firstArg) break;
571
- if (core.isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
582
+ if (isUseCallbackCall2(node.parent)) {
572
583
  const maybeVd = ast.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
573
584
  if (tools.O.isNone(maybeVd)) break;
574
585
  const vd = maybeVd.value;
575
586
  const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
576
587
  indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
577
588
  }
578
- if (core.isReactHookCallWithNameAlias("useEffect", context, useEffectAlias)(node.parent)) {
589
+ if (isUseEffectCall(node.parent)) {
579
590
  const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
580
591
  indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
581
592
  }
@@ -584,7 +595,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
584
595
  case ast.NodeType.ArrowFunctionExpression: {
585
596
  const parent = node.parent.parent;
586
597
  if (parent?.type !== ast.NodeType.CallExpression) break;
587
- if (!core.isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
598
+ if (!isUseMemoCall2(parent)) break;
588
599
  const maybeVd = ast.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
589
600
  if (tools.O.isNone(maybeVd)) break;
590
601
  const vd = maybeVd.value;
@@ -610,7 +621,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
610
621
  for (const call of calls) {
611
622
  context.report({
612
623
  data: { name: call.name },
613
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
624
+ messageId: "noDirectSetStateInUseEffect",
614
625
  node: call
615
626
  });
616
627
  }
@@ -622,7 +633,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
622
633
  for (const setStateCall of setStateCalls) {
623
634
  context.report({
624
635
  data: { name: name2 },
625
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
636
+ messageId: "noDirectSetStateInUseEffect",
626
637
  node: setStateCall
627
638
  });
628
639
  }
@@ -632,7 +643,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
632
643
  for (const setStateCall of setStateCalls) {
633
644
  context.report({
634
645
  data: { name: id.name },
635
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
646
+ messageId: "noDirectSetStateInUseEffect",
636
647
  node: setStateCall
637
648
  });
638
649
  }
@@ -643,16 +654,6 @@ var no_direct_set_state_in_use_effect_default = createRule({
643
654
  defaultOptions: []
644
655
  });
645
656
  var RULE_NAME5 = "no-direct-set-state-in-use-layout-effect";
646
- function isEffectFunction2(context, useSomeEffectAlias) {
647
- return (node) => node.parent?.type === ast.NodeType.CallExpression && node.parent.callee !== node && core.isReactHookCallWithNameAlias("useLayoutEffect", context, useSomeEffectAlias)(node.parent);
648
- }
649
- function getCallKind2(node, context, alias) {
650
- const [useStateAlias, useLayoutEffectAlias] = alias;
651
- return $(node).when(core.isReactHookCallWithNameAlias("useState", context, useStateAlias), () => "useState").when(core.isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias), () => "useLayoutEffect").when(isSetStateCall(context, useStateAlias), () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
652
- }
653
- function getFunctionKind2(node, context, useLayoutEffectAlias) {
654
- return $(node).when(isEffectFunction2(context, useLayoutEffectAlias), () => "effect").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
655
- }
656
657
  var no_direct_set_state_in_use_layout_effect_default = createRule({
657
658
  meta: {
658
659
  type: "problem",
@@ -660,14 +661,24 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
660
661
  description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
661
662
  },
662
663
  messages: {
663
- NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
664
+ noDirectSetStateInUseLayoutEffect: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
664
665
  },
665
666
  schema: []
666
667
  },
667
668
  name: RULE_NAME5,
668
669
  create(context) {
669
670
  const settings = shared.decodeSettings(context.settings);
670
- const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
671
+ const additionalHooks = settings.additionalHooks ?? {};
672
+ const isUseEffectCall = core.isReactHookCallWithNameAlias(
673
+ "useLayoutEffect",
674
+ context,
675
+ additionalHooks.useLayoutEffect ?? []
676
+ );
677
+ const isUseStateCall2 = core.isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
678
+ const isUseMemoCall2 = core.isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
679
+ const isUseCallbackCall2 = core.isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
680
+ const isSetStateCall = isSetFunctionCall(context, settings);
681
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
671
682
  const functionStack = [];
672
683
  const effectFunctionRef = tools.MutRef.make(null);
673
684
  const effectFunctionIdentifiers = [];
@@ -682,9 +693,18 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
682
693
  const onEffectFunctionExit = (node) => {
683
694
  tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
684
695
  };
696
+ function isEffectFunction(node) {
697
+ return node.parent?.type === ast.NodeType.CallExpression && node.parent.callee !== node && isUseEffectCall(node.parent);
698
+ }
699
+ function getCallKind(node) {
700
+ return $(node).when(isUseStateCall2, () => "useState").when(isUseEffectCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
701
+ }
702
+ function getFunctionKind(node) {
703
+ return $(node).when(isEffectFunction, () => "effect").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
704
+ }
685
705
  return {
686
706
  ":function"(node) {
687
- const functionKind = getFunctionKind2(node, context, useLayoutEffectAlias);
707
+ const functionKind = getFunctionKind(node);
688
708
  functionStack.push([node, functionKind]);
689
709
  $(functionKind).with("effect", () => {
690
710
  onEffectFunctionEnter(node);
@@ -698,7 +718,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
698
718
  const effectFn = tools.MutRef.get(effectFunctionRef);
699
719
  const [parentFn, parentFnKind] = R__namespace.last(functionStack) ?? [];
700
720
  if (parentFn?.async) return;
701
- $(getCallKind2(node, context, [useStateAlias, useLayoutEffectAlias])).with("setState", () => {
721
+ $(getCallKind(node)).with("setState", () => {
702
722
  if (!parentFn) return;
703
723
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
704
724
  const maybeVd = ast.traverseUpGuard(node, isVariableDeclaratorFromHookCall);
@@ -713,7 +733,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
713
733
  return;
714
734
  }
715
735
  context.report({
716
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
736
+ messageId: "noDirectSetStateInUseLayoutEffect",
717
737
  node
718
738
  });
719
739
  }).with("useLayoutEffect", () => {
@@ -729,19 +749,20 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
729
749
  },
730
750
  Identifier(node) {
731
751
  if (!node.parent) return;
732
- if (!isFromUseStateCall(context, useStateAlias)(node)) return;
752
+ if (node.parent.type === ast.NodeType.CallExpression && node.parent.callee === node) return;
753
+ if (!isIdFromUseStateCall(node)) return;
733
754
  switch (node.parent.type) {
734
755
  case ast.NodeType.CallExpression: {
735
756
  const [firstArg] = node.parent.arguments;
736
757
  if (node !== firstArg) break;
737
- if (core.isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
758
+ if (isUseCallbackCall2(node.parent)) {
738
759
  const maybeVd = ast.traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
739
760
  if (tools.O.isNone(maybeVd)) break;
740
761
  const vd = maybeVd.value;
741
762
  const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
742
763
  indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
743
764
  }
744
- if (core.isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias)(node.parent)) {
765
+ if (isUseEffectCall(node.parent)) {
745
766
  const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
746
767
  indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
747
768
  }
@@ -750,7 +771,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
750
771
  case ast.NodeType.ArrowFunctionExpression: {
751
772
  const parent = node.parent.parent;
752
773
  if (parent?.type !== ast.NodeType.CallExpression) break;
753
- if (!core.isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
774
+ if (!isUseMemoCall2(parent)) break;
754
775
  const maybeVd = ast.traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
755
776
  if (tools.O.isNone(maybeVd)) break;
756
777
  const vd = maybeVd.value;
@@ -776,7 +797,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
776
797
  for (const call of calls) {
777
798
  context.report({
778
799
  data: { name: call.name },
779
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
800
+ messageId: "noDirectSetStateInUseLayoutEffect",
780
801
  node: call
781
802
  });
782
803
  }
@@ -788,7 +809,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
788
809
  for (const setStateCall of setStateCalls) {
789
810
  context.report({
790
811
  data: { name: name2 },
791
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
812
+ messageId: "noDirectSetStateInUseLayoutEffect",
792
813
  node: setStateCall
793
814
  });
794
815
  }
@@ -798,7 +819,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
798
819
  for (const setStateCall of setStateCalls) {
799
820
  context.report({
800
821
  data: { name: id.name },
801
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
822
+ messageId: "noDirectSetStateInUseLayoutEffect",
802
823
  node: setStateCall
803
824
  });
804
825
  }
@@ -817,7 +838,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
817
838
  description: "disallow function calls in 'useState' that aren't wrapped in an initializer function"
818
839
  },
819
840
  messages: {
820
- PREFER_USE_STATE_LAZY_INITIALIZATION: "To prevent re-computation, consider using lazy initial state for useState calls that involve function calls. Ex: 'useState(() => getValue())'."
841
+ preferUseStateLazyInitialization: "To prevent re-computation, consider using lazy initial state for useState calls that involve function calls. Ex: 'useState(() => getValue())'."
821
842
  },
822
843
  schema: []
823
844
  },
@@ -836,7 +857,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
836
857
  });
837
858
  if (!hasFunctionCall) return;
838
859
  context.report({
839
- messageId: "PREFER_USE_STATE_LAZY_INITIALIZATION",
860
+ messageId: "preferUseStateLazyInitialization",
840
861
  node: useStateInput
841
862
  });
842
863
  }
package/dist/index.mjs CHANGED
@@ -8,21 +8,25 @@ 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.2";
11
+ var version = "1.8.3-beta.2";
12
12
  var createRule = createRuleForPlugin("hooks-extra");
13
- function isFromUseStateCall(context, useStateAlias) {
14
- return (topLevelId) => {
15
- const useStateCall = F.pipe(
16
- findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
17
- O.flatMap(getVariableNode(0)),
18
- O.filter(is(NodeType.CallExpression)),
19
- O.filter(isReactHookCallWithNameAlias("useState", context, useStateAlias))
20
- );
21
- if (O.isNone(useStateCall)) return false;
22
- const { parent } = useStateCall.value;
13
+ function isFromHookCall(name2, context, settings, predicate = F.constTrue) {
14
+ const hookAlias = settings.additionalHooks?.[name2] ?? [];
15
+ return (topLevelId) => F.pipe(
16
+ findVariable(topLevelId, context.sourceCode.getScope(topLevelId)),
17
+ O.flatMap(getVariableNode(0)),
18
+ O.filter(is(NodeType.CallExpression)),
19
+ O.filter(isReactHookCallWithNameAlias(name2, context, hookAlias)),
20
+ O.exists((call) => predicate(topLevelId, call))
21
+ );
22
+ }
23
+ function isFromUseStateCall(context, settings) {
24
+ const predicate = (topLevelId, call) => {
25
+ const { parent } = call;
23
26
  if (!("id" in parent && parent.id?.type === NodeType.ArrayPattern)) return true;
24
27
  return parent.id.elements.findIndex((e2) => e2?.type === NodeType.Identifier && e2.name === topLevelId.name) === 1;
25
28
  };
29
+ return isFromHookCall("useState", context, settings, predicate);
26
30
  }
27
31
 
28
32
  // ../../../node_modules/.pnpm/ts-pattern@5.2.0/node_modules/ts-pattern/dist/index.js
@@ -208,18 +212,19 @@ var z = class _z {
208
212
  }
209
213
  };
210
214
 
211
- // src/utils/is-set-state-call.ts
212
- function isSetStateCall(context, useStateAlias) {
215
+ // src/utils/is-set-function-call.ts
216
+ function isSetFunctionCall(context, settings) {
217
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
213
218
  return (node) => {
214
219
  switch (node.callee.type) {
215
220
  case NodeType.Identifier: {
216
- return isFromUseStateCall(context, useStateAlias)(node.callee);
221
+ return isIdFromUseStateCall(node.callee);
217
222
  }
218
223
  case NodeType.MemberExpression: {
219
224
  if (!("name" in node.callee.object)) return false;
220
225
  const initialScope = context.sourceCode.getScope(node);
221
226
  const property = getStaticValue(node.callee.property, initialScope);
222
- if (property?.value === 1) return isFromUseStateCall(context, useStateAlias)(node.callee.object);
227
+ if (property?.value === 1) return isIdFromUseStateCall(node.callee.object);
223
228
  return false;
224
229
  }
225
230
  case NodeType.CallExpression: {
@@ -237,7 +242,7 @@ function isSetStateCall(context, useStateAlias) {
237
242
  if (!isAt || !index) return false;
238
243
  const initialScope = context.sourceCode.getScope(node);
239
244
  const value = getStaticValue(index, initialScope);
240
- if (value?.value === 1) return isFromUseStateCall(context, useStateAlias)(callee.object);
245
+ if (value?.value === 1) return isIdFromUseStateCall(callee.object);
241
246
  return false;
242
247
  }
243
248
  default: {
@@ -272,7 +277,7 @@ var ensure_custom_hooks_using_other_hooks_default = createRule({
272
277
  description: "enforce custom hooks using other hooks"
273
278
  },
274
279
  messages: {
275
- ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS: "A custom hook '{{name}}' should use other hooks."
280
+ ensureCustomHooksUsingOtherHooks: "A custom hook '{{name}}' should use other hooks."
276
281
  },
277
282
  schema: []
278
283
  },
@@ -289,7 +294,7 @@ var ensure_custom_hooks_using_other_hooks_default = createRule({
289
294
  data: {
290
295
  name: name2.value
291
296
  },
292
- messageId: "ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS",
297
+ messageId: "ensureCustomHooksUsingOtherHooks",
293
298
  node: node2
294
299
  });
295
300
  }
@@ -306,7 +311,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
306
311
  description: "enforce 'useCallback' has non-empty dependencies array"
307
312
  },
308
313
  messages: {
309
- ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS: "An 'useCallback' should have a non-empty dependencies array."
314
+ ensureUseCallbackHasNonEmptyDeps: "An 'useCallback' should have a non-empty dependencies array."
310
315
  },
311
316
  schema: []
312
317
  },
@@ -326,7 +331,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
326
331
  const [cb, deps] = node.arguments;
327
332
  if (!deps) {
328
333
  context.report({
329
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
334
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
330
335
  node
331
336
  });
332
337
  return;
@@ -344,7 +349,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
344
349
  if (!hasEmptyDeps) return;
345
350
  if (!cb) {
346
351
  context.report({
347
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
352
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
348
353
  node
349
354
  });
350
355
  return;
@@ -368,7 +373,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
368
373
  );
369
374
  if (isReferencedToComponentScope) return;
370
375
  context.report({
371
- messageId: "ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS",
376
+ messageId: "ensureUseCallbackHasNonEmptyDeps",
372
377
  node
373
378
  });
374
379
  }
@@ -384,7 +389,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
384
389
  description: "enforce 'useMemo' has non-empty dependencies array"
385
390
  },
386
391
  messages: {
387
- ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS: "An 'useMemo' should have a non-empty dependencies array."
392
+ ensureUseMemoHasNonEmptyDeps: "An 'useMemo' should have a non-empty dependencies array."
388
393
  },
389
394
  schema: []
390
395
  },
@@ -404,7 +409,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
404
409
  const [cb, deps] = node.arguments;
405
410
  if (!deps) {
406
411
  context.report({
407
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
412
+ messageId: "ensureUseMemoHasNonEmptyDeps",
408
413
  node
409
414
  });
410
415
  return;
@@ -422,7 +427,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
422
427
  if (!hasEmptyDeps) return;
423
428
  if (!cb) {
424
429
  context.report({
425
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
430
+ messageId: "ensureUseMemoHasNonEmptyDeps",
426
431
  node
427
432
  });
428
433
  return;
@@ -446,7 +451,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
446
451
  );
447
452
  if (isReferencedToComponentScope) return;
448
453
  context.report({
449
- messageId: "ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS",
454
+ messageId: "ensureUseMemoHasNonEmptyDeps",
450
455
  node
451
456
  });
452
457
  }
@@ -455,16 +460,6 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
455
460
  defaultOptions: []
456
461
  });
457
462
  var RULE_NAME4 = "no-direct-set-state-in-use-effect";
458
- function isEffectFunction(context, useSomeEffectAlias) {
459
- return (node) => node.parent?.type === NodeType.CallExpression && node.parent.callee !== node && isReactHookCallWithNameAlias("useEffect", context, useSomeEffectAlias)(node.parent);
460
- }
461
- function getCallKind(node, context, alias) {
462
- const [useStateAlias, useEffectAlias] = alias;
463
- return $(node).when(isReactHookCallWithNameAlias("useState", context, useStateAlias), () => "useState").when(isReactHookCallWithNameAlias("useEffect", context, useEffectAlias), () => "useEffect").when(isSetStateCall(context, useStateAlias), () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
464
- }
465
- function getFunctionKind(node, context, useEffectAlias) {
466
- return $(node).when(isEffectFunction(context, useEffectAlias), () => "effect").when(isIIFE, () => "immediate").otherwise(() => "other");
467
- }
468
463
  var no_direct_set_state_in_use_effect_default = createRule({
469
464
  meta: {
470
465
  type: "problem",
@@ -472,14 +467,20 @@ var no_direct_set_state_in_use_effect_default = createRule({
472
467
  description: "disallow direct calls to the 'set' function of 'useState' in 'useEffect'"
473
468
  },
474
469
  messages: {
475
- NO_DIRECT_SET_STATE_IN_USE_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
470
+ noDirectSetStateInUseEffect: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
476
471
  },
477
472
  schema: []
478
473
  },
479
474
  name: RULE_NAME4,
480
475
  create(context) {
481
476
  const settings = decodeSettings(context.settings);
482
- const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
477
+ const additionalHooks = settings.additionalHooks ?? {};
478
+ const isUseEffectCall = isReactHookCallWithNameAlias("useEffect", context, additionalHooks.useEffect ?? []);
479
+ const isUseStateCall2 = isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
480
+ const isUseMemoCall2 = isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
481
+ const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
482
+ const isSetStateCall = isSetFunctionCall(context, settings);
483
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
483
484
  const functionStack = [];
484
485
  const effectFunctionRef = MutRef.make(null);
485
486
  const effectFunctionIdentifiers = [];
@@ -494,9 +495,18 @@ var no_direct_set_state_in_use_effect_default = createRule({
494
495
  const onEffectFunctionExit = (node) => {
495
496
  MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
496
497
  };
498
+ function isEffectFunction(node) {
499
+ return node.parent?.type === NodeType.CallExpression && node.parent.callee !== node && isUseEffectCall(node.parent);
500
+ }
501
+ function getCallKind(node) {
502
+ return $(node).when(isUseStateCall2, () => "useState").when(isUseEffectCall, () => "useEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
503
+ }
504
+ function getFunctionKind(node) {
505
+ return $(node).when(isEffectFunction, () => "effect").when(isIIFE, () => "immediate").otherwise(() => "other");
506
+ }
497
507
  return {
498
508
  ":function"(node) {
499
- const functionKind = getFunctionKind(node, context, useEffectAlias);
509
+ const functionKind = getFunctionKind(node);
500
510
  functionStack.push([node, functionKind]);
501
511
  $(functionKind).with("effect", () => {
502
512
  onEffectFunctionEnter(node);
@@ -510,7 +520,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
510
520
  const effectFn = MutRef.get(effectFunctionRef);
511
521
  const [parentFn, parentFnKind] = R.last(functionStack) ?? [];
512
522
  if (parentFn?.async) return;
513
- $(getCallKind(node, context, [useStateAlias, useEffectAlias])).with("setState", () => {
523
+ $(getCallKind(node)).with("setState", () => {
514
524
  if (!parentFn) return;
515
525
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
516
526
  const maybeVd = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
@@ -525,7 +535,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
525
535
  return;
526
536
  }
527
537
  context.report({
528
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
538
+ messageId: "noDirectSetStateInUseEffect",
529
539
  node
530
540
  });
531
541
  }).with("useEffect", () => {
@@ -541,19 +551,20 @@ var no_direct_set_state_in_use_effect_default = createRule({
541
551
  },
542
552
  Identifier(node) {
543
553
  if (!node.parent) return;
544
- if (!isFromUseStateCall(context, useStateAlias)(node)) return;
554
+ if (node.parent.type === NodeType.CallExpression && node.parent.callee === node) return;
555
+ if (!isIdFromUseStateCall(node)) return;
545
556
  switch (node.parent.type) {
546
557
  case NodeType.CallExpression: {
547
558
  const [firstArg] = node.parent.arguments;
548
559
  if (node !== firstArg) break;
549
- if (isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
560
+ if (isUseCallbackCall2(node.parent)) {
550
561
  const maybeVd = traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
551
562
  if (O.isNone(maybeVd)) break;
552
563
  const vd = maybeVd.value;
553
564
  const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
554
565
  indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
555
566
  }
556
- if (isReactHookCallWithNameAlias("useEffect", context, useEffectAlias)(node.parent)) {
567
+ if (isUseEffectCall(node.parent)) {
557
568
  const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
558
569
  indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
559
570
  }
@@ -562,7 +573,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
562
573
  case NodeType.ArrowFunctionExpression: {
563
574
  const parent = node.parent.parent;
564
575
  if (parent?.type !== NodeType.CallExpression) break;
565
- if (!isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
576
+ if (!isUseMemoCall2(parent)) break;
566
577
  const maybeVd = traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
567
578
  if (O.isNone(maybeVd)) break;
568
579
  const vd = maybeVd.value;
@@ -588,7 +599,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
588
599
  for (const call of calls) {
589
600
  context.report({
590
601
  data: { name: call.name },
591
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
602
+ messageId: "noDirectSetStateInUseEffect",
592
603
  node: call
593
604
  });
594
605
  }
@@ -600,7 +611,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
600
611
  for (const setStateCall of setStateCalls) {
601
612
  context.report({
602
613
  data: { name: name2 },
603
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
614
+ messageId: "noDirectSetStateInUseEffect",
604
615
  node: setStateCall
605
616
  });
606
617
  }
@@ -610,7 +621,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
610
621
  for (const setStateCall of setStateCalls) {
611
622
  context.report({
612
623
  data: { name: id.name },
613
- messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
624
+ messageId: "noDirectSetStateInUseEffect",
614
625
  node: setStateCall
615
626
  });
616
627
  }
@@ -621,16 +632,6 @@ var no_direct_set_state_in_use_effect_default = createRule({
621
632
  defaultOptions: []
622
633
  });
623
634
  var RULE_NAME5 = "no-direct-set-state-in-use-layout-effect";
624
- function isEffectFunction2(context, useSomeEffectAlias) {
625
- return (node) => node.parent?.type === NodeType.CallExpression && node.parent.callee !== node && isReactHookCallWithNameAlias("useLayoutEffect", context, useSomeEffectAlias)(node.parent);
626
- }
627
- function getCallKind2(node, context, alias) {
628
- const [useStateAlias, useLayoutEffectAlias] = alias;
629
- return $(node).when(isReactHookCallWithNameAlias("useState", context, useStateAlias), () => "useState").when(isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias), () => "useLayoutEffect").when(isSetStateCall(context, useStateAlias), () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
630
- }
631
- function getFunctionKind2(node, context, useLayoutEffectAlias) {
632
- return $(node).when(isEffectFunction2(context, useLayoutEffectAlias), () => "effect").when(isIIFE, () => "immediate").otherwise(() => "other");
633
- }
634
635
  var no_direct_set_state_in_use_layout_effect_default = createRule({
635
636
  meta: {
636
637
  type: "problem",
@@ -638,14 +639,24 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
638
639
  description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
639
640
  },
640
641
  messages: {
641
- NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
642
+ noDirectSetStateInUseLayoutEffect: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
642
643
  },
643
644
  schema: []
644
645
  },
645
646
  name: RULE_NAME5,
646
647
  create(context) {
647
648
  const settings = decodeSettings(context.settings);
648
- const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
649
+ const additionalHooks = settings.additionalHooks ?? {};
650
+ const isUseEffectCall = isReactHookCallWithNameAlias(
651
+ "useLayoutEffect",
652
+ context,
653
+ additionalHooks.useLayoutEffect ?? []
654
+ );
655
+ const isUseStateCall2 = isReactHookCallWithNameAlias("useState", context, additionalHooks.useState ?? []);
656
+ const isUseMemoCall2 = isReactHookCallWithNameAlias("useMemo", context, additionalHooks.useMemo ?? []);
657
+ const isUseCallbackCall2 = isReactHookCallWithNameAlias("useCallback", context, additionalHooks.useCallback ?? []);
658
+ const isSetStateCall = isSetFunctionCall(context, settings);
659
+ const isIdFromUseStateCall = isFromUseStateCall(context, settings);
649
660
  const functionStack = [];
650
661
  const effectFunctionRef = MutRef.make(null);
651
662
  const effectFunctionIdentifiers = [];
@@ -660,9 +671,18 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
660
671
  const onEffectFunctionExit = (node) => {
661
672
  MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
662
673
  };
674
+ function isEffectFunction(node) {
675
+ return node.parent?.type === NodeType.CallExpression && node.parent.callee !== node && isUseEffectCall(node.parent);
676
+ }
677
+ function getCallKind(node) {
678
+ return $(node).when(isUseStateCall2, () => "useState").when(isUseEffectCall, () => "useLayoutEffect").when(isSetStateCall, () => "setState").when(isThenCall, () => "then").otherwise(() => "other");
679
+ }
680
+ function getFunctionKind(node) {
681
+ return $(node).when(isEffectFunction, () => "effect").when(isIIFE, () => "immediate").otherwise(() => "other");
682
+ }
663
683
  return {
664
684
  ":function"(node) {
665
- const functionKind = getFunctionKind2(node, context, useLayoutEffectAlias);
685
+ const functionKind = getFunctionKind(node);
666
686
  functionStack.push([node, functionKind]);
667
687
  $(functionKind).with("effect", () => {
668
688
  onEffectFunctionEnter(node);
@@ -676,7 +696,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
676
696
  const effectFn = MutRef.get(effectFunctionRef);
677
697
  const [parentFn, parentFnKind] = R.last(functionStack) ?? [];
678
698
  if (parentFn?.async) return;
679
- $(getCallKind2(node, context, [useStateAlias, useLayoutEffectAlias])).with("setState", () => {
699
+ $(getCallKind(node)).with("setState", () => {
680
700
  if (!parentFn) return;
681
701
  if (parentFn !== effectFn && parentFnKind !== "immediate") {
682
702
  const maybeVd = traverseUpGuard(node, isVariableDeclaratorFromHookCall);
@@ -691,7 +711,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
691
711
  return;
692
712
  }
693
713
  context.report({
694
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
714
+ messageId: "noDirectSetStateInUseLayoutEffect",
695
715
  node
696
716
  });
697
717
  }).with("useLayoutEffect", () => {
@@ -707,19 +727,20 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
707
727
  },
708
728
  Identifier(node) {
709
729
  if (!node.parent) return;
710
- if (!isFromUseStateCall(context, useStateAlias)(node)) return;
730
+ if (node.parent.type === NodeType.CallExpression && node.parent.callee === node) return;
731
+ if (!isIdFromUseStateCall(node)) return;
711
732
  switch (node.parent.type) {
712
733
  case NodeType.CallExpression: {
713
734
  const [firstArg] = node.parent.arguments;
714
735
  if (node !== firstArg) break;
715
- if (isReactHookCallWithNameAlias("useCallback", context, useStateAlias)(node.parent)) {
736
+ if (isUseCallbackCall2(node.parent)) {
716
737
  const maybeVd = traverseUpGuard(node.parent, isVariableDeclaratorFromHookCall);
717
738
  if (O.isNone(maybeVd)) break;
718
739
  const vd = maybeVd.value;
719
740
  const calls = indirectSetStateCallsAsArgs.get(vd.init) ?? [];
720
741
  indirectSetStateCallsAsArgs.set(vd.init, [...calls, node]);
721
742
  }
722
- if (isReactHookCallWithNameAlias("useLayoutEffect", context, useLayoutEffectAlias)(node.parent)) {
743
+ if (isUseEffectCall(node.parent)) {
723
744
  const calls = indirectSetStateCallsAsArgs.get(node.parent) ?? [];
724
745
  indirectSetStateCallsAsEFs.set(node.parent, [...calls, node]);
725
746
  }
@@ -728,7 +749,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
728
749
  case NodeType.ArrowFunctionExpression: {
729
750
  const parent = node.parent.parent;
730
751
  if (parent?.type !== NodeType.CallExpression) break;
731
- if (!isReactHookCallWithNameAlias("useMemo", context, useStateAlias)(parent)) break;
752
+ if (!isUseMemoCall2(parent)) break;
732
753
  const maybeVd = traverseUpGuard(parent, isVariableDeclaratorFromHookCall);
733
754
  if (O.isNone(maybeVd)) break;
734
755
  const vd = maybeVd.value;
@@ -754,7 +775,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
754
775
  for (const call of calls) {
755
776
  context.report({
756
777
  data: { name: call.name },
757
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
778
+ messageId: "noDirectSetStateInUseLayoutEffect",
758
779
  node: call
759
780
  });
760
781
  }
@@ -766,7 +787,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
766
787
  for (const setStateCall of setStateCalls) {
767
788
  context.report({
768
789
  data: { name: name2 },
769
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
790
+ messageId: "noDirectSetStateInUseLayoutEffect",
770
791
  node: setStateCall
771
792
  });
772
793
  }
@@ -776,7 +797,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
776
797
  for (const setStateCall of setStateCalls) {
777
798
  context.report({
778
799
  data: { name: id.name },
779
- messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
800
+ messageId: "noDirectSetStateInUseLayoutEffect",
780
801
  node: setStateCall
781
802
  });
782
803
  }
@@ -795,7 +816,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
795
816
  description: "disallow function calls in 'useState' that aren't wrapped in an initializer function"
796
817
  },
797
818
  messages: {
798
- PREFER_USE_STATE_LAZY_INITIALIZATION: "To prevent re-computation, consider using lazy initial state for useState calls that involve function calls. Ex: 'useState(() => getValue())'."
819
+ preferUseStateLazyInitialization: "To prevent re-computation, consider using lazy initial state for useState calls that involve function calls. Ex: 'useState(() => getValue())'."
799
820
  },
800
821
  schema: []
801
822
  },
@@ -814,7 +835,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
814
835
  });
815
836
  if (!hasFunctionCall) return;
816
837
  context.report({
817
- messageId: "PREFER_USE_STATE_LAZY_INITIALIZATION",
838
+ messageId: "preferUseStateLazyInitialization",
818
839
  node: useStateInput
819
840
  });
820
841
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "1.8.2",
3
+ "version": "1.8.3-beta.2",
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.7.0",
49
- "@eslint-react/ast": "1.8.2",
50
- "@eslint-react/core": "1.8.2",
51
- "@eslint-react/jsx": "1.8.2",
52
- "@eslint-react/tools": "1.8.2",
53
- "@eslint-react/shared": "1.8.2",
54
- "@eslint-react/var": "1.8.2",
55
- "@eslint-react/types": "1.8.2"
49
+ "@eslint-react/ast": "1.8.3-beta.2",
50
+ "@eslint-react/jsx": "1.8.3-beta.2",
51
+ "@eslint-react/shared": "1.8.3-beta.2",
52
+ "@eslint-react/core": "1.8.3-beta.2",
53
+ "@eslint-react/types": "1.8.3-beta.2",
54
+ "@eslint-react/var": "1.8.3-beta.2",
55
+ "@eslint-react/tools": "1.8.3-beta.2"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/react": "^18.3.3",