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,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rollout and fix safety
|
|
3
|
+
description: Guidance for phased rollout, fix safety, and manual verification.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rollout and fix safety
|
|
7
|
+
|
|
8
|
+
This page centralizes rollout guidance used across rule migrations.
|
|
9
|
+
|
|
10
|
+
## Phased rollout model
|
|
11
|
+
|
|
12
|
+
1. Start with `warn` severity to measure blast radius.
|
|
13
|
+
2. Run `--fix` only on a scoped folder first.
|
|
14
|
+
3. Review runtime-sensitive call sites manually.
|
|
15
|
+
4. Promote to `error` after baseline cleanup.
|
|
16
|
+
|
|
17
|
+
## Fix safety expectations
|
|
18
|
+
|
|
19
|
+
- **Autofix-safe patterns:** simple API shape substitutions where runtime behavior is equivalent.
|
|
20
|
+
- **Suggestion-only patterns:** potentially behavior-sensitive changes requiring explicit reviewer choice.
|
|
21
|
+
- **Manual migrations:** replacements that depend on local typing, nullability, or control flow assumptions.
|
|
22
|
+
|
|
23
|
+
## Manual verification checklist
|
|
24
|
+
|
|
25
|
+
1. Verify import changes are deduplicated and stable.
|
|
26
|
+
2. Confirm narrowed types still match downstream usage.
|
|
27
|
+
3. Validate guard/predicate behavior with tests.
|
|
28
|
+
4. Confirm no accidental semantic changes in edge cases.
|
|
29
|
+
|
|
30
|
+
## FAQ
|
|
31
|
+
|
|
32
|
+
### Why not migrate everything in one pass?
|
|
33
|
+
|
|
34
|
+
Large one-shot migrations make regressions harder to detect and review. Smaller batches isolate risk.
|
|
35
|
+
|
|
36
|
+
### Should we always use `--fix` in CI?
|
|
37
|
+
|
|
38
|
+
No. Prefer running `--fix` locally and committing explicit changes. CI should validate, not rewrite, code.
|
|
39
|
+
|
|
40
|
+
### How do we handle wrapper utilities?
|
|
41
|
+
|
|
42
|
+
Either align wrappers to canonical helpers/types used by this plugin or deprecate wrappers that duplicate behavior.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: How to use Vitest snapshots safely and effectively in eslint-plugin-runtime-cleanup.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Snapshot testing
|
|
6
|
+
|
|
7
|
+
Prefer explicit assertions for rule behavior. Snapshots are useful only for
|
|
8
|
+
stable generated surfaces such as preset matrices or public contract summaries.
|
|
9
|
+
|
|
10
|
+
## Good snapshot targets
|
|
11
|
+
|
|
12
|
+
- plugin preset contract summaries
|
|
13
|
+
- generated README rule tables
|
|
14
|
+
- generated docs navigation fragments
|
|
15
|
+
|
|
16
|
+
## Avoid snapshots for rule fixes
|
|
17
|
+
|
|
18
|
+
Autofix output should be written as explicit strings in RuleTester cases. That
|
|
19
|
+
keeps cleanup transformations reviewable and prevents accidental acceptance of
|
|
20
|
+
unsafe fixer behavior.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Checklist and rollout playbook for enabling type-aware eslint-plugin-runtime-cleanup rules safely.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Type-aware linting readiness
|
|
6
|
+
|
|
7
|
+
Some future cleanup rules may need TypeScript parser services to distinguish
|
|
8
|
+
resource-like values from unrelated identifiers. Use type-aware rules only when
|
|
9
|
+
the project can provide reliable type information.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- `@typescript-eslint/parser` configured for the files being linted.
|
|
14
|
+
- `parserOptions.projectService` or an equivalent typed parser setup.
|
|
15
|
+
- CI memory and runtime budgets sized for type-aware linting.
|
|
16
|
+
|
|
17
|
+
## Rollout
|
|
18
|
+
|
|
19
|
+
Start with a small package or workspace. Compare runtime and findings against
|
|
20
|
+
the non-type-aware preset before enabling typed rules broadly.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# no-floating-abort-controllers
|
|
2
|
+
|
|
3
|
+
Require `AbortController` handles to be retained so work can be aborted during
|
|
4
|
+
cleanup.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R006
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets `AbortController` construction where the controller handle is
|
|
11
|
+
lost before the owning lifecycle can call `abort()`:
|
|
12
|
+
|
|
13
|
+
- `new AbortController()`
|
|
14
|
+
- `new window.AbortController()`
|
|
15
|
+
- `new self.AbortController()`
|
|
16
|
+
- `new globalThis.AbortController()`
|
|
17
|
+
- `new AbortController().signal`
|
|
18
|
+
|
|
19
|
+
The rule reports controllers that are immediately discarded and inline
|
|
20
|
+
`.signal` handoffs that keep only the signal. A signal can observe cancellation,
|
|
21
|
+
but it cannot initiate cancellation; the controller is the cleanup handle.
|
|
22
|
+
|
|
23
|
+
## What this rule reports
|
|
24
|
+
|
|
25
|
+
The rule reports:
|
|
26
|
+
|
|
27
|
+
- standalone controller construction such as `new AbortController();`
|
|
28
|
+
- voided controller construction such as `void new AbortController();`
|
|
29
|
+
- inline signal-only ownership such as
|
|
30
|
+
`fetch(url, { signal: new AbortController().signal })`
|
|
31
|
+
- assignments that keep only `new AbortController().signal`
|
|
32
|
+
|
|
33
|
+
It intentionally does not require a same-function `abort()` call. Ownership can
|
|
34
|
+
be transferred to a component instance, request manager, disposable collection,
|
|
35
|
+
returned object, or another lifecycle owner.
|
|
36
|
+
|
|
37
|
+
## Why this rule exists
|
|
38
|
+
|
|
39
|
+
`AbortController` is the cancellation handle for an `AbortSignal`. APIs such as
|
|
40
|
+
`fetch()` and `addEventListener()` accept a signal, and calling
|
|
41
|
+
`AbortController.abort()` later cancels or removes the associated work. If code
|
|
42
|
+
passes only `new AbortController().signal`, no reachable owner remains that can
|
|
43
|
+
abort the work during cleanup.
|
|
44
|
+
|
|
45
|
+
## Incorrect
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
new AbortController();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
void new AbortController();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
fetch("/api", {
|
|
57
|
+
signal: new AbortController().signal,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const signal = new AbortController().signal;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Correct
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
|
|
70
|
+
fetch("/api", {
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
controller.abort();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
return new AbortController();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
registerAbortController(new AbortController());
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
using controller = createAbortControllerDisposable();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Behavior and migration notes
|
|
90
|
+
|
|
91
|
+
Store the controller in the same owner that is responsible for cancelling the
|
|
92
|
+
work. In UI code, that is usually the component setup scope or a cleanup
|
|
93
|
+
callback. In service code, it may be a request context, operation manager, or
|
|
94
|
+
explicit disposable.
|
|
95
|
+
|
|
96
|
+
When a controller is intentionally owned elsewhere, pass the controller itself
|
|
97
|
+
to that owner instead of passing only the signal. A signal-only API boundary is
|
|
98
|
+
appropriate for consumers that should observe cancellation but should not be
|
|
99
|
+
able to cancel the operation.
|
|
100
|
+
|
|
101
|
+
This rule does not autofix. Introducing a new controller variable without
|
|
102
|
+
placing the matching `abort()` call in the correct lifecycle path would make the
|
|
103
|
+
code look managed without actually solving cleanup.
|
|
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 code that intentionally creates page-lifetime or
|
|
118
|
+
process-lifetime cancellation signals and never expects to abort them. Prefer a
|
|
119
|
+
narrow disable comment with a reason when the lifecycle is intentionally owned
|
|
120
|
+
by the whole runtime.
|
|
121
|
+
|
|
122
|
+
## Further reading
|
|
123
|
+
|
|
124
|
+
- [MDN: `AbortController`](https://developer.mozilla.org/docs/Web/API/AbortController)
|
|
125
|
+
- [MDN: `AbortSignal`](https://developer.mozilla.org/docs/Web/API/AbortSignal)
|
|
126
|
+
- [MDN: `addEventListener()` signal option](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# no-floating-audio-contexts
|
|
2
|
+
|
|
3
|
+
Require `AudioContext` instances to be retained so they can be closed.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R018
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets browser audio context construction:
|
|
10
|
+
|
|
11
|
+
- `new AudioContext(...)`
|
|
12
|
+
- `new webkitAudioContext(...)`
|
|
13
|
+
- `new window.AudioContext(...)`
|
|
14
|
+
- `new globalThis.AudioContext(...)`
|
|
15
|
+
|
|
16
|
+
The rule reports contexts that are immediately discarded, explicitly voided, or
|
|
17
|
+
used through an immediate non-cleanup method call such as
|
|
18
|
+
`new AudioContext().resume()`. It ignores locally shadowed direct constructor
|
|
19
|
+
bindings so project-local classes with the same name are not treated as browser
|
|
20
|
+
audio contexts.
|
|
21
|
+
|
|
22
|
+
## What this rule reports
|
|
23
|
+
|
|
24
|
+
The rule reports:
|
|
25
|
+
|
|
26
|
+
- standalone `new AudioContext()` expressions
|
|
27
|
+
- `void new AudioContext()`
|
|
28
|
+
- immediate non-cleanup use of an unowned context
|
|
29
|
+
|
|
30
|
+
Immediate `close()` calls are allowed because they do not leave an owned context
|
|
31
|
+
behind.
|
|
32
|
+
|
|
33
|
+
## Why this rule exists
|
|
34
|
+
|
|
35
|
+
`AudioContext` can hold audio hardware, decoding, graph, and scheduling
|
|
36
|
+
resources. `AudioContext.close()` releases system audio resources used by the
|
|
37
|
+
context. If the context is discarded, cleanup code cannot close it.
|
|
38
|
+
|
|
39
|
+
## Incorrect
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
new AudioContext();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
void new window.AudioContext();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
new AudioContext().resume();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Correct
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const context = new AudioContext();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await context.resume();
|
|
60
|
+
} finally {
|
|
61
|
+
await context.close();
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
function createAudioContext() {
|
|
67
|
+
return new AudioContext();
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
audioContextRegistry.add(new AudioContext());
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Behavior and migration notes
|
|
76
|
+
|
|
77
|
+
Store the context in the audio session, component, game engine, visualizer, or
|
|
78
|
+
test fixture that owns the audio graph. That owner should call `close()` during
|
|
79
|
+
cleanup.
|
|
80
|
+
|
|
81
|
+
This rule does not autofix because adding a variable without a matching
|
|
82
|
+
`close()` path would not fix the resource lifecycle.
|
|
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 tiny one-page demos where the browser page lifetime
|
|
97
|
+
is intentionally the cleanup boundary. Use narrow inline disables for those
|
|
98
|
+
examples.
|
|
99
|
+
|
|
100
|
+
## Further reading
|
|
101
|
+
|
|
102
|
+
- [MDN: `AudioContext`](https://developer.mozilla.org/docs/Web/API/AudioContext)
|
|
103
|
+
- [MDN: `AudioContext.close()`](https://developer.mozilla.org/docs/Web/API/AudioContext/close)
|
|
104
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# no-floating-broadcast-channels
|
|
2
|
+
|
|
3
|
+
Require `BroadcastChannel` handles to be retained so they can be closed.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R011
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets browser `BroadcastChannel` constructors:
|
|
10
|
+
|
|
11
|
+
- `BroadcastChannel`
|
|
12
|
+
- `window.BroadcastChannel`
|
|
13
|
+
- `self.BroadcastChannel`
|
|
14
|
+
- `globalThis.BroadcastChannel`
|
|
15
|
+
|
|
16
|
+
The rule reports channel instances that are immediately discarded or chained
|
|
17
|
+
directly into a method call other than `.close()`. In both cases there is no
|
|
18
|
+
remaining channel handle available for teardown.
|
|
19
|
+
|
|
20
|
+
## What this rule reports
|
|
21
|
+
|
|
22
|
+
The rule reports:
|
|
23
|
+
|
|
24
|
+
- standalone channel construction such as `new BroadcastChannel("updates");`
|
|
25
|
+
- voided channel construction such as `void new BroadcastChannel("updates");`
|
|
26
|
+
- immediate message sends such as
|
|
27
|
+
`new BroadcastChannel("updates").postMessage(message);`
|
|
28
|
+
- immediate listener registration on a discarded channel
|
|
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
|
|
32
|
+
longer-lived runtime owner.
|
|
33
|
+
|
|
34
|
+
## Why this rule exists
|
|
35
|
+
|
|
36
|
+
`BroadcastChannel.close()` terminates the connection to the underlying channel
|
|
37
|
+
and lets the browser know the channel is no longer needed. If the channel handle
|
|
38
|
+
is discarded, code cannot reliably call `.close()` or remove listeners during
|
|
39
|
+
cleanup.
|
|
40
|
+
|
|
41
|
+
## Incorrect
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
new BroadcastChannel("updates");
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
void new BroadcastChannel("updates");
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
new BroadcastChannel("updates").postMessage(message);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
new BroadcastChannel("updates").addEventListener("message", onMessage);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Correct
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const channel = new BroadcastChannel("updates");
|
|
63
|
+
|
|
64
|
+
channel.addEventListener("message", onMessage);
|
|
65
|
+
channel.close();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
return new BroadcastChannel(name);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
registerChannel(new BroadcastChannel(name));
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Behavior and migration notes
|
|
77
|
+
|
|
78
|
+
Store the channel in the owner that will close it. For UI code, that is
|
|
79
|
+
usually the component, route, or application shell lifecycle. For shared
|
|
80
|
+
libraries, returning the channel or passing it to a channel manager keeps
|
|
81
|
+
ownership explicit.
|
|
82
|
+
|
|
83
|
+
This rule does not autofix. Choosing the owner and close point is a semantic
|
|
84
|
+
decision.
|
|
85
|
+
|
|
86
|
+
## ESLint flat config example
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
90
|
+
|
|
91
|
+
export default [
|
|
92
|
+
runtimeCleanup.configs.recommended,
|
|
93
|
+
];
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## When not to use it
|
|
97
|
+
|
|
98
|
+
Do not enable this rule for code that intentionally creates application-lifetime
|
|
99
|
+
channels and does not need explicit teardown. Prefer a narrow disable comment
|
|
100
|
+
with a reason when a channel is meant to live for the whole runtime.
|
|
101
|
+
|
|
102
|
+
## Further reading
|
|
103
|
+
|
|
104
|
+
- [MDN: `BroadcastChannel`](https://developer.mozilla.org/docs/Web/API/BroadcastChannel)
|
|
105
|
+
- [MDN: `BroadcastChannel.close()`](https://developer.mozilla.org/docs/Web/API/BroadcastChannel/close)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# no-floating-child-processes
|
|
2
|
+
|
|
3
|
+
Require child process handles to be retained so they can be killed during
|
|
4
|
+
cleanup.
|
|
5
|
+
|
|
6
|
+
> **Rule catalog ID:** R005
|
|
7
|
+
|
|
8
|
+
## Targeted pattern scope
|
|
9
|
+
|
|
10
|
+
This rule targets asynchronous Node.js child process factories whose returned
|
|
11
|
+
handle is needed for cleanup:
|
|
12
|
+
|
|
13
|
+
- `spawn`
|
|
14
|
+
- `exec`
|
|
15
|
+
- `execFile`
|
|
16
|
+
- `fork`
|
|
17
|
+
|
|
18
|
+
The rule recognizes those factories when they come from `node:child_process` or
|
|
19
|
+
`child_process` through named imports, default or namespace imports, CommonJS
|
|
20
|
+
`require(...)` module bindings, destructured `require(...)`, or direct
|
|
21
|
+
`require("node:child_process").spawn(...)` calls.
|
|
22
|
+
|
|
23
|
+
The rule reports child process handles that are immediately discarded or chained
|
|
24
|
+
directly into a method call other than `.kill()` or `.disconnect()`.
|
|
25
|
+
|
|
26
|
+
## What this rule reports
|
|
27
|
+
|
|
28
|
+
The rule reports:
|
|
29
|
+
|
|
30
|
+
- standalone child process calls such as `spawn("node", ["worker.js"]);`
|
|
31
|
+
- voided child process calls such as `void childProcess.exec("node worker.js");`
|
|
32
|
+
- immediate method chains such as
|
|
33
|
+
`childProcess.fork("./worker.js").on("exit", handleExit);`
|
|
34
|
+
- inline require calls such as
|
|
35
|
+
`require("node:child_process").spawn("node", ["worker.js"]);`
|
|
36
|
+
|
|
37
|
+
It intentionally does not require same-function `kill()` calls. Ownership can be
|
|
38
|
+
transferred to a supervisor, returned handle, disposable collection, or
|
|
39
|
+
process-lifetime owner.
|
|
40
|
+
|
|
41
|
+
## Why this rule exists
|
|
42
|
+
|
|
43
|
+
Asynchronous child process factories return a `ChildProcess` handle. That handle
|
|
44
|
+
is the API surface for terminating the process, disconnecting IPC, inspecting
|
|
45
|
+
exit state, and wiring teardown. Discarding it leaves no explicit owner that can
|
|
46
|
+
clean up the subprocess when the surrounding runtime scope ends.
|
|
47
|
+
|
|
48
|
+
## Incorrect
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { spawn } from "node:child_process";
|
|
52
|
+
|
|
53
|
+
spawn("node", ["worker.js"]);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import childProcess from "node:child_process";
|
|
58
|
+
|
|
59
|
+
void childProcess.exec("node worker.js");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import * as childProcess from "child_process";
|
|
64
|
+
|
|
65
|
+
childProcess.fork("./worker.js").on("exit", handleExit);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
require("node:child_process").spawn("node", ["worker.js"]);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Correct
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { spawn } from "node:child_process";
|
|
76
|
+
|
|
77
|
+
const child = spawn("node", ["worker.js"]);
|
|
78
|
+
|
|
79
|
+
child.kill();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { fork } from "node:child_process";
|
|
84
|
+
|
|
85
|
+
return fork("./worker.js");
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import childProcess from "node:child_process";
|
|
90
|
+
|
|
91
|
+
registerChildProcess(childProcess.spawn("node", ["worker.js"]));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Behavior and migration notes
|
|
95
|
+
|
|
96
|
+
Store the `ChildProcess` handle in the owner that will terminate or supervise
|
|
97
|
+
the subprocess. For test helpers and task runners, that is usually a local
|
|
98
|
+
cleanup scope. For long-running services, it can be a process supervisor or
|
|
99
|
+
resource registry.
|
|
100
|
+
|
|
101
|
+
This rule does not autofix. Inserting a variable without a real shutdown path
|
|
102
|
+
would only make ownership look explicit while preserving the leak.
|
|
103
|
+
|
|
104
|
+
## ESLint flat config example
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
108
|
+
|
|
109
|
+
export default [
|
|
110
|
+
runtimeCleanup.configs.recommended,
|
|
111
|
+
];
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## When not to use it
|
|
115
|
+
|
|
116
|
+
Do not enable this rule for scripts that intentionally start detached,
|
|
117
|
+
process-lifetime children and do not own their cleanup. Prefer a narrow disable
|
|
118
|
+
comment with a reason for those launch points.
|
|
119
|
+
|
|
120
|
+
## Further reading
|
|
121
|
+
|
|
122
|
+
- [Node.js: asynchronous process creation](https://nodejs.org/api/child_process.html#asynchronous-process-creation)
|
|
123
|
+
- [Node.js: `subprocess.kill()`](https://nodejs.org/api/child_process.html#subprocesskillsignal)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# no-floating-disposable-stacks
|
|
2
|
+
|
|
3
|
+
Require `DisposableStack` handles to be retained so registered disposers run.
|
|
4
|
+
|
|
5
|
+
> **Rule catalog ID:** R008
|
|
6
|
+
|
|
7
|
+
## Targeted pattern scope
|
|
8
|
+
|
|
9
|
+
This rule targets the standard explicit resource-management stack
|
|
10
|
+
constructors:
|
|
11
|
+
|
|
12
|
+
- `DisposableStack`
|
|
13
|
+
- `AsyncDisposableStack`
|
|
14
|
+
- `globalThis.DisposableStack`, `window.DisposableStack`, and
|
|
15
|
+
`self.DisposableStack`
|
|
16
|
+
- `globalThis.AsyncDisposableStack`, `window.AsyncDisposableStack`, and
|
|
17
|
+
`self.AsyncDisposableStack`
|
|
18
|
+
|
|
19
|
+
The rule reports disposable stack instances that are immediately discarded or
|
|
20
|
+
chained directly into a method call other than `.dispose()` or
|
|
21
|
+
`.disposeAsync()`. In both cases there is no remaining stack handle available to
|
|
22
|
+
dispose registered resources.
|
|
23
|
+
|
|
24
|
+
## What this rule reports
|
|
25
|
+
|
|
26
|
+
The rule reports:
|
|
27
|
+
|
|
28
|
+
- standalone stack construction such as `new DisposableStack();`
|
|
29
|
+
- voided stack construction such as `void new AsyncDisposableStack();`
|
|
30
|
+
- immediate registration calls such as `new DisposableStack().defer(cleanup);`
|
|
31
|
+
- immediate async registration calls such as
|
|
32
|
+
`new AsyncDisposableStack().use(resource);`
|
|
33
|
+
|
|
34
|
+
It intentionally does not require same-function disposal for retained stacks.
|
|
35
|
+
Ownership can be handled by a `using` or `await using` declaration, a returned
|
|
36
|
+
stack, or a dedicated lifecycle manager.
|
|
37
|
+
|
|
38
|
+
## Why this rule exists
|
|
39
|
+
|
|
40
|
+
`DisposableStack` and `AsyncDisposableStack` are ownership containers. They only
|
|
41
|
+
provide cleanup if the stack itself is retained and later disposed. Discarding
|
|
42
|
+
the stack after registering work silently drops the only object that can run the
|
|
43
|
+
registered disposers.
|
|
44
|
+
|
|
45
|
+
## Incorrect
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
new DisposableStack();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
void new AsyncDisposableStack();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
new DisposableStack().defer(cleanup);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
new AsyncDisposableStack().use(resource);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Correct
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
using stack = new DisposableStack();
|
|
67
|
+
|
|
68
|
+
stack.defer(cleanup);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
await using stack = new AsyncDisposableStack();
|
|
73
|
+
|
|
74
|
+
stack.use(resource);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
return new DisposableStack();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
registerDisposableStack(new AsyncDisposableStack());
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Behavior and migration notes
|
|
86
|
+
|
|
87
|
+
Keep the stack in the same lifecycle owner that will dispose it. Prefer
|
|
88
|
+
`using` for synchronous stacks and `await using` for asynchronous stacks when
|
|
89
|
+
the current runtime and compiler settings support explicit resource
|
|
90
|
+
management. If a framework or library owns disposal, pass the stack directly to
|
|
91
|
+
that owner.
|
|
92
|
+
|
|
93
|
+
This rule does not autofix. Choosing between `using`, `await using`, returning
|
|
94
|
+
the stack, or wiring a lifecycle manager changes ownership semantics and must
|
|
95
|
+
be decided by the developer.
|
|
96
|
+
|
|
97
|
+
## ESLint flat config example
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
101
|
+
|
|
102
|
+
export default [
|
|
103
|
+
runtimeCleanup.configs.recommended,
|
|
104
|
+
];
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## When not to use it
|
|
108
|
+
|
|
109
|
+
Do not enable this rule for generated code or compatibility layers that
|
|
110
|
+
intentionally construct disposable stack shims for feature detection. Prefer a
|
|
111
|
+
narrow disable comment with a reason for those cases.
|
|
112
|
+
|
|
113
|
+
## Further reading
|
|
114
|
+
|
|
115
|
+
- [MDN: JavaScript resource management](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Resource_management)
|
|
116
|
+
- [MDN: `DisposableStack`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/DisposableStack)
|
|
117
|
+
- [MDN: `AsyncDisposableStack`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/AsyncDisposableStack)
|
|
118
|
+
- [TypeScript 5.2: Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html)
|