eslint-plugin-react-hooks-extra 1.5.25 → 1.5.26-next.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/README.md +7 -6
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +83 -3
- package/dist/index.mjs +85 -5
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -40,9 +40,10 @@ export default [
|
|
|
40
40
|
|
|
41
41
|
## Rules
|
|
42
42
|
|
|
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
|
-
| `
|
|
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. | 🚀 | | |
|
package/dist/index.d.mts
CHANGED
|
@@ -8,6 +8,7 @@ declare const rules: {
|
|
|
8
8
|
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
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
|
+
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>;
|
|
11
12
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
13
|
};
|
|
13
14
|
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const rules: {
|
|
|
8
8
|
readonly "ensure-custom-hooks-using-other-hooks": _typescript_eslint_utils_ts_eslint.RuleModule<"ENSURE_CUSTOM_HOOKS_USING_OTHER_HOOKS", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
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
|
+
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>;
|
|
11
12
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
13
|
};
|
|
13
14
|
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,11 @@ var shared = require('@eslint-react/shared');
|
|
|
5
5
|
var ast = require('@eslint-react/ast');
|
|
6
6
|
var tools = require('@eslint-react/tools');
|
|
7
7
|
var _var = require('@eslint-react/var');
|
|
8
|
+
var astUtils = require('@typescript-eslint/utils/ast-utils');
|
|
8
9
|
|
|
9
10
|
// package.json
|
|
10
11
|
var name = "eslint-plugin-react-hooks-extra";
|
|
11
|
-
var version = "1.5.
|
|
12
|
+
var version = "1.5.26-next.0";
|
|
12
13
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
13
14
|
|
|
14
15
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -316,7 +317,85 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
316
317
|
},
|
|
317
318
|
defaultOptions: []
|
|
318
319
|
});
|
|
319
|
-
var RULE_NAME4 = "
|
|
320
|
+
var RULE_NAME4 = "no-direct-set-state-in-use-effect";
|
|
321
|
+
var no_direct_set_state_in_use_effect_default = createRule({
|
|
322
|
+
meta: {
|
|
323
|
+
type: "problem",
|
|
324
|
+
docs: {
|
|
325
|
+
description: "disallow direct calls to the 'set' function of 'useState' in 'useEffect'"
|
|
326
|
+
},
|
|
327
|
+
messages: {
|
|
328
|
+
NO_DIRECT_SET_STATE_IN_USE_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
|
|
329
|
+
},
|
|
330
|
+
schema: []
|
|
331
|
+
},
|
|
332
|
+
name: RULE_NAME4,
|
|
333
|
+
create(context) {
|
|
334
|
+
const settings = shared.getESLintReactSettings(context.settings);
|
|
335
|
+
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
|
|
336
|
+
function isUseEffectCallWithAlias(node, context2) {
|
|
337
|
+
return core.isUseEffectCall(node, context2) || useEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
|
|
338
|
+
}
|
|
339
|
+
function isUseStateCallWithAlias(node, context2) {
|
|
340
|
+
return core.isUseStateCall(node, context2) || useStateAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
|
|
341
|
+
}
|
|
342
|
+
function isEffectFunction(node) {
|
|
343
|
+
return node.parent?.type === ast.NodeType.CallExpression && isUseEffectCallWithAlias(node.parent, context);
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
CallExpression(node) {
|
|
347
|
+
const effectFunction = ast.traverseUp(node, isEffectFunction);
|
|
348
|
+
if (tools.O.isNone(effectFunction)) return;
|
|
349
|
+
const scope = context.sourceCode.getScope(node);
|
|
350
|
+
if (scope.block !== effectFunction.value) return;
|
|
351
|
+
const name2 = $(node.callee).with({ type: ast.NodeType.Identifier }, (n2) => tools.O.some(n2.name)).with({ type: ast.NodeType.MemberExpression }, (n2) => {
|
|
352
|
+
if (!("name" in n2.object)) return tools.O.none();
|
|
353
|
+
const initialScope = context.sourceCode.getScope(n2);
|
|
354
|
+
const property = astUtils.getStaticValue(n2.property, initialScope);
|
|
355
|
+
if (property?.value === 1) return tools.O.fromNullable(n2.object.name);
|
|
356
|
+
return tools.O.none();
|
|
357
|
+
}).with({ type: ast.NodeType.CallExpression }, (n2) => {
|
|
358
|
+
if (!ast.is(ast.NodeType.MemberExpression)(n2.callee)) return tools.O.none();
|
|
359
|
+
if (!("name" in n2.callee.object)) return tools.O.none();
|
|
360
|
+
const isAt = $(n2.callee).with(
|
|
361
|
+
{
|
|
362
|
+
type: ast.NodeType.MemberExpression,
|
|
363
|
+
property: {
|
|
364
|
+
type: ast.NodeType.Identifier,
|
|
365
|
+
name: "at"
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
tools.F.constTrue
|
|
369
|
+
).otherwise(tools.F.constFalse);
|
|
370
|
+
if (!isAt) return tools.O.none();
|
|
371
|
+
const [index] = n2.arguments;
|
|
372
|
+
if (!index) return tools.O.none();
|
|
373
|
+
const initialScope = context.sourceCode.getScope(n2);
|
|
374
|
+
const value = astUtils.getStaticValue(index, initialScope);
|
|
375
|
+
if (value?.value === 1) return tools.O.fromNullable(n2.callee.object.name);
|
|
376
|
+
return tools.O.none();
|
|
377
|
+
}).otherwise(tools.O.none);
|
|
378
|
+
tools.F.pipe(
|
|
379
|
+
name2,
|
|
380
|
+
tools.O.flatMap(_var.findVariable(scope)),
|
|
381
|
+
tools.O.flatMap(_var.getVariableInit(0)),
|
|
382
|
+
tools.O.filter(ast.is(ast.NodeType.CallExpression)),
|
|
383
|
+
tools.O.filter((name3) => isUseStateCallWithAlias(name3, context)),
|
|
384
|
+
tools.O.map((name3) => ({
|
|
385
|
+
data: {
|
|
386
|
+
setState: name3
|
|
387
|
+
},
|
|
388
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
389
|
+
node
|
|
390
|
+
})),
|
|
391
|
+
tools.O.map(context.report)
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
},
|
|
396
|
+
defaultOptions: []
|
|
397
|
+
});
|
|
398
|
+
var RULE_NAME5 = "prefer-use-state-lazy-initialization";
|
|
320
399
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
321
400
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
322
401
|
meta: {
|
|
@@ -329,7 +408,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
329
408
|
},
|
|
330
409
|
schema: []
|
|
331
410
|
},
|
|
332
|
-
name:
|
|
411
|
+
name: RULE_NAME5,
|
|
333
412
|
create(context) {
|
|
334
413
|
const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
|
|
335
414
|
return {
|
|
@@ -362,6 +441,7 @@ var rules = {
|
|
|
362
441
|
"ensure-custom-hooks-using-other-hooks": ensure_custom_hooks_using_other_hooks_default,
|
|
363
442
|
"ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
|
|
364
443
|
"ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
|
|
444
|
+
"no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
|
|
365
445
|
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
366
446
|
};
|
|
367
447
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall } from '@eslint-react/core';
|
|
1
|
+
import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall } from '@eslint-react/core';
|
|
2
2
|
import { createRuleForPlugin, getESLintReactSettings } from '@eslint-react/shared';
|
|
3
|
-
import { NodeType, is, getNestedCallExpressions } from '@eslint-react/ast';
|
|
3
|
+
import { NodeType, is, traverseUp, getNestedCallExpressions } from '@eslint-react/ast';
|
|
4
4
|
import { F, O } from '@eslint-react/tools';
|
|
5
5
|
import { findVariable, getVariableInit } from '@eslint-react/var';
|
|
6
|
+
import { getStaticValue } from '@typescript-eslint/utils/ast-utils';
|
|
6
7
|
|
|
7
8
|
// package.json
|
|
8
9
|
var name = "eslint-plugin-react-hooks-extra";
|
|
9
|
-
var version = "1.5.
|
|
10
|
+
var version = "1.5.26-next.0";
|
|
10
11
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
11
12
|
|
|
12
13
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -314,7 +315,85 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
314
315
|
},
|
|
315
316
|
defaultOptions: []
|
|
316
317
|
});
|
|
317
|
-
var RULE_NAME4 = "
|
|
318
|
+
var RULE_NAME4 = "no-direct-set-state-in-use-effect";
|
|
319
|
+
var no_direct_set_state_in_use_effect_default = createRule({
|
|
320
|
+
meta: {
|
|
321
|
+
type: "problem",
|
|
322
|
+
docs: {
|
|
323
|
+
description: "disallow direct calls to the 'set' function of 'useState' in 'useEffect'"
|
|
324
|
+
},
|
|
325
|
+
messages: {
|
|
326
|
+
NO_DIRECT_SET_STATE_IN_USE_EFFECT: "Do not call the 'set' function of 'useState' directly in 'useEffect'."
|
|
327
|
+
},
|
|
328
|
+
schema: []
|
|
329
|
+
},
|
|
330
|
+
name: RULE_NAME4,
|
|
331
|
+
create(context) {
|
|
332
|
+
const settings = getESLintReactSettings(context.settings);
|
|
333
|
+
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
|
|
334
|
+
function isUseEffectCallWithAlias(node, context2) {
|
|
335
|
+
return isUseEffectCall(node, context2) || useEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
|
|
336
|
+
}
|
|
337
|
+
function isUseStateCallWithAlias(node, context2) {
|
|
338
|
+
return isUseStateCall(node, context2) || useStateAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
|
|
339
|
+
}
|
|
340
|
+
function isEffectFunction(node) {
|
|
341
|
+
return node.parent?.type === NodeType.CallExpression && isUseEffectCallWithAlias(node.parent, context);
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
CallExpression(node) {
|
|
345
|
+
const effectFunction = traverseUp(node, isEffectFunction);
|
|
346
|
+
if (O.isNone(effectFunction)) return;
|
|
347
|
+
const scope = context.sourceCode.getScope(node);
|
|
348
|
+
if (scope.block !== effectFunction.value) return;
|
|
349
|
+
const name2 = $(node.callee).with({ type: NodeType.Identifier }, (n2) => O.some(n2.name)).with({ type: NodeType.MemberExpression }, (n2) => {
|
|
350
|
+
if (!("name" in n2.object)) return O.none();
|
|
351
|
+
const initialScope = context.sourceCode.getScope(n2);
|
|
352
|
+
const property = getStaticValue(n2.property, initialScope);
|
|
353
|
+
if (property?.value === 1) return O.fromNullable(n2.object.name);
|
|
354
|
+
return O.none();
|
|
355
|
+
}).with({ type: NodeType.CallExpression }, (n2) => {
|
|
356
|
+
if (!is(NodeType.MemberExpression)(n2.callee)) return O.none();
|
|
357
|
+
if (!("name" in n2.callee.object)) return O.none();
|
|
358
|
+
const isAt = $(n2.callee).with(
|
|
359
|
+
{
|
|
360
|
+
type: NodeType.MemberExpression,
|
|
361
|
+
property: {
|
|
362
|
+
type: NodeType.Identifier,
|
|
363
|
+
name: "at"
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
F.constTrue
|
|
367
|
+
).otherwise(F.constFalse);
|
|
368
|
+
if (!isAt) return O.none();
|
|
369
|
+
const [index] = n2.arguments;
|
|
370
|
+
if (!index) return O.none();
|
|
371
|
+
const initialScope = context.sourceCode.getScope(n2);
|
|
372
|
+
const value = getStaticValue(index, initialScope);
|
|
373
|
+
if (value?.value === 1) return O.fromNullable(n2.callee.object.name);
|
|
374
|
+
return O.none();
|
|
375
|
+
}).otherwise(O.none);
|
|
376
|
+
F.pipe(
|
|
377
|
+
name2,
|
|
378
|
+
O.flatMap(findVariable(scope)),
|
|
379
|
+
O.flatMap(getVariableInit(0)),
|
|
380
|
+
O.filter(is(NodeType.CallExpression)),
|
|
381
|
+
O.filter((name3) => isUseStateCallWithAlias(name3, context)),
|
|
382
|
+
O.map((name3) => ({
|
|
383
|
+
data: {
|
|
384
|
+
setState: name3
|
|
385
|
+
},
|
|
386
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
387
|
+
node
|
|
388
|
+
})),
|
|
389
|
+
O.map(context.report)
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
},
|
|
394
|
+
defaultOptions: []
|
|
395
|
+
});
|
|
396
|
+
var RULE_NAME5 = "prefer-use-state-lazy-initialization";
|
|
318
397
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
319
398
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
320
399
|
meta: {
|
|
@@ -327,7 +406,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
327
406
|
},
|
|
328
407
|
schema: []
|
|
329
408
|
},
|
|
330
|
-
name:
|
|
409
|
+
name: RULE_NAME5,
|
|
331
410
|
create(context) {
|
|
332
411
|
const alias = getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
|
|
333
412
|
return {
|
|
@@ -360,6 +439,7 @@ var rules = {
|
|
|
360
439
|
"ensure-custom-hooks-using-other-hooks": ensure_custom_hooks_using_other_hooks_default,
|
|
361
440
|
"ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
|
|
362
441
|
"ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
|
|
442
|
+
"no-direct-set-state-in-use-effect": no_direct_set_state_in_use_effect_default,
|
|
363
443
|
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
364
444
|
};
|
|
365
445
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks-extra",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.26-next.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,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/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/jsx": "1.5.
|
|
45
|
-
"@eslint-react/tools": "1.5.
|
|
46
|
-
"@eslint-react/
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/var": "1.5.
|
|
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"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"string-ts": "2.2.0",
|