eslint-plugin-react-web-api 2.7.4-next.0 → 2.7.4-next.10
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 +12 -0
- package/dist/index.js +22 -22
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -36,6 +36,18 @@ export default defineConfig(
|
|
|
36
36
|
);
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Web API Rules
|
|
40
|
+
|
|
41
|
+
> [!NOTE]
|
|
42
|
+
> Web API rules prevent resource leaks by detecting uncleaned Web API subscriptions in components and hooks.
|
|
43
|
+
|
|
44
|
+
**Implemented Rules:**
|
|
45
|
+
|
|
46
|
+
- [`no-leaked-event-listener`](./web-api-no-leaked-event-listener) - Prevents leaked `addEventListener` calls
|
|
47
|
+
- [`no-leaked-interval`](./web-api-no-leaked-interval) - Prevents leaked `setInterval` calls
|
|
48
|
+
- [`no-leaked-resize-observer`](./web-api-no-leaked-resize-observer) - Prevents leaked `ResizeObserver` instances
|
|
49
|
+
- [`no-leaked-timeout`](./web-api-no-leaked-timeout) - Prevents leaked `setTimeout` calls
|
|
50
|
+
|
|
39
51
|
## Rules
|
|
40
52
|
|
|
41
53
|
<https://eslint-react.xyz/docs/rules/overview#web-api-rules>
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_ESLINT_REACT_SETTINGS, WEBSITE_URL, getConfigAdapters } from "@eslint-react/shared";
|
|
2
2
|
import * as AST from "@eslint-react/ast";
|
|
3
|
-
import { ComponentPhaseRelevance,
|
|
3
|
+
import { ComponentPhaseRelevance, getPhaseKindOfFunction, isInitializedFromReactNative, isInversePhase } from "@eslint-react/core";
|
|
4
4
|
import { or, unit } from "@eslint-react/eff";
|
|
5
|
-
import {
|
|
5
|
+
import { findEnclosingAssignmentTarget, findProperty, findVariable, getVariableDefinitionNode, isAssignmentTargetEqual, isNodeValueEqual } from "@eslint-react/var";
|
|
6
6
|
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
7
7
|
import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
|
|
8
8
|
import { P, isMatching, match } from "ts-pattern";
|
|
@@ -11,9 +11,9 @@ import { P, isMatching, match } from "ts-pattern";
|
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
12
12
|
var __exportAll = (all, symbols) => {
|
|
13
13
|
let target = {};
|
|
14
|
-
for (var name
|
|
15
|
-
__defProp(target, name
|
|
16
|
-
get: all[name
|
|
14
|
+
for (var name in all) {
|
|
15
|
+
__defProp(target, name, {
|
|
16
|
+
get: all[name],
|
|
17
17
|
enumerable: true
|
|
18
18
|
});
|
|
19
19
|
}
|
|
@@ -42,7 +42,7 @@ const settings = { "react-x": DEFAULT_ESLINT_REACT_SETTINGS };
|
|
|
42
42
|
//#endregion
|
|
43
43
|
//#region package.json
|
|
44
44
|
var name = "eslint-plugin-react-web-api";
|
|
45
|
-
var version = "2.7.4-next.
|
|
45
|
+
var version = "2.7.4-next.10";
|
|
46
46
|
|
|
47
47
|
//#endregion
|
|
48
48
|
//#region src/utils/create-rule.ts
|
|
@@ -94,20 +94,20 @@ function getOptions(node, initialScope) {
|
|
|
94
94
|
}
|
|
95
95
|
return filter(v) ? v : unit;
|
|
96
96
|
}
|
|
97
|
-
function getOpts(node
|
|
98
|
-
switch (node
|
|
97
|
+
function getOpts(node) {
|
|
98
|
+
switch (node.type) {
|
|
99
99
|
case AST_NODE_TYPES.Identifier: {
|
|
100
|
-
const variableNode = getVariableDefinitionNode(findVariable(node
|
|
100
|
+
const variableNode = getVariableDefinitionNode(findVariable(node, initialScope), 0);
|
|
101
101
|
if (variableNode?.type === AST_NODE_TYPES.ObjectExpression) return getOpts(variableNode);
|
|
102
102
|
return defaultOptions;
|
|
103
103
|
}
|
|
104
104
|
case AST_NODE_TYPES.Literal: return {
|
|
105
105
|
...defaultOptions,
|
|
106
|
-
capture: Boolean(node
|
|
106
|
+
capture: Boolean(node.value)
|
|
107
107
|
};
|
|
108
108
|
case AST_NODE_TYPES.ObjectExpression: {
|
|
109
|
-
const vCapture = !!getPropValue(findProp(node
|
|
110
|
-
const pSignal = findProp(node
|
|
109
|
+
const vCapture = !!getPropValue(findProp(node.properties, "capture"));
|
|
110
|
+
const pSignal = findProp(node.properties, "signal");
|
|
111
111
|
return {
|
|
112
112
|
capture: vCapture,
|
|
113
113
|
signal: pSignal?.type === AST_NODE_TYPES.Property ? getSignalValueExpression(pSignal.value, initialScope) : unit
|
|
@@ -269,7 +269,7 @@ function create$2(context) {
|
|
|
269
269
|
const sEntries = [];
|
|
270
270
|
const cEntries = [];
|
|
271
271
|
function isInverseEntry(a, b) {
|
|
272
|
-
return
|
|
272
|
+
return isAssignmentTargetEqual(context, a.timerId, b.timerId);
|
|
273
273
|
}
|
|
274
274
|
return {
|
|
275
275
|
[":function"](node) {
|
|
@@ -288,7 +288,7 @@ function create$2(context) {
|
|
|
288
288
|
const fEntry = fEntries.findLast((x) => x.kind !== "other");
|
|
289
289
|
if (fEntry == null) break;
|
|
290
290
|
if (!ComponentPhaseRelevance.has(fEntry.kind)) break;
|
|
291
|
-
const intervalIdNode =
|
|
291
|
+
const intervalIdNode = findEnclosingAssignmentTarget(node);
|
|
292
292
|
if (intervalIdNode == null) {
|
|
293
293
|
context.report({
|
|
294
294
|
messageId: "expectedIntervalId",
|
|
@@ -466,11 +466,11 @@ function create$1(context) {
|
|
|
466
466
|
},
|
|
467
467
|
["Program:exit"]() {
|
|
468
468
|
for (const { id, node, phaseNode } of observers) {
|
|
469
|
-
if (dEntries.some((e) =>
|
|
470
|
-
const oentries = oEntries.filter((e) =>
|
|
471
|
-
const uentries = uEntries.filter((e) =>
|
|
472
|
-
const isDynamic = (node
|
|
473
|
-
const isPhaseNode = (node
|
|
469
|
+
if (dEntries.some((e) => isAssignmentTargetEqual(context, e.observer, id))) continue;
|
|
470
|
+
const oentries = oEntries.filter((e) => isAssignmentTargetEqual(context, e.observer, id));
|
|
471
|
+
const uentries = uEntries.filter((e) => isAssignmentTargetEqual(context, e.observer, id));
|
|
472
|
+
const isDynamic = (node) => node?.type === AST_NODE_TYPES.CallExpression || AST.isConditional(node);
|
|
473
|
+
const isPhaseNode = (node) => node === phaseNode;
|
|
474
474
|
if (oentries.some((e) => !isPhaseNode(AST.findParentNode(e.node, or(isDynamic, isPhaseNode))))) {
|
|
475
475
|
context.report({
|
|
476
476
|
messageId: "expectedDisconnectInControlFlow",
|
|
@@ -479,7 +479,7 @@ function create$1(context) {
|
|
|
479
479
|
continue;
|
|
480
480
|
}
|
|
481
481
|
for (const oEntry of oentries) {
|
|
482
|
-
if (uentries.some((uEntry) =>
|
|
482
|
+
if (uentries.some((uEntry) => isAssignmentTargetEqual(context, uEntry.element, oEntry.element))) continue;
|
|
483
483
|
context.report({
|
|
484
484
|
messageId: "expectedDisconnectOrUnobserveInCleanup",
|
|
485
485
|
node: oEntry.node
|
|
@@ -521,7 +521,7 @@ function create(context) {
|
|
|
521
521
|
const sEntries = [];
|
|
522
522
|
const rEntries = [];
|
|
523
523
|
function isInverseEntry(a, b) {
|
|
524
|
-
return
|
|
524
|
+
return isAssignmentTargetEqual(context, a.timerId, b.timerId);
|
|
525
525
|
}
|
|
526
526
|
return {
|
|
527
527
|
[":function"](node) {
|
|
@@ -539,7 +539,7 @@ function create(context) {
|
|
|
539
539
|
if (!ComponentPhaseRelevance.has(fEntry?.kind)) return;
|
|
540
540
|
switch (getCallKind(node)) {
|
|
541
541
|
case "setTimeout": {
|
|
542
|
-
const timeoutIdNode =
|
|
542
|
+
const timeoutIdNode = findEnclosingAssignmentTarget(node);
|
|
543
543
|
if (timeoutIdNode == null) {
|
|
544
544
|
context.report({
|
|
545
545
|
messageId: "expectedTimeoutId",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-web-api",
|
|
3
|
-
"version": "2.7.4-next.
|
|
3
|
+
"version": "2.7.4-next.10",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for interacting with Web APIs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"directory": "packages/plugins/eslint-plugin-react-web-api"
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
|
-
"author": "Rel1cx
|
|
23
|
+
"author": "Rel1cx",
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"type": "module",
|
|
26
26
|
"exports": {
|
|
@@ -43,16 +43,16 @@
|
|
|
43
43
|
"@typescript-eslint/utils": "^8.53.1",
|
|
44
44
|
"string-ts": "^2.3.1",
|
|
45
45
|
"ts-pattern": "^5.9.0",
|
|
46
|
-
"@eslint-react/ast": "2.7.4-next.
|
|
47
|
-
"@eslint-react/core": "2.7.4-next.
|
|
48
|
-
"@eslint-react/
|
|
49
|
-
"@eslint-react/
|
|
50
|
-
"@eslint-react/var": "2.7.4-next.
|
|
46
|
+
"@eslint-react/ast": "2.7.4-next.10",
|
|
47
|
+
"@eslint-react/core": "2.7.4-next.10",
|
|
48
|
+
"@eslint-react/eff": "2.7.4-next.10",
|
|
49
|
+
"@eslint-react/shared": "2.7.4-next.10",
|
|
50
|
+
"@eslint-react/var": "2.7.4-next.10"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/react": "^19.2.9",
|
|
54
54
|
"@types/react-dom": "^19.2.3",
|
|
55
|
-
"tsdown": "^0.20.
|
|
55
|
+
"tsdown": "^0.20.1",
|
|
56
56
|
"@local/configs": "0.0.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|