eslint-plugin-react-web-api 1.34.0-beta.0 → 1.34.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +400 -396
  2. package/dist/index.mjs +400 -396
  3. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -55,7 +55,7 @@ var settings = {
55
55
 
56
56
  // package.json
57
57
  var name2 = "eslint-plugin-react-web-api";
58
- var version = "1.34.0-beta.0";
58
+ var version = "1.34.0-beta.1";
59
59
  var createRule = shared.createRuleForPlugin("web-api");
60
60
  function getPhaseKindOfFunction(node) {
61
61
  return tsPattern.match(node).when(core.isFunctionOfUseEffectSetup, () => "setup").when(core.isFunctionOfUseEffectCleanup, () => "cleanup").when(core.isFunctionOfComponentDidMount, () => "mount").when(core.isFunctionOfComponentWillUnmount, () => "unmount").otherwise(() => eff._);
@@ -158,139 +158,140 @@ var no_leaked_event_listener_default = createRule({
158
158
  schema: []
159
159
  },
160
160
  name: RULE_NAME,
161
- create(context) {
162
- if (!context.sourceCode.text.includes("addEventListener")) {
163
- return {};
161
+ create,
162
+ defaultOptions: []
163
+ });
164
+ function create(context) {
165
+ if (!context.sourceCode.text.includes("addEventListener")) {
166
+ return {};
167
+ }
168
+ if (!/use\w*Effect|componentDidMount|componentWillUnmount/u.test(context.sourceCode.text)) {
169
+ return {};
170
+ }
171
+ const fEntries = [];
172
+ const aEntries = [];
173
+ const rEntries = [];
174
+ const abortedSignals = [];
175
+ function isSameObject(a, b) {
176
+ switch (true) {
177
+ case (a.type === utils.AST_NODE_TYPES.MemberExpression && b.type === utils.AST_NODE_TYPES.MemberExpression):
178
+ return AST__namespace.isNodeEqual(a.object, b.object);
179
+ // TODO: Maybe there other cases to consider here.
180
+ default:
181
+ return false;
164
182
  }
165
- if (!/use\w*Effect|componentDidMount|componentWillUnmount/u.test(context.sourceCode.text)) {
166
- return {};
183
+ }
184
+ function isInverseEntry(aEntry, rEntry) {
185
+ const { type: aType, callee: aCallee, capture: aCapture, listener: aListener, phase: aPhase } = aEntry;
186
+ const { type: rType, callee: rCallee, capture: rCapture, listener: rListener, phase: rPhase } = rEntry;
187
+ if (!core.isInversePhase(aPhase, rPhase)) {
188
+ return false;
167
189
  }
168
- const fEntries = [];
169
- const aEntries = [];
170
- const rEntries = [];
171
- const abortedSignals = [];
172
- function isSameObject(a, b) {
173
- switch (true) {
174
- case (a.type === utils.AST_NODE_TYPES.MemberExpression && b.type === utils.AST_NODE_TYPES.MemberExpression):
175
- return AST__namespace.isNodeEqual(a.object, b.object);
176
- // TODO: Maybe there other cases to consider here.
177
- default:
178
- return false;
179
- }
190
+ return isSameObject(aCallee, rCallee) && AST__namespace.isNodeEqual(aListener, rListener) && VAR__namespace.isNodeValueEqual(aType, rType, [
191
+ context.sourceCode.getScope(aType),
192
+ context.sourceCode.getScope(rType)
193
+ ]) && aCapture === rCapture;
194
+ }
195
+ function checkInlineFunction(node, callKind, options) {
196
+ const listener = node.arguments.at(1);
197
+ if (!AST__namespace.isFunction(listener)) {
198
+ return;
180
199
  }
181
- function isInverseEntry(aEntry, rEntry) {
182
- const { type: aType, callee: aCallee, capture: aCapture, listener: aListener, phase: aPhase } = aEntry;
183
- const { type: rType, callee: rCallee, capture: rCapture, listener: rListener, phase: rPhase } = rEntry;
184
- if (!core.isInversePhase(aPhase, rPhase)) {
185
- return false;
186
- }
187
- return isSameObject(aCallee, rCallee) && AST__namespace.isNodeEqual(aListener, rListener) && VAR__namespace.isNodeValueEqual(aType, rType, [
188
- context.sourceCode.getScope(aType),
189
- context.sourceCode.getScope(rType)
190
- ]) && aCapture === rCapture;
200
+ if (options.signal != null) {
201
+ return;
191
202
  }
192
- function checkInlineFunction(node, callKind, options) {
193
- const listener = node.arguments.at(1);
194
- if (!AST__namespace.isFunction(listener)) {
203
+ context.report({
204
+ messageId: "unexpectedInlineFunction",
205
+ node: listener,
206
+ data: { eventMethodKind: callKind }
207
+ });
208
+ }
209
+ return {
210
+ [":function"](node) {
211
+ const kind = getFunctionKind(node);
212
+ fEntries.push({ kind, node });
213
+ },
214
+ [":function:exit"]() {
215
+ fEntries.pop();
216
+ },
217
+ ["CallExpression"](node) {
218
+ const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
219
+ if (fKind == null) {
195
220
  return;
196
221
  }
197
- if (options.signal != null) {
222
+ if (!core.ERPhaseRelevance.has(fKind)) {
198
223
  return;
199
224
  }
200
- context.report({
201
- messageId: "unexpectedInlineFunction",
202
- node: listener,
203
- data: { eventMethodKind: callKind }
204
- });
205
- }
206
- return {
207
- [":function"](node) {
208
- const kind = getFunctionKind(node);
209
- fEntries.push({ kind, node });
210
- },
211
- [":function:exit"]() {
212
- fEntries.pop();
213
- },
214
- ["CallExpression"](node) {
215
- const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
216
- if (fKind == null) {
225
+ tsPattern.match(getCallKind(node)).with("addEventListener", (callKind) => {
226
+ const [type, listener, options] = node.arguments;
227
+ if (type == null || listener == null) {
217
228
  return;
218
229
  }
219
- if (!core.ERPhaseRelevance.has(fKind)) {
230
+ const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
231
+ const { callee } = node;
232
+ checkInlineFunction(node, callKind, opts);
233
+ aEntries.push({
234
+ ...opts,
235
+ kind: "addEventListener",
236
+ type,
237
+ node,
238
+ callee,
239
+ listener,
240
+ phase: fKind
241
+ });
242
+ }).with("removeEventListener", (callKind) => {
243
+ const [type, listener, options] = node.arguments;
244
+ if (type == null || listener == null) {
220
245
  return;
221
246
  }
222
- tsPattern.match(getCallKind(node)).with("addEventListener", (callKind) => {
223
- const [type, listener, options] = node.arguments;
224
- if (type == null || listener == null) {
225
- return;
226
- }
227
- const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
228
- const { callee } = node;
229
- checkInlineFunction(node, callKind, opts);
230
- aEntries.push({
231
- ...opts,
232
- kind: "addEventListener",
233
- type,
234
- node,
235
- callee,
236
- listener,
237
- phase: fKind
238
- });
239
- }).with("removeEventListener", (callKind) => {
240
- const [type, listener, options] = node.arguments;
241
- if (type == null || listener == null) {
242
- return;
243
- }
244
- const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
245
- const { callee } = node;
246
- checkInlineFunction(node, callKind, opts);
247
- rEntries.push({
248
- ...opts,
249
- kind: "removeEventListener",
250
- type,
251
- node,
252
- callee,
253
- listener,
254
- phase: fKind
255
- });
256
- }).with("abort", () => {
257
- abortedSignals.push(node.callee);
258
- }).otherwise(() => eff._);
259
- },
260
- ["Program:exit"]() {
261
- for (const aEntry of aEntries) {
262
- const signal = aEntry.signal;
263
- if (signal != null && abortedSignals.some((a) => isSameObject(a, signal))) {
247
+ const opts = options == null ? defaultOptions : getOptions(options, context.sourceCode.getScope(options));
248
+ const { callee } = node;
249
+ checkInlineFunction(node, callKind, opts);
250
+ rEntries.push({
251
+ ...opts,
252
+ kind: "removeEventListener",
253
+ type,
254
+ node,
255
+ callee,
256
+ listener,
257
+ phase: fKind
258
+ });
259
+ }).with("abort", () => {
260
+ abortedSignals.push(node.callee);
261
+ }).otherwise(() => eff._);
262
+ },
263
+ ["Program:exit"]() {
264
+ for (const aEntry of aEntries) {
265
+ const signal = aEntry.signal;
266
+ if (signal != null && abortedSignals.some((a) => isSameObject(a, signal))) {
267
+ continue;
268
+ }
269
+ if (rEntries.some((rEntry) => isInverseEntry(aEntry, rEntry))) {
270
+ continue;
271
+ }
272
+ switch (aEntry.phase) {
273
+ case "setup":
274
+ case "cleanup":
275
+ context.report({
276
+ messageId: "expectedRemoveEventListenerInCleanup",
277
+ node: aEntry.node,
278
+ data: {
279
+ effectMethodKind: "useEffect"
280
+ }
281
+ });
264
282
  continue;
265
- }
266
- if (rEntries.some((rEntry) => isInverseEntry(aEntry, rEntry))) {
283
+ case "mount":
284
+ case "unmount":
285
+ context.report({
286
+ messageId: "expectedRemoveEventListenerInUnmount",
287
+ node: aEntry.node
288
+ });
267
289
  continue;
268
- }
269
- switch (aEntry.phase) {
270
- case "setup":
271
- case "cleanup":
272
- context.report({
273
- messageId: "expectedRemoveEventListenerInCleanup",
274
- node: aEntry.node,
275
- data: {
276
- effectMethodKind: "useEffect"
277
- }
278
- });
279
- continue;
280
- case "mount":
281
- case "unmount":
282
- context.report({
283
- messageId: "expectedRemoveEventListenerInUnmount",
284
- node: aEntry.node
285
- });
286
- continue;
287
- }
288
290
  }
289
291
  }
290
- };
291
- },
292
- defaultOptions: []
293
- });
292
+ }
293
+ };
294
+ }
294
295
  var RULE_NAME2 = "no-leaked-interval";
295
296
  var RULE_FEATURES2 = [
296
297
  "CHK"
@@ -320,107 +321,108 @@ var no_leaked_interval_default = createRule({
320
321
  schema: []
321
322
  },
322
323
  name: RULE_NAME2,
323
- create(context) {
324
- if (!context.sourceCode.text.includes("setInterval")) {
325
- return {};
326
- }
327
- const fEntries = [];
328
- const sEntries = [];
329
- const cEntries = [];
330
- function isInverseEntry(a, b) {
331
- return core.isInstanceIdEqual(context, a.timerId, b.timerId);
332
- }
333
- return {
334
- [":function"](node) {
335
- const kind = getPhaseKindOfFunction(node) ?? "other";
336
- fEntries.push({ kind, node });
337
- },
338
- [":function:exit"]() {
339
- fEntries.pop();
340
- },
341
- ["CallExpression"](node) {
342
- switch (getCallKind2(node)) {
343
- case "setInterval": {
344
- const fEntry = fEntries.findLast((x) => x.kind !== "other");
345
- if (fEntry == null) {
346
- break;
347
- }
348
- if (!core.ERPhaseRelevance.has(fEntry.kind)) {
349
- break;
350
- }
351
- const intervalIdNode = VAR__namespace.getVariableDeclaratorId(node);
352
- if (intervalIdNode == null) {
353
- context.report({
354
- messageId: "expectedIntervalId",
355
- node
356
- });
357
- break;
358
- }
359
- sEntries.push({
360
- kind: "interval",
361
- node,
362
- callee: node.callee,
363
- phase: fEntry.kind,
364
- timerId: intervalIdNode
365
- });
324
+ create: create2,
325
+ defaultOptions: []
326
+ });
327
+ function create2(context) {
328
+ if (!context.sourceCode.text.includes("setInterval")) {
329
+ return {};
330
+ }
331
+ const fEntries = [];
332
+ const sEntries = [];
333
+ const cEntries = [];
334
+ function isInverseEntry(a, b) {
335
+ return core.isInstanceIdEqual(context, a.timerId, b.timerId);
336
+ }
337
+ return {
338
+ [":function"](node) {
339
+ const kind = getPhaseKindOfFunction(node) ?? "other";
340
+ fEntries.push({ kind, node });
341
+ },
342
+ [":function:exit"]() {
343
+ fEntries.pop();
344
+ },
345
+ ["CallExpression"](node) {
346
+ switch (getCallKind2(node)) {
347
+ case "setInterval": {
348
+ const fEntry = fEntries.findLast((x) => x.kind !== "other");
349
+ if (fEntry == null) {
366
350
  break;
367
351
  }
368
- case "clearInterval": {
369
- const fEntry = fEntries.findLast((x) => x.kind !== "other");
370
- if (fEntry == null) {
371
- break;
372
- }
373
- if (!core.ERPhaseRelevance.has(fEntry.kind)) {
374
- break;
375
- }
376
- const [intervalIdNode] = node.arguments;
377
- if (intervalIdNode == null) {
378
- break;
379
- }
380
- cEntries.push({
381
- kind: "interval",
382
- node,
383
- callee: node.callee,
384
- phase: fEntry.kind,
385
- timerId: intervalIdNode
352
+ if (!core.ERPhaseRelevance.has(fEntry.kind)) {
353
+ break;
354
+ }
355
+ const intervalIdNode = VAR__namespace.getVariableDeclaratorId(node);
356
+ if (intervalIdNode == null) {
357
+ context.report({
358
+ messageId: "expectedIntervalId",
359
+ node
386
360
  });
387
361
  break;
388
362
  }
363
+ sEntries.push({
364
+ kind: "interval",
365
+ node,
366
+ callee: node.callee,
367
+ phase: fEntry.kind,
368
+ timerId: intervalIdNode
369
+ });
370
+ break;
389
371
  }
390
- },
391
- ["Program:exit"]() {
392
- for (const sEntry of sEntries) {
393
- if (cEntries.some((cEntry) => isInverseEntry(sEntry, cEntry))) {
394
- continue;
372
+ case "clearInterval": {
373
+ const fEntry = fEntries.findLast((x) => x.kind !== "other");
374
+ if (fEntry == null) {
375
+ break;
395
376
  }
396
- switch (sEntry.phase) {
397
- case "setup":
398
- case "cleanup":
399
- context.report({
400
- messageId: "expectedClearIntervalInCleanup",
401
- node: sEntry.node,
402
- data: {
403
- kind: "useEffect"
404
- }
405
- });
406
- continue;
407
- case "mount":
408
- case "unmount":
409
- context.report({
410
- messageId: "expectedClearIntervalInUnmount",
411
- node: sEntry.node,
412
- data: {
413
- kind: "componentDidMount"
414
- }
415
- });
416
- continue;
377
+ if (!core.ERPhaseRelevance.has(fEntry.kind)) {
378
+ break;
417
379
  }
380
+ const [intervalIdNode] = node.arguments;
381
+ if (intervalIdNode == null) {
382
+ break;
383
+ }
384
+ cEntries.push({
385
+ kind: "interval",
386
+ node,
387
+ callee: node.callee,
388
+ phase: fEntry.kind,
389
+ timerId: intervalIdNode
390
+ });
391
+ break;
418
392
  }
419
393
  }
420
- };
421
- },
422
- defaultOptions: []
423
- });
394
+ },
395
+ ["Program:exit"]() {
396
+ for (const sEntry of sEntries) {
397
+ if (cEntries.some((cEntry) => isInverseEntry(sEntry, cEntry))) {
398
+ continue;
399
+ }
400
+ switch (sEntry.phase) {
401
+ case "setup":
402
+ case "cleanup":
403
+ context.report({
404
+ messageId: "expectedClearIntervalInCleanup",
405
+ node: sEntry.node,
406
+ data: {
407
+ kind: "useEffect"
408
+ }
409
+ });
410
+ continue;
411
+ case "mount":
412
+ case "unmount":
413
+ context.report({
414
+ messageId: "expectedClearIntervalInUnmount",
415
+ node: sEntry.node,
416
+ data: {
417
+ kind: "componentDidMount"
418
+ }
419
+ });
420
+ continue;
421
+ }
422
+ }
423
+ }
424
+ };
425
+ }
424
426
  var RULE_NAME3 = "no-leaked-resize-observer";
425
427
  var RULE_FEATURES3 = [
426
428
  "CHK"
@@ -469,121 +471,122 @@ var no_leaked_resize_observer_default = createRule({
469
471
  schema: []
470
472
  },
471
473
  name: RULE_NAME3,
472
- create(context) {
473
- if (!context.sourceCode.text.includes("ResizeObserver")) {
474
- return {};
475
- }
476
- const fEntries = [];
477
- const observers = [];
478
- const oEntries = [];
479
- const uEntries = [];
480
- const dEntries = [];
481
- return {
482
- [":function"](node) {
483
- const kind = getFunctionKind2(node);
484
- fEntries.push({ kind, node });
485
- },
486
- [":function:exit"]() {
487
- fEntries.pop();
488
- },
489
- ["CallExpression"](node) {
490
- if (node.callee.type !== utils.AST_NODE_TYPES.MemberExpression) {
491
- return;
492
- }
493
- const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
494
- if (fKind == null || !core.ERPhaseRelevance.has(fKind)) {
495
- return;
496
- }
497
- const { object } = node.callee;
498
- tsPattern.match(getCallKind3(context, node)).with("disconnect", () => {
499
- dEntries.push({
500
- kind: "disconnect",
501
- node,
502
- callee: node.callee,
503
- observer: object,
504
- observerKind: "ResizeObserver",
505
- phase: fKind
506
- });
507
- }).with("observe", () => {
508
- const [element] = node.arguments;
509
- if (element == null) {
510
- return;
511
- }
512
- oEntries.push({
513
- kind: "observe",
514
- node,
515
- callee: node.callee,
516
- element,
517
- observer: object,
518
- observerKind: "ResizeObserver",
519
- phase: fKind
520
- });
521
- }).with("unobserve", () => {
522
- const [element] = node.arguments;
523
- if (element == null) {
524
- return;
525
- }
526
- uEntries.push({
527
- kind: "unobserve",
528
- node,
529
- callee: node.callee,
530
- element,
531
- observer: object,
532
- observerKind: "ResizeObserver",
533
- phase: fKind
534
- });
535
- }).otherwise(() => eff._);
536
- },
537
- ["NewExpression"](node) {
538
- const fEntry = fEntries.findLast((x) => x.kind !== "other");
539
- if (fEntry == null) return;
540
- if (!core.ERPhaseRelevance.has(fEntry.kind)) {
541
- return;
542
- }
543
- if (!isNewResizeObserver(node)) {
474
+ create: create3,
475
+ defaultOptions: []
476
+ });
477
+ function create3(context) {
478
+ if (!context.sourceCode.text.includes("ResizeObserver")) {
479
+ return {};
480
+ }
481
+ const fEntries = [];
482
+ const observers = [];
483
+ const oEntries = [];
484
+ const uEntries = [];
485
+ const dEntries = [];
486
+ return {
487
+ [":function"](node) {
488
+ const kind = getFunctionKind2(node);
489
+ fEntries.push({ kind, node });
490
+ },
491
+ [":function:exit"]() {
492
+ fEntries.pop();
493
+ },
494
+ ["CallExpression"](node) {
495
+ if (node.callee.type !== utils.AST_NODE_TYPES.MemberExpression) {
496
+ return;
497
+ }
498
+ const fKind = fEntries.findLast((x) => x.kind !== "other")?.kind;
499
+ if (fKind == null || !core.ERPhaseRelevance.has(fKind)) {
500
+ return;
501
+ }
502
+ const { object } = node.callee;
503
+ tsPattern.match(getCallKind3(context, node)).with("disconnect", () => {
504
+ dEntries.push({
505
+ kind: "disconnect",
506
+ node,
507
+ callee: node.callee,
508
+ observer: object,
509
+ observerKind: "ResizeObserver",
510
+ phase: fKind
511
+ });
512
+ }).with("observe", () => {
513
+ const [element] = node.arguments;
514
+ if (element == null) {
544
515
  return;
545
516
  }
546
- const id = core.getInstanceId(node);
547
- if (id == null) {
548
- context.report({
549
- messageId: "unexpectedFloatingInstance",
550
- node
551
- });
517
+ oEntries.push({
518
+ kind: "observe",
519
+ node,
520
+ callee: node.callee,
521
+ element,
522
+ observer: object,
523
+ observerKind: "ResizeObserver",
524
+ phase: fKind
525
+ });
526
+ }).with("unobserve", () => {
527
+ const [element] = node.arguments;
528
+ if (element == null) {
552
529
  return;
553
530
  }
554
- observers.push({
555
- id,
531
+ uEntries.push({
532
+ kind: "unobserve",
556
533
  node,
557
- phase: fEntry.kind,
558
- phaseNode: fEntry.node
534
+ callee: node.callee,
535
+ element,
536
+ observer: object,
537
+ observerKind: "ResizeObserver",
538
+ phase: fKind
559
539
  });
560
- },
561
- ["Program:exit"]() {
562
- for (const { id, node, phaseNode } of observers) {
563
- if (dEntries.some((e) => core.isInstanceIdEqual(context, e.observer, id))) {
564
- continue;
565
- }
566
- const oentries = oEntries.filter((e) => core.isInstanceIdEqual(context, e.observer, id));
567
- const uentries = uEntries.filter((e) => core.isInstanceIdEqual(context, e.observer, id));
568
- const isDynamic = (node2) => node2?.type === utils.AST_NODE_TYPES.CallExpression || AST__namespace.isConditional(node2);
569
- const isPhaseNode = (node2) => node2 === phaseNode;
570
- const hasDynamicallyAdded = oentries.some((e) => !isPhaseNode(AST__namespace.findParentNode(e.node, eff.or(isDynamic, isPhaseNode))));
571
- if (hasDynamicallyAdded) {
572
- context.report({ messageId: "expectedDisconnectInControlFlow", node });
540
+ }).otherwise(() => eff._);
541
+ },
542
+ ["NewExpression"](node) {
543
+ const fEntry = fEntries.findLast((x) => x.kind !== "other");
544
+ if (fEntry == null) return;
545
+ if (!core.ERPhaseRelevance.has(fEntry.kind)) {
546
+ return;
547
+ }
548
+ if (!isNewResizeObserver(node)) {
549
+ return;
550
+ }
551
+ const id = core.getInstanceId(node);
552
+ if (id == null) {
553
+ context.report({
554
+ messageId: "unexpectedFloatingInstance",
555
+ node
556
+ });
557
+ return;
558
+ }
559
+ observers.push({
560
+ id,
561
+ node,
562
+ phase: fEntry.kind,
563
+ phaseNode: fEntry.node
564
+ });
565
+ },
566
+ ["Program:exit"]() {
567
+ for (const { id, node, phaseNode } of observers) {
568
+ if (dEntries.some((e) => core.isInstanceIdEqual(context, e.observer, id))) {
569
+ continue;
570
+ }
571
+ const oentries = oEntries.filter((e) => core.isInstanceIdEqual(context, e.observer, id));
572
+ const uentries = uEntries.filter((e) => core.isInstanceIdEqual(context, e.observer, id));
573
+ const isDynamic = (node2) => node2?.type === utils.AST_NODE_TYPES.CallExpression || AST__namespace.isConditional(node2);
574
+ const isPhaseNode = (node2) => node2 === phaseNode;
575
+ const hasDynamicallyAdded = oentries.some((e) => !isPhaseNode(AST__namespace.findParentNode(e.node, eff.or(isDynamic, isPhaseNode))));
576
+ if (hasDynamicallyAdded) {
577
+ context.report({ messageId: "expectedDisconnectInControlFlow", node });
578
+ continue;
579
+ }
580
+ for (const oEntry of oentries) {
581
+ if (uentries.some((uEntry) => core.isInstanceIdEqual(context, uEntry.element, oEntry.element))) {
573
582
  continue;
574
583
  }
575
- for (const oEntry of oentries) {
576
- if (uentries.some((uEntry) => core.isInstanceIdEqual(context, uEntry.element, oEntry.element))) {
577
- continue;
578
- }
579
- context.report({ messageId: "expectedDisconnectOrUnobserveInCleanup", node: oEntry.node });
580
- }
584
+ context.report({ messageId: "expectedDisconnectOrUnobserveInCleanup", node: oEntry.node });
581
585
  }
582
586
  }
583
- };
584
- },
585
- defaultOptions: []
586
- });
587
+ }
588
+ };
589
+ }
587
590
  var RULE_NAME4 = "no-leaked-timeout";
588
591
  var RULE_FEATURES4 = [
589
592
  "CHK"
@@ -613,97 +616,98 @@ var no_leaked_timeout_default = createRule({
613
616
  schema: []
614
617
  },
615
618
  name: RULE_NAME4,
616
- create(context) {
617
- if (!context.sourceCode.text.includes("setTimeout")) {
618
- return {};
619
- }
620
- const fEntries = [];
621
- const sEntries = [];
622
- const rEntries = [];
623
- function isInverseEntry(a, b) {
624
- return core.isInstanceIdEqual(context, a.timerId, b.timerId);
625
- }
626
- return {
627
- [":function"](node) {
628
- const kind = getPhaseKindOfFunction(node) ?? "other";
629
- fEntries.push({ kind, node });
630
- },
631
- [":function:exit"]() {
632
- fEntries.pop();
633
- },
634
- ["CallExpression"](node) {
635
- const fEntry = fEntries.findLast((f) => f.kind !== "other");
636
- if (!core.ERPhaseRelevance.has(fEntry?.kind)) {
637
- return;
638
- }
639
- switch (getCallKind4(node)) {
640
- case "setTimeout": {
641
- const timeoutIdNode = VAR__namespace.getVariableDeclaratorId(node);
642
- if (timeoutIdNode == null) {
643
- context.report({
644
- messageId: "expectedTimeoutId",
645
- node
646
- });
647
- break;
648
- }
649
- sEntries.push({
650
- kind: "timeout",
651
- node,
652
- callee: node.callee,
653
- phase: fEntry.kind,
654
- timerId: timeoutIdNode
619
+ create: create4,
620
+ defaultOptions: []
621
+ });
622
+ function create4(context) {
623
+ if (!context.sourceCode.text.includes("setTimeout")) {
624
+ return {};
625
+ }
626
+ const fEntries = [];
627
+ const sEntries = [];
628
+ const rEntries = [];
629
+ function isInverseEntry(a, b) {
630
+ return core.isInstanceIdEqual(context, a.timerId, b.timerId);
631
+ }
632
+ return {
633
+ [":function"](node) {
634
+ const kind = getPhaseKindOfFunction(node) ?? "other";
635
+ fEntries.push({ kind, node });
636
+ },
637
+ [":function:exit"]() {
638
+ fEntries.pop();
639
+ },
640
+ ["CallExpression"](node) {
641
+ const fEntry = fEntries.findLast((f) => f.kind !== "other");
642
+ if (!core.ERPhaseRelevance.has(fEntry?.kind)) {
643
+ return;
644
+ }
645
+ switch (getCallKind4(node)) {
646
+ case "setTimeout": {
647
+ const timeoutIdNode = VAR__namespace.getVariableDeclaratorId(node);
648
+ if (timeoutIdNode == null) {
649
+ context.report({
650
+ messageId: "expectedTimeoutId",
651
+ node
655
652
  });
656
653
  break;
657
654
  }
658
- case "clearTimeout": {
659
- const [timeoutIdNode] = node.arguments;
660
- if (timeoutIdNode == null) {
661
- break;
662
- }
663
- rEntries.push({
664
- kind: "timeout",
665
- node,
666
- callee: node.callee,
667
- phase: fEntry.kind,
668
- timerId: timeoutIdNode
669
- });
655
+ sEntries.push({
656
+ kind: "timeout",
657
+ node,
658
+ callee: node.callee,
659
+ phase: fEntry.kind,
660
+ timerId: timeoutIdNode
661
+ });
662
+ break;
663
+ }
664
+ case "clearTimeout": {
665
+ const [timeoutIdNode] = node.arguments;
666
+ if (timeoutIdNode == null) {
670
667
  break;
671
668
  }
669
+ rEntries.push({
670
+ kind: "timeout",
671
+ node,
672
+ callee: node.callee,
673
+ phase: fEntry.kind,
674
+ timerId: timeoutIdNode
675
+ });
676
+ break;
672
677
  }
673
- },
674
- ["Program:exit"]() {
675
- for (const sEntry of sEntries) {
676
- if (rEntries.some((rEntry) => isInverseEntry(sEntry, rEntry))) {
678
+ }
679
+ },
680
+ ["Program:exit"]() {
681
+ for (const sEntry of sEntries) {
682
+ if (rEntries.some((rEntry) => isInverseEntry(sEntry, rEntry))) {
683
+ continue;
684
+ }
685
+ switch (sEntry.phase) {
686
+ case "setup":
687
+ case "cleanup":
688
+ context.report({
689
+ messageId: "expectedClearTimeoutInCleanup",
690
+ node: sEntry.node,
691
+ data: {
692
+ kind: "useEffect"
693
+ }
694
+ });
695
+ continue;
696
+ case "mount":
697
+ case "unmount":
698
+ context.report({
699
+ messageId: "expectedClearTimeoutInUnmount",
700
+ node: sEntry.node,
701
+ data: {
702
+ kind: "componentDidMount"
703
+ }
704
+ });
677
705
  continue;
678
- }
679
- switch (sEntry.phase) {
680
- case "setup":
681
- case "cleanup":
682
- context.report({
683
- messageId: "expectedClearTimeoutInCleanup",
684
- node: sEntry.node,
685
- data: {
686
- kind: "useEffect"
687
- }
688
- });
689
- continue;
690
- case "mount":
691
- case "unmount":
692
- context.report({
693
- messageId: "expectedClearTimeoutInUnmount",
694
- node: sEntry.node,
695
- data: {
696
- kind: "componentDidMount"
697
- }
698
- });
699
- continue;
700
- }
701
706
  }
702
707
  }
703
- };
704
- },
705
- defaultOptions: []
706
- });
708
+ }
709
+ };
710
+ }
707
711
 
708
712
  // src/plugin.ts
709
713
  var plugin = {