eslint-plugin-react-hooks-extra 1.5.28 → 1.5.29-beta.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/dist/index.js +81 -59
- package/dist/index.mjs +85 -63
- package/package.json +13 -13
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.
|
|
12
|
+
var version = "1.5.29-beta.1";
|
|
13
13
|
var createRule = shared.createRuleForPlugin("hooks-extra");
|
|
14
14
|
|
|
15
15
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -246,7 +246,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
246
246
|
},
|
|
247
247
|
name: RULE_NAME2,
|
|
248
248
|
create(context) {
|
|
249
|
-
const alias = shared.
|
|
249
|
+
const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useCallback ?? [];
|
|
250
250
|
return {
|
|
251
251
|
CallExpression(node) {
|
|
252
252
|
if (!core.isReactHookCall(node)) return;
|
|
@@ -269,7 +269,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
269
269
|
$(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
|
|
270
270
|
return tools.F.pipe(
|
|
271
271
|
_var.findVariable(n2.name, initialScope),
|
|
272
|
-
tools.O.flatMap(_var.
|
|
272
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
273
273
|
tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
|
|
274
274
|
);
|
|
275
275
|
}).otherwise(tools.O.none),
|
|
@@ -292,7 +292,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
292
292
|
}).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
|
|
293
293
|
return tools.F.pipe(
|
|
294
294
|
_var.findVariable(n2.name, initialScope),
|
|
295
|
-
tools.O.flatMap(_var.
|
|
295
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
296
296
|
tools.O.filter(ast.isFunction)
|
|
297
297
|
);
|
|
298
298
|
}).otherwise(tools.O.none),
|
|
@@ -324,7 +324,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
324
324
|
},
|
|
325
325
|
name: RULE_NAME3,
|
|
326
326
|
create(context) {
|
|
327
|
-
const alias = shared.
|
|
327
|
+
const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useMemo ?? [];
|
|
328
328
|
return {
|
|
329
329
|
CallExpression(node) {
|
|
330
330
|
if (!core.isReactHookCall(node)) return;
|
|
@@ -347,7 +347,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
347
347
|
$(deps).with({ type: ast.NodeType.ArrayExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
|
|
348
348
|
return tools.F.pipe(
|
|
349
349
|
_var.findVariable(n2.name, initialScope),
|
|
350
|
-
tools.O.flatMap(_var.
|
|
350
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
351
351
|
tools.O.filter(ast.is(ast.NodeType.ArrayExpression))
|
|
352
352
|
);
|
|
353
353
|
}).otherwise(tools.O.none),
|
|
@@ -370,7 +370,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
370
370
|
}).with({ type: ast.NodeType.FunctionExpression }, tools.O.some).with({ type: ast.NodeType.Identifier }, (n2) => {
|
|
371
371
|
return tools.F.pipe(
|
|
372
372
|
_var.findVariable(n2.name, initialScope),
|
|
373
|
-
tools.O.flatMap(_var.
|
|
373
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
374
374
|
tools.O.filter(ast.isFunction)
|
|
375
375
|
);
|
|
376
376
|
}).otherwise(tools.O.none),
|
|
@@ -402,8 +402,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
402
402
|
},
|
|
403
403
|
name: RULE_NAME4,
|
|
404
404
|
create(context) {
|
|
405
|
-
const settings = shared.
|
|
406
|
-
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings
|
|
405
|
+
const settings = shared.parseESLintSettings(context.settings)["react-x"];
|
|
406
|
+
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings?.additionalHooks ?? {};
|
|
407
407
|
function isUseEffectCallWithAlias(node) {
|
|
408
408
|
return core.isUseEffectCall(node, context) || useEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
|
|
409
409
|
}
|
|
@@ -422,7 +422,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
422
422
|
object: { type: ast.NodeType.Identifier }
|
|
423
423
|
}, (n2) => tools.O.some(n2.object.name)).otherwise(tools.O.none),
|
|
424
424
|
tools.O.flatMap(_var.findVariable(context.sourceCode.getScope(id))),
|
|
425
|
-
tools.O.flatMap(_var.
|
|
425
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
426
426
|
tools.O.filter(ast.is(ast.NodeType.CallExpression)),
|
|
427
427
|
tools.O.exists(isUseStateCallWithAlias)
|
|
428
428
|
);
|
|
@@ -466,24 +466,27 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
466
466
|
function getFunctionKind(node) {
|
|
467
467
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
|
|
468
468
|
}
|
|
469
|
-
const useEffectCallRef = tools.MutRef.make(null);
|
|
470
469
|
const functionStack = tools.MutList.make();
|
|
471
470
|
const effectFunctionRef = tools.MutRef.make(null);
|
|
472
|
-
const
|
|
471
|
+
const effectFunctionIdentifiers = tools.MutRef.make(tools.Chunk.empty());
|
|
473
472
|
const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
|
|
474
|
-
const indirectSetStateCalls = /* @__PURE__ */ new
|
|
473
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
474
|
+
const onEffectFunctionEnter = (node) => {
|
|
475
|
+
tools.MutRef.set(effectFunctionRef, node);
|
|
476
|
+
};
|
|
477
|
+
const onEffectFunctionExit = (node) => {
|
|
478
|
+
tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
|
|
479
|
+
};
|
|
475
480
|
return {
|
|
476
481
|
":function"(node) {
|
|
477
482
|
const functionKind = getFunctionKind(node);
|
|
478
483
|
tools.MutList.append(functionStack, [node, functionKind]);
|
|
479
484
|
$(functionKind).with("effect", () => {
|
|
480
|
-
|
|
481
|
-
}).with("cleanup", () => {
|
|
482
|
-
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
485
|
+
onEffectFunctionEnter(node);
|
|
483
486
|
}).otherwise(tools.F.constVoid);
|
|
484
487
|
},
|
|
485
488
|
":function:exit"(node) {
|
|
486
|
-
|
|
489
|
+
onEffectFunctionExit(node);
|
|
487
490
|
tools.MutList.pop(functionStack);
|
|
488
491
|
},
|
|
489
492
|
CallExpression(node) {
|
|
@@ -503,35 +506,43 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
503
506
|
node
|
|
504
507
|
});
|
|
505
508
|
}).with("useEffect", () => {
|
|
506
|
-
|
|
509
|
+
if (node.arguments.every(ast.isFunction)) return;
|
|
510
|
+
const identifiers = ast.getNestedIdentifiers(node);
|
|
511
|
+
tools.MutRef.update(effectFunctionIdentifiers, tools.Chunk.appendAll(tools.Chunk.unsafeFromArray(identifiers)));
|
|
507
512
|
}).with("other", () => {
|
|
508
513
|
const isInEffectFunction = effectFn === parentFn;
|
|
509
514
|
if (!isInEffectFunction) return;
|
|
510
515
|
tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
|
|
511
516
|
}).otherwise(tools.F.constVoid);
|
|
512
517
|
},
|
|
513
|
-
"CallExpression:exit"(node) {
|
|
514
|
-
if (tools.MutRef.get(useEffectCallRef) === node) {
|
|
515
|
-
tools.MutRef.set(useEffectCallRef, null);
|
|
516
|
-
}
|
|
517
|
-
},
|
|
518
518
|
"Program:exit"() {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
_var.findVariable(name2, context.sourceCode.getScope(call)),
|
|
524
|
-
tools.O.flatMap(_var.getVariableInit(0)),
|
|
519
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
520
|
+
return tools.F.pipe(
|
|
521
|
+
_var.findVariable(id, initialScope),
|
|
522
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
525
523
|
tools.O.filter(ast.isFunction),
|
|
526
|
-
tools.O.flatMapNullable((
|
|
524
|
+
tools.O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
|
|
527
525
|
tools.O.map(tools.Chunk.toReadonlyArray),
|
|
528
526
|
tools.O.getOrElse(() => [])
|
|
529
527
|
);
|
|
528
|
+
};
|
|
529
|
+
for (const { callee } of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
|
|
530
|
+
if (!("name" in callee)) continue;
|
|
531
|
+
const { name: name2 } = callee;
|
|
532
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
533
|
+
for (const setStateCall of setStateCalls) {
|
|
534
|
+
context.report({
|
|
535
|
+
data: { name: name2 },
|
|
536
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
537
|
+
node: setStateCall
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
for (const id of tools.Chunk.toReadonlyArray(tools.MutRef.get(effectFunctionIdentifiers))) {
|
|
542
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
530
543
|
for (const setStateCall of setStateCalls) {
|
|
531
544
|
context.report({
|
|
532
|
-
data: {
|
|
533
|
-
name: name2
|
|
534
|
-
},
|
|
545
|
+
data: { name: id.name },
|
|
535
546
|
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
536
547
|
node: setStateCall
|
|
537
548
|
});
|
|
@@ -556,7 +567,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
556
567
|
},
|
|
557
568
|
name: RULE_NAME5,
|
|
558
569
|
create(context) {
|
|
559
|
-
const settings = shared.
|
|
570
|
+
const settings = shared.parseESLintSettings(context.settings)["react-x"] ?? {};
|
|
560
571
|
const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
|
|
561
572
|
function isUseLayoutEffectCallWithAlias(node) {
|
|
562
573
|
return core.isUseLayoutEffectCall(node, context) || useLayoutEffectAlias.some(tools.F.flip(core.isReactHookCallWithNameLoose)(node));
|
|
@@ -576,7 +587,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
576
587
|
object: { type: ast.NodeType.Identifier }
|
|
577
588
|
}, (n2) => tools.O.some(n2.object.name)).otherwise(tools.O.none),
|
|
578
589
|
tools.O.flatMap(_var.findVariable(context.sourceCode.getScope(id))),
|
|
579
|
-
tools.O.flatMap(_var.
|
|
590
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
580
591
|
tools.O.filter(ast.is(ast.NodeType.CallExpression)),
|
|
581
592
|
tools.O.exists(isUseStateCallWithAlias)
|
|
582
593
|
);
|
|
@@ -620,24 +631,27 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
620
631
|
function getFunctionKind(node) {
|
|
621
632
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(ast.isIIFE, () => "immediate").otherwise(() => "other");
|
|
622
633
|
}
|
|
623
|
-
const useLayoutEffectCallRef = tools.MutRef.make(null);
|
|
624
634
|
const functionStack = tools.MutList.make();
|
|
625
635
|
const effectFunctionRef = tools.MutRef.make(null);
|
|
626
|
-
const
|
|
636
|
+
const effectFunctionIdentifiers = tools.MutRef.make(tools.Chunk.empty());
|
|
627
637
|
const indirectFunctionCalls = tools.MutRef.make(tools.Chunk.empty());
|
|
628
|
-
const indirectSetStateCalls = /* @__PURE__ */ new
|
|
638
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
639
|
+
const onEffectFunctionEnter = (node) => {
|
|
640
|
+
tools.MutRef.set(effectFunctionRef, node);
|
|
641
|
+
};
|
|
642
|
+
const onEffectFunctionExit = (node) => {
|
|
643
|
+
tools.MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
|
|
644
|
+
};
|
|
629
645
|
return {
|
|
630
646
|
":function"(node) {
|
|
631
647
|
const functionKind = getFunctionKind(node);
|
|
632
648
|
tools.MutList.append(functionStack, [node, functionKind]);
|
|
633
649
|
$(functionKind).with("effect", () => {
|
|
634
|
-
|
|
635
|
-
}).with("cleanup", () => {
|
|
636
|
-
tools.MutRef.set(cleanUpFunctionRef, node);
|
|
650
|
+
onEffectFunctionEnter(node);
|
|
637
651
|
}).otherwise(tools.F.constVoid);
|
|
638
652
|
},
|
|
639
653
|
":function:exit"(node) {
|
|
640
|
-
|
|
654
|
+
onEffectFunctionExit(node);
|
|
641
655
|
tools.MutList.pop(functionStack);
|
|
642
656
|
},
|
|
643
657
|
CallExpression(node) {
|
|
@@ -657,35 +671,43 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
657
671
|
node
|
|
658
672
|
});
|
|
659
673
|
}).with("useLayoutEffect", () => {
|
|
660
|
-
|
|
674
|
+
if (node.arguments.every(ast.isFunction)) return;
|
|
675
|
+
const identifiers = ast.getNestedIdentifiers(node);
|
|
676
|
+
tools.MutRef.update(effectFunctionIdentifiers, tools.Chunk.appendAll(tools.Chunk.unsafeFromArray(identifiers)));
|
|
661
677
|
}).with("other", () => {
|
|
662
678
|
const isInEffectFunction = effectFn === parentFn;
|
|
663
679
|
if (!isInEffectFunction) return;
|
|
664
680
|
tools.MutRef.update(indirectFunctionCalls, tools.Chunk.append(node));
|
|
665
681
|
}).otherwise(tools.F.constVoid);
|
|
666
682
|
},
|
|
667
|
-
"CallExpression:exit"(node) {
|
|
668
|
-
if (tools.MutRef.get(useLayoutEffectCallRef) === node) {
|
|
669
|
-
tools.MutRef.set(useLayoutEffectCallRef, null);
|
|
670
|
-
}
|
|
671
|
-
},
|
|
672
683
|
"Program:exit"() {
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
_var.findVariable(name2, context.sourceCode.getScope(call)),
|
|
678
|
-
tools.O.flatMap(_var.getVariableInit(0)),
|
|
684
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
685
|
+
return tools.F.pipe(
|
|
686
|
+
_var.findVariable(id, initialScope),
|
|
687
|
+
tools.O.flatMap(_var.getVariableNode(0)),
|
|
679
688
|
tools.O.filter(ast.isFunction),
|
|
680
|
-
tools.O.flatMapNullable((
|
|
689
|
+
tools.O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
|
|
681
690
|
tools.O.map(tools.Chunk.toReadonlyArray),
|
|
682
691
|
tools.O.getOrElse(() => [])
|
|
683
692
|
);
|
|
693
|
+
};
|
|
694
|
+
for (const { callee } of tools.Chunk.toReadonlyArray(tools.MutRef.get(indirectFunctionCalls))) {
|
|
695
|
+
if (!("name" in callee)) continue;
|
|
696
|
+
const { name: name2 } = callee;
|
|
697
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
698
|
+
for (const setStateCall of setStateCalls) {
|
|
699
|
+
context.report({
|
|
700
|
+
data: { name: name2 },
|
|
701
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
702
|
+
node: setStateCall
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
for (const id of tools.Chunk.toReadonlyArray(tools.MutRef.get(effectFunctionIdentifiers))) {
|
|
707
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
684
708
|
for (const setStateCall of setStateCalls) {
|
|
685
709
|
context.report({
|
|
686
|
-
data: {
|
|
687
|
-
name: name2
|
|
688
|
-
},
|
|
710
|
+
data: { name: id.name },
|
|
689
711
|
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
690
712
|
node: setStateCall
|
|
691
713
|
});
|
|
@@ -711,7 +733,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
711
733
|
},
|
|
712
734
|
name: RULE_NAME6,
|
|
713
735
|
create(context) {
|
|
714
|
-
const alias = shared.
|
|
736
|
+
const alias = shared.parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useState ?? [];
|
|
715
737
|
return {
|
|
716
738
|
CallExpression(node) {
|
|
717
739
|
if (!core.isReactHookCall(node)) return;
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useHookCollector, isReactHookCall, isUseCallbackCall, isReactHookCallWithNameLoose, isUseMemoCall, isUseStateCall, isUseEffectCall, isUseLayoutEffectCall } from '@eslint-react/core';
|
|
2
|
-
import { createRuleForPlugin,
|
|
3
|
-
import { isFunction, NodeType, is, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
|
|
4
|
-
import { F, O,
|
|
5
|
-
import { findVariable,
|
|
2
|
+
import { createRuleForPlugin, parseESLintSettings } from '@eslint-react/shared';
|
|
3
|
+
import { isFunction, NodeType, is, getNestedIdentifiers, getNestedCallExpressions, isIIFE } from '@eslint-react/ast';
|
|
4
|
+
import { F, O, MutList, MutRef, Chunk } from '@eslint-react/tools';
|
|
5
|
+
import { findVariable, getVariableNode } from '@eslint-react/var';
|
|
6
6
|
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.
|
|
10
|
+
var version = "1.5.29-beta.1";
|
|
11
11
|
var createRule = createRuleForPlugin("hooks-extra");
|
|
12
12
|
|
|
13
13
|
// src/rules/ensure-custom-hooks-using-other-hooks.ts
|
|
@@ -244,7 +244,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
244
244
|
},
|
|
245
245
|
name: RULE_NAME2,
|
|
246
246
|
create(context) {
|
|
247
|
-
const alias =
|
|
247
|
+
const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useCallback ?? [];
|
|
248
248
|
return {
|
|
249
249
|
CallExpression(node) {
|
|
250
250
|
if (!isReactHookCall(node)) return;
|
|
@@ -267,7 +267,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
267
267
|
$(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
|
|
268
268
|
return F.pipe(
|
|
269
269
|
findVariable(n2.name, initialScope),
|
|
270
|
-
O.flatMap(
|
|
270
|
+
O.flatMap(getVariableNode(0)),
|
|
271
271
|
O.filter(is(NodeType.ArrayExpression))
|
|
272
272
|
);
|
|
273
273
|
}).otherwise(O.none),
|
|
@@ -290,7 +290,7 @@ var ensure_use_callback_has_non_empty_deps_default = createRule({
|
|
|
290
290
|
}).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
|
|
291
291
|
return F.pipe(
|
|
292
292
|
findVariable(n2.name, initialScope),
|
|
293
|
-
O.flatMap(
|
|
293
|
+
O.flatMap(getVariableNode(0)),
|
|
294
294
|
O.filter(isFunction)
|
|
295
295
|
);
|
|
296
296
|
}).otherwise(O.none),
|
|
@@ -322,7 +322,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
322
322
|
},
|
|
323
323
|
name: RULE_NAME3,
|
|
324
324
|
create(context) {
|
|
325
|
-
const alias =
|
|
325
|
+
const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useMemo ?? [];
|
|
326
326
|
return {
|
|
327
327
|
CallExpression(node) {
|
|
328
328
|
if (!isReactHookCall(node)) return;
|
|
@@ -345,7 +345,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
345
345
|
$(deps).with({ type: NodeType.ArrayExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
|
|
346
346
|
return F.pipe(
|
|
347
347
|
findVariable(n2.name, initialScope),
|
|
348
|
-
O.flatMap(
|
|
348
|
+
O.flatMap(getVariableNode(0)),
|
|
349
349
|
O.filter(is(NodeType.ArrayExpression))
|
|
350
350
|
);
|
|
351
351
|
}).otherwise(O.none),
|
|
@@ -368,7 +368,7 @@ var ensure_use_memo_has_non_empty_deps_default = createRule({
|
|
|
368
368
|
}).with({ type: NodeType.FunctionExpression }, O.some).with({ type: NodeType.Identifier }, (n2) => {
|
|
369
369
|
return F.pipe(
|
|
370
370
|
findVariable(n2.name, initialScope),
|
|
371
|
-
O.flatMap(
|
|
371
|
+
O.flatMap(getVariableNode(0)),
|
|
372
372
|
O.filter(isFunction)
|
|
373
373
|
);
|
|
374
374
|
}).otherwise(O.none),
|
|
@@ -400,8 +400,8 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
400
400
|
},
|
|
401
401
|
name: RULE_NAME4,
|
|
402
402
|
create(context) {
|
|
403
|
-
const settings =
|
|
404
|
-
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings
|
|
403
|
+
const settings = parseESLintSettings(context.settings)["react-x"];
|
|
404
|
+
const { useEffect: useEffectAlias = [], useState: useStateAlias = [] } = settings?.additionalHooks ?? {};
|
|
405
405
|
function isUseEffectCallWithAlias(node) {
|
|
406
406
|
return isUseEffectCall(node, context) || useEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
|
|
407
407
|
}
|
|
@@ -420,7 +420,7 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
420
420
|
object: { type: NodeType.Identifier }
|
|
421
421
|
}, (n2) => O.some(n2.object.name)).otherwise(O.none),
|
|
422
422
|
O.flatMap(findVariable(context.sourceCode.getScope(id))),
|
|
423
|
-
O.flatMap(
|
|
423
|
+
O.flatMap(getVariableNode(0)),
|
|
424
424
|
O.filter(is(NodeType.CallExpression)),
|
|
425
425
|
O.exists(isUseStateCallWithAlias)
|
|
426
426
|
);
|
|
@@ -464,24 +464,27 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
464
464
|
function getFunctionKind(node) {
|
|
465
465
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
|
|
466
466
|
}
|
|
467
|
-
const useEffectCallRef = MutRef.make(null);
|
|
468
467
|
const functionStack = MutList.make();
|
|
469
468
|
const effectFunctionRef = MutRef.make(null);
|
|
470
|
-
const
|
|
469
|
+
const effectFunctionIdentifiers = MutRef.make(Chunk.empty());
|
|
471
470
|
const indirectFunctionCalls = MutRef.make(Chunk.empty());
|
|
472
|
-
const indirectSetStateCalls = /* @__PURE__ */ new
|
|
471
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
472
|
+
const onEffectFunctionEnter = (node) => {
|
|
473
|
+
MutRef.set(effectFunctionRef, node);
|
|
474
|
+
};
|
|
475
|
+
const onEffectFunctionExit = (node) => {
|
|
476
|
+
MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
|
|
477
|
+
};
|
|
473
478
|
return {
|
|
474
479
|
":function"(node) {
|
|
475
480
|
const functionKind = getFunctionKind(node);
|
|
476
481
|
MutList.append(functionStack, [node, functionKind]);
|
|
477
482
|
$(functionKind).with("effect", () => {
|
|
478
|
-
|
|
479
|
-
}).with("cleanup", () => {
|
|
480
|
-
MutRef.set(cleanUpFunctionRef, node);
|
|
483
|
+
onEffectFunctionEnter(node);
|
|
481
484
|
}).otherwise(F.constVoid);
|
|
482
485
|
},
|
|
483
486
|
":function:exit"(node) {
|
|
484
|
-
|
|
487
|
+
onEffectFunctionExit(node);
|
|
485
488
|
MutList.pop(functionStack);
|
|
486
489
|
},
|
|
487
490
|
CallExpression(node) {
|
|
@@ -501,35 +504,43 @@ var no_direct_set_state_in_use_effect_default = createRule({
|
|
|
501
504
|
node
|
|
502
505
|
});
|
|
503
506
|
}).with("useEffect", () => {
|
|
504
|
-
|
|
507
|
+
if (node.arguments.every(isFunction)) return;
|
|
508
|
+
const identifiers = getNestedIdentifiers(node);
|
|
509
|
+
MutRef.update(effectFunctionIdentifiers, Chunk.appendAll(Chunk.unsafeFromArray(identifiers)));
|
|
505
510
|
}).with("other", () => {
|
|
506
511
|
const isInEffectFunction = effectFn === parentFn;
|
|
507
512
|
if (!isInEffectFunction) return;
|
|
508
513
|
MutRef.update(indirectFunctionCalls, Chunk.append(node));
|
|
509
514
|
}).otherwise(F.constVoid);
|
|
510
515
|
},
|
|
511
|
-
"CallExpression:exit"(node) {
|
|
512
|
-
if (MutRef.get(useEffectCallRef) === node) {
|
|
513
|
-
MutRef.set(useEffectCallRef, null);
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
516
|
"Program:exit"() {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
findVariable(name2, context.sourceCode.getScope(call)),
|
|
522
|
-
O.flatMap(getVariableInit(0)),
|
|
517
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
518
|
+
return F.pipe(
|
|
519
|
+
findVariable(id, initialScope),
|
|
520
|
+
O.flatMap(getVariableNode(0)),
|
|
523
521
|
O.filter(isFunction),
|
|
524
|
-
O.flatMapNullable((
|
|
522
|
+
O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
|
|
525
523
|
O.map(Chunk.toReadonlyArray),
|
|
526
524
|
O.getOrElse(() => [])
|
|
527
525
|
);
|
|
526
|
+
};
|
|
527
|
+
for (const { callee } of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
|
|
528
|
+
if (!("name" in callee)) continue;
|
|
529
|
+
const { name: name2 } = callee;
|
|
530
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
531
|
+
for (const setStateCall of setStateCalls) {
|
|
532
|
+
context.report({
|
|
533
|
+
data: { name: name2 },
|
|
534
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
535
|
+
node: setStateCall
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
for (const id of Chunk.toReadonlyArray(MutRef.get(effectFunctionIdentifiers))) {
|
|
540
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
528
541
|
for (const setStateCall of setStateCalls) {
|
|
529
542
|
context.report({
|
|
530
|
-
data: {
|
|
531
|
-
name: name2
|
|
532
|
-
},
|
|
543
|
+
data: { name: id.name },
|
|
533
544
|
messageId: "NO_DIRECT_SET_STATE_IN_USE_EFFECT",
|
|
534
545
|
node: setStateCall
|
|
535
546
|
});
|
|
@@ -554,7 +565,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
554
565
|
},
|
|
555
566
|
name: RULE_NAME5,
|
|
556
567
|
create(context) {
|
|
557
|
-
const settings =
|
|
568
|
+
const settings = parseESLintSettings(context.settings)["react-x"] ?? {};
|
|
558
569
|
const { useLayoutEffect: useLayoutEffectAlias = [], useState: useStateAlias = [] } = settings.additionalHooks ?? {};
|
|
559
570
|
function isUseLayoutEffectCallWithAlias(node) {
|
|
560
571
|
return isUseLayoutEffectCall(node, context) || useLayoutEffectAlias.some(F.flip(isReactHookCallWithNameLoose)(node));
|
|
@@ -574,7 +585,7 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
574
585
|
object: { type: NodeType.Identifier }
|
|
575
586
|
}, (n2) => O.some(n2.object.name)).otherwise(O.none),
|
|
576
587
|
O.flatMap(findVariable(context.sourceCode.getScope(id))),
|
|
577
|
-
O.flatMap(
|
|
588
|
+
O.flatMap(getVariableNode(0)),
|
|
578
589
|
O.filter(is(NodeType.CallExpression)),
|
|
579
590
|
O.exists(isUseStateCallWithAlias)
|
|
580
591
|
);
|
|
@@ -618,24 +629,27 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
618
629
|
function getFunctionKind(node) {
|
|
619
630
|
return $(node).when(isEffectFunction, () => "effect").when(isCleanUpFunction, () => "cleanup").when(isIIFE, () => "immediate").otherwise(() => "other");
|
|
620
631
|
}
|
|
621
|
-
const useLayoutEffectCallRef = MutRef.make(null);
|
|
622
632
|
const functionStack = MutList.make();
|
|
623
633
|
const effectFunctionRef = MutRef.make(null);
|
|
624
|
-
const
|
|
634
|
+
const effectFunctionIdentifiers = MutRef.make(Chunk.empty());
|
|
625
635
|
const indirectFunctionCalls = MutRef.make(Chunk.empty());
|
|
626
|
-
const indirectSetStateCalls = /* @__PURE__ */ new
|
|
636
|
+
const indirectSetStateCalls = /* @__PURE__ */ new WeakMap();
|
|
637
|
+
const onEffectFunctionEnter = (node) => {
|
|
638
|
+
MutRef.set(effectFunctionRef, node);
|
|
639
|
+
};
|
|
640
|
+
const onEffectFunctionExit = (node) => {
|
|
641
|
+
MutRef.update(effectFunctionRef, (current) => current === node ? null : current);
|
|
642
|
+
};
|
|
627
643
|
return {
|
|
628
644
|
":function"(node) {
|
|
629
645
|
const functionKind = getFunctionKind(node);
|
|
630
646
|
MutList.append(functionStack, [node, functionKind]);
|
|
631
647
|
$(functionKind).with("effect", () => {
|
|
632
|
-
|
|
633
|
-
}).with("cleanup", () => {
|
|
634
|
-
MutRef.set(cleanUpFunctionRef, node);
|
|
648
|
+
onEffectFunctionEnter(node);
|
|
635
649
|
}).otherwise(F.constVoid);
|
|
636
650
|
},
|
|
637
651
|
":function:exit"(node) {
|
|
638
|
-
|
|
652
|
+
onEffectFunctionExit(node);
|
|
639
653
|
MutList.pop(functionStack);
|
|
640
654
|
},
|
|
641
655
|
CallExpression(node) {
|
|
@@ -655,35 +669,43 @@ var no_direct_set_state_in_use_layout_effect_default = createRule({
|
|
|
655
669
|
node
|
|
656
670
|
});
|
|
657
671
|
}).with("useLayoutEffect", () => {
|
|
658
|
-
|
|
672
|
+
if (node.arguments.every(isFunction)) return;
|
|
673
|
+
const identifiers = getNestedIdentifiers(node);
|
|
674
|
+
MutRef.update(effectFunctionIdentifiers, Chunk.appendAll(Chunk.unsafeFromArray(identifiers)));
|
|
659
675
|
}).with("other", () => {
|
|
660
676
|
const isInEffectFunction = effectFn === parentFn;
|
|
661
677
|
if (!isInEffectFunction) return;
|
|
662
678
|
MutRef.update(indirectFunctionCalls, Chunk.append(node));
|
|
663
679
|
}).otherwise(F.constVoid);
|
|
664
680
|
},
|
|
665
|
-
"CallExpression:exit"(node) {
|
|
666
|
-
if (MutRef.get(useLayoutEffectCallRef) === node) {
|
|
667
|
-
MutRef.set(useLayoutEffectCallRef, null);
|
|
668
|
-
}
|
|
669
|
-
},
|
|
670
681
|
"Program:exit"() {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
findVariable(name2, context.sourceCode.getScope(call)),
|
|
676
|
-
O.flatMap(getVariableInit(0)),
|
|
682
|
+
const getSetStateCalls = (id, initialScope) => {
|
|
683
|
+
return F.pipe(
|
|
684
|
+
findVariable(id, initialScope),
|
|
685
|
+
O.flatMap(getVariableNode(0)),
|
|
677
686
|
O.filter(isFunction),
|
|
678
|
-
O.flatMapNullable((
|
|
687
|
+
O.flatMapNullable((fn) => indirectSetStateCalls.get(fn)),
|
|
679
688
|
O.map(Chunk.toReadonlyArray),
|
|
680
689
|
O.getOrElse(() => [])
|
|
681
690
|
);
|
|
691
|
+
};
|
|
692
|
+
for (const { callee } of Chunk.toReadonlyArray(MutRef.get(indirectFunctionCalls))) {
|
|
693
|
+
if (!("name" in callee)) continue;
|
|
694
|
+
const { name: name2 } = callee;
|
|
695
|
+
const setStateCalls = getSetStateCalls(name2, context.sourceCode.getScope(callee));
|
|
696
|
+
for (const setStateCall of setStateCalls) {
|
|
697
|
+
context.report({
|
|
698
|
+
data: { name: name2 },
|
|
699
|
+
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
700
|
+
node: setStateCall
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
for (const id of Chunk.toReadonlyArray(MutRef.get(effectFunctionIdentifiers))) {
|
|
705
|
+
const setStateCalls = getSetStateCalls(id.name, context.sourceCode.getScope(id));
|
|
682
706
|
for (const setStateCall of setStateCalls) {
|
|
683
707
|
context.report({
|
|
684
|
-
data: {
|
|
685
|
-
name: name2
|
|
686
|
-
},
|
|
708
|
+
data: { name: id.name },
|
|
687
709
|
messageId: "NO_DIRECT_SET_STATE_IN_USE_LAYOUT_EFFECT",
|
|
688
710
|
node: setStateCall
|
|
689
711
|
});
|
|
@@ -709,7 +731,7 @@ var prefer_use_state_lazy_initialization_default = createRule({
|
|
|
709
731
|
},
|
|
710
732
|
name: RULE_NAME6,
|
|
711
733
|
create(context) {
|
|
712
|
-
const alias =
|
|
734
|
+
const alias = parseESLintSettings(context.settings)["react-x"]?.additionalHooks?.useState ?? [];
|
|
713
735
|
return {
|
|
714
736
|
CallExpression(node) {
|
|
715
737
|
if (!isReactHookCall(node)) return;
|
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.29-beta.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": {
|
|
@@ -35,21 +35,21 @@
|
|
|
35
35
|
"./package.json"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@typescript-eslint/scope-manager": "^7.16.1",
|
|
39
|
-
"@typescript-eslint/type-utils": "^7.16.1",
|
|
40
|
-
"@typescript-eslint/types": "^7.16.1",
|
|
41
|
-
"@typescript-eslint/utils": "^7.16.1",
|
|
42
|
-
"@eslint-react/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
46
|
-
"@eslint-react/
|
|
47
|
-
"@eslint-react/
|
|
48
|
-
"@eslint-react/
|
|
38
|
+
"@typescript-eslint/scope-manager": "^7.16.1 || ^rc-v8",
|
|
39
|
+
"@typescript-eslint/type-utils": "^7.16.1 || ^rc-v8",
|
|
40
|
+
"@typescript-eslint/types": "^7.16.1 || ^rc-v8",
|
|
41
|
+
"@typescript-eslint/utils": "^7.16.1 || ^rc-v8",
|
|
42
|
+
"@eslint-react/ast": "1.5.29-beta.1",
|
|
43
|
+
"@eslint-react/shared": "1.5.29-beta.1",
|
|
44
|
+
"@eslint-react/core": "1.5.29-beta.1",
|
|
45
|
+
"@eslint-react/jsx": "1.5.29-beta.1",
|
|
46
|
+
"@eslint-react/tools": "1.5.29-beta.1",
|
|
47
|
+
"@eslint-react/types": "1.5.29-beta.1",
|
|
48
|
+
"@eslint-react/var": "1.5.29-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"string-ts": "2.2.0",
|
|
52
|
-
"tsup": "8.2.
|
|
52
|
+
"tsup": "8.2.1"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"eslint": "^8.57.0 || ^9.0.0",
|