eslint-plugin-react-hooks-extra 1.5.26-next.0 → 1.5.26-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
@@ -30,7 +30,8 @@ export default [
30
30
  "react-hooks-extra": reactHooksExtra,
31
31
  rules: {
32
32
  // react-hooks-extra recommended rules
33
- "react-hooks-extra/ensure-custom-hooks-using-other-hooks": "warn",
33
+ "react-hooks-extra/no-direct-set-state-in-use-effect": "warn",
34
+ "react-hooks-extra/no-direct-set-state-in-use-layout-effect": "warn",
34
35
  "react-hooks-extra/prefer-use-state-lazy-initialization": "warn",
35
36
  }
36
37
  ],
@@ -40,10 +41,11 @@ export default [
40
41
 
41
42
  ## Rules
42
43
 
43
- | Rule | Description | 💼 | 💭 | ❌ |
44
- | :--------------------------------------- | :------------------------------------------------------------------------ | :-: | :-: | :-: |
45
- | `ensure-custom-hooks-using-other-hooks` | Warns when custom Hooks that don't use other Hooks. | ✔️ | | |
46
- | `ensure-use-callback-has-non-empty-deps` | Warns when `useCallback` is called with empty dependencies array. | 🧐 | | |
47
- | `ensure-use-memo-has-non-empty-deps` | Warns when `useMemo` is called with empty dependencies array. | 🧐 | | |
48
- | `no-direct-set-state-in-use-effect` | Disallow direct calls to the `set` function of `useState` in `useEffect`. | ✔️ | | |
49
- | `prefer-use-state-lazy-initialization` | Warns function calls made inside `useState` calls. | 🚀 | | |
44
+ | Rule | Description | 💼 | 💭 | ❌ |
45
+ | :----------------------------------------- | :------------------------------------------------------------------------------ | :-: | :-: | :-: |
46
+ | `ensure-custom-hooks-using-other-hooks` | Warns when custom Hooks that don't use other Hooks. | ✔️ | | |
47
+ | `ensure-use-callback-has-non-empty-deps` | Warns when `useCallback` is called with empty dependencies array. | 🧐 | | |
48
+ | `ensure-use-memo-has-non-empty-deps` | Warns when `useMemo` is called with empty dependencies array. | 🧐 | | |
49
+ | `no-direct-set-state-in-use-effect` | Disallow direct calls to the `set` function of `useState` in `useEffect`. | ✔️ | | |
50
+ | `no-direct-set-state-in-use-layout-effect` | Disallow direct calls to the `set` function of `useState` in `useLayoutEffect`. | ✔️ | | |
51
+ | `prefer-use-state-lazy-initialization` | Warns function calls made inside `useState` calls. | 🚀 | | |
package/dist/index.d.mts CHANGED
@@ -9,6 +9,7 @@ declare const rules: {
9
9
  readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
10
10
  readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
11
11
  readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_EFFECT", [], _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", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
12
13
  readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
13
14
  };
14
15
 
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ declare const rules: {
9
9
  readonly "ensure-use-callback-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_CALLBACK_HAS_NON_EMPTY_DEPS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
10
10
  readonly "ensure-use-memo-has-non-empty-deps": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_USE_MEMO_HAS_NON_EMPTY_DEPS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
11
11
  readonly "no-direct-set-state-in-use-effect": _typescript_eslint_utils_ts_eslint.RuleModule<"NO_DIRECT_SET_STATE_IN_USE_EFFECT", [], _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", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
12
13
  readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
13
14
  };
14
15
 
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.26-next.0";
12
+ var version = "1.5.26-next.1";
13
13
  var createRule = shared.createRuleForPlugin("hooks-extra");
14
14
 
15
15
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -395,7 +395,85 @@ var no_direct_set_state_in_use_effect_default = createRule({
395
395
  },
396
396
  defaultOptions: []
397
397
  });
398
- var RULE_NAME5 = "prefer-use-state-lazy-initialization";
398
+ var RULE_NAME5 = "no-direct-set-state-in-use-layout-effect";
399
+ var no_direct_set_state_in_use_layout_effect_default = createRule({
400
+ meta: {
401
+ type: "problem",
402
+ docs: {
403
+ description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
404
+ },
405
+ messages: {
406
+ NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
407
+ },
408
+ schema: []
409
+ },
410
+ name: RULE_NAME5,
411
+ create(context) {
412
+ const settings = shared.getESLintReactSettings(context.settings);
413
+ const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
414
+ function isUseLayoutEffectCallWithAlias(node, context2) {
415
+ return core.isUseLayoutEffectCall(node, context2) || useLayoutEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
416
+ }
417
+ function isUseStateCallWithAlias(node, context2) {
418
+ return core.isUseStateCall(node, context2) || useStateAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
419
+ }
420
+ function isEffectFunction(node) {
421
+ return node.parent?.type === ast.NodeType.CallExpression && isUseLayoutEffectCallWithAlias(node.parent, context);
422
+ }
423
+ return {
424
+ CallExpression(node) {
425
+ const effectFunction = ast.traverseUp(node, isEffectFunction);
426
+ if (tools.O.isNone(effectFunction)) return;
427
+ const scope = context.sourceCode.getScope(node);
428
+ if (scope.block !== effectFunction.value) return;
429
+ const name2 = $(node.callee).with({ type: ast.NodeType.Identifier }, (n2) => tools.O.some(n2.name)).with({ type: ast.NodeType.MemberExpression }, (n2) => {
430
+ if (!("name" in n2.object)) return tools.O.none();
431
+ const initialScope = context.sourceCode.getScope(n2);
432
+ const property = astUtils.getStaticValue(n2.property, initialScope);
433
+ if (property?.value === 1) return tools.O.fromNullable(n2.object.name);
434
+ return tools.O.none();
435
+ }).with({ type: ast.NodeType.CallExpression }, (n2) => {
436
+ if (!ast.is(ast.NodeType.MemberExpression)(n2.callee)) return tools.O.none();
437
+ if (!("name" in n2.callee.object)) return tools.O.none();
438
+ const isAt = $(n2.callee).with(
439
+ {
440
+ type: ast.NodeType.MemberExpression,
441
+ property: {
442
+ type: ast.NodeType.Identifier,
443
+ name: "at"
444
+ }
445
+ },
446
+ tools.F.constTrue
447
+ ).otherwise(tools.F.constFalse);
448
+ if (!isAt) return tools.O.none();
449
+ const [index] = n2.arguments;
450
+ if (!index) return tools.O.none();
451
+ const initialScope = context.sourceCode.getScope(n2);
452
+ const value = astUtils.getStaticValue(index, initialScope);
453
+ if (value?.value === 1) return tools.O.fromNullable(n2.callee.object.name);
454
+ return tools.O.none();
455
+ }).otherwise(tools.O.none);
456
+ tools.F.pipe(
457
+ name2,
458
+ tools.O.flatMap(_var.findVariable(scope)),
459
+ tools.O.flatMap(_var.getVariableInit(0)),
460
+ tools.O.filter(ast.is(ast.NodeType.CallExpression)),
461
+ tools.O.filter((name3) => isUseStateCallWithAlias(name3, context)),
462
+ tools.O.map((name3) => ({
463
+ data: {
464
+ setState: name3
465
+ },
466
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
467
+ node
468
+ })),
469
+ tools.O.map(context.report)
470
+ );
471
+ }
472
+ };
473
+ },
474
+ defaultOptions: []
475
+ });
476
+ var RULE_NAME6 = "prefer-use-state-lazy-initialization";
399
477
  var ALLOW_LIST = ["Boolean", "String", "Number"];
400
478
  var prefer_use_state_lazy_initialization_default = createRule({
401
479
  meta: {
@@ -408,7 +486,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
408
486
  },
409
487
  schema: []
410
488
  },
411
- name: RULE_NAME5,
489
+ name: RULE_NAME6,
412
490
  create(context) {
413
491
  const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
414
492
  return {
@@ -442,6 +520,7 @@ var rules = {
442
520
  "ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
443
521
  "ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
444
522
  "no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
523
+ "no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_layout_effect_default,
445
524
  "prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
446
525
  };
447
526
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall } from '@eslint-react/core';
1
+ import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall, isUseLayoutEffectCall } from '@eslint-react/core';
2
2
  import { createRuleForPlugin, getESLintReactSettings } from '@eslint-react/shared';
3
3
  import { NodeType, is, traverseUp, getNestedCallExpressions } from '@eslint-react/ast';
4
4
  import { F, O } from '@eslint-react/tools';
@@ -7,7 +7,7 @@ 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.26-next.0";
10
+ var version = "1.5.26-next.1";
11
11
  var createRule = createRuleForPlugin("hooks-extra");
12
12
 
13
13
  // src/rules/ensure-custom-hooks-using-other-hooks.ts
@@ -393,7 +393,85 @@ var no_direct_set_state_in_use_effect_default = createRule({
393
393
  },
394
394
  defaultOptions: []
395
395
  });
396
- var RULE_NAME5 = "prefer-use-state-lazy-initialization";
396
+ var RULE_NAME5 = "no-direct-set-state-in-use-layout-effect";
397
+ var no_direct_set_state_in_use_layout_effect_default = createRule({
398
+ meta: {
399
+ type: "problem",
400
+ docs: {
401
+ description: "disallow direct calls to the 'set' function of 'useState' in 'useLayoutEffect'"
402
+ },
403
+ messages: {
404
+ NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useLayoutEffect'."
405
+ },
406
+ schema: []
407
+ },
408
+ name: RULE_NAME5,
409
+ create(context) {
410
+ const settings = getESLintReactSettings(context.settings);
411
+ const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
412
+ function isUseLayoutEffectCallWithAlias(node, context2) {
413
+ return isUseLayoutEffectCall(node, context2) || useLayoutEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
414
+ }
415
+ function isUseStateCallWithAlias(node, context2) {
416
+ return isUseStateCall(node, context2) || useStateAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
417
+ }
418
+ function isEffectFunction(node) {
419
+ return node.parent?.type === NodeType.CallExpression && isUseLayoutEffectCallWithAlias(node.parent, context);
420
+ }
421
+ return {
422
+ CallExpression(node) {
423
+ const effectFunction = traverseUp(node, isEffectFunction);
424
+ if (O.isNone(effectFunction)) return;
425
+ const scope = context.sourceCode.getScope(node);
426
+ if (scope.block !== effectFunction.value) return;
427
+ const name2 = $(node.callee).with({ type: NodeType.Identifier }, (n2) => O.some(n2.name)).with({ type: NodeType.MemberExpression }, (n2) => {
428
+ if (!("name" in n2.object)) return O.none();
429
+ const initialScope = context.sourceCode.getScope(n2);
430
+ const property = getStaticValue(n2.property, initialScope);
431
+ if (property?.value === 1) return O.fromNullable(n2.object.name);
432
+ return O.none();
433
+ }).with({ type: NodeType.CallExpression }, (n2) => {
434
+ if (!is(NodeType.MemberExpression)(n2.callee)) return O.none();
435
+ if (!("name" in n2.callee.object)) return O.none();
436
+ const isAt = $(n2.callee).with(
437
+ {
438
+ type: NodeType.MemberExpression,
439
+ property: {
440
+ type: NodeType.Identifier,
441
+ name: "at"
442
+ }
443
+ },
444
+ F.constTrue
445
+ ).otherwise(F.constFalse);
446
+ if (!isAt) return O.none();
447
+ const [index] = n2.arguments;
448
+ if (!index) return O.none();
449
+ const initialScope = context.sourceCode.getScope(n2);
450
+ const value = getStaticValue(index, initialScope);
451
+ if (value?.value === 1) return O.fromNullable(n2.callee.object.name);
452
+ return O.none();
453
+ }).otherwise(O.none);
454
+ F.pipe(
455
+ name2,
456
+ O.flatMap(findVariable(scope)),
457
+ O.flatMap(getVariableInit(0)),
458
+ O.filter(is(NodeType.CallExpression)),
459
+ O.filter((name3) => isUseStateCallWithAlias(name3, context)),
460
+ O.map((name3) => ({
461
+ data: {
462
+ setState: name3
463
+ },
464
+ messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
465
+ node
466
+ })),
467
+ O.map(context.report)
468
+ );
469
+ }
470
+ };
471
+ },
472
+ defaultOptions: []
473
+ });
474
+ var RULE_NAME6 = "prefer-use-state-lazy-initialization";
397
475
  var ALLOW_LIST = ["Boolean", "String", "Number"];
398
476
  var prefer_use_state_lazy_initialization_default = createRule({
399
477
  meta: {
@@ -406,7 +484,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
406
484
  },
407
485
  schema: []
408
486
  },
409
- name: RULE_NAME5,
487
+ name: RULE_NAME6,
410
488
  create(context) {
411
489
  const alias = getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
412
490
  return {
@@ -440,6 +518,7 @@ var rules = {
440
518
  "ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
441
519
  "ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
442
520
  "no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
521
+ "no-direct-set-state-in-use-layout-effect": no_direct_set_state_in_use_layout_effect_default,
443
522
  "prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
444
523
  };
445
524
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks-extra",
3
- "version": "1.5.26-next.0",
3
+ "version": "1.5.26-next.1",
4
4
  "description": "ESLint React's ESLint plugin for React Hooks related rules.",
5
5
  "homepage": "https://github.com/rel1cx/eslint-react",
6
6
  "bugs": {
@@ -39,13 +39,13 @@
39
39
  "@typescript-eslint/type-utils": "^7.16.0",
40
40
  "@typescript-eslint/types": "^7.16.0",
41
41
  "@typescript-eslint/utils": "^7.16.0",
42
- "@eslint-react/ast": "1.5.26-next.0",
43
- "@eslint-react/core": "1.5.26-next.0",
44
- "@eslint-react/jsx": "1.5.26-next.0",
45
- "@eslint-react/tools": "1.5.26-next.0",
46
- "@eslint-react/shared": "1.5.26-next.0",
47
- "@eslint-react/types": "1.5.26-next.0",
48
- "@eslint-react/var": "1.5.26-next.0"
42
+ "@eslint-react/ast": "1.5.26-next.1",
43
+ "@eslint-react/jsx": "1.5.26-next.1",
44
+ "@eslint-react/tools": "1.5.26-next.1",
45
+ "@eslint-react/types": "1.5.26-next.1",
46
+ "@eslint-react/var": "1.5.26-next.1",
47
+ "@eslint-react/shared": "1.5.26-next.1",
48
+ "@eslint-react/core": "1.5.26-next.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "string-ts": "2.2.0",