@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.
- package/README.md +2 -1
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/cli.js +733 -35
- package/dist/configs/recommended-effect.js +767 -69
- package/dist/configs/recommended.js +535 -30
- package/dist/upstream/anti-slop.js +535 -30
- package/dist/upstream/effect.js +196 -3
- package/package.json +1 -1
- package/vendor/anti-slop/src/effect/index.ts +8 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-effect-error-tag.ts +52 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-tag-comparison.ts +45 -0
- package/vendor/anti-slop/src/effect/rules/no-manual-tagged-construction.ts +37 -0
- package/vendor/anti-slop/src/effect/rules/prefer-effect-match.ts +54 -0
- package/vendor/anti-slop/src/effect/shared/tagged-values.ts +97 -0
- package/vendor/anti-slop/src/index.ts +2 -0
- package/vendor/anti-slop/src/rules/no-known-value-widening.ts +2 -14
- package/vendor/anti-slop/src/rules/no-module-mocking.ts +3 -14
- package/vendor/anti-slop/src/rules/require-readable-spacing.ts +47 -0
- package/vendor/anti-slop/src/shared/reflect-method.ts +2 -13
- package/vendor/anti-slop/src/shared/scope.ts +15 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/LICENSE +22 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/UPSTREAM.md +28 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts +51 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts +906 -0
- package/vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-options.d.ts +87 -0
|
@@ -829,14 +829,7 @@ function functionParameterBindingName(parameter, sourceCode) {
|
|
|
829
829
|
return annotationStart === undefined ? sourceText : sourceText.slice(0, annotationStart - parameter.start).trimEnd();
|
|
830
830
|
}
|
|
831
831
|
|
|
832
|
-
// vendor/anti-slop/src/
|
|
833
|
-
function unwrapExpression(expression) {
|
|
834
|
-
let current = expression;
|
|
835
|
-
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
836
|
-
current = current.expression;
|
|
837
|
-
}
|
|
838
|
-
return current;
|
|
839
|
-
}
|
|
832
|
+
// vendor/anti-slop/src/shared/scope.ts
|
|
840
833
|
function resolveVariable(sourceCode, identifier) {
|
|
841
834
|
let scope = sourceCode.getScope(identifier);
|
|
842
835
|
while (scope !== null) {
|
|
@@ -847,6 +840,15 @@ function resolveVariable(sourceCode, identifier) {
|
|
|
847
840
|
}
|
|
848
841
|
return null;
|
|
849
842
|
}
|
|
843
|
+
|
|
844
|
+
// vendor/anti-slop/src/rules/no-known-value-widening.ts
|
|
845
|
+
function unwrapExpression(expression) {
|
|
846
|
+
let current = expression;
|
|
847
|
+
while (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
848
|
+
current = current.expression;
|
|
849
|
+
}
|
|
850
|
+
return current;
|
|
851
|
+
}
|
|
850
852
|
function variableDeclarator(variable) {
|
|
851
853
|
if (variable.defs.length !== 1)
|
|
852
854
|
return null;
|
|
@@ -1114,16 +1116,6 @@ var noKnownValueWideningRule = defineRule5({
|
|
|
1114
1116
|
// vendor/anti-slop/src/rules/no-module-mocking.ts
|
|
1115
1117
|
import { defineRule as defineRule6 } from "@oxlint/plugins";
|
|
1116
1118
|
var moduleMockMethods = new Set(["doMock", "mock", "unstable_mockModule"]);
|
|
1117
|
-
function resolveVariable2(sourceCode, identifier) {
|
|
1118
|
-
let scope = sourceCode.getScope(identifier);
|
|
1119
|
-
while (scope !== null) {
|
|
1120
|
-
const variable = scope.set.get(identifier.name);
|
|
1121
|
-
if (variable !== undefined)
|
|
1122
|
-
return variable;
|
|
1123
|
-
scope = scope.upper;
|
|
1124
|
-
}
|
|
1125
|
-
return null;
|
|
1126
|
-
}
|
|
1127
1119
|
function importedName(node) {
|
|
1128
1120
|
if (node.type !== "ImportSpecifier")
|
|
1129
1121
|
return null;
|
|
@@ -1135,7 +1127,7 @@ function isTestFrameworkObject(sourceCode, expression) {
|
|
|
1135
1127
|
if ((expression.name === "vi" || expression.name === "jest") && sourceCode.isGlobalReference(expression)) {
|
|
1136
1128
|
return true;
|
|
1137
1129
|
}
|
|
1138
|
-
const variable =
|
|
1130
|
+
const variable = resolveVariable(sourceCode, expression);
|
|
1139
1131
|
if (variable === null || variable.defs.length === 0) {
|
|
1140
1132
|
return expression.name === "vi" || expression.name === "jest";
|
|
1141
1133
|
}
|
|
@@ -1238,22 +1230,12 @@ var noObjectParametersRule = defineRule7({
|
|
|
1238
1230
|
import { defineRule as defineRule8 } from "@oxlint/plugins";
|
|
1239
1231
|
|
|
1240
1232
|
// vendor/anti-slop/src/shared/reflect-method.ts
|
|
1241
|
-
function resolveVariable3(sourceCode, identifier) {
|
|
1242
|
-
let scope = sourceCode.getScope(identifier);
|
|
1243
|
-
while (scope !== null) {
|
|
1244
|
-
const variable = scope.set.get(identifier.name);
|
|
1245
|
-
if (variable !== undefined)
|
|
1246
|
-
return variable;
|
|
1247
|
-
scope = scope.upper;
|
|
1248
|
-
}
|
|
1249
|
-
return null;
|
|
1250
|
-
}
|
|
1251
1233
|
function isGlobalReflect(sourceCode, expression) {
|
|
1252
1234
|
if (expression.type !== "Identifier" || expression.name !== "Reflect")
|
|
1253
1235
|
return false;
|
|
1254
1236
|
if (sourceCode.isGlobalReference(expression))
|
|
1255
1237
|
return true;
|
|
1256
|
-
const variable =
|
|
1238
|
+
const variable = resolveVariable(sourceCode, expression);
|
|
1257
1239
|
return variable === null || variable.defs.length === 0;
|
|
1258
1240
|
}
|
|
1259
1241
|
function isGlobalReflectMethodCall(sourceCode, callee, methodName) {
|
|
@@ -1918,6 +1900,528 @@ var noWidenThenAssertRule = defineRule16({
|
|
|
1918
1900
|
}
|
|
1919
1901
|
});
|
|
1920
1902
|
|
|
1903
|
+
// vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-ast.ts
|
|
1904
|
+
var LINEBREAKS = new Set([`\r
|
|
1905
|
+
`, "\r", `
|
|
1906
|
+
`, "\u2028", "\u2029"]);
|
|
1907
|
+
var isClosingBraceToken = (token) => token.type === "Punctuator" && token.value === "}";
|
|
1908
|
+
var isSemicolonToken = (token) => token.type === "Punctuator" && token.value === ";";
|
|
1909
|
+
var isNotSemicolonToken = (token) => !isSemicolonToken(token);
|
|
1910
|
+
var isTokenOnSameLine = (left, right) => left.loc.end.line === right.loc.start.line;
|
|
1911
|
+
var isFunction = (node) => node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
1912
|
+
var isSingleLine = (node) => node.loc.start.line === node.loc.end.line;
|
|
1913
|
+
var skipChainExpression = (node) => node.type === "ChainExpression" ? node.expression : node;
|
|
1914
|
+
var isTopLevelExpressionStatement = (node) => node.type === "ExpressionStatement" && (node.parent.type === "Program" || node.parent.type === "BlockStatement" && isFunction(node.parent.parent));
|
|
1915
|
+
function isParenthesized(node, sourceCode) {
|
|
1916
|
+
const before = sourceCode.getTokenBefore(node);
|
|
1917
|
+
const after = sourceCode.getTokenAfter(node);
|
|
1918
|
+
return before?.value === "(" && after?.value === ")";
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// vendor/anti-slop/src/vendor/eslint-stylistic/padding-line-between-statements.ts
|
|
1922
|
+
var CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
|
|
1923
|
+
var CJS_IMPORT = /^require\(/u;
|
|
1924
|
+
var LT = `[${Array.from(LINEBREAKS).join("")}]`;
|
|
1925
|
+
var PADDING_LINE_SEQUENCE = new RegExp(String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, "u");
|
|
1926
|
+
function isSelectorOption(option) {
|
|
1927
|
+
return typeof option === "object" && !Array.isArray(option);
|
|
1928
|
+
}
|
|
1929
|
+
function newKeywordTester(type, keyword) {
|
|
1930
|
+
return {
|
|
1931
|
+
test(node, sourceCode) {
|
|
1932
|
+
const isSameKeyword = sourceCode.getFirstToken(node)?.value === keyword;
|
|
1933
|
+
const isSameType = Array.isArray(type) ? type.includes(node.type) : type === node.type;
|
|
1934
|
+
return isSameKeyword && isSameType;
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
function newNodeTypeTester(type) {
|
|
1939
|
+
return {
|
|
1940
|
+
test: (node) => node.type === type
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function isIIFEStatement(node) {
|
|
1944
|
+
if (node.type === "ExpressionStatement") {
|
|
1945
|
+
let expression = skipChainExpression(node.expression);
|
|
1946
|
+
if (expression.type === "UnaryExpression")
|
|
1947
|
+
expression = skipChainExpression(expression.argument);
|
|
1948
|
+
if (expression.type === "CallExpression") {
|
|
1949
|
+
let node = expression.callee;
|
|
1950
|
+
while (node.type === "SequenceExpression") {
|
|
1951
|
+
const lastExpression = node.expressions.at(-1);
|
|
1952
|
+
if (lastExpression === undefined)
|
|
1953
|
+
throw new Error("Padding rule invariant: sequence expression is empty");
|
|
1954
|
+
node = lastExpression;
|
|
1955
|
+
}
|
|
1956
|
+
return isFunction(node);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
function isCJSRequire(node) {
|
|
1962
|
+
if (node.type === "VariableDeclaration") {
|
|
1963
|
+
const declaration = node.declarations[0];
|
|
1964
|
+
if (declaration?.init) {
|
|
1965
|
+
let call = declaration?.init;
|
|
1966
|
+
while (call.type === "MemberExpression")
|
|
1967
|
+
call = call.object;
|
|
1968
|
+
if (call.type === "CallExpression" && call.callee.type === "Identifier") {
|
|
1969
|
+
return call.callee.name === "require";
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return false;
|
|
1974
|
+
}
|
|
1975
|
+
function isBlockLikeStatement(node, sourceCode) {
|
|
1976
|
+
if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") {
|
|
1977
|
+
return true;
|
|
1978
|
+
}
|
|
1979
|
+
if (isIIFEStatement(node))
|
|
1980
|
+
return true;
|
|
1981
|
+
const lastToken = sourceCode.getLastToken(node, isNotSemicolonToken);
|
|
1982
|
+
const belongingNode = lastToken && isClosingBraceToken(lastToken) ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) : null;
|
|
1983
|
+
return !!belongingNode && (belongingNode.type === "BlockStatement" || belongingNode.type === "SwitchStatement");
|
|
1984
|
+
}
|
|
1985
|
+
function isDirective(node, sourceCode) {
|
|
1986
|
+
return isTopLevelExpressionStatement(node) && node.expression.type === "Literal" && typeof node.expression.value === "string" && !isParenthesized(node.expression, sourceCode);
|
|
1987
|
+
}
|
|
1988
|
+
function isDirectivePrologue(node, sourceCode) {
|
|
1989
|
+
if (isDirective(node, sourceCode) && node.parent && "body" in node.parent && Array.isArray(node.parent.body)) {
|
|
1990
|
+
for (const sibling of node.parent.body) {
|
|
1991
|
+
if (sibling === node)
|
|
1992
|
+
break;
|
|
1993
|
+
if (!isDirective(sibling, sourceCode))
|
|
1994
|
+
return false;
|
|
1995
|
+
}
|
|
1996
|
+
return true;
|
|
1997
|
+
}
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
2000
|
+
function isCJSExport(node) {
|
|
2001
|
+
if (node.type === "ExpressionStatement") {
|
|
2002
|
+
const expression = node.expression;
|
|
2003
|
+
if (expression.type === "AssignmentExpression") {
|
|
2004
|
+
let left = expression.left;
|
|
2005
|
+
if (left.type === "MemberExpression") {
|
|
2006
|
+
while (left.object.type === "MemberExpression")
|
|
2007
|
+
left = left.object;
|
|
2008
|
+
return left.object.type === "Identifier" && (left.object.name === "exports" || left.object.name === "module" && left.property.type === "Identifier" && left.property.name === "exports");
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return false;
|
|
2013
|
+
}
|
|
2014
|
+
function isExpression(node, sourceCode) {
|
|
2015
|
+
return node.type === "ExpressionStatement" && !isDirectivePrologue(node, sourceCode);
|
|
2016
|
+
}
|
|
2017
|
+
function getActualLastToken(node, sourceCode) {
|
|
2018
|
+
const semiToken = sourceCode.getLastToken(node);
|
|
2019
|
+
const prevToken = sourceCode.getTokenBefore(semiToken);
|
|
2020
|
+
const nextToken = sourceCode.getTokenAfter(semiToken);
|
|
2021
|
+
const isSemicolonLessStyle = prevToken && nextToken && prevToken.range[0] >= node.range[0] && isSemicolonToken(semiToken) && !isTokenOnSameLine(prevToken, semiToken) && isTokenOnSameLine(semiToken, nextToken);
|
|
2022
|
+
return isSemicolonLessStyle ? prevToken : semiToken;
|
|
2023
|
+
}
|
|
2024
|
+
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
|
|
2025
|
+
return trailingSpaces + indentSpaces;
|
|
2026
|
+
}
|
|
2027
|
+
function getReportLoc(node, sourceCode) {
|
|
2028
|
+
if (isSingleLine(node))
|
|
2029
|
+
return node.loc;
|
|
2030
|
+
const line = node.loc.start.line;
|
|
2031
|
+
const sourceLine = sourceCode.lines[line - 1];
|
|
2032
|
+
if (sourceLine === undefined)
|
|
2033
|
+
throw new Error("Padding rule invariant: statement source line is missing");
|
|
2034
|
+
return {
|
|
2035
|
+
start: node.loc.start,
|
|
2036
|
+
end: {
|
|
2037
|
+
line,
|
|
2038
|
+
column: sourceLine.length
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
function verifyForAny() {}
|
|
2043
|
+
function verifyForNever(context, _, nextNode, paddingLines) {
|
|
2044
|
+
if (paddingLines.length === 0)
|
|
2045
|
+
return;
|
|
2046
|
+
context.report({
|
|
2047
|
+
node: nextNode,
|
|
2048
|
+
messageId: "unexpectedBlankLine",
|
|
2049
|
+
loc: getReportLoc(nextNode, context.sourceCode),
|
|
2050
|
+
fix(fixer) {
|
|
2051
|
+
if (paddingLines.length >= 2)
|
|
2052
|
+
return null;
|
|
2053
|
+
const paddingPair = paddingLines[0];
|
|
2054
|
+
if (paddingPair === undefined)
|
|
2055
|
+
throw new Error("Padding rule invariant: reported padding pair is missing");
|
|
2056
|
+
const [prevToken, nextToken] = paddingPair;
|
|
2057
|
+
const start = prevToken.range[1];
|
|
2058
|
+
const end = nextToken.range[0];
|
|
2059
|
+
const text = context.sourceCode.text.slice(start, end).replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
|
|
2060
|
+
return fixer.replaceTextRange([start, end], text);
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
function verifyForAlways(context, prevNode, nextNode, paddingLines) {
|
|
2065
|
+
if (paddingLines.length > 0)
|
|
2066
|
+
return;
|
|
2067
|
+
context.report({
|
|
2068
|
+
node: nextNode,
|
|
2069
|
+
messageId: "expectedBlankLine",
|
|
2070
|
+
loc: getReportLoc(nextNode, context.sourceCode),
|
|
2071
|
+
fix(fixer) {
|
|
2072
|
+
const sourceCode = context.sourceCode;
|
|
2073
|
+
let prevToken = getActualLastToken(prevNode, sourceCode);
|
|
2074
|
+
const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
|
|
2075
|
+
includeComments: true,
|
|
2076
|
+
filter(token) {
|
|
2077
|
+
if (isTokenOnSameLine(prevToken, token)) {
|
|
2078
|
+
prevToken = token;
|
|
2079
|
+
return false;
|
|
2080
|
+
}
|
|
2081
|
+
return true;
|
|
2082
|
+
}
|
|
2083
|
+
}) || nextNode;
|
|
2084
|
+
const insertText = isTokenOnSameLine(prevToken, nextToken) ? `
|
|
2085
|
+
|
|
2086
|
+
` : `
|
|
2087
|
+
`;
|
|
2088
|
+
return fixer.insertTextAfter(prevToken, insertText);
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
var PaddingTypes = {
|
|
2093
|
+
any: { verify: verifyForAny },
|
|
2094
|
+
never: { verify: verifyForNever },
|
|
2095
|
+
always: { verify: verifyForAlways }
|
|
2096
|
+
};
|
|
2097
|
+
var MaybeMultilineStatementType = {
|
|
2098
|
+
"block-like": { test: isBlockLikeStatement },
|
|
2099
|
+
expression: { test: isExpression },
|
|
2100
|
+
return: newKeywordTester("ReturnStatement", "return"),
|
|
2101
|
+
export: newKeywordTester([
|
|
2102
|
+
"ExportAllDeclaration",
|
|
2103
|
+
"ExportDefaultDeclaration",
|
|
2104
|
+
"ExportNamedDeclaration"
|
|
2105
|
+
], "export"),
|
|
2106
|
+
var: newKeywordTester("VariableDeclaration", "var"),
|
|
2107
|
+
let: newKeywordTester("VariableDeclaration", "let"),
|
|
2108
|
+
const: newKeywordTester("VariableDeclaration", "const"),
|
|
2109
|
+
using: {
|
|
2110
|
+
test: (node) => node.type === "VariableDeclaration" && (node.kind === "using" || node.kind === "await using")
|
|
2111
|
+
},
|
|
2112
|
+
type: newKeywordTester("TSTypeAliasDeclaration", "type")
|
|
2113
|
+
};
|
|
2114
|
+
var StatementTypes = {
|
|
2115
|
+
"*": { test: () => true },
|
|
2116
|
+
exports: { test: isCJSExport },
|
|
2117
|
+
require: { test: isCJSRequire },
|
|
2118
|
+
directive: { test: isDirectivePrologue },
|
|
2119
|
+
iife: { test: isIIFEStatement },
|
|
2120
|
+
block: newNodeTypeTester("BlockStatement"),
|
|
2121
|
+
empty: newNodeTypeTester("EmptyStatement"),
|
|
2122
|
+
function: newNodeTypeTester("FunctionDeclaration"),
|
|
2123
|
+
"ts-method": newNodeTypeTester("TSMethodSignature"),
|
|
2124
|
+
break: newKeywordTester("BreakStatement", "break"),
|
|
2125
|
+
case: newKeywordTester("SwitchCase", "case"),
|
|
2126
|
+
class: newKeywordTester("ClassDeclaration", "class"),
|
|
2127
|
+
continue: newKeywordTester("ContinueStatement", "continue"),
|
|
2128
|
+
debugger: newKeywordTester("DebuggerStatement", "debugger"),
|
|
2129
|
+
default: newKeywordTester(["SwitchCase", "ExportDefaultDeclaration"], "default"),
|
|
2130
|
+
do: newKeywordTester("DoWhileStatement", "do"),
|
|
2131
|
+
for: newKeywordTester([
|
|
2132
|
+
"ForStatement",
|
|
2133
|
+
"ForInStatement",
|
|
2134
|
+
"ForOfStatement"
|
|
2135
|
+
], "for"),
|
|
2136
|
+
if: newKeywordTester("IfStatement", "if"),
|
|
2137
|
+
import: newKeywordTester("ImportDeclaration", "import"),
|
|
2138
|
+
switch: newKeywordTester("SwitchStatement", "switch"),
|
|
2139
|
+
throw: newKeywordTester("ThrowStatement", "throw"),
|
|
2140
|
+
try: newKeywordTester("TryStatement", "try"),
|
|
2141
|
+
while: newKeywordTester(["WhileStatement", "DoWhileStatement"], "while"),
|
|
2142
|
+
with: newKeywordTester("WithStatement", "with"),
|
|
2143
|
+
"cjs-export": {
|
|
2144
|
+
test: (node, sourceCode) => node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && CJS_EXPORT.test(sourceCode.getText(node.expression.left))
|
|
2145
|
+
},
|
|
2146
|
+
"cjs-import": {
|
|
2147
|
+
test: (node, sourceCode) => node.type === "VariableDeclaration" && node.declarations.length > 0 && node.declarations[0]?.init != null && CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
|
|
2148
|
+
},
|
|
2149
|
+
enum: newKeywordTester("TSEnumDeclaration", "enum"),
|
|
2150
|
+
interface: newKeywordTester("TSInterfaceDeclaration", "interface"),
|
|
2151
|
+
"function-overload": newNodeTypeTester("TSDeclareFunction"),
|
|
2152
|
+
...Object.fromEntries(Object.entries(MaybeMultilineStatementType).flatMap(([key, value]) => [
|
|
2153
|
+
[key, value],
|
|
2154
|
+
[
|
|
2155
|
+
`singleline-${key}`,
|
|
2156
|
+
{
|
|
2157
|
+
...value,
|
|
2158
|
+
test: (node, sourceCode) => value.test(node, sourceCode) && isSingleLine(node)
|
|
2159
|
+
}
|
|
2160
|
+
],
|
|
2161
|
+
[
|
|
2162
|
+
`multiline-${key}`,
|
|
2163
|
+
{
|
|
2164
|
+
...value,
|
|
2165
|
+
test: (node, sourceCode) => value.test(node, sourceCode) && !isSingleLine(node)
|
|
2166
|
+
}
|
|
2167
|
+
]
|
|
2168
|
+
]))
|
|
2169
|
+
};
|
|
2170
|
+
function createPaddingLineRule(options) {
|
|
2171
|
+
return {
|
|
2172
|
+
meta: {
|
|
2173
|
+
type: "layout",
|
|
2174
|
+
docs: {
|
|
2175
|
+
description: "Require or disallow padding lines between statements"
|
|
2176
|
+
},
|
|
2177
|
+
fixable: "whitespace",
|
|
2178
|
+
hasSuggestions: false,
|
|
2179
|
+
schema: {
|
|
2180
|
+
$defs: {
|
|
2181
|
+
paddingType: {
|
|
2182
|
+
type: "string",
|
|
2183
|
+
enum: Object.keys(PaddingTypes)
|
|
2184
|
+
},
|
|
2185
|
+
statementType: {
|
|
2186
|
+
type: "string",
|
|
2187
|
+
enum: Object.keys(StatementTypes)
|
|
2188
|
+
},
|
|
2189
|
+
selectorOption: {
|
|
2190
|
+
type: "object",
|
|
2191
|
+
properties: {
|
|
2192
|
+
selector: {
|
|
2193
|
+
type: "string"
|
|
2194
|
+
},
|
|
2195
|
+
lineMode: {
|
|
2196
|
+
type: "string",
|
|
2197
|
+
enum: ["any", "singleline", "multiline"]
|
|
2198
|
+
}
|
|
2199
|
+
},
|
|
2200
|
+
required: ["selector"],
|
|
2201
|
+
additionalProperties: false
|
|
2202
|
+
},
|
|
2203
|
+
statementMatcher: {
|
|
2204
|
+
anyOf: [
|
|
2205
|
+
{ $ref: "#/$defs/statementType" },
|
|
2206
|
+
{ $ref: "#/$defs/selectorOption" }
|
|
2207
|
+
]
|
|
2208
|
+
},
|
|
2209
|
+
statementOption: {
|
|
2210
|
+
anyOf: [
|
|
2211
|
+
{ $ref: "#/$defs/statementMatcher" },
|
|
2212
|
+
{
|
|
2213
|
+
type: "array",
|
|
2214
|
+
items: { $ref: "#/$defs/statementMatcher" },
|
|
2215
|
+
minItems: 1,
|
|
2216
|
+
uniqueItems: true,
|
|
2217
|
+
additionalItems: false
|
|
2218
|
+
}
|
|
2219
|
+
]
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
type: "array",
|
|
2223
|
+
additionalItems: false,
|
|
2224
|
+
items: {
|
|
2225
|
+
type: "object",
|
|
2226
|
+
properties: {
|
|
2227
|
+
blankLine: { $ref: "#/$defs/paddingType" },
|
|
2228
|
+
prev: { $ref: "#/$defs/statementOption" },
|
|
2229
|
+
next: { $ref: "#/$defs/statementOption" }
|
|
2230
|
+
},
|
|
2231
|
+
additionalProperties: false,
|
|
2232
|
+
required: ["blankLine", "prev", "next"]
|
|
2233
|
+
}
|
|
2234
|
+
},
|
|
2235
|
+
messages: {
|
|
2236
|
+
unexpectedBlankLine: "Unexpected blank line before this statement.",
|
|
2237
|
+
expectedBlankLine: "Expected blank line before this statement."
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
create(context) {
|
|
2241
|
+
const sourceCode = context.sourceCode;
|
|
2242
|
+
const selectorMatchedNodes = new Map;
|
|
2243
|
+
const pendingPairs = [];
|
|
2244
|
+
function collectSelectorOption(option) {
|
|
2245
|
+
if (Array.isArray(option)) {
|
|
2246
|
+
for (const item of option)
|
|
2247
|
+
collectSelectorOption(item);
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
if (!isSelectorOption(option))
|
|
2251
|
+
return;
|
|
2252
|
+
selectorMatchedNodes.set(option.selector, new Set);
|
|
2253
|
+
}
|
|
2254
|
+
for (const configure of options) {
|
|
2255
|
+
collectSelectorOption(configure.prev);
|
|
2256
|
+
collectSelectorOption(configure.next);
|
|
2257
|
+
}
|
|
2258
|
+
let scopeInfo = null;
|
|
2259
|
+
function enterScope() {
|
|
2260
|
+
scopeInfo = {
|
|
2261
|
+
upper: scopeInfo,
|
|
2262
|
+
prevNode: null
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
function exitScope() {
|
|
2266
|
+
if (scopeInfo)
|
|
2267
|
+
scopeInfo = scopeInfo.upper;
|
|
2268
|
+
}
|
|
2269
|
+
function match(node, type) {
|
|
2270
|
+
let innerStatementNode = node;
|
|
2271
|
+
while (innerStatementNode.type === "LabeledStatement")
|
|
2272
|
+
innerStatementNode = innerStatementNode.body;
|
|
2273
|
+
if (Array.isArray(type))
|
|
2274
|
+
return type.some(match.bind(null, innerStatementNode));
|
|
2275
|
+
if (isSelectorOption(type)) {
|
|
2276
|
+
const matchedNodes = selectorMatchedNodes.get(type.selector);
|
|
2277
|
+
if (!matchedNodes?.has(innerStatementNode))
|
|
2278
|
+
return false;
|
|
2279
|
+
const lineMode = type.lineMode;
|
|
2280
|
+
if (lineMode === "singleline")
|
|
2281
|
+
return isSingleLine(innerStatementNode);
|
|
2282
|
+
else if (lineMode === "multiline")
|
|
2283
|
+
return !isSingleLine(innerStatementNode);
|
|
2284
|
+
return true;
|
|
2285
|
+
} else {
|
|
2286
|
+
const statementType = StatementTypes[type];
|
|
2287
|
+
if (statementType === undefined)
|
|
2288
|
+
throw new Error(`Padding rule invariant: unsupported statement type ${type}`);
|
|
2289
|
+
return statementType.test(innerStatementNode, sourceCode);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
function getPaddingType(prevNode, nextNode) {
|
|
2293
|
+
for (let i = options.length - 1;i >= 0; --i) {
|
|
2294
|
+
const configure = options[i];
|
|
2295
|
+
if (configure === undefined)
|
|
2296
|
+
throw new Error("Padding rule invariant: configuration entry is missing");
|
|
2297
|
+
if (match(prevNode, configure.prev) && match(nextNode, configure.next)) {
|
|
2298
|
+
return PaddingTypes[configure.blankLine];
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
return PaddingTypes.any;
|
|
2302
|
+
}
|
|
2303
|
+
function getPaddingLineSequences(prevNode, nextNode) {
|
|
2304
|
+
const pairs = [];
|
|
2305
|
+
let prevToken = getActualLastToken(prevNode, sourceCode);
|
|
2306
|
+
if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
|
|
2307
|
+
do {
|
|
2308
|
+
const token = sourceCode.getTokenAfter(prevToken, {
|
|
2309
|
+
includeComments: true
|
|
2310
|
+
});
|
|
2311
|
+
if (token.loc.start.line - prevToken.loc.end.line >= 2)
|
|
2312
|
+
pairs.push([prevToken, token]);
|
|
2313
|
+
prevToken = token;
|
|
2314
|
+
} while (prevToken.range[0] < nextNode.range[0]);
|
|
2315
|
+
}
|
|
2316
|
+
return pairs;
|
|
2317
|
+
}
|
|
2318
|
+
function verify(node) {
|
|
2319
|
+
if (!node.parent || ![
|
|
2320
|
+
"BlockStatement",
|
|
2321
|
+
"Program",
|
|
2322
|
+
"StaticBlock",
|
|
2323
|
+
"SwitchCase",
|
|
2324
|
+
"SwitchStatement",
|
|
2325
|
+
"TSInterfaceBody",
|
|
2326
|
+
"TSModuleBlock",
|
|
2327
|
+
"TSTypeLiteral"
|
|
2328
|
+
].includes(node.parent.type)) {
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
const prevNode = scopeInfo.prevNode;
|
|
2332
|
+
if (prevNode)
|
|
2333
|
+
pendingPairs.push({ prevNode, nextNode: node });
|
|
2334
|
+
scopeInfo.prevNode = node;
|
|
2335
|
+
}
|
|
2336
|
+
function verifyPendingPairs() {
|
|
2337
|
+
for (const { prevNode, nextNode } of pendingPairs) {
|
|
2338
|
+
const type = getPaddingType(prevNode, nextNode);
|
|
2339
|
+
const paddingLines = getPaddingLineSequences(prevNode, nextNode);
|
|
2340
|
+
type.verify(context, prevNode, nextNode, paddingLines);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
function verifyThenEnterScope(node) {
|
|
2344
|
+
verify(node);
|
|
2345
|
+
enterScope();
|
|
2346
|
+
}
|
|
2347
|
+
const selectorMatchListeners = Object.fromEntries(Array.from(selectorMatchedNodes.keys(), (selector) => [
|
|
2348
|
+
selector,
|
|
2349
|
+
(node) => {
|
|
2350
|
+
selectorMatchedNodes.get(selector)?.add(node);
|
|
2351
|
+
}
|
|
2352
|
+
]));
|
|
2353
|
+
return {
|
|
2354
|
+
Program: enterScope,
|
|
2355
|
+
"Program:exit": () => {
|
|
2356
|
+
verifyPendingPairs();
|
|
2357
|
+
exitScope();
|
|
2358
|
+
},
|
|
2359
|
+
BlockStatement: enterScope,
|
|
2360
|
+
"BlockStatement:exit": exitScope,
|
|
2361
|
+
SwitchStatement: enterScope,
|
|
2362
|
+
"SwitchStatement:exit": exitScope,
|
|
2363
|
+
SwitchCase: verifyThenEnterScope,
|
|
2364
|
+
"SwitchCase:exit": exitScope,
|
|
2365
|
+
StaticBlock: enterScope,
|
|
2366
|
+
"StaticBlock:exit": exitScope,
|
|
2367
|
+
TSInterfaceBody: enterScope,
|
|
2368
|
+
"TSInterfaceBody:exit": exitScope,
|
|
2369
|
+
TSModuleBlock: enterScope,
|
|
2370
|
+
"TSModuleBlock:exit": exitScope,
|
|
2371
|
+
TSTypeLiteral: enterScope,
|
|
2372
|
+
"TSTypeLiteral:exit": exitScope,
|
|
2373
|
+
TSDeclareFunction: verifyThenEnterScope,
|
|
2374
|
+
"TSDeclareFunction:exit": exitScope,
|
|
2375
|
+
TSMethodSignature: verifyThenEnterScope,
|
|
2376
|
+
"TSMethodSignature:exit": exitScope,
|
|
2377
|
+
":statement": verify,
|
|
2378
|
+
...selectorMatchListeners
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
// vendor/anti-slop/src/rules/require-readable-spacing.ts
|
|
2385
|
+
var paddingRule = createPaddingLineRule([
|
|
2386
|
+
{ blankLine: "always", prev: "import", next: "*" },
|
|
2387
|
+
{ blankLine: "always", prev: "*", next: { selector: "Program > :not(ImportDeclaration)" } },
|
|
2388
|
+
{ blankLine: "always", prev: { selector: "Program > :not(ImportDeclaration)" }, next: "*" },
|
|
2389
|
+
{ blankLine: "always", prev: "*", next: ["function", "class", "interface", "type"] },
|
|
2390
|
+
{ blankLine: "always", prev: ["function", "class", "interface", "type"], next: "*" },
|
|
2391
|
+
{
|
|
2392
|
+
blankLine: "always",
|
|
2393
|
+
prev: "*",
|
|
2394
|
+
next: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"]
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
blankLine: "always",
|
|
2398
|
+
prev: ["multiline-const", "multiline-let", "multiline-var", "multiline-using"],
|
|
2399
|
+
next: "*"
|
|
2400
|
+
},
|
|
2401
|
+
{ blankLine: "always", prev: "*", next: ["return", "if", "switch", "try", "for", "while", "do"] },
|
|
2402
|
+
{ blankLine: "always", prev: "block-like", next: "*" },
|
|
2403
|
+
{ blankLine: "any", prev: "import", next: "import" },
|
|
2404
|
+
{
|
|
2405
|
+
blankLine: "any",
|
|
2406
|
+
prev: {
|
|
2407
|
+
selector: ':matches(TSDeclareFunction, ExportNamedDeclaration[declaration.type="TSDeclareFunction"])'
|
|
2408
|
+
},
|
|
2409
|
+
next: {
|
|
2410
|
+
selector: ':matches(TSDeclareFunction, FunctionDeclaration, ExportNamedDeclaration[declaration.type="TSDeclareFunction"], ExportNamedDeclaration[declaration.type="FunctionDeclaration"])'
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
]);
|
|
2414
|
+
var requireReadableSpacingRule = {
|
|
2415
|
+
...paddingRule,
|
|
2416
|
+
meta: {
|
|
2417
|
+
...paddingRule.meta,
|
|
2418
|
+
docs: {
|
|
2419
|
+
description: "Require readable spacing between declarations and logical statement groups."
|
|
2420
|
+
},
|
|
2421
|
+
schema: []
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
|
|
1921
2425
|
// vendor/anti-slop/src/rules/require-safety-comment-for-type-assertion.ts
|
|
1922
2426
|
import { defineRule as defineRule17 } from "@oxlint/plugins";
|
|
1923
2427
|
var DEFAULT_SAFETY_MARKERS = ["SAFETY"];
|
|
@@ -2031,6 +2535,7 @@ var antiSlopPlugin = eslintCompatPlugin({
|
|
|
2031
2535
|
"no-unknown-returns": noUnknownReturnsRule,
|
|
2032
2536
|
"no-unknown-type-aliases": noUnknownTypeAliasesRule,
|
|
2033
2537
|
"no-widen-then-assert": noWidenThenAssertRule,
|
|
2538
|
+
"require-readable-spacing": requireReadableSpacingRule,
|
|
2034
2539
|
"require-safety-comment-for-type-assertion": requireSafetyCommentForTypeAssertionRule
|
|
2035
2540
|
}
|
|
2036
2541
|
});
|