@timmo001/oxlint-rules 0.3.1 → 0.3.3
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 +3 -21
- 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
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/
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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-
|
|
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 =
|
|
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-
|
|
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;
|