@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.
Files changed (28) hide show
  1. package/README.md +2 -1
  2. package/THIRD_PARTY_NOTICES.md +9 -0
  3. package/dist/cli.js +978 -83
  4. package/dist/configs/recommended-effect.js +1207 -312
  5. package/dist/configs/recommended.js +772 -70
  6. package/dist/upstream/anti-slop.js +765 -63
  7. package/dist/upstream/effect.js +196 -3
  8. package/package.json +5 -5
  9. package/vendor/anti-slop/src/effect/index.ts +8 -0
  10. package/vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts +52 -0
  11. package/vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts +45 -0
  12. package/vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts +37 -0
  13. package/vendor/anti-slop/src/effect/rules/prefer-effect-match.ts +54 -0
  14. package/vendor/anti-slop/src/effect/shared/tagged-values.ts +97 -0
  15. package/vendor/anti-slop/src/index.ts +6 -0
  16. package/vendor/anti-slop/src/rules/no-array-filter-map.ts +28 -0
  17. package/vendor/anti-slop/src/rules/no-known-value-widening.ts +2 -14
  18. package/vendor/anti-slop/src/rules/no-module-mocking.ts +3 -14
  19. package/vendor/anti-slop/src/rules/no-reduce-accumulator-copy.ts +109 -0
  20. package/vendor/anti-slop/src/rules/require-readable-spacing.ts +47 -0
  21. package/vendor/anti-slop/src/shared/array-method.ts +94 -0
  22. package/vendor/anti-slop/src/shared/reflect-method.ts +2 -13
  23. package/vendor/anti-slop/src/shared/scope.ts +15 -0
  24. package/vendor/anti-slop/src/vendor/eslint-stylistic/LICENSE +22 -0
  25. package/vendor/anti-slop/src/vendor/eslint-stylistic/UPSTREAM.md +28 -0
  26. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts +51 -0
  27. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts +906 -0
  28. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-options.d.ts +87 -0
@@ -1,8 +1,566 @@
1
- // vendor/anti-slop/src/index.ts
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 = defineRule({
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 defineRule2 } from "@oxlint/plugins";
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 = defineRule2({
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 defineRule3 } from "@oxlint/plugins";
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 substitutions2 = aliasSubstitution(alias, unwrapped, new Map);
507
- const resolved2 = substitutions2 === null ? null : classifyAliasBroadTarget(alias.typeAnnotation, environment, substitutions2, new Set([name]));
508
- return resolved2?.kind === "open dictionary" ? { kind: "generic container" } : null;
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/rules/no-known-value-widening.ts
633
- function unwrapExpression(expression) {
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 = resolveVariable(sourceCode, unwrapped);
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 = resolveVariable(sourceCode, unwrapped);
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 = resolveVariable(sourceCode, expression);
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 = defineRule3({
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 = resolveVariable(context.sourceCode, node.left);
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 defineRule4 } from "@oxlint/plugins";
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 = defineRule4({
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 defineRule5 } from "@oxlint/plugins";
985
- var noObjectParametersRule = defineRule5({
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 defineRule6 } from "@oxlint/plugins";
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 = resolveVariable3(sourceCode, expression);
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 = defineRule6({
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 defineRule7 } from "@oxlint/plugins";
1094
- var noReflectGetRule = defineRule7({
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 defineRule8 } from "@oxlint/plugins";
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 = defineRule8({
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 defineRule9 } from "@oxlint/plugins";
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 = defineRule9({
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 defineRule10 } from "@oxlint/plugins";
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 = defineRule10({
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 defineRule11 } from "@oxlint/plugins";
1265
- var noUnknownReturnsRule = defineRule11({
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 defineRule12 } from "@oxlint/plugins";
1319
- var noUnknownTypeAliasesRule = defineRule12({
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 defineRule13 } from "@oxlint/plugins";
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 = defineRule13({
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 defineRule14 } from "@oxlint/plugins";
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
- function widenedBinding(variable, scopes) {
1655
- const declarator = variableDeclarator2(variable);
1656
- 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)) {
1657
- return null;
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
- const boundary = functionBoundary(declarator);
1660
- const declaredType = declarator.id.typeAnnotation?.typeAnnotation;
1661
- const initializerAssertion = assertionFromExpression(declarator.init);
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
- type: "problem",
2775
+ ...paddingRule.meta,
1685
2776
  docs: {
1686
- description: "Disallow local const flows that explicitly widen a known value before asserting the widened binding to a narrower type."
2777
+ description: "Require readable spacing between declarations and logical statement groups."
1687
2778
  },
1688
- messages: {
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 defineRule15 } from "@oxlint/plugins";
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 = defineRule15({
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 = eslintCompatPlugin({
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 eslintCompatPlugin2 } from "@oxlint/plugins";
2902
+ import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
1838
2903
 
1839
2904
  // src/generic/rules/prefer-event-parameter-type.ts
1840
- import { defineRule as defineRule16 } from "@oxlint/plugins";
1841
- function nearestEnclosingFunction(node) {
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 = nearestEnclosingFunction(node);
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 = defineRule16({
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 = eslintCompatPlugin2({
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],