eslint-plugin-react-web-api 2.0.0-next.17 → 2.0.0-next.171

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