@timmo001/oxlint-rules 0.4.0 → 0.5.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 +39 -3
- package/dist/cli.js +436 -57
- package/dist/configs/recommended-effect.js +472 -97
- package/dist/configs/recommended.js +336 -16
- package/dist/effect/index.js +95 -11
- package/dist/generic/index.js +331 -15
- package/package.json +3 -1
- package/src/effect/index.ts +2 -0
- package/src/effect/rules/no-try-catch-in-effect-generators.ts +4 -98
- package/src/effect/rules/prefer-platform-services.ts +105 -0
- package/src/generic/index.ts +9 -0
- package/src/generic/rules/no-explicit-inferable-type-predicate.ts +146 -0
- package/src/generic/rules/no-unchecked-query-selector-type.ts +48 -0
- package/src/generic/rules/no-untyped-custom-event.ts +31 -0
- package/src/generic/rules/prefer-event-parameter-type.ts +36 -35
- package/src/generic/rules/prefer-lit-query-decorators.ts +84 -0
- package/src/generic/rules/prefer-types-over-assertions.ts +6 -5
- package/src/shared/ast.ts +31 -0
- package/src/shared/effect.ts +72 -0
- package/src/shared/json.ts +11 -0
- package/src/shared/lit.ts +219 -0
- package/src/shared/scope.ts +1 -0
|
@@ -8,16 +8,8 @@ import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
|
8
8
|
|
|
9
9
|
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
10
10
|
import { defineRule } from "@oxlint/plugins";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
while (scope) {
|
|
14
|
-
const variable = scope.set.get(identifier.name);
|
|
15
|
-
if (variable)
|
|
16
|
-
return variable;
|
|
17
|
-
scope = scope.upper;
|
|
18
|
-
}
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
11
|
+
|
|
12
|
+
// src/shared/ast.ts
|
|
21
13
|
function nearestEnclosingFunction(node) {
|
|
22
14
|
let current = node.parent;
|
|
23
15
|
while (current) {
|
|
@@ -33,6 +25,19 @@ function staticMemberName(node) {
|
|
|
33
25
|
return null;
|
|
34
26
|
return node.property.type === "Identifier" ? node.property.name : null;
|
|
35
27
|
}
|
|
28
|
+
|
|
29
|
+
// vendor/anti-slop/src/shared/scope.ts
|
|
30
|
+
function resolveVariable(sourceCode, identifier) {
|
|
31
|
+
let scope = sourceCode.getScope(identifier);
|
|
32
|
+
while (scope !== null) {
|
|
33
|
+
const variable = scope.set.get(identifier.name);
|
|
34
|
+
if (variable !== undefined)
|
|
35
|
+
return variable;
|
|
36
|
+
scope = scope.upper;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// src/shared/effect.ts
|
|
36
41
|
function isEffectImport(sourceCode, identifier, namespace) {
|
|
37
42
|
const variable = resolveVariable(sourceCode, identifier);
|
|
38
43
|
return variable?.defs.some((definition) => {
|
|
@@ -60,6 +65,11 @@ function isEffectMethod(sourceCode, node, method) {
|
|
|
60
65
|
}
|
|
61
66
|
return isEffectImport(sourceCode, object.object, true);
|
|
62
67
|
}
|
|
68
|
+
function importsEffect(program) {
|
|
69
|
+
return program.body.some((statement) => statement.type === "ImportDeclaration" && statement.importKind !== "type" && (statement.source.value === "effect" || statement.source.value.startsWith("effect/") || statement.source.value.startsWith("@effect/")));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
63
73
|
function isDirectArgument(owner, call) {
|
|
64
74
|
return call.arguments.some((argument) => argument === owner);
|
|
65
75
|
}
|
|
@@ -98,11 +108,85 @@ var noTryCatchInEffectGeneratorsRule = defineRule({
|
|
|
98
108
|
}
|
|
99
109
|
});
|
|
100
110
|
|
|
111
|
+
// src/effect/rules/prefer-platform-services.ts
|
|
112
|
+
import { defineRule as defineRule2 } from "@oxlint/plugins";
|
|
113
|
+
var moduleServices = new Map([
|
|
114
|
+
["child_process", "childProcess"],
|
|
115
|
+
["fs", "fileSystem"],
|
|
116
|
+
["fs/promises", "fileSystem"]
|
|
117
|
+
]);
|
|
118
|
+
var bunServices = new Map([
|
|
119
|
+
["$", "childProcess"],
|
|
120
|
+
["file", "fileSystem"],
|
|
121
|
+
["spawn", "childProcess"],
|
|
122
|
+
["spawnSync", "childProcess"],
|
|
123
|
+
["write", "fileSystem"]
|
|
124
|
+
]);
|
|
125
|
+
var preferPlatformServicesRule = defineRule2({
|
|
126
|
+
meta: {
|
|
127
|
+
type: "suggestion",
|
|
128
|
+
docs: {
|
|
129
|
+
description: "Prefer Effect's ChildProcessSpawner and FileSystem services over Node or Bun process and file system APIs in modules that use Effect."
|
|
130
|
+
},
|
|
131
|
+
messages: {
|
|
132
|
+
childProcess: "Use Effect's `ChildProcessSpawner` service (`effect/process`) instead of `{{api}}`, so scope cleanup, interruption, and test layers apply.",
|
|
133
|
+
fileSystem: "Use Effect's `FileSystem` service instead of `{{api}}`, so failures stay typed and tests can provide a layer."
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
create(context) {
|
|
137
|
+
let enabled = false;
|
|
138
|
+
return {
|
|
139
|
+
Program(node) {
|
|
140
|
+
enabled = importsEffect(node);
|
|
141
|
+
},
|
|
142
|
+
ImportDeclaration(node) {
|
|
143
|
+
if (!enabled || node.importKind === "type")
|
|
144
|
+
return;
|
|
145
|
+
const source = node.source.value;
|
|
146
|
+
const service = moduleServices.get(source.replace(/^node:/u, ""));
|
|
147
|
+
if (service) {
|
|
148
|
+
context.report({ node, messageId: service, data: { api: source } });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (source !== "bun")
|
|
152
|
+
return;
|
|
153
|
+
for (const specifier of node.specifiers) {
|
|
154
|
+
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const imported = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
158
|
+
const bunService = bunServices.get(imported);
|
|
159
|
+
if (bunService) {
|
|
160
|
+
context.report({
|
|
161
|
+
node: specifier,
|
|
162
|
+
messageId: bunService,
|
|
163
|
+
data: { api: `Bun.${imported}` }
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
MemberExpression(node) {
|
|
169
|
+
const name = staticMemberName(node);
|
|
170
|
+
const service = name ? bunServices.get(name) : undefined;
|
|
171
|
+
if (!enabled || !service || node.object.type !== "Identifier" || node.object.name !== "Bun" || (resolveVariable(context.sourceCode, node.object)?.defs.length ?? 0) > 0) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
context.report({
|
|
175
|
+
node,
|
|
176
|
+
messageId: service,
|
|
177
|
+
data: { api: `Bun.${name}` }
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
101
184
|
// src/effect/index.ts
|
|
102
185
|
var timmoEffectPlugin = eslintCompatPlugin({
|
|
103
186
|
meta: { name: "timmo-effect" },
|
|
104
187
|
rules: {
|
|
105
|
-
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule
|
|
188
|
+
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule,
|
|
189
|
+
"prefer-platform-services": preferPlatformServicesRule
|
|
106
190
|
}
|
|
107
191
|
});
|
|
108
192
|
var effect_default = timmoEffectPlugin;
|
|
@@ -111,7 +195,7 @@ var effect_default = timmoEffectPlugin;
|
|
|
111
195
|
import { eslintCompatPlugin as eslintCompatPlugin2 } from "@oxlint/plugins";
|
|
112
196
|
|
|
113
197
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
114
|
-
import { defineRule as
|
|
198
|
+
import { defineRule as defineRule3 } from "@oxlint/plugins";
|
|
115
199
|
|
|
116
200
|
// vendor/anti-slop/src/effect/shared/tagged-values.ts
|
|
117
201
|
var equalityOperators = new Set(["==", "===", "!=", "!=="]);
|
|
@@ -158,7 +242,7 @@ var isMatchPatternObject = (node) => {
|
|
|
158
242
|
};
|
|
159
243
|
|
|
160
244
|
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
161
|
-
var noManualEffectErrorTagRule =
|
|
245
|
+
var noManualEffectErrorTagRule = defineRule3({
|
|
162
246
|
meta: {
|
|
163
247
|
type: "problem",
|
|
164
248
|
docs: {
|
|
@@ -195,8 +279,8 @@ var noManualEffectErrorTagRule = defineRule2({
|
|
|
195
279
|
});
|
|
196
280
|
|
|
197
281
|
// vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts
|
|
198
|
-
import { defineRule as
|
|
199
|
-
var noManualTagComparisonRule =
|
|
282
|
+
import { defineRule as defineRule4 } from "@oxlint/plugins";
|
|
283
|
+
var noManualTagComparisonRule = defineRule4({
|
|
200
284
|
meta: {
|
|
201
285
|
type: "problem",
|
|
202
286
|
docs: {
|
|
@@ -226,8 +310,8 @@ var noManualTagComparisonRule = defineRule3({
|
|
|
226
310
|
});
|
|
227
311
|
|
|
228
312
|
// vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts
|
|
229
|
-
import { defineRule as
|
|
230
|
-
var noManualTaggedConstructionRule =
|
|
313
|
+
import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
314
|
+
var noManualTaggedConstructionRule = defineRule5({
|
|
231
315
|
meta: {
|
|
232
316
|
type: "problem",
|
|
233
317
|
docs: {
|
|
@@ -252,7 +336,7 @@ var noManualTaggedConstructionRule = defineRule4({
|
|
|
252
336
|
});
|
|
253
337
|
|
|
254
338
|
// vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
|
|
255
|
-
import { defineRule as
|
|
339
|
+
import { defineRule as defineRule6 } from "@oxlint/plugins";
|
|
256
340
|
var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
|
|
257
341
|
var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
258
342
|
function isProjectLocalImport(source) {
|
|
@@ -263,7 +347,7 @@ function getImportedName(specifier) {
|
|
|
263
347
|
return specifier.imported.name;
|
|
264
348
|
return specifier.imported.value;
|
|
265
349
|
}
|
|
266
|
-
var noServiceConstructorImportsRule =
|
|
350
|
+
var noServiceConstructorImportsRule = defineRule6({
|
|
267
351
|
meta: {
|
|
268
352
|
type: "problem",
|
|
269
353
|
docs: {
|
|
@@ -297,9 +381,9 @@ var noServiceConstructorImportsRule = defineRule5({
|
|
|
297
381
|
});
|
|
298
382
|
|
|
299
383
|
// vendor/anti-slop/src/effect/rules/prefer-effect-match.ts
|
|
300
|
-
import { defineRule as
|
|
384
|
+
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
301
385
|
var equalityOperators2 = new Set(["==", "===", "!=", "!=="]);
|
|
302
|
-
var preferEffectMatchRule =
|
|
386
|
+
var preferEffectMatchRule = defineRule7({
|
|
303
387
|
meta: {
|
|
304
388
|
type: "problem",
|
|
305
389
|
docs: {
|
|
@@ -365,7 +449,7 @@ function enablePluginRules(namespace, plugin) {
|
|
|
365
449
|
import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
|
|
366
450
|
|
|
367
451
|
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
368
|
-
import { defineRule as
|
|
452
|
+
import { defineRule as defineRule8 } from "@oxlint/plugins";
|
|
369
453
|
|
|
370
454
|
// vendor/anti-slop/src/shared/array-method.ts
|
|
371
455
|
function unwrapArrayExpression(node) {
|
|
@@ -440,7 +524,7 @@ function isKnownArrayExpression(sourceCode, node, visited = new Set) {
|
|
|
440
524
|
}
|
|
441
525
|
|
|
442
526
|
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
443
|
-
var noArrayFilterMapRule =
|
|
527
|
+
var noArrayFilterMapRule = defineRule8({
|
|
444
528
|
meta: {
|
|
445
529
|
type: "suggestion",
|
|
446
530
|
docs: { description: "Disallow adjacent array filter/map passes in favor of lazy iterator helpers or a single transformation." },
|
|
@@ -469,7 +553,7 @@ var noArrayFilterMapRule = defineRule7({
|
|
|
469
553
|
});
|
|
470
554
|
|
|
471
555
|
// vendor/anti-slop/src/rules/no-reduce-accumulator-copy.ts
|
|
472
|
-
import { defineRule as
|
|
556
|
+
import { defineRule as defineRule9 } from "@oxlint/plugins";
|
|
473
557
|
function enclosingReducer(node) {
|
|
474
558
|
let parent = node.parent;
|
|
475
559
|
while (parent !== null) {
|
|
@@ -519,7 +603,7 @@ function isGlobalCopyOwner(sourceCode, node, name) {
|
|
|
519
603
|
const variable = resolveArrayBinding(sourceCode, node);
|
|
520
604
|
return variable === null || variable.defs.length === 0;
|
|
521
605
|
}
|
|
522
|
-
var noReduceAccumulatorCopyRule =
|
|
606
|
+
var noReduceAccumulatorCopyRule = defineRule9({
|
|
523
607
|
meta: {
|
|
524
608
|
type: "problem",
|
|
525
609
|
docs: { description: "Disallow copying growing reducer accumulators with Object.assign, Array.from, or array copy methods." },
|
|
@@ -560,7 +644,7 @@ var noReduceAccumulatorCopyRule = defineRule8({
|
|
|
560
644
|
});
|
|
561
645
|
|
|
562
646
|
// vendor/anti-slop/src/rules/no-chained-type-assertions.ts
|
|
563
|
-
import { defineRule as
|
|
647
|
+
import { defineRule as defineRule10 } from "@oxlint/plugins";
|
|
564
648
|
function isTypeAssertionExpression(node) {
|
|
565
649
|
return node.type === "TSAsExpression" || node.type === "TSTypeAssertion";
|
|
566
650
|
}
|
|
@@ -595,7 +679,7 @@ function isForbiddenAssertionChain(node) {
|
|
|
595
679
|
}
|
|
596
680
|
return assertionCount > 1 && hasNonConstAssertion;
|
|
597
681
|
}
|
|
598
|
-
var noChainedTypeAssertionsRule =
|
|
682
|
+
var noChainedTypeAssertionsRule = defineRule10({
|
|
599
683
|
meta: {
|
|
600
684
|
type: "problem",
|
|
601
685
|
docs: {
|
|
@@ -619,7 +703,7 @@ var noChainedTypeAssertionsRule = defineRule9({
|
|
|
619
703
|
});
|
|
620
704
|
|
|
621
705
|
// vendor/anti-slop/src/rules/no-conditional-empty-object-spread.ts
|
|
622
|
-
import { defineRule as
|
|
706
|
+
import { defineRule as defineRule11 } from "@oxlint/plugins";
|
|
623
707
|
function unwrapParentheses(node) {
|
|
624
708
|
let current = node;
|
|
625
709
|
while (current.type === "ParenthesizedExpression") {
|
|
@@ -634,7 +718,7 @@ function isConditionalEmptyObjectSpread(node) {
|
|
|
634
718
|
const conditional = unwrapParentheses(node);
|
|
635
719
|
return conditional.type === "ConditionalExpression" && (isEmptyObjectExpression(conditional.consequent) || isEmptyObjectExpression(conditional.alternate));
|
|
636
720
|
}
|
|
637
|
-
var noConditionalEmptyObjectSpreadRule =
|
|
721
|
+
var noConditionalEmptyObjectSpreadRule = defineRule11({
|
|
638
722
|
meta: {
|
|
639
723
|
type: "suggestion",
|
|
640
724
|
docs: {
|
|
@@ -658,7 +742,7 @@ var noConditionalEmptyObjectSpreadRule = defineRule10({
|
|
|
658
742
|
});
|
|
659
743
|
|
|
660
744
|
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
661
|
-
import { defineRule as
|
|
745
|
+
import { defineRule as defineRule12 } from "@oxlint/plugins";
|
|
662
746
|
|
|
663
747
|
// vendor/anti-slop/src/shared/lexical-type-parameters.ts
|
|
664
748
|
function isNode(value) {
|
|
@@ -1187,18 +1271,6 @@ function functionParameterBindingName(parameter, sourceCode) {
|
|
|
1187
1271
|
return annotationStart === undefined ? sourceText : sourceText.slice(0, annotationStart - parameter.start).trimEnd();
|
|
1188
1272
|
}
|
|
1189
1273
|
|
|
1190
|
-
// vendor/anti-slop/src/shared/scope.ts
|
|
1191
|
-
function resolveVariable2(sourceCode, identifier) {
|
|
1192
|
-
let scope = sourceCode.getScope(identifier);
|
|
1193
|
-
while (scope !== null) {
|
|
1194
|
-
const variable = scope.set.get(identifier.name);
|
|
1195
|
-
if (variable !== undefined)
|
|
1196
|
-
return variable;
|
|
1197
|
-
scope = scope.upper;
|
|
1198
|
-
}
|
|
1199
|
-
return null;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
1274
|
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
1203
1275
|
function unwrapExpression(expression) {
|
|
1204
1276
|
let current = expression;
|
|
@@ -1222,7 +1294,7 @@ function hasKnownEvidence(sourceCode, expression, visitedVariables = new Set) {
|
|
|
1222
1294
|
const unwrapped = unwrapExpression(expression);
|
|
1223
1295
|
if (unwrapped.type !== "Identifier")
|
|
1224
1296
|
return false;
|
|
1225
|
-
const variable =
|
|
1297
|
+
const variable = resolveVariable(sourceCode, unwrapped);
|
|
1226
1298
|
if (variable === null || visitedVariables.has(variable))
|
|
1227
1299
|
return false;
|
|
1228
1300
|
const declarator = variableDeclarator(variable);
|
|
@@ -1241,7 +1313,7 @@ function localFunctionForCall(sourceCode, callee) {
|
|
|
1241
1313
|
return unwrapped;
|
|
1242
1314
|
if (unwrapped.type !== "Identifier")
|
|
1243
1315
|
return null;
|
|
1244
|
-
const variable =
|
|
1316
|
+
const variable = resolveVariable(sourceCode, unwrapped);
|
|
1245
1317
|
if (variable === null || variable.defs.length !== 1)
|
|
1246
1318
|
return null;
|
|
1247
1319
|
const [definition] = variable.defs;
|
|
@@ -1294,7 +1366,7 @@ function hasKnownCallArgumentEvidence(sourceCode, expression, environment, visit
|
|
|
1294
1366
|
}
|
|
1295
1367
|
if (expression.type !== "Identifier")
|
|
1296
1368
|
return isKnownEvidenceExpression(expression);
|
|
1297
|
-
const variable =
|
|
1369
|
+
const variable = resolveVariable(sourceCode, expression);
|
|
1298
1370
|
if (variable === null || visitedVariables.has(variable))
|
|
1299
1371
|
return false;
|
|
1300
1372
|
const annotation = variableTypeAnnotation(sourceCode, variable);
|
|
@@ -1359,7 +1431,7 @@ function isDictionaryAccumulatorTarget(destination) {
|
|
|
1359
1431
|
function hasParentAssertion(node) {
|
|
1360
1432
|
return node.parent?.type === "TSAsExpression" || node.parent?.type === "TSTypeAssertion";
|
|
1361
1433
|
}
|
|
1362
|
-
var noKnownValueWideningRule =
|
|
1434
|
+
var noKnownValueWideningRule = defineRule12({
|
|
1363
1435
|
meta: {
|
|
1364
1436
|
type: "problem",
|
|
1365
1437
|
docs: {
|
|
@@ -1408,7 +1480,7 @@ var noKnownValueWideningRule = defineRule11({
|
|
|
1408
1480
|
AssignmentExpression(node) {
|
|
1409
1481
|
if (node.operator !== "=" || node.left.type !== "Identifier")
|
|
1410
1482
|
return;
|
|
1411
|
-
const variable =
|
|
1483
|
+
const variable = resolveVariable(context.sourceCode, node.left);
|
|
1412
1484
|
if (variable === null)
|
|
1413
1485
|
return;
|
|
1414
1486
|
const declarator = variableDeclarator(variable);
|
|
@@ -1472,7 +1544,7 @@ var noKnownValueWideningRule = defineRule11({
|
|
|
1472
1544
|
});
|
|
1473
1545
|
|
|
1474
1546
|
// vendor/anti-slop/src/rules/no-module-mocking.ts
|
|
1475
|
-
import { defineRule as
|
|
1547
|
+
import { defineRule as defineRule13 } from "@oxlint/plugins";
|
|
1476
1548
|
var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
|
|
1477
1549
|
function importedName(node) {
|
|
1478
1550
|
if (node.type !== "ImportSpecifier")
|
|
@@ -1485,7 +1557,7 @@ function isTestFrameworkObject(sourceCode, expression) {
|
|
|
1485
1557
|
if ((expression.name === "vi" || expression.name === "jest") && sourceCode.isGlobalReference(expression)) {
|
|
1486
1558
|
return true;
|
|
1487
1559
|
}
|
|
1488
|
-
const variable =
|
|
1560
|
+
const variable = resolveVariable(sourceCode, expression);
|
|
1489
1561
|
if (variable === null || variable.defs.length === 0) {
|
|
1490
1562
|
return expression.name === "vi" || expression.name === "jest";
|
|
1491
1563
|
}
|
|
@@ -1507,7 +1579,7 @@ function moduleMockCall(sourceCode, callee) {
|
|
|
1507
1579
|
const method = callee.computed ? property.type === "Literal" && (property.value === "doMock" || property.value === "mock" || property.value === "unstable_mockModule") ? property.value : null : property.type === "Identifier" ? property.name : null;
|
|
1508
1580
|
return method !== null && moduleMockMethods.has(method);
|
|
1509
1581
|
}
|
|
1510
|
-
var noModuleMockingRule =
|
|
1582
|
+
var noModuleMockingRule = defineRule13({
|
|
1511
1583
|
meta: {
|
|
1512
1584
|
type: "problem",
|
|
1513
1585
|
docs: {
|
|
@@ -1531,8 +1603,8 @@ var noModuleMockingRule = defineRule12({
|
|
|
1531
1603
|
});
|
|
1532
1604
|
|
|
1533
1605
|
// vendor/anti-slop/src/rules/no-object-parameters.ts
|
|
1534
|
-
import { defineRule as
|
|
1535
|
-
var noObjectParametersRule =
|
|
1606
|
+
import { defineRule as defineRule14 } from "@oxlint/plugins";
|
|
1607
|
+
var noObjectParametersRule = defineRule14({
|
|
1536
1608
|
meta: {
|
|
1537
1609
|
type: "problem",
|
|
1538
1610
|
docs: {
|
|
@@ -1585,7 +1657,7 @@ var noObjectParametersRule = defineRule13({
|
|
|
1585
1657
|
});
|
|
1586
1658
|
|
|
1587
1659
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1588
|
-
import { defineRule as
|
|
1660
|
+
import { defineRule as defineRule15 } from "@oxlint/plugins";
|
|
1589
1661
|
|
|
1590
1662
|
// vendor/anti-slop/src/shared/reflect-method.ts
|
|
1591
1663
|
function isGlobalReflect(sourceCode, expression) {
|
|
@@ -1593,7 +1665,7 @@ function isGlobalReflect(sourceCode, expression) {
|
|
|
1593
1665
|
return false;
|
|
1594
1666
|
if (sourceCode.isGlobalReference(expression))
|
|
1595
1667
|
return true;
|
|
1596
|
-
const variable =
|
|
1668
|
+
const variable = resolveVariable(sourceCode, expression);
|
|
1597
1669
|
return variable === null || variable.defs.length === 0;
|
|
1598
1670
|
}
|
|
1599
1671
|
function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
@@ -1606,7 +1678,7 @@ function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
|
1606
1678
|
}
|
|
1607
1679
|
|
|
1608
1680
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1609
|
-
var noReflectApplyRule =
|
|
1681
|
+
var noReflectApplyRule = defineRule15({
|
|
1610
1682
|
meta: {
|
|
1611
1683
|
type: "problem",
|
|
1612
1684
|
docs: {
|
|
@@ -1630,8 +1702,8 @@ var noReflectApplyRule = defineRule14({
|
|
|
1630
1702
|
});
|
|
1631
1703
|
|
|
1632
1704
|
// vendor/anti-slop/src/rules/no-reflect-get.ts
|
|
1633
|
-
import { defineRule as
|
|
1634
|
-
var noReflectGetRule =
|
|
1705
|
+
import { defineRule as defineRule16 } from "@oxlint/plugins";
|
|
1706
|
+
var noReflectGetRule = defineRule16({
|
|
1635
1707
|
meta: {
|
|
1636
1708
|
type: "problem",
|
|
1637
1709
|
docs: {
|
|
@@ -1655,7 +1727,7 @@ var noReflectGetRule = defineRule15({
|
|
|
1655
1727
|
});
|
|
1656
1728
|
|
|
1657
1729
|
// vendor/anti-slop/src/rules/no-runtime-typeof.ts
|
|
1658
|
-
import { defineRule as
|
|
1730
|
+
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
1659
1731
|
function isRuntimeFunction(node) {
|
|
1660
1732
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
|
|
1661
1733
|
}
|
|
@@ -1678,7 +1750,7 @@ function isExistenceProbe(node) {
|
|
|
1678
1750
|
const other = parent.left === node ? parent.right : parent.left;
|
|
1679
1751
|
return other.type === "Literal" && other.value === "undefined";
|
|
1680
1752
|
}
|
|
1681
|
-
var noRuntimeTypeofRule =
|
|
1753
|
+
var noRuntimeTypeofRule = defineRule17({
|
|
1682
1754
|
meta: {
|
|
1683
1755
|
type: "problem",
|
|
1684
1756
|
docs: {
|
|
@@ -1712,7 +1784,7 @@ var noRuntimeTypeofRule = defineRule16({
|
|
|
1712
1784
|
});
|
|
1713
1785
|
|
|
1714
1786
|
// vendor/anti-slop/src/rules/no-shape-in-symbol-names.ts
|
|
1715
|
-
import { defineRule as
|
|
1787
|
+
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
1716
1788
|
var FORBIDDEN_SYMBOL_NAME = "shape";
|
|
1717
1789
|
function containsForbiddenSymbolName(name) {
|
|
1718
1790
|
return name.toLowerCase().includes(FORBIDDEN_SYMBOL_NAME);
|
|
@@ -1723,7 +1795,7 @@ function isBorrowedMemberName(node) {
|
|
|
1723
1795
|
return false;
|
|
1724
1796
|
return parent.property === node && parent.computed === false;
|
|
1725
1797
|
}
|
|
1726
|
-
var noForbiddenTermInSymbolNamesRule =
|
|
1798
|
+
var noForbiddenTermInSymbolNamesRule = defineRule18({
|
|
1727
1799
|
meta: {
|
|
1728
1800
|
type: "problem",
|
|
1729
1801
|
docs: {
|
|
@@ -1752,12 +1824,12 @@ var noForbiddenTermInSymbolNamesRule = defineRule17({
|
|
|
1752
1824
|
});
|
|
1753
1825
|
|
|
1754
1826
|
// vendor/anti-slop/src/rules/no-unknown-parameters.ts
|
|
1755
|
-
import { defineRule as
|
|
1827
|
+
import { defineRule as defineRule19 } from "@oxlint/plugins";
|
|
1756
1828
|
function isTypePredicateSubject(owner, parameterName) {
|
|
1757
1829
|
const predicate = owner.returnType?.typeAnnotation;
|
|
1758
1830
|
return predicate?.type === "TSTypePredicate" && predicate.parameterName.type === "Identifier" && predicate.parameterName.name === parameterName;
|
|
1759
1831
|
}
|
|
1760
|
-
var noUnknownParametersRule =
|
|
1832
|
+
var noUnknownParametersRule = defineRule19({
|
|
1761
1833
|
meta: {
|
|
1762
1834
|
type: "problem",
|
|
1763
1835
|
docs: {
|
|
@@ -1801,8 +1873,8 @@ var noUnknownParametersRule = defineRule18({
|
|
|
1801
1873
|
});
|
|
1802
1874
|
|
|
1803
1875
|
// vendor/anti-slop/src/rules/no-unknown-returns.ts
|
|
1804
|
-
import { defineRule as
|
|
1805
|
-
var noUnknownReturnsRule =
|
|
1876
|
+
import { defineRule as defineRule20 } from "@oxlint/plugins";
|
|
1877
|
+
var noUnknownReturnsRule = defineRule20({
|
|
1806
1878
|
meta: {
|
|
1807
1879
|
type: "problem",
|
|
1808
1880
|
docs: {
|
|
@@ -1855,8 +1927,8 @@ var noUnknownReturnsRule = defineRule19({
|
|
|
1855
1927
|
});
|
|
1856
1928
|
|
|
1857
1929
|
// vendor/anti-slop/src/rules/no-unknown-type-aliases.ts
|
|
1858
|
-
import { defineRule as
|
|
1859
|
-
var noUnknownTypeAliasesRule =
|
|
1930
|
+
import { defineRule as defineRule21 } from "@oxlint/plugins";
|
|
1931
|
+
var noUnknownTypeAliasesRule = defineRule21({
|
|
1860
1932
|
meta: {
|
|
1861
1933
|
type: "problem",
|
|
1862
1934
|
docs: {
|
|
@@ -1894,7 +1966,7 @@ var noUnknownTypeAliasesRule = defineRule20({
|
|
|
1894
1966
|
});
|
|
1895
1967
|
|
|
1896
1968
|
// vendor/anti-slop/src/rules/no-unsafe-dictionary-type.ts
|
|
1897
|
-
import { defineRule as
|
|
1969
|
+
import { defineRule as defineRule22 } from "@oxlint/plugins";
|
|
1898
1970
|
var typeNodeKinds = new Set([
|
|
1899
1971
|
"JSDocNonNullableType",
|
|
1900
1972
|
"JSDocNullableType",
|
|
@@ -1981,7 +2053,7 @@ function shouldReportType(node, environment) {
|
|
|
1981
2053
|
}
|
|
1982
2054
|
return true;
|
|
1983
2055
|
}
|
|
1984
|
-
var noUnsafeDictionaryTypeRule =
|
|
2056
|
+
var noUnsafeDictionaryTypeRule = defineRule22({
|
|
1985
2057
|
meta: {
|
|
1986
2058
|
type: "problem",
|
|
1987
2059
|
docs: {
|
|
@@ -2023,7 +2095,7 @@ var noUnsafeDictionaryTypeRule = defineRule21({
|
|
|
2023
2095
|
});
|
|
2024
2096
|
|
|
2025
2097
|
// vendor/anti-slop/src/rules/no-widen-then-assert.ts
|
|
2026
|
-
import { defineRule as
|
|
2098
|
+
import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
2027
2099
|
var functionBoundaryTypes = new Set([
|
|
2028
2100
|
"ArrowFunctionExpression",
|
|
2029
2101
|
"FunctionDeclaration",
|
|
@@ -2219,7 +2291,7 @@ function assertionIsNarrower(sourceText, broadKind, evidence, assertedType) {
|
|
|
2219
2291
|
return isDefinitelyObjectType(assertedType);
|
|
2220
2292
|
return isDefinitelyNarrowerRecordType(assertedType);
|
|
2221
2293
|
}
|
|
2222
|
-
var noWidenThenAssertRule =
|
|
2294
|
+
var noWidenThenAssertRule = defineRule23({
|
|
2223
2295
|
meta: {
|
|
2224
2296
|
type: "problem",
|
|
2225
2297
|
docs: {
|
|
@@ -2781,7 +2853,7 @@ var requireReadableSpacingRule = {
|
|
|
2781
2853
|
};
|
|
2782
2854
|
|
|
2783
2855
|
// vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
|
|
2784
|
-
import { defineRule as
|
|
2856
|
+
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
2785
2857
|
var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
|
|
2786
2858
|
var commentOwnerKinds = new Set([
|
|
2787
2859
|
"ExpressionStatement",
|
|
@@ -2824,7 +2896,7 @@ function hasSafetyComment(sourceCode, node, pattern) {
|
|
|
2824
2896
|
current = current.parent;
|
|
2825
2897
|
}
|
|
2826
2898
|
}
|
|
2827
|
-
var requireSafetyCommentForTypeAssertionRule =
|
|
2899
|
+
var requireSafetyCommentForTypeAssertionRule = defineRule24({
|
|
2828
2900
|
meta: {
|
|
2829
2901
|
type: "problem",
|
|
2830
2902
|
docs: {
|
|
@@ -2901,42 +2973,277 @@ var src_default = antiSlopPlugin;
|
|
|
2901
2973
|
// src/generic/index.ts
|
|
2902
2974
|
import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
|
|
2903
2975
|
|
|
2904
|
-
// src/generic/rules/
|
|
2905
|
-
import { defineRule as
|
|
2906
|
-
function
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2976
|
+
// src/generic/rules/no-explicit-inferable-type-predicate.ts
|
|
2977
|
+
import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
2978
|
+
function isNamed(node, name) {
|
|
2979
|
+
return node.type === "Identifier" && node.name === name;
|
|
2980
|
+
}
|
|
2981
|
+
function isNullish(node) {
|
|
2982
|
+
return node.type === "Literal" && node.value === null || isNamed(node, "undefined");
|
|
2983
|
+
}
|
|
2984
|
+
function isNullishExclusion(node, name) {
|
|
2985
|
+
return node.type === "BinaryExpression" && (node.operator === "!==" || node.operator === "!=") && (isNamed(node.left, name) && isNullish(node.right) || isNullish(node.left) && isNamed(node.right, name));
|
|
2986
|
+
}
|
|
2987
|
+
function isTypeofCheck(node, name) {
|
|
2988
|
+
if (node.type !== "BinaryExpression" || node.operator !== "===" && node.operator !== "==") {
|
|
2989
|
+
return false;
|
|
2990
|
+
}
|
|
2991
|
+
const isTypeof = (side) => side.type === "UnaryExpression" && side.operator === "typeof" && isNamed(side.argument, name);
|
|
2992
|
+
const isLiteral = (side) => side.type === "Literal";
|
|
2993
|
+
return isTypeof(node.left) && isLiteral(node.right) || isLiteral(node.left) && isTypeof(node.right);
|
|
2994
|
+
}
|
|
2995
|
+
function isInferableCheck(node, name) {
|
|
2996
|
+
if (node.type === "LogicalExpression") {
|
|
2997
|
+
return node.operator === "&&" && isNullishExclusion(node.left, name) && isNullishExclusion(node.right, name);
|
|
2998
|
+
}
|
|
2999
|
+
return isNullishExclusion(node, name) || isTypeofCheck(node, name) || node.type === "BinaryExpression" && node.operator === "instanceof" && isNamed(node.left, name);
|
|
3000
|
+
}
|
|
3001
|
+
function returnedExpression(owner) {
|
|
3002
|
+
const body = owner.body;
|
|
3003
|
+
if (!body)
|
|
3004
|
+
return null;
|
|
3005
|
+
if (body.type !== "BlockStatement")
|
|
3006
|
+
return body;
|
|
3007
|
+
const [statement] = body.body;
|
|
3008
|
+
return body.body.length === 1 && statement?.type === "ReturnStatement" ? statement.argument : null;
|
|
3009
|
+
}
|
|
3010
|
+
function isPublicSignature(owner) {
|
|
3011
|
+
const parent = owner.parent;
|
|
3012
|
+
if (parent.type === "MethodDefinition" || parent.type === "ExportNamedDeclaration" || parent.type === "ExportDefaultDeclaration") {
|
|
3013
|
+
return true;
|
|
3014
|
+
}
|
|
3015
|
+
return parent.type === "VariableDeclarator" && parent.parent.parent?.type === "ExportNamedDeclaration";
|
|
3016
|
+
}
|
|
3017
|
+
var noExplicitInferableTypePredicateRule = defineRule25({
|
|
3018
|
+
meta: {
|
|
3019
|
+
type: "suggestion",
|
|
3020
|
+
docs: {
|
|
3021
|
+
description: "Disallow explicit type predicates on simple null, typeof, or instanceof checks that TypeScript 5.5+ infers."
|
|
3022
|
+
},
|
|
3023
|
+
messages: {
|
|
3024
|
+
removeTypePredicate: "TypeScript infers the type predicate for `{{parameter}}` from this check, while an explicit predicate is never checked against the body. Remove the annotation and rely on the inferred predicate."
|
|
2911
3025
|
}
|
|
2912
|
-
|
|
3026
|
+
},
|
|
3027
|
+
create(context) {
|
|
3028
|
+
const check = (owner) => {
|
|
3029
|
+
const annotation = owner.returnType?.typeAnnotation;
|
|
3030
|
+
if (annotation?.type !== "TSTypePredicate" || annotation.asserts || annotation.parameterName.type !== "Identifier" || isPublicSignature(owner)) {
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
const parameter = annotation.parameterName.name;
|
|
3034
|
+
const expression = returnedExpression(owner);
|
|
3035
|
+
if (!owner.params.some((param) => isNamed(param, parameter)) || !expression || !isInferableCheck(expression, parameter)) {
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
context.report({
|
|
3039
|
+
node: annotation,
|
|
3040
|
+
messageId: "removeTypePredicate",
|
|
3041
|
+
data: { parameter }
|
|
3042
|
+
});
|
|
3043
|
+
};
|
|
3044
|
+
return {
|
|
3045
|
+
ArrowFunctionExpression: check,
|
|
3046
|
+
FunctionDeclaration: check,
|
|
3047
|
+
FunctionExpression: check
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
});
|
|
3051
|
+
|
|
3052
|
+
// src/generic/rules/no-unchecked-query-selector-type.ts
|
|
3053
|
+
import { defineRule as defineRule26 } from "@oxlint/plugins";
|
|
3054
|
+
|
|
3055
|
+
// src/shared/json.ts
|
|
3056
|
+
function jsonObject(value) {
|
|
3057
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
// src/shared/lit.ts
|
|
3061
|
+
var renderRoots = new Set(["renderRoot", "shadowRoot"]);
|
|
3062
|
+
function litElementBaseClasses(settings) {
|
|
3063
|
+
const configured = jsonObject(settings.lit)?.elementBaseClasses;
|
|
3064
|
+
return new Set([
|
|
3065
|
+
"LitElement",
|
|
3066
|
+
...Array.isArray(configured) ? configured.map(String) : []
|
|
3067
|
+
]);
|
|
3068
|
+
}
|
|
3069
|
+
function isWrapperExpression(node) {
|
|
3070
|
+
return node?.type === "ChainExpression" || node?.type === "ParenthesizedExpression" || node?.type === "TSAsExpression" || node?.type === "TSNonNullExpression" || node?.type === "TSSatisfiesExpression" || node?.type === "TSTypeAssertion";
|
|
3071
|
+
}
|
|
3072
|
+
function unwrapExpression2(node) {
|
|
3073
|
+
let current = node;
|
|
3074
|
+
while (isWrapperExpression(current))
|
|
3075
|
+
current = current.expression;
|
|
3076
|
+
return current;
|
|
3077
|
+
}
|
|
3078
|
+
function extendsLitBase(node, bases) {
|
|
3079
|
+
if (node.type === "Identifier")
|
|
3080
|
+
return bases.has(node.name);
|
|
3081
|
+
return node.type === "CallExpression" && node.arguments.some((argument) => extendsLitBase(argument, bases));
|
|
3082
|
+
}
|
|
3083
|
+
function isLitClass(node, bases) {
|
|
3084
|
+
const hasCustomElementDecorator = node.decorators.some((decorator) => decorator.expression.type === "CallExpression" && decorator.expression.callee.type === "Identifier" && decorator.expression.callee.name === "customElement");
|
|
3085
|
+
return hasCustomElementDecorator || node.superClass !== null && extendsLitBase(node.superClass, bases);
|
|
3086
|
+
}
|
|
3087
|
+
function classOfBody(body) {
|
|
3088
|
+
const owner = body.parent;
|
|
3089
|
+
return owner?.type === "ClassDeclaration" || owner?.type === "ClassExpression" ? owner : null;
|
|
3090
|
+
}
|
|
3091
|
+
function thisClass(node) {
|
|
3092
|
+
let current = node;
|
|
3093
|
+
while (current.parent) {
|
|
3094
|
+
const parent = current.parent;
|
|
3095
|
+
if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression") {
|
|
3096
|
+
const owner = parent.parent;
|
|
3097
|
+
return (owner?.type === "MethodDefinition" || owner?.type === "TSAbstractMethodDefinition") && !owner.static && owner.value === parent ? classOfBody(owner.parent) : null;
|
|
3098
|
+
}
|
|
3099
|
+
if ((parent.type === "PropertyDefinition" || parent.type === "AccessorProperty") && parent.value === current) {
|
|
3100
|
+
return parent.static ? null : classOfBody(parent.parent);
|
|
3101
|
+
}
|
|
3102
|
+
if (parent.type === "StaticBlock" || parent.type === "ClassBody" || parent.type === "Program") {
|
|
3103
|
+
return null;
|
|
3104
|
+
}
|
|
3105
|
+
current = parent;
|
|
2913
3106
|
}
|
|
2914
3107
|
return null;
|
|
2915
3108
|
}
|
|
2916
|
-
function
|
|
2917
|
-
|
|
2918
|
-
|
|
3109
|
+
function isStaticSelector(node) {
|
|
3110
|
+
return node?.type === "Literal" || node?.type === "TemplateLiteral" && node.expressions.length === 0;
|
|
3111
|
+
}
|
|
3112
|
+
function renderRootQuery(node, bases) {
|
|
3113
|
+
const method = staticMemberName(node.callee);
|
|
3114
|
+
if (method !== "querySelector" && method !== "querySelectorAll" || node.callee.type !== "MemberExpression" || !isStaticSelector(node.arguments[0])) {
|
|
2919
3115
|
return null;
|
|
2920
3116
|
}
|
|
2921
|
-
const
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2924
|
-
|
|
3117
|
+
const receiver = unwrapExpression2(node.callee.object);
|
|
3118
|
+
const root = staticMemberName(receiver);
|
|
3119
|
+
if (!root || !renderRoots.has(root) || receiver.type !== "MemberExpression" || receiver.object.type !== "ThisExpression") {
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
const owner = thisClass(receiver.object);
|
|
3123
|
+
return owner && isLitClass(owner, bases) ? { method, root } : null;
|
|
3124
|
+
}
|
|
3125
|
+
function isAssignedMethod(name) {
|
|
3126
|
+
return name === "assignedElements" || name === "assignedNodes";
|
|
3127
|
+
}
|
|
3128
|
+
function feedsAssignedCall(node) {
|
|
3129
|
+
let current = node;
|
|
3130
|
+
while (isWrapperExpression(current.parent))
|
|
3131
|
+
current = current.parent;
|
|
3132
|
+
const member = current.parent;
|
|
3133
|
+
return member?.type === "MemberExpression" && member.object === current && isAssignedMethod(staticMemberName(member));
|
|
3134
|
+
}
|
|
3135
|
+
function litQueryDecoratorCall(node, bases) {
|
|
3136
|
+
const method = staticMemberName(node.callee);
|
|
3137
|
+
if (!isAssignedMethod(method)) {
|
|
3138
|
+
return feedsAssignedCall(node) ? null : renderRootQuery(node, bases);
|
|
3139
|
+
}
|
|
3140
|
+
if (node.callee.type !== "MemberExpression")
|
|
3141
|
+
return null;
|
|
3142
|
+
const inner = unwrapExpression2(node.callee.object);
|
|
3143
|
+
if (inner.type !== "CallExpression")
|
|
3144
|
+
return null;
|
|
3145
|
+
const query = renderRootQuery(inner, bases);
|
|
3146
|
+
return query?.method === "querySelector" ? { method, root: query.root } : null;
|
|
3147
|
+
}
|
|
3148
|
+
function isLitRenderRootQuery(node, bases) {
|
|
3149
|
+
return renderRootQuery(node, bases) !== null;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
// src/generic/rules/no-unchecked-query-selector-type.ts
|
|
3153
|
+
var queryMethods = new Set(["closest", "querySelector", "querySelectorAll"]);
|
|
3154
|
+
var noUncheckedQuerySelectorTypeRule = defineRule26({
|
|
3155
|
+
meta: {
|
|
3156
|
+
type: "problem",
|
|
3157
|
+
docs: {
|
|
3158
|
+
description: "Disallow type arguments on `querySelector`, `querySelectorAll`, and `closest`, which cast the result without checking it."
|
|
3159
|
+
},
|
|
3160
|
+
messages: {
|
|
3161
|
+
checkQueryResult: "`{{method}}<T>` casts the result without checking it. Check it with `instanceof`, use a checked lookup helper, or query a tag name so TypeScript infers the element type."
|
|
3162
|
+
}
|
|
3163
|
+
},
|
|
3164
|
+
create(context) {
|
|
3165
|
+
const bases = litElementBaseClasses(context.settings);
|
|
3166
|
+
return {
|
|
3167
|
+
CallExpression(node) {
|
|
3168
|
+
const method = staticMemberName(node.callee);
|
|
3169
|
+
if (!node.typeArguments || !method || !queryMethods.has(method) || isLitRenderRootQuery(node, bases)) {
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
context.report({
|
|
3173
|
+
node: node.typeArguments,
|
|
3174
|
+
messageId: "checkQueryResult",
|
|
3175
|
+
data: { method }
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
});
|
|
3181
|
+
|
|
3182
|
+
// src/generic/rules/no-untyped-custom-event.ts
|
|
3183
|
+
import { defineRule as defineRule27 } from "@oxlint/plugins";
|
|
3184
|
+
var noUntypedCustomEventRule = defineRule27({
|
|
3185
|
+
meta: {
|
|
3186
|
+
type: "problem",
|
|
3187
|
+
docs: {
|
|
3188
|
+
description: "Disallow `CustomEvent` type references without a detail type, which leave `detail` as `any`."
|
|
3189
|
+
},
|
|
3190
|
+
messages: {
|
|
3191
|
+
typeCustomEventDetail: "`CustomEvent` without a type argument leaves `detail` as `any`. Reuse the project's typed event helper if one fits, or add one, instead of a bare `CustomEvent`."
|
|
3192
|
+
}
|
|
3193
|
+
},
|
|
3194
|
+
create(context) {
|
|
3195
|
+
return {
|
|
3196
|
+
TSTypeReference(node) {
|
|
3197
|
+
if (node.typeName.type !== "Identifier" || node.typeName.name !== "CustomEvent" || node.typeArguments) {
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
context.report({ node, messageId: "typeCustomEventDetail" });
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3204
|
+
});
|
|
3205
|
+
|
|
3206
|
+
// src/generic/rules/prefer-event-parameter-type.ts
|
|
3207
|
+
import { defineRule as defineRule28 } from "@oxlint/plugins";
|
|
3208
|
+
function staticMemberPath(node) {
|
|
3209
|
+
const path = [];
|
|
3210
|
+
let current = node.type === "ChainExpression" ? node.expression : node;
|
|
3211
|
+
while (current.type === "MemberExpression") {
|
|
3212
|
+
if (current.computed || current.property.type !== "Identifier") {
|
|
3213
|
+
return null;
|
|
3214
|
+
}
|
|
3215
|
+
path.unshift(current.property.name);
|
|
3216
|
+
current = current.object;
|
|
3217
|
+
}
|
|
3218
|
+
return current.type === "Identifier" && path.length > 0 ? { root: current.name, path } : null;
|
|
3219
|
+
}
|
|
3220
|
+
function isEventMemberPath(path) {
|
|
3221
|
+
const [first] = path;
|
|
3222
|
+
if (first === "detail")
|
|
3223
|
+
return true;
|
|
3224
|
+
return path.length === 1 && (first === "currentTarget" || first === "target");
|
|
3225
|
+
}
|
|
3226
|
+
function assertedEventParameter(node) {
|
|
3227
|
+
const member = staticMemberPath(node.expression);
|
|
3228
|
+
if (!member || !isEventMemberPath(member.path))
|
|
3229
|
+
return null;
|
|
3230
|
+
const owner = nearestEnclosingFunction(node);
|
|
3231
|
+
if (!owner?.params.some((parameter) => parameter.type === "Identifier" && parameter.name === member.root)) {
|
|
2925
3232
|
return null;
|
|
2926
3233
|
}
|
|
2927
3234
|
return {
|
|
2928
|
-
parameter:
|
|
2929
|
-
property
|
|
3235
|
+
parameter: member.root,
|
|
3236
|
+
property: member.path.join(".")
|
|
2930
3237
|
};
|
|
2931
3238
|
}
|
|
2932
|
-
var preferEventParameterTypeRule =
|
|
3239
|
+
var preferEventParameterTypeRule = defineRule28({
|
|
2933
3240
|
meta: {
|
|
2934
3241
|
type: "suggestion",
|
|
2935
3242
|
docs: {
|
|
2936
|
-
description: "Prefer typing event target properties in the handler parameter instead of asserting them at use sites."
|
|
3243
|
+
description: "Prefer typing event target and detail properties in the handler parameter instead of asserting them at use sites."
|
|
2937
3244
|
},
|
|
2938
3245
|
messages: {
|
|
2939
|
-
typeEventParameter: "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site."
|
|
3246
|
+
typeEventParameter: "Type `{{parameter}}.{{property}}` in the function signature instead of asserting it at the use site. Reuse the project's typed event helper if one fits, or add one."
|
|
2940
3247
|
}
|
|
2941
3248
|
},
|
|
2942
3249
|
create(context) {
|
|
@@ -2957,9 +3264,69 @@ var preferEventParameterTypeRule = defineRule24({
|
|
|
2957
3264
|
}
|
|
2958
3265
|
});
|
|
2959
3266
|
|
|
3267
|
+
// src/generic/rules/prefer-lit-query-decorators.ts
|
|
3268
|
+
import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
3269
|
+
var messageIds = {
|
|
3270
|
+
assignedElements: "preferQueryAssignedElements",
|
|
3271
|
+
assignedNodes: "preferQueryAssignedNodes",
|
|
3272
|
+
querySelector: "preferQuery",
|
|
3273
|
+
querySelectorAll: "preferQueryAll"
|
|
3274
|
+
};
|
|
3275
|
+
var preferLitQueryDecoratorsRule = defineRule29({
|
|
3276
|
+
meta: {
|
|
3277
|
+
type: "suggestion",
|
|
3278
|
+
docs: {
|
|
3279
|
+
description: "Require Lit query decorators instead of static render-root queries in Lit elements."
|
|
3280
|
+
},
|
|
3281
|
+
schema: [
|
|
3282
|
+
{
|
|
3283
|
+
type: "object",
|
|
3284
|
+
properties: {
|
|
3285
|
+
querySelector: { type: "boolean" },
|
|
3286
|
+
querySelectorAll: { type: "boolean" },
|
|
3287
|
+
assignedElements: { type: "boolean" },
|
|
3288
|
+
assignedNodes: { type: "boolean" }
|
|
3289
|
+
},
|
|
3290
|
+
additionalProperties: false
|
|
3291
|
+
}
|
|
3292
|
+
],
|
|
3293
|
+
defaultOptions: [
|
|
3294
|
+
{
|
|
3295
|
+
querySelector: true,
|
|
3296
|
+
querySelectorAll: true,
|
|
3297
|
+
assignedElements: true,
|
|
3298
|
+
assignedNodes: true
|
|
3299
|
+
}
|
|
3300
|
+
],
|
|
3301
|
+
messages: {
|
|
3302
|
+
preferQuery: "Use the @query decorator instead of this.{{root}}.querySelector().",
|
|
3303
|
+
preferQueryAll: "Use the @queryAll decorator instead of this.{{root}}.querySelectorAll().",
|
|
3304
|
+
preferQueryAssignedElements: "Use the @queryAssignedElements decorator instead of this.{{root}}.querySelector().assignedElements().",
|
|
3305
|
+
preferQueryAssignedNodes: "Use the @queryAssignedNodes decorator instead of this.{{root}}.querySelector().assignedNodes()."
|
|
3306
|
+
}
|
|
3307
|
+
},
|
|
3308
|
+
create(context) {
|
|
3309
|
+
const bases = litElementBaseClasses(context.settings);
|
|
3310
|
+
const options = jsonObject(context.options[0]);
|
|
3311
|
+
const disabled = new Set(options ? Object.keys(options).filter((method) => options[method] === false) : []);
|
|
3312
|
+
return {
|
|
3313
|
+
CallExpression(node) {
|
|
3314
|
+
const query = litQueryDecoratorCall(node, bases);
|
|
3315
|
+
if (!query || disabled.has(query.method))
|
|
3316
|
+
return;
|
|
3317
|
+
context.report({
|
|
3318
|
+
node,
|
|
3319
|
+
messageId: messageIds[query.method],
|
|
3320
|
+
data: { root: query.root }
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
});
|
|
3326
|
+
|
|
2960
3327
|
// src/generic/rules/prefer-types-over-assertions.ts
|
|
2961
|
-
import { defineRule as
|
|
2962
|
-
var preferTypesOverAssertionsRule =
|
|
3328
|
+
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3329
|
+
var preferTypesOverAssertionsRule = defineRule30({
|
|
2963
3330
|
meta: {
|
|
2964
3331
|
type: "problem",
|
|
2965
3332
|
docs: {
|
|
@@ -2971,7 +3338,7 @@ var preferTypesOverAssertionsRule = defineRule25({
|
|
|
2971
3338
|
},
|
|
2972
3339
|
create(context) {
|
|
2973
3340
|
const checkAssertion = (node) => {
|
|
2974
|
-
if (node.typeAnnotation.type === "TSTypeReference" && node.typeAnnotation.typeName.type === "Identifier" && node.typeAnnotation.typeName.name === "const") {
|
|
3341
|
+
if (node.typeAnnotation.type === "TSTypeReference" && node.typeAnnotation.typeName.type === "Identifier" && node.typeAnnotation.typeName.name === "const" || assertedEventParameter(node)) {
|
|
2975
3342
|
return;
|
|
2976
3343
|
}
|
|
2977
3344
|
context.report({ node, messageId: "avoidAssertion" });
|
|
@@ -2987,7 +3354,11 @@ var preferTypesOverAssertionsRule = defineRule25({
|
|
|
2987
3354
|
var timmoPlugin = eslintCompatPlugin4({
|
|
2988
3355
|
meta: { name: "timmo" },
|
|
2989
3356
|
rules: {
|
|
3357
|
+
"no-explicit-inferable-type-predicate": noExplicitInferableTypePredicateRule,
|
|
3358
|
+
"no-unchecked-query-selector-type": noUncheckedQuerySelectorTypeRule,
|
|
3359
|
+
"no-untyped-custom-event": noUntypedCustomEventRule,
|
|
2990
3360
|
"prefer-event-parameter-type": preferEventParameterTypeRule,
|
|
3361
|
+
"prefer-lit-query-decorators": preferLitQueryDecoratorsRule,
|
|
2991
3362
|
"prefer-types-over-assertions": preferTypesOverAssertionsRule
|
|
2992
3363
|
}
|
|
2993
3364
|
});
|
|
@@ -3005,7 +3376,11 @@ var recommended = defineConfig({
|
|
|
3005
3376
|
rules: {
|
|
3006
3377
|
...enablePluginRules("anti-slop", src_default),
|
|
3007
3378
|
...enablePluginRules("timmo", generic_default),
|
|
3008
|
-
"anti-slop/require-safety-comment-for-type-assertion": "off"
|
|
3379
|
+
"anti-slop/require-safety-comment-for-type-assertion": "off",
|
|
3380
|
+
"typescript/no-non-null-assertion": "error",
|
|
3381
|
+
"typescript/no-unnecessary-type-arguments": "error",
|
|
3382
|
+
"typescript/no-unnecessary-type-assertion": "error",
|
|
3383
|
+
"typescript/no-unnecessary-type-parameters": "error"
|
|
3009
3384
|
}
|
|
3010
3385
|
});
|
|
3011
3386
|
var recommended_default = recommended;
|