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,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Require Web Stream readers and writers to be retained so their locks can be released.
|
|
4
|
+
*/
|
|
5
|
+
import { AST_NODE_TYPES, } from "@typescript-eslint/utils";
|
|
6
|
+
import { getStaticPropertyName, isDiscardedResourceExpression, isImmediateUnownedMemberReceiver, } from "../_internal/floating-resource.js";
|
|
7
|
+
import { createRuleDocsUrl } from "../_internal/rule-docs-url.js";
|
|
8
|
+
import { hasTypeNameInHierarchy } from "../_internal/type-checker.js";
|
|
9
|
+
import { createTypedRule, getTypedRuleServices, } from "../_internal/typed-rule.js";
|
|
10
|
+
const lockFactoryNames = ["getReader", "getWriter"];
|
|
11
|
+
const cleanupMemberNames = new Set(["releaseLock"]);
|
|
12
|
+
const lockFactoryMetadataByName = {
|
|
13
|
+
getReader: {
|
|
14
|
+
lockKind: "reader",
|
|
15
|
+
streamTypeName: "ReadableStream",
|
|
16
|
+
},
|
|
17
|
+
getWriter: {
|
|
18
|
+
lockKind: "writer",
|
|
19
|
+
streamTypeName: "WritableStream",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const lockFactoryNameSet = new Set(lockFactoryNames);
|
|
23
|
+
const isLockFactoryName = (name) => lockFactoryNameSet.has(name);
|
|
24
|
+
const getLockFactoryMetadata = (callee) => {
|
|
25
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression || callee.optional) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
if (callee.object.type === AST_NODE_TYPES.Super) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const factoryName = getStaticPropertyName(callee.property, callee.computed);
|
|
32
|
+
if (factoryName === undefined || !isLockFactoryName(factoryName)) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...lockFactoryMetadataByName[factoryName],
|
|
37
|
+
receiver: callee.object,
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const isReceiverExpectedWebStream = (context, receiver, streamTypeName) => {
|
|
41
|
+
const { checker, parserServices } = getTypedRuleServices(context);
|
|
42
|
+
const receiverType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(receiver));
|
|
43
|
+
return hasTypeNameInHierarchy(checker, receiverType, streamTypeName);
|
|
44
|
+
};
|
|
45
|
+
/** Rule implementation for `runtime-cleanup/no-floating-web-stream-locks`. */
|
|
46
|
+
const noFloatingWebStreamLocks = createTypedRule({
|
|
47
|
+
create(context) {
|
|
48
|
+
return {
|
|
49
|
+
CallExpression(node) {
|
|
50
|
+
const metadata = getLockFactoryMetadata(node.callee);
|
|
51
|
+
if (metadata === undefined ||
|
|
52
|
+
!isReceiverExpectedWebStream(context, metadata.receiver, metadata.streamTypeName) ||
|
|
53
|
+
(!isDiscardedResourceExpression(node) &&
|
|
54
|
+
!isImmediateUnownedMemberReceiver(node, cleanupMemberNames))) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
context.report({
|
|
58
|
+
data: { lockKind: metadata.lockKind },
|
|
59
|
+
messageId: "floatingWebStreamLock",
|
|
60
|
+
node,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
defaultOptions: [],
|
|
66
|
+
meta: {
|
|
67
|
+
docs: {
|
|
68
|
+
description: "require Web Stream readers and writers to be retained so their locks can be released.",
|
|
69
|
+
recommended: true,
|
|
70
|
+
requiresTypeChecking: true,
|
|
71
|
+
runtimeCleanupConfigs: [
|
|
72
|
+
"runtime-cleanup.configs.recommended-type-checked",
|
|
73
|
+
"runtime-cleanup.configs.strict",
|
|
74
|
+
"runtime-cleanup.configs.all",
|
|
75
|
+
],
|
|
76
|
+
url: createRuleDocsUrl("no-floating-web-stream-locks"),
|
|
77
|
+
},
|
|
78
|
+
messages: {
|
|
79
|
+
floatingWebStreamLock: "Store or return the Web Stream {{lockKind}} so releaseLock() can release the stream lock during cleanup.",
|
|
80
|
+
},
|
|
81
|
+
schema: [],
|
|
82
|
+
type: "problem",
|
|
83
|
+
},
|
|
84
|
+
name: "no-floating-web-stream-locks",
|
|
85
|
+
});
|
|
86
|
+
export default noFloatingWebStreamLocks;
|
|
87
|
+
//# sourceMappingURL=no-floating-web-stream-locks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-floating-web-stream-locks.js","sourceRoot":"","sources":["../../src/rules/no-floating-web-stream-locks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Require worker handles to be retained so they can be terminated during cleanup.
|
|
4
|
+
*/
|
|
5
|
+
import { type TSESLint } from "@typescript-eslint/utils";
|
|
6
|
+
/** Rule implementation for `runtime-cleanup/no-floating-workers`. */
|
|
7
|
+
declare const noFloatingWorkers: TSESLint.RuleModule<"floatingWorker", readonly []>;
|
|
8
|
+
export default noFloatingWorkers;
|
|
9
|
+
//# sourceMappingURL=no-floating-workers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-floating-workers.d.ts","sourceRoot":"","sources":["../../src/rules/no-floating-workers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAEH,KAAK,QAAQ,EAEhB,MAAM,0BAA0B,CAAC;AA2OlC,qEAAqE;AACrE,QAAA,MAAM,iBAAiB,EAAE,QAAQ,CAAC,UAAU,CACxC,gBAAgB,EAChB,SAAS,EAAE,CAiDb,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Require worker handles to be retained so they can be terminated during cleanup.
|
|
4
|
+
*/
|
|
5
|
+
import { AST_NODE_TYPES, } from "@typescript-eslint/utils";
|
|
6
|
+
import { getParentNode } from "../_internal/ast-node.js";
|
|
7
|
+
import { createRuleDocsUrl } from "../_internal/rule-docs-url.js";
|
|
8
|
+
import { getVariableInScopeChain } from "../_internal/scope-variable.js";
|
|
9
|
+
import { createTypedRule, } from "../_internal/typed-rule.js";
|
|
10
|
+
const browserWorkerConstructorNames = [
|
|
11
|
+
"SharedWorker",
|
|
12
|
+
"Worker",
|
|
13
|
+
];
|
|
14
|
+
const globalReceiverNames = [
|
|
15
|
+
"globalThis",
|
|
16
|
+
"self",
|
|
17
|
+
"window",
|
|
18
|
+
];
|
|
19
|
+
const workerThreadsModuleNames = [
|
|
20
|
+
"node:worker_threads",
|
|
21
|
+
"worker_threads",
|
|
22
|
+
];
|
|
23
|
+
const browserWorkerConstructorNameSet = new Set(browserWorkerConstructorNames);
|
|
24
|
+
const globalReceiverNameSet = new Set(globalReceiverNames);
|
|
25
|
+
const workerThreadsModuleNameSet = new Set(workerThreadsModuleNames);
|
|
26
|
+
const isBrowserWorkerConstructorName = (name) => browserWorkerConstructorNameSet.has(name);
|
|
27
|
+
const isGlobalReceiverName = (name) => globalReceiverNameSet.has(name);
|
|
28
|
+
const getTransparentWrappedExpression = (node) => {
|
|
29
|
+
if (node.type === AST_NODE_TYPES.ChainExpression) {
|
|
30
|
+
return node.expression;
|
|
31
|
+
}
|
|
32
|
+
if (node.type === AST_NODE_TYPES.TSAsExpression) {
|
|
33
|
+
return node.expression;
|
|
34
|
+
}
|
|
35
|
+
if (node.type === AST_NODE_TYPES.TSNonNullExpression) {
|
|
36
|
+
return node.expression;
|
|
37
|
+
}
|
|
38
|
+
if (node.type === AST_NODE_TYPES.TSSatisfiesExpression) {
|
|
39
|
+
return node.expression;
|
|
40
|
+
}
|
|
41
|
+
if (node.type === AST_NODE_TYPES.TSTypeAssertion) {
|
|
42
|
+
return node.expression;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
};
|
|
46
|
+
const getImportSourceValue = (node) => {
|
|
47
|
+
const parent = getParentNode(node);
|
|
48
|
+
if (parent?.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
49
|
+
typeof parent.source.value === "string") {
|
|
50
|
+
return parent.source.value;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
const isWorkerThreadsImportBinding = (context, identifier) => {
|
|
55
|
+
const scope = context.sourceCode.getScope(identifier);
|
|
56
|
+
const variable = getVariableInScopeChain(scope, identifier.name);
|
|
57
|
+
const definition = variable?.defs[0];
|
|
58
|
+
const importSource = definition === undefined
|
|
59
|
+
? undefined
|
|
60
|
+
: getImportSourceValue(definition.node);
|
|
61
|
+
return (identifier.name === "Worker" &&
|
|
62
|
+
importSource !== undefined &&
|
|
63
|
+
workerThreadsModuleNameSet.has(importSource));
|
|
64
|
+
};
|
|
65
|
+
const isShadowedBrowserWorkerIdentifier = (context, identifier) => {
|
|
66
|
+
const scope = context.sourceCode.getScope(identifier);
|
|
67
|
+
const variable = getVariableInScopeChain(scope, identifier.name);
|
|
68
|
+
return variable !== null && variable.defs.length > 0;
|
|
69
|
+
};
|
|
70
|
+
const getDirectWorkerConstructorName = (context, callee) => {
|
|
71
|
+
if (callee.type !== AST_NODE_TYPES.Identifier ||
|
|
72
|
+
!isBrowserWorkerConstructorName(callee.name)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
if (isWorkerThreadsImportBinding(context, callee)) {
|
|
76
|
+
return "Worker";
|
|
77
|
+
}
|
|
78
|
+
return isShadowedBrowserWorkerIdentifier(context, callee)
|
|
79
|
+
? undefined
|
|
80
|
+
: callee.name;
|
|
81
|
+
};
|
|
82
|
+
const getMemberWorkerConstructorName = (callee) => {
|
|
83
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
84
|
+
callee.computed ||
|
|
85
|
+
callee.optional ||
|
|
86
|
+
callee.object.type !== AST_NODE_TYPES.Identifier ||
|
|
87
|
+
callee.property.type !== AST_NODE_TYPES.Identifier ||
|
|
88
|
+
!isGlobalReceiverName(callee.object.name) ||
|
|
89
|
+
!isBrowserWorkerConstructorName(callee.property.name)) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
return callee.property.name;
|
|
93
|
+
};
|
|
94
|
+
const getWorkerConstructorName = (context, callee) => getDirectWorkerConstructorName(context, callee) ??
|
|
95
|
+
getMemberWorkerConstructorName(callee);
|
|
96
|
+
const isDiscardedWorkerInstance = (node) => {
|
|
97
|
+
let current = node;
|
|
98
|
+
let parent = getParentNode(current);
|
|
99
|
+
while (parent !== undefined) {
|
|
100
|
+
const wrappedExpression = getTransparentWrappedExpression(parent);
|
|
101
|
+
if (wrappedExpression === current) {
|
|
102
|
+
current = parent;
|
|
103
|
+
parent = getParentNode(current);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (parent.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
107
|
+
parent.expression === current) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (parent.type === AST_NODE_TYPES.UnaryExpression &&
|
|
111
|
+
parent.operator === "void" &&
|
|
112
|
+
parent.argument === current) {
|
|
113
|
+
const unaryParent = getParentNode(parent);
|
|
114
|
+
return unaryParent?.type === AST_NODE_TYPES.ExpressionStatement;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
const isImmediateWorkerMethodReceiver = (node) => {
|
|
121
|
+
let current = node;
|
|
122
|
+
let parent = getParentNode(current);
|
|
123
|
+
while (parent !== undefined) {
|
|
124
|
+
const wrappedExpression = getTransparentWrappedExpression(parent);
|
|
125
|
+
if (wrappedExpression === current) {
|
|
126
|
+
current = parent;
|
|
127
|
+
parent = getParentNode(current);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (parent.type !== AST_NODE_TYPES.MemberExpression ||
|
|
131
|
+
parent.object !== current ||
|
|
132
|
+
parent.computed ||
|
|
133
|
+
parent.property.type !== AST_NODE_TYPES.Identifier ||
|
|
134
|
+
parent.property.name === "terminate") {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const callExpression = getParentNode(parent);
|
|
138
|
+
return (callExpression?.type === AST_NODE_TYPES.CallExpression &&
|
|
139
|
+
callExpression.callee === parent);
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
};
|
|
143
|
+
/** Rule implementation for `runtime-cleanup/no-floating-workers`. */
|
|
144
|
+
const noFloatingWorkers = createTypedRule({
|
|
145
|
+
create(context) {
|
|
146
|
+
return {
|
|
147
|
+
NewExpression(node) {
|
|
148
|
+
const workerName = getWorkerConstructorName(context, node.callee);
|
|
149
|
+
if (workerName === undefined ||
|
|
150
|
+
(!isDiscardedWorkerInstance(node) &&
|
|
151
|
+
!isImmediateWorkerMethodReceiver(node))) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
context.report({
|
|
155
|
+
data: { workerName },
|
|
156
|
+
messageId: "floatingWorker",
|
|
157
|
+
node,
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
defaultOptions: [],
|
|
163
|
+
meta: {
|
|
164
|
+
docs: {
|
|
165
|
+
description: "require worker handles to be retained so they can be terminated during cleanup.",
|
|
166
|
+
recommended: true,
|
|
167
|
+
requiresTypeChecking: false,
|
|
168
|
+
runtimeCleanupConfigs: [
|
|
169
|
+
"runtime-cleanup.configs.recommended",
|
|
170
|
+
"runtime-cleanup.configs.recommended-type-checked",
|
|
171
|
+
"runtime-cleanup.configs.strict",
|
|
172
|
+
"runtime-cleanup.configs.all",
|
|
173
|
+
],
|
|
174
|
+
url: createRuleDocsUrl("no-floating-workers"),
|
|
175
|
+
},
|
|
176
|
+
messages: {
|
|
177
|
+
floatingWorker: "Store or return the {{workerName}} handle so it can be terminated during cleanup.",
|
|
178
|
+
},
|
|
179
|
+
schema: [],
|
|
180
|
+
type: "problem",
|
|
181
|
+
},
|
|
182
|
+
name: "no-floating-workers",
|
|
183
|
+
});
|
|
184
|
+
export default noFloatingWorkers;
|
|
185
|
+
//# sourceMappingURL=no-floating-workers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-floating-workers.js","sourceRoot":"","sources":["../../src/rules/no-floating-workers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Require event listeners to have an explicit cleanup path.
|
|
4
|
+
*/
|
|
5
|
+
import { type TSESLint } from "@typescript-eslint/utils";
|
|
6
|
+
/** Rule implementation for `runtime-cleanup/no-unmanaged-event-listeners`. */
|
|
7
|
+
declare const noUnmanagedEventListeners: TSESLint.RuleModule<"unmanagedEventListener", readonly []>;
|
|
8
|
+
export default noUnmanagedEventListeners;
|
|
9
|
+
//# sourceMappingURL=no-unmanaged-event-listeners.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unmanaged-event-listeners.d.ts","sourceRoot":"","sources":["../../src/rules/no-unmanaged-event-listeners.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAEH,KAAK,QAAQ,EAEhB,MAAM,0BAA0B,CAAC;AAiPlC,8EAA8E;AAC9E,QAAA,MAAM,yBAAyB,EAAE,QAAQ,CAAC,UAAU,CAChD,wBAAwB,EACxB,SAAS,EAAE,CA+Gb,CAAC;AAEH,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Require event listeners to have an explicit cleanup path.
|
|
4
|
+
*/
|
|
5
|
+
import { AST_NODE_TYPES, } from "@typescript-eslint/utils";
|
|
6
|
+
import { getParentNode } from "../_internal/ast-node.js";
|
|
7
|
+
import { createRuleDocsUrl } from "../_internal/rule-docs-url.js";
|
|
8
|
+
import { getVariableInScopeChain } from "../_internal/scope-variable.js";
|
|
9
|
+
import { createTypedRule, } from "../_internal/typed-rule.js";
|
|
10
|
+
const unknownCaptureKey = "*";
|
|
11
|
+
const isCleanupBoundary = (node) => node.type === AST_NODE_TYPES.Program ||
|
|
12
|
+
node.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
13
|
+
node.type === AST_NODE_TYPES.FunctionExpression ||
|
|
14
|
+
node.type === AST_NODE_TYPES.ArrowFunctionExpression;
|
|
15
|
+
const getCleanupBoundary = (node) => {
|
|
16
|
+
let current = node;
|
|
17
|
+
while (current !== undefined) {
|
|
18
|
+
if (isCleanupBoundary(current)) {
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
current = getParentNode(current);
|
|
22
|
+
}
|
|
23
|
+
throw new TypeError("Expected an addEventListener call to have a program.");
|
|
24
|
+
};
|
|
25
|
+
const getStaticPropertyName = (property) => {
|
|
26
|
+
if (property.type === AST_NODE_TYPES.Identifier) {
|
|
27
|
+
return property.name;
|
|
28
|
+
}
|
|
29
|
+
if (property.type === AST_NODE_TYPES.Literal &&
|
|
30
|
+
typeof property.value === "string") {
|
|
31
|
+
return property.value;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
};
|
|
35
|
+
const isMethodCallNamed = (callee, methodName) => callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
36
|
+
!callee.computed &&
|
|
37
|
+
callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
38
|
+
callee.property.name === methodName;
|
|
39
|
+
const isUnknownRecord = (value) => typeof value === "object" && value !== null;
|
|
40
|
+
const isVariableDeclarator = (node) => isUnknownRecord(node) &&
|
|
41
|
+
node["type"] === AST_NODE_TYPES.VariableDeclarator;
|
|
42
|
+
const getVariableInitializer = (context, identifier) => {
|
|
43
|
+
const scope = context.sourceCode.getScope(identifier);
|
|
44
|
+
const variable = getVariableInScopeChain(scope, identifier.name);
|
|
45
|
+
const definition = variable?.defs[0];
|
|
46
|
+
const definitionNode = definition?.node;
|
|
47
|
+
if (!isVariableDeclarator(definitionNode)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return definitionNode.init ?? undefined;
|
|
51
|
+
};
|
|
52
|
+
const resolveOptionsExpression = (context, argument) => {
|
|
53
|
+
if (argument.type === AST_NODE_TYPES.SpreadElement) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (argument.type === AST_NODE_TYPES.Identifier) {
|
|
57
|
+
return getVariableInitializer(context, argument);
|
|
58
|
+
}
|
|
59
|
+
return argument;
|
|
60
|
+
};
|
|
61
|
+
const objectExpressionHasProperty = (objectExpression, propertyName) => objectExpression.properties.some((property) => {
|
|
62
|
+
if (property.type === AST_NODE_TYPES.SpreadElement) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return getStaticPropertyName(property.key) === propertyName;
|
|
66
|
+
});
|
|
67
|
+
const getBooleanPropertyValue = (objectExpression, propertyName) => {
|
|
68
|
+
for (const property of objectExpression.properties) {
|
|
69
|
+
if (property.type === AST_NODE_TYPES.Property &&
|
|
70
|
+
getStaticPropertyName(property.key) === propertyName &&
|
|
71
|
+
property.value.type === AST_NODE_TYPES.Literal &&
|
|
72
|
+
typeof property.value.value === "boolean") {
|
|
73
|
+
return property.value.value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
};
|
|
78
|
+
const hasAbortSignalOption = (context, argument) => {
|
|
79
|
+
if (argument === undefined) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const resolvedOptions = resolveOptionsExpression(context, argument);
|
|
83
|
+
return (resolvedOptions?.type === AST_NODE_TYPES.ObjectExpression &&
|
|
84
|
+
objectExpressionHasProperty(resolvedOptions, "signal"));
|
|
85
|
+
};
|
|
86
|
+
const getCaptureKey = (context, argument) => {
|
|
87
|
+
if (argument === undefined) {
|
|
88
|
+
return "false";
|
|
89
|
+
}
|
|
90
|
+
const resolvedOptions = resolveOptionsExpression(context, argument);
|
|
91
|
+
if (resolvedOptions === undefined) {
|
|
92
|
+
return unknownCaptureKey;
|
|
93
|
+
}
|
|
94
|
+
if (resolvedOptions.type === AST_NODE_TYPES.Literal &&
|
|
95
|
+
typeof resolvedOptions.value === "boolean") {
|
|
96
|
+
return String(resolvedOptions.value);
|
|
97
|
+
}
|
|
98
|
+
if (resolvedOptions.type === AST_NODE_TYPES.ObjectExpression) {
|
|
99
|
+
const captureValue = getBooleanPropertyValue(resolvedOptions, "capture");
|
|
100
|
+
return captureValue === undefined ? "false" : String(captureValue);
|
|
101
|
+
}
|
|
102
|
+
return unknownCaptureKey;
|
|
103
|
+
};
|
|
104
|
+
const getCleanupKey = (context, node) => {
|
|
105
|
+
if (node.arguments.length < 2 || node.callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const [eventType, listener, options] = node.arguments;
|
|
109
|
+
if (eventType === undefined ||
|
|
110
|
+
listener === undefined ||
|
|
111
|
+
eventType.type === AST_NODE_TYPES.SpreadElement ||
|
|
112
|
+
listener.type === AST_NODE_TYPES.SpreadElement) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const targetText = context.sourceCode.getText(node.callee.object);
|
|
116
|
+
const eventTypeText = context.sourceCode.getText(eventType);
|
|
117
|
+
const listenerText = context.sourceCode.getText(listener);
|
|
118
|
+
const captureKey = getCaptureKey(context, options);
|
|
119
|
+
return `${targetText}\u0000${eventTypeText}\u0000${listenerText}\u0000${captureKey}`;
|
|
120
|
+
};
|
|
121
|
+
const getWildcardCleanupKey = (cleanupKey) => {
|
|
122
|
+
const [targetText, eventTypeText, listenerText] = cleanupKey.split("\u0000", 3);
|
|
123
|
+
if (targetText === undefined ||
|
|
124
|
+
eventTypeText === undefined ||
|
|
125
|
+
listenerText === undefined) {
|
|
126
|
+
throw new TypeError("Expected a complete event listener cleanup key.");
|
|
127
|
+
}
|
|
128
|
+
return `${targetText}\u0000${eventTypeText}\u0000${listenerText}\u0000${unknownCaptureKey}`;
|
|
129
|
+
};
|
|
130
|
+
/** Rule implementation for `runtime-cleanup/no-unmanaged-event-listeners`. */
|
|
131
|
+
const noUnmanagedEventListeners = createTypedRule({
|
|
132
|
+
create(context) {
|
|
133
|
+
const addsByBoundary = new Map();
|
|
134
|
+
const removesByBoundary = new Map();
|
|
135
|
+
const addRecord = (boundary, record) => {
|
|
136
|
+
const records = addsByBoundary.get(boundary);
|
|
137
|
+
if (records === undefined) {
|
|
138
|
+
addsByBoundary.set(boundary, [record]);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
records.push(record);
|
|
142
|
+
};
|
|
143
|
+
const addRemoveKey = (boundary, cleanupKey) => {
|
|
144
|
+
const cleanupKeys = removesByBoundary.get(boundary);
|
|
145
|
+
if (cleanupKeys === undefined) {
|
|
146
|
+
removesByBoundary.set(boundary, new Set([cleanupKey]));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
cleanupKeys.add(cleanupKey);
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
'CallExpression[callee.type="MemberExpression"]'(node) {
|
|
153
|
+
if (isMethodCallNamed(node.callee, "addEventListener") &&
|
|
154
|
+
!hasAbortSignalOption(context, node.arguments[2])) {
|
|
155
|
+
const cleanupKey = getCleanupKey(context, node);
|
|
156
|
+
if (cleanupKey !== undefined) {
|
|
157
|
+
addRecord(getCleanupBoundary(node), {
|
|
158
|
+
cleanupKey,
|
|
159
|
+
node,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (isMethodCallNamed(node.callee, "removeEventListener")) {
|
|
164
|
+
const cleanupKey = getCleanupKey(context, node);
|
|
165
|
+
if (cleanupKey !== undefined) {
|
|
166
|
+
addRemoveKey(getCleanupBoundary(node), cleanupKey);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"Program:exit"() {
|
|
171
|
+
for (const [boundary, addRecords] of addsByBoundary) {
|
|
172
|
+
const removeKeys = removesByBoundary.get(boundary) ?? new Set();
|
|
173
|
+
for (const { cleanupKey, node } of addRecords) {
|
|
174
|
+
if (removeKeys.has(cleanupKey) ||
|
|
175
|
+
removeKeys.has(getWildcardCleanupKey(cleanupKey))) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
context.report({
|
|
179
|
+
messageId: "unmanagedEventListener",
|
|
180
|
+
node,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
defaultOptions: [],
|
|
188
|
+
meta: {
|
|
189
|
+
docs: {
|
|
190
|
+
description: "require event listeners to have an abort signal or a matching cleanup call.",
|
|
191
|
+
recommended: true,
|
|
192
|
+
requiresTypeChecking: false,
|
|
193
|
+
runtimeCleanupConfigs: [
|
|
194
|
+
"runtime-cleanup.configs.recommended",
|
|
195
|
+
"runtime-cleanup.configs.recommended-type-checked",
|
|
196
|
+
"runtime-cleanup.configs.strict",
|
|
197
|
+
"runtime-cleanup.configs.all",
|
|
198
|
+
],
|
|
199
|
+
url: createRuleDocsUrl("no-unmanaged-event-listeners"),
|
|
200
|
+
},
|
|
201
|
+
messages: {
|
|
202
|
+
unmanagedEventListener: "Add an AbortSignal option or a matching removeEventListener call for this listener.",
|
|
203
|
+
},
|
|
204
|
+
schema: [],
|
|
205
|
+
type: "problem",
|
|
206
|
+
},
|
|
207
|
+
name: "no-unmanaged-event-listeners",
|
|
208
|
+
});
|
|
209
|
+
export default noUnmanagedEventListeners;
|
|
210
|
+
//# sourceMappingURL=no-unmanaged-event-listeners.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unmanaged-event-listeners.js","sourceRoot":"","sources":["../../src/rules/no-unmanaged-event-listeners.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
`eslint-plugin-runtime-cleanup` is an ESLint plugin scaffold for enforcing
|
|
4
|
+
explicit cleanup of runtime resources in TypeScript projects.
|
|
5
|
+
|
|
6
|
+
The initial package intentionally ships no rules. This keeps the repository
|
|
7
|
+
clean after template conversion and prevents unrelated rule behavior from being
|
|
8
|
+
published under the runtime-cleanup name.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install --save-dev eslint-plugin-runtime-cleanup typescript
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Flat config
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import runtimeCleanup from "eslint-plugin-runtime-cleanup";
|
|
20
|
+
|
|
21
|
+
export default [runtimeCleanup.configs.recommended];
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Current status
|
|
25
|
+
|
|
26
|
+
The plugin exports stable preset keys, parser defaults, package metadata, and
|
|
27
|
+
documentation structure. Add concrete rules only when the resource pattern,
|
|
28
|
+
cleanup expectation, false-positive boundaries, and fix or suggestion strategy
|
|
29
|
+
are defined.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Practical checklist for adopting eslint-plugin-runtime-cleanup rules with low risk.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Adoption checklist
|
|
6
|
+
|
|
7
|
+
Use this checklist when concrete runtime-cleanup rules are added.
|
|
8
|
+
|
|
9
|
+
## Before enabling a rule
|
|
10
|
+
|
|
11
|
+
- Confirm the rule targets a real resource lifetime problem in your codebase.
|
|
12
|
+
- Read the rule docs and understand the cleanup pattern it expects.
|
|
13
|
+
- Start with `runtime-cleanup.configs.recommended` or a single rule entry.
|
|
14
|
+
- Run ESLint without `--fix` first and review every report category.
|
|
15
|
+
- Enable type-aware presets only when your parser setup supports project
|
|
16
|
+
services.
|
|
17
|
+
|
|
18
|
+
## During rollout
|
|
19
|
+
|
|
20
|
+
- Fix the highest-confidence findings first.
|
|
21
|
+
- Prefer explicit cleanup near the resource allocation site.
|
|
22
|
+
- Use suggestions where cleanup placement depends on program structure.
|
|
23
|
+
- Avoid blanket disables. Use narrow `eslint-disable-next-line` comments only
|
|
24
|
+
when the resource is intentionally long-lived.
|
|
25
|
+
|
|
26
|
+
## Before release
|
|
27
|
+
|
|
28
|
+
- Run `npm run lint:nocache`.
|
|
29
|
+
- Run `npm run typecheck`.
|
|
30
|
+
- Run `npm run test`.
|
|
31
|
+
- Run the repository release gate when preparing a package release.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Choose the right eslint-plugin-runtime-cleanup preset and roll it out with minimal migration risk.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Preset selection strategy
|
|
6
|
+
|
|
7
|
+
The package currently exposes empty presets. These keys are stable adoption
|
|
8
|
+
points for future runtime cleanup rules.
|
|
9
|
+
|
|
10
|
+
## Recommended path
|
|
11
|
+
|
|
12
|
+
1. Use `runtime-cleanup.configs.recommended` for broad, low-noise checks.
|
|
13
|
+
2. Add `runtime-cleanup.configs["recommended-type-checked"]` when type-aware
|
|
14
|
+
rules are useful and project services are available.
|
|
15
|
+
3. Evaluate `runtime-cleanup.configs.strict` only after recommended findings are
|
|
16
|
+
clean.
|
|
17
|
+
4. Use `runtime-cleanup.configs.experimental` in narrow CI jobs or local audits
|
|
18
|
+
before making it a default.
|
|
19
|
+
|
|
20
|
+
## Direct rule enablement
|
|
21
|
+
|
|
22
|
+
For early rollout, enabling one concrete rule at a time is usually better than
|
|
23
|
+
turning on a broader preset. That gives reviewers a clear resource category and
|
|
24
|
+
cleanup strategy to evaluate.
|