eslint-plugin-react-web-api 1.52.10-next.0 → 1.52.10-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/dist/index.d.mts +19 -18
- package/dist/index.d.ts +18 -18
- package/dist/index.js +631 -700
- package/dist/index.mjs +611 -675
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,744 +1,675 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var shared = require('@eslint-react/shared');
|
|
4
|
-
var AST = require('@eslint-react/ast');
|
|
5
|
-
var ER4 = require('@eslint-react/core');
|
|
6
|
-
var eff = require('@eslint-react/eff');
|
|
7
|
-
var VAR = require('@eslint-react/var');
|
|
8
|
-
var utils = require('@typescript-eslint/utils');
|
|
9
|
-
var tsPattern = require('ts-pattern');
|
|
10
|
-
var types = require('@typescript-eslint/types');
|
|
11
|
-
|
|
12
|
-
function _interopNamespace(e) {
|
|
13
|
-
if (e && e.__esModule) return e;
|
|
14
|
-
var n = Object.create(null);
|
|
15
|
-
if (e) {
|
|
16
|
-
Object.keys(e).forEach(function (k) {
|
|
17
|
-
if (k !== 'default') {
|
|
18
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
get: function () { return e[k]; }
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
n.default = e;
|
|
27
|
-
return Object.freeze(n);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
var AST__namespace = /*#__PURE__*/_interopNamespace(AST);
|
|
31
|
-
var ER4__namespace = /*#__PURE__*/_interopNamespace(ER4);
|
|
32
|
-
var VAR__namespace = /*#__PURE__*/_interopNamespace(VAR);
|
|
33
|
-
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
34
3
|
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
35
8
|
var __export = (target, all) => {
|
|
36
|
-
|
|
37
|
-
|
|
9
|
+
for (var name$2 in all) __defProp(target, name$2, {
|
|
10
|
+
get: all[name$2],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
38
13
|
};
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
+
key = keys[i];
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: ((k) => from[k]).bind(null, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
const __eslint_react_shared = __toESM(require("@eslint-react/shared"));
|
|
31
|
+
const __eslint_react_ast = __toESM(require("@eslint-react/ast"));
|
|
32
|
+
const __eslint_react_core = __toESM(require("@eslint-react/core"));
|
|
33
|
+
const __eslint_react_eff = __toESM(require("@eslint-react/eff"));
|
|
34
|
+
const __eslint_react_var = __toESM(require("@eslint-react/var"));
|
|
35
|
+
const __typescript_eslint_utils = __toESM(require("@typescript-eslint/utils"));
|
|
36
|
+
const ts_pattern = __toESM(require("ts-pattern"));
|
|
37
|
+
const __typescript_eslint_types = __toESM(require("@typescript-eslint/types"));
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
//#region src/configs/recommended.ts
|
|
41
40
|
var recommended_exports = {};
|
|
42
41
|
__export(recommended_exports, {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
name: () => name$1,
|
|
43
|
+
rules: () => rules,
|
|
44
|
+
settings: () => settings
|
|
46
45
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
54
|
-
var settings = {
|
|
55
|
-
"react-x": shared.DEFAULT_ESLINT_REACT_SETTINGS
|
|
46
|
+
const name$1 = "react-web-api/recommended";
|
|
47
|
+
const rules = {
|
|
48
|
+
"react-web-api/no-leaked-event-listener": "warn",
|
|
49
|
+
"react-web-api/no-leaked-interval": "warn",
|
|
50
|
+
"react-web-api/no-leaked-resize-observer": "warn",
|
|
51
|
+
"react-web-api/no-leaked-timeout": "warn"
|
|
56
52
|
};
|
|
53
|
+
const settings = { "react-x": __eslint_react_shared.DEFAULT_ESLINT_REACT_SETTINGS };
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region package.json
|
|
57
|
+
var name = "eslint-plugin-react-web-api";
|
|
58
|
+
var version = "1.52.10-next.1";
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/utils/create-rule.ts
|
|
62
|
+
const createRule = __typescript_eslint_utils.ESLintUtils.RuleCreator((0, __eslint_react_shared.getDocsUrl)("web-api"));
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
var version = "1.52.10-next.0";
|
|
61
|
-
var createRule = utils.ESLintUtils.RuleCreator(shared.getDocsUrl("web-api"));
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/utils/get-phase-kind-of-function.ts
|
|
62
66
|
function getPhaseKindOfFunction(node) {
|
|
63
|
-
|
|
67
|
+
return (0, ts_pattern.match)(node).when(__eslint_react_core.isFunctionOfUseEffectSetup, () => "setup").when(__eslint_react_core.isFunctionOfUseEffectCleanup, () => "cleanup").when(__eslint_react_core.isFunctionOfComponentDidMount, () => "mount").when(__eslint_react_core.isFunctionOfComponentWillUnmount, () => "unmount").otherwise(() => null);
|
|
64
68
|
}
|
|
65
|
-
var isControlFlow = eff.or(
|
|
66
|
-
AST__namespace.isLoop,
|
|
67
|
-
AST__namespace.isOneOf([
|
|
68
|
-
types.AST_NODE_TYPES.IfStatement,
|
|
69
|
-
types.AST_NODE_TYPES.SwitchStatement
|
|
70
|
-
])
|
|
71
|
-
);
|
|
72
|
-
var isConditional = eff.or(
|
|
73
|
-
isControlFlow,
|
|
74
|
-
AST__namespace.isOneOf([
|
|
75
|
-
types.AST_NODE_TYPES.LogicalExpression,
|
|
76
|
-
types.AST_NODE_TYPES.ConditionalExpression
|
|
77
|
-
])
|
|
78
|
-
);
|
|
79
69
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/utils/is-conditional.ts
|
|
72
|
+
const isControlFlow = (0, __eslint_react_eff.or)(__eslint_react_ast.isLoop, __eslint_react_ast.isOneOf([__typescript_eslint_types.AST_NODE_TYPES.IfStatement, __typescript_eslint_types.AST_NODE_TYPES.SwitchStatement]));
|
|
73
|
+
const isConditional = (0, __eslint_react_eff.or)(isControlFlow, __eslint_react_ast.isOneOf([__typescript_eslint_types.AST_NODE_TYPES.LogicalExpression, __typescript_eslint_types.AST_NODE_TYPES.ConditionalExpression]));
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/rules/no-leaked-event-listener.ts
|
|
77
|
+
const RULE_NAME$3 = "no-leaked-event-listener";
|
|
78
|
+
const RULE_FEATURES$3 = [];
|
|
79
|
+
const defaultOptions = {
|
|
80
|
+
capture: false,
|
|
81
|
+
signal: __eslint_react_eff.unit
|
|
87
82
|
};
|
|
88
|
-
function getCallKind(node) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
default:
|
|
95
|
-
return "other";
|
|
96
|
-
}
|
|
83
|
+
function getCallKind$3(node) {
|
|
84
|
+
switch (true) {
|
|
85
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("addEventListener", "removeEventListener", "abort"))(node.callee.name): return node.callee.name;
|
|
86
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("addEventListener", "removeEventListener", "abort"))(node.callee.property.name): return node.callee.property.name;
|
|
87
|
+
default: return "other";
|
|
88
|
+
}
|
|
97
89
|
}
|
|
98
|
-
function getFunctionKind(node) {
|
|
99
|
-
|
|
90
|
+
function getFunctionKind$1(node) {
|
|
91
|
+
return getPhaseKindOfFunction(node) ?? "other";
|
|
100
92
|
}
|
|
101
93
|
function getSignalValueExpression(node, initialScope) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return node;
|
|
109
|
-
default:
|
|
110
|
-
return eff.unit;
|
|
111
|
-
}
|
|
94
|
+
if (node == null) return __eslint_react_eff.unit;
|
|
95
|
+
switch (node.type) {
|
|
96
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.Identifier: return getSignalValueExpression(__eslint_react_var.getVariableInitNode(__eslint_react_var.findVariable(node, initialScope), 0), initialScope);
|
|
97
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression: return node;
|
|
98
|
+
default: return __eslint_react_eff.unit;
|
|
99
|
+
}
|
|
112
100
|
}
|
|
113
101
|
function getOptions(node, initialScope) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
102
|
+
function findProp(properties, propName) {
|
|
103
|
+
return __eslint_react_var.findPropertyInProperties(propName, properties, initialScope);
|
|
104
|
+
}
|
|
105
|
+
function getPropValue(prop, filter = (a) => true) {
|
|
106
|
+
if (prop?.type !== __typescript_eslint_utils.AST_NODE_TYPES.Property) return __eslint_react_eff.unit;
|
|
107
|
+
const { value } = prop;
|
|
108
|
+
let v = value;
|
|
109
|
+
switch (value.type) {
|
|
110
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.Literal:
|
|
111
|
+
v = value.value;
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
v = __eslint_react_var.toStaticValue({
|
|
115
|
+
kind: "lazy",
|
|
116
|
+
node: value,
|
|
117
|
+
initialScope
|
|
118
|
+
}).value;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
return filter(v) ? v : __eslint_react_eff.unit;
|
|
122
|
+
}
|
|
123
|
+
function getOpts(node$1) {
|
|
124
|
+
switch (node$1.type) {
|
|
125
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.Identifier: {
|
|
126
|
+
const variable = __eslint_react_var.findVariable(node$1, initialScope);
|
|
127
|
+
const variableNode = __eslint_react_var.getVariableInitNode(variable, 0);
|
|
128
|
+
if (variableNode?.type === __typescript_eslint_utils.AST_NODE_TYPES.ObjectExpression) return getOpts(variableNode);
|
|
129
|
+
return defaultOptions;
|
|
130
|
+
}
|
|
131
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.Literal: return {
|
|
132
|
+
...defaultOptions,
|
|
133
|
+
capture: Boolean(node$1.value)
|
|
134
|
+
};
|
|
135
|
+
case __typescript_eslint_utils.AST_NODE_TYPES.ObjectExpression: {
|
|
136
|
+
const pCapture = findProp(node$1.properties, "capture");
|
|
137
|
+
const vCapture = !!getPropValue(pCapture);
|
|
138
|
+
const pSignal = findProp(node$1.properties, "signal");
|
|
139
|
+
const vSignal = pSignal?.type === __typescript_eslint_utils.AST_NODE_TYPES.Property ? getSignalValueExpression(pSignal.value, initialScope) : __eslint_react_eff.unit;
|
|
140
|
+
return {
|
|
141
|
+
capture: vCapture,
|
|
142
|
+
signal: vSignal
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
default: return defaultOptions;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return getOpts(node);
|
|
159
149
|
}
|
|
160
150
|
var no_leaked_event_listener_default = createRule({
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
151
|
+
meta: {
|
|
152
|
+
type: "problem",
|
|
153
|
+
docs: {
|
|
154
|
+
description: "Prevents leaked `addEventListener` in a component or custom Hook.",
|
|
155
|
+
[Symbol.for("rule_features")]: RULE_FEATURES$3
|
|
156
|
+
},
|
|
157
|
+
messages: {
|
|
158
|
+
expectedRemoveEventListenerInCleanup: "An 'addEventListener' in '{{effectMethodKind}}' should have a corresponding 'removeEventListener' in its cleanup function.",
|
|
159
|
+
expectedRemoveEventListenerInUnmount: "An 'addEventListener' in 'componentDidMount' should have a corresponding 'removeEventListener' in 'componentWillUnmount' method.",
|
|
160
|
+
unexpectedInlineFunction: "A/an '{{eventMethodKind}}' should not have an inline listener function."
|
|
161
|
+
},
|
|
162
|
+
schema: []
|
|
163
|
+
},
|
|
164
|
+
name: RULE_NAME$3,
|
|
165
|
+
create: create$3,
|
|
166
|
+
defaultOptions: []
|
|
177
167
|
});
|
|
178
|
-
function create(context) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
if (rEntries.some((rEntry) => isInverseEntry(aEntry, rEntry))) {
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
switch (aEntry.phase) {
|
|
287
|
-
case "setup":
|
|
288
|
-
case "cleanup":
|
|
289
|
-
context.report({
|
|
290
|
-
messageId: "expectedRemoveEventListenerInCleanup",
|
|
291
|
-
node: aEntry.node,
|
|
292
|
-
data: {
|
|
293
|
-
effectMethodKind: "useEffect"
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
continue;
|
|
297
|
-
case "mount":
|
|
298
|
-
case "unmount":
|
|
299
|
-
context.report({
|
|
300
|
-
messageId: "expectedRemoveEventListenerInUnmount",
|
|
301
|
-
node: aEntry.node
|
|
302
|
-
});
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
};
|
|
168
|
+
function create$3(context) {
|
|
169
|
+
if (!context.sourceCode.text.includes("addEventListener")) return {};
|
|
170
|
+
if (!/use\w*Effect|componentDidMount|componentWillUnmount/u.test(context.sourceCode.text)) return {};
|
|
171
|
+
const fEntries = [];
|
|
172
|
+
const aEntries = [];
|
|
173
|
+
const rEntries = [];
|
|
174
|
+
const abortedSignals = [];
|
|
175
|
+
function isSameObject(a, b) {
|
|
176
|
+
switch (true) {
|
|
177
|
+
case a.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && b.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression: return __eslint_react_ast.isNodeEqual(a.object, b.object);
|
|
178
|
+
default: return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function isInverseEntry(aEntry, rEntry) {
|
|
182
|
+
const { type: aType, callee: aCallee, capture: aCapture, listener: aListener, phase: aPhase } = aEntry;
|
|
183
|
+
const { type: rType, callee: rCallee, capture: rCapture, listener: rListener, phase: rPhase } = rEntry;
|
|
184
|
+
if (!__eslint_react_core.isInversePhase(aPhase, rPhase)) return false;
|
|
185
|
+
return isSameObject(aCallee, rCallee) && __eslint_react_ast.isNodeEqual(aListener, rListener) && __eslint_react_var.isNodeValueEqual(aType, rType, [context.sourceCode.getScope(aType), context.sourceCode.getScope(rType)]) && aCapture === rCapture;
|
|
186
|
+
}
|
|
187
|
+
function checkInlineFunction(node, callKind, options) {
|
|
188
|
+
const listener = node.arguments.at(1);
|
|
189
|
+
if (!__eslint_react_ast.isFunction(listener)) return;
|
|
190
|
+
if (options.signal != null) return;
|
|
191
|
+
context.report({
|
|
192
|
+
messageId: "unexpectedInlineFunction",
|
|
193
|
+
node: listener,
|
|
194
|
+
data: { eventMethodKind: callKind }
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
[":function"](node) {
|
|
199
|
+
const kind = getFunctionKind$1(node);
|
|
200
|
+
fEntries.push({
|
|
201
|
+
kind,
|
|
202
|
+
node
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
[":function:exit"]() {
|
|
206
|
+
fEntries.pop();
|
|
207
|
+
},
|
|
208
|
+
["CallExpression"](node) {
|
|
209
|
+
const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
|
|
210
|
+
if (fKind == null) return;
|
|
211
|
+
if (!__eslint_react_core.ComponentPhaseRelevance.has(fKind)) return;
|
|
212
|
+
(0, ts_pattern.match)(getCallKind$3(node)).with("addEventListener", (callKind) => {
|
|
213
|
+
const [type, listener, options] = node.arguments;
|
|
214
|
+
if (type == null || listener == null) return;
|
|
215
|
+
const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
|
|
216
|
+
const { callee } = node;
|
|
217
|
+
checkInlineFunction(node, callKind, opts);
|
|
218
|
+
aEntries.push({
|
|
219
|
+
...opts,
|
|
220
|
+
kind: "addEventListener",
|
|
221
|
+
type,
|
|
222
|
+
node,
|
|
223
|
+
callee,
|
|
224
|
+
listener,
|
|
225
|
+
phase: fKind
|
|
226
|
+
});
|
|
227
|
+
}).with("removeEventListener", (callKind) => {
|
|
228
|
+
const [type, listener, options] = node.arguments;
|
|
229
|
+
if (type == null || listener == null) return;
|
|
230
|
+
const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
|
|
231
|
+
const { callee } = node;
|
|
232
|
+
checkInlineFunction(node, callKind, opts);
|
|
233
|
+
rEntries.push({
|
|
234
|
+
...opts,
|
|
235
|
+
kind: "removeEventListener",
|
|
236
|
+
type,
|
|
237
|
+
node,
|
|
238
|
+
callee,
|
|
239
|
+
listener,
|
|
240
|
+
phase: fKind
|
|
241
|
+
});
|
|
242
|
+
}).with("abort", () => {
|
|
243
|
+
abortedSignals.push(node.callee);
|
|
244
|
+
}).otherwise(() => null);
|
|
245
|
+
},
|
|
246
|
+
["Program:exit"]() {
|
|
247
|
+
for (const aEntry of aEntries) {
|
|
248
|
+
const signal = aEntry.signal;
|
|
249
|
+
if (signal != null && abortedSignals.some((a) => isSameObject(a, signal))) continue;
|
|
250
|
+
if (rEntries.some((rEntry) => isInverseEntry(aEntry, rEntry))) continue;
|
|
251
|
+
switch (aEntry.phase) {
|
|
252
|
+
case "setup":
|
|
253
|
+
case "cleanup":
|
|
254
|
+
context.report({
|
|
255
|
+
messageId: "expectedRemoveEventListenerInCleanup",
|
|
256
|
+
node: aEntry.node,
|
|
257
|
+
data: { effectMethodKind: "useEffect" }
|
|
258
|
+
});
|
|
259
|
+
continue;
|
|
260
|
+
case "mount":
|
|
261
|
+
case "unmount":
|
|
262
|
+
context.report({
|
|
263
|
+
messageId: "expectedRemoveEventListenerInUnmount",
|
|
264
|
+
node: aEntry.node
|
|
265
|
+
});
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
308
271
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
272
|
+
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/rules/no-leaked-interval.ts
|
|
275
|
+
const RULE_NAME$2 = "no-leaked-interval";
|
|
276
|
+
const RULE_FEATURES$2 = [];
|
|
277
|
+
function getCallKind$2(node) {
|
|
278
|
+
switch (true) {
|
|
279
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("setInterval", "clearInterval"))(node.callee.name): return node.callee.name;
|
|
280
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("setInterval", "clearInterval"))(node.callee.property.name): return node.callee.property.name;
|
|
281
|
+
default: return "other";
|
|
282
|
+
}
|
|
320
283
|
}
|
|
321
284
|
var no_leaked_interval_default = createRule({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
285
|
+
meta: {
|
|
286
|
+
type: "problem",
|
|
287
|
+
docs: {
|
|
288
|
+
description: "Prevents leaked `setInterval` in a component or custom Hook.",
|
|
289
|
+
[Symbol.for("rule_features")]: RULE_FEATURES$2
|
|
290
|
+
},
|
|
291
|
+
messages: {
|
|
292
|
+
expectedClearIntervalInCleanup: "A 'setInterval' created in '{{ kind }}' must be cleared with 'clearInterval' in the cleanup function.",
|
|
293
|
+
expectedClearIntervalInUnmount: "A 'setInterval' created in '{{ kind }}' must be cleared with 'clearInterval' in the 'componentWillUnmount' method.",
|
|
294
|
+
expectedIntervalId: "A 'setInterval' must be assigned to a variable for proper cleanup."
|
|
295
|
+
},
|
|
296
|
+
schema: []
|
|
297
|
+
},
|
|
298
|
+
name: RULE_NAME$2,
|
|
299
|
+
create: create$2,
|
|
300
|
+
defaultOptions: []
|
|
338
301
|
});
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
continue;
|
|
423
|
-
case "mount":
|
|
424
|
-
case "unmount":
|
|
425
|
-
context.report({
|
|
426
|
-
messageId: "expectedClearIntervalInUnmount",
|
|
427
|
-
node: sEntry.node,
|
|
428
|
-
data: {
|
|
429
|
-
kind: "componentDidMount"
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
continue;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
};
|
|
302
|
+
function create$2(context) {
|
|
303
|
+
if (!context.sourceCode.text.includes("setInterval")) return {};
|
|
304
|
+
const fEntries = [];
|
|
305
|
+
const sEntries = [];
|
|
306
|
+
const cEntries = [];
|
|
307
|
+
function isInverseEntry(a, b) {
|
|
308
|
+
return __eslint_react_core.isInstanceIdEqual(context, a.timerId, b.timerId);
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
[":function"](node) {
|
|
312
|
+
const kind = getPhaseKindOfFunction(node) ?? "other";
|
|
313
|
+
fEntries.push({
|
|
314
|
+
kind,
|
|
315
|
+
node
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
[":function:exit"]() {
|
|
319
|
+
fEntries.pop();
|
|
320
|
+
},
|
|
321
|
+
["CallExpression"](node) {
|
|
322
|
+
switch (getCallKind$2(node)) {
|
|
323
|
+
case "setInterval": {
|
|
324
|
+
const fEntry = fEntries.findLast((x) => x.kind !== "other");
|
|
325
|
+
if (fEntry == null) break;
|
|
326
|
+
if (!__eslint_react_core.ComponentPhaseRelevance.has(fEntry.kind)) break;
|
|
327
|
+
const intervalIdNode = __eslint_react_var.getVariableDeclaratorId(node);
|
|
328
|
+
if (intervalIdNode == null) {
|
|
329
|
+
context.report({
|
|
330
|
+
messageId: "expectedIntervalId",
|
|
331
|
+
node
|
|
332
|
+
});
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
sEntries.push({
|
|
336
|
+
kind: "interval",
|
|
337
|
+
node,
|
|
338
|
+
callee: node.callee,
|
|
339
|
+
phase: fEntry.kind,
|
|
340
|
+
timerId: intervalIdNode
|
|
341
|
+
});
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case "clearInterval": {
|
|
345
|
+
const fEntry = fEntries.findLast((x) => x.kind !== "other");
|
|
346
|
+
if (fEntry == null) break;
|
|
347
|
+
if (!__eslint_react_core.ComponentPhaseRelevance.has(fEntry.kind)) break;
|
|
348
|
+
const [intervalIdNode] = node.arguments;
|
|
349
|
+
if (intervalIdNode == null) break;
|
|
350
|
+
cEntries.push({
|
|
351
|
+
kind: "interval",
|
|
352
|
+
node,
|
|
353
|
+
callee: node.callee,
|
|
354
|
+
phase: fEntry.kind,
|
|
355
|
+
timerId: intervalIdNode
|
|
356
|
+
});
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
["Program:exit"]() {
|
|
362
|
+
for (const sEntry of sEntries) {
|
|
363
|
+
if (cEntries.some((cEntry) => isInverseEntry(sEntry, cEntry))) continue;
|
|
364
|
+
switch (sEntry.phase) {
|
|
365
|
+
case "setup":
|
|
366
|
+
case "cleanup":
|
|
367
|
+
context.report({
|
|
368
|
+
messageId: "expectedClearIntervalInCleanup",
|
|
369
|
+
node: sEntry.node,
|
|
370
|
+
data: { kind: "useEffect" }
|
|
371
|
+
});
|
|
372
|
+
continue;
|
|
373
|
+
case "mount":
|
|
374
|
+
case "unmount":
|
|
375
|
+
context.report({
|
|
376
|
+
messageId: "expectedClearIntervalInUnmount",
|
|
377
|
+
node: sEntry.node,
|
|
378
|
+
data: { kind: "componentDidMount" }
|
|
379
|
+
});
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
437
385
|
}
|
|
438
|
-
|
|
439
|
-
|
|
386
|
+
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/rules/no-leaked-resize-observer.ts
|
|
389
|
+
const RULE_NAME$1 = "no-leaked-resize-observer";
|
|
390
|
+
const RULE_FEATURES$1 = [];
|
|
440
391
|
function isNewResizeObserver(node) {
|
|
441
|
-
|
|
392
|
+
return node?.type === __typescript_eslint_utils.AST_NODE_TYPES.NewExpression && node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && node.callee.name === "ResizeObserver";
|
|
442
393
|
}
|
|
443
394
|
function isFromObserver(context, node) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return false;
|
|
454
|
-
}
|
|
395
|
+
switch (true) {
|
|
396
|
+
case node.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier: {
|
|
397
|
+
const initialScope = context.sourceCode.getScope(node);
|
|
398
|
+
const object = __eslint_react_var.getVariableInitNode(__eslint_react_var.findVariable(node, initialScope), 0);
|
|
399
|
+
return isNewResizeObserver(object);
|
|
400
|
+
}
|
|
401
|
+
case node.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression: return isFromObserver(context, node.object);
|
|
402
|
+
default: return false;
|
|
403
|
+
}
|
|
455
404
|
}
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
default:
|
|
463
|
-
return "other";
|
|
464
|
-
}
|
|
405
|
+
function getCallKind$1(context, node) {
|
|
406
|
+
switch (true) {
|
|
407
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("observe", "unobserve", "disconnect"))(node.callee.name) && isFromObserver(context, node.callee): return node.callee.name;
|
|
408
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("observe", "unobserve", "disconnect"))(node.callee.property.name) && isFromObserver(context, node.callee): return node.callee.property.name;
|
|
409
|
+
default: return "other";
|
|
410
|
+
}
|
|
465
411
|
}
|
|
466
|
-
function
|
|
467
|
-
|
|
412
|
+
function getFunctionKind(node) {
|
|
413
|
+
return getPhaseKindOfFunction(node) ?? "other";
|
|
468
414
|
}
|
|
469
415
|
var no_leaked_resize_observer_default = createRule({
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
416
|
+
meta: {
|
|
417
|
+
type: "problem",
|
|
418
|
+
docs: {
|
|
419
|
+
description: "Prevents leaked `ResizeObserver` in a component or custom Hook.",
|
|
420
|
+
[Symbol.for("rule_features")]: RULE_FEATURES$1
|
|
421
|
+
},
|
|
422
|
+
messages: {
|
|
423
|
+
expectedDisconnectInControlFlow: "Dynamically added 'ResizeObserver.observe' should be cleared all at once using 'ResizeObserver.disconnect' in the cleanup function.",
|
|
424
|
+
expectedDisconnectOrUnobserveInCleanup: "A 'ResizeObserver' instance created in 'useEffect' must be disconnected in the cleanup function.",
|
|
425
|
+
unexpectedFloatingInstance: "A 'ResizeObserver' instance created in component or custom Hook must be assigned to a variable for proper cleanup."
|
|
426
|
+
},
|
|
427
|
+
schema: []
|
|
428
|
+
},
|
|
429
|
+
name: RULE_NAME$1,
|
|
430
|
+
create: create$1,
|
|
431
|
+
defaultOptions: []
|
|
486
432
|
});
|
|
487
|
-
function
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
for (const oEntry of oentries) {
|
|
591
|
-
if (uentries.some((uEntry) => ER4__namespace.isInstanceIdEqual(context, uEntry.element, oEntry.element))) {
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
context.report({ messageId: "expectedDisconnectOrUnobserveInCleanup", node: oEntry.node });
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
};
|
|
433
|
+
function create$1(context) {
|
|
434
|
+
if (!context.sourceCode.text.includes("ResizeObserver")) return {};
|
|
435
|
+
const fEntries = [];
|
|
436
|
+
const observers = [];
|
|
437
|
+
const oEntries = [];
|
|
438
|
+
const uEntries = [];
|
|
439
|
+
const dEntries = [];
|
|
440
|
+
return {
|
|
441
|
+
[":function"](node) {
|
|
442
|
+
const kind = getFunctionKind(node);
|
|
443
|
+
fEntries.push({
|
|
444
|
+
kind,
|
|
445
|
+
node
|
|
446
|
+
});
|
|
447
|
+
},
|
|
448
|
+
[":function:exit"]() {
|
|
449
|
+
fEntries.pop();
|
|
450
|
+
},
|
|
451
|
+
["CallExpression"](node) {
|
|
452
|
+
if (node.callee.type !== __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression) return;
|
|
453
|
+
const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
|
|
454
|
+
if (fKind == null || !__eslint_react_core.ComponentPhaseRelevance.has(fKind)) return;
|
|
455
|
+
const { object } = node.callee;
|
|
456
|
+
(0, ts_pattern.match)(getCallKind$1(context, node)).with("disconnect", () => {
|
|
457
|
+
dEntries.push({
|
|
458
|
+
kind: "disconnect",
|
|
459
|
+
node,
|
|
460
|
+
callee: node.callee,
|
|
461
|
+
observer: object,
|
|
462
|
+
observerKind: "ResizeObserver",
|
|
463
|
+
phase: fKind
|
|
464
|
+
});
|
|
465
|
+
}).with("observe", () => {
|
|
466
|
+
const [element] = node.arguments;
|
|
467
|
+
if (element == null) return;
|
|
468
|
+
oEntries.push({
|
|
469
|
+
kind: "observe",
|
|
470
|
+
node,
|
|
471
|
+
callee: node.callee,
|
|
472
|
+
element,
|
|
473
|
+
observer: object,
|
|
474
|
+
observerKind: "ResizeObserver",
|
|
475
|
+
phase: fKind
|
|
476
|
+
});
|
|
477
|
+
}).with("unobserve", () => {
|
|
478
|
+
const [element] = node.arguments;
|
|
479
|
+
if (element == null) return;
|
|
480
|
+
uEntries.push({
|
|
481
|
+
kind: "unobserve",
|
|
482
|
+
node,
|
|
483
|
+
callee: node.callee,
|
|
484
|
+
element,
|
|
485
|
+
observer: object,
|
|
486
|
+
observerKind: "ResizeObserver",
|
|
487
|
+
phase: fKind
|
|
488
|
+
});
|
|
489
|
+
}).otherwise(() => null);
|
|
490
|
+
},
|
|
491
|
+
["NewExpression"](node) {
|
|
492
|
+
const fEntry = fEntries.findLast((x) => x.kind !== "other");
|
|
493
|
+
if (fEntry == null) return;
|
|
494
|
+
if (!__eslint_react_core.ComponentPhaseRelevance.has(fEntry.kind)) return;
|
|
495
|
+
if (!isNewResizeObserver(node)) return;
|
|
496
|
+
const id = __eslint_react_core.getInstanceId(node);
|
|
497
|
+
if (id == null) {
|
|
498
|
+
context.report({
|
|
499
|
+
messageId: "unexpectedFloatingInstance",
|
|
500
|
+
node
|
|
501
|
+
});
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
observers.push({
|
|
505
|
+
id,
|
|
506
|
+
node,
|
|
507
|
+
phase: fEntry.kind,
|
|
508
|
+
phaseNode: fEntry.node
|
|
509
|
+
});
|
|
510
|
+
},
|
|
511
|
+
["Program:exit"]() {
|
|
512
|
+
for (const { id, node, phaseNode } of observers) {
|
|
513
|
+
if (dEntries.some((e) => __eslint_react_core.isInstanceIdEqual(context, e.observer, id))) continue;
|
|
514
|
+
const oentries = oEntries.filter((e) => __eslint_react_core.isInstanceIdEqual(context, e.observer, id));
|
|
515
|
+
const uentries = uEntries.filter((e) => __eslint_react_core.isInstanceIdEqual(context, e.observer, id));
|
|
516
|
+
const isDynamic = (node$1) => node$1?.type === __typescript_eslint_utils.AST_NODE_TYPES.CallExpression || isConditional(node$1);
|
|
517
|
+
const isPhaseNode = (node$1) => node$1 === phaseNode;
|
|
518
|
+
const hasDynamicallyAdded = oentries.some((e) => !isPhaseNode(__eslint_react_ast.findParentNode(e.node, (0, __eslint_react_eff.or)(isDynamic, isPhaseNode))));
|
|
519
|
+
if (hasDynamicallyAdded) {
|
|
520
|
+
context.report({
|
|
521
|
+
messageId: "expectedDisconnectInControlFlow",
|
|
522
|
+
node
|
|
523
|
+
});
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
for (const oEntry of oentries) {
|
|
527
|
+
if (uentries.some((uEntry) => __eslint_react_core.isInstanceIdEqual(context, uEntry.element, oEntry.element))) continue;
|
|
528
|
+
context.report({
|
|
529
|
+
messageId: "expectedDisconnectOrUnobserveInCleanup",
|
|
530
|
+
node: oEntry.node
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
};
|
|
599
536
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
537
|
+
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/rules/no-leaked-timeout.ts
|
|
540
|
+
const RULE_NAME = "no-leaked-timeout";
|
|
541
|
+
const RULE_FEATURES = [];
|
|
542
|
+
function getCallKind(node) {
|
|
543
|
+
switch (true) {
|
|
544
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("setTimeout", "clearTimeout"))(node.callee.name): return node.callee.name;
|
|
545
|
+
case node.callee.type === __typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.AST_NODE_TYPES.Identifier && (0, ts_pattern.isMatching)(ts_pattern.P.union("setTimeout", "clearTimeout"))(node.callee.property.name): return node.callee.property.name;
|
|
546
|
+
default: return "other";
|
|
547
|
+
}
|
|
611
548
|
}
|
|
612
549
|
var no_leaked_timeout_default = createRule({
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
550
|
+
meta: {
|
|
551
|
+
type: "problem",
|
|
552
|
+
docs: {
|
|
553
|
+
description: "Prevents leaked `setTimeout` in a component or custom Hook.",
|
|
554
|
+
[Symbol.for("rule_features")]: RULE_FEATURES
|
|
555
|
+
},
|
|
556
|
+
messages: {
|
|
557
|
+
expectedClearTimeoutInCleanup: "A 'setTimeout' created in '{{ kind }}' must be cleared with 'clearTimeout' in the cleanup function.",
|
|
558
|
+
expectedClearTimeoutInUnmount: "A 'setTimeout' created in '{{ kind }}' must be cleared with 'clearTimeout' in the 'componentWillUnmount' method.",
|
|
559
|
+
expectedTimeoutId: "A 'setTimeout' must be assigned to a variable for proper cleanup."
|
|
560
|
+
},
|
|
561
|
+
schema: []
|
|
562
|
+
},
|
|
563
|
+
name: RULE_NAME,
|
|
564
|
+
create,
|
|
565
|
+
defaultOptions: []
|
|
629
566
|
});
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
data: {
|
|
710
|
-
kind: "componentDidMount"
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
continue;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
};
|
|
567
|
+
function create(context) {
|
|
568
|
+
if (!context.sourceCode.text.includes("setTimeout")) return {};
|
|
569
|
+
const fEntries = [];
|
|
570
|
+
const sEntries = [];
|
|
571
|
+
const rEntries = [];
|
|
572
|
+
function isInverseEntry(a, b) {
|
|
573
|
+
return __eslint_react_core.isInstanceIdEqual(context, a.timerId, b.timerId);
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
[":function"](node) {
|
|
577
|
+
const kind = getPhaseKindOfFunction(node) ?? "other";
|
|
578
|
+
fEntries.push({
|
|
579
|
+
kind,
|
|
580
|
+
node
|
|
581
|
+
});
|
|
582
|
+
},
|
|
583
|
+
[":function:exit"]() {
|
|
584
|
+
fEntries.pop();
|
|
585
|
+
},
|
|
586
|
+
["CallExpression"](node) {
|
|
587
|
+
const fEntry = fEntries.findLast((f) => f.kind !== "other");
|
|
588
|
+
if (!__eslint_react_core.ComponentPhaseRelevance.has(fEntry?.kind)) return;
|
|
589
|
+
switch (getCallKind(node)) {
|
|
590
|
+
case "setTimeout": {
|
|
591
|
+
const timeoutIdNode = __eslint_react_var.getVariableDeclaratorId(node);
|
|
592
|
+
if (timeoutIdNode == null) {
|
|
593
|
+
context.report({
|
|
594
|
+
messageId: "expectedTimeoutId",
|
|
595
|
+
node
|
|
596
|
+
});
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
sEntries.push({
|
|
600
|
+
kind: "timeout",
|
|
601
|
+
node,
|
|
602
|
+
callee: node.callee,
|
|
603
|
+
phase: fEntry.kind,
|
|
604
|
+
timerId: timeoutIdNode
|
|
605
|
+
});
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
case "clearTimeout": {
|
|
609
|
+
const [timeoutIdNode] = node.arguments;
|
|
610
|
+
if (timeoutIdNode == null) break;
|
|
611
|
+
rEntries.push({
|
|
612
|
+
kind: "timeout",
|
|
613
|
+
node,
|
|
614
|
+
callee: node.callee,
|
|
615
|
+
phase: fEntry.kind,
|
|
616
|
+
timerId: timeoutIdNode
|
|
617
|
+
});
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
["Program:exit"]() {
|
|
623
|
+
for (const sEntry of sEntries) {
|
|
624
|
+
if (rEntries.some((rEntry) => isInverseEntry(sEntry, rEntry))) continue;
|
|
625
|
+
switch (sEntry.phase) {
|
|
626
|
+
case "setup":
|
|
627
|
+
case "cleanup":
|
|
628
|
+
context.report({
|
|
629
|
+
messageId: "expectedClearTimeoutInCleanup",
|
|
630
|
+
node: sEntry.node,
|
|
631
|
+
data: { kind: "useEffect" }
|
|
632
|
+
});
|
|
633
|
+
continue;
|
|
634
|
+
case "mount":
|
|
635
|
+
case "unmount":
|
|
636
|
+
context.report({
|
|
637
|
+
messageId: "expectedClearTimeoutInUnmount",
|
|
638
|
+
node: sEntry.node,
|
|
639
|
+
data: { kind: "componentDidMount" }
|
|
640
|
+
});
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
};
|
|
718
646
|
}
|
|
719
647
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/plugin.ts
|
|
650
|
+
const plugin = {
|
|
651
|
+
meta: {
|
|
652
|
+
name,
|
|
653
|
+
version
|
|
654
|
+
},
|
|
655
|
+
rules: {
|
|
656
|
+
"no-leaked-event-listener": no_leaked_event_listener_default,
|
|
657
|
+
"no-leaked-interval": no_leaked_interval_default,
|
|
658
|
+
"no-leaked-resize-observer": no_leaked_resize_observer_default,
|
|
659
|
+
"no-leaked-timeout": no_leaked_timeout_default
|
|
660
|
+
}
|
|
732
661
|
};
|
|
733
662
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/index.ts
|
|
665
|
+
const { toFlatConfig, toLegacyConfig } = (0, __eslint_react_shared.getConfigAdapters)("react-web-api", plugin);
|
|
666
|
+
var src_default = {
|
|
667
|
+
...plugin,
|
|
668
|
+
configs: {
|
|
669
|
+
["recommended"]: toFlatConfig(recommended_exports),
|
|
670
|
+
["recommended-legacy"]: toLegacyConfig(recommended_exports)
|
|
671
|
+
}
|
|
742
672
|
};
|
|
743
673
|
|
|
744
|
-
|
|
674
|
+
//#endregion
|
|
675
|
+
module.exports = src_default;
|