eslint-plugin-react-web-api 2.0.0-beta.20 → 2.0.0-beta.200

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