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,102 @@
|
|
|
1
|
+
# no-floating-object-urls
|
|
2
|
+
|
|
3
|
+
Require object URLs to be retained so they can be revoked.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R017
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets browser object URL creation APIs:
|
|
10
|
+
|
|
11
|
+
- `URL.createObjectURL(...)`
|
|
12
|
+
- `globalThis.URL.createObjectURL(...)`
|
|
13
|
+
- `window.URL.createObjectURL(...)`
|
|
14
|
+
- `self.URL.createObjectURL(...)`
|
|
15
|
+
|
|
16
|
+
The rule reports object URLs that are immediately discarded or explicitly
|
|
17
|
+
voided. It ignores locally shadowed `URL` bindings so project-local helpers with
|
|
18
|
+
the same name are not treated as the platform API.
|
|
19
|
+
|
|
20
|
+
## What this rule reports
|
|
21
|
+
|
|
22
|
+
The rule reports:
|
|
23
|
+
|
|
24
|
+
- standalone `URL.createObjectURL(...)` calls
|
|
25
|
+
- `void URL.createObjectURL(...)`
|
|
26
|
+
- static computed global forms such as:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
window["URL"]["createObjectURL"](blob);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The rule intentionally allows object URLs that are stored, returned, or passed to
|
|
33
|
+
a lifecycle manager. It does not try to prove that every retained URL is later
|
|
34
|
+
revoked.
|
|
35
|
+
|
|
36
|
+
## Why this rule exists
|
|
37
|
+
|
|
38
|
+
`URL.createObjectURL()` creates a URL that keeps the backing `Blob`, `File`, or
|
|
39
|
+
media source reachable until the URL is revoked. Discarding the returned string
|
|
40
|
+
means cleanup code cannot call `URL.revokeObjectURL()` for that object URL.
|
|
41
|
+
|
|
42
|
+
## Incorrect
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
URL.createObjectURL(blob);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
void window.URL.createObjectURL(file);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Correct
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
image.src = objectUrl;
|
|
59
|
+
} finally {
|
|
60
|
+
URL.revokeObjectURL(objectUrl);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
function createDownloadUrl(file: File) {
|
|
66
|
+
return URL.createObjectURL(file);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
objectUrlRegistry.add(URL.createObjectURL(blob));
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Behavior and migration notes
|
|
75
|
+
|
|
76
|
+
Store the object URL in the owner that will revoke it. For UI code, that is
|
|
77
|
+
usually a component cleanup hook, image preview controller, download manager, or
|
|
78
|
+
temporary asset registry.
|
|
79
|
+
|
|
80
|
+
This rule does not autofix. Adding a generated variable without adding
|
|
81
|
+
`URL.revokeObjectURL()` would make the lint error disappear while preserving the
|
|
82
|
+
resource leak.
|
|
83
|
+
|
|
84
|
+
## ESLint flat config example
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
88
|
+
|
|
89
|
+
export default [
|
|
90
|
+
runtimeCleanup.configs.recommended,
|
|
91
|
+
];
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## When not to use it
|
|
95
|
+
|
|
96
|
+
Do not enable this rule for throwaway snippets where the document lifetime is
|
|
97
|
+
the intended cleanup boundary. Prefer a narrow disable comment for those cases.
|
|
98
|
+
|
|
99
|
+
## Further reading
|
|
100
|
+
|
|
101
|
+
- [MDN: `URL.createObjectURL()`](https://developer.mozilla.org/docs/Web/API/URL/createObjectURL_static)
|
|
102
|
+
- [MDN: `URL.revokeObjectURL()`](https://developer.mozilla.org/docs/Web/API/URL/revokeObjectURL_static)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# no-floating-observers
|
|
2
|
+
|
|
3
|
+
Require observer instances to be retained so they can be disconnected during
|
|
4
|
+
cleanup.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R003
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets native observer constructors whose active observation must be
|
|
11
|
+
stopped with `disconnect()`:
|
|
12
|
+
|
|
13
|
+
- `IntersectionObserver`
|
|
14
|
+
- `MutationObserver`
|
|
15
|
+
- `PerformanceObserver`
|
|
16
|
+
- `ReportingObserver`
|
|
17
|
+
- `ResizeObserver`
|
|
18
|
+
|
|
19
|
+
The rule reports observer instances that are immediately discarded or chained
|
|
20
|
+
directly into `.observe(...)`. In both cases there is no remaining observer
|
|
21
|
+
handle available for teardown.
|
|
22
|
+
|
|
23
|
+
## What this rule reports
|
|
24
|
+
|
|
25
|
+
The rule reports:
|
|
26
|
+
|
|
27
|
+
- standalone observer construction expressions such as
|
|
28
|
+
`new ResizeObserver(callback);`
|
|
29
|
+
- voided observer construction such as
|
|
30
|
+
`void new MutationObserver(callback);`
|
|
31
|
+
- immediate observation chains such as
|
|
32
|
+
`new IntersectionObserver(callback).observe(element);`
|
|
33
|
+
|
|
34
|
+
It intentionally does not require same-function `disconnect()` calls. That is a
|
|
35
|
+
separate ownership question, and many valid designs transfer observer ownership
|
|
36
|
+
to a manager, component instance, returned disposable, or framework lifecycle.
|
|
37
|
+
|
|
38
|
+
## Why this rule exists
|
|
39
|
+
|
|
40
|
+
Observer APIs hold callbacks and continue delivering records until disconnected.
|
|
41
|
+
If the observer instance is not retained, later cleanup cannot call
|
|
42
|
+
`disconnect()`, and the code has no explicit ownership point for the active
|
|
43
|
+
runtime resource.
|
|
44
|
+
|
|
45
|
+
## Incorrect
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
new ResizeObserver(handleResize);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
void new MutationObserver(handleMutations);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
new IntersectionObserver(handleIntersections).observe(element);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Correct
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const observer = new ResizeObserver(handleResize);
|
|
63
|
+
|
|
64
|
+
observer.observe(element);
|
|
65
|
+
observer.disconnect();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
return new MutationObserver(handleMutations);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
registerObserver(new IntersectionObserver(handleIntersections));
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Behavior and migration notes
|
|
77
|
+
|
|
78
|
+
Store observer instances in the same lifecycle owner that will disconnect them.
|
|
79
|
+
For components, that is usually the component setup scope or an owned disposable
|
|
80
|
+
collection. For shared utilities, returning the observer or passing it to a
|
|
81
|
+
dedicated resource manager is acceptable because ownership is explicit.
|
|
82
|
+
|
|
83
|
+
This rule does not autofix. Choosing the right owner and cleanup point is a
|
|
84
|
+
semantic decision, and inserting a variable without a matching `disconnect()`
|
|
85
|
+
would only make the leak less obvious.
|
|
86
|
+
|
|
87
|
+
## ESLint flat config example
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
91
|
+
|
|
92
|
+
export default [
|
|
93
|
+
runtimeCleanup.configs.recommended,
|
|
94
|
+
];
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## When not to use it
|
|
98
|
+
|
|
99
|
+
Do not enable this rule for code that intentionally creates page-lifetime or
|
|
100
|
+
process-lifetime observers without later cleanup. Prefer a narrow disable
|
|
101
|
+
comment with a reason when an observer is meant to live for the whole runtime.
|
|
102
|
+
|
|
103
|
+
## Further reading
|
|
104
|
+
|
|
105
|
+
- [MDN: `IntersectionObserver`](https://developer.mozilla.org/docs/Web/API/IntersectionObserver)
|
|
106
|
+
- [MDN: `MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver)
|
|
107
|
+
- [MDN: `PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver)
|
|
108
|
+
- [MDN: `ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# no-floating-servers
|
|
2
|
+
|
|
3
|
+
Require Node.js server handles to be retained so they can be closed.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R013
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets server factory calls from Node.js core networking modules:
|
|
10
|
+
|
|
11
|
+
- `http.createServer`
|
|
12
|
+
- `https.createServer`
|
|
13
|
+
- `http2.createServer`
|
|
14
|
+
- `http2.createSecureServer`
|
|
15
|
+
- `net.createServer`
|
|
16
|
+
- equivalent named imports, namespace imports, default imports, destructured
|
|
17
|
+
`require(...)` calls, and inline `require(...).createServer(...)` calls
|
|
18
|
+
|
|
19
|
+
The rule reports server handles that are immediately discarded, including
|
|
20
|
+
discarded `.listen(...)` chains. It does not require type information because
|
|
21
|
+
the recognized factories are tied to import and `require` sources.
|
|
22
|
+
|
|
23
|
+
## What this rule reports
|
|
24
|
+
|
|
25
|
+
The rule reports:
|
|
26
|
+
|
|
27
|
+
- standalone server creation such as `createServer(handler);`
|
|
28
|
+
- voided server creation such as `void http.createServer(handler);`
|
|
29
|
+
- discarded listen chains such as `http.createServer(handler).listen(3000);`
|
|
30
|
+
- discarded method chains that start from an unretained server handle
|
|
31
|
+
|
|
32
|
+
It intentionally does not require same-function `close()` calls. Ownership can
|
|
33
|
+
be transferred to a framework adapter, test harness, returned value, or shutdown
|
|
34
|
+
manager.
|
|
35
|
+
|
|
36
|
+
## Why this rule exists
|
|
37
|
+
|
|
38
|
+
Node.js server factories create long-lived handles that can keep the process
|
|
39
|
+
alive and hold open ports. Once a server is listening, retaining the `Server`
|
|
40
|
+
object is the normal way to call `.close()`, coordinate graceful shutdown, and
|
|
41
|
+
stop accepting new work.
|
|
42
|
+
|
|
43
|
+
## Incorrect
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { createServer } from "node:http";
|
|
47
|
+
|
|
48
|
+
createServer(handler);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import http from "node:http";
|
|
53
|
+
|
|
54
|
+
http.createServer(handler).listen(3000);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const http2 = require("http2");
|
|
59
|
+
|
|
60
|
+
http2.createSecureServer(options, handler).listen(8443);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Correct
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { createServer } from "node:http";
|
|
67
|
+
|
|
68
|
+
const server = createServer(handler);
|
|
69
|
+
|
|
70
|
+
server.listen(3000);
|
|
71
|
+
server.close();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import * as http from "node:http";
|
|
76
|
+
|
|
77
|
+
const server = http.createServer(handler).listen(3000);
|
|
78
|
+
|
|
79
|
+
server.close();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
return createServer(handler).listen(3000);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
registerServer(createServer(handler).listen(3000));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Behavior and migration notes
|
|
91
|
+
|
|
92
|
+
Store the returned server handle in the owner that will shut it down. In
|
|
93
|
+
applications, that is usually the process lifecycle or graceful-shutdown
|
|
94
|
+
coordinator. In tests, it is usually the test fixture cleanup hook.
|
|
95
|
+
|
|
96
|
+
This rule allows immediate `.close()` calls and returned or passed server
|
|
97
|
+
handles. It reports discarded `.listen(...)` chains because `listen()` returns
|
|
98
|
+
the server object; discarding that returned handle makes later shutdown
|
|
99
|
+
coordination difficult.
|
|
100
|
+
|
|
101
|
+
This rule does not autofix. Choosing whether the owner is a module variable,
|
|
102
|
+
dependency-injected lifecycle manager, framework adapter, or test fixture is a
|
|
103
|
+
semantic decision.
|
|
104
|
+
|
|
105
|
+
## ESLint flat config example
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
109
|
+
|
|
110
|
+
export default [
|
|
111
|
+
runtimeCleanup.configs.recommended,
|
|
112
|
+
];
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## When not to use it
|
|
116
|
+
|
|
117
|
+
Do not enable this rule for tiny one-off scripts where the server is
|
|
118
|
+
intentionally process-lifetime and never needs graceful shutdown. Prefer a
|
|
119
|
+
narrow disable comment with a reason in those files rather than weakening the
|
|
120
|
+
rule globally.
|
|
121
|
+
|
|
122
|
+
## Further reading
|
|
123
|
+
|
|
124
|
+
- [Node.js: `http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener)
|
|
125
|
+
- [Node.js: `server.close()`](https://nodejs.org/api/http.html#serverclosecallback)
|
|
126
|
+
- [Node.js: `net.createServer()`](https://nodejs.org/api/net.html#netcreateserveroptions-connectionlistener)
|
|
127
|
+
- [Node.js: `net.Server.close()`](https://nodejs.org/api/net.html#serverclosecallback)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# no-floating-streams
|
|
2
|
+
|
|
3
|
+
Require Node.js file stream handles to be retained so they can be closed during
|
|
4
|
+
cleanup.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R007
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets Node.js file stream factories from `fs` and `node:fs`:
|
|
11
|
+
|
|
12
|
+
- `createReadStream`
|
|
13
|
+
- `createWriteStream`
|
|
14
|
+
- `fs.createReadStream`
|
|
15
|
+
- `fs.createWriteStream`
|
|
16
|
+
- `require("fs").createReadStream`
|
|
17
|
+
- `require("node:fs").createWriteStream`
|
|
18
|
+
|
|
19
|
+
The rule reports only immediately discarded stream factory calls. It does not
|
|
20
|
+
report assigned, returned, manager-owned, or immediately piped streams.
|
|
21
|
+
|
|
22
|
+
## What this rule reports
|
|
23
|
+
|
|
24
|
+
The rule reports:
|
|
25
|
+
|
|
26
|
+
- standalone file stream creation such as `createReadStream(path);`
|
|
27
|
+
- voided file stream creation such as `void fs.createWriteStream(path);`
|
|
28
|
+
- discarded CommonJS namespace or destructured factory calls
|
|
29
|
+
|
|
30
|
+
It intentionally does not require same-function `destroy()` or `end()` calls.
|
|
31
|
+
Streams are often owned by a pipeline, HTTP response, higher-level resource
|
|
32
|
+
manager, or returned API contract.
|
|
33
|
+
|
|
34
|
+
## Why this rule exists
|
|
35
|
+
|
|
36
|
+
Node.js streams can hold file descriptors and other underlying resources until
|
|
37
|
+
they close or are destroyed. A discarded file stream has no reachable handle for
|
|
38
|
+
`destroy()`, `end()`, error handling, or ownership transfer. Unlike an
|
|
39
|
+
immediately piped stream, a bare discarded stream is almost always accidental.
|
|
40
|
+
|
|
41
|
+
## Incorrect
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { createReadStream } from "node:fs";
|
|
45
|
+
|
|
46
|
+
createReadStream("input.txt");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import * as fs from "node:fs";
|
|
51
|
+
|
|
52
|
+
void fs.createWriteStream("output.txt");
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const fs = require("fs");
|
|
57
|
+
|
|
58
|
+
fs.createReadStream("input.txt");
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Correct
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { createReadStream } from "node:fs";
|
|
65
|
+
|
|
66
|
+
const stream = createReadStream("input.txt");
|
|
67
|
+
|
|
68
|
+
stream.destroy();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import * as fs from "node:fs";
|
|
73
|
+
|
|
74
|
+
return fs.createWriteStream("output.txt");
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import * as fs from "node:fs";
|
|
79
|
+
|
|
80
|
+
fs.createReadStream("input.txt").pipe(response);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
registerStream(fs.createReadStream("input.txt"));
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Behavior and migration notes
|
|
88
|
+
|
|
89
|
+
Keep the stream handle in the same lifecycle owner that will observe errors and
|
|
90
|
+
close or destroy the stream. If another API owns the stream, make that
|
|
91
|
+
ownership explicit by returning it, passing it to a resource manager, or piping
|
|
92
|
+
it into the destination that owns the pipeline.
|
|
93
|
+
|
|
94
|
+
This rule is intentionally narrow. It focuses on `fs` file stream factories
|
|
95
|
+
because they are common, have concrete resource ownership implications, and can
|
|
96
|
+
be detected reliably without type information.
|
|
97
|
+
|
|
98
|
+
This rule does not autofix. Choosing whether to store, return, pipe, or destroy
|
|
99
|
+
the stream changes ownership semantics and must be decided by the developer.
|
|
100
|
+
|
|
101
|
+
## ESLint flat config example
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
105
|
+
|
|
106
|
+
export default [
|
|
107
|
+
runtimeCleanup.configs.recommended,
|
|
108
|
+
];
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## When not to use it
|
|
112
|
+
|
|
113
|
+
Do not enable this rule for generated code or one-off scripts where discarded
|
|
114
|
+
file stream construction is intentional and process lifetime owns the cleanup.
|
|
115
|
+
Prefer a narrow disable comment with a reason for those cases.
|
|
116
|
+
|
|
117
|
+
## Further reading
|
|
118
|
+
|
|
119
|
+
- [Node.js: Stream](https://nodejs.org/api/stream.html)
|
|
120
|
+
- [Node.js: File system streams](https://nodejs.org/api/fs.html#filehandlecreatereadstreamoptions)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# no-floating-timers
|
|
2
|
+
|
|
3
|
+
Require timer handles to be retained so they can be cleared during cleanup.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R001
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets timer APIs that return handles with an explicit cleanup
|
|
10
|
+
counterpart. It is intentionally conservative: the rule reports only calls where
|
|
11
|
+
the returned handle is immediately discarded.
|
|
12
|
+
|
|
13
|
+
### Matched patterns
|
|
14
|
+
|
|
15
|
+
The rule checks direct global calls to:
|
|
16
|
+
|
|
17
|
+
- `setTimeout`
|
|
18
|
+
- `setInterval`
|
|
19
|
+
- `setImmediate`
|
|
20
|
+
- `requestAnimationFrame`
|
|
21
|
+
- `requestIdleCallback`
|
|
22
|
+
|
|
23
|
+
It also checks the same methods when called through these global receivers:
|
|
24
|
+
|
|
25
|
+
- `globalThis`
|
|
26
|
+
- `window`
|
|
27
|
+
- `self`
|
|
28
|
+
- `global`
|
|
29
|
+
|
|
30
|
+
### Detection boundaries
|
|
31
|
+
|
|
32
|
+
The rule does not prove that every retained handle is cleared. That broader
|
|
33
|
+
lifetime analysis needs separate rules that understand cleanup blocks, effect
|
|
34
|
+
callbacks, disposal protocols, and framework lifecycle APIs.
|
|
35
|
+
|
|
36
|
+
The rule also skips locally declared or imported direct timer identifiers, so a
|
|
37
|
+
project-local helper named `setTimeout` is not treated as the platform timer API.
|
|
38
|
+
|
|
39
|
+
## What this rule reports
|
|
40
|
+
|
|
41
|
+
The rule reports timer calls when the returned handle is used only as a bare
|
|
42
|
+
expression or deliberately discarded with `void`.
|
|
43
|
+
|
|
44
|
+
## Why this rule exists
|
|
45
|
+
|
|
46
|
+
Floating timer handles make cleanup impossible. Intervals keep running, timeouts
|
|
47
|
+
can fire after teardown, animation callbacks can touch detached state, and idle
|
|
48
|
+
callbacks can outlive the component or request that scheduled them. Retaining
|
|
49
|
+
the handle is the first enforceable step toward calling the matching cleanup API
|
|
50
|
+
from the correct lifecycle boundary.
|
|
51
|
+
|
|
52
|
+
## ❌ Incorrect
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
setInterval(poll, 1000);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
globalThis.setTimeout(flush, 250);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
void window.requestAnimationFrame(render);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## ✅ Correct
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const intervalId = setInterval(poll, 1000);
|
|
70
|
+
|
|
71
|
+
clearInterval(intervalId);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const timeoutId = globalThis.setTimeout(flush, 250);
|
|
76
|
+
|
|
77
|
+
clearTimeout(timeoutId);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
return window.requestAnimationFrame(render);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
timerRegistry.add(setTimeout(flush, 250));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Behavior and migration notes
|
|
89
|
+
|
|
90
|
+
Start by storing each reported handle in a variable, returning it to the caller,
|
|
91
|
+
or passing it to an existing timer registry. Then clear the handle from the
|
|
92
|
+
nearest lifecycle cleanup path, such as a `finally` block, component cleanup
|
|
93
|
+
callback, request teardown hook, or disposable object.
|
|
94
|
+
|
|
95
|
+
This rule does not autofix because choosing the correct cleanup location is a
|
|
96
|
+
semantic decision. A mechanical fix that only introduces a variable would still
|
|
97
|
+
leave the resource alive.
|
|
98
|
+
|
|
99
|
+
## ESLint flat config example
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
103
|
+
|
|
104
|
+
export default [
|
|
105
|
+
runtimeCleanup.configs.recommended,
|
|
106
|
+
];
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## When not to use it
|
|
110
|
+
|
|
111
|
+
Do not enable this rule for files that intentionally schedule process-lifetime
|
|
112
|
+
timers and have no teardown path. Prefer a narrow ESLint disable comment at the
|
|
113
|
+
call site with a reason when that pattern is deliberate.
|
|
114
|
+
|
|
115
|
+
## Further reading
|
|
116
|
+
|
|
117
|
+
- [MDN: `setTimeout`](https://developer.mozilla.org/docs/Web/API/Window/setTimeout)
|
|
118
|
+
- [MDN: `clearTimeout`](https://developer.mozilla.org/docs/Web/API/Window/clearTimeout)
|
|
119
|
+
- [MDN: `requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/Window/requestAnimationFrame)
|
|
120
|
+
- [Node.js timers](https://nodejs.org/api/timers.html)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# no-floating-wake-locks
|
|
2
|
+
|
|
3
|
+
Require `WakeLockSentinel` handles to be retained so they can be released.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R016
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets screen wake lock requests:
|
|
10
|
+
|
|
11
|
+
- `navigator.wakeLock.request(...)`
|
|
12
|
+
- `window.navigator.wakeLock.request(...)`
|
|
13
|
+
- `globalThis.navigator.wakeLock.request(...)`
|
|
14
|
+
|
|
15
|
+
The rule reports wake lock requests whose resulting `WakeLockSentinel` is
|
|
16
|
+
immediately discarded, including `await` expressions that do not store, return,
|
|
17
|
+
or pass the sentinel to another owner.
|
|
18
|
+
|
|
19
|
+
## What this rule reports
|
|
20
|
+
|
|
21
|
+
The rule reports:
|
|
22
|
+
|
|
23
|
+
- standalone wake lock requests such as
|
|
24
|
+
`navigator.wakeLock.request("screen");`
|
|
25
|
+
- voided wake lock requests such as
|
|
26
|
+
`void navigator.wakeLock.request("screen");`
|
|
27
|
+
- awaited standalone requests such as
|
|
28
|
+
`await navigator.wakeLock.request("screen");`
|
|
29
|
+
|
|
30
|
+
It intentionally allows promise chains and lifecycle-manager calls that receive
|
|
31
|
+
the sentinel. The rule focuses on obviously unowned wake lock handles.
|
|
32
|
+
|
|
33
|
+
## Why this rule exists
|
|
34
|
+
|
|
35
|
+
`navigator.wakeLock.request()` resolves to a `WakeLockSentinel`. The sentinel is
|
|
36
|
+
the handle that can be released manually with `release()`, and applications
|
|
37
|
+
usually need to retain it to update UI, react to release events, or release the
|
|
38
|
+
wake lock during cleanup.
|
|
39
|
+
|
|
40
|
+
## Incorrect
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
navigator.wakeLock.request("screen");
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
void navigator.wakeLock.request("screen");
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
async function keepAwake() {
|
|
52
|
+
await navigator.wakeLock.request("screen");
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Correct
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
async function keepAwake() {
|
|
60
|
+
const sentinel = await navigator.wakeLock.request("screen");
|
|
61
|
+
|
|
62
|
+
await sentinel.release();
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
async function keepAwake() {
|
|
68
|
+
return navigator.wakeLock.request("screen");
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
async function keepAwake() {
|
|
74
|
+
registerWakeLock(await navigator.wakeLock.request("screen"));
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Behavior and migration notes
|
|
79
|
+
|
|
80
|
+
Store the sentinel in the owner that will call `release()`. For UI code, that is
|
|
81
|
+
usually a component, route, or media/session controller. Applications should
|
|
82
|
+
also consider listening for the sentinel's `release` event because user agents
|
|
83
|
+
can release wake locks automatically.
|
|
84
|
+
|
|
85
|
+
This rule does not autofix. Choosing the owning lifecycle and release behavior
|
|
86
|
+
is a semantic decision.
|
|
87
|
+
|
|
88
|
+
## ESLint flat config example
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
92
|
+
|
|
93
|
+
export default [
|
|
94
|
+
runtimeCleanup.configs.recommended,
|
|
95
|
+
];
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## When not to use it
|
|
99
|
+
|
|
100
|
+
Do not enable this rule for code where wake lock ownership is intentionally
|
|
101
|
+
hidden inside an abstraction that the rule cannot see. Prefer wrapping
|
|
102
|
+
`navigator.wakeLock.request()` in that abstraction and disabling the rule only
|
|
103
|
+
inside the wrapper if needed.
|
|
104
|
+
|
|
105
|
+
## Further reading
|
|
106
|
+
|
|
107
|
+
- [MDN: Screen Wake Lock API](https://developer.mozilla.org/docs/Web/API/Screen_Wake_Lock_API)
|
|
108
|
+
- [MDN: `WakeLockSentinel`](https://developer.mozilla.org/docs/Web/API/WakeLockSentinel)
|
|
109
|
+
- [MDN: `WakeLockSentinel.release()`](https://developer.mozilla.org/docs/Web/API/WakeLockSentinel/release)
|