eslint-plugin-runtime-cleanup 1.2.8
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/_internal/ast-node.d.ts +19 -0
- package/dist/_internal/ast-node.d.ts.map +1 -0
- package/dist/_internal/ast-node.js +42 -0
- package/dist/_internal/ast-node.js.map +1 -0
- package/dist/_internal/bounded-cache.d.ts +37 -0
- package/dist/_internal/bounded-cache.d.ts.map +1 -0
- package/dist/_internal/bounded-cache.js +63 -0
- package/dist/_internal/bounded-cache.js.map +1 -0
- package/dist/_internal/cycle-safe-linked-search.d.ts +48 -0
- package/dist/_internal/cycle-safe-linked-search.d.ts.map +1 -0
- package/dist/_internal/cycle-safe-linked-search.js +70 -0
- package/dist/_internal/cycle-safe-linked-search.js.map +1 -0
- package/dist/_internal/expression-boolean-memoizer.d.ts +17 -0
- package/dist/_internal/expression-boolean-memoizer.d.ts.map +1 -0
- package/dist/_internal/expression-boolean-memoizer.js +22 -0
- package/dist/_internal/expression-boolean-memoizer.js.map +1 -0
- package/dist/_internal/filter-callback.d.ts +52 -0
- package/dist/_internal/filter-callback.d.ts.map +1 -0
- package/dist/_internal/filter-callback.js +108 -0
- package/dist/_internal/filter-callback.js.map +1 -0
- package/dist/_internal/floating-resource.d.ts +29 -0
- package/dist/_internal/floating-resource.d.ts.map +1 -0
- package/dist/_internal/floating-resource.js +114 -0
- package/dist/_internal/floating-resource.js.map +1 -0
- package/dist/_internal/member-call.d.ts +53 -0
- package/dist/_internal/member-call.d.ts.map +1 -0
- package/dist/_internal/member-call.js +61 -0
- package/dist/_internal/member-call.js.map +1 -0
- package/dist/_internal/normalize-expression-text.d.ts +21 -0
- package/dist/_internal/normalize-expression-text.d.ts.map +1 -0
- package/dist/_internal/normalize-expression-text.js +186 -0
- package/dist/_internal/normalize-expression-text.js.map +1 -0
- package/dist/_internal/nullish-comparison.d.ts +44 -0
- package/dist/_internal/nullish-comparison.d.ts.map +1 -0
- package/dist/_internal/nullish-comparison.js +162 -0
- package/dist/_internal/nullish-comparison.js.map +1 -0
- package/dist/_internal/plugin-settings.d.ts +30 -0
- package/dist/_internal/plugin-settings.d.ts.map +1 -0
- package/dist/_internal/plugin-settings.js +90 -0
- package/dist/_internal/plugin-settings.js.map +1 -0
- package/dist/_internal/report-adapter.d.ts +24 -0
- package/dist/_internal/report-adapter.d.ts.map +1 -0
- package/dist/_internal/report-adapter.js +35 -0
- package/dist/_internal/report-adapter.js.map +1 -0
- package/dist/_internal/rule-catalog.d.ts +47 -0
- package/dist/_internal/rule-catalog.d.ts.map +1 -0
- package/dist/_internal/rule-catalog.js +97 -0
- package/dist/_internal/rule-catalog.js.map +1 -0
- package/dist/_internal/rule-docs-metadata.d.ts +35 -0
- package/dist/_internal/rule-docs-metadata.d.ts.map +1 -0
- package/dist/_internal/rule-docs-metadata.js +172 -0
- package/dist/_internal/rule-docs-metadata.js.map +1 -0
- package/dist/_internal/rule-docs-url.d.ts +15 -0
- package/dist/_internal/rule-docs-url.d.ts.map +1 -0
- package/dist/_internal/rule-docs-url.js +15 -0
- package/dist/_internal/rule-docs-url.js.map +1 -0
- package/dist/_internal/rules-registry.d.ts +11 -0
- package/dist/_internal/rules-registry.d.ts.map +1 -0
- package/dist/_internal/rules-registry.js +53 -0
- package/dist/_internal/rules-registry.js.map +1 -0
- package/dist/_internal/runtime-cleanup-config-references.d.ts +38 -0
- package/dist/_internal/runtime-cleanup-config-references.d.ts.map +1 -0
- package/dist/_internal/runtime-cleanup-config-references.js +78 -0
- package/dist/_internal/runtime-cleanup-config-references.js.map +1 -0
- package/dist/_internal/safe-type-operation.d.ts +89 -0
- package/dist/_internal/safe-type-operation.d.ts.map +1 -0
- package/dist/_internal/safe-type-operation.js +147 -0
- package/dist/_internal/safe-type-operation.js.map +1 -0
- package/dist/_internal/scope-variable.d.ts +17 -0
- package/dist/_internal/scope-variable.d.ts.map +1 -0
- package/dist/_internal/scope-variable.js +30 -0
- package/dist/_internal/scope-variable.js.map +1 -0
- package/dist/_internal/type-checker.d.ts +11 -0
- package/dist/_internal/type-checker.d.ts.map +1 -0
- package/dist/_internal/type-checker.js +25 -0
- package/dist/_internal/type-checker.js.map +1 -0
- package/dist/_internal/type-predicate-autofix-safety.d.ts +16 -0
- package/dist/_internal/type-predicate-autofix-safety.d.ts.map +1 -0
- package/dist/_internal/type-predicate-autofix-safety.js +54 -0
- package/dist/_internal/type-predicate-autofix-safety.js.map +1 -0
- package/dist/_internal/type-reference-node.d.ts +23 -0
- package/dist/_internal/type-reference-node.d.ts.map +1 -0
- package/dist/_internal/type-reference-node.js +41 -0
- package/dist/_internal/type-reference-node.js.map +1 -0
- package/dist/_internal/typed-rule.d.ts +91 -0
- package/dist/_internal/typed-rule.d.ts.map +1 -0
- package/dist/_internal/typed-rule.js +121 -0
- package/dist/_internal/typed-rule.js.map +1 -0
- package/dist/_internal/value-rewrite-autofix-safety.d.ts +29 -0
- package/dist/_internal/value-rewrite-autofix-safety.d.ts.map +1 -0
- package/dist/_internal/value-rewrite-autofix-safety.js +108 -0
- package/dist/_internal/value-rewrite-autofix-safety.js.map +1 -0
- package/dist/plugin.cjs +3693 -0
- package/dist/plugin.cjs.map +7 -0
- package/dist/plugin.d.cts +75 -0
- package/dist/plugin.d.ts +75 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +223 -0
- package/dist/plugin.js.map +1 -0
- package/dist/rules/no-floating-abort-controllers.d.ts +9 -0
- package/dist/rules/no-floating-abort-controllers.d.ts.map +1 -0
- package/dist/rules/no-floating-abort-controllers.js +144 -0
- package/dist/rules/no-floating-abort-controllers.js.map +1 -0
- package/dist/rules/no-floating-audio-contexts.d.ts +9 -0
- package/dist/rules/no-floating-audio-contexts.d.ts.map +1 -0
- package/dist/rules/no-floating-audio-contexts.js +95 -0
- package/dist/rules/no-floating-audio-contexts.js.map +1 -0
- package/dist/rules/no-floating-broadcast-channels.d.ts +9 -0
- package/dist/rules/no-floating-broadcast-channels.d.ts.map +1 -0
- package/dist/rules/no-floating-broadcast-channels.js +151 -0
- package/dist/rules/no-floating-broadcast-channels.js.map +1 -0
- package/dist/rules/no-floating-child-processes.d.ts +9 -0
- package/dist/rules/no-floating-child-processes.d.ts.map +1 -0
- package/dist/rules/no-floating-child-processes.js +259 -0
- package/dist/rules/no-floating-child-processes.js.map +1 -0
- package/dist/rules/no-floating-disposable-stacks.d.ts +9 -0
- package/dist/rules/no-floating-disposable-stacks.d.ts.map +1 -0
- package/dist/rules/no-floating-disposable-stacks.js +177 -0
- package/dist/rules/no-floating-disposable-stacks.js.map +1 -0
- package/dist/rules/no-floating-file-watchers.d.ts +9 -0
- package/dist/rules/no-floating-file-watchers.d.ts.map +1 -0
- package/dist/rules/no-floating-file-watchers.js +241 -0
- package/dist/rules/no-floating-file-watchers.js.map +1 -0
- package/dist/rules/no-floating-geolocation-watches.d.ts +9 -0
- package/dist/rules/no-floating-geolocation-watches.d.ts.map +1 -0
- package/dist/rules/no-floating-geolocation-watches.js +156 -0
- package/dist/rules/no-floating-geolocation-watches.js.map +1 -0
- package/dist/rules/no-floating-infinite-animations.d.ts +9 -0
- package/dist/rules/no-floating-infinite-animations.d.ts.map +1 -0
- package/dist/rules/no-floating-infinite-animations.js +131 -0
- package/dist/rules/no-floating-infinite-animations.js.map +1 -0
- package/dist/rules/no-floating-media-streams.d.ts +9 -0
- package/dist/rules/no-floating-media-streams.d.ts.map +1 -0
- package/dist/rules/no-floating-media-streams.js +175 -0
- package/dist/rules/no-floating-media-streams.js.map +1 -0
- package/dist/rules/no-floating-message-channels.d.ts +9 -0
- package/dist/rules/no-floating-message-channels.d.ts.map +1 -0
- package/dist/rules/no-floating-message-channels.js +150 -0
- package/dist/rules/no-floating-message-channels.js.map +1 -0
- package/dist/rules/no-floating-network-connections.d.ts +9 -0
- package/dist/rules/no-floating-network-connections.d.ts.map +1 -0
- package/dist/rules/no-floating-network-connections.js +170 -0
- package/dist/rules/no-floating-network-connections.js.map +1 -0
- package/dist/rules/no-floating-object-urls.d.ts +9 -0
- package/dist/rules/no-floating-object-urls.d.ts.map +1 -0
- package/dist/rules/no-floating-object-urls.js +83 -0
- package/dist/rules/no-floating-object-urls.js.map +1 -0
- package/dist/rules/no-floating-observers.d.ts +9 -0
- package/dist/rules/no-floating-observers.d.ts.map +1 -0
- package/dist/rules/no-floating-observers.js +160 -0
- package/dist/rules/no-floating-observers.js.map +1 -0
- package/dist/rules/no-floating-servers.d.ts +9 -0
- package/dist/rules/no-floating-servers.d.ts.map +1 -0
- package/dist/rules/no-floating-servers.js +282 -0
- package/dist/rules/no-floating-servers.js.map +1 -0
- package/dist/rules/no-floating-streams.d.ts +9 -0
- package/dist/rules/no-floating-streams.d.ts.map +1 -0
- package/dist/rules/no-floating-streams.js +222 -0
- package/dist/rules/no-floating-streams.js.map +1 -0
- package/dist/rules/no-floating-timers.d.ts +9 -0
- package/dist/rules/no-floating-timers.d.ts.map +1 -0
- package/dist/rules/no-floating-timers.js +145 -0
- package/dist/rules/no-floating-timers.js.map +1 -0
- package/dist/rules/no-floating-wake-locks.d.ts +9 -0
- package/dist/rules/no-floating-wake-locks.d.ts.map +1 -0
- package/dist/rules/no-floating-wake-locks.js +159 -0
- package/dist/rules/no-floating-wake-locks.js.map +1 -0
- package/dist/rules/no-floating-web-stream-locks.d.ts +9 -0
- package/dist/rules/no-floating-web-stream-locks.d.ts.map +1 -0
- package/dist/rules/no-floating-web-stream-locks.js +87 -0
- package/dist/rules/no-floating-web-stream-locks.js.map +1 -0
- package/dist/rules/no-floating-workers.d.ts +9 -0
- package/dist/rules/no-floating-workers.d.ts.map +1 -0
- package/dist/rules/no-floating-workers.js +185 -0
- package/dist/rules/no-floating-workers.js.map +1 -0
- package/dist/rules/no-unmanaged-event-listeners.d.ts +9 -0
- package/dist/rules/no-unmanaged-event-listeners.d.ts.map +1 -0
- package/dist/rules/no-unmanaged-event-listeners.js +210 -0
- package/dist/rules/no-unmanaged-event-listeners.js.map +1 -0
- package/docs/rules/getting-started.md +29 -0
- package/docs/rules/guides/adoption-checklist.md +31 -0
- package/docs/rules/guides/preset-selection-strategy.md +24 -0
- package/docs/rules/guides/rollout-and-fix-safety.md +42 -0
- package/docs/rules/guides/snapshot-testing.md +20 -0
- package/docs/rules/guides/type-aware-linting-readiness.md +20 -0
- package/docs/rules/no-floating-abort-controllers.md +126 -0
- package/docs/rules/no-floating-audio-contexts.md +104 -0
- package/docs/rules/no-floating-broadcast-channels.md +105 -0
- package/docs/rules/no-floating-child-processes.md +123 -0
- package/docs/rules/no-floating-disposable-stacks.md +118 -0
- package/docs/rules/no-floating-file-watchers.md +111 -0
- package/docs/rules/no-floating-geolocation-watches.md +95 -0
- package/docs/rules/no-floating-infinite-animations.md +110 -0
- package/docs/rules/no-floating-media-streams.md +113 -0
- package/docs/rules/no-floating-message-channels.md +114 -0
- package/docs/rules/no-floating-network-connections.md +116 -0
- package/docs/rules/no-floating-object-urls.md +102 -0
- package/docs/rules/no-floating-observers.md +108 -0
- package/docs/rules/no-floating-servers.md +127 -0
- package/docs/rules/no-floating-streams.md +120 -0
- package/docs/rules/no-floating-timers.md +120 -0
- package/docs/rules/no-floating-wake-locks.md +109 -0
- package/docs/rules/no-floating-web-stream-locks.md +105 -0
- package/docs/rules/no-floating-workers.md +123 -0
- package/docs/rules/no-unmanaged-event-listeners.md +143 -0
- package/docs/rules/overview.md +44 -0
- package/docs/rules/presets/all.md +35 -0
- package/docs/rules/presets/experimental.md +44 -0
- package/docs/rules/presets/index.md +54 -0
- package/docs/rules/presets/minimal.md +17 -0
- package/docs/rules/presets/recommended-type-checked.md +43 -0
- package/docs/rules/presets/recommended.md +34 -0
- package/docs/rules/presets/strict.md +36 -0
- package/package.json +323 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# no-floating-file-watchers
|
|
2
|
+
|
|
3
|
+
Require Node.js file watcher handles to be retained so they can be closed.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R010
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets Node.js `fs.watch()` calls from `fs` and `node:fs`:
|
|
10
|
+
|
|
11
|
+
- `watch`
|
|
12
|
+
- `fs.watch`
|
|
13
|
+
- `require("fs").watch`
|
|
14
|
+
- `require("node:fs").watch`
|
|
15
|
+
|
|
16
|
+
The rule reports watcher handles that are immediately discarded or chained
|
|
17
|
+
directly into a method call other than `.close()`. It does not target
|
|
18
|
+
`node:fs/promises` async iterator watchers because those use different
|
|
19
|
+
ownership and cancellation patterns.
|
|
20
|
+
|
|
21
|
+
## What this rule reports
|
|
22
|
+
|
|
23
|
+
The rule reports:
|
|
24
|
+
|
|
25
|
+
- standalone watcher creation such as `watch("src", onChange);`
|
|
26
|
+
- voided watcher creation such as `void fs.watch("src", onChange);`
|
|
27
|
+
- discarded CommonJS namespace or destructured `watch` calls
|
|
28
|
+
- immediate event registration on an unowned watcher such as
|
|
29
|
+
`fs.watch("src").on("error", onError);`
|
|
30
|
+
|
|
31
|
+
It intentionally does not require same-function `close()` calls. Watcher
|
|
32
|
+
ownership can be transferred by returning the watcher or passing it to a
|
|
33
|
+
dedicated lifecycle manager.
|
|
34
|
+
|
|
35
|
+
## Why this rule exists
|
|
36
|
+
|
|
37
|
+
`fs.watch()` returns an `FSWatcher`. Active watchers can keep the Node.js event
|
|
38
|
+
loop alive and continue receiving file-system events until they are closed. If
|
|
39
|
+
the watcher handle is discarded, later cleanup cannot reliably call `.close()`
|
|
40
|
+
or coordinate shutdown behavior.
|
|
41
|
+
|
|
42
|
+
## Incorrect
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { watch } from "node:fs";
|
|
46
|
+
|
|
47
|
+
watch("src", onChange);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import * as fs from "node:fs";
|
|
52
|
+
|
|
53
|
+
void fs.watch("src", onChange);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import * as fs from "node:fs";
|
|
58
|
+
|
|
59
|
+
fs.watch("src", onChange).on("error", onError);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Correct
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { watch } from "node:fs";
|
|
66
|
+
|
|
67
|
+
const watcher = watch("src", onChange);
|
|
68
|
+
|
|
69
|
+
watcher.close();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import * as fs from "node:fs";
|
|
74
|
+
|
|
75
|
+
return fs.watch("src", onChange);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
registerWatcher(fs.watch("src", onChange));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Behavior and migration notes
|
|
83
|
+
|
|
84
|
+
Keep the watcher handle in the owner that will close it during teardown. For
|
|
85
|
+
development tools, that is usually the process-level watcher manager. For
|
|
86
|
+
library code, returning the watcher or passing it to a lifecycle manager makes
|
|
87
|
+
the ownership contract explicit.
|
|
88
|
+
|
|
89
|
+
This rule does not autofix. Introducing a variable without a matching close
|
|
90
|
+
path would hide the lifecycle problem instead of fixing it.
|
|
91
|
+
|
|
92
|
+
## ESLint flat config example
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
96
|
+
|
|
97
|
+
export default [
|
|
98
|
+
runtimeCleanup.configs.recommended,
|
|
99
|
+
];
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## When not to use it
|
|
103
|
+
|
|
104
|
+
Do not enable this rule for scripts where a watcher intentionally lives until
|
|
105
|
+
process exit and no explicit shutdown path is needed. Prefer a narrow disable
|
|
106
|
+
comment with a reason for those cases.
|
|
107
|
+
|
|
108
|
+
## Further reading
|
|
109
|
+
|
|
110
|
+
- [Node.js: `fs.watch()`](https://nodejs.org/api/fs.html#fswatchfilename-options-listener)
|
|
111
|
+
- [Node.js: `FSWatcher.close()`](https://nodejs.org/api/fs.html#watcherclose)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# no-floating-geolocation-watches
|
|
2
|
+
|
|
3
|
+
Require geolocation watch IDs to be retained so they can be cleared.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R014
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets browser geolocation watcher registration:
|
|
10
|
+
|
|
11
|
+
- `navigator.geolocation.watchPosition(...)`
|
|
12
|
+
- `window.navigator.geolocation.watchPosition(...)`
|
|
13
|
+
- `globalThis.navigator.geolocation.watchPosition(...)`
|
|
14
|
+
|
|
15
|
+
The rule reports calls whose returned watch ID is immediately discarded. The
|
|
16
|
+
watch ID is the handle needed to unregister the watcher with
|
|
17
|
+
`navigator.geolocation.clearWatch(...)`.
|
|
18
|
+
|
|
19
|
+
## What this rule reports
|
|
20
|
+
|
|
21
|
+
The rule reports:
|
|
22
|
+
|
|
23
|
+
- standalone watcher registration such as
|
|
24
|
+
`navigator.geolocation.watchPosition(onPosition);`
|
|
25
|
+
- voided watcher registration such as
|
|
26
|
+
`void navigator.geolocation.watchPosition(onPosition);`
|
|
27
|
+
- TypeScript-wrapped expressions that still discard the returned watch ID
|
|
28
|
+
|
|
29
|
+
It intentionally does not require same-function `clearWatch()` calls. Ownership
|
|
30
|
+
can be transferred to a component instance, route lifecycle, returned value, or
|
|
31
|
+
watch manager.
|
|
32
|
+
|
|
33
|
+
## Why this rule exists
|
|
34
|
+
|
|
35
|
+
`watchPosition()` installs repeated location monitoring callbacks. The browser
|
|
36
|
+
returns a numeric ID for that watch, and `clearWatch(id)` uses that ID to remove
|
|
37
|
+
the registered monitoring handlers. If the ID is discarded, the code that owns
|
|
38
|
+
the lifecycle cannot reliably unregister the watcher.
|
|
39
|
+
|
|
40
|
+
## Incorrect
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
navigator.geolocation.watchPosition(onPosition);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
void navigator.geolocation.watchPosition(onPosition, onError);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Correct
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const watchId = navigator.geolocation.watchPosition(onPosition);
|
|
54
|
+
|
|
55
|
+
navigator.geolocation.clearWatch(watchId);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
return navigator.geolocation.watchPosition(onPosition);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
registerWatch(navigator.geolocation.watchPosition(onPosition));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Behavior and migration notes
|
|
67
|
+
|
|
68
|
+
Store the returned watch ID in the owner that will call `clearWatch()`. In UI
|
|
69
|
+
code, that is usually the component, route, or view lifecycle. In shared
|
|
70
|
+
libraries, returning the ID or passing it to a watcher manager keeps ownership
|
|
71
|
+
explicit.
|
|
72
|
+
|
|
73
|
+
This rule does not autofix. Choosing the owner and cleanup point is a semantic
|
|
74
|
+
decision.
|
|
75
|
+
|
|
76
|
+
## ESLint flat config example
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
80
|
+
|
|
81
|
+
export default [
|
|
82
|
+
runtimeCleanup.configs.recommended,
|
|
83
|
+
];
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## When not to use it
|
|
87
|
+
|
|
88
|
+
Do not enable this rule for code that intentionally creates page-lifetime
|
|
89
|
+
geolocation watches and does not need explicit teardown. Prefer a narrow disable
|
|
90
|
+
comment with a reason in those files.
|
|
91
|
+
|
|
92
|
+
## Further reading
|
|
93
|
+
|
|
94
|
+
- [MDN: `Geolocation.watchPosition()`](https://developer.mozilla.org/docs/Web/API/Geolocation/watchPosition)
|
|
95
|
+
- [MDN: `Geolocation.clearWatch()`](https://developer.mozilla.org/docs/Web/API/Geolocation/clearWatch)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# no-floating-infinite-animations
|
|
2
|
+
|
|
3
|
+
Require infinite Web Animations to be retained so they can be canceled.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R020
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This type-aware rule targets `Element#animate(...)` calls whose timing options
|
|
10
|
+
explicitly use infinite iterations:
|
|
11
|
+
|
|
12
|
+
- `{ iterations: Infinity }`
|
|
13
|
+
- `{ iterations: +Infinity }`
|
|
14
|
+
- `{ iterations: Number.POSITIVE_INFINITY }`
|
|
15
|
+
- `{ iterations: globalThis.Number.POSITIVE_INFINITY }`
|
|
16
|
+
|
|
17
|
+
The rule uses TypeScript parser services to confirm that the receiver is a DOM
|
|
18
|
+
`Element`. That keeps project-local `animate(...)` methods out of scope.
|
|
19
|
+
|
|
20
|
+
## What this rule reports
|
|
21
|
+
|
|
22
|
+
The rule reports infinite animations when the returned `Animation` is discarded
|
|
23
|
+
or immediately used through a non-cleanup member:
|
|
24
|
+
|
|
25
|
+
- standalone infinite `element.animate(...)` calls
|
|
26
|
+
- immediate chains such as `element.animate(...).play()`
|
|
27
|
+
|
|
28
|
+
Finite one-shot animations are intentionally allowed. Fire-and-forget finite
|
|
29
|
+
animations are common and do not have the same long-running cleanup risk.
|
|
30
|
+
Immediate `cancel()` and `finish()` calls are allowed.
|
|
31
|
+
|
|
32
|
+
## Why this rule exists
|
|
33
|
+
|
|
34
|
+
`Element#animate()` returns an `Animation` object. Infinite animations continue
|
|
35
|
+
until canceled, finished, the effect is removed, or the document lifecycle ends.
|
|
36
|
+
If the returned `Animation` is discarded, component or route cleanup code cannot
|
|
37
|
+
reliably stop it.
|
|
38
|
+
|
|
39
|
+
## Incorrect
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
element.animate(keyframes, {
|
|
43
|
+
duration: 1000,
|
|
44
|
+
iterations: Infinity,
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
element.animate(keyframes, {
|
|
50
|
+
iterations: Number.POSITIVE_INFINITY,
|
|
51
|
+
}).play();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Correct
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const animation = element.animate(keyframes, {
|
|
58
|
+
duration: 1000,
|
|
59
|
+
iterations: Infinity,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
cleanupCallbacks.add(() => animation.cancel());
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
function startPulse(element: Element) {
|
|
67
|
+
return element.animate(keyframes, {
|
|
68
|
+
duration: 1000,
|
|
69
|
+
iterations: Infinity,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
element.animate(keyframes, {
|
|
76
|
+
iterations: 1,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Behavior and migration notes
|
|
81
|
+
|
|
82
|
+
This rule is deliberately narrower than a general `Element#animate()` rule. It
|
|
83
|
+
only targets explicitly infinite animations because one-shot animations are often
|
|
84
|
+
safe to let complete naturally.
|
|
85
|
+
|
|
86
|
+
This rule does not autofix. A safe fix needs to choose the correct lifecycle
|
|
87
|
+
owner and cancellation point.
|
|
88
|
+
|
|
89
|
+
## ESLint flat config example
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
93
|
+
|
|
94
|
+
export default [
|
|
95
|
+
runtimeCleanup.configs["recommended-type-checked"],
|
|
96
|
+
];
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## When not to use it
|
|
100
|
+
|
|
101
|
+
Do not enable this rule without TypeScript parser services. For intentionally
|
|
102
|
+
page-lifetime infinite decorative animations, use a narrow disable comment near
|
|
103
|
+
the animation start.
|
|
104
|
+
|
|
105
|
+
## Further reading
|
|
106
|
+
|
|
107
|
+
- [MDN: `Element.animate()`](https://developer.mozilla.org/docs/Web/API/Element/animate)
|
|
108
|
+
- [MDN: `Animation.cancel()`](https://developer.mozilla.org/docs/Web/API/Animation/cancel)
|
|
109
|
+
- [MDN: `Animation.finish()`](https://developer.mozilla.org/docs/Web/API/Animation/finish)
|
|
110
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# no-floating-media-streams
|
|
2
|
+
|
|
3
|
+
Require captured `MediaStream` handles to be retained so their tracks can be
|
|
4
|
+
stopped.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R015
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets browser media capture APIs:
|
|
11
|
+
|
|
12
|
+
- `navigator.mediaDevices.getUserMedia(...)`
|
|
13
|
+
- `navigator.mediaDevices.getDisplayMedia(...)`
|
|
14
|
+
- `window.navigator.mediaDevices.getUserMedia(...)`
|
|
15
|
+
- `globalThis.navigator.mediaDevices.getDisplayMedia(...)`
|
|
16
|
+
|
|
17
|
+
The rule reports capture requests whose resulting `MediaStream` is immediately
|
|
18
|
+
discarded, including `await` expressions that do not store, return, or pass the
|
|
19
|
+
stream to another owner.
|
|
20
|
+
|
|
21
|
+
## What this rule reports
|
|
22
|
+
|
|
23
|
+
The rule reports:
|
|
24
|
+
|
|
25
|
+
- standalone media capture requests
|
|
26
|
+
- voided media capture requests
|
|
27
|
+
- `await navigator.mediaDevices.getUserMedia(...)` used as a standalone
|
|
28
|
+
expression
|
|
29
|
+
- `await navigator.mediaDevices.getDisplayMedia(...)` used as a standalone
|
|
30
|
+
expression
|
|
31
|
+
|
|
32
|
+
It intentionally allows promise chains and lifecycle-manager calls that receive
|
|
33
|
+
the stream. The rule focuses on obviously unowned stream handles, not on proving
|
|
34
|
+
that every possible owner eventually stops every track.
|
|
35
|
+
|
|
36
|
+
## Why this rule exists
|
|
37
|
+
|
|
38
|
+
`getUserMedia()` and `getDisplayMedia()` return `MediaStream` objects backed by
|
|
39
|
+
media tracks. Tracks can keep cameras, microphones, screen capture, indicators,
|
|
40
|
+
or permission-sensitive resources active. `MediaStreamTrack.stop()` tells the
|
|
41
|
+
browser that a track's source is no longer needed. If the stream handle is
|
|
42
|
+
discarded, cleanup code cannot reliably stop its tracks.
|
|
43
|
+
|
|
44
|
+
## Incorrect
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
navigator.mediaDevices.getUserMedia({ video: true });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
void navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
async function openCamera() {
|
|
56
|
+
await navigator.mediaDevices.getUserMedia({ video: true });
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Correct
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
async function openCamera() {
|
|
64
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
65
|
+
|
|
66
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
async function openScreen() {
|
|
72
|
+
return navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
async function openCamera() {
|
|
78
|
+
registerStream(
|
|
79
|
+
await navigator.mediaDevices.getUserMedia({ audio: true }),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Behavior and migration notes
|
|
85
|
+
|
|
86
|
+
Store the stream in the lifecycle owner that will stop its tracks. For UI code,
|
|
87
|
+
that is usually a component cleanup hook, route cleanup hook, recording
|
|
88
|
+
controller, or media session manager.
|
|
89
|
+
|
|
90
|
+
This rule does not autofix. Introducing a local variable without a matching
|
|
91
|
+
track-stop lifecycle would hide the cleanup bug instead of solving it.
|
|
92
|
+
|
|
93
|
+
## ESLint flat config example
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
97
|
+
|
|
98
|
+
export default [
|
|
99
|
+
runtimeCleanup.configs.recommended,
|
|
100
|
+
];
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## When not to use it
|
|
104
|
+
|
|
105
|
+
Do not enable this rule for short demo snippets where the browser page lifetime
|
|
106
|
+
is the intended cleanup boundary. Prefer narrow disable comments for those
|
|
107
|
+
snippets rather than weakening the rule globally.
|
|
108
|
+
|
|
109
|
+
## Further reading
|
|
110
|
+
|
|
111
|
+
- [MDN: `MediaDevices.getUserMedia()`](https://developer.mozilla.org/docs/Web/API/MediaDevices/getUserMedia)
|
|
112
|
+
- [MDN: `MediaDevices.getDisplayMedia()`](https://developer.mozilla.org/docs/Web/API/MediaDevices/getDisplayMedia)
|
|
113
|
+
- [MDN: `MediaStreamTrack.stop()`](https://developer.mozilla.org/docs/Web/API/MediaStreamTrack/stop)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# no-floating-message-channels
|
|
2
|
+
|
|
3
|
+
Require `MessageChannel` ports to be retained so they can be closed.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R012
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets browser `MessageChannel` constructors:
|
|
10
|
+
|
|
11
|
+
- `MessageChannel`
|
|
12
|
+
- `window.MessageChannel`
|
|
13
|
+
- `self.MessageChannel`
|
|
14
|
+
- `globalThis.MessageChannel`
|
|
15
|
+
|
|
16
|
+
The rule reports channel instances that are immediately discarded or whose
|
|
17
|
+
`port1`/`port2` properties are accessed directly from the temporary channel
|
|
18
|
+
object. Keeping only one inline port loses the peer port and makes full
|
|
19
|
+
teardown ambiguous.
|
|
20
|
+
|
|
21
|
+
## What this rule reports
|
|
22
|
+
|
|
23
|
+
The rule reports:
|
|
24
|
+
|
|
25
|
+
- standalone channel construction such as `new MessageChannel();`
|
|
26
|
+
- voided channel construction such as `void new MessageChannel();`
|
|
27
|
+
- immediate port sends such as `new MessageChannel().port1.postMessage(message);`
|
|
28
|
+
- retaining a single inline port such as `const port = new MessageChannel().port1;`
|
|
29
|
+
|
|
30
|
+
It intentionally does not require same-function `close()` calls. Ownership can
|
|
31
|
+
be transferred to a component instance, channel manager, returned value, or both
|
|
32
|
+
destructured `MessagePort` handles.
|
|
33
|
+
|
|
34
|
+
## Why this rule exists
|
|
35
|
+
|
|
36
|
+
`MessageChannel` creates two linked `MessagePort` handles. `MessagePort.close()`
|
|
37
|
+
disconnects a port so it is no longer active. If the channel object is discarded
|
|
38
|
+
or only one port is retained from an inline expression, code cannot reliably
|
|
39
|
+
close both sides of the channel during cleanup.
|
|
40
|
+
|
|
41
|
+
## Incorrect
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
new MessageChannel();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
void new MessageChannel();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
new MessageChannel().port1.postMessage(message);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const port = new MessageChannel().port2;
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Correct
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const channel = new MessageChannel();
|
|
63
|
+
|
|
64
|
+
channel.port1.addEventListener("message", onMessage);
|
|
65
|
+
channel.port1.close();
|
|
66
|
+
channel.port2.close();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const { port1, port2 } = new MessageChannel();
|
|
71
|
+
|
|
72
|
+
port1.postMessage(message);
|
|
73
|
+
port1.close();
|
|
74
|
+
port2.close();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
return new MessageChannel();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
registerChannel(new MessageChannel());
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Behavior and migration notes
|
|
86
|
+
|
|
87
|
+
Store either the `MessageChannel` object or both `MessagePort` handles in the
|
|
88
|
+
owner that will close them. For UI code, that owner is usually a component,
|
|
89
|
+
worker bridge, route lifecycle, or application-level channel manager.
|
|
90
|
+
|
|
91
|
+
This rule does not autofix. Introducing a variable without selecting the owner
|
|
92
|
+
and teardown point would hide the lifecycle problem instead of solving it.
|
|
93
|
+
|
|
94
|
+
## ESLint flat config example
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
98
|
+
|
|
99
|
+
export default [
|
|
100
|
+
runtimeCleanup.configs.recommended,
|
|
101
|
+
];
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## When not to use it
|
|
105
|
+
|
|
106
|
+
Do not enable this rule for code that intentionally creates runtime-lifetime
|
|
107
|
+
message ports and does not need explicit teardown. Prefer a narrow disable
|
|
108
|
+
comment with a reason when a channel is meant to live for the whole runtime.
|
|
109
|
+
|
|
110
|
+
## Further reading
|
|
111
|
+
|
|
112
|
+
- [MDN: `MessageChannel`](https://developer.mozilla.org/docs/Web/API/MessageChannel)
|
|
113
|
+
- [MDN: `MessagePort`](https://developer.mozilla.org/docs/Web/API/MessagePort)
|
|
114
|
+
- [MDN: `MessagePort.close()`](https://developer.mozilla.org/docs/Web/API/MessagePort/close)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# no-floating-network-connections
|
|
2
|
+
|
|
3
|
+
Require browser network connection handles to be retained so they can be
|
|
4
|
+
closed.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R009
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets browser connection constructors with explicit `.close()`
|
|
11
|
+
lifecycle APIs:
|
|
12
|
+
|
|
13
|
+
- `WebSocket`
|
|
14
|
+
- `EventSource`
|
|
15
|
+
- `window.WebSocket`, `self.WebSocket`, and `globalThis.WebSocket`
|
|
16
|
+
- `window.EventSource`, `self.EventSource`, and `globalThis.EventSource`
|
|
17
|
+
|
|
18
|
+
The rule reports connection instances that are immediately discarded or chained
|
|
19
|
+
directly into a method call other than `.close()`. In both cases there is no
|
|
20
|
+
remaining connection handle available for teardown.
|
|
21
|
+
|
|
22
|
+
## What this rule reports
|
|
23
|
+
|
|
24
|
+
The rule reports:
|
|
25
|
+
|
|
26
|
+
- standalone connection construction such as `new WebSocket(url);`
|
|
27
|
+
- voided connection construction such as `void new EventSource(url);`
|
|
28
|
+
- immediate send or listener chains such as `new WebSocket(url).send(message);`
|
|
29
|
+
- immediate `EventSource` listener registration on a discarded instance
|
|
30
|
+
|
|
31
|
+
It intentionally does not require same-function `close()` calls. Ownership can
|
|
32
|
+
be transferred to a component instance, connection manager, returned value, or
|
|
33
|
+
longer-lived runtime owner.
|
|
34
|
+
|
|
35
|
+
## Why this rule exists
|
|
36
|
+
|
|
37
|
+
`WebSocket` and `EventSource` create long-lived network connections. If the
|
|
38
|
+
handle is not retained, code cannot reliably call `.close()`, remove listeners,
|
|
39
|
+
or coordinate reconnect and shutdown behavior. Discarding the handle makes the
|
|
40
|
+
connection lifecycle implicit and usually leaks work until the page or runtime
|
|
41
|
+
exits.
|
|
42
|
+
|
|
43
|
+
## Incorrect
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
new WebSocket("wss://example.com/socket");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
void new EventSource("/events");
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
new WebSocket(url).send("hello");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
new EventSource("/events").addEventListener("message", onMessage);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Correct
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const socket = new WebSocket("wss://example.com/socket");
|
|
65
|
+
|
|
66
|
+
socket.addEventListener("message", onMessage);
|
|
67
|
+
socket.close();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const source = new EventSource("/events");
|
|
72
|
+
|
|
73
|
+
source.addEventListener("message", onMessage);
|
|
74
|
+
source.close();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
return new WebSocket(url);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
registerConnection(new EventSource(url));
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Behavior and migration notes
|
|
86
|
+
|
|
87
|
+
Store the connection handle in the owner that will close it. For UI code, that
|
|
88
|
+
is usually the component or route lifecycle. For shared libraries, returning
|
|
89
|
+
the connection or passing it to a connection manager makes the ownership
|
|
90
|
+
contract explicit.
|
|
91
|
+
|
|
92
|
+
This rule does not autofix. Introducing a variable without a matching close
|
|
93
|
+
path would hide the lifecycle problem instead of solving it.
|
|
94
|
+
|
|
95
|
+
## ESLint flat config example
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
99
|
+
|
|
100
|
+
export default [
|
|
101
|
+
runtimeCleanup.configs.recommended,
|
|
102
|
+
];
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## When not to use it
|
|
106
|
+
|
|
107
|
+
Do not enable this rule for code that intentionally creates page-lifetime
|
|
108
|
+
connections and does not need explicit teardown. Prefer a narrow disable
|
|
109
|
+
comment with a reason when a connection is meant to live for the whole runtime.
|
|
110
|
+
|
|
111
|
+
## Further reading
|
|
112
|
+
|
|
113
|
+
- [MDN: `WebSocket`](https://developer.mozilla.org/docs/Web/API/WebSocket)
|
|
114
|
+
- [MDN: `WebSocket.close()`](https://developer.mozilla.org/docs/Web/API/WebSocket/close)
|
|
115
|
+
- [MDN: `EventSource`](https://developer.mozilla.org/docs/Web/API/EventSource)
|
|
116
|
+
- [MDN: `EventSource.close()`](https://developer.mozilla.org/docs/Web/API/EventSource/close)
|