eslint-plugin-react-web-api 5.24.6 → 5.24.7
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 +167 -25
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_ESLINT_REACT_SETTINGS } from "@eslint-react/shared";
|
|
2
2
|
import * as ESLintUtils from "@typescript-eslint/utils/eslint-utils";
|
|
3
3
|
import { Check, Compare, Extract, Traverse } from "@eslint-react/ast";
|
|
4
|
-
import { getFunctionId, isUseEffectCleanupCallback, isUseEffectSetupCallback } from "@eslint-react/core";
|
|
4
|
+
import { getFunctionId, isUseEffectCleanupCallback, isUseEffectSetupCallback, isUseRefLikeCall } from "@eslint-react/core";
|
|
5
5
|
import "@eslint-react/eslint";
|
|
6
6
|
import { isAssignmentTargetEqual, isInitializedFromReactNative, isValueEqual, resolve, resolveEnclosingAssignmentTarget, resolveOrigin } from "@eslint-react/var";
|
|
7
7
|
import { AST_NODE_TYPES } from "@typescript-eslint/types";
|
|
@@ -21,7 +21,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region package.json
|
|
23
23
|
var name$1 = "eslint-plugin-react-web-api";
|
|
24
|
-
var version = "5.24.
|
|
24
|
+
var version = "5.24.7";
|
|
25
25
|
//#endregion
|
|
26
26
|
//#region src/utils/create-rule.ts
|
|
27
27
|
function getDocsUrl(ruleName) {
|
|
@@ -131,6 +131,18 @@ dual(2, (self, predicate) => {
|
|
|
131
131
|
});
|
|
132
132
|
//#endregion
|
|
133
133
|
//#region src/rules/no-leaked-event-listener/lib.ts
|
|
134
|
+
function isSignalPropertyBinding(context, node) {
|
|
135
|
+
const defNode = findVariable(context.sourceCode.getScope(node), node)?.defs.at(-1)?.node;
|
|
136
|
+
if (defNode?.type !== AST_NODE_TYPES.VariableDeclarator || defNode.id.type !== AST_NODE_TYPES.ObjectPattern) return false;
|
|
137
|
+
for (const prop of defNode.id.properties) {
|
|
138
|
+
if (prop.type !== AST_NODE_TYPES.Property || prop.computed) continue;
|
|
139
|
+
const key = Extract.unwrap(prop.key);
|
|
140
|
+
if (key.type !== AST_NODE_TYPES.Identifier || key.name !== "signal") continue;
|
|
141
|
+
const value = Extract.unwrap(prop.value);
|
|
142
|
+
return value.type === AST_NODE_TYPES.Identifier && value.name === node.name;
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
134
146
|
function getSignalValueExpression(context, node) {
|
|
135
147
|
if (node == null) return null;
|
|
136
148
|
switch (node.type) {
|
|
@@ -138,6 +150,10 @@ function getSignalValueExpression(context, node) {
|
|
|
138
150
|
const resolved = resolveOrigin(context, node);
|
|
139
151
|
const unwrapped = resolved == null ? null : Extract.unwrap(resolved);
|
|
140
152
|
if (unwrapped != null && Check.isFunction(unwrapped)) return node;
|
|
153
|
+
if (unwrapped != null && unwrapped.type === AST_NODE_TYPES.NewExpression && isSignalPropertyBinding(context, node)) {
|
|
154
|
+
const newCallee = Extract.unwrap(unwrapped.callee);
|
|
155
|
+
if (newCallee.type === AST_NODE_TYPES.Identifier && newCallee.name === "AbortController") return node;
|
|
156
|
+
}
|
|
141
157
|
return getSignalValueExpression(context, unwrapped);
|
|
142
158
|
}
|
|
143
159
|
case AST_NODE_TYPES.MemberExpression: return node;
|
|
@@ -246,10 +262,15 @@ function create$5(context) {
|
|
|
246
262
|
const aEntries = [];
|
|
247
263
|
const rEntries = [];
|
|
248
264
|
const cleanupCallees = /* @__PURE__ */ new Map();
|
|
265
|
+
function isGlobalObject(node) {
|
|
266
|
+
return Check.isIdentifier(node) && (node.name === "window" || node.name === "globalThis" || node.name === "self");
|
|
267
|
+
}
|
|
249
268
|
function isSameObject(a, b) {
|
|
250
269
|
switch (true) {
|
|
251
270
|
case a.type === AST_NODE_TYPES.MemberExpression && b.type === AST_NODE_TYPES.MemberExpression: return Compare.isEqual(a.object, b.object);
|
|
252
|
-
|
|
271
|
+
case a.type === AST_NODE_TYPES.MemberExpression: return isGlobalObject(a.object);
|
|
272
|
+
case b.type === AST_NODE_TYPES.MemberExpression: return isGlobalObject(b.object);
|
|
273
|
+
default: return true;
|
|
253
274
|
}
|
|
254
275
|
}
|
|
255
276
|
function isInverseEntry(aEntry, rEntry) {
|
|
@@ -365,7 +386,7 @@ function getCallKind$4(node) {
|
|
|
365
386
|
if (name != null && isMatching(P.union("fetch", "abort"))(name)) return name;
|
|
366
387
|
return "other";
|
|
367
388
|
}
|
|
368
|
-
function getControllerFromSignal(context, node) {
|
|
389
|
+
function getControllerFromSignal(context, node, seen = /* @__PURE__ */ new Set()) {
|
|
369
390
|
node = Extract.unwrap(node);
|
|
370
391
|
switch (node.type) {
|
|
371
392
|
case AST_NODE_TYPES.MemberExpression: return {
|
|
@@ -373,6 +394,11 @@ function getControllerFromSignal(context, node) {
|
|
|
373
394
|
isParamSignal: false
|
|
374
395
|
};
|
|
375
396
|
case AST_NODE_TYPES.Identifier: {
|
|
397
|
+
if (seen.has(node)) return {
|
|
398
|
+
controller: null,
|
|
399
|
+
isParamSignal: false
|
|
400
|
+
};
|
|
401
|
+
seen.add(node);
|
|
376
402
|
const resolved = resolveOrigin(context, node);
|
|
377
403
|
const resolvedUnwrapped = resolved == null ? null : Extract.unwrap(resolved);
|
|
378
404
|
if (resolvedUnwrapped?.type === AST_NODE_TYPES.MemberExpression) return {
|
|
@@ -383,6 +409,7 @@ function getControllerFromSignal(context, node) {
|
|
|
383
409
|
controller: node,
|
|
384
410
|
isParamSignal: true
|
|
385
411
|
};
|
|
412
|
+
if (resolvedUnwrapped != null) return getControllerFromSignal(context, resolvedUnwrapped, seen);
|
|
386
413
|
return {
|
|
387
414
|
controller: null,
|
|
388
415
|
isParamSignal: false
|
|
@@ -487,12 +514,28 @@ function isFromObserver$1(context, node, name) {
|
|
|
487
514
|
switch (true) {
|
|
488
515
|
case Check.isIdentifier(node): {
|
|
489
516
|
const initNode = resolve(context, node);
|
|
490
|
-
|
|
517
|
+
const unwrapped = initNode == null ? null : Extract.unwrap(initNode);
|
|
518
|
+
if (unwrapped == null) return false;
|
|
519
|
+
if (isNewObserver$1(unwrapped, name)) return true;
|
|
520
|
+
if (unwrapped.type === AST_NODE_TYPES.CallExpression && isUseRefLikeCall(unwrapped)) {
|
|
521
|
+
const arg = unwrapped.arguments.at(0);
|
|
522
|
+
return arg != null && isNewObserver$1(Extract.unwrap(arg), name);
|
|
523
|
+
}
|
|
524
|
+
if (unwrapped.type === AST_NODE_TYPES.MemberExpression) return isFromObserver$1(context, unwrapped, name);
|
|
525
|
+
return false;
|
|
491
526
|
}
|
|
492
527
|
case node.type === AST_NODE_TYPES.MemberExpression: return isFromObserver$1(context, node.object, name);
|
|
493
528
|
default: return false;
|
|
494
529
|
}
|
|
495
530
|
}
|
|
531
|
+
function isFromRefCurrent$1(context, node, refId) {
|
|
532
|
+
let unwrapped = Check.isTypeExpression(node) ? Extract.unwrap(node) : node;
|
|
533
|
+
if (Check.isIdentifier(unwrapped)) {
|
|
534
|
+
const initNode = resolve(context, unwrapped);
|
|
535
|
+
if (initNode != null) unwrapped = Extract.unwrap(initNode);
|
|
536
|
+
}
|
|
537
|
+
return unwrapped.type === AST_NODE_TYPES.MemberExpression && !unwrapped.computed && Check.isIdentifier(Extract.unwrap(unwrapped.property), "current") && isAssignmentTargetEqual(context, unwrapped.object, refId);
|
|
538
|
+
}
|
|
496
539
|
//#endregion
|
|
497
540
|
//#region src/rules/no-leaked-intersection-observer/no-leaked-intersection-observer.ts
|
|
498
541
|
const RULE_NAME$3 = "no-leaked-intersection-observer";
|
|
@@ -504,6 +547,20 @@ function getCallKind$3(context, node) {
|
|
|
504
547
|
if (name != null && isMatching(P.union("observe", "unobserve", "disconnect"))(name) && isFromObserver$1(context, callee, "IntersectionObserver")) return name;
|
|
505
548
|
return "other";
|
|
506
549
|
}
|
|
550
|
+
function isUseRefInitialValue$1(node) {
|
|
551
|
+
let child = node;
|
|
552
|
+
let parent = node.parent;
|
|
553
|
+
while (Check.isTypeExpression(parent)) {
|
|
554
|
+
child = parent;
|
|
555
|
+
parent = parent.parent;
|
|
556
|
+
}
|
|
557
|
+
return parent.type === AST_NODE_TYPES.CallExpression && parent.arguments.at(0) === child && isUseRefLikeCall(parent);
|
|
558
|
+
}
|
|
559
|
+
function resolveLocalFunction$1(context, node, id) {
|
|
560
|
+
const defNode = findVariable(context.sourceCode.getScope(node), id)?.defs.at(-1)?.node;
|
|
561
|
+
const fn = defNode == null ? null : defNode.type === AST_NODE_TYPES.FunctionDeclaration ? defNode : defNode.type === AST_NODE_TYPES.VariableDeclarator && defNode.init != null ? Extract.unwrap(defNode.init) : null;
|
|
562
|
+
return fn != null && Check.isFunction(fn) ? fn : null;
|
|
563
|
+
}
|
|
507
564
|
var no_leaked_intersection_observer_default = createRule({
|
|
508
565
|
meta: {
|
|
509
566
|
type: "problem",
|
|
@@ -524,10 +581,25 @@ function create$3(context) {
|
|
|
524
581
|
const oEntries = [];
|
|
525
582
|
const uEntries = [];
|
|
526
583
|
const dEntries = [];
|
|
584
|
+
const returnedCleanups = /* @__PURE__ */ new Set();
|
|
585
|
+
const cleanupCallees = /* @__PURE__ */ new Set();
|
|
586
|
+
function isInCleanupPhase(node) {
|
|
587
|
+
if (Traverse.findParent(node, isUseEffectCleanupCallback) != null) return true;
|
|
588
|
+
for (const fn of [...returnedCleanups, ...cleanupCallees]) if (Traverse.findParent(node, (n) => n === fn) != null) return true;
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
527
591
|
return {
|
|
528
592
|
["CallExpression"](node) {
|
|
529
|
-
|
|
593
|
+
const effectCallback = Traverse.findParent(node, isUseEffectCallback$3);
|
|
594
|
+
if (effectCallback == null) return;
|
|
530
595
|
const callee = Extract.unwrap(node.callee);
|
|
596
|
+
if (callee.type === AST_NODE_TYPES.Identifier) {
|
|
597
|
+
if (isUseEffectCleanupCallback(effectCallback)) {
|
|
598
|
+
const fn = resolveLocalFunction$1(context, node, callee);
|
|
599
|
+
if (fn != null) cleanupCallees.add(fn);
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
531
603
|
if (callee.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
532
604
|
const { object } = callee;
|
|
533
605
|
match(getCallKind$3(context, node)).with("disconnect", () => {
|
|
@@ -557,9 +629,9 @@ function create$3(context) {
|
|
|
557
629
|
}).otherwise(() => null);
|
|
558
630
|
},
|
|
559
631
|
["NewExpression"](node) {
|
|
560
|
-
const fn = Traverse.findParent(node, and(Check.isFunction, isUseEffectCallback$3));
|
|
561
|
-
if (fn == null) return;
|
|
562
632
|
if (!isNewObserver$1(node, "IntersectionObserver")) return;
|
|
633
|
+
const fn = Traverse.findParent(node, and(Check.isFunction, isUseEffectCallback$3));
|
|
634
|
+
if (fn == null && !isUseRefInitialValue$1(node)) return;
|
|
563
635
|
const id = resolveEnclosingAssignmentTarget(node);
|
|
564
636
|
if (id == null) {
|
|
565
637
|
context.report({
|
|
@@ -574,14 +646,27 @@ function create$3(context) {
|
|
|
574
646
|
phaseNode: fn
|
|
575
647
|
});
|
|
576
648
|
},
|
|
649
|
+
["ReturnStatement"](node) {
|
|
650
|
+
const arg = node.argument == null ? null : Extract.unwrap(node.argument);
|
|
651
|
+
if (arg == null || arg.type !== AST_NODE_TYPES.Identifier) return;
|
|
652
|
+
const setupFn = Traverse.findParent(node, Check.isFunction);
|
|
653
|
+
if (setupFn == null || !isUseEffectSetupCallback(setupFn)) return;
|
|
654
|
+
const fn = resolveLocalFunction$1(context, node, arg);
|
|
655
|
+
if (fn != null) returnedCleanups.add(fn);
|
|
656
|
+
},
|
|
577
657
|
["Program:exit"]() {
|
|
578
658
|
for (const { id, node, phaseNode } of observers) {
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
659
|
+
const isSameObserver = phaseNode == null ? (observer) => isFromRefCurrent$1(context, observer, id) : (observer) => isAssignmentTargetEqual(context, observer, id);
|
|
660
|
+
const isInsideObserverCallback = (entryNode) => Traverse.findParent(entryNode, (n) => n === node) != null;
|
|
661
|
+
if (dEntries.some((e) => !isInsideObserverCallback(e.node) && isInCleanupPhase(e.node) && isSameObserver(e.observer))) continue;
|
|
662
|
+
const matchedOEntries = oEntries.filter((e) => isSameObserver(e.observer));
|
|
663
|
+
const matchedUEntries = uEntries.filter((e) => isSameObserver(e.observer));
|
|
582
664
|
const isDynamic = or(Check.isCallExpression, Check.isConditional);
|
|
583
|
-
|
|
584
|
-
|
|
665
|
+
if (matchedOEntries.some((e) => {
|
|
666
|
+
const entryPhaseNode = phaseNode ?? Traverse.findParent(e.node, isUseEffectCallback$3);
|
|
667
|
+
const isPhaseNode = (n) => n === entryPhaseNode;
|
|
668
|
+
return !isPhaseNode(Traverse.findParent(e.node, or(isDynamic, isPhaseNode)));
|
|
669
|
+
})) {
|
|
585
670
|
context.report({
|
|
586
671
|
messageId: "expected-disconnect-in-control-flow",
|
|
587
672
|
node
|
|
@@ -589,7 +674,7 @@ function create$3(context) {
|
|
|
589
674
|
continue;
|
|
590
675
|
}
|
|
591
676
|
for (const oEntry of matchedOEntries) {
|
|
592
|
-
if (matchedUEntries.some((uEntry) => isAssignmentTargetEqual(context, uEntry.element, oEntry.element))) continue;
|
|
677
|
+
if (matchedUEntries.some((uEntry) => !isInsideObserverCallback(uEntry.node) && isInCleanupPhase(uEntry.node) && isAssignmentTargetEqual(context, uEntry.element, oEntry.element))) continue;
|
|
593
678
|
context.report({
|
|
594
679
|
messageId: "expected-disconnect-or-unobserve-in-cleanup",
|
|
595
680
|
node: oEntry.node
|
|
@@ -676,12 +761,28 @@ function isFromObserver(context, node, name) {
|
|
|
676
761
|
switch (true) {
|
|
677
762
|
case Check.isIdentifier(node): {
|
|
678
763
|
const initNode = resolve(context, node);
|
|
679
|
-
|
|
764
|
+
const unwrapped = initNode == null ? null : Extract.unwrap(initNode);
|
|
765
|
+
if (unwrapped == null) return false;
|
|
766
|
+
if (isNewObserver(unwrapped, name)) return true;
|
|
767
|
+
if (unwrapped.type === AST_NODE_TYPES.CallExpression && isUseRefLikeCall(unwrapped)) {
|
|
768
|
+
const arg = unwrapped.arguments.at(0);
|
|
769
|
+
return arg != null && isNewObserver(Extract.unwrap(arg), name);
|
|
770
|
+
}
|
|
771
|
+
if (unwrapped.type === AST_NODE_TYPES.MemberExpression) return isFromObserver(context, unwrapped, name);
|
|
772
|
+
return false;
|
|
680
773
|
}
|
|
681
774
|
case node.type === AST_NODE_TYPES.MemberExpression: return isFromObserver(context, node.object, name);
|
|
682
775
|
default: return false;
|
|
683
776
|
}
|
|
684
777
|
}
|
|
778
|
+
function isFromRefCurrent(context, node, refId) {
|
|
779
|
+
let unwrapped = Check.isTypeExpression(node) ? Extract.unwrap(node) : node;
|
|
780
|
+
if (Check.isIdentifier(unwrapped)) {
|
|
781
|
+
const initNode = resolve(context, unwrapped);
|
|
782
|
+
if (initNode != null) unwrapped = Extract.unwrap(initNode);
|
|
783
|
+
}
|
|
784
|
+
return unwrapped.type === AST_NODE_TYPES.MemberExpression && !unwrapped.computed && Check.isIdentifier(Extract.unwrap(unwrapped.property), "current") && isAssignmentTargetEqual(context, unwrapped.object, refId);
|
|
785
|
+
}
|
|
685
786
|
//#endregion
|
|
686
787
|
//#region src/rules/no-leaked-resize-observer/no-leaked-resize-observer.ts
|
|
687
788
|
const RULE_NAME$1 = "no-leaked-resize-observer";
|
|
@@ -693,6 +794,20 @@ function getCallKind$1(context, node) {
|
|
|
693
794
|
if (name != null && isMatching(P.union("observe", "unobserve", "disconnect"))(name) && isFromObserver(context, callee, "ResizeObserver")) return name;
|
|
694
795
|
return "other";
|
|
695
796
|
}
|
|
797
|
+
function isUseRefInitialValue(node) {
|
|
798
|
+
let child = node;
|
|
799
|
+
let parent = node.parent;
|
|
800
|
+
while (Check.isTypeExpression(parent)) {
|
|
801
|
+
child = parent;
|
|
802
|
+
parent = parent.parent;
|
|
803
|
+
}
|
|
804
|
+
return parent.type === AST_NODE_TYPES.CallExpression && parent.arguments.at(0) === child && isUseRefLikeCall(parent);
|
|
805
|
+
}
|
|
806
|
+
function resolveLocalFunction(context, node, id) {
|
|
807
|
+
const defNode = findVariable(context.sourceCode.getScope(node), id)?.defs.at(-1)?.node;
|
|
808
|
+
const fn = defNode == null ? null : defNode.type === AST_NODE_TYPES.FunctionDeclaration ? defNode : defNode.type === AST_NODE_TYPES.VariableDeclarator && defNode.init != null ? Extract.unwrap(defNode.init) : null;
|
|
809
|
+
return fn != null && Check.isFunction(fn) ? fn : null;
|
|
810
|
+
}
|
|
696
811
|
var no_leaked_resize_observer_default = createRule({
|
|
697
812
|
meta: {
|
|
698
813
|
type: "problem",
|
|
@@ -713,10 +828,25 @@ function create$1(context) {
|
|
|
713
828
|
const oEntries = [];
|
|
714
829
|
const uEntries = [];
|
|
715
830
|
const dEntries = [];
|
|
831
|
+
const returnedCleanups = /* @__PURE__ */ new Set();
|
|
832
|
+
const cleanupCallees = /* @__PURE__ */ new Set();
|
|
833
|
+
function isInCleanupPhase(node) {
|
|
834
|
+
if (Traverse.findParent(node, isUseEffectCleanupCallback) != null) return true;
|
|
835
|
+
for (const fn of [...returnedCleanups, ...cleanupCallees]) if (Traverse.findParent(node, (n) => n === fn) != null) return true;
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
716
838
|
return {
|
|
717
839
|
["CallExpression"](node) {
|
|
718
|
-
|
|
840
|
+
const effectCallback = Traverse.findParent(node, isUseEffectCallback$1);
|
|
841
|
+
if (effectCallback == null) return;
|
|
719
842
|
const callee = Extract.unwrap(node.callee);
|
|
843
|
+
if (callee.type === AST_NODE_TYPES.Identifier) {
|
|
844
|
+
if (isUseEffectCleanupCallback(effectCallback)) {
|
|
845
|
+
const fn = resolveLocalFunction(context, node, callee);
|
|
846
|
+
if (fn != null) cleanupCallees.add(fn);
|
|
847
|
+
}
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
720
850
|
if (callee.type !== AST_NODE_TYPES.MemberExpression) return;
|
|
721
851
|
const { object } = callee;
|
|
722
852
|
match(getCallKind$1(context, node)).with("disconnect", () => {
|
|
@@ -746,9 +876,9 @@ function create$1(context) {
|
|
|
746
876
|
}).otherwise(() => null);
|
|
747
877
|
},
|
|
748
878
|
["NewExpression"](node) {
|
|
749
|
-
const fn = Traverse.findParent(node, and(Check.isFunction, isUseEffectCallback$1));
|
|
750
|
-
if (fn == null) return;
|
|
751
879
|
if (!isNewObserver(node, "ResizeObserver")) return;
|
|
880
|
+
const fn = Traverse.findParent(node, and(Check.isFunction, isUseEffectCallback$1));
|
|
881
|
+
if (fn == null && !isUseRefInitialValue(node)) return;
|
|
752
882
|
const id = resolveEnclosingAssignmentTarget(node);
|
|
753
883
|
if (id == null) {
|
|
754
884
|
context.report({
|
|
@@ -763,15 +893,27 @@ function create$1(context) {
|
|
|
763
893
|
phaseNode: fn
|
|
764
894
|
});
|
|
765
895
|
},
|
|
896
|
+
["ReturnStatement"](node) {
|
|
897
|
+
const arg = node.argument == null ? null : Extract.unwrap(node.argument);
|
|
898
|
+
if (arg == null || arg.type !== AST_NODE_TYPES.Identifier) return;
|
|
899
|
+
const setupFn = Traverse.findParent(node, Check.isFunction);
|
|
900
|
+
if (setupFn == null || !isUseEffectSetupCallback(setupFn)) return;
|
|
901
|
+
const fn = resolveLocalFunction(context, node, arg);
|
|
902
|
+
if (fn != null) returnedCleanups.add(fn);
|
|
903
|
+
},
|
|
766
904
|
["Program:exit"]() {
|
|
767
905
|
for (const { id, node, phaseNode } of observers) {
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const
|
|
906
|
+
const isSameObserver = phaseNode == null ? (observer) => isFromRefCurrent(context, observer, id) : (observer) => isAssignmentTargetEqual(context, observer, id);
|
|
907
|
+
const isInsideObserverCallback = (entryNode) => Traverse.findParent(entryNode, (n) => n === node) != null;
|
|
908
|
+
if (dEntries.some((e) => !isInsideObserverCallback(e.node) && isInCleanupPhase(e.node) && isSameObserver(e.observer))) continue;
|
|
909
|
+
const matchedOEntries = oEntries.filter((e) => isSameObserver(e.observer));
|
|
910
|
+
const matchedUEntries = uEntries.filter((e) => isSameObserver(e.observer));
|
|
772
911
|
const isDynamic = (node) => node?.type === AST_NODE_TYPES.CallExpression || Check.isConditional(node);
|
|
773
|
-
|
|
774
|
-
|
|
912
|
+
if (matchedOEntries.some((e) => {
|
|
913
|
+
const entryPhaseNode = phaseNode ?? Traverse.findParent(e.node, isUseEffectCallback$1);
|
|
914
|
+
const isPhaseNode = (node) => node === entryPhaseNode;
|
|
915
|
+
return !isPhaseNode(Traverse.findParent(e.node, or(isDynamic, isPhaseNode)));
|
|
916
|
+
})) {
|
|
775
917
|
context.report({
|
|
776
918
|
messageId: "expected-disconnect-in-control-flow",
|
|
777
919
|
node
|
|
@@ -779,7 +921,7 @@ function create$1(context) {
|
|
|
779
921
|
continue;
|
|
780
922
|
}
|
|
781
923
|
for (const oEntry of matchedOEntries) {
|
|
782
|
-
if (matchedUEntries.some((uEntry) => isAssignmentTargetEqual(context, uEntry.element, oEntry.element))) continue;
|
|
924
|
+
if (matchedUEntries.some((uEntry) => !isInsideObserverCallback(uEntry.node) && isInCleanupPhase(uEntry.node) && isAssignmentTargetEqual(context, uEntry.element, oEntry.element))) continue;
|
|
783
925
|
context.report({
|
|
784
926
|
messageId: "expected-disconnect-or-unobserve-in-cleanup",
|
|
785
927
|
node: oEntry.node
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-web-api",
|
|
3
|
-
"version": "5.24.
|
|
3
|
+
"version": "5.24.7",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for interacting with Web APIs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@eslint-react/ast": "5.24.
|
|
41
|
-
"@eslint-react/core": "5.24.
|
|
42
|
-
"@eslint-react/eslint": "5.24.
|
|
43
|
-
"@eslint-react/shared": "5.24.
|
|
44
|
-
"@eslint-react/var": "5.24.
|
|
45
|
-
"@typescript-eslint/types": "^8.71.
|
|
46
|
-
"@typescript-eslint/utils": "^8.71.
|
|
40
|
+
"@eslint-react/ast": "5.24.7",
|
|
41
|
+
"@eslint-react/core": "5.24.7",
|
|
42
|
+
"@eslint-react/eslint": "5.24.7",
|
|
43
|
+
"@eslint-react/shared": "5.24.7",
|
|
44
|
+
"@eslint-react/var": "5.24.7",
|
|
45
|
+
"@typescript-eslint/types": "^8.71.1",
|
|
46
|
+
"@typescript-eslint/utils": "^8.71.1",
|
|
47
47
|
"ts-pattern": "^5.9.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|