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.
Files changed (217) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +117 -0
  4. package/dist/_internal/ast-node.d.ts +19 -0
  5. package/dist/_internal/ast-node.d.ts.map +1 -0
  6. package/dist/_internal/ast-node.js +42 -0
  7. package/dist/_internal/ast-node.js.map +1 -0
  8. package/dist/_internal/bounded-cache.d.ts +37 -0
  9. package/dist/_internal/bounded-cache.d.ts.map +1 -0
  10. package/dist/_internal/bounded-cache.js +63 -0
  11. package/dist/_internal/bounded-cache.js.map +1 -0
  12. package/dist/_internal/cycle-safe-linked-search.d.ts +48 -0
  13. package/dist/_internal/cycle-safe-linked-search.d.ts.map +1 -0
  14. package/dist/_internal/cycle-safe-linked-search.js +70 -0
  15. package/dist/_internal/cycle-safe-linked-search.js.map +1 -0
  16. package/dist/_internal/expression-boolean-memoizer.d.ts +17 -0
  17. package/dist/_internal/expression-boolean-memoizer.d.ts.map +1 -0
  18. package/dist/_internal/expression-boolean-memoizer.js +22 -0
  19. package/dist/_internal/expression-boolean-memoizer.js.map +1 -0
  20. package/dist/_internal/filter-callback.d.ts +52 -0
  21. package/dist/_internal/filter-callback.d.ts.map +1 -0
  22. package/dist/_internal/filter-callback.js +108 -0
  23. package/dist/_internal/filter-callback.js.map +1 -0
  24. package/dist/_internal/floating-resource.d.ts +29 -0
  25. package/dist/_internal/floating-resource.d.ts.map +1 -0
  26. package/dist/_internal/floating-resource.js +114 -0
  27. package/dist/_internal/floating-resource.js.map +1 -0
  28. package/dist/_internal/member-call.d.ts +53 -0
  29. package/dist/_internal/member-call.d.ts.map +1 -0
  30. package/dist/_internal/member-call.js +61 -0
  31. package/dist/_internal/member-call.js.map +1 -0
  32. package/dist/_internal/normalize-expression-text.d.ts +21 -0
  33. package/dist/_internal/normalize-expression-text.d.ts.map +1 -0
  34. package/dist/_internal/normalize-expression-text.js +186 -0
  35. package/dist/_internal/normalize-expression-text.js.map +1 -0
  36. package/dist/_internal/nullish-comparison.d.ts +44 -0
  37. package/dist/_internal/nullish-comparison.d.ts.map +1 -0
  38. package/dist/_internal/nullish-comparison.js +162 -0
  39. package/dist/_internal/nullish-comparison.js.map +1 -0
  40. package/dist/_internal/plugin-settings.d.ts +30 -0
  41. package/dist/_internal/plugin-settings.d.ts.map +1 -0
  42. package/dist/_internal/plugin-settings.js +90 -0
  43. package/dist/_internal/plugin-settings.js.map +1 -0
  44. package/dist/_internal/report-adapter.d.ts +24 -0
  45. package/dist/_internal/report-adapter.d.ts.map +1 -0
  46. package/dist/_internal/report-adapter.js +35 -0
  47. package/dist/_internal/report-adapter.js.map +1 -0
  48. package/dist/_internal/rule-catalog.d.ts +47 -0
  49. package/dist/_internal/rule-catalog.d.ts.map +1 -0
  50. package/dist/_internal/rule-catalog.js +97 -0
  51. package/dist/_internal/rule-catalog.js.map +1 -0
  52. package/dist/_internal/rule-docs-metadata.d.ts +35 -0
  53. package/dist/_internal/rule-docs-metadata.d.ts.map +1 -0
  54. package/dist/_internal/rule-docs-metadata.js +172 -0
  55. package/dist/_internal/rule-docs-metadata.js.map +1 -0
  56. package/dist/_internal/rule-docs-url.d.ts +15 -0
  57. package/dist/_internal/rule-docs-url.d.ts.map +1 -0
  58. package/dist/_internal/rule-docs-url.js +15 -0
  59. package/dist/_internal/rule-docs-url.js.map +1 -0
  60. package/dist/_internal/rules-registry.d.ts +11 -0
  61. package/dist/_internal/rules-registry.d.ts.map +1 -0
  62. package/dist/_internal/rules-registry.js +53 -0
  63. package/dist/_internal/rules-registry.js.map +1 -0
  64. package/dist/_internal/runtime-cleanup-config-references.d.ts +38 -0
  65. package/dist/_internal/runtime-cleanup-config-references.d.ts.map +1 -0
  66. package/dist/_internal/runtime-cleanup-config-references.js +78 -0
  67. package/dist/_internal/runtime-cleanup-config-references.js.map +1 -0
  68. package/dist/_internal/safe-type-operation.d.ts +89 -0
  69. package/dist/_internal/safe-type-operation.d.ts.map +1 -0
  70. package/dist/_internal/safe-type-operation.js +147 -0
  71. package/dist/_internal/safe-type-operation.js.map +1 -0
  72. package/dist/_internal/scope-variable.d.ts +17 -0
  73. package/dist/_internal/scope-variable.d.ts.map +1 -0
  74. package/dist/_internal/scope-variable.js +30 -0
  75. package/dist/_internal/scope-variable.js.map +1 -0
  76. package/dist/_internal/type-checker.d.ts +11 -0
  77. package/dist/_internal/type-checker.d.ts.map +1 -0
  78. package/dist/_internal/type-checker.js +25 -0
  79. package/dist/_internal/type-checker.js.map +1 -0
  80. package/dist/_internal/type-predicate-autofix-safety.d.ts +16 -0
  81. package/dist/_internal/type-predicate-autofix-safety.d.ts.map +1 -0
  82. package/dist/_internal/type-predicate-autofix-safety.js +54 -0
  83. package/dist/_internal/type-predicate-autofix-safety.js.map +1 -0
  84. package/dist/_internal/type-reference-node.d.ts +23 -0
  85. package/dist/_internal/type-reference-node.d.ts.map +1 -0
  86. package/dist/_internal/type-reference-node.js +41 -0
  87. package/dist/_internal/type-reference-node.js.map +1 -0
  88. package/dist/_internal/typed-rule.d.ts +91 -0
  89. package/dist/_internal/typed-rule.d.ts.map +1 -0
  90. package/dist/_internal/typed-rule.js +121 -0
  91. package/dist/_internal/typed-rule.js.map +1 -0
  92. package/dist/_internal/value-rewrite-autofix-safety.d.ts +29 -0
  93. package/dist/_internal/value-rewrite-autofix-safety.d.ts.map +1 -0
  94. package/dist/_internal/value-rewrite-autofix-safety.js +108 -0
  95. package/dist/_internal/value-rewrite-autofix-safety.js.map +1 -0
  96. package/dist/plugin.cjs +3693 -0
  97. package/dist/plugin.cjs.map +7 -0
  98. package/dist/plugin.d.cts +75 -0
  99. package/dist/plugin.d.ts +75 -0
  100. package/dist/plugin.d.ts.map +1 -0
  101. package/dist/plugin.js +223 -0
  102. package/dist/plugin.js.map +1 -0
  103. package/dist/rules/no-floating-abort-controllers.d.ts +9 -0
  104. package/dist/rules/no-floating-abort-controllers.d.ts.map +1 -0
  105. package/dist/rules/no-floating-abort-controllers.js +144 -0
  106. package/dist/rules/no-floating-abort-controllers.js.map +1 -0
  107. package/dist/rules/no-floating-audio-contexts.d.ts +9 -0
  108. package/dist/rules/no-floating-audio-contexts.d.ts.map +1 -0
  109. package/dist/rules/no-floating-audio-contexts.js +95 -0
  110. package/dist/rules/no-floating-audio-contexts.js.map +1 -0
  111. package/dist/rules/no-floating-broadcast-channels.d.ts +9 -0
  112. package/dist/rules/no-floating-broadcast-channels.d.ts.map +1 -0
  113. package/dist/rules/no-floating-broadcast-channels.js +151 -0
  114. package/dist/rules/no-floating-broadcast-channels.js.map +1 -0
  115. package/dist/rules/no-floating-child-processes.d.ts +9 -0
  116. package/dist/rules/no-floating-child-processes.d.ts.map +1 -0
  117. package/dist/rules/no-floating-child-processes.js +259 -0
  118. package/dist/rules/no-floating-child-processes.js.map +1 -0
  119. package/dist/rules/no-floating-disposable-stacks.d.ts +9 -0
  120. package/dist/rules/no-floating-disposable-stacks.d.ts.map +1 -0
  121. package/dist/rules/no-floating-disposable-stacks.js +177 -0
  122. package/dist/rules/no-floating-disposable-stacks.js.map +1 -0
  123. package/dist/rules/no-floating-file-watchers.d.ts +9 -0
  124. package/dist/rules/no-floating-file-watchers.d.ts.map +1 -0
  125. package/dist/rules/no-floating-file-watchers.js +241 -0
  126. package/dist/rules/no-floating-file-watchers.js.map +1 -0
  127. package/dist/rules/no-floating-geolocation-watches.d.ts +9 -0
  128. package/dist/rules/no-floating-geolocation-watches.d.ts.map +1 -0
  129. package/dist/rules/no-floating-geolocation-watches.js +156 -0
  130. package/dist/rules/no-floating-geolocation-watches.js.map +1 -0
  131. package/dist/rules/no-floating-infinite-animations.d.ts +9 -0
  132. package/dist/rules/no-floating-infinite-animations.d.ts.map +1 -0
  133. package/dist/rules/no-floating-infinite-animations.js +131 -0
  134. package/dist/rules/no-floating-infinite-animations.js.map +1 -0
  135. package/dist/rules/no-floating-media-streams.d.ts +9 -0
  136. package/dist/rules/no-floating-media-streams.d.ts.map +1 -0
  137. package/dist/rules/no-floating-media-streams.js +175 -0
  138. package/dist/rules/no-floating-media-streams.js.map +1 -0
  139. package/dist/rules/no-floating-message-channels.d.ts +9 -0
  140. package/dist/rules/no-floating-message-channels.d.ts.map +1 -0
  141. package/dist/rules/no-floating-message-channels.js +150 -0
  142. package/dist/rules/no-floating-message-channels.js.map +1 -0
  143. package/dist/rules/no-floating-network-connections.d.ts +9 -0
  144. package/dist/rules/no-floating-network-connections.d.ts.map +1 -0
  145. package/dist/rules/no-floating-network-connections.js +170 -0
  146. package/dist/rules/no-floating-network-connections.js.map +1 -0
  147. package/dist/rules/no-floating-object-urls.d.ts +9 -0
  148. package/dist/rules/no-floating-object-urls.d.ts.map +1 -0
  149. package/dist/rules/no-floating-object-urls.js +83 -0
  150. package/dist/rules/no-floating-object-urls.js.map +1 -0
  151. package/dist/rules/no-floating-observers.d.ts +9 -0
  152. package/dist/rules/no-floating-observers.d.ts.map +1 -0
  153. package/dist/rules/no-floating-observers.js +160 -0
  154. package/dist/rules/no-floating-observers.js.map +1 -0
  155. package/dist/rules/no-floating-servers.d.ts +9 -0
  156. package/dist/rules/no-floating-servers.d.ts.map +1 -0
  157. package/dist/rules/no-floating-servers.js +282 -0
  158. package/dist/rules/no-floating-servers.js.map +1 -0
  159. package/dist/rules/no-floating-streams.d.ts +9 -0
  160. package/dist/rules/no-floating-streams.d.ts.map +1 -0
  161. package/dist/rules/no-floating-streams.js +222 -0
  162. package/dist/rules/no-floating-streams.js.map +1 -0
  163. package/dist/rules/no-floating-timers.d.ts +9 -0
  164. package/dist/rules/no-floating-timers.d.ts.map +1 -0
  165. package/dist/rules/no-floating-timers.js +145 -0
  166. package/dist/rules/no-floating-timers.js.map +1 -0
  167. package/dist/rules/no-floating-wake-locks.d.ts +9 -0
  168. package/dist/rules/no-floating-wake-locks.d.ts.map +1 -0
  169. package/dist/rules/no-floating-wake-locks.js +159 -0
  170. package/dist/rules/no-floating-wake-locks.js.map +1 -0
  171. package/dist/rules/no-floating-web-stream-locks.d.ts +9 -0
  172. package/dist/rules/no-floating-web-stream-locks.d.ts.map +1 -0
  173. package/dist/rules/no-floating-web-stream-locks.js +87 -0
  174. package/dist/rules/no-floating-web-stream-locks.js.map +1 -0
  175. package/dist/rules/no-floating-workers.d.ts +9 -0
  176. package/dist/rules/no-floating-workers.d.ts.map +1 -0
  177. package/dist/rules/no-floating-workers.js +185 -0
  178. package/dist/rules/no-floating-workers.js.map +1 -0
  179. package/dist/rules/no-unmanaged-event-listeners.d.ts +9 -0
  180. package/dist/rules/no-unmanaged-event-listeners.d.ts.map +1 -0
  181. package/dist/rules/no-unmanaged-event-listeners.js +210 -0
  182. package/dist/rules/no-unmanaged-event-listeners.js.map +1 -0
  183. package/docs/rules/getting-started.md +29 -0
  184. package/docs/rules/guides/adoption-checklist.md +31 -0
  185. package/docs/rules/guides/preset-selection-strategy.md +24 -0
  186. package/docs/rules/guides/rollout-and-fix-safety.md +42 -0
  187. package/docs/rules/guides/snapshot-testing.md +20 -0
  188. package/docs/rules/guides/type-aware-linting-readiness.md +20 -0
  189. package/docs/rules/no-floating-abort-controllers.md +126 -0
  190. package/docs/rules/no-floating-audio-contexts.md +104 -0
  191. package/docs/rules/no-floating-broadcast-channels.md +105 -0
  192. package/docs/rules/no-floating-child-processes.md +123 -0
  193. package/docs/rules/no-floating-disposable-stacks.md +118 -0
  194. package/docs/rules/no-floating-file-watchers.md +111 -0
  195. package/docs/rules/no-floating-geolocation-watches.md +95 -0
  196. package/docs/rules/no-floating-infinite-animations.md +110 -0
  197. package/docs/rules/no-floating-media-streams.md +113 -0
  198. package/docs/rules/no-floating-message-channels.md +114 -0
  199. package/docs/rules/no-floating-network-connections.md +116 -0
  200. package/docs/rules/no-floating-object-urls.md +102 -0
  201. package/docs/rules/no-floating-observers.md +108 -0
  202. package/docs/rules/no-floating-servers.md +127 -0
  203. package/docs/rules/no-floating-streams.md +120 -0
  204. package/docs/rules/no-floating-timers.md +120 -0
  205. package/docs/rules/no-floating-wake-locks.md +109 -0
  206. package/docs/rules/no-floating-web-stream-locks.md +105 -0
  207. package/docs/rules/no-floating-workers.md +123 -0
  208. package/docs/rules/no-unmanaged-event-listeners.md +143 -0
  209. package/docs/rules/overview.md +44 -0
  210. package/docs/rules/presets/all.md +35 -0
  211. package/docs/rules/presets/experimental.md +44 -0
  212. package/docs/rules/presets/index.md +54 -0
  213. package/docs/rules/presets/minimal.md +17 -0
  214. package/docs/rules/presets/recommended-type-checked.md +43 -0
  215. package/docs/rules/presets/recommended.md +34 -0
  216. package/docs/rules/presets/strict.md +36 -0
  217. 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)