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 +10 -8
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +82 -3
- package/dist/index.mjs +83 -4
- package/package.json +8 -8
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/
|
|
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
|
|
44
|
-
|
|
|
45
|
-
| `ensure-custom-hooks-using-other-hooks`
|
|
46
|
-
| `ensure-use-callback-has-non-empty-deps`
|
|
47
|
-
| `ensure-use-memo-has-non-empty-deps`
|
|
48
|
-
| `no-direct-set-state-in-use-effect`
|
|
49
|
-
| `
|
|
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.
|
|
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 = "
|
|
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:
|
|
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.
|
|
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 = "
|
|
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:
|
|
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.
|
|
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.
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/
|
|
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",
|