@timmo001/oxlint-rules 0.3.0 → 0.3.2
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 +2 -1
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/cli.js +978 -83
- package/dist/configs/recommended-effect.js +1207 -312
- package/dist/configs/recommended.js +772 -70
- package/dist/upstream/anti-slop.js +765 -63
- package/dist/upstream/effect.js +196 -3
- package/package.json +5 -5
- package/vendor/anti-slop/src/effect/index.ts +8 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts +52 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts +45 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts +37 -0
- package/vendor/anti-slop/src/effect/rules/prefer-effect-match.ts +54 -0
- package/vendor/anti-slop/src/effect/shared/tagged-values.ts +97 -0
- package/vendor/anti-slop/src/index.ts +6 -0
- package/vendor/anti-slop/src/rules/no-array-filter-map.ts +28 -0
- package/vendor/anti-slop/src/rules/no-known-value-widening.ts +2 -14
- package/vendor/anti-slop/src/rules/no-module-mocking.ts +3 -14
- package/vendor/anti-slop/src/rules/no-reduce-accumulator-copy.ts +109 -0
- package/vendor/anti-slop/src/rules/require-readable-spacing.ts +47 -0
- package/vendor/anti-slop/src/shared/array-method.ts +94 -0
- package/vendor/anti-slop/src/shared/reflect-method.ts +2 -13
- package/vendor/anti-slop/src/shared/scope.ts +15 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/LICENSE +22 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/UPSTREAM.md +28 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts +51 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts +906 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-options.d.ts +87 -0
|
@@ -1,8 +1,566 @@
|
|
|
1
|
-
//
|
|
1
|
+
// node_modules/oxlint/dist/index.js
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return config;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/effect/index.ts
|
|
2
7
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
3
8
|
|
|
9
|
+
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
10
|
+
import { defineRule } from "@oxlint/plugins";
|
|
11
|
+
function resolveVariable(sourceCode, identifier) {
|
|
12
|
+
let scope = sourceCode.getScope(identifier);
|
|
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
|
+
}
|
|
21
|
+
function nearestEnclosingFunction(node) {
|
|
22
|
+
let current = node.parent;
|
|
23
|
+
while (current) {
|
|
24
|
+
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
current = current.parent;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function staticMemberName(node) {
|
|
32
|
+
if (node.type !== "MemberExpression" || node.computed)
|
|
33
|
+
return null;
|
|
34
|
+
return node.property.type === "Identifier" ? node.property.name : null;
|
|
35
|
+
}
|
|
36
|
+
function isEffectImport(sourceCode, identifier, namespace) {
|
|
37
|
+
const variable = resolveVariable(sourceCode, identifier);
|
|
38
|
+
return variable?.defs.some((definition) => {
|
|
39
|
+
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== "effect") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (namespace)
|
|
43
|
+
return definition.node.type === "ImportNamespaceSpecifier";
|
|
44
|
+
if (definition.node.type !== "ImportSpecifier")
|
|
45
|
+
return false;
|
|
46
|
+
const imported = definition.node.imported;
|
|
47
|
+
return (imported.type === "Identifier" ? imported.name : imported.value) === "Effect";
|
|
48
|
+
}) ?? false;
|
|
49
|
+
}
|
|
50
|
+
function isEffectMethod(sourceCode, node, method) {
|
|
51
|
+
if (staticMemberName(node) !== method || node.type !== "MemberExpression") {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const object = node.object;
|
|
55
|
+
if (object.type === "Identifier") {
|
|
56
|
+
return isEffectImport(sourceCode, object, false);
|
|
57
|
+
}
|
|
58
|
+
if (staticMemberName(object) !== "Effect" || object.type !== "MemberExpression" || object.object.type !== "Identifier") {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return isEffectImport(sourceCode, object.object, true);
|
|
62
|
+
}
|
|
63
|
+
function isDirectArgument(owner, call) {
|
|
64
|
+
return call.arguments.some((argument) => argument === owner);
|
|
65
|
+
}
|
|
66
|
+
function isRecognisedEffectGenerator(sourceCode, owner) {
|
|
67
|
+
const parent = owner.parent;
|
|
68
|
+
if (parent.type !== "CallExpression" || !isDirectArgument(owner, parent)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (isEffectMethod(sourceCode, parent.callee, "gen"))
|
|
72
|
+
return true;
|
|
73
|
+
const factoryCall = parent.callee;
|
|
74
|
+
return factoryCall.type === "CallExpression" && isEffectMethod(sourceCode, factoryCall.callee, "fn");
|
|
75
|
+
}
|
|
76
|
+
var noTryCatchInEffectGeneratorsRule = defineRule({
|
|
77
|
+
meta: {
|
|
78
|
+
type: "problem",
|
|
79
|
+
docs: {
|
|
80
|
+
description: "Disallow synchronous try/catch owned by recognised Effect generator callbacks."
|
|
81
|
+
},
|
|
82
|
+
messages: {
|
|
83
|
+
useEffectErrorChannel: "Keep expected failures in the Effect error channel. Use Effect.try for synchronous throwing work, Effect.tryPromise for asynchronous throwing work, Effect-returning schema APIs for decoding, and Effect recovery combinators for recovery."
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
create(context) {
|
|
87
|
+
return {
|
|
88
|
+
TryStatement(node) {
|
|
89
|
+
if (!node.handler)
|
|
90
|
+
return;
|
|
91
|
+
const owner = nearestEnclosingFunction(node);
|
|
92
|
+
if (!owner?.generator || !isRecognisedEffectGenerator(context.sourceCode, owner)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
context.report({ node, messageId: "useEffectErrorChannel" });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/effect/index.ts
|
|
102
|
+
var timmoEffectPlugin = eslintCompatPlugin({
|
|
103
|
+
meta: { name: "timmo-effect" },
|
|
104
|
+
rules: {
|
|
105
|
+
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
var effect_default = timmoEffectPlugin;
|
|
109
|
+
|
|
110
|
+
// vendor/anti-slop/src/effect/index.ts
|
|
111
|
+
import { eslintCompatPlugin as eslintCompatPlugin2 } from "@oxlint/plugins";
|
|
112
|
+
|
|
113
|
+
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
114
|
+
import { defineRule as defineRule2 } from "@oxlint/plugins";
|
|
115
|
+
|
|
116
|
+
// vendor/anti-slop/src/effect/shared/tagged-values.ts
|
|
117
|
+
var equalityOperators = new Set(["==", "===", "!=", "!=="]);
|
|
118
|
+
var broadEffectCatchMethods = new Set(["catch", "catchAll", "catchIf"]);
|
|
119
|
+
var isStringLiteral = (node) => node?.type === "Literal" && typeof node.value === "string";
|
|
120
|
+
var isTagMember = (node) => node?.type === "MemberExpression" && (!node.computed && node.property.type === "Identifier" && node.property.name === "_tag" || node.computed && isStringLiteral(node.property) && node.property.value === "_tag");
|
|
121
|
+
var tagMemberFromComparison = (node) => {
|
|
122
|
+
if (!equalityOperators.has(node.operator))
|
|
123
|
+
return;
|
|
124
|
+
if (isTagMember(node.left) && isStringLiteral(node.right))
|
|
125
|
+
return node.left;
|
|
126
|
+
if (isTagMember(node.right) && isStringLiteral(node.left))
|
|
127
|
+
return node.right;
|
|
128
|
+
return;
|
|
129
|
+
};
|
|
130
|
+
var isBroadEffectCatchCall = (node) => node?.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Effect" && !node.callee.computed && node.callee.property.type === "Identifier" && broadEffectCatchMethods.has(node.callee.property.name);
|
|
131
|
+
var isInsideBroadEffectHandler = (node) => {
|
|
132
|
+
let current = node.parent;
|
|
133
|
+
while (current !== null && current !== undefined) {
|
|
134
|
+
if (current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") {
|
|
135
|
+
return isBroadEffectCatchCall(current.parent) && current.parent.arguments.includes(current);
|
|
136
|
+
}
|
|
137
|
+
current = current.parent;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
};
|
|
141
|
+
var isReasonTagMember = (node) => node.object.type === "MemberExpression" && (!node.object.computed && node.object.property.type === "Identifier" && node.object.property.name === "reason" || node.object.computed && isStringLiteral(node.object.property) && node.object.property.value === "reason");
|
|
142
|
+
var propertyName = (property) => {
|
|
143
|
+
if (!property.computed && property.key.type === "Identifier") {
|
|
144
|
+
return property.key.name;
|
|
145
|
+
}
|
|
146
|
+
if (property.key.type === "Literal" && typeof property.key.value === "string") {
|
|
147
|
+
return property.key.value;
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
};
|
|
151
|
+
var isMatchPatternObject = (node) => {
|
|
152
|
+
const call = node.parent;
|
|
153
|
+
if (call?.type !== "CallExpression" || !call.arguments.includes(node)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const callee = call.callee;
|
|
157
|
+
return callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "Match" && !callee.computed && callee.property.type === "Identifier" && (callee.property.name === "when" || callee.property.name === "not");
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
|
|
161
|
+
var noManualEffectErrorTagRule = defineRule2({
|
|
162
|
+
meta: {
|
|
163
|
+
type: "problem",
|
|
164
|
+
docs: {
|
|
165
|
+
description: "Use Effect tagged error handlers instead of manually branching on `_tag` in a catch handler."
|
|
166
|
+
},
|
|
167
|
+
messages: {
|
|
168
|
+
tag: "Use Effect.catchTag or Effect.catchTags instead of manually discriminating a tagged error in a broad Effect catch handler.",
|
|
169
|
+
reason: "Use Effect.catchReason or Effect.catchReasons instead of manually discriminating a tagged `reason` in a broad Effect catch handler."
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
createOnce(context) {
|
|
173
|
+
return {
|
|
174
|
+
BinaryExpression(node) {
|
|
175
|
+
const tagMember = tagMemberFromComparison(node);
|
|
176
|
+
if (tagMember === undefined || !isInsideBroadEffectHandler(node)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
context.report({
|
|
180
|
+
node,
|
|
181
|
+
messageId: isReasonTagMember(tagMember) ? "reason" : "tag"
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
SwitchStatement(node) {
|
|
185
|
+
if (!isTagMember(node.discriminant) || !isInsideBroadEffectHandler(node)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
context.report({
|
|
189
|
+
node,
|
|
190
|
+
messageId: isReasonTagMember(node.discriminant) ? "reason" : "tag"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts
|
|
198
|
+
import { defineRule as defineRule3 } from "@oxlint/plugins";
|
|
199
|
+
var noManualTagComparisonRule = defineRule3({
|
|
200
|
+
meta: {
|
|
201
|
+
type: "problem",
|
|
202
|
+
docs: {
|
|
203
|
+
description: "Use Effect Match or Predicate helpers instead of manually branching on `_tag`."
|
|
204
|
+
},
|
|
205
|
+
messages: {
|
|
206
|
+
manualComparison: "Use Match.tag/Match.tags for tagged-value branching, or Predicate.isTagged for a simple reusable predicate.",
|
|
207
|
+
manualSwitch: "Use Match.value(value).pipe(Match.tag/Match.tags/Match.tagsExhaustive) or the tagged enum `$match` helper instead of switching on `_tag`."
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
createOnce(context) {
|
|
211
|
+
return {
|
|
212
|
+
BinaryExpression(node) {
|
|
213
|
+
if (tagMemberFromComparison(node) === undefined || isInsideBroadEffectHandler(node)) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
context.report({ node, messageId: "manualComparison" });
|
|
217
|
+
},
|
|
218
|
+
SwitchStatement(node) {
|
|
219
|
+
if (!isTagMember(node.discriminant) || isInsideBroadEffectHandler(node)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
context.report({ node, messageId: "manualSwitch" });
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts
|
|
229
|
+
import { defineRule as defineRule4 } from "@oxlint/plugins";
|
|
230
|
+
var noManualTaggedConstructionRule = defineRule4({
|
|
231
|
+
meta: {
|
|
232
|
+
type: "problem",
|
|
233
|
+
docs: {
|
|
234
|
+
description: "Construct tagged values with their existing Effect constructor instead of writing `_tag` manually."
|
|
235
|
+
},
|
|
236
|
+
messages: {
|
|
237
|
+
manualConstruction: "Use the existing Schema tagged `.make`, tagged class/error constructor, or Data.taggedEnum variant constructor instead of writing a literal `_tag` object."
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
createOnce(context) {
|
|
241
|
+
return {
|
|
242
|
+
ObjectExpression(node) {
|
|
243
|
+
if (isMatchPatternObject(node))
|
|
244
|
+
return;
|
|
245
|
+
const tag = node.properties.find((property) => property.type === "Property" && propertyName(property) === "_tag" && isStringLiteral(property.value));
|
|
246
|
+
if (tag !== undefined) {
|
|
247
|
+
context.report({ node: tag, messageId: "manualConstruction" });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
|
|
255
|
+
import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
256
|
+
var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
|
|
257
|
+
var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
258
|
+
function isProjectLocalImport(source) {
|
|
259
|
+
return source.startsWith("./") || source.startsWith("../");
|
|
260
|
+
}
|
|
261
|
+
function getImportedName(specifier) {
|
|
262
|
+
if (specifier.imported.type === "Identifier")
|
|
263
|
+
return specifier.imported.name;
|
|
264
|
+
return specifier.imported.value;
|
|
265
|
+
}
|
|
266
|
+
var noServiceConstructorImportsRule = defineRule5({
|
|
267
|
+
meta: {
|
|
268
|
+
type: "problem",
|
|
269
|
+
docs: {
|
|
270
|
+
description: "Disallow project-local make<CapabilityName> imports outside test and spec files."
|
|
271
|
+
},
|
|
272
|
+
messages: {
|
|
273
|
+
serviceConstructorImport: 'Do not import Effect service constructor "{{name}}" into runtime code. Import the owning Layer, yield the contextual service, and allow its requirements to propagate to the composition root.'
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
create(context) {
|
|
277
|
+
const isTestFile = TEST_FILE.test(context.filename.replaceAll("\\", "/"));
|
|
278
|
+
return {
|
|
279
|
+
ImportDeclaration(node) {
|
|
280
|
+
if (isTestFile || !isProjectLocalImport(node.source.value))
|
|
281
|
+
return;
|
|
282
|
+
for (const specifier of node.specifiers) {
|
|
283
|
+
if (specifier.type !== "ImportSpecifier")
|
|
284
|
+
continue;
|
|
285
|
+
const importedName = getImportedName(specifier);
|
|
286
|
+
if (!SERVICE_CONSTRUCTOR_NAME.test(importedName))
|
|
287
|
+
continue;
|
|
288
|
+
context.report({
|
|
289
|
+
node: specifier,
|
|
290
|
+
messageId: "serviceConstructorImport",
|
|
291
|
+
data: { name: importedName }
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// vendor/anti-slop/src/effect/rules/prefer-effect-match.ts
|
|
300
|
+
import { defineRule as defineRule6 } from "@oxlint/plugins";
|
|
301
|
+
var equalityOperators2 = new Set(["==", "===", "!=", "!=="]);
|
|
302
|
+
var preferEffectMatchRule = defineRule6({
|
|
303
|
+
meta: {
|
|
304
|
+
type: "problem",
|
|
305
|
+
docs: {
|
|
306
|
+
description: "Use Match from Effect for chained literal ternaries over the same value."
|
|
307
|
+
},
|
|
308
|
+
messages: {
|
|
309
|
+
preferMatch: "Use Match from Effect instead of a chained literal ternary."
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
createOnce(context) {
|
|
313
|
+
const isLiteral = (node) => node.type === "Literal" || node.type === "TemplateLiteral" && node.expressions.length === 0;
|
|
314
|
+
const comparedValue = (node) => {
|
|
315
|
+
if (node.type !== "BinaryExpression" || !equalityOperators2.has(node.operator)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (isLiteral(node.left))
|
|
319
|
+
return context.sourceCode.getText(node.right);
|
|
320
|
+
if (isLiteral(node.right))
|
|
321
|
+
return context.sourceCode.getText(node.left);
|
|
322
|
+
return;
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
ConditionalExpression(node) {
|
|
326
|
+
if (node.parent?.type === "ConditionalExpression")
|
|
327
|
+
return;
|
|
328
|
+
const value = comparedValue(node.test);
|
|
329
|
+
if (value === undefined)
|
|
330
|
+
return;
|
|
331
|
+
let alternate = node.alternate;
|
|
332
|
+
let literalChecks = 1;
|
|
333
|
+
while (alternate.type === "ConditionalExpression") {
|
|
334
|
+
if (comparedValue(alternate.test) !== value)
|
|
335
|
+
return;
|
|
336
|
+
literalChecks += 1;
|
|
337
|
+
alternate = alternate.alternate;
|
|
338
|
+
}
|
|
339
|
+
if (literalChecks > 1) {
|
|
340
|
+
context.report({ node, messageId: "preferMatch" });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// vendor/anti-slop/src/effect/index.ts
|
|
348
|
+
var antiSlopEffectPlugin = eslintCompatPlugin2({
|
|
349
|
+
meta: { name: "anti-slop-effect" },
|
|
350
|
+
rules: {
|
|
351
|
+
"no-manual-effect-error-tag": noManualEffectErrorTagRule,
|
|
352
|
+
"no-manual-tag-comparison": noManualTagComparisonRule,
|
|
353
|
+
"no-manual-tagged-construction": noManualTaggedConstructionRule,
|
|
354
|
+
"no-service-constructor-imports": noServiceConstructorImportsRule,
|
|
355
|
+
"prefer-effect-match": preferEffectMatchRule
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
var effect_default2 = antiSlopEffectPlugin;
|
|
359
|
+
// src/configs/enable-plugin-rules.ts
|
|
360
|
+
function enablePluginRules(namespace, plugin) {
|
|
361
|
+
return Object.fromEntries(Object.keys(plugin.rules).sort().map((rule) => [`${namespace}/${rule}`, "error"]));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// vendor/anti-slop/src/index.ts
|
|
365
|
+
import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
|
|
366
|
+
|
|
367
|
+
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
368
|
+
import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
369
|
+
|
|
370
|
+
// vendor/anti-slop/src/shared/array-method.ts
|
|
371
|
+
function unwrapArrayExpression(node) {
|
|
372
|
+
while (node.type === "ParenthesizedExpression" || node.type === "ChainExpression" || node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression" || node.type === "TSSatisfiesExpression") {
|
|
373
|
+
node = node.expression;
|
|
374
|
+
}
|
|
375
|
+
return node;
|
|
376
|
+
}
|
|
377
|
+
function resolveArrayBinding(sourceCode, node) {
|
|
378
|
+
node = unwrapArrayExpression(node);
|
|
379
|
+
if (node.type !== "Identifier")
|
|
380
|
+
return null;
|
|
381
|
+
let scope = sourceCode.getScope(node);
|
|
382
|
+
while (scope !== null) {
|
|
383
|
+
const variable = scope.set.get(node.name);
|
|
384
|
+
if (variable !== undefined)
|
|
385
|
+
return variable;
|
|
386
|
+
scope = scope.upper;
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
function arrayMethodTarget(node) {
|
|
391
|
+
node = unwrapArrayExpression(node);
|
|
392
|
+
if (node.type !== "MemberExpression")
|
|
393
|
+
return null;
|
|
394
|
+
const property = node.property;
|
|
395
|
+
if (!node.computed && property.type === "Identifier") {
|
|
396
|
+
return { name: property.name, object: node.object };
|
|
397
|
+
}
|
|
398
|
+
if (node.computed && property.type === "Literal" && typeof property.value === "string") {
|
|
399
|
+
return { name: property.value, object: node.object };
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
function isArrayAnnotation(type) {
|
|
404
|
+
if (type.type === "TSArrayType" || type.type === "TSTupleType")
|
|
405
|
+
return true;
|
|
406
|
+
if (type.type === "TSParenthesizedType")
|
|
407
|
+
return isArrayAnnotation(type.typeAnnotation);
|
|
408
|
+
if (type.type === "TSTypeOperator" && type.operator === "readonly") {
|
|
409
|
+
return isArrayAnnotation(type.typeAnnotation);
|
|
410
|
+
}
|
|
411
|
+
return type.type === "TSTypeReference" && type.typeName.type === "Identifier" && (type.typeName.name === "Array" || type.typeName.name === "ReadonlyArray");
|
|
412
|
+
}
|
|
413
|
+
function isKnownArrayExpression(sourceCode, node, visited = new Set) {
|
|
414
|
+
node = unwrapArrayExpression(node);
|
|
415
|
+
if (node.type === "ArrayExpression")
|
|
416
|
+
return true;
|
|
417
|
+
if (node.type === "CallExpression") {
|
|
418
|
+
const method = arrayMethodTarget(node.callee);
|
|
419
|
+
return method !== null && ["map", "filter", "flatMap", "slice", "concat", "toSorted", "toReversed", "toSpliced"].includes(method.name) && isKnownArrayExpression(sourceCode, method.object, visited);
|
|
420
|
+
}
|
|
421
|
+
if (node.type !== "Identifier")
|
|
422
|
+
return false;
|
|
423
|
+
const variable = resolveArrayBinding(sourceCode, node);
|
|
424
|
+
if (variable === null || visited.has(variable))
|
|
425
|
+
return false;
|
|
426
|
+
visited.add(variable);
|
|
427
|
+
if (variable.references.some((reference) => reference.isWrite() && !reference.init))
|
|
428
|
+
return false;
|
|
429
|
+
for (const identifier of variable.identifiers) {
|
|
430
|
+
const annotation = identifier.typeAnnotation?.typeAnnotation;
|
|
431
|
+
if (annotation !== undefined)
|
|
432
|
+
return isArrayAnnotation(annotation);
|
|
433
|
+
}
|
|
434
|
+
for (const definition of variable.defs) {
|
|
435
|
+
if (definition.type === "Variable" && definition.node.type === "VariableDeclarator" && definition.node.id.type === "Identifier" && definition.node.init !== null && definition.node.parent.type === "VariableDeclaration" && definition.node.parent.kind === "const") {
|
|
436
|
+
return isKnownArrayExpression(sourceCode, definition.node.init, visited);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// vendor/anti-slop/src/rules/no-array-filter-map.ts
|
|
443
|
+
var noArrayFilterMapRule = defineRule7({
|
|
444
|
+
meta: {
|
|
445
|
+
type: "suggestion",
|
|
446
|
+
docs: { description: "Disallow adjacent array filter/map passes in favor of lazy iterator helpers or a single transformation." },
|
|
447
|
+
messages: {
|
|
448
|
+
arrayFilterMap: "Avoid consecutive array `{{first}}` and `{{second}}` passes. Prefer `.values().{{first}}(...).{{second}}(...).toArray()` where iterator helpers are supported, or a single `flatMap`/mutating reducer. Preserve callback ordering, indexes, and filtering semantics."
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
createOnce(context) {
|
|
452
|
+
return {
|
|
453
|
+
CallExpression(node) {
|
|
454
|
+
const outer = arrayMethodTarget(node.callee);
|
|
455
|
+
if (outer === null || outer.name !== "map" && outer.name !== "filter")
|
|
456
|
+
return;
|
|
457
|
+
const innerCall = unwrapArrayExpression(outer.object);
|
|
458
|
+
if (innerCall.type !== "CallExpression")
|
|
459
|
+
return;
|
|
460
|
+
const inner = arrayMethodTarget(innerCall.callee);
|
|
461
|
+
if (inner === null || inner.name !== (outer.name === "map" ? "filter" : "map"))
|
|
462
|
+
return;
|
|
463
|
+
if (!isKnownArrayExpression(context.sourceCode, inner.object))
|
|
464
|
+
return;
|
|
465
|
+
context.report({ node, messageId: "arrayFilterMap", data: { first: inner.name, second: outer.name } });
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// vendor/anti-slop/src/rules/no-reduce-accumulator-copy.ts
|
|
472
|
+
import { defineRule as defineRule8 } from "@oxlint/plugins";
|
|
473
|
+
function enclosingReducer(node) {
|
|
474
|
+
let parent = node.parent;
|
|
475
|
+
while (parent !== null) {
|
|
476
|
+
if (parent.type === "FunctionDeclaration")
|
|
477
|
+
return null;
|
|
478
|
+
if (parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression") {
|
|
479
|
+
const callback = parent;
|
|
480
|
+
let owner = callback.parent;
|
|
481
|
+
while (owner !== null && unwrapArrayExpression(owner) === callback)
|
|
482
|
+
owner = owner.parent;
|
|
483
|
+
if (owner?.type !== "CallExpression")
|
|
484
|
+
return null;
|
|
485
|
+
const method = arrayMethodTarget(owner.callee);
|
|
486
|
+
const firstArgument = owner.arguments[0];
|
|
487
|
+
if (method === null || method.name !== "reduce" && method.name !== "reduceRight" || owner.arguments.length > 2 || firstArgument === undefined || unwrapArrayExpression(firstArgument) !== callback)
|
|
488
|
+
return null;
|
|
489
|
+
const firstParameter = callback.params[0];
|
|
490
|
+
const accumulator = firstParameter?.type === "AssignmentPattern" ? firstParameter.left : firstParameter;
|
|
491
|
+
if (accumulator?.type !== "Identifier")
|
|
492
|
+
return null;
|
|
493
|
+
return { callback, accumulator, initialValue: owner.arguments[1] };
|
|
494
|
+
}
|
|
495
|
+
parent = parent.parent;
|
|
496
|
+
}
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
function referencesAccumulator(sourceCode, node, accumulator, visited = new Set) {
|
|
500
|
+
const variable = resolveArrayBinding(sourceCode, node);
|
|
501
|
+
if (variable === null || visited.has(variable))
|
|
502
|
+
return false;
|
|
503
|
+
if (variable === accumulator)
|
|
504
|
+
return true;
|
|
505
|
+
visited.add(variable);
|
|
506
|
+
if (variable.references.some((reference) => reference.isWrite() && !reference.init))
|
|
507
|
+
return false;
|
|
508
|
+
for (const definition of variable.defs) {
|
|
509
|
+
if (definition.type === "Variable" && definition.node.type === "VariableDeclarator" && definition.node.id.type === "Identifier" && definition.node.init !== null && definition.node.parent.type === "VariableDeclaration" && definition.node.parent.kind === "const") {
|
|
510
|
+
return referencesAccumulator(sourceCode, definition.node.init, accumulator, visited);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
function isGlobalCopyOwner(sourceCode, node, name) {
|
|
516
|
+
node = unwrapArrayExpression(node);
|
|
517
|
+
if (node.type !== "Identifier" || node.name !== name)
|
|
518
|
+
return false;
|
|
519
|
+
const variable = resolveArrayBinding(sourceCode, node);
|
|
520
|
+
return variable === null || variable.defs.length === 0;
|
|
521
|
+
}
|
|
522
|
+
var noReduceAccumulatorCopyRule = defineRule8({
|
|
523
|
+
meta: {
|
|
524
|
+
type: "problem",
|
|
525
|
+
docs: { description: "Disallow copying growing reducer accumulators with Object.assign, Array.from, or array copy methods." },
|
|
526
|
+
messages: {
|
|
527
|
+
accumulatorCopy: "Do not copy the reducer accumulator on every iteration; growing copies can cause quadratic work. Mutate a fresh, locally owned accumulator and return it, or use an iterator pipeline/flatMap."
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
createOnce(context) {
|
|
531
|
+
return {
|
|
532
|
+
CallExpression(node) {
|
|
533
|
+
const method = arrayMethodTarget(node.callee);
|
|
534
|
+
if (method === null)
|
|
535
|
+
return;
|
|
536
|
+
const reducer = enclosingReducer(node);
|
|
537
|
+
if (reducer === null)
|
|
538
|
+
return;
|
|
539
|
+
const accumulator = context.sourceCode.getDeclaredVariables(reducer.callback).find((variable) => variable.identifiers.some((identifier) => identifier.start === reducer.accumulator.start));
|
|
540
|
+
if (accumulator === undefined)
|
|
541
|
+
return;
|
|
542
|
+
const isAccumulator = (expression) => referencesAccumulator(context.sourceCode, expression, accumulator);
|
|
543
|
+
let copiesAccumulator = false;
|
|
544
|
+
if (method.name === "assign" && isGlobalCopyOwner(context.sourceCode, method.object, "Object")) {
|
|
545
|
+
const target = node.arguments[0];
|
|
546
|
+
copiesAccumulator = target !== undefined && unwrapArrayExpression(target).type === "ObjectExpression" && node.arguments.slice(1).some(isAccumulator);
|
|
547
|
+
} else if (method.name === "from" && isGlobalCopyOwner(context.sourceCode, method.object, "Array")) {
|
|
548
|
+
const source = node.arguments[0];
|
|
549
|
+
copiesAccumulator = source !== undefined && isAccumulator(source);
|
|
550
|
+
} else if (["concat", "slice", "toSpliced", "toSorted", "toReversed", "with"].includes(method.name)) {
|
|
551
|
+
const initialValue = reducer.initialValue;
|
|
552
|
+
const arrayAccumulator = initialValue !== undefined && isKnownArrayExpression(context.sourceCode, initialValue);
|
|
553
|
+
copiesAccumulator = arrayAccumulator && isAccumulator(method.object);
|
|
554
|
+
}
|
|
555
|
+
if (copiesAccumulator)
|
|
556
|
+
context.report({ node, messageId: "accumulatorCopy" });
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
4
562
|
// vendor/anti-slop/src/rules/no-chained-type-assertions.ts
|
|
5
|
-
import { defineRule } from "@oxlint/plugins";
|
|
563
|
+
import { defineRule as defineRule9 } from "@oxlint/plugins";
|
|
6
564
|
function isTypeAssertionExpression(node) {
|
|
7
565
|
return node.type === "TSAsExpression" || node.type === "TSTypeAssertion";
|
|
8
566
|
}
|
|
@@ -37,7 +595,7 @@ function isForbiddenAssertionChain(node) {
|
|
|
37
595
|
}
|
|
38
596
|
return assertionCount > 1 && hasNonConstAssertion;
|
|
39
597
|
}
|
|
40
|
-
var noChainedTypeAssertionsRule =
|
|
598
|
+
var noChainedTypeAssertionsRule = defineRule9({
|
|
41
599
|
meta: {
|
|
42
600
|
type: "problem",
|
|
43
601
|
docs: {
|
|
@@ -61,7 +619,7 @@ var noChainedTypeAssertionsRule = defineRule({
|
|
|
61
619
|
});
|
|
62
620
|
|
|
63
621
|
// vendor/anti-slop/src/rules/no-conditional-empty-object-spread.ts
|
|
64
|
-
import { defineRule as
|
|
622
|
+
import { defineRule as defineRule10 } from "@oxlint/plugins";
|
|
65
623
|
function unwrapParentheses(node) {
|
|
66
624
|
let current = node;
|
|
67
625
|
while (current.type === "ParenthesizedExpression") {
|
|
@@ -76,7 +634,7 @@ function isConditionalEmptyObjectSpread(node) {
|
|
|
76
634
|
const conditional = unwrapParentheses(node);
|
|
77
635
|
return conditional.type === "ConditionalExpression" && (isEmptyObjectExpression(conditional.consequent) || isEmptyObjectExpression(conditional.alternate));
|
|
78
636
|
}
|
|
79
|
-
var noConditionalEmptyObjectSpreadRule =
|
|
637
|
+
var noConditionalEmptyObjectSpreadRule = defineRule10({
|
|
80
638
|
meta: {
|
|
81
639
|
type: "suggestion",
|
|
82
640
|
docs: {
|
|
@@ -100,7 +658,7 @@ var noConditionalEmptyObjectSpreadRule = defineRule2({
|
|
|
100
658
|
});
|
|
101
659
|
|
|
102
660
|
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
103
|
-
import { defineRule as
|
|
661
|
+
import { defineRule as defineRule11 } from "@oxlint/plugins";
|
|
104
662
|
|
|
105
663
|
// vendor/anti-slop/src/shared/lexical-type-parameters.ts
|
|
106
664
|
function isNode(value) {
|
|
@@ -503,9 +1061,9 @@ function classifyWideningTarget(type, environment) {
|
|
|
503
1061
|
if (alias === null)
|
|
504
1062
|
return null;
|
|
505
1063
|
if ((alias.typeParameters?.params.length ?? 0) > 0) {
|
|
506
|
-
const
|
|
507
|
-
const
|
|
508
|
-
return
|
|
1064
|
+
const substitutions = aliasSubstitution(alias, unwrapped, new Map);
|
|
1065
|
+
const resolved = substitutions === null ? null : classifyAliasBroadTarget(alias.typeAnnotation, environment, substitutions, new Set([name]));
|
|
1066
|
+
return resolved?.kind === "open dictionary" ? { kind: "generic container" } : null;
|
|
509
1067
|
}
|
|
510
1068
|
const substitutions = aliasSubstitution(alias, unwrapped, new Map);
|
|
511
1069
|
if (substitutions === null)
|
|
@@ -629,15 +1187,8 @@ function functionParameterBindingName(parameter, sourceCode) {
|
|
|
629
1187
|
return annotationStart === undefined ? sourceText : sourceText.slice(0, annotationStart - parameter.start).trimEnd();
|
|
630
1188
|
}
|
|
631
1189
|
|
|
632
|
-
// vendor/anti-slop/src/
|
|
633
|
-
function
|
|
634
|
-
let current = expression;
|
|
635
|
-
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
636
|
-
current = current.expression;
|
|
637
|
-
}
|
|
638
|
-
return current;
|
|
639
|
-
}
|
|
640
|
-
function resolveVariable(sourceCode, identifier) {
|
|
1190
|
+
// vendor/anti-slop/src/shared/scope.ts
|
|
1191
|
+
function resolveVariable2(sourceCode, identifier) {
|
|
641
1192
|
let scope = sourceCode.getScope(identifier);
|
|
642
1193
|
while (scope !== null) {
|
|
643
1194
|
const variable = scope.set.get(identifier.name);
|
|
@@ -647,6 +1198,15 @@ function resolveVariable(sourceCode, identifier) {
|
|
|
647
1198
|
}
|
|
648
1199
|
return null;
|
|
649
1200
|
}
|
|
1201
|
+
|
|
1202
|
+
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
1203
|
+
function unwrapExpression(expression) {
|
|
1204
|
+
let current = expression;
|
|
1205
|
+
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
1206
|
+
current = current.expression;
|
|
1207
|
+
}
|
|
1208
|
+
return current;
|
|
1209
|
+
}
|
|
650
1210
|
function variableDeclarator(variable) {
|
|
651
1211
|
if (variable.defs.length !== 1)
|
|
652
1212
|
return null;
|
|
@@ -662,7 +1222,7 @@ function hasKnownEvidence(sourceCode, expression, visitedVariables = new Set) {
|
|
|
662
1222
|
const unwrapped = unwrapExpression(expression);
|
|
663
1223
|
if (unwrapped.type !== "Identifier")
|
|
664
1224
|
return false;
|
|
665
|
-
const variable =
|
|
1225
|
+
const variable = resolveVariable2(sourceCode, unwrapped);
|
|
666
1226
|
if (variable === null || visitedVariables.has(variable))
|
|
667
1227
|
return false;
|
|
668
1228
|
const declarator = variableDeclarator(variable);
|
|
@@ -681,7 +1241,7 @@ function localFunctionForCall(sourceCode, callee) {
|
|
|
681
1241
|
return unwrapped;
|
|
682
1242
|
if (unwrapped.type !== "Identifier")
|
|
683
1243
|
return null;
|
|
684
|
-
const variable =
|
|
1244
|
+
const variable = resolveVariable2(sourceCode, unwrapped);
|
|
685
1245
|
if (variable === null || variable.defs.length !== 1)
|
|
686
1246
|
return null;
|
|
687
1247
|
const [definition] = variable.defs;
|
|
@@ -734,7 +1294,7 @@ function hasKnownCallArgumentEvidence(sourceCode, expression, environment, visit
|
|
|
734
1294
|
}
|
|
735
1295
|
if (expression.type !== "Identifier")
|
|
736
1296
|
return isKnownEvidenceExpression(expression);
|
|
737
|
-
const variable =
|
|
1297
|
+
const variable = resolveVariable2(sourceCode, expression);
|
|
738
1298
|
if (variable === null || visitedVariables.has(variable))
|
|
739
1299
|
return false;
|
|
740
1300
|
const annotation = variableTypeAnnotation(sourceCode, variable);
|
|
@@ -799,7 +1359,7 @@ function isDictionaryAccumulatorTarget(destination) {
|
|
|
799
1359
|
function hasParentAssertion(node) {
|
|
800
1360
|
return node.parent?.type === "TSAsExpression" || node.parent?.type === "TSTypeAssertion";
|
|
801
1361
|
}
|
|
802
|
-
var noKnownValueWideningRule =
|
|
1362
|
+
var noKnownValueWideningRule = defineRule11({
|
|
803
1363
|
meta: {
|
|
804
1364
|
type: "problem",
|
|
805
1365
|
docs: {
|
|
@@ -848,7 +1408,7 @@ var noKnownValueWideningRule = defineRule3({
|
|
|
848
1408
|
AssignmentExpression(node) {
|
|
849
1409
|
if (node.operator !== "=" || node.left.type !== "Identifier")
|
|
850
1410
|
return;
|
|
851
|
-
const variable =
|
|
1411
|
+
const variable = resolveVariable2(context.sourceCode, node.left);
|
|
852
1412
|
if (variable === null)
|
|
853
1413
|
return;
|
|
854
1414
|
const declarator = variableDeclarator(variable);
|
|
@@ -912,18 +1472,8 @@ var noKnownValueWideningRule = defineRule3({
|
|
|
912
1472
|
});
|
|
913
1473
|
|
|
914
1474
|
// vendor/anti-slop/src/rules/no-module-mocking.ts
|
|
915
|
-
import { defineRule as
|
|
1475
|
+
import { defineRule as defineRule12 } from "@oxlint/plugins";
|
|
916
1476
|
var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
|
|
917
|
-
function resolveVariable2(sourceCode, identifier) {
|
|
918
|
-
let scope = sourceCode.getScope(identifier);
|
|
919
|
-
while (scope !== null) {
|
|
920
|
-
const variable = scope.set.get(identifier.name);
|
|
921
|
-
if (variable !== undefined)
|
|
922
|
-
return variable;
|
|
923
|
-
scope = scope.upper;
|
|
924
|
-
}
|
|
925
|
-
return null;
|
|
926
|
-
}
|
|
927
1477
|
function importedName(node) {
|
|
928
1478
|
if (node.type !== "ImportSpecifier")
|
|
929
1479
|
return null;
|
|
@@ -957,7 +1507,7 @@ function moduleMockCall(sourceCode, callee) {
|
|
|
957
1507
|
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;
|
|
958
1508
|
return method !== null && moduleMockMethods.has(method);
|
|
959
1509
|
}
|
|
960
|
-
var noModuleMockingRule =
|
|
1510
|
+
var noModuleMockingRule = defineRule12({
|
|
961
1511
|
meta: {
|
|
962
1512
|
type: "problem",
|
|
963
1513
|
docs: {
|
|
@@ -981,8 +1531,8 @@ var noModuleMockingRule = defineRule4({
|
|
|
981
1531
|
});
|
|
982
1532
|
|
|
983
1533
|
// vendor/anti-slop/src/rules/no-object-parameters.ts
|
|
984
|
-
import { defineRule as
|
|
985
|
-
var noObjectParametersRule =
|
|
1534
|
+
import { defineRule as defineRule13 } from "@oxlint/plugins";
|
|
1535
|
+
var noObjectParametersRule = defineRule13({
|
|
986
1536
|
meta: {
|
|
987
1537
|
type: "problem",
|
|
988
1538
|
docs: {
|
|
@@ -1035,25 +1585,15 @@ var noObjectParametersRule = defineRule5({
|
|
|
1035
1585
|
});
|
|
1036
1586
|
|
|
1037
1587
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1038
|
-
import { defineRule as
|
|
1588
|
+
import { defineRule as defineRule14 } from "@oxlint/plugins";
|
|
1039
1589
|
|
|
1040
1590
|
// vendor/anti-slop/src/shared/reflect-method.ts
|
|
1041
|
-
function resolveVariable3(sourceCode, identifier) {
|
|
1042
|
-
let scope = sourceCode.getScope(identifier);
|
|
1043
|
-
while (scope !== null) {
|
|
1044
|
-
const variable = scope.set.get(identifier.name);
|
|
1045
|
-
if (variable !== undefined)
|
|
1046
|
-
return variable;
|
|
1047
|
-
scope = scope.upper;
|
|
1048
|
-
}
|
|
1049
|
-
return null;
|
|
1050
|
-
}
|
|
1051
1591
|
function isGlobalReflect(sourceCode, expression) {
|
|
1052
1592
|
if (expression.type !== "Identifier" || expression.name !== "Reflect")
|
|
1053
1593
|
return false;
|
|
1054
1594
|
if (sourceCode.isGlobalReference(expression))
|
|
1055
1595
|
return true;
|
|
1056
|
-
const variable =
|
|
1596
|
+
const variable = resolveVariable2(sourceCode, expression);
|
|
1057
1597
|
return variable === null || variable.defs.length === 0;
|
|
1058
1598
|
}
|
|
1059
1599
|
function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
@@ -1066,7 +1606,7 @@ function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
|
1066
1606
|
}
|
|
1067
1607
|
|
|
1068
1608
|
// vendor/anti-slop/src/rules/no-reflect-apply.ts
|
|
1069
|
-
var noReflectApplyRule =
|
|
1609
|
+
var noReflectApplyRule = defineRule14({
|
|
1070
1610
|
meta: {
|
|
1071
1611
|
type: "problem",
|
|
1072
1612
|
docs: {
|
|
@@ -1090,8 +1630,8 @@ var noReflectApplyRule = defineRule6({
|
|
|
1090
1630
|
});
|
|
1091
1631
|
|
|
1092
1632
|
// vendor/anti-slop/src/rules/no-reflect-get.ts
|
|
1093
|
-
import { defineRule as
|
|
1094
|
-
var noReflectGetRule =
|
|
1633
|
+
import { defineRule as defineRule15 } from "@oxlint/plugins";
|
|
1634
|
+
var noReflectGetRule = defineRule15({
|
|
1095
1635
|
meta: {
|
|
1096
1636
|
type: "problem",
|
|
1097
1637
|
docs: {
|
|
@@ -1115,7 +1655,7 @@ var noReflectGetRule = defineRule7({
|
|
|
1115
1655
|
});
|
|
1116
1656
|
|
|
1117
1657
|
// vendor/anti-slop/src/rules/no-runtime-typeof.ts
|
|
1118
|
-
import { defineRule as
|
|
1658
|
+
import { defineRule as defineRule16 } from "@oxlint/plugins";
|
|
1119
1659
|
function isRuntimeFunction(node) {
|
|
1120
1660
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression";
|
|
1121
1661
|
}
|
|
@@ -1138,7 +1678,7 @@ function isExistenceProbe(node) {
|
|
|
1138
1678
|
const other = parent.left === node ? parent.right : parent.left;
|
|
1139
1679
|
return other.type === "Literal" && other.value === "undefined";
|
|
1140
1680
|
}
|
|
1141
|
-
var noRuntimeTypeofRule =
|
|
1681
|
+
var noRuntimeTypeofRule = defineRule16({
|
|
1142
1682
|
meta: {
|
|
1143
1683
|
type: "problem",
|
|
1144
1684
|
docs: {
|
|
@@ -1172,7 +1712,7 @@ var noRuntimeTypeofRule = defineRule8({
|
|
|
1172
1712
|
});
|
|
1173
1713
|
|
|
1174
1714
|
// vendor/anti-slop/src/rules/no-shape-in-symbol-names.ts
|
|
1175
|
-
import { defineRule as
|
|
1715
|
+
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
1176
1716
|
var FORBIDDEN_SYMBOL_NAME = "shape";
|
|
1177
1717
|
function containsForbiddenSymbolName(name) {
|
|
1178
1718
|
return name.toLowerCase().includes(FORBIDDEN_SYMBOL_NAME);
|
|
@@ -1183,7 +1723,7 @@ function isBorrowedMemberName(node) {
|
|
|
1183
1723
|
return false;
|
|
1184
1724
|
return parent.property === node && parent.computed === false;
|
|
1185
1725
|
}
|
|
1186
|
-
var noForbiddenTermInSymbolNamesRule =
|
|
1726
|
+
var noForbiddenTermInSymbolNamesRule = defineRule17({
|
|
1187
1727
|
meta: {
|
|
1188
1728
|
type: "problem",
|
|
1189
1729
|
docs: {
|
|
@@ -1212,12 +1752,12 @@ var noForbiddenTermInSymbolNamesRule = defineRule9({
|
|
|
1212
1752
|
});
|
|
1213
1753
|
|
|
1214
1754
|
// vendor/anti-slop/src/rules/no-unknown-parameters.ts
|
|
1215
|
-
import { defineRule as
|
|
1755
|
+
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
1216
1756
|
function isTypePredicateSubject(owner, parameterName) {
|
|
1217
1757
|
const predicate = owner.returnType?.typeAnnotation;
|
|
1218
1758
|
return predicate?.type === "TSTypePredicate" && predicate.parameterName.type === "Identifier" && predicate.parameterName.name === parameterName;
|
|
1219
1759
|
}
|
|
1220
|
-
var noUnknownParametersRule =
|
|
1760
|
+
var noUnknownParametersRule = defineRule18({
|
|
1221
1761
|
meta: {
|
|
1222
1762
|
type: "problem",
|
|
1223
1763
|
docs: {
|
|
@@ -1261,8 +1801,8 @@ var noUnknownParametersRule = defineRule10({
|
|
|
1261
1801
|
});
|
|
1262
1802
|
|
|
1263
1803
|
// vendor/anti-slop/src/rules/no-unknown-returns.ts
|
|
1264
|
-
import { defineRule as
|
|
1265
|
-
var noUnknownReturnsRule =
|
|
1804
|
+
import { defineRule as defineRule19 } from "@oxlint/plugins";
|
|
1805
|
+
var noUnknownReturnsRule = defineRule19({
|
|
1266
1806
|
meta: {
|
|
1267
1807
|
type: "problem",
|
|
1268
1808
|
docs: {
|
|
@@ -1315,8 +1855,8 @@ var noUnknownReturnsRule = defineRule11({
|
|
|
1315
1855
|
});
|
|
1316
1856
|
|
|
1317
1857
|
// vendor/anti-slop/src/rules/no-unknown-type-aliases.ts
|
|
1318
|
-
import { defineRule as
|
|
1319
|
-
var noUnknownTypeAliasesRule =
|
|
1858
|
+
import { defineRule as defineRule20 } from "@oxlint/plugins";
|
|
1859
|
+
var noUnknownTypeAliasesRule = defineRule20({
|
|
1320
1860
|
meta: {
|
|
1321
1861
|
type: "problem",
|
|
1322
1862
|
docs: {
|
|
@@ -1354,7 +1894,7 @@ var noUnknownTypeAliasesRule = defineRule12({
|
|
|
1354
1894
|
});
|
|
1355
1895
|
|
|
1356
1896
|
// vendor/anti-slop/src/rules/no-unsafe-dictionary-type.ts
|
|
1357
|
-
import { defineRule as
|
|
1897
|
+
import { defineRule as defineRule21 } from "@oxlint/plugins";
|
|
1358
1898
|
var typeNodeKinds = new Set([
|
|
1359
1899
|
"JSDocNonNullableType",
|
|
1360
1900
|
"JSDocNullableType",
|
|
@@ -1441,7 +1981,7 @@ function shouldReportType(node, environment) {
|
|
|
1441
1981
|
}
|
|
1442
1982
|
return true;
|
|
1443
1983
|
}
|
|
1444
|
-
var noUnsafeDictionaryTypeRule =
|
|
1984
|
+
var noUnsafeDictionaryTypeRule = defineRule21({
|
|
1445
1985
|
meta: {
|
|
1446
1986
|
type: "problem",
|
|
1447
1987
|
docs: {
|
|
@@ -1483,7 +2023,7 @@ var noUnsafeDictionaryTypeRule = defineRule13({
|
|
|
1483
2023
|
});
|
|
1484
2024
|
|
|
1485
2025
|
// vendor/anti-slop/src/rules/no-widen-then-assert.ts
|
|
1486
|
-
import { defineRule as
|
|
2026
|
+
import { defineRule as defineRule22 } from "@oxlint/plugins";
|
|
1487
2027
|
var functionBoundaryTypes = new Set([
|
|
1488
2028
|
"ArrowFunctionExpression",
|
|
1489
2029
|
"FunctionDeclaration",
|
|
@@ -1643,83 +2183,605 @@ function knownValueEvidence(expression, scopes, boundary, visitedVariables) {
|
|
|
1643
2183
|
if (functionBoundary(annotatedIdentifier) !== boundary || broadTypeKind(annotation) !== null) {
|
|
1644
2184
|
return null;
|
|
1645
2185
|
}
|
|
1646
|
-
return { type: annotation };
|
|
1647
|
-
}
|
|
1648
|
-
const declarator = variableDeclarator2(variable);
|
|
1649
|
-
if (declarator === null || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || declarator.init === null || variable.references.some((reference) => reference.isWrite() && !reference.init) || functionBoundary(declarator) !== boundary) {
|
|
1650
|
-
return null;
|
|
1651
|
-
}
|
|
1652
|
-
return knownValueEvidence(declarator.init, scopes, boundary, new Set([...visitedVariables, variable]));
|
|
2186
|
+
return { type: annotation };
|
|
2187
|
+
}
|
|
2188
|
+
const declarator = variableDeclarator2(variable);
|
|
2189
|
+
if (declarator === null || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || declarator.init === null || variable.references.some((reference) => reference.isWrite() && !reference.init) || functionBoundary(declarator) !== boundary) {
|
|
2190
|
+
return null;
|
|
2191
|
+
}
|
|
2192
|
+
return knownValueEvidence(declarator.init, scopes, boundary, new Set([...visitedVariables, variable]));
|
|
2193
|
+
}
|
|
2194
|
+
function widenedBinding(variable, scopes) {
|
|
2195
|
+
const declarator = variableDeclarator2(variable);
|
|
2196
|
+
if (declarator === null || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || declarator.id.type !== "Identifier" || declarator.init === null || variable.references.some((reference) => reference.isWrite() && !reference.init)) {
|
|
2197
|
+
return null;
|
|
2198
|
+
}
|
|
2199
|
+
const boundary = functionBoundary(declarator);
|
|
2200
|
+
const declaredType = declarator.id.typeAnnotation?.typeAnnotation;
|
|
2201
|
+
const initializerAssertion = assertionFromExpression(declarator.init);
|
|
2202
|
+
const initializerBroadKind = initializerAssertion === null ? null : broadTypeKind(initializerAssertion.typeAnnotation);
|
|
2203
|
+
const declaredBroadKind = declaredType === undefined ? null : broadTypeKind(declaredType);
|
|
2204
|
+
const broadKind = declaredBroadKind ?? initializerBroadKind;
|
|
2205
|
+
if (broadKind === null)
|
|
2206
|
+
return null;
|
|
2207
|
+
const originalExpression = initializerAssertion !== null && initializerBroadKind !== null ? assertedExpression(initializerAssertion) : declarator.init;
|
|
2208
|
+
const evidence = knownValueEvidence(originalExpression, scopes, boundary, new Set([variable]));
|
|
2209
|
+
return evidence === null ? null : { broadKind, evidence, declaredAt: declarator.end, boundary };
|
|
2210
|
+
}
|
|
2211
|
+
function assertionIsNarrower(sourceText, broadKind, evidence, assertedType) {
|
|
2212
|
+
if (broadTypeKind(assertedType) !== null)
|
|
2213
|
+
return false;
|
|
2214
|
+
if (broadKind === "top")
|
|
2215
|
+
return true;
|
|
2216
|
+
if (typesHaveSameSyntax(sourceText, evidence.type, assertedType))
|
|
2217
|
+
return true;
|
|
2218
|
+
if (broadKind === "object")
|
|
2219
|
+
return isDefinitelyObjectType(assertedType);
|
|
2220
|
+
return isDefinitelyNarrowerRecordType(assertedType);
|
|
2221
|
+
}
|
|
2222
|
+
var noWidenThenAssertRule = defineRule22({
|
|
2223
|
+
meta: {
|
|
2224
|
+
type: "problem",
|
|
2225
|
+
docs: {
|
|
2226
|
+
description: "Disallow local const flows that explicitly widen a known value before asserting the widened binding to a narrower type."
|
|
2227
|
+
},
|
|
2228
|
+
messages: {
|
|
2229
|
+
widenThenAssert: 'Binding "{{name}}" discards type evidence and later recreates it with an assertion. Keep the precise type from initialization through use; parse boundary input once.'
|
|
2230
|
+
}
|
|
2231
|
+
},
|
|
2232
|
+
createOnce(context) {
|
|
2233
|
+
let scopes = [];
|
|
2234
|
+
const checkAssertion = (node) => {
|
|
2235
|
+
const expression = assertedExpression(node);
|
|
2236
|
+
if (expression.type !== "Identifier")
|
|
2237
|
+
return;
|
|
2238
|
+
const variable = resolvedVariableForIdentifier(scopes, expression);
|
|
2239
|
+
if (variable === null)
|
|
2240
|
+
return;
|
|
2241
|
+
const widened = widenedBinding(variable, scopes);
|
|
2242
|
+
if (widened === null || node.start <= widened.declaredAt || functionBoundary(node) !== widened.boundary || !assertionIsNarrower(context.sourceCode.text, widened.broadKind, widened.evidence, node.typeAnnotation)) {
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
context.report({
|
|
2246
|
+
node,
|
|
2247
|
+
messageId: "widenThenAssert",
|
|
2248
|
+
data: { name: expression.name }
|
|
2249
|
+
});
|
|
2250
|
+
};
|
|
2251
|
+
return {
|
|
2252
|
+
Program() {
|
|
2253
|
+
scopes = context.sourceCode.scopeManager.scopes;
|
|
2254
|
+
},
|
|
2255
|
+
TSAsExpression: checkAssertion,
|
|
2256
|
+
TSTypeAssertion: checkAssertion
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2260
|
+
|
|
2261
|
+
// vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts
|
|
2262
|
+
var LINEBREAKS = new Set([`\r
|
|
2263
|
+
`, "\r", `
|
|
2264
|
+
`, "\u2028", "\u2029"]);
|
|
2265
|
+
var isClosingBraceToken = (token) => token.type === "Punctuator" && token.value === "}";
|
|
2266
|
+
var isSemicolonToken = (token) => token.type === "Punctuator" && token.value === ";";
|
|
2267
|
+
var isNotSemicolonToken = (token) => !isSemicolonToken(token);
|
|
2268
|
+
var isTokenOnSameLine = (left, right) => left.loc.end.line === right.loc.start.line;
|
|
2269
|
+
var isFunction = (node) => node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
2270
|
+
var isSingleLine = (node) => node.loc.start.line === node.loc.end.line;
|
|
2271
|
+
var skipChainExpression = (node) => node.type === "ChainExpression" ? node.expression : node;
|
|
2272
|
+
var isTopLevelExpressionStatement = (node) => node.type === "ExpressionStatement" && (node.parent.type === "Program" || node.parent.type === "BlockStatement" && isFunction(node.parent.parent));
|
|
2273
|
+
function isParenthesized(node, sourceCode) {
|
|
2274
|
+
const before = sourceCode.getTokenBefore(node);
|
|
2275
|
+
const after = sourceCode.getTokenAfter(node);
|
|
2276
|
+
return before?.value === "(" && after?.value === ")";
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts
|
|
2280
|
+
var CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
|
|
2281
|
+
var CJS_IMPORT = /^require\(/u;
|
|
2282
|
+
var LT = `[${Array.from(LINEBREAKS).join("")}]`;
|
|
2283
|
+
var PADDING_LINE_SEQUENCE = new RegExp(String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, "u");
|
|
2284
|
+
function isSelectorOption(option) {
|
|
2285
|
+
return typeof option === "object" && !Array.isArray(option);
|
|
2286
|
+
}
|
|
2287
|
+
function newKeywordTester(type, keyword) {
|
|
2288
|
+
return {
|
|
2289
|
+
test(node, sourceCode) {
|
|
2290
|
+
const isSameKeyword = sourceCode.getFirstToken(node)?.value === keyword;
|
|
2291
|
+
const isSameType = Array.isArray(type) ? type.includes(node.type) : type === node.type;
|
|
2292
|
+
return isSameKeyword && isSameType;
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
function newNodeTypeTester(type) {
|
|
2297
|
+
return {
|
|
2298
|
+
test: (node) => node.type === type
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
function isIIFEStatement(node) {
|
|
2302
|
+
if (node.type === "ExpressionStatement") {
|
|
2303
|
+
let expression = skipChainExpression(node.expression);
|
|
2304
|
+
if (expression.type === "UnaryExpression")
|
|
2305
|
+
expression = skipChainExpression(expression.argument);
|
|
2306
|
+
if (expression.type === "CallExpression") {
|
|
2307
|
+
let node = expression.callee;
|
|
2308
|
+
while (node.type === "SequenceExpression") {
|
|
2309
|
+
const lastExpression = node.expressions.at(-1);
|
|
2310
|
+
if (lastExpression === undefined)
|
|
2311
|
+
throw new Error("Padding rule invariant: sequence expression is empty");
|
|
2312
|
+
node = lastExpression;
|
|
2313
|
+
}
|
|
2314
|
+
return isFunction(node);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
return false;
|
|
2318
|
+
}
|
|
2319
|
+
function isCJSRequire(node) {
|
|
2320
|
+
if (node.type === "VariableDeclaration") {
|
|
2321
|
+
const declaration = node.declarations[0];
|
|
2322
|
+
if (declaration?.init) {
|
|
2323
|
+
let call = declaration?.init;
|
|
2324
|
+
while (call.type === "MemberExpression")
|
|
2325
|
+
call = call.object;
|
|
2326
|
+
if (call.type === "CallExpression" && call.callee.type === "Identifier") {
|
|
2327
|
+
return call.callee.name === "require";
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
return false;
|
|
2332
|
+
}
|
|
2333
|
+
function isBlockLikeStatement(node, sourceCode) {
|
|
2334
|
+
if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") {
|
|
2335
|
+
return true;
|
|
2336
|
+
}
|
|
2337
|
+
if (isIIFEStatement(node))
|
|
2338
|
+
return true;
|
|
2339
|
+
const lastToken = sourceCode.getLastToken(node, isNotSemicolonToken);
|
|
2340
|
+
const belongingNode = lastToken && isClosingBraceToken(lastToken) ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) : null;
|
|
2341
|
+
return !!belongingNode && (belongingNode.type === "BlockStatement" || belongingNode.type === "SwitchStatement");
|
|
2342
|
+
}
|
|
2343
|
+
function isDirective(node, sourceCode) {
|
|
2344
|
+
return isTopLevelExpressionStatement(node) && node.expression.type === "Literal" && typeof node.expression.value === "string" && !isParenthesized(node.expression, sourceCode);
|
|
2345
|
+
}
|
|
2346
|
+
function isDirectivePrologue(node, sourceCode) {
|
|
2347
|
+
if (isDirective(node, sourceCode) && node.parent && "body" in node.parent && Array.isArray(node.parent.body)) {
|
|
2348
|
+
for (const sibling of node.parent.body) {
|
|
2349
|
+
if (sibling === node)
|
|
2350
|
+
break;
|
|
2351
|
+
if (!isDirective(sibling, sourceCode))
|
|
2352
|
+
return false;
|
|
2353
|
+
}
|
|
2354
|
+
return true;
|
|
2355
|
+
}
|
|
2356
|
+
return false;
|
|
2357
|
+
}
|
|
2358
|
+
function isCJSExport(node) {
|
|
2359
|
+
if (node.type === "ExpressionStatement") {
|
|
2360
|
+
const expression = node.expression;
|
|
2361
|
+
if (expression.type === "AssignmentExpression") {
|
|
2362
|
+
let left = expression.left;
|
|
2363
|
+
if (left.type === "MemberExpression") {
|
|
2364
|
+
while (left.object.type === "MemberExpression")
|
|
2365
|
+
left = left.object;
|
|
2366
|
+
return left.object.type === "Identifier" && (left.object.name === "exports" || left.object.name === "module" && left.property.type === "Identifier" && left.property.name === "exports");
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
return false;
|
|
2371
|
+
}
|
|
2372
|
+
function isExpression(node, sourceCode) {
|
|
2373
|
+
return node.type === "ExpressionStatement" && !isDirectivePrologue(node, sourceCode);
|
|
2374
|
+
}
|
|
2375
|
+
function getActualLastToken(node, sourceCode) {
|
|
2376
|
+
const semiToken = sourceCode.getLastToken(node);
|
|
2377
|
+
const prevToken = sourceCode.getTokenBefore(semiToken);
|
|
2378
|
+
const nextToken = sourceCode.getTokenAfter(semiToken);
|
|
2379
|
+
const isSemicolonLessStyle = prevToken && nextToken && prevToken.range[0] >= node.range[0] && isSemicolonToken(semiToken) && !isTokenOnSameLine(prevToken, semiToken) && isTokenOnSameLine(semiToken, nextToken);
|
|
2380
|
+
return isSemicolonLessStyle ? prevToken : semiToken;
|
|
2381
|
+
}
|
|
2382
|
+
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
|
|
2383
|
+
return trailingSpaces + indentSpaces;
|
|
2384
|
+
}
|
|
2385
|
+
function getReportLoc(node, sourceCode) {
|
|
2386
|
+
if (isSingleLine(node))
|
|
2387
|
+
return node.loc;
|
|
2388
|
+
const line = node.loc.start.line;
|
|
2389
|
+
const sourceLine = sourceCode.lines[line - 1];
|
|
2390
|
+
if (sourceLine === undefined)
|
|
2391
|
+
throw new Error("Padding rule invariant: statement source line is missing");
|
|
2392
|
+
return {
|
|
2393
|
+
start: node.loc.start,
|
|
2394
|
+
end: {
|
|
2395
|
+
line,
|
|
2396
|
+
column: sourceLine.length
|
|
2397
|
+
}
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
function verifyForAny() {}
|
|
2401
|
+
function verifyForNever(context, _, nextNode, paddingLines) {
|
|
2402
|
+
if (paddingLines.length === 0)
|
|
2403
|
+
return;
|
|
2404
|
+
context.report({
|
|
2405
|
+
node: nextNode,
|
|
2406
|
+
messageId: "unexpectedBlankLine",
|
|
2407
|
+
loc: getReportLoc(nextNode, context.sourceCode),
|
|
2408
|
+
fix(fixer) {
|
|
2409
|
+
if (paddingLines.length >= 2)
|
|
2410
|
+
return null;
|
|
2411
|
+
const paddingPair = paddingLines[0];
|
|
2412
|
+
if (paddingPair === undefined)
|
|
2413
|
+
throw new Error("Padding rule invariant: reported padding pair is missing");
|
|
2414
|
+
const [prevToken, nextToken] = paddingPair;
|
|
2415
|
+
const start = prevToken.range[1];
|
|
2416
|
+
const end = nextToken.range[0];
|
|
2417
|
+
const text = context.sourceCode.text.slice(start, end).replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
|
|
2418
|
+
return fixer.replaceTextRange([start, end], text);
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
function verifyForAlways(context, prevNode, nextNode, paddingLines) {
|
|
2423
|
+
if (paddingLines.length > 0)
|
|
2424
|
+
return;
|
|
2425
|
+
context.report({
|
|
2426
|
+
node: nextNode,
|
|
2427
|
+
messageId: "expectedBlankLine",
|
|
2428
|
+
loc: getReportLoc(nextNode, context.sourceCode),
|
|
2429
|
+
fix(fixer) {
|
|
2430
|
+
const sourceCode = context.sourceCode;
|
|
2431
|
+
let prevToken = getActualLastToken(prevNode, sourceCode);
|
|
2432
|
+
const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
|
|
2433
|
+
includeComments: true,
|
|
2434
|
+
filter(token) {
|
|
2435
|
+
if (isTokenOnSameLine(prevToken, token)) {
|
|
2436
|
+
prevToken = token;
|
|
2437
|
+
return false;
|
|
2438
|
+
}
|
|
2439
|
+
return true;
|
|
2440
|
+
}
|
|
2441
|
+
}) || nextNode;
|
|
2442
|
+
const insertText = isTokenOnSameLine(prevToken, nextToken) ? `
|
|
2443
|
+
|
|
2444
|
+
` : `
|
|
2445
|
+
`;
|
|
2446
|
+
return fixer.insertTextAfter(prevToken, insertText);
|
|
2447
|
+
}
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2450
|
+
var PaddingTypes = {
|
|
2451
|
+
any: { verify: verifyForAny },
|
|
2452
|
+
never: { verify: verifyForNever },
|
|
2453
|
+
always: { verify: verifyForAlways }
|
|
2454
|
+
};
|
|
2455
|
+
var MaybeMultilineStatementType = {
|
|
2456
|
+
"block-like": { test: isBlockLikeStatement },
|
|
2457
|
+
expression: { test: isExpression },
|
|
2458
|
+
return: newKeywordTester("ReturnStatement", "return"),
|
|
2459
|
+
export: newKeywordTester([
|
|
2460
|
+
"ExportAllDeclaration",
|
|
2461
|
+
"ExportDefaultDeclaration",
|
|
2462
|
+
"ExportNamedDeclaration"
|
|
2463
|
+
], "export"),
|
|
2464
|
+
var: newKeywordTester("VariableDeclaration", "var"),
|
|
2465
|
+
let: newKeywordTester("VariableDeclaration", "let"),
|
|
2466
|
+
const: newKeywordTester("VariableDeclaration", "const"),
|
|
2467
|
+
using: {
|
|
2468
|
+
test: (node) => node.type === "VariableDeclaration" && (node.kind === "using" || node.kind === "await using")
|
|
2469
|
+
},
|
|
2470
|
+
type: newKeywordTester("TSTypeAliasDeclaration", "type")
|
|
2471
|
+
};
|
|
2472
|
+
var StatementTypes = {
|
|
2473
|
+
"*": { test: () => true },
|
|
2474
|
+
exports: { test: isCJSExport },
|
|
2475
|
+
require: { test: isCJSRequire },
|
|
2476
|
+
directive: { test: isDirectivePrologue },
|
|
2477
|
+
iife: { test: isIIFEStatement },
|
|
2478
|
+
block: newNodeTypeTester("BlockStatement"),
|
|
2479
|
+
empty: newNodeTypeTester("EmptyStatement"),
|
|
2480
|
+
function: newNodeTypeTester("FunctionDeclaration"),
|
|
2481
|
+
"ts-method": newNodeTypeTester("TSMethodSignature"),
|
|
2482
|
+
break: newKeywordTester("BreakStatement", "break"),
|
|
2483
|
+
case: newKeywordTester("SwitchCase", "case"),
|
|
2484
|
+
class: newKeywordTester("ClassDeclaration", "class"),
|
|
2485
|
+
continue: newKeywordTester("ContinueStatement", "continue"),
|
|
2486
|
+
debugger: newKeywordTester("DebuggerStatement", "debugger"),
|
|
2487
|
+
default: newKeywordTester(["SwitchCase", "ExportDefaultDeclaration"], "default"),
|
|
2488
|
+
do: newKeywordTester("DoWhileStatement", "do"),
|
|
2489
|
+
for: newKeywordTester([
|
|
2490
|
+
"ForStatement",
|
|
2491
|
+
"ForInStatement",
|
|
2492
|
+
"ForOfStatement"
|
|
2493
|
+
], "for"),
|
|
2494
|
+
if: newKeywordTester("IfStatement", "if"),
|
|
2495
|
+
import: newKeywordTester("ImportDeclaration", "import"),
|
|
2496
|
+
switch: newKeywordTester("SwitchStatement", "switch"),
|
|
2497
|
+
throw: newKeywordTester("ThrowStatement", "throw"),
|
|
2498
|
+
try: newKeywordTester("TryStatement", "try"),
|
|
2499
|
+
while: newKeywordTester(["WhileStatement", "DoWhileStatement"], "while"),
|
|
2500
|
+
with: newKeywordTester("WithStatement", "with"),
|
|
2501
|
+
"cjs-export": {
|
|
2502
|
+
test: (node, sourceCode) => node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && CJS_EXPORT.test(sourceCode.getText(node.expression.left))
|
|
2503
|
+
},
|
|
2504
|
+
"cjs-import": {
|
|
2505
|
+
test: (node, sourceCode) => node.type === "VariableDeclaration" && node.declarations.length > 0 && node.declarations[0]?.init != null && CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
|
|
2506
|
+
},
|
|
2507
|
+
enum: newKeywordTester("TSEnumDeclaration", "enum"),
|
|
2508
|
+
interface: newKeywordTester("TSInterfaceDeclaration", "interface"),
|
|
2509
|
+
"function-overload": newNodeTypeTester("TSDeclareFunction"),
|
|
2510
|
+
...Object.fromEntries(Object.entries(MaybeMultilineStatementType).flatMap(([key, value]) => [
|
|
2511
|
+
[key, value],
|
|
2512
|
+
[
|
|
2513
|
+
`singleline-${key}`,
|
|
2514
|
+
{
|
|
2515
|
+
...value,
|
|
2516
|
+
test: (node, sourceCode) => value.test(node, sourceCode) && isSingleLine(node)
|
|
2517
|
+
}
|
|
2518
|
+
],
|
|
2519
|
+
[
|
|
2520
|
+
`multiline-${key}`,
|
|
2521
|
+
{
|
|
2522
|
+
...value,
|
|
2523
|
+
test: (node, sourceCode) => value.test(node, sourceCode) && !isSingleLine(node)
|
|
2524
|
+
}
|
|
2525
|
+
]
|
|
2526
|
+
]))
|
|
2527
|
+
};
|
|
2528
|
+
function createPaddingLineRule(options) {
|
|
2529
|
+
return {
|
|
2530
|
+
meta: {
|
|
2531
|
+
type: "layout",
|
|
2532
|
+
docs: {
|
|
2533
|
+
description: "Require or disallow padding lines between statements"
|
|
2534
|
+
},
|
|
2535
|
+
fixable: "whitespace",
|
|
2536
|
+
hasSuggestions: false,
|
|
2537
|
+
schema: {
|
|
2538
|
+
$defs: {
|
|
2539
|
+
paddingType: {
|
|
2540
|
+
type: "string",
|
|
2541
|
+
enum: Object.keys(PaddingTypes)
|
|
2542
|
+
},
|
|
2543
|
+
statementType: {
|
|
2544
|
+
type: "string",
|
|
2545
|
+
enum: Object.keys(StatementTypes)
|
|
2546
|
+
},
|
|
2547
|
+
selectorOption: {
|
|
2548
|
+
type: "object",
|
|
2549
|
+
properties: {
|
|
2550
|
+
selector: {
|
|
2551
|
+
type: "string"
|
|
2552
|
+
},
|
|
2553
|
+
lineMode: {
|
|
2554
|
+
type: "string",
|
|
2555
|
+
enum: ["any", "singleline", "multiline"]
|
|
2556
|
+
}
|
|
2557
|
+
},
|
|
2558
|
+
required: ["selector"],
|
|
2559
|
+
additionalProperties: false
|
|
2560
|
+
},
|
|
2561
|
+
statementMatcher: {
|
|
2562
|
+
anyOf: [
|
|
2563
|
+
{ $ref: "#/$defs/statementType" },
|
|
2564
|
+
{ $ref: "#/$defs/selectorOption" }
|
|
2565
|
+
]
|
|
2566
|
+
},
|
|
2567
|
+
statementOption: {
|
|
2568
|
+
anyOf: [
|
|
2569
|
+
{ $ref: "#/$defs/statementMatcher" },
|
|
2570
|
+
{
|
|
2571
|
+
type: "array",
|
|
2572
|
+
items: { $ref: "#/$defs/statementMatcher" },
|
|
2573
|
+
minItems: 1,
|
|
2574
|
+
uniqueItems: true,
|
|
2575
|
+
additionalItems: false
|
|
2576
|
+
}
|
|
2577
|
+
]
|
|
2578
|
+
}
|
|
2579
|
+
},
|
|
2580
|
+
type: "array",
|
|
2581
|
+
additionalItems: false,
|
|
2582
|
+
items: {
|
|
2583
|
+
type: "object",
|
|
2584
|
+
properties: {
|
|
2585
|
+
blankLine: { $ref: "#/$defs/paddingType" },
|
|
2586
|
+
prev: { $ref: "#/$defs/statementOption" },
|
|
2587
|
+
next: { $ref: "#/$defs/statementOption" }
|
|
2588
|
+
},
|
|
2589
|
+
additionalProperties: false,
|
|
2590
|
+
required: ["blankLine", "prev", "next"]
|
|
2591
|
+
}
|
|
2592
|
+
},
|
|
2593
|
+
messages: {
|
|
2594
|
+
unexpectedBlankLine: "Unexpected blank line before this statement.",
|
|
2595
|
+
expectedBlankLine: "Expected blank line before this statement."
|
|
2596
|
+
}
|
|
2597
|
+
},
|
|
2598
|
+
create(context) {
|
|
2599
|
+
const sourceCode = context.sourceCode;
|
|
2600
|
+
const selectorMatchedNodes = new Map;
|
|
2601
|
+
const pendingPairs = [];
|
|
2602
|
+
function collectSelectorOption(option) {
|
|
2603
|
+
if (Array.isArray(option)) {
|
|
2604
|
+
for (const item of option)
|
|
2605
|
+
collectSelectorOption(item);
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
if (!isSelectorOption(option))
|
|
2609
|
+
return;
|
|
2610
|
+
selectorMatchedNodes.set(option.selector, new Set);
|
|
2611
|
+
}
|
|
2612
|
+
for (const configure of options) {
|
|
2613
|
+
collectSelectorOption(configure.prev);
|
|
2614
|
+
collectSelectorOption(configure.next);
|
|
2615
|
+
}
|
|
2616
|
+
let scopeInfo = null;
|
|
2617
|
+
function enterScope() {
|
|
2618
|
+
scopeInfo = {
|
|
2619
|
+
upper: scopeInfo,
|
|
2620
|
+
prevNode: null
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
function exitScope() {
|
|
2624
|
+
if (scopeInfo)
|
|
2625
|
+
scopeInfo = scopeInfo.upper;
|
|
2626
|
+
}
|
|
2627
|
+
function match(node, type) {
|
|
2628
|
+
let innerStatementNode = node;
|
|
2629
|
+
while (innerStatementNode.type === "LabeledStatement")
|
|
2630
|
+
innerStatementNode = innerStatementNode.body;
|
|
2631
|
+
if (Array.isArray(type))
|
|
2632
|
+
return type.some(match.bind(null, innerStatementNode));
|
|
2633
|
+
if (isSelectorOption(type)) {
|
|
2634
|
+
const matchedNodes = selectorMatchedNodes.get(type.selector);
|
|
2635
|
+
if (!matchedNodes?.has(innerStatementNode))
|
|
2636
|
+
return false;
|
|
2637
|
+
const lineMode = type.lineMode;
|
|
2638
|
+
if (lineMode === "singleline")
|
|
2639
|
+
return isSingleLine(innerStatementNode);
|
|
2640
|
+
else if (lineMode === "multiline")
|
|
2641
|
+
return !isSingleLine(innerStatementNode);
|
|
2642
|
+
return true;
|
|
2643
|
+
} else {
|
|
2644
|
+
const statementType = StatementTypes[type];
|
|
2645
|
+
if (statementType === undefined)
|
|
2646
|
+
throw new Error(`Padding rule invariant: unsupported statement type ${type}`);
|
|
2647
|
+
return statementType.test(innerStatementNode, sourceCode);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
function getPaddingType(prevNode, nextNode) {
|
|
2651
|
+
for (let i = options.length - 1;i >= 0; --i) {
|
|
2652
|
+
const configure = options[i];
|
|
2653
|
+
if (configure === undefined)
|
|
2654
|
+
throw new Error("Padding rule invariant: configuration entry is missing");
|
|
2655
|
+
if (match(prevNode, configure.prev) && match(nextNode, configure.next)) {
|
|
2656
|
+
return PaddingTypes[configure.blankLine];
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
return PaddingTypes.any;
|
|
2660
|
+
}
|
|
2661
|
+
function getPaddingLineSequences(prevNode, nextNode) {
|
|
2662
|
+
const pairs = [];
|
|
2663
|
+
let prevToken = getActualLastToken(prevNode, sourceCode);
|
|
2664
|
+
if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
|
|
2665
|
+
do {
|
|
2666
|
+
const token = sourceCode.getTokenAfter(prevToken, {
|
|
2667
|
+
includeComments: true
|
|
2668
|
+
});
|
|
2669
|
+
if (token.loc.start.line - prevToken.loc.end.line >= 2)
|
|
2670
|
+
pairs.push([prevToken, token]);
|
|
2671
|
+
prevToken = token;
|
|
2672
|
+
} while (prevToken.range[0] < nextNode.range[0]);
|
|
2673
|
+
}
|
|
2674
|
+
return pairs;
|
|
2675
|
+
}
|
|
2676
|
+
function verify(node) {
|
|
2677
|
+
if (!node.parent || ![
|
|
2678
|
+
"BlockStatement",
|
|
2679
|
+
"Program",
|
|
2680
|
+
"StaticBlock",
|
|
2681
|
+
"SwitchCase",
|
|
2682
|
+
"SwitchStatement",
|
|
2683
|
+
"TSInterfaceBody",
|
|
2684
|
+
"TSModuleBlock",
|
|
2685
|
+
"TSTypeLiteral"
|
|
2686
|
+
].includes(node.parent.type)) {
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
const prevNode = scopeInfo.prevNode;
|
|
2690
|
+
if (prevNode)
|
|
2691
|
+
pendingPairs.push({ prevNode, nextNode: node });
|
|
2692
|
+
scopeInfo.prevNode = node;
|
|
2693
|
+
}
|
|
2694
|
+
function verifyPendingPairs() {
|
|
2695
|
+
for (const { prevNode, nextNode } of pendingPairs) {
|
|
2696
|
+
const type = getPaddingType(prevNode, nextNode);
|
|
2697
|
+
const paddingLines = getPaddingLineSequences(prevNode, nextNode);
|
|
2698
|
+
type.verify(context, prevNode, nextNode, paddingLines);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
function verifyThenEnterScope(node) {
|
|
2702
|
+
verify(node);
|
|
2703
|
+
enterScope();
|
|
2704
|
+
}
|
|
2705
|
+
const selectorMatchListeners = Object.fromEntries(Array.from(selectorMatchedNodes.keys(), (selector) => [
|
|
2706
|
+
selector,
|
|
2707
|
+
(node) => {
|
|
2708
|
+
selectorMatchedNodes.get(selector)?.add(node);
|
|
2709
|
+
}
|
|
2710
|
+
]));
|
|
2711
|
+
return {
|
|
2712
|
+
Program: enterScope,
|
|
2713
|
+
"Program:exit": () => {
|
|
2714
|
+
verifyPendingPairs();
|
|
2715
|
+
exitScope();
|
|
2716
|
+
},
|
|
2717
|
+
BlockStatement: enterScope,
|
|
2718
|
+
"BlockStatement:exit": exitScope,
|
|
2719
|
+
SwitchStatement: enterScope,
|
|
2720
|
+
"SwitchStatement:exit": exitScope,
|
|
2721
|
+
SwitchCase: verifyThenEnterScope,
|
|
2722
|
+
"SwitchCase:exit": exitScope,
|
|
2723
|
+
StaticBlock: enterScope,
|
|
2724
|
+
"StaticBlock:exit": exitScope,
|
|
2725
|
+
TSInterfaceBody: enterScope,
|
|
2726
|
+
"TSInterfaceBody:exit": exitScope,
|
|
2727
|
+
TSModuleBlock: enterScope,
|
|
2728
|
+
"TSModuleBlock:exit": exitScope,
|
|
2729
|
+
TSTypeLiteral: enterScope,
|
|
2730
|
+
"TSTypeLiteral:exit": exitScope,
|
|
2731
|
+
TSDeclareFunction: verifyThenEnterScope,
|
|
2732
|
+
"TSDeclareFunction:exit": exitScope,
|
|
2733
|
+
TSMethodSignature: verifyThenEnterScope,
|
|
2734
|
+
"TSMethodSignature:exit": exitScope,
|
|
2735
|
+
":statement": verify,
|
|
2736
|
+
...selectorMatchListeners
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
};
|
|
1653
2740
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
2741
|
+
|
|
2742
|
+
// vendor/anti-slop/src/rules/require-readable-spacing.ts
|
|
2743
|
+
var paddingRule = createPaddingLineRule([
|
|
2744
|
+
{ blankLine: "always", prev: "import", next: "*" },
|
|
2745
|
+
{ blankLine: "always", prev: "*", next: { selector: "Program > :not(ImportDeclaration)" } },
|
|
2746
|
+
{ blankLine: "always", prev: { selector: "Program > :not(ImportDeclaration)" }, next: "*" },
|
|
2747
|
+
{ blankLine: "always", prev: "*", next: ["function", "class", "interface", "type"] },
|
|
2748
|
+
{ blankLine: "always", prev: ["function", "class", "interface", "type"], next: "*" },
|
|
2749
|
+
{
|
|
2750
|
+
blankLine: "always",
|
|
2751
|
+
prev: "*",
|
|
2752
|
+
next: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"]
|
|
2753
|
+
},
|
|
2754
|
+
{
|
|
2755
|
+
blankLine: "always",
|
|
2756
|
+
prev: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"],
|
|
2757
|
+
next: "*"
|
|
2758
|
+
},
|
|
2759
|
+
{ blankLine: "always", prev: "*", next: ["return", "if", "switch", "try", "for", "while", "do"] },
|
|
2760
|
+
{ blankLine: "always", prev: "block-like", next: "*" },
|
|
2761
|
+
{ blankLine: "any", prev: "import", next: "import" },
|
|
2762
|
+
{
|
|
2763
|
+
blankLine: "any",
|
|
2764
|
+
prev: {
|
|
2765
|
+
selector: ':matches(TSDeclareFunction, ExportNamedDeclaration[declaration.type="TSDeclareFunction"])'
|
|
2766
|
+
},
|
|
2767
|
+
next: {
|
|
2768
|
+
selector: ':matches(TSDeclareFunction, FunctionDeclaration, ExportNamedDeclaration[declaration.type="TSDeclareFunction"], ExportNamedDeclaration[declaration.type="FunctionDeclaration"])'
|
|
2769
|
+
}
|
|
1658
2770
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
const initializerBroadKind = initializerAssertion === null ? null : broadTypeKind(initializerAssertion.typeAnnotation);
|
|
1663
|
-
const declaredBroadKind = declaredType === undefined ? null : broadTypeKind(declaredType);
|
|
1664
|
-
const broadKind = declaredBroadKind ?? initializerBroadKind;
|
|
1665
|
-
if (broadKind === null)
|
|
1666
|
-
return null;
|
|
1667
|
-
const originalExpression = initializerAssertion !== null && initializerBroadKind !== null ? assertedExpression(initializerAssertion) : declarator.init;
|
|
1668
|
-
const evidence = knownValueEvidence(originalExpression, scopes, boundary, new Set([variable]));
|
|
1669
|
-
return evidence === null ? null : { broadKind, evidence, declaredAt: declarator.end, boundary };
|
|
1670
|
-
}
|
|
1671
|
-
function assertionIsNarrower(sourceText, broadKind, evidence, assertedType) {
|
|
1672
|
-
if (broadTypeKind(assertedType) !== null)
|
|
1673
|
-
return false;
|
|
1674
|
-
if (broadKind === "top")
|
|
1675
|
-
return true;
|
|
1676
|
-
if (typesHaveSameSyntax(sourceText, evidence.type, assertedType))
|
|
1677
|
-
return true;
|
|
1678
|
-
if (broadKind === "object")
|
|
1679
|
-
return isDefinitelyObjectType(assertedType);
|
|
1680
|
-
return isDefinitelyNarrowerRecordType(assertedType);
|
|
1681
|
-
}
|
|
1682
|
-
var noWidenThenAssertRule = defineRule14({
|
|
2771
|
+
]);
|
|
2772
|
+
var requireReadableSpacingRule = {
|
|
2773
|
+
...paddingRule,
|
|
1683
2774
|
meta: {
|
|
1684
|
-
|
|
2775
|
+
...paddingRule.meta,
|
|
1685
2776
|
docs: {
|
|
1686
|
-
description: "
|
|
2777
|
+
description: "Require readable spacing between declarations and logical statement groups."
|
|
1687
2778
|
},
|
|
1688
|
-
|
|
1689
|
-
widenThenAssert: 'Binding "{{name}}" discards type evidence and later recreates it with an assertion. Keep the precise type from initialization through use; parse boundary input once.'
|
|
1690
|
-
}
|
|
1691
|
-
},
|
|
1692
|
-
createOnce(context) {
|
|
1693
|
-
let scopes = [];
|
|
1694
|
-
const checkAssertion = (node) => {
|
|
1695
|
-
const expression = assertedExpression(node);
|
|
1696
|
-
if (expression.type !== "Identifier")
|
|
1697
|
-
return;
|
|
1698
|
-
const variable = resolvedVariableForIdentifier(scopes, expression);
|
|
1699
|
-
if (variable === null)
|
|
1700
|
-
return;
|
|
1701
|
-
const widened = widenedBinding(variable, scopes);
|
|
1702
|
-
if (widened === null || node.start <= widened.declaredAt || functionBoundary(node) !== widened.boundary || !assertionIsNarrower(context.sourceCode.text, widened.broadKind, widened.evidence, node.typeAnnotation)) {
|
|
1703
|
-
return;
|
|
1704
|
-
}
|
|
1705
|
-
context.report({
|
|
1706
|
-
node,
|
|
1707
|
-
messageId: "widenThenAssert",
|
|
1708
|
-
data: { name: expression.name }
|
|
1709
|
-
});
|
|
1710
|
-
};
|
|
1711
|
-
return {
|
|
1712
|
-
Program() {
|
|
1713
|
-
scopes = context.sourceCode.scopeManager.scopes;
|
|
1714
|
-
},
|
|
1715
|
-
TSAsExpression: checkAssertion,
|
|
1716
|
-
TSTypeAssertion: checkAssertion
|
|
1717
|
-
};
|
|
2779
|
+
schema: []
|
|
1718
2780
|
}
|
|
1719
|
-
}
|
|
2781
|
+
};
|
|
1720
2782
|
|
|
1721
2783
|
// vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
|
|
1722
|
-
import { defineRule as
|
|
2784
|
+
import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
1723
2785
|
var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
|
|
1724
2786
|
var commentOwnerKinds = new Set([
|
|
1725
2787
|
"ExpressionStatement",
|
|
@@ -1762,7 +2824,7 @@ function hasSafetyComment(sourceCode, node, pattern) {
|
|
|
1762
2824
|
current = current.parent;
|
|
1763
2825
|
}
|
|
1764
2826
|
}
|
|
1765
|
-
var requireSafetyCommentForTypeAssertionRule =
|
|
2827
|
+
var requireSafetyCommentForTypeAssertionRule = defineRule23({
|
|
1766
2828
|
meta: {
|
|
1767
2829
|
type: "problem",
|
|
1768
2830
|
docs: {
|
|
@@ -1812,9 +2874,11 @@ var requireSafetyCommentForTypeAssertionRule = defineRule15({
|
|
|
1812
2874
|
});
|
|
1813
2875
|
|
|
1814
2876
|
// vendor/anti-slop/src/index.ts
|
|
1815
|
-
var antiSlopPlugin =
|
|
2877
|
+
var antiSlopPlugin = eslintCompatPlugin3({
|
|
1816
2878
|
meta: { name: "anti-slop" },
|
|
1817
2879
|
rules: {
|
|
2880
|
+
"no-array-filter-map": noArrayFilterMapRule,
|
|
2881
|
+
"no-reduce-accumulator-copy": noReduceAccumulatorCopyRule,
|
|
1818
2882
|
"no-chained-type-assertions": noChainedTypeAssertionsRule,
|
|
1819
2883
|
"no-conditional-empty-object-spread": noConditionalEmptyObjectSpreadRule,
|
|
1820
2884
|
"no-known-value-widening": noKnownValueWideningRule,
|
|
@@ -1829,16 +2893,17 @@ var antiSlopPlugin = eslintCompatPlugin({
|
|
|
1829
2893
|
"no-unknown-returns": noUnknownReturnsRule,
|
|
1830
2894
|
"no-unknown-type-aliases": noUnknownTypeAliasesRule,
|
|
1831
2895
|
"no-widen-then-assert": noWidenThenAssertRule,
|
|
2896
|
+
"require-readable-spacing": requireReadableSpacingRule,
|
|
1832
2897
|
"require-safety-comment-for-type-assertion": requireSafetyCommentForTypeAssertionRule
|
|
1833
2898
|
}
|
|
1834
2899
|
});
|
|
1835
2900
|
var src_default = antiSlopPlugin;
|
|
1836
2901
|
// src/generic/index.ts
|
|
1837
|
-
import { eslintCompatPlugin as
|
|
2902
|
+
import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
|
|
1838
2903
|
|
|
1839
2904
|
// src/generic/rules/prefer-event-parameter-type.ts
|
|
1840
|
-
import { defineRule as
|
|
1841
|
-
function
|
|
2905
|
+
import { defineRule as defineRule24 } from "@oxlint/plugins";
|
|
2906
|
+
function nearestEnclosingFunction2(node) {
|
|
1842
2907
|
let current = node.parent;
|
|
1843
2908
|
while (current) {
|
|
1844
2909
|
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
@@ -1855,7 +2920,7 @@ function assertedEventParameter(node) {
|
|
|
1855
2920
|
}
|
|
1856
2921
|
const parameterName = expression.object.name;
|
|
1857
2922
|
const property = expression.property.name;
|
|
1858
|
-
const owner =
|
|
2923
|
+
const owner = nearestEnclosingFunction2(node);
|
|
1859
2924
|
if (!owner?.params.some((parameter) => parameter.type === "Identifier" && parameter.name === parameterName)) {
|
|
1860
2925
|
return null;
|
|
1861
2926
|
}
|
|
@@ -1864,7 +2929,7 @@ function assertedEventParameter(node) {
|
|
|
1864
2929
|
property
|
|
1865
2930
|
};
|
|
1866
2931
|
}
|
|
1867
|
-
var preferEventParameterTypeRule =
|
|
2932
|
+
var preferEventParameterTypeRule = defineRule24({
|
|
1868
2933
|
meta: {
|
|
1869
2934
|
type: "suggestion",
|
|
1870
2935
|
docs: {
|
|
@@ -1893,7 +2958,7 @@ var preferEventParameterTypeRule = defineRule16({
|
|
|
1893
2958
|
});
|
|
1894
2959
|
|
|
1895
2960
|
// src/generic/index.ts
|
|
1896
|
-
var timmoPlugin =
|
|
2961
|
+
var timmoPlugin = eslintCompatPlugin4({
|
|
1897
2962
|
meta: { name: "timmo" },
|
|
1898
2963
|
rules: {
|
|
1899
2964
|
"prefer-event-parameter-type": preferEventParameterTypeRule
|
|
@@ -1901,16 +2966,6 @@ var timmoPlugin = eslintCompatPlugin2({
|
|
|
1901
2966
|
});
|
|
1902
2967
|
var generic_default = timmoPlugin;
|
|
1903
2968
|
|
|
1904
|
-
// node_modules/oxlint/dist/index.js
|
|
1905
|
-
function defineConfig(config) {
|
|
1906
|
-
return config;
|
|
1907
|
-
}
|
|
1908
|
-
|
|
1909
|
-
// src/configs/enable-plugin-rules.ts
|
|
1910
|
-
function enablePluginRules(namespace, plugin) {
|
|
1911
|
-
return Object.fromEntries(Object.keys(plugin.rules).sort().map((rule) => [`${namespace}/${rule}`, "error"]));
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
2969
|
// src/configs/recommended.ts
|
|
1915
2970
|
var recommended = defineConfig({
|
|
1916
2971
|
jsPlugins: [
|
|
@@ -1927,166 +2982,6 @@ var recommended = defineConfig({
|
|
|
1927
2982
|
});
|
|
1928
2983
|
var recommended_default = recommended;
|
|
1929
2984
|
|
|
1930
|
-
// src/effect/index.ts
|
|
1931
|
-
import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
|
|
1932
|
-
|
|
1933
|
-
// src/effect/rules/no-try-catch-in-effect-generators.ts
|
|
1934
|
-
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
1935
|
-
function resolveVariable4(sourceCode, identifier) {
|
|
1936
|
-
let scope = sourceCode.getScope(identifier);
|
|
1937
|
-
while (scope) {
|
|
1938
|
-
const variable = scope.set.get(identifier.name);
|
|
1939
|
-
if (variable)
|
|
1940
|
-
return variable;
|
|
1941
|
-
scope = scope.upper;
|
|
1942
|
-
}
|
|
1943
|
-
return null;
|
|
1944
|
-
}
|
|
1945
|
-
function nearestEnclosingFunction2(node) {
|
|
1946
|
-
let current = node.parent;
|
|
1947
|
-
while (current) {
|
|
1948
|
-
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
1949
|
-
return current;
|
|
1950
|
-
}
|
|
1951
|
-
current = current.parent;
|
|
1952
|
-
}
|
|
1953
|
-
return null;
|
|
1954
|
-
}
|
|
1955
|
-
function staticMemberName(node) {
|
|
1956
|
-
if (node.type !== "MemberExpression" || node.computed)
|
|
1957
|
-
return null;
|
|
1958
|
-
return node.property.type === "Identifier" ? node.property.name : null;
|
|
1959
|
-
}
|
|
1960
|
-
function isEffectImport(sourceCode, identifier, namespace) {
|
|
1961
|
-
const variable = resolveVariable4(sourceCode, identifier);
|
|
1962
|
-
return variable?.defs.some((definition) => {
|
|
1963
|
-
if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== "effect") {
|
|
1964
|
-
return false;
|
|
1965
|
-
}
|
|
1966
|
-
if (namespace)
|
|
1967
|
-
return definition.node.type === "ImportNamespaceSpecifier";
|
|
1968
|
-
if (definition.node.type !== "ImportSpecifier")
|
|
1969
|
-
return false;
|
|
1970
|
-
const imported = definition.node.imported;
|
|
1971
|
-
return (imported.type === "Identifier" ? imported.name : imported.value) === "Effect";
|
|
1972
|
-
}) ?? false;
|
|
1973
|
-
}
|
|
1974
|
-
function isEffectMethod(sourceCode, node, method) {
|
|
1975
|
-
if (staticMemberName(node) !== method || node.type !== "MemberExpression") {
|
|
1976
|
-
return false;
|
|
1977
|
-
}
|
|
1978
|
-
const object = node.object;
|
|
1979
|
-
if (object.type === "Identifier") {
|
|
1980
|
-
return isEffectImport(sourceCode, object, false);
|
|
1981
|
-
}
|
|
1982
|
-
if (staticMemberName(object) !== "Effect" || object.type !== "MemberExpression" || object.object.type !== "Identifier") {
|
|
1983
|
-
return false;
|
|
1984
|
-
}
|
|
1985
|
-
return isEffectImport(sourceCode, object.object, true);
|
|
1986
|
-
}
|
|
1987
|
-
function isDirectArgument(owner, call) {
|
|
1988
|
-
return call.arguments.some((argument) => argument === owner);
|
|
1989
|
-
}
|
|
1990
|
-
function isRecognisedEffectGenerator(sourceCode, owner) {
|
|
1991
|
-
const parent = owner.parent;
|
|
1992
|
-
if (parent.type !== "CallExpression" || !isDirectArgument(owner, parent)) {
|
|
1993
|
-
return false;
|
|
1994
|
-
}
|
|
1995
|
-
if (isEffectMethod(sourceCode, parent.callee, "gen"))
|
|
1996
|
-
return true;
|
|
1997
|
-
const factoryCall = parent.callee;
|
|
1998
|
-
return factoryCall.type === "CallExpression" && isEffectMethod(sourceCode, factoryCall.callee, "fn");
|
|
1999
|
-
}
|
|
2000
|
-
var noTryCatchInEffectGeneratorsRule = defineRule17({
|
|
2001
|
-
meta: {
|
|
2002
|
-
type: "problem",
|
|
2003
|
-
docs: {
|
|
2004
|
-
description: "Disallow synchronous try/catch owned by recognised Effect generator callbacks."
|
|
2005
|
-
},
|
|
2006
|
-
messages: {
|
|
2007
|
-
useEffectErrorChannel: "Keep expected failures in the Effect error channel. Use Effect.try for synchronous throwing work, Effect.tryPromise for asynchronous throwing work, Effect-returning schema APIs for decoding, and Effect recovery combinators for recovery."
|
|
2008
|
-
}
|
|
2009
|
-
},
|
|
2010
|
-
create(context) {
|
|
2011
|
-
return {
|
|
2012
|
-
TryStatement(node) {
|
|
2013
|
-
if (!node.handler)
|
|
2014
|
-
return;
|
|
2015
|
-
const owner = nearestEnclosingFunction2(node);
|
|
2016
|
-
if (!owner?.generator || !isRecognisedEffectGenerator(context.sourceCode, owner)) {
|
|
2017
|
-
return;
|
|
2018
|
-
}
|
|
2019
|
-
context.report({ node, messageId: "useEffectErrorChannel" });
|
|
2020
|
-
}
|
|
2021
|
-
};
|
|
2022
|
-
}
|
|
2023
|
-
});
|
|
2024
|
-
|
|
2025
|
-
// src/effect/index.ts
|
|
2026
|
-
var timmoEffectPlugin = eslintCompatPlugin3({
|
|
2027
|
-
meta: { name: "timmo-effect" },
|
|
2028
|
-
rules: {
|
|
2029
|
-
"no-try-catch-in-effect-generators": noTryCatchInEffectGeneratorsRule
|
|
2030
|
-
}
|
|
2031
|
-
});
|
|
2032
|
-
var effect_default = timmoEffectPlugin;
|
|
2033
|
-
|
|
2034
|
-
// vendor/anti-slop/src/effect/index.ts
|
|
2035
|
-
import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
|
|
2036
|
-
|
|
2037
|
-
// vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
|
|
2038
|
-
import { defineRule as defineRule18 } from "@oxlint/plugins";
|
|
2039
|
-
var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
|
|
2040
|
-
var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
|
|
2041
|
-
function isProjectLocalImport(source) {
|
|
2042
|
-
return source.startsWith("./") || source.startsWith("../");
|
|
2043
|
-
}
|
|
2044
|
-
function getImportedName(specifier) {
|
|
2045
|
-
if (specifier.imported.type === "Identifier")
|
|
2046
|
-
return specifier.imported.name;
|
|
2047
|
-
return specifier.imported.value;
|
|
2048
|
-
}
|
|
2049
|
-
var noServiceConstructorImportsRule = defineRule18({
|
|
2050
|
-
meta: {
|
|
2051
|
-
type: "problem",
|
|
2052
|
-
docs: {
|
|
2053
|
-
description: "Disallow project-local make<CapabilityName> imports outside test and spec files."
|
|
2054
|
-
},
|
|
2055
|
-
messages: {
|
|
2056
|
-
serviceConstructorImport: 'Do not import Effect service constructor "{{name}}" into runtime code. Import the owning Layer, yield the contextual service, and allow its requirements to propagate to the composition root.'
|
|
2057
|
-
}
|
|
2058
|
-
},
|
|
2059
|
-
create(context) {
|
|
2060
|
-
const isTestFile = TEST_FILE.test(context.filename.replaceAll("\\", "/"));
|
|
2061
|
-
return {
|
|
2062
|
-
ImportDeclaration(node) {
|
|
2063
|
-
if (isTestFile || !isProjectLocalImport(node.source.value))
|
|
2064
|
-
return;
|
|
2065
|
-
for (const specifier of node.specifiers) {
|
|
2066
|
-
if (specifier.type !== "ImportSpecifier")
|
|
2067
|
-
continue;
|
|
2068
|
-
const importedName2 = getImportedName(specifier);
|
|
2069
|
-
if (!SERVICE_CONSTRUCTOR_NAME.test(importedName2))
|
|
2070
|
-
continue;
|
|
2071
|
-
context.report({
|
|
2072
|
-
node: specifier,
|
|
2073
|
-
messageId: "serviceConstructorImport",
|
|
2074
|
-
data: { name: importedName2 }
|
|
2075
|
-
});
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
};
|
|
2079
|
-
}
|
|
2080
|
-
});
|
|
2081
|
-
|
|
2082
|
-
// vendor/anti-slop/src/effect/index.ts
|
|
2083
|
-
var antiSlopEffectPlugin = eslintCompatPlugin4({
|
|
2084
|
-
meta: { name: "anti-slop-effect" },
|
|
2085
|
-
rules: {
|
|
2086
|
-
"no-service-constructor-imports": noServiceConstructorImportsRule
|
|
2087
|
-
}
|
|
2088
|
-
});
|
|
2089
|
-
var effect_default2 = antiSlopEffectPlugin;
|
|
2090
2985
|
// src/configs/recommended-effect.ts
|
|
2091
2986
|
var recommendedEffect = defineConfig({
|
|
2092
2987
|
extends: [recommended_default],
|