homebridge-plugin-utils 1.34.0 → 2.0.0
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/README.md +130 -2
- package/build/eslint-plugin/README.md +164 -0
- package/build/eslint-plugin/config.mjs +308 -0
- package/build/eslint-plugin/index.mjs +7 -0
- package/build/eslint-plugin/plugin.mjs +46 -0
- package/build/eslint-plugin/rules/blank-line-after-open-brace.mjs +137 -0
- package/build/eslint-plugin/rules/blank-line-after-open-brace.test.mjs +112 -0
- package/build/eslint-plugin/rules/comment-style.mjs +190 -0
- package/build/eslint-plugin/rules/comment-style.test.mjs +190 -0
- package/build/eslint-plugin/rules/enforce-node-protocol.mjs +114 -0
- package/build/eslint-plugin/rules/enforce-node-protocol.test.mjs +116 -0
- package/build/eslint-plugin/rules/paren-comparisons-in-logical.mjs +94 -0
- package/build/eslint-plugin/rules/paren-comparisons-in-logical.test.mjs +107 -0
- package/build/eslint-plugin/rules/split-type-imports.mjs +354 -0
- package/build/eslint-plugin/rules/split-type-imports.test.mjs +268 -0
- package/build/eslint-plugin/test-setup.mjs +21 -0
- package/build/fs-ops.mjs +184 -0
- package/build/tsconfig.json +18 -3
- package/dist/backpressure.d.ts +128 -0
- package/dist/backpressure.js +273 -0
- package/dist/backpressure.js.map +1 -0
- package/dist/cli/index.d.ts +95 -0
- package/dist/cli/index.js +355 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/clock-double.d.ts +93 -0
- package/dist/clock-double.js +141 -0
- package/dist/clock-double.js.map +1 -0
- package/dist/clock.d.ts +39 -0
- package/dist/clock.js +34 -0
- package/dist/clock.js.map +1 -0
- package/dist/eslint-plugin/config.d.mts +193 -0
- package/dist/eslint-plugin/index.d.mts +2 -0
- package/dist/eslint-plugin/plugin.d.mts +87 -0
- package/dist/eslint-plugin/rules/blank-line-after-open-brace.d.mts +19 -0
- package/dist/eslint-plugin/rules/comment-style.d.mts +15 -0
- package/dist/eslint-plugin/rules/enforce-node-protocol.d.mts +18 -0
- package/dist/eslint-plugin/rules/paren-comparisons-in-logical.d.mts +15 -0
- package/dist/eslint-plugin/rules/split-type-imports.d.mts +16 -0
- package/dist/featureOptions-docs.d.ts +96 -0
- package/dist/featureOptions-docs.js +193 -0
- package/dist/featureOptions-docs.js.map +1 -0
- package/dist/featureOptions.d.ts +674 -0
- package/dist/featureOptions.js +870 -0
- package/dist/featureOptions.js.map +1 -0
- package/dist/ffmpeg/codecs.d.ts +256 -72
- package/dist/ffmpeg/codecs.js +477 -257
- package/dist/ffmpeg/codecs.js.map +1 -1
- package/dist/ffmpeg/dgram-util.d.ts +46 -0
- package/dist/ffmpeg/dgram-util.js +38 -0
- package/dist/ffmpeg/dgram-util.js.map +1 -0
- package/dist/ffmpeg/exec.d.ts +83 -64
- package/dist/ffmpeg/exec.js +77 -86
- package/dist/ffmpeg/exec.js.map +1 -1
- package/dist/ffmpeg/fmp4.d.ts +70 -4
- package/dist/ffmpeg/fmp4.js +100 -11
- package/dist/ffmpeg/fmp4.js.map +1 -1
- package/dist/ffmpeg/hap-enums.d.ts +214 -0
- package/dist/ffmpeg/hap-enums.js +92 -0
- package/dist/ffmpeg/hap-enums.js.map +1 -0
- package/dist/ffmpeg/index.d.ts +16 -9
- package/dist/ffmpeg/index.js +6 -0
- package/dist/ffmpeg/index.js.map +1 -1
- package/dist/ffmpeg/mp4-assembler.d.ts +120 -0
- package/dist/ffmpeg/mp4-assembler.js +387 -0
- package/dist/ffmpeg/mp4-assembler.js.map +1 -0
- package/dist/ffmpeg/mp4-parser.d.ts +94 -0
- package/dist/ffmpeg/mp4-parser.js +130 -0
- package/dist/ffmpeg/mp4-parser.js.map +1 -0
- package/dist/ffmpeg/options.d.ts +62 -149
- package/dist/ffmpeg/options.js +608 -499
- package/dist/ffmpeg/options.js.map +1 -1
- package/dist/ffmpeg/process.d.ts +143 -91
- package/dist/ffmpeg/process.js +406 -265
- package/dist/ffmpeg/process.js.map +1 -1
- package/dist/ffmpeg/record.d.ts +302 -188
- package/dist/ffmpeg/record.js +408 -560
- package/dist/ffmpeg/record.js.map +1 -1
- package/dist/ffmpeg/recording-process-double.d.ts +144 -0
- package/dist/ffmpeg/recording-process-double.js +168 -0
- package/dist/ffmpeg/recording-process-double.js.map +1 -0
- package/dist/ffmpeg/rtp-parser.d.ts +70 -0
- package/dist/ffmpeg/rtp-parser.js +77 -0
- package/dist/ffmpeg/rtp-parser.js.map +1 -0
- package/dist/ffmpeg/rtp.d.ts +198 -141
- package/dist/ffmpeg/rtp.js +473 -244
- package/dist/ffmpeg/rtp.js.map +1 -1
- package/dist/ffmpeg/settings.d.ts +5 -1
- package/dist/ffmpeg/settings.js +20 -2
- package/dist/ffmpeg/settings.js.map +1 -1
- package/dist/ffmpeg/stream.d.ts +57 -107
- package/dist/ffmpeg/stream.js +121 -148
- package/dist/ffmpeg/stream.js.map +1 -1
- package/dist/formatters.d.ts +106 -0
- package/dist/formatters.js +174 -0
- package/dist/formatters.js.map +1 -0
- package/dist/homebridge-enums.d.ts +30 -0
- package/dist/homebridge-enums.js +17 -0
- package/dist/homebridge-enums.js.map +1 -0
- package/dist/index.d.ts +12 -5
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/logclient/auth.d.ts +114 -0
- package/dist/logclient/auth.js +199 -0
- package/dist/logclient/auth.js.map +1 -0
- package/dist/logclient/cli-run.d.ts +76 -0
- package/dist/logclient/cli-run.js +639 -0
- package/dist/logclient/cli-run.js.map +1 -0
- package/dist/logclient/cli.d.ts +3 -0
- package/dist/logclient/cli.js +97 -0
- package/dist/logclient/cli.js.map +1 -0
- package/dist/logclient/client.d.ts +145 -0
- package/dist/logclient/client.js +600 -0
- package/dist/logclient/client.js.map +1 -0
- package/dist/logclient/config.d.ts +173 -0
- package/dist/logclient/config.js +199 -0
- package/dist/logclient/config.js.map +1 -0
- package/dist/logclient/endpoints.d.ts +54 -0
- package/dist/logclient/endpoints.js +73 -0
- package/dist/logclient/endpoints.js.map +1 -0
- package/dist/logclient/filter.d.ts +45 -0
- package/dist/logclient/filter.js +51 -0
- package/dist/logclient/filter.js.map +1 -0
- package/dist/logclient/frame.d.ts +93 -0
- package/dist/logclient/frame.js +203 -0
- package/dist/logclient/frame.js.map +1 -0
- package/dist/logclient/index.d.ts +31 -0
- package/dist/logclient/index.js +12 -0
- package/dist/logclient/index.js.map +1 -0
- package/dist/logclient/parser.d.ts +211 -0
- package/dist/logclient/parser.js +393 -0
- package/dist/logclient/parser.js.map +1 -0
- package/dist/logclient/rest.d.ts +41 -0
- package/dist/logclient/rest.js +111 -0
- package/dist/logclient/rest.js.map +1 -0
- package/dist/logclient/settings.d.ts +15 -0
- package/dist/logclient/settings.js +64 -0
- package/dist/logclient/settings.js.map +1 -0
- package/dist/logclient/socket-double.d.ts +201 -0
- package/dist/logclient/socket-double.js +384 -0
- package/dist/logclient/socket-double.js.map +1 -0
- package/dist/logclient/socket.d.ts +257 -0
- package/dist/logclient/socket.js +620 -0
- package/dist/logclient/socket.js.map +1 -0
- package/dist/logclient/stitch.d.ts +83 -0
- package/dist/logclient/stitch.js +146 -0
- package/dist/logclient/stitch.js.map +1 -0
- package/dist/logclient/time-expression.d.ts +42 -0
- package/dist/logclient/time-expression.js +181 -0
- package/dist/logclient/time-expression.js.map +1 -0
- package/dist/logclient/time-window.d.ts +38 -0
- package/dist/logclient/time-window.js +53 -0
- package/dist/logclient/time-window.js.map +1 -0
- package/dist/logclient/types.d.ts +107 -0
- package/dist/logclient/types.js +6 -0
- package/dist/logclient/types.js.map +1 -0
- package/dist/mqttClient.d.ts +287 -0
- package/dist/mqttClient.js +433 -0
- package/dist/mqttClient.js.map +1 -0
- package/dist/service.d.ts +64 -15
- package/dist/service.js +93 -66
- package/dist/service.js.map +1 -1
- package/dist/ui/featureOptions.js +870 -0
- package/dist/ui/featureOptions.js.map +1 -0
- package/dist/ui/formatters.js +174 -0
- package/dist/ui/formatters.js.map +1 -0
- package/dist/ui/pluginConfigSession.mjs +141 -0
- package/dist/ui/webUi-featureOptions/categoryState.mjs +135 -0
- package/dist/ui/webUi-featureOptions/effects/keyboard.mjs +61 -0
- package/dist/ui/webUi-featureOptions/effects/persist.mjs +226 -0
- package/dist/ui/webUi-featureOptions/effects/theme.mjs +398 -0
- package/dist/ui/webUi-featureOptions/effects/tokens.mjs +152 -0
- package/dist/ui/webUi-featureOptions/rendering.mjs +431 -0
- package/dist/ui/webUi-featureOptions/selectors.mjs +360 -0
- package/dist/ui/webUi-featureOptions/state.mjs +319 -0
- package/dist/ui/webUi-featureOptions/store.mjs +181 -0
- package/dist/ui/webUi-featureOptions/utils.mjs +200 -0
- package/dist/ui/webUi-featureOptions/views/connectionError.mjs +152 -0
- package/dist/ui/webUi-featureOptions/views/deviceInfo.mjs +80 -0
- package/dist/ui/webUi-featureOptions/views/header.mjs +77 -0
- package/dist/ui/webUi-featureOptions/views/nav.mjs +327 -0
- package/dist/ui/webUi-featureOptions/views/options.mjs +521 -0
- package/dist/ui/webUi-featureOptions/views/search.mjs +395 -0
- package/dist/ui/webUi-featureOptions.mjs +677 -0
- package/dist/ui/webUi.mjs +192 -87
- package/dist/util.d.ts +597 -45
- package/dist/util.js +787 -68
- package/dist/util.js.map +1 -1
- package/package.json +33 -15
- package/build/eslint-rules.mjs +0 -511
- package/dist/featureoptions.d.ts +0 -264
- package/dist/featureoptions.js +0 -480
- package/dist/featureoptions.js.map +0 -1
- package/dist/mqttclient.d.ts +0 -178
- package/dist/mqttclient.js +0 -310
- package/dist/mqttclient.js.map +0 -1
- package/dist/ui/featureoptions.js +0 -480
- package/dist/ui/featureoptions.js.map +0 -1
- package/dist/ui/webUi-featureoptions.mjs +0 -3765
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* enforce-node-protocol.mjs: Require the `node:` protocol prefix on imports and re-exports of Node.js built-in modules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// The Node.js built-in modules that must be referenced via the `node:` protocol. Subpath imports (e.g., `timers/promises`) are matched by comparing the
|
|
7
|
+
// top-level segment before the first `/`, so adding a new builtin here covers both bare and subpath references in one shot. This list is hand-maintained
|
|
8
|
+
// rather than sourced from `node:module`'s `builtinModules` array so the rule's behavior stays fixed and version-independent across whatever Node runtime
|
|
9
|
+
// ESLint happens to execute under, instead of silently drifting as builtins are added or removed between Node releases.
|
|
10
|
+
const NODE_BUILTIN_MODULES = new Set([
|
|
11
|
+
"assert", "async_hooks", "buffer", "child_process", "cluster", "console", "crypto", "dgram", "diagnostics_channel", "dns", "domain", "events", "fs", "http", "http2",
|
|
12
|
+
"https", "inspector", "module", "net", "os", "path", "perf_hooks", "process", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "sys",
|
|
13
|
+
"timers", "tls", "trace_events", "tty", "url", "util", "v8", "vm", "wasi", "worker_threads", "zlib"
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
// Find the source literal that should be rewritten with a `node:` prefix, or null when the reference is not a built-in or already correctly prefixed. We
|
|
17
|
+
// look at the top-level module name (split on first `/`) so subpath references match too. For every supported visitor - Import / Export declarations
|
|
18
|
+
// and `ImportExpression` (dynamic `import("x")`) - the typescript-eslint AST places the source literal at `node.source`. A null result means either the
|
|
19
|
+
// value isn't a string (which also covers a declaration with no source clause at all, e.g. `export { foo };`, since `node.source` is then undefined) or
|
|
20
|
+
// the module isn't a builtin.
|
|
21
|
+
function findBuiltinSourceToPrefix(node, value) {
|
|
22
|
+
|
|
23
|
+
if(typeof value !== "string") {
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const topLevel = value.split("/", 1)[0];
|
|
29
|
+
|
|
30
|
+
if(!NODE_BUILTIN_MODULES.has(topLevel)) {
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return node.source ?? null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Require the `node:` protocol prefix on every reference to a Node.js built-in module.
|
|
39
|
+
//
|
|
40
|
+
// Cases covered:
|
|
41
|
+
// * `import x from "child_process"` is flagged; autofix rewrites to `import x from "node:child_process"`.
|
|
42
|
+
// * `export { x } from "fs"` is flagged; autofix rewrites the source string.
|
|
43
|
+
// * `export * from "stream"` is flagged; same shape, same autofix.
|
|
44
|
+
// * `import("crypto")` dynamic-import call form, flagged when the argument is a string literal.
|
|
45
|
+
// * `import x from "node:child_process"` canonical; ignored.
|
|
46
|
+
// * `import x from "lodash"` non-builtin; ignored.
|
|
47
|
+
// * `import x from "timers/promises"` subpath of a builtin; flagged via the top-level-segment match.
|
|
48
|
+
//
|
|
49
|
+
// The reported location is the source string literal itself, so editor markers pinpoint the offending text. The autofix replaces the literal with a
|
|
50
|
+
// double-quoted `node:`-prefixed form.
|
|
51
|
+
const ruleEnforceNodeProtocol = {
|
|
52
|
+
|
|
53
|
+
create(context) {
|
|
54
|
+
|
|
55
|
+
function reportIfViolation(node, value) {
|
|
56
|
+
|
|
57
|
+
const source = findBuiltinSourceToPrefix(node, value);
|
|
58
|
+
|
|
59
|
+
if(source === null) {
|
|
60
|
+
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
context.report({
|
|
65
|
+
|
|
66
|
+
fix(fixer) {
|
|
67
|
+
|
|
68
|
+
return fixer.replaceText(source, "\"node:" + value + "\"");
|
|
69
|
+
},
|
|
70
|
+
message: "Use the `node:` protocol prefix when importing Node.js built-in modules.",
|
|
71
|
+
node: source
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
|
|
77
|
+
ExportAllDeclaration(node) {
|
|
78
|
+
|
|
79
|
+
reportIfViolation(node, node.source?.value);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
ExportNamedDeclaration(node) {
|
|
83
|
+
|
|
84
|
+
reportIfViolation(node, node.source?.value);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
ImportDeclaration(node) {
|
|
88
|
+
|
|
89
|
+
reportIfViolation(node, node.source.value);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
ImportExpression(node) {
|
|
93
|
+
|
|
94
|
+
if(node.source.type === "Literal") {
|
|
95
|
+
|
|
96
|
+
reportIfViolation(node, node.source.value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
meta: {
|
|
102
|
+
|
|
103
|
+
docs: {
|
|
104
|
+
|
|
105
|
+
description: "require the `node:` protocol prefix on imports and re-exports of Node.js built-in modules",
|
|
106
|
+
recommended: false
|
|
107
|
+
},
|
|
108
|
+
fixable: "code",
|
|
109
|
+
schema: [],
|
|
110
|
+
type: "problem"
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default ruleEnforceNodeProtocol;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* enforce-node-protocol.test.mjs: Unit tests for the enforce-node-protocol rule covering each visitor (ImportDeclaration, ExportNamedDeclaration,
|
|
4
|
+
* ExportAllDeclaration, ImportExpression), the subpath-import case where the rule matches by the top-level segment, and the negative cases that prove
|
|
5
|
+
* already-prefixed and non-builtin references pass without flagging.
|
|
6
|
+
*/
|
|
7
|
+
import { RuleTester } from "../test-setup.mjs";
|
|
8
|
+
import rule from "./enforce-node-protocol.mjs";
|
|
9
|
+
|
|
10
|
+
const ruleTester = new RuleTester();
|
|
11
|
+
|
|
12
|
+
ruleTester.run("enforce-node-protocol", rule, {
|
|
13
|
+
|
|
14
|
+
invalid: [
|
|
15
|
+
|
|
16
|
+
// ImportDeclaration - bare builtin name, autofix prepends `node:`.
|
|
17
|
+
{
|
|
18
|
+
|
|
19
|
+
code: "import x from \"child_process\";",
|
|
20
|
+
errors: [{ message: /Use the `node:` protocol prefix/ }],
|
|
21
|
+
output: "import x from \"node:child_process\";"
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Named-import form of ImportDeclaration.
|
|
25
|
+
{
|
|
26
|
+
|
|
27
|
+
code: "import { spawn } from \"child_process\";",
|
|
28
|
+
errors: 1,
|
|
29
|
+
output: "import { spawn } from \"node:child_process\";"
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Namespace-import form of ImportDeclaration. The rule's `ImportDeclaration` visitor dispatches on `node.source.value` regardless of specifier
|
|
33
|
+
// shape, so namespace imports flag the same way as default and named imports do.
|
|
34
|
+
{
|
|
35
|
+
|
|
36
|
+
code: "import * as cp from \"child_process\";",
|
|
37
|
+
errors: 1,
|
|
38
|
+
output: "import * as cp from \"node:child_process\";"
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// ExportNamedDeclaration with a source clause - the source string gets prefixed.
|
|
42
|
+
{
|
|
43
|
+
|
|
44
|
+
code: "export { foo } from \"fs\";",
|
|
45
|
+
errors: 1,
|
|
46
|
+
output: "export { foo } from \"node:fs\";"
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// ExportAllDeclaration - same shape, same autofix.
|
|
50
|
+
{
|
|
51
|
+
|
|
52
|
+
code: "export * from \"stream\";",
|
|
53
|
+
errors: 1,
|
|
54
|
+
output: "export * from \"node:stream\";"
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Re-export with an alias clause - the autofix targets only the source literal, not the named clause.
|
|
58
|
+
{
|
|
59
|
+
|
|
60
|
+
code: "export * as nodeStream from \"stream\";",
|
|
61
|
+
errors: 1,
|
|
62
|
+
output: "export * as nodeStream from \"node:stream\";"
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// ImportExpression (dynamic import) with a string-literal argument. The rule's ImportExpression visitor guards on `node.source.type === "Literal"`
|
|
66
|
+
// before reporting, so this case fires; the dynamic-with-variable case in the valid list does not.
|
|
67
|
+
{
|
|
68
|
+
|
|
69
|
+
code: "const m = import(\"crypto\");",
|
|
70
|
+
errors: 1,
|
|
71
|
+
output: "const m = import(\"node:crypto\");"
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Subpath import of a builtin (`timers/promises`). The top-level segment matches the `NODE_BUILTIN_MODULES` Set, so the rule fires and the autofix
|
|
75
|
+
// prefixes the whole specifier including the subpath.
|
|
76
|
+
{
|
|
77
|
+
|
|
78
|
+
code: "import { setTimeout } from \"timers/promises\";",
|
|
79
|
+
errors: 1,
|
|
80
|
+
output: "import { setTimeout } from \"node:timers/promises\";"
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Subpath via a re-export.
|
|
84
|
+
{
|
|
85
|
+
|
|
86
|
+
code: "export { setImmediate } from \"timers/promises\";",
|
|
87
|
+
errors: 1,
|
|
88
|
+
output: "export { setImmediate } from \"node:timers/promises\";"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
|
|
92
|
+
valid: [
|
|
93
|
+
|
|
94
|
+
// Already-prefixed builtins - canonical, no rewrite.
|
|
95
|
+
"import x from \"node:child_process\";",
|
|
96
|
+
"import { spawn } from \"node:child_process\";",
|
|
97
|
+
"export { foo } from \"node:fs\";",
|
|
98
|
+
"export * from \"node:stream\";",
|
|
99
|
+
"const m = import(\"node:crypto\");",
|
|
100
|
+
"import { setTimeout } from \"node:timers/promises\";",
|
|
101
|
+
|
|
102
|
+
// Non-builtin packages - the rule's `NODE_BUILTIN_MODULES` Set does not contain them, so they pass through.
|
|
103
|
+
"import x from \"lodash\";",
|
|
104
|
+
"import { foo } from \"./local-module.mjs\";",
|
|
105
|
+
"import { bar } from \"@scope/package\";",
|
|
106
|
+
"export { baz } from \"my-package\";",
|
|
107
|
+
|
|
108
|
+
// Local re-export with no source clause - `ExportNamedDeclaration.source` is null, the rule's `findBuiltinSourceToPrefix` short-circuits via the
|
|
109
|
+
// `typeof value !== \"string\"` guard.
|
|
110
|
+
"const foo = 1; export { foo };",
|
|
111
|
+
|
|
112
|
+
// Dynamic import with a non-literal argument - the ImportExpression visitor's `node.source.type === \"Literal\"` guard skips this case so the rule
|
|
113
|
+
// does not fire on runtime-computed module specifiers.
|
|
114
|
+
"const name = \"crypto\"; const m = import(name);"
|
|
115
|
+
]
|
|
116
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* paren-comparisons-in-logical.mjs: Require parentheses around comparison operands when used inside compound logical expressions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// The set of comparison operators whose operands must be parenthesized when nested inside a compound logical expression.
|
|
7
|
+
const COMPARISON_OPERATORS = new Set([ "!=", "!==", "<", "<=", "==", "===", ">", ">=", "in", "instanceof" ]);
|
|
8
|
+
|
|
9
|
+
// The set of compound logical operators that trigger the parenthesization requirement on their direct comparison operands.
|
|
10
|
+
const LOGICAL_OPERATORS = new Set([ "&&", "||" ]);
|
|
11
|
+
|
|
12
|
+
// Test whether a node is a comparison binary expression with one of the recognized operators.
|
|
13
|
+
function isComparison(node) {
|
|
14
|
+
|
|
15
|
+
return !!node && (node.type === "BinaryExpression") && COMPARISON_OPERATORS.has(node.operator);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Test whether a node is already parenthesized in source. Duck-types a sourceCode.isParenthesized method for environments that may expose one,
|
|
19
|
+
// falling back to inspecting the tokens immediately surrounding the node when it is absent.
|
|
20
|
+
function isParenthesized(sourceCode, node) {
|
|
21
|
+
|
|
22
|
+
if(typeof sourceCode.isParenthesized === "function") {
|
|
23
|
+
|
|
24
|
+
return sourceCode.isParenthesized(node);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const before = sourceCode.getTokenBefore(node);
|
|
28
|
+
const after = sourceCode.getTokenAfter(node);
|
|
29
|
+
|
|
30
|
+
return !!before && !!after && (before.value === "(") && (after.value === ")");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Require parentheses around any comparison operand that is a direct child of a compound logical expression.
|
|
34
|
+
//
|
|
35
|
+
// Cases covered:
|
|
36
|
+
// * `a === b && c === d` both operands are flagged independently; autofix produces `(a === b) && (c === d)`.
|
|
37
|
+
// * `(a === b) && (c === d)` is canonical; ignored.
|
|
38
|
+
// * `a && b && c` has no comparison operands and is ignored.
|
|
39
|
+
// * `(a && b) === (c && d)` is a comparison containing logicals (not a logical containing comparisons); ignored at this level.
|
|
40
|
+
//
|
|
41
|
+
// The rule only inspects direct operands of `&&` and `||`. Deeper nested comparisons are reached because ESLint dispatches the `LogicalExpression` visitor on each
|
|
42
|
+
// nested logical node as it traverses the already-built AST.
|
|
43
|
+
const ruleParenComparisonsInLogical = {
|
|
44
|
+
|
|
45
|
+
create(context) {
|
|
46
|
+
|
|
47
|
+
const sourceCode = context.sourceCode;
|
|
48
|
+
|
|
49
|
+
function reportIfViolation(node) {
|
|
50
|
+
|
|
51
|
+
if(!isComparison(node) || isParenthesized(sourceCode, node)) {
|
|
52
|
+
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
context.report({
|
|
57
|
+
|
|
58
|
+
fix(fixer) {
|
|
59
|
+
|
|
60
|
+
return fixer.replaceText(node, "(" + sourceCode.getText(node) + ")");
|
|
61
|
+
},
|
|
62
|
+
message: "Wrap comparison operands in parentheses inside compound logical expressions (&&, ||).",
|
|
63
|
+
node
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
|
|
69
|
+
LogicalExpression(node) {
|
|
70
|
+
|
|
71
|
+
if(!LOGICAL_OPERATORS.has(node.operator)) {
|
|
72
|
+
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
reportIfViolation(node.left);
|
|
77
|
+
reportIfViolation(node.right);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
meta: {
|
|
82
|
+
|
|
83
|
+
docs: {
|
|
84
|
+
|
|
85
|
+
description: "require parentheses around comparison operands when used inside compound logical expressions (&&, ||)",
|
|
86
|
+
recommended: false
|
|
87
|
+
},
|
|
88
|
+
fixable: "code",
|
|
89
|
+
schema: [],
|
|
90
|
+
type: "layout"
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default ruleParenComparisonsInLogical;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* paren-comparisons-in-logical.test.mjs: Unit tests for the paren-comparisons-in-logical rule covering each comparison operator on either side of a
|
|
4
|
+
* compound logical expression, the partial-wrap case where only one operand is a comparison, and the negative passthrough cases that prove already-
|
|
5
|
+
* parenthesized comparisons and non-comparison operands are correctly ignored.
|
|
6
|
+
*/
|
|
7
|
+
import { RuleTester } from "../test-setup.mjs";
|
|
8
|
+
import rule from "./paren-comparisons-in-logical.mjs";
|
|
9
|
+
|
|
10
|
+
const ruleTester = new RuleTester();
|
|
11
|
+
|
|
12
|
+
ruleTester.run("paren-comparisons-in-logical", rule, {
|
|
13
|
+
|
|
14
|
+
invalid: [
|
|
15
|
+
|
|
16
|
+
// Equality comparisons on both operands of `&&` - both sides wrap.
|
|
17
|
+
{
|
|
18
|
+
|
|
19
|
+
code: "const r = a === b && c === d;",
|
|
20
|
+
errors: [ { message: /Wrap comparison operands in parentheses/ }, { message: /Wrap comparison operands in parentheses/ } ],
|
|
21
|
+
output: "const r = (a === b) && (c === d);"
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Inequality comparisons on both operands of `||`.
|
|
25
|
+
{
|
|
26
|
+
|
|
27
|
+
code: "const r = a !== b || c !== d;",
|
|
28
|
+
errors: 2,
|
|
29
|
+
output: "const r = (a !== b) || (c !== d);"
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Relational comparisons (`<`, `<=`, `>`, `>=`) and loose equality (`==`, `!=`) all wrap when used as direct operands of a logical expression.
|
|
33
|
+
{
|
|
34
|
+
|
|
35
|
+
code: "const r = x < 10 || y > 20;",
|
|
36
|
+
errors: 2,
|
|
37
|
+
output: "const r = (x < 10) || (y > 20);"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
|
|
41
|
+
code: "const r = x <= 10 && y >= 20;",
|
|
42
|
+
errors: 2,
|
|
43
|
+
output: "const r = (x <= 10) && (y >= 20);"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
|
|
47
|
+
code: "const r = x == null || y != 0;",
|
|
48
|
+
errors: 2,
|
|
49
|
+
output: "const r = (x == null) || (y != 0);"
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// `in` and `instanceof` are recognized as comparison operators by the rule and wrap when used as logical operands.
|
|
53
|
+
{
|
|
54
|
+
|
|
55
|
+
code: "const r = key in obj && key !== \"reserved\";",
|
|
56
|
+
errors: 2,
|
|
57
|
+
output: "const r = (key in obj) && (key !== \"reserved\");"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
|
|
61
|
+
code: "const r = x instanceof Foo || y instanceof Bar;",
|
|
62
|
+
errors: 2,
|
|
63
|
+
output: "const r = (x instanceof Foo) || (y instanceof Bar);"
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Asymmetric case - only the left operand is a comparison. Only that side wraps; the non-comparison right operand stays as-is.
|
|
67
|
+
{
|
|
68
|
+
|
|
69
|
+
code: "const r = a === b && ready;",
|
|
70
|
+
errors: 1,
|
|
71
|
+
output: "const r = (a === b) && ready;"
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Asymmetric case - only the right operand is a comparison.
|
|
75
|
+
{
|
|
76
|
+
|
|
77
|
+
code: "const r = ready && a === b;",
|
|
78
|
+
errors: 1,
|
|
79
|
+
output: "const r = ready && (a === b);"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
|
|
83
|
+
valid: [
|
|
84
|
+
|
|
85
|
+
// Already-parenthesized comparisons - canonical, no rewrite.
|
|
86
|
+
"const r = (a === b) && (c === d);",
|
|
87
|
+
"const r = (x < 10) || (y > 20);",
|
|
88
|
+
|
|
89
|
+
// Logical expressions whose operands are not comparisons - the rule has nothing to wrap.
|
|
90
|
+
"const r = a && b;",
|
|
91
|
+
"const r = a && b && c;",
|
|
92
|
+
"const r = a || b;",
|
|
93
|
+
"const r = ready && configured;",
|
|
94
|
+
|
|
95
|
+
// Comparison without a surrounding logical - the rule only flags direct operands of `&&` / `||`, so a bare comparison is ignored.
|
|
96
|
+
"const r = a === b;",
|
|
97
|
+
"const r = x < 10;",
|
|
98
|
+
|
|
99
|
+
// Comparison containing logicals (not a logical containing comparisons). The top-level binary expression is `===` - the rule only inspects operands
|
|
100
|
+
// when the parent is `&&` / `||`, so this configuration is ignored at the top level.
|
|
101
|
+
"const r = (a && b) === (c && d);",
|
|
102
|
+
|
|
103
|
+
// Non-comparison binary expressions (arithmetic) as logical operands - not in the comparison-operator set, no wrap.
|
|
104
|
+
"const r = (a + b) && (c - d);",
|
|
105
|
+
"const r = a + b && c - d;"
|
|
106
|
+
]
|
|
107
|
+
});
|