@timmo001/oxlint-rules 0.3.1 → 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 (25) hide show
  1. package/README.md +2 -1
  2. package/THIRD_PARTY_NOTICES.md +9 -0
  3. package/dist/cli.js +733 -35
  4. package/dist/configs/recommended-effect.js +767 -69
  5. package/dist/configs/recommended.js +535 -30
  6. package/dist/upstream/anti-slop.js +535 -30
  7. package/dist/upstream/effect.js +196 -3
  8. package/package.json +1 -1
  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 +2 -0
  16. package/vendor/anti-slop/src/rules/no-known-value-widening.ts +2 -14
  17. package/vendor/anti-slop/src/rules/no-module-mocking.ts +3 -14
  18. package/vendor/anti-slop/src/rules/require-readable-spacing.ts +47 -0
  19. package/vendor/anti-slop/src/shared/reflect-method.ts +2 -13
  20. package/vendor/anti-slop/src/shared/scope.ts +15 -0
  21. package/vendor/anti-slop/src/vendor/eslint-stylistic/LICENSE +22 -0
  22. package/vendor/anti-slop/src/vendor/eslint-stylistic/UPSTREAM.md +28 -0
  23. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts +51 -0
  24. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts +906 -0
  25. package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-options.d.ts +87 -0
package/dist/cli.js CHANGED
@@ -834,14 +834,7 @@ function functionParameterBindingName(parameter, sourceCode) {
834
834
  return annotationStart === undefined ? sourceText : sourceText.slice(0, annotationStart - parameter.start).trimEnd();
835
835
  }
836
836
 
837
- // vendor/anti-slop/src/rules/no-known-value-widening.ts
838
- function unwrapExpression(expression) {
839
- let current = expression;
840
- while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
841
- current = current.expression;
842
- }
843
- return current;
844
- }
837
+ // vendor/anti-slop/src/shared/scope.ts
845
838
  function resolveVariable(sourceCode, identifier) {
846
839
  let scope = sourceCode.getScope(identifier);
847
840
  while (scope !== null) {
@@ -852,6 +845,15 @@ function resolveVariable(sourceCode, identifier) {
852
845
  }
853
846
  return null;
854
847
  }
848
+
849
+ // vendor/anti-slop/src/rules/no-known-value-widening.ts
850
+ function unwrapExpression(expression) {
851
+ let current = expression;
852
+ while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
853
+ current = current.expression;
854
+ }
855
+ return current;
856
+ }
855
857
  function variableDeclarator(variable) {
856
858
  if (variable.defs.length !== 1)
857
859
  return null;
@@ -1119,16 +1121,6 @@ var noKnownValueWideningRule = defineRule5({
1119
1121
  // vendor/anti-slop/src/rules/no-module-mocking.ts
1120
1122
  import { defineRule as defineRule6 } from "@oxlint/plugins";
1121
1123
  var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
1122
- function resolveVariable2(sourceCode, identifier) {
1123
- let scope = sourceCode.getScope(identifier);
1124
- while (scope !== null) {
1125
- const variable = scope.set.get(identifier.name);
1126
- if (variable !== undefined)
1127
- return variable;
1128
- scope = scope.upper;
1129
- }
1130
- return null;
1131
- }
1132
1124
  function importedName(node) {
1133
1125
  if (node.type !== "ImportSpecifier")
1134
1126
  return null;
@@ -1140,7 +1132,7 @@ function isTestFrameworkObject(sourceCode, expression) {
1140
1132
  if ((expression.name === "vi" || expression.name === "jest") && sourceCode.isGlobalReference(expression)) {
1141
1133
  return true;
1142
1134
  }
1143
- const variable = resolveVariable2(sourceCode, expression);
1135
+ const variable = resolveVariable(sourceCode, expression);
1144
1136
  if (variable === null || variable.defs.length === 0) {
1145
1137
  return expression.name === "vi" || expression.name === "jest";
1146
1138
  }
@@ -1243,22 +1235,12 @@ var noObjectParametersRule = defineRule7({
1243
1235
  import { defineRule as defineRule8 } from "@oxlint/plugins";
1244
1236
 
1245
1237
  // vendor/anti-slop/src/shared/reflect-method.ts
1246
- function resolveVariable3(sourceCode, identifier) {
1247
- let scope = sourceCode.getScope(identifier);
1248
- while (scope !== null) {
1249
- const variable = scope.set.get(identifier.name);
1250
- if (variable !== undefined)
1251
- return variable;
1252
- scope = scope.upper;
1253
- }
1254
- return null;
1255
- }
1256
1238
  function isGlobalReflect(sourceCode, expression) {
1257
1239
  if (expression.type !== "Identifier" || expression.name !== "Reflect")
1258
1240
  return false;
1259
1241
  if (sourceCode.isGlobalReference(expression))
1260
1242
  return true;
1261
- const variable = resolveVariable3(sourceCode, expression);
1243
+ const variable = resolveVariable(sourceCode, expression);
1262
1244
  return variable === null || variable.defs.length === 0;
1263
1245
  }
1264
1246
  function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
@@ -1923,6 +1905,528 @@ var noWidenThenAssertRule = defineRule16({
1923
1905
  }
1924
1906
  });
1925
1907
 
1908
+ // vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts
1909
+ var LINEBREAKS = new Set([`\r
1910
+ `, "\r", `
1911
+ `, "\u2028", "\u2029"]);
1912
+ var isClosingBraceToken = (token) => token.type === "Punctuator" && token.value === "}";
1913
+ var isSemicolonToken = (token) => token.type === "Punctuator" && token.value === ";";
1914
+ var isNotSemicolonToken = (token) => !isSemicolonToken(token);
1915
+ var isTokenOnSameLine = (left, right) => left.loc.end.line === right.loc.start.line;
1916
+ var isFunction = (node) => node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
1917
+ var isSingleLine = (node) => node.loc.start.line === node.loc.end.line;
1918
+ var skipChainExpression = (node) => node.type === "ChainExpression" ? node.expression : node;
1919
+ var isTopLevelExpressionStatement = (node) => node.type === "ExpressionStatement" && (node.parent.type === "Program" || node.parent.type === "BlockStatement" && isFunction(node.parent.parent));
1920
+ function isParenthesized(node, sourceCode) {
1921
+ const before = sourceCode.getTokenBefore(node);
1922
+ const after = sourceCode.getTokenAfter(node);
1923
+ return before?.value === "(" && after?.value === ")";
1924
+ }
1925
+
1926
+ // vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts
1927
+ var CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
1928
+ var CJS_IMPORT = /^require\(/u;
1929
+ var LT = `[${Array.from(LINEBREAKS).join("")}]`;
1930
+ var PADDING_LINE_SEQUENCE = new RegExp(String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, "u");
1931
+ function isSelectorOption(option) {
1932
+ return typeof option === "object" && !Array.isArray(option);
1933
+ }
1934
+ function newKeywordTester(type, keyword) {
1935
+ return {
1936
+ test(node, sourceCode) {
1937
+ const isSameKeyword = sourceCode.getFirstToken(node)?.value === keyword;
1938
+ const isSameType = Array.isArray(type) ? type.includes(node.type) : type === node.type;
1939
+ return isSameKeyword && isSameType;
1940
+ }
1941
+ };
1942
+ }
1943
+ function newNodeTypeTester(type) {
1944
+ return {
1945
+ test: (node) => node.type === type
1946
+ };
1947
+ }
1948
+ function isIIFEStatement(node) {
1949
+ if (node.type === "ExpressionStatement") {
1950
+ let expression = skipChainExpression(node.expression);
1951
+ if (expression.type === "UnaryExpression")
1952
+ expression = skipChainExpression(expression.argument);
1953
+ if (expression.type === "CallExpression") {
1954
+ let node = expression.callee;
1955
+ while (node.type === "SequenceExpression") {
1956
+ const lastExpression = node.expressions.at(-1);
1957
+ if (lastExpression === undefined)
1958
+ throw new Error("Padding rule invariant: sequence expression is empty");
1959
+ node = lastExpression;
1960
+ }
1961
+ return isFunction(node);
1962
+ }
1963
+ }
1964
+ return false;
1965
+ }
1966
+ function isCJSRequire(node) {
1967
+ if (node.type === "VariableDeclaration") {
1968
+ const declaration = node.declarations[0];
1969
+ if (declaration?.init) {
1970
+ let call = declaration?.init;
1971
+ while (call.type === "MemberExpression")
1972
+ call = call.object;
1973
+ if (call.type === "CallExpression" && call.callee.type === "Identifier") {
1974
+ return call.callee.name === "require";
1975
+ }
1976
+ }
1977
+ }
1978
+ return false;
1979
+ }
1980
+ function isBlockLikeStatement(node, sourceCode) {
1981
+ if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") {
1982
+ return true;
1983
+ }
1984
+ if (isIIFEStatement(node))
1985
+ return true;
1986
+ const lastToken = sourceCode.getLastToken(node, isNotSemicolonToken);
1987
+ const belongingNode = lastToken && isClosingBraceToken(lastToken) ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) : null;
1988
+ return !!belongingNode && (belongingNode.type === "BlockStatement" || belongingNode.type === "SwitchStatement");
1989
+ }
1990
+ function isDirective(node, sourceCode) {
1991
+ return isTopLevelExpressionStatement(node) && node.expression.type === "Literal" && typeof node.expression.value === "string" && !isParenthesized(node.expression, sourceCode);
1992
+ }
1993
+ function isDirectivePrologue(node, sourceCode) {
1994
+ if (isDirective(node, sourceCode) && node.parent && "body" in node.parent && Array.isArray(node.parent.body)) {
1995
+ for (const sibling of node.parent.body) {
1996
+ if (sibling === node)
1997
+ break;
1998
+ if (!isDirective(sibling, sourceCode))
1999
+ return false;
2000
+ }
2001
+ return true;
2002
+ }
2003
+ return false;
2004
+ }
2005
+ function isCJSExport(node) {
2006
+ if (node.type === "ExpressionStatement") {
2007
+ const expression = node.expression;
2008
+ if (expression.type === "AssignmentExpression") {
2009
+ let left = expression.left;
2010
+ if (left.type === "MemberExpression") {
2011
+ while (left.object.type === "MemberExpression")
2012
+ left = left.object;
2013
+ return left.object.type === "Identifier" && (left.object.name === "exports" || left.object.name === "module" && left.property.type === "Identifier" && left.property.name === "exports");
2014
+ }
2015
+ }
2016
+ }
2017
+ return false;
2018
+ }
2019
+ function isExpression(node, sourceCode) {
2020
+ return node.type === "ExpressionStatement" && !isDirectivePrologue(node, sourceCode);
2021
+ }
2022
+ function getActualLastToken(node, sourceCode) {
2023
+ const semiToken = sourceCode.getLastToken(node);
2024
+ const prevToken = sourceCode.getTokenBefore(semiToken);
2025
+ const nextToken = sourceCode.getTokenAfter(semiToken);
2026
+ const isSemicolonLessStyle = prevToken && nextToken && prevToken.range[0] >= node.range[0] && isSemicolonToken(semiToken) && !isTokenOnSameLine(prevToken, semiToken) && isTokenOnSameLine(semiToken, nextToken);
2027
+ return isSemicolonLessStyle ? prevToken : semiToken;
2028
+ }
2029
+ function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
2030
+ return trailingSpaces + indentSpaces;
2031
+ }
2032
+ function getReportLoc(node, sourceCode) {
2033
+ if (isSingleLine(node))
2034
+ return node.loc;
2035
+ const line = node.loc.start.line;
2036
+ const sourceLine = sourceCode.lines[line - 1];
2037
+ if (sourceLine === undefined)
2038
+ throw new Error("Padding rule invariant: statement source line is missing");
2039
+ return {
2040
+ start: node.loc.start,
2041
+ end: {
2042
+ line,
2043
+ column: sourceLine.length
2044
+ }
2045
+ };
2046
+ }
2047
+ function verifyForAny() {}
2048
+ function verifyForNever(context, _, nextNode, paddingLines) {
2049
+ if (paddingLines.length === 0)
2050
+ return;
2051
+ context.report({
2052
+ node: nextNode,
2053
+ messageId: "unexpectedBlankLine",
2054
+ loc: getReportLoc(nextNode, context.sourceCode),
2055
+ fix(fixer) {
2056
+ if (paddingLines.length >= 2)
2057
+ return null;
2058
+ const paddingPair = paddingLines[0];
2059
+ if (paddingPair === undefined)
2060
+ throw new Error("Padding rule invariant: reported padding pair is missing");
2061
+ const [prevToken, nextToken] = paddingPair;
2062
+ const start = prevToken.range[1];
2063
+ const end = nextToken.range[0];
2064
+ const text = context.sourceCode.text.slice(start, end).replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
2065
+ return fixer.replaceTextRange([start, end], text);
2066
+ }
2067
+ });
2068
+ }
2069
+ function verifyForAlways(context, prevNode, nextNode, paddingLines) {
2070
+ if (paddingLines.length > 0)
2071
+ return;
2072
+ context.report({
2073
+ node: nextNode,
2074
+ messageId: "expectedBlankLine",
2075
+ loc: getReportLoc(nextNode, context.sourceCode),
2076
+ fix(fixer) {
2077
+ const sourceCode = context.sourceCode;
2078
+ let prevToken = getActualLastToken(prevNode, sourceCode);
2079
+ const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
2080
+ includeComments: true,
2081
+ filter(token) {
2082
+ if (isTokenOnSameLine(prevToken, token)) {
2083
+ prevToken = token;
2084
+ return false;
2085
+ }
2086
+ return true;
2087
+ }
2088
+ }) || nextNode;
2089
+ const insertText = isTokenOnSameLine(prevToken, nextToken) ? `
2090
+
2091
+ ` : `
2092
+ `;
2093
+ return fixer.insertTextAfter(prevToken, insertText);
2094
+ }
2095
+ });
2096
+ }
2097
+ var PaddingTypes = {
2098
+ any: { verify: verifyForAny },
2099
+ never: { verify: verifyForNever },
2100
+ always: { verify: verifyForAlways }
2101
+ };
2102
+ var MaybeMultilineStatementType = {
2103
+ "block-like": { test: isBlockLikeStatement },
2104
+ expression: { test: isExpression },
2105
+ return: newKeywordTester("ReturnStatement", "return"),
2106
+ export: newKeywordTester([
2107
+ "ExportAllDeclaration",
2108
+ "ExportDefaultDeclaration",
2109
+ "ExportNamedDeclaration"
2110
+ ], "export"),
2111
+ var: newKeywordTester("VariableDeclaration", "var"),
2112
+ let: newKeywordTester("VariableDeclaration", "let"),
2113
+ const: newKeywordTester("VariableDeclaration", "const"),
2114
+ using: {
2115
+ test: (node) => node.type === "VariableDeclaration" && (node.kind === "using" || node.kind === "await using")
2116
+ },
2117
+ type: newKeywordTester("TSTypeAliasDeclaration", "type")
2118
+ };
2119
+ var StatementTypes = {
2120
+ "*": { test: () => true },
2121
+ exports: { test: isCJSExport },
2122
+ require: { test: isCJSRequire },
2123
+ directive: { test: isDirectivePrologue },
2124
+ iife: { test: isIIFEStatement },
2125
+ block: newNodeTypeTester("BlockStatement"),
2126
+ empty: newNodeTypeTester("EmptyStatement"),
2127
+ function: newNodeTypeTester("FunctionDeclaration"),
2128
+ "ts-method": newNodeTypeTester("TSMethodSignature"),
2129
+ break: newKeywordTester("BreakStatement", "break"),
2130
+ case: newKeywordTester("SwitchCase", "case"),
2131
+ class: newKeywordTester("ClassDeclaration", "class"),
2132
+ continue: newKeywordTester("ContinueStatement", "continue"),
2133
+ debugger: newKeywordTester("DebuggerStatement", "debugger"),
2134
+ default: newKeywordTester(["SwitchCase", "ExportDefaultDeclaration"], "default"),
2135
+ do: newKeywordTester("DoWhileStatement", "do"),
2136
+ for: newKeywordTester([
2137
+ "ForStatement",
2138
+ "ForInStatement",
2139
+ "ForOfStatement"
2140
+ ], "for"),
2141
+ if: newKeywordTester("IfStatement", "if"),
2142
+ import: newKeywordTester("ImportDeclaration", "import"),
2143
+ switch: newKeywordTester("SwitchStatement", "switch"),
2144
+ throw: newKeywordTester("ThrowStatement", "throw"),
2145
+ try: newKeywordTester("TryStatement", "try"),
2146
+ while: newKeywordTester(["WhileStatement", "DoWhileStatement"], "while"),
2147
+ with: newKeywordTester("WithStatement", "with"),
2148
+ "cjs-export": {
2149
+ test: (node, sourceCode) => node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && CJS_EXPORT.test(sourceCode.getText(node.expression.left))
2150
+ },
2151
+ "cjs-import": {
2152
+ test: (node, sourceCode) => node.type === "VariableDeclaration" && node.declarations.length > 0 && node.declarations[0]?.init != null && CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
2153
+ },
2154
+ enum: newKeywordTester("TSEnumDeclaration", "enum"),
2155
+ interface: newKeywordTester("TSInterfaceDeclaration", "interface"),
2156
+ "function-overload": newNodeTypeTester("TSDeclareFunction"),
2157
+ ...Object.fromEntries(Object.entries(MaybeMultilineStatementType).flatMap(([key, value]) => [
2158
+ [key, value],
2159
+ [
2160
+ `singleline-${key}`,
2161
+ {
2162
+ ...value,
2163
+ test: (node, sourceCode) => value.test(node, sourceCode) && isSingleLine(node)
2164
+ }
2165
+ ],
2166
+ [
2167
+ `multiline-${key}`,
2168
+ {
2169
+ ...value,
2170
+ test: (node, sourceCode) => value.test(node, sourceCode) && !isSingleLine(node)
2171
+ }
2172
+ ]
2173
+ ]))
2174
+ };
2175
+ function createPaddingLineRule(options) {
2176
+ return {
2177
+ meta: {
2178
+ type: "layout",
2179
+ docs: {
2180
+ description: "Require or disallow padding lines between statements"
2181
+ },
2182
+ fixable: "whitespace",
2183
+ hasSuggestions: false,
2184
+ schema: {
2185
+ $defs: {
2186
+ paddingType: {
2187
+ type: "string",
2188
+ enum: Object.keys(PaddingTypes)
2189
+ },
2190
+ statementType: {
2191
+ type: "string",
2192
+ enum: Object.keys(StatementTypes)
2193
+ },
2194
+ selectorOption: {
2195
+ type: "object",
2196
+ properties: {
2197
+ selector: {
2198
+ type: "string"
2199
+ },
2200
+ lineMode: {
2201
+ type: "string",
2202
+ enum: ["any", "singleline", "multiline"]
2203
+ }
2204
+ },
2205
+ required: ["selector"],
2206
+ additionalProperties: false
2207
+ },
2208
+ statementMatcher: {
2209
+ anyOf: [
2210
+ { $ref: "#/$defs/statementType" },
2211
+ { $ref: "#/$defs/selectorOption" }
2212
+ ]
2213
+ },
2214
+ statementOption: {
2215
+ anyOf: [
2216
+ { $ref: "#/$defs/statementMatcher" },
2217
+ {
2218
+ type: "array",
2219
+ items: { $ref: "#/$defs/statementMatcher" },
2220
+ minItems: 1,
2221
+ uniqueItems: true,
2222
+ additionalItems: false
2223
+ }
2224
+ ]
2225
+ }
2226
+ },
2227
+ type: "array",
2228
+ additionalItems: false,
2229
+ items: {
2230
+ type: "object",
2231
+ properties: {
2232
+ blankLine: { $ref: "#/$defs/paddingType" },
2233
+ prev: { $ref: "#/$defs/statementOption" },
2234
+ next: { $ref: "#/$defs/statementOption" }
2235
+ },
2236
+ additionalProperties: false,
2237
+ required: ["blankLine", "prev", "next"]
2238
+ }
2239
+ },
2240
+ messages: {
2241
+ unexpectedBlankLine: "Unexpected blank line before this statement.",
2242
+ expectedBlankLine: "Expected blank line before this statement."
2243
+ }
2244
+ },
2245
+ create(context) {
2246
+ const sourceCode = context.sourceCode;
2247
+ const selectorMatchedNodes = new Map;
2248
+ const pendingPairs = [];
2249
+ function collectSelectorOption(option) {
2250
+ if (Array.isArray(option)) {
2251
+ for (const item of option)
2252
+ collectSelectorOption(item);
2253
+ return;
2254
+ }
2255
+ if (!isSelectorOption(option))
2256
+ return;
2257
+ selectorMatchedNodes.set(option.selector, new Set);
2258
+ }
2259
+ for (const configure of options) {
2260
+ collectSelectorOption(configure.prev);
2261
+ collectSelectorOption(configure.next);
2262
+ }
2263
+ let scopeInfo = null;
2264
+ function enterScope() {
2265
+ scopeInfo = {
2266
+ upper: scopeInfo,
2267
+ prevNode: null
2268
+ };
2269
+ }
2270
+ function exitScope() {
2271
+ if (scopeInfo)
2272
+ scopeInfo = scopeInfo.upper;
2273
+ }
2274
+ function match(node, type) {
2275
+ let innerStatementNode = node;
2276
+ while (innerStatementNode.type === "LabeledStatement")
2277
+ innerStatementNode = innerStatementNode.body;
2278
+ if (Array.isArray(type))
2279
+ return type.some(match.bind(null, innerStatementNode));
2280
+ if (isSelectorOption(type)) {
2281
+ const matchedNodes = selectorMatchedNodes.get(type.selector);
2282
+ if (!matchedNodes?.has(innerStatementNode))
2283
+ return false;
2284
+ const lineMode = type.lineMode;
2285
+ if (lineMode === "singleline")
2286
+ return isSingleLine(innerStatementNode);
2287
+ else if (lineMode === "multiline")
2288
+ return !isSingleLine(innerStatementNode);
2289
+ return true;
2290
+ } else {
2291
+ const statementType = StatementTypes[type];
2292
+ if (statementType === undefined)
2293
+ throw new Error(`Padding rule invariant: unsupported statement type ${type}`);
2294
+ return statementType.test(innerStatementNode, sourceCode);
2295
+ }
2296
+ }
2297
+ function getPaddingType(prevNode, nextNode) {
2298
+ for (let i = options.length - 1;i >= 0; --i) {
2299
+ const configure = options[i];
2300
+ if (configure === undefined)
2301
+ throw new Error("Padding rule invariant: configuration entry is missing");
2302
+ if (match(prevNode, configure.prev) && match(nextNode, configure.next)) {
2303
+ return PaddingTypes[configure.blankLine];
2304
+ }
2305
+ }
2306
+ return PaddingTypes.any;
2307
+ }
2308
+ function getPaddingLineSequences(prevNode, nextNode) {
2309
+ const pairs = [];
2310
+ let prevToken = getActualLastToken(prevNode, sourceCode);
2311
+ if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
2312
+ do {
2313
+ const token = sourceCode.getTokenAfter(prevToken, {
2314
+ includeComments: true
2315
+ });
2316
+ if (token.loc.start.line - prevToken.loc.end.line >= 2)
2317
+ pairs.push([prevToken, token]);
2318
+ prevToken = token;
2319
+ } while (prevToken.range[0] < nextNode.range[0]);
2320
+ }
2321
+ return pairs;
2322
+ }
2323
+ function verify(node) {
2324
+ if (!node.parent || ![
2325
+ "BlockStatement",
2326
+ "Program",
2327
+ "StaticBlock",
2328
+ "SwitchCase",
2329
+ "SwitchStatement",
2330
+ "TSInterfaceBody",
2331
+ "TSModuleBlock",
2332
+ "TSTypeLiteral"
2333
+ ].includes(node.parent.type)) {
2334
+ return;
2335
+ }
2336
+ const prevNode = scopeInfo.prevNode;
2337
+ if (prevNode)
2338
+ pendingPairs.push({ prevNode, nextNode: node });
2339
+ scopeInfo.prevNode = node;
2340
+ }
2341
+ function verifyPendingPairs() {
2342
+ for (const { prevNode, nextNode } of pendingPairs) {
2343
+ const type = getPaddingType(prevNode, nextNode);
2344
+ const paddingLines = getPaddingLineSequences(prevNode, nextNode);
2345
+ type.verify(context, prevNode, nextNode, paddingLines);
2346
+ }
2347
+ }
2348
+ function verifyThenEnterScope(node) {
2349
+ verify(node);
2350
+ enterScope();
2351
+ }
2352
+ const selectorMatchListeners = Object.fromEntries(Array.from(selectorMatchedNodes.keys(), (selector) => [
2353
+ selector,
2354
+ (node) => {
2355
+ selectorMatchedNodes.get(selector)?.add(node);
2356
+ }
2357
+ ]));
2358
+ return {
2359
+ Program: enterScope,
2360
+ "Program:exit": () => {
2361
+ verifyPendingPairs();
2362
+ exitScope();
2363
+ },
2364
+ BlockStatement: enterScope,
2365
+ "BlockStatement:exit": exitScope,
2366
+ SwitchStatement: enterScope,
2367
+ "SwitchStatement:exit": exitScope,
2368
+ SwitchCase: verifyThenEnterScope,
2369
+ "SwitchCase:exit": exitScope,
2370
+ StaticBlock: enterScope,
2371
+ "StaticBlock:exit": exitScope,
2372
+ TSInterfaceBody: enterScope,
2373
+ "TSInterfaceBody:exit": exitScope,
2374
+ TSModuleBlock: enterScope,
2375
+ "TSModuleBlock:exit": exitScope,
2376
+ TSTypeLiteral: enterScope,
2377
+ "TSTypeLiteral:exit": exitScope,
2378
+ TSDeclareFunction: verifyThenEnterScope,
2379
+ "TSDeclareFunction:exit": exitScope,
2380
+ TSMethodSignature: verifyThenEnterScope,
2381
+ "TSMethodSignature:exit": exitScope,
2382
+ ":statement": verify,
2383
+ ...selectorMatchListeners
2384
+ };
2385
+ }
2386
+ };
2387
+ }
2388
+
2389
+ // vendor/anti-slop/src/rules/require-readable-spacing.ts
2390
+ var paddingRule = createPaddingLineRule([
2391
+ { blankLine: "always", prev: "import", next: "*" },
2392
+ { blankLine: "always", prev: "*", next: { selector: "Program > :not(ImportDeclaration)" } },
2393
+ { blankLine: "always", prev: { selector: "Program > :not(ImportDeclaration)" }, next: "*" },
2394
+ { blankLine: "always", prev: "*", next: ["function", "class", "interface", "type"] },
2395
+ { blankLine: "always", prev: ["function", "class", "interface", "type"], next: "*" },
2396
+ {
2397
+ blankLine: "always",
2398
+ prev: "*",
2399
+ next: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"]
2400
+ },
2401
+ {
2402
+ blankLine: "always",
2403
+ prev: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"],
2404
+ next: "*"
2405
+ },
2406
+ { blankLine: "always", prev: "*", next: ["return", "if", "switch", "try", "for", "while", "do"] },
2407
+ { blankLine: "always", prev: "block-like", next: "*" },
2408
+ { blankLine: "any", prev: "import", next: "import" },
2409
+ {
2410
+ blankLine: "any",
2411
+ prev: {
2412
+ selector: ':matches(TSDeclareFunction, ExportNamedDeclaration[declaration.type="TSDeclareFunction"])'
2413
+ },
2414
+ next: {
2415
+ selector: ':matches(TSDeclareFunction, FunctionDeclaration, ExportNamedDeclaration[declaration.type="TSDeclareFunction"], ExportNamedDeclaration[declaration.type="FunctionDeclaration"])'
2416
+ }
2417
+ }
2418
+ ]);
2419
+ var requireReadableSpacingRule = {
2420
+ ...paddingRule,
2421
+ meta: {
2422
+ ...paddingRule.meta,
2423
+ docs: {
2424
+ description: "Require readable spacing between declarations and logical statement groups."
2425
+ },
2426
+ schema: []
2427
+ }
2428
+ };
2429
+
1926
2430
  // vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
1927
2431
  import { defineRule as defineRule17 } from "@oxlint/plugins";
1928
2432
  var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
@@ -2036,6 +2540,7 @@ var antiSlopPlugin = eslintCompatPlugin({
2036
2540
  "no-unknown-returns": noUnknownReturnsRule,
2037
2541
  "no-unknown-type-aliases": noUnknownTypeAliasesRule,
2038
2542
  "no-widen-then-assert": noWidenThenAssertRule,
2543
+ "require-readable-spacing": requireReadableSpacingRule,
2039
2544
  "require-safety-comment-for-type-assertion": requireSafetyCommentForTypeAssertionRule
2040
2545
  }
2041
2546
  });
@@ -2134,7 +2639,7 @@ import { eslintCompatPlugin as eslintCompatPlugin3 } from "@oxlint/plugins";
2134
2639
 
2135
2640
  // src/effect/rules/no-try-catch-in-effect-generators.ts
2136
2641
  import { defineRule as defineRule19 } from "@oxlint/plugins";
2137
- function resolveVariable4(sourceCode, identifier) {
2642
+ function resolveVariable2(sourceCode, identifier) {
2138
2643
  let scope = sourceCode.getScope(identifier);
2139
2644
  while (scope) {
2140
2645
  const variable = scope.set.get(identifier.name);
@@ -2160,7 +2665,7 @@ function staticMemberName(node) {
2160
2665
  return node.property.type === "Identifier" ? node.property.name : null;
2161
2666
  }
2162
2667
  function isEffectImport(sourceCode, identifier, namespace) {
2163
- const variable = resolveVariable4(sourceCode, identifier);
2668
+ const variable = resolveVariable2(sourceCode, identifier);
2164
2669
  return variable?.defs.some((definition) => {
2165
2670
  if (definition.type !== "ImportBinding" || definition.parent?.type !== "ImportDeclaration" || definition.parent.source.value !== "effect") {
2166
2671
  return false;
@@ -2236,8 +2741,149 @@ var effect_default = timmoEffectPlugin;
2236
2741
  // vendor/anti-slop/src/effect/index.ts
2237
2742
  import { eslintCompatPlugin as eslintCompatPlugin4 } from "@oxlint/plugins";
2238
2743
 
2239
- // vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
2744
+ // vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
2240
2745
  import { defineRule as defineRule20 } from "@oxlint/plugins";
2746
+
2747
+ // vendor/anti-slop/src/effect/shared/tagged-values.ts
2748
+ var equalityOperators = new Set(["==", "===", "!=", "!=="]);
2749
+ var broadEffectCatchMethods = new Set(["catch", "catchAll", "catchIf"]);
2750
+ var isStringLiteral = (node) => node?.type === "Literal" && typeof node.value === "string";
2751
+ 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");
2752
+ var tagMemberFromComparison = (node) => {
2753
+ if (!equalityOperators.has(node.operator))
2754
+ return;
2755
+ if (isTagMember(node.left) && isStringLiteral(node.right))
2756
+ return node.left;
2757
+ if (isTagMember(node.right) && isStringLiteral(node.left))
2758
+ return node.right;
2759
+ return;
2760
+ };
2761
+ 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);
2762
+ var isInsideBroadEffectHandler = (node) => {
2763
+ let current = node.parent;
2764
+ while (current !== null && current !== undefined) {
2765
+ if (current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") {
2766
+ return isBroadEffectCatchCall(current.parent) && current.parent.arguments.includes(current);
2767
+ }
2768
+ current = current.parent;
2769
+ }
2770
+ return false;
2771
+ };
2772
+ 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");
2773
+ var propertyName = (property) => {
2774
+ if (!property.computed && property.key.type === "Identifier") {
2775
+ return property.key.name;
2776
+ }
2777
+ if (property.key.type === "Literal" && typeof property.key.value === "string") {
2778
+ return property.key.value;
2779
+ }
2780
+ return;
2781
+ };
2782
+ var isMatchPatternObject = (node) => {
2783
+ const call = node.parent;
2784
+ if (call?.type !== "CallExpression" || !call.arguments.includes(node)) {
2785
+ return false;
2786
+ }
2787
+ const callee = call.callee;
2788
+ 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");
2789
+ };
2790
+
2791
+ // vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts
2792
+ var noManualEffectErrorTagRule = defineRule20({
2793
+ meta: {
2794
+ type: "problem",
2795
+ docs: {
2796
+ description: "Use Effect tagged error handlers instead of manually branching on `_tag` in a catch handler."
2797
+ },
2798
+ messages: {
2799
+ tag: "Use Effect.catchTag or Effect.catchTags instead of manually discriminating a tagged error in a broad Effect catch handler.",
2800
+ reason: "Use Effect.catchReason or Effect.catchReasons instead of manually discriminating a tagged `reason` in a broad Effect catch handler."
2801
+ }
2802
+ },
2803
+ createOnce(context) {
2804
+ return {
2805
+ BinaryExpression(node) {
2806
+ const tagMember = tagMemberFromComparison(node);
2807
+ if (tagMember === undefined || !isInsideBroadEffectHandler(node)) {
2808
+ return;
2809
+ }
2810
+ context.report({
2811
+ node,
2812
+ messageId: isReasonTagMember(tagMember) ? "reason" : "tag"
2813
+ });
2814
+ },
2815
+ SwitchStatement(node) {
2816
+ if (!isTagMember(node.discriminant) || !isInsideBroadEffectHandler(node)) {
2817
+ return;
2818
+ }
2819
+ context.report({
2820
+ node,
2821
+ messageId: isReasonTagMember(node.discriminant) ? "reason" : "tag"
2822
+ });
2823
+ }
2824
+ };
2825
+ }
2826
+ });
2827
+
2828
+ // vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts
2829
+ import { defineRule as defineRule21 } from "@oxlint/plugins";
2830
+ var noManualTagComparisonRule = defineRule21({
2831
+ meta: {
2832
+ type: "problem",
2833
+ docs: {
2834
+ description: "Use Effect Match or Predicate helpers instead of manually branching on `_tag`."
2835
+ },
2836
+ messages: {
2837
+ manualComparison: "Use Match.tag/Match.tags for tagged-value branching, or Predicate.isTagged for a simple reusable predicate.",
2838
+ manualSwitch: "Use Match.value(value).pipe(Match.tag/Match.tags/Match.tagsExhaustive) or the tagged enum `$match` helper instead of switching on `_tag`."
2839
+ }
2840
+ },
2841
+ createOnce(context) {
2842
+ return {
2843
+ BinaryExpression(node) {
2844
+ if (tagMemberFromComparison(node) === undefined || isInsideBroadEffectHandler(node)) {
2845
+ return;
2846
+ }
2847
+ context.report({ node, messageId: "manualComparison" });
2848
+ },
2849
+ SwitchStatement(node) {
2850
+ if (!isTagMember(node.discriminant) || isInsideBroadEffectHandler(node)) {
2851
+ return;
2852
+ }
2853
+ context.report({ node, messageId: "manualSwitch" });
2854
+ }
2855
+ };
2856
+ }
2857
+ });
2858
+
2859
+ // vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts
2860
+ import { defineRule as defineRule22 } from "@oxlint/plugins";
2861
+ var noManualTaggedConstructionRule = defineRule22({
2862
+ meta: {
2863
+ type: "problem",
2864
+ docs: {
2865
+ description: "Construct tagged values with their existing Effect constructor instead of writing `_tag` manually."
2866
+ },
2867
+ messages: {
2868
+ manualConstruction: "Use the existing Schema tagged `.make`, tagged class/error constructor, or Data.taggedEnum variant constructor instead of writing a literal `_tag` object."
2869
+ }
2870
+ },
2871
+ createOnce(context) {
2872
+ return {
2873
+ ObjectExpression(node) {
2874
+ if (isMatchPatternObject(node))
2875
+ return;
2876
+ const tag = node.properties.find((property) => property.type === "Property" && propertyName(property) === "_tag" && isStringLiteral(property.value));
2877
+ if (tag !== undefined) {
2878
+ context.report({ node: tag, messageId: "manualConstruction" });
2879
+ }
2880
+ }
2881
+ };
2882
+ }
2883
+ });
2884
+
2885
+ // vendor/anti-slop/src/effect/rules/no-service-constructor-imports.ts
2886
+ import { defineRule as defineRule23 } from "@oxlint/plugins";
2241
2887
  var SERVICE_CONSTRUCTOR_NAME = /^make[A-Z]/u;
2242
2888
  var TEST_FILE = /\.(?:test|spec)\.[cm]?[jt]sx?$/u;
2243
2889
  function isProjectLocalImport(source) {
@@ -2248,7 +2894,7 @@ function getImportedName(specifier) {
2248
2894
  return specifier.imported.name;
2249
2895
  return specifier.imported.value;
2250
2896
  }
2251
- var noServiceConstructorImportsRule = defineRule20({
2897
+ var noServiceConstructorImportsRule = defineRule23({
2252
2898
  meta: {
2253
2899
  type: "problem",
2254
2900
  docs: {
@@ -2281,11 +2927,63 @@ var noServiceConstructorImportsRule = defineRule20({
2281
2927
  }
2282
2928
  });
2283
2929
 
2930
+ // vendor/anti-slop/src/effect/rules/prefer-effect-match.ts
2931
+ import { defineRule as defineRule24 } from "@oxlint/plugins";
2932
+ var equalityOperators2 = new Set(["==", "===", "!=", "!=="]);
2933
+ var preferEffectMatchRule = defineRule24({
2934
+ meta: {
2935
+ type: "problem",
2936
+ docs: {
2937
+ description: "Use Match from Effect for chained literal ternaries over the same value."
2938
+ },
2939
+ messages: {
2940
+ preferMatch: "Use Match from Effect instead of a chained literal ternary."
2941
+ }
2942
+ },
2943
+ createOnce(context) {
2944
+ const isLiteral = (node) => node.type === "Literal" || node.type === "TemplateLiteral" && node.expressions.length === 0;
2945
+ const comparedValue = (node) => {
2946
+ if (node.type !== "BinaryExpression" || !equalityOperators2.has(node.operator)) {
2947
+ return;
2948
+ }
2949
+ if (isLiteral(node.left))
2950
+ return context.sourceCode.getText(node.right);
2951
+ if (isLiteral(node.right))
2952
+ return context.sourceCode.getText(node.left);
2953
+ return;
2954
+ };
2955
+ return {
2956
+ ConditionalExpression(node) {
2957
+ if (node.parent?.type === "ConditionalExpression")
2958
+ return;
2959
+ const value = comparedValue(node.test);
2960
+ if (value === undefined)
2961
+ return;
2962
+ let alternate = node.alternate;
2963
+ let literalChecks = 1;
2964
+ while (alternate.type === "ConditionalExpression") {
2965
+ if (comparedValue(alternate.test) !== value)
2966
+ return;
2967
+ literalChecks += 1;
2968
+ alternate = alternate.alternate;
2969
+ }
2970
+ if (literalChecks > 1) {
2971
+ context.report({ node, messageId: "preferMatch" });
2972
+ }
2973
+ }
2974
+ };
2975
+ }
2976
+ });
2977
+
2284
2978
  // vendor/anti-slop/src/effect/index.ts
2285
2979
  var antiSlopEffectPlugin = eslintCompatPlugin4({
2286
2980
  meta: { name: "anti-slop-effect" },
2287
2981
  rules: {
2288
- "no-service-constructor-imports": noServiceConstructorImportsRule
2982
+ "no-manual-effect-error-tag": noManualEffectErrorTagRule,
2983
+ "no-manual-tag-comparison": noManualTagComparisonRule,
2984
+ "no-manual-tagged-construction": noManualTaggedConstructionRule,
2985
+ "no-service-constructor-imports": noServiceConstructorImportsRule,
2986
+ "prefer-effect-match": preferEffectMatchRule
2289
2987
  }
2290
2988
  });
2291
2989
  var effect_default2 = antiSlopEffectPlugin;