eslint-plugin-react-hooks-extra 1.5.25 → 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 -7
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +162 -3
- package/dist/index.mjs +164 -5
- 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,9 +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
|
-
| `
|
|
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
|
@@ -8,6 +8,8 @@ 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>;
|
|
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>;
|
|
11
13
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
14
|
};
|
|
13
15
|
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ 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>;
|
|
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>;
|
|
11
13
|
readonly "prefer-use-state-lazy-initialization": _typescript_eslint_utils_ts_eslint.RuleModule<"PREFER_USE_STATE_LAZY_INITIALIZATION", [], _typescript_eslint_utils_ts_eslint.RuleListener>;
|
|
12
14
|
};
|
|
13
15
|
|
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.1";
|
|
12
13
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
13
14
|
|
|
14
15
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -316,7 +317,163 @@ 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 = "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";
|
|
320
477
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
321
478
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
322
479
|
meta: {
|
|
@@ -329,7 +486,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
329
486
|
},
|
|
330
487
|
schema: []
|
|
331
488
|
},
|
|
332
|
-
name:
|
|
489
|
+
name: RULE_NAME6,
|
|
333
490
|
create(context) {
|
|
334
491
|
const alias = shared.getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
|
|
335
492
|
return {
|
|
@@ -362,6 +519,8 @@ var rules = {
|
|
|
362
519
|
"ensure-custom-hooks-using-other-hooks": ensure_custom_hooks_using_other_hooks_default,
|
|
363
520
|
"ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
|
|
364
521
|
"ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
|
|
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,
|
|
365
524
|
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
366
525
|
};
|
|
367
526
|
|
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, isUseLayoutEffectCall } 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.1";
|
|
10
11
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
11
12
|
|
|
12
13
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -314,7 +315,163 @@ 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 = "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";
|
|
318
475
|
var ALLOW_LIST = ["Boolean", "String", "Number"];
|
|
319
476
|
var prefer_use_state_lazy_initialization_default = createRule({
|
|
320
477
|
meta: {
|
|
@@ -327,7 +484,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
327
484
|
},
|
|
328
485
|
schema: []
|
|
329
486
|
},
|
|
330
|
-
name:
|
|
487
|
+
name: RULE_NAME6,
|
|
331
488
|
create(context) {
|
|
332
489
|
const alias = getESLintReactSettings(context.settings).additionalHooks?.useState ?? [];
|
|
333
490
|
return {
|
|
@@ -360,6 +517,8 @@ var rules = {
|
|
|
360
517
|
"ensure-custom-hooks-using-other-hooks": ensure_custom_hooks_using_other_hooks_default,
|
|
361
518
|
"ensure-use-callback-has-non-empty-deps": ensure_use_callback_has_non_empty_deps_default,
|
|
362
519
|
"ensure-use-memo-has-non-empty-deps": ensure_use_memo_has_non_empty_deps_default,
|
|
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,
|
|
363
522
|
"prefer-use-state-lazy-initialization": prefer_use_state_lazy_initialization_default
|
|
364
523
|
};
|
|
365
524
|
|
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.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/
|
|
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",
|