@sarj/eslint-plugin 15.0.0 → 15.2.0
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/dist/index.cjs +554 -133
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +579 -154
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -967,11 +967,11 @@ function isTrivialInitializer(node) {
|
|
|
967
967
|
}
|
|
968
968
|
function restatesStatementHead(body2, statement) {
|
|
969
969
|
if (statement === null) return false;
|
|
970
|
-
const
|
|
971
|
-
const opener =
|
|
972
|
-
if (opener === void 0 ||
|
|
970
|
+
const words2 = body2.match(/[A-Za-z][\w$]*/g) ?? [];
|
|
971
|
+
const opener = words2[0];
|
|
972
|
+
if (opener === void 0 || words2.length > NARRATION_MAX_WORDS) return false;
|
|
973
973
|
if (!NARRATION_VERB_RE.test(opener)) return false;
|
|
974
|
-
const content =
|
|
974
|
+
const content = words2.slice(1).map(normalizeToken).filter((word) => !NARRATION_STOPWORDS.has(word));
|
|
975
975
|
if (content.length < NARRATION_MIN_CONTENT) return false;
|
|
976
976
|
const head = statement.split("(")[0] ?? statement;
|
|
977
977
|
const code = headTokens(head);
|
|
@@ -1239,8 +1239,8 @@ function isRedundantNarration(body2, statementBelow, standalone, isolatedEnumera
|
|
|
1239
1239
|
if (META_COMMENTARY_RE.test(t) && !justified) return true;
|
|
1240
1240
|
if (isBareDeferral(t) && !justified) return true;
|
|
1241
1241
|
if (HELPER_OPENER_RE.test(t) || LETS_RE.test(t)) return true;
|
|
1242
|
-
const
|
|
1243
|
-
if (
|
|
1242
|
+
const words2 = t.split(/\s+/);
|
|
1243
|
+
if (words2.length > 1 && words2.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {
|
|
1244
1244
|
const lowerT = t.toLowerCase();
|
|
1245
1245
|
if (!RATIONALE_WORDS.some((word) => lowerT.includes(word)) && restatesWholeStatement(t, statementBelow)) {
|
|
1246
1246
|
return true;
|
|
@@ -1322,8 +1322,8 @@ function isWeakWalkthroughComment(body2, statement) {
|
|
|
1322
1322
|
if (normalized.length === 0 || normalized.endsWith("?") || normalized.split(/\s+/).length > WALL_MAX_WORDS || isDirective(normalized) || isProtected(normalized) || !WALL_NARRATION_RE.test(normalized)) {
|
|
1323
1323
|
return false;
|
|
1324
1324
|
}
|
|
1325
|
-
const
|
|
1326
|
-
const described =
|
|
1325
|
+
const words2 = contentTokens(normalized);
|
|
1326
|
+
const described = words2.slice(1);
|
|
1327
1327
|
if (described.length === 0) return false;
|
|
1328
1328
|
const code = codeTokens(statement);
|
|
1329
1329
|
const matched = described.filter((word) => restates([word], code)).length;
|
|
@@ -2022,12 +2022,13 @@ function createSqlListener(handler) {
|
|
|
2022
2022
|
|
|
2023
2023
|
// src/rules/no-dynamic-sql.ts
|
|
2024
2024
|
var noDynamicSqlDocumentation = {
|
|
2025
|
-
summary: "Disallow runtime
|
|
2025
|
+
summary: "Disallow runtime values embedded inside quoted SQL values passed to statement-execution methods.",
|
|
2026
2026
|
rationale: "Embedding runtime values in SQL bypasses driver parameterization and can introduce injection defects or unstable query plans.",
|
|
2027
2027
|
remediation: "Use SQL placeholders and pass runtime values through the driver's binding API.",
|
|
2028
2028
|
category: "security",
|
|
2029
2029
|
limitations: [
|
|
2030
|
-
"The rule
|
|
2030
|
+
"The rule reports only visibly quoted runtime values; dynamic identifiers and unquoted fragments require provenance that syntax-only linting cannot prove.",
|
|
2031
|
+
"Static fragments and parameterizing tagged templates are exempt."
|
|
2031
2032
|
],
|
|
2032
2033
|
examples: [
|
|
2033
2034
|
{
|
|
@@ -2062,27 +2063,48 @@ function isStaticFragment(expression) {
|
|
|
2062
2063
|
if (expression.type === AST_NODE_TYPES8.Literal) {
|
|
2063
2064
|
return typeof expression.value === "string";
|
|
2064
2065
|
}
|
|
2066
|
+
if (expression.type === AST_NODE_TYPES8.TemplateLiteral) {
|
|
2067
|
+
return expression.expressions.length === 0;
|
|
2068
|
+
}
|
|
2065
2069
|
return false;
|
|
2066
2070
|
}
|
|
2067
2071
|
function runtimeInterpolations(template) {
|
|
2068
2072
|
return template.expressions.filter(
|
|
2069
|
-
(expression) => !isStaticFragment(expression)
|
|
2073
|
+
(expression, index) => !isStaticFragment(expression) && endsWithSqlQuote(template.quasis[index]?.value.raw ?? "") && startsWithSqlQuote(template.quasis[index + 1]?.value.raw ?? "")
|
|
2070
2074
|
);
|
|
2071
2075
|
}
|
|
2076
|
+
function endsWithSqlQuote(text) {
|
|
2077
|
+
return /['"]\s*$/u.test(text);
|
|
2078
|
+
}
|
|
2079
|
+
function startsWithSqlQuote(text) {
|
|
2080
|
+
return /^\s*['"]/u.test(text);
|
|
2081
|
+
}
|
|
2082
|
+
function staticLiteralText(node) {
|
|
2083
|
+
if (node.type === AST_NODE_TYPES8.Literal && typeof node.value === "string") {
|
|
2084
|
+
return node.value;
|
|
2085
|
+
}
|
|
2086
|
+
if (node.type === AST_NODE_TYPES8.TemplateLiteral && node.expressions.length === 0) {
|
|
2087
|
+
return node.quasis[0]?.value.raw;
|
|
2088
|
+
}
|
|
2089
|
+
return void 0;
|
|
2090
|
+
}
|
|
2072
2091
|
function runtimeConcatOperands(node) {
|
|
2073
2092
|
if (node.type !== AST_NODE_TYPES8.BinaryExpression || node.operator !== "+") {
|
|
2074
2093
|
return [];
|
|
2075
2094
|
}
|
|
2076
2095
|
const operands = concatOperands(node);
|
|
2077
2096
|
const hasStringLiteral = operands.some(
|
|
2078
|
-
(operand) => operand.type === AST_NODE_TYPES8.Literal && typeof operand.value === "string"
|
|
2097
|
+
(operand) => operand.type === AST_NODE_TYPES8.Literal && typeof operand.value === "string" || operand.type === AST_NODE_TYPES8.TemplateLiteral && operand.expressions.length === 0
|
|
2079
2098
|
);
|
|
2080
2099
|
if (!hasStringLiteral) {
|
|
2081
2100
|
return [];
|
|
2082
2101
|
}
|
|
2083
|
-
return operands.filter(
|
|
2084
|
-
(operand)
|
|
2085
|
-
|
|
2102
|
+
return operands.filter((operand, index) => {
|
|
2103
|
+
if (isStaticFragment(operand)) return false;
|
|
2104
|
+
const before = operands[index - 1];
|
|
2105
|
+
const after = operands[index + 1];
|
|
2106
|
+
return before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "");
|
|
2107
|
+
});
|
|
2086
2108
|
}
|
|
2087
2109
|
function concatOperands(node) {
|
|
2088
2110
|
if (node.type === AST_NODE_TYPES8.BinaryExpression && node.operator === "+") {
|
|
@@ -2121,7 +2143,7 @@ var no_dynamic_sql_default = createRule({
|
|
|
2121
2143
|
meta: {
|
|
2122
2144
|
type: "problem",
|
|
2123
2145
|
docs: {
|
|
2124
|
-
description:
|
|
2146
|
+
description: noDynamicSqlDocumentation.summary
|
|
2125
2147
|
},
|
|
2126
2148
|
schema: [
|
|
2127
2149
|
{
|
|
@@ -2137,7 +2159,7 @@ var no_dynamic_sql_default = createRule({
|
|
|
2137
2159
|
}
|
|
2138
2160
|
],
|
|
2139
2161
|
messages: {
|
|
2140
|
-
dynamicSql: "Runtime value
|
|
2162
|
+
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately."
|
|
2141
2163
|
}
|
|
2142
2164
|
},
|
|
2143
2165
|
defaultOptions: [{}],
|
|
@@ -2648,7 +2670,7 @@ var no_fat_try_blocks_default = createRule({
|
|
|
2648
2670
|
const sourceCode = context.sourceCode;
|
|
2649
2671
|
return {
|
|
2650
2672
|
TryStatement(node) {
|
|
2651
|
-
if (node.finalizer !== null) {
|
|
2673
|
+
if (node.finalizer !== null && node.handler === null) {
|
|
2652
2674
|
return;
|
|
2653
2675
|
}
|
|
2654
2676
|
if (handlerRethrows(node.handler)) {
|
|
@@ -2888,15 +2910,32 @@ var noHandRolledSpinnerDocumentation = {
|
|
|
2888
2910
|
rationale: "One-off loading indicators duplicate a shared primitive and let accessibility and styling diverge.",
|
|
2889
2911
|
remediation: "Render the design-system Spinner component instead.",
|
|
2890
2912
|
category: "maintainability",
|
|
2891
|
-
limitations: ["Only static className values on div and span elements are inspected."],
|
|
2913
|
+
limitations: ["Only static className values on div and span elements are inspected; tests, stories, generated files, and the design-system implementation are excluded."],
|
|
2892
2914
|
examples: [
|
|
2893
2915
|
{ id: "design-system-spinner", title: "Use the shared spinner", outcome: "no-match", files: [{ path: "src/loading-state.tsx", source: '<Spinner className="size-4" />' }], focusPath: "src/loading-state.tsx", expectedCount: 0, public: true },
|
|
2894
2916
|
{ id: "border-ring-spinner", title: "Do not rebuild a spinner", outcome: "match", files: [{ path: "src/loading-state.tsx", source: '<div className="size-4 animate-spin rounded-full border-2 border-t-transparent" />' }], focusPath: "src/loading-state.tsx", expectedCount: 1, public: true }
|
|
2895
2917
|
]
|
|
2896
2918
|
};
|
|
2897
2919
|
var DESIGN_SYSTEM_PATH = /(?:^|[/\\])components[/\\]ui[/\\]/u;
|
|
2898
|
-
var
|
|
2899
|
-
var
|
|
2920
|
+
var DIRECTIONAL_BORDER = /^border-([trblsexy])-(.+)$/u;
|
|
2921
|
+
var CSS_LENGTH = /^-?(?:\d+(?:\.\d+)?|\.\d+)(?:cap|ch|cm|dvh|dvw|em|ex|ic|in|lh|lvh|lvw|mm|pc|pt|px|q|rcap|rch|rem|rex|ric|rlh|svh|svw|vb|vh|vi|vmax|vmin|vw|%)$/u;
|
|
2922
|
+
var ARBITRARY_LENGTH_FUNCTION = /^(?:calc|clamp|max|min)\(.+\)$/u;
|
|
2923
|
+
function isBorderWidthValue(value) {
|
|
2924
|
+
if (/^\d+$/u.test(value)) return true;
|
|
2925
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
2926
|
+
const arbitrary = value.slice(1, -1);
|
|
2927
|
+
const length = arbitrary.startsWith("length:") ? arbitrary.slice("length:".length) : arbitrary;
|
|
2928
|
+
return CSS_LENGTH.test(length) || ARBITRARY_LENGTH_FUNCTION.test(length) || arbitrary.startsWith("length:") && /^var\(.+\)$/u.test(length);
|
|
2929
|
+
}
|
|
2930
|
+
return value.startsWith("(length:") && value.endsWith(")") && value.length > "(length:)".length;
|
|
2931
|
+
}
|
|
2932
|
+
function isBorderWidth(token) {
|
|
2933
|
+
return token === "border" || token.startsWith("border-") && isBorderWidthValue(token.slice("border-".length));
|
|
2934
|
+
}
|
|
2935
|
+
function isContrastingEdge(token) {
|
|
2936
|
+
const match = DIRECTIONAL_BORDER.exec(token);
|
|
2937
|
+
return match?.[2] !== void 0 && !isBorderWidthValue(match[2]);
|
|
2938
|
+
}
|
|
2900
2939
|
function staticClassName(attribute) {
|
|
2901
2940
|
const value = attribute.value;
|
|
2902
2941
|
if (value?.type === AST_NODE_TYPES11.Literal && typeof value.value === "string") {
|
|
@@ -2925,7 +2964,7 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
2925
2964
|
},
|
|
2926
2965
|
defaultOptions: [],
|
|
2927
2966
|
create(context) {
|
|
2928
|
-
if (DESIGN_SYSTEM_PATH.test(context.filename)) {
|
|
2967
|
+
if (DESIGN_SYSTEM_PATH.test(context.filename) || isTestFile(context.filename) || isStoryFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
2929
2968
|
return {};
|
|
2930
2969
|
}
|
|
2931
2970
|
return {
|
|
@@ -2940,7 +2979,7 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
2940
2979
|
const className = staticClassName(classNameAttribute);
|
|
2941
2980
|
if (className === null) return;
|
|
2942
2981
|
const classes = className.split(/\s+/u);
|
|
2943
|
-
if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some(
|
|
2982
|
+
if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some(isBorderWidth) && classes.some(isContrastingEdge)) {
|
|
2944
2983
|
context.report({ node, messageId: "handRolledSpinner" });
|
|
2945
2984
|
}
|
|
2946
2985
|
}
|
|
@@ -2961,8 +3000,59 @@ var noInsecureRandomIdDocumentation = {
|
|
|
2961
3000
|
{ id: "predictable-token", title: "Do not derive a token from Math.random", outcome: "match", files: [{ path: "src/session.ts", source: "const sessionToken = Math.random();" }], focusPath: "src/session.ts", expectedCount: 1, public: true }
|
|
2962
3001
|
]
|
|
2963
3002
|
};
|
|
2964
|
-
var
|
|
2965
|
-
|
|
3003
|
+
var STRONG_SECURITY_WORDS = /* @__PURE__ */ new Set([
|
|
3004
|
+
"apikey",
|
|
3005
|
+
"csrf",
|
|
3006
|
+
"nonce",
|
|
3007
|
+
"otp",
|
|
3008
|
+
"password",
|
|
3009
|
+
"passwd",
|
|
3010
|
+
"pin",
|
|
3011
|
+
"salt",
|
|
3012
|
+
"secret",
|
|
3013
|
+
"token",
|
|
3014
|
+
"uuid",
|
|
3015
|
+
"verificationcode"
|
|
3016
|
+
]);
|
|
3017
|
+
var NON_SECURITY_ID_WORDS = /* @__PURE__ */ new Set([
|
|
3018
|
+
"aria",
|
|
3019
|
+
"cache",
|
|
3020
|
+
"component",
|
|
3021
|
+
"correlation",
|
|
3022
|
+
"dev",
|
|
3023
|
+
"dialog",
|
|
3024
|
+
"dom",
|
|
3025
|
+
"element",
|
|
3026
|
+
"execution",
|
|
3027
|
+
"field",
|
|
3028
|
+
"form",
|
|
3029
|
+
"hmr",
|
|
3030
|
+
"input",
|
|
3031
|
+
"marker",
|
|
3032
|
+
"menu",
|
|
3033
|
+
"mock",
|
|
3034
|
+
"perf",
|
|
3035
|
+
"req",
|
|
3036
|
+
"request",
|
|
3037
|
+
"select",
|
|
3038
|
+
"tab",
|
|
3039
|
+
"temp",
|
|
3040
|
+
"test",
|
|
3041
|
+
"tmp",
|
|
3042
|
+
"trace"
|
|
3043
|
+
]);
|
|
3044
|
+
function nameWords(name) {
|
|
3045
|
+
return name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").split(/[^A-Za-z0-9]+/u).filter(Boolean).map((word) => word.toLowerCase());
|
|
3046
|
+
}
|
|
3047
|
+
function isStrongSecurityName(name) {
|
|
3048
|
+
const words2 = nameWords(name);
|
|
3049
|
+
return words2.some((word) => STRONG_SECURITY_WORDS.has(word)) || words2.some(
|
|
3050
|
+
(word, index) => word === "api" && words2[index + 1] === "key" || word === "auth" && words2[index + 1] === "id" || word === "verification" && words2[index + 1] === "code"
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
function isNonSecurityName(name) {
|
|
3054
|
+
return nameWords(name).some((word) => NON_SECURITY_ID_WORDS.has(word));
|
|
3055
|
+
}
|
|
2966
3056
|
var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
|
|
2967
3057
|
function isMathRandomCall(node) {
|
|
2968
3058
|
if (node.type !== "CallExpression") {
|
|
@@ -2975,70 +3065,57 @@ function isMathRandomCall(node) {
|
|
|
2975
3065
|
const { object, property } = callee;
|
|
2976
3066
|
return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
|
|
2977
3067
|
}
|
|
2978
|
-
function
|
|
2979
|
-
|
|
2980
|
-
let
|
|
2981
|
-
while (parent) {
|
|
2982
|
-
if (parent.type === "MemberExpression" && parent.object === current && !parent.computed && parent.property.type === "Identifier" && parent.property.name === "toString") {
|
|
2983
|
-
const grandparent = parent.parent;
|
|
2984
|
-
if (grandparent && grandparent.type === "CallExpression" && grandparent.callee === parent) {
|
|
2985
|
-
const firstArg = grandparent.arguments[0];
|
|
2986
|
-
if (firstArg && firstArg.type === "Literal" && firstArg.value === 36) {
|
|
2987
|
-
return true;
|
|
2988
|
-
}
|
|
2989
|
-
}
|
|
2990
|
-
}
|
|
2991
|
-
if (parent.type === "MemberExpression" && parent.object === current) {
|
|
2992
|
-
current = parent;
|
|
2993
|
-
parent = current.parent;
|
|
2994
|
-
continue;
|
|
2995
|
-
}
|
|
2996
|
-
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
2997
|
-
current = parent;
|
|
2998
|
-
parent = current.parent;
|
|
2999
|
-
continue;
|
|
3000
|
-
}
|
|
3001
|
-
break;
|
|
3002
|
-
}
|
|
3003
|
-
return false;
|
|
3004
|
-
}
|
|
3005
|
-
function findEnclosingName(node) {
|
|
3068
|
+
function findEnclosingNames(node) {
|
|
3069
|
+
const names = [];
|
|
3070
|
+
let directBinding = true;
|
|
3006
3071
|
let current = node;
|
|
3007
3072
|
let parent = current.parent;
|
|
3008
3073
|
while (parent) {
|
|
3009
3074
|
if (parent.type === "VariableDeclarator" && parent.init === current) {
|
|
3010
|
-
if (parent.id.type === "Identifier") {
|
|
3011
|
-
|
|
3075
|
+
if (directBinding && parent.id.type === "Identifier") {
|
|
3076
|
+
names.push(parent.id.name);
|
|
3012
3077
|
}
|
|
3013
|
-
return void 0;
|
|
3014
3078
|
}
|
|
3015
3079
|
if (parent.type === "Property" && parent.value === current) {
|
|
3016
3080
|
const key = parent.key;
|
|
3017
3081
|
if (!parent.computed && key.type === "Identifier") {
|
|
3018
|
-
|
|
3082
|
+
names.push(key.name);
|
|
3019
3083
|
}
|
|
3020
3084
|
if (key.type === "Literal" && typeof key.value === "string") {
|
|
3021
|
-
|
|
3085
|
+
names.push(key.value);
|
|
3022
3086
|
}
|
|
3023
|
-
|
|
3087
|
+
directBinding = false;
|
|
3024
3088
|
}
|
|
3025
3089
|
if (parent.type === "PropertyDefinition" && parent.value === current) {
|
|
3026
3090
|
const key = parent.key;
|
|
3027
3091
|
if (!parent.computed && key.type === "Identifier") {
|
|
3028
|
-
|
|
3092
|
+
names.push(key.name);
|
|
3029
3093
|
}
|
|
3030
3094
|
if (key.type === "Literal" && typeof key.value === "string") {
|
|
3031
|
-
|
|
3095
|
+
names.push(key.value);
|
|
3032
3096
|
}
|
|
3033
|
-
|
|
3097
|
+
directBinding = false;
|
|
3034
3098
|
}
|
|
3035
|
-
if (parent.type === "
|
|
3036
|
-
|
|
3099
|
+
if (parent.type === "AssignmentExpression" && parent.right === current) {
|
|
3100
|
+
if (directBinding && parent.left.type === "Identifier") names.push(parent.left.name);
|
|
3101
|
+
if (directBinding && parent.left.type === "MemberExpression" && !parent.left.computed && parent.left.property.type === "Identifier") {
|
|
3102
|
+
names.push(parent.left.property.name);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
|
|
3106
|
+
directBinding = false;
|
|
3107
|
+
}
|
|
3108
|
+
if (parent.type === "FunctionDeclaration") {
|
|
3109
|
+
if (directBinding && parent.id !== null) names.push(parent.id.name);
|
|
3110
|
+
return names;
|
|
3111
|
+
}
|
|
3112
|
+
if (parent.type === "ExpressionStatement") {
|
|
3113
|
+
return names;
|
|
3037
3114
|
}
|
|
3038
3115
|
current = parent;
|
|
3039
3116
|
parent = current.parent;
|
|
3040
3117
|
}
|
|
3041
|
-
return
|
|
3118
|
+
return names;
|
|
3042
3119
|
}
|
|
3043
3120
|
function isConcatenatedIntoPathOrDomId(node) {
|
|
3044
3121
|
const valueNode = climbValueChain(node);
|
|
@@ -3124,20 +3201,17 @@ var no_insecure_random_id_default = createRule({
|
|
|
3124
3201
|
if (!isMathRandomCall(node)) {
|
|
3125
3202
|
return;
|
|
3126
3203
|
}
|
|
3127
|
-
const
|
|
3128
|
-
if (
|
|
3204
|
+
const names = findEnclosingNames(node);
|
|
3205
|
+
if (names.some(isStrongSecurityName)) {
|
|
3129
3206
|
context.report({ node, messageId: "insecureRandomId" });
|
|
3130
3207
|
return;
|
|
3131
3208
|
}
|
|
3132
|
-
if (
|
|
3209
|
+
if (names.some(isNonSecurityName)) {
|
|
3133
3210
|
return;
|
|
3134
3211
|
}
|
|
3135
3212
|
if (isConcatenatedIntoPathOrDomId(node)) {
|
|
3136
3213
|
return;
|
|
3137
3214
|
}
|
|
3138
|
-
if (isPartOfToString36Chain(node)) {
|
|
3139
|
-
context.report({ node, messageId: "insecureRandomId" });
|
|
3140
|
-
}
|
|
3141
3215
|
}
|
|
3142
3216
|
};
|
|
3143
3217
|
}
|
|
@@ -3937,7 +4011,7 @@ var VALUE_TAG_RE = /@(example|deprecated|see|remarks|throws|internal|public|alph
|
|
|
3937
4011
|
var BOUNDARY_RE = /(?<=[.!?])["'`)\]]*\s+(?=[A-Z0-9`])/;
|
|
3938
4012
|
var BULLET_RE = /^\s*(?:[-*+] |\d+[.)] )/;
|
|
3939
4013
|
var HEADING_RE = /^[A-Za-z][A-Za-z ]+:$/;
|
|
3940
|
-
var TECHNICAL_ANCHOR_RE = /https?:\/\/|`[^`\n]+`|:[a-z][a-z0-9_-]*:|(["'])[^"'\n]+\1|\d|\b[a-z][a-z0-9]*[A-Z][A-Za-z0-9]*\b|\b[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9_]+\b|(?:^|\s)(?:[\w.-]+\/)+[\w.-]+|\b[\w.-]+\.(?:py|pyi|js|jsx|ts|tsx|json|ya?ml|toml|csv|parquet|md)\b|->|=>|==|!=|<=|>=|\|/mu;
|
|
4014
|
+
var TECHNICAL_ANCHOR_RE = /https?:\/\/|`[^`\n]+`|:[a-z][a-z0-9_-]*:|(["'])[^"'\n]+\1|\bv?\d+\.\d+(?:\.\d+)?\b|\b\d+(?:\.\d+)?\s?(?:ns|us|ms|s|sec|secs|seconds?|mins?|minutes?|hours?|days?|bytes?|kib|mib|gib|kb|mb|gb|hz|khz|mhz|px|%)\b|\b[a-z][a-z0-9]*[A-Z][A-Za-z0-9]*\b|\b[A-Za-z][A-Za-z0-9]*_[A-Za-z0-9_]+\b|(?:^|\s)(?:[\w.-]+\/)+[\w.-]+|\b[\w.-]+\.(?:py|pyi|js|jsx|ts|tsx|json|ya?ml|toml|csv|parquet|md)\b|->|=>|==|!=|<=|>=|\|/mu;
|
|
3941
4015
|
function body(comment) {
|
|
3942
4016
|
return comment.value.replace(/^\*/, "").split("\n").map((line) => line.replace(/^\s*\*?\s?/, "")).join("\n").trim();
|
|
3943
4017
|
}
|
|
@@ -4801,7 +4875,8 @@ var DEFAULT_ALLOW = [
|
|
|
4801
4875
|
"[\\\\/](connectors|providers|integrations|adapters|fetchers)[\\\\/]",
|
|
4802
4876
|
"[\\\\/]notifications[\\\\/]",
|
|
4803
4877
|
"[Ss]ervice\\.[cm]?[jt]sx?$",
|
|
4804
|
-
"-(service|connector|adapter|sdk|fetcher)\\.[cm]?[jt]sx?$"
|
|
4878
|
+
"-(service|connector|adapter|sdk|fetcher)\\.[cm]?[jt]sx?$",
|
|
4879
|
+
"[\\\\/][^\\\\/]*(?:Client|client)\\.[cm]?[jt]sx?$"
|
|
4805
4880
|
];
|
|
4806
4881
|
var NON_PRODUCTION_TREE_RE = /[\\/](playwright|cypress|__testfixtures__)[\\/]/;
|
|
4807
4882
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
|
|
@@ -4935,7 +5010,7 @@ var no_raw_fetch_outside_clients_default = createRule({
|
|
|
4935
5010
|
defaultOptions: [{}],
|
|
4936
5011
|
create(context, [options]) {
|
|
4937
5012
|
const filename = context.filename;
|
|
4938
|
-
if (isTestFile(filename) || NON_PRODUCTION_TREE_RE.test(filename)) {
|
|
5013
|
+
if (isTestFile(filename) || isScriptFile(filename) || NON_PRODUCTION_TREE_RE.test(filename)) {
|
|
4939
5014
|
return {};
|
|
4940
5015
|
}
|
|
4941
5016
|
const patterns = options?.allow ?? DEFAULT_ALLOW;
|
|
@@ -4975,10 +5050,15 @@ var no_raw_fetch_outside_clients_default = createRule({
|
|
|
4975
5050
|
function isInternalApiUrl(node) {
|
|
4976
5051
|
const resolved = resolveNode2(node ?? void 0);
|
|
4977
5052
|
if (resolved?.type === AST_NODE_TYPES18.Literal) {
|
|
4978
|
-
return typeof resolved.value === "string" && resolved.value
|
|
5053
|
+
return typeof resolved.value === "string" && /^\/(?!\/)(?:[^/]+\/)*api(?:\/|$)/.test(resolved.value);
|
|
4979
5054
|
}
|
|
4980
5055
|
if (resolved?.type === AST_NODE_TYPES18.TemplateLiteral) {
|
|
4981
|
-
|
|
5056
|
+
const prefix = resolved.quasis[0]?.value.cooked;
|
|
5057
|
+
return typeof prefix === "string" && /^\/(?!\/)(?:[^/]+\/)*api(?:\/|$)/.test(prefix);
|
|
5058
|
+
}
|
|
5059
|
+
if (resolved?.type === AST_NODE_TYPES18.CallExpression && resolved.callee.type === AST_NODE_TYPES18.Identifier && resolved.callee.name === "withBase") {
|
|
5060
|
+
const first = resolved.arguments[0];
|
|
5061
|
+
return first !== void 0 && first.type !== AST_NODE_TYPES18.SpreadElement ? isInternalApiUrl(first) : false;
|
|
4982
5062
|
}
|
|
4983
5063
|
return resolved?.type === AST_NODE_TYPES18.BinaryExpression && resolved.operator === "+" && isInternalApiUrl(resolved.left);
|
|
4984
5064
|
}
|
|
@@ -5822,12 +5902,17 @@ var BLOB_REDACTION_TOKENS = /* @__PURE__ */ new Set([
|
|
|
5822
5902
|
"public"
|
|
5823
5903
|
]);
|
|
5824
5904
|
function rawBlobValueName(value) {
|
|
5905
|
+
if (value.type === "AwaitExpression") return rawBlobValueName(value.argument);
|
|
5906
|
+
if (value.type === "ChainExpression") return rawBlobValueName(value.expression);
|
|
5825
5907
|
if (value.type === "Identifier") {
|
|
5826
5908
|
return isRawBlobName(value.name) ? value.name : null;
|
|
5827
5909
|
}
|
|
5828
5910
|
if (value.type === "MemberExpression" && !value.computed && value.property.type === "Identifier") {
|
|
5829
5911
|
return isRawBlobName(value.property.name) ? value.property.name : null;
|
|
5830
5912
|
}
|
|
5913
|
+
if (value.type === "CallExpression" && value.arguments.length === 0 && value.callee.type === "MemberExpression" && !value.callee.computed && value.callee.object.type === "Identifier" && /^(?:res|response|\w+Response)$/.test(value.callee.object.name) && value.callee.property.type === "Identifier" && (value.callee.property.name === "json" || value.callee.property.name === "text")) {
|
|
5914
|
+
return `${value.callee.object.name}.${value.callee.property.name}()`;
|
|
5915
|
+
}
|
|
5831
5916
|
return null;
|
|
5832
5917
|
}
|
|
5833
5918
|
function isRawBlobName(name) {
|
|
@@ -6492,6 +6577,9 @@ var isExplanatory = (comment) => !DIRECTIVE_COMMENT_RE.test(comment.value);
|
|
|
6492
6577
|
function isTeardownCall(node) {
|
|
6493
6578
|
return node.type === AST_NODE_TYPES24.CallExpression && node.callee.type === AST_NODE_TYPES24.MemberExpression && !node.callee.computed && node.callee.property.type === AST_NODE_TYPES24.Identifier && TEARDOWN_METHODS.has(node.callee.property.name);
|
|
6494
6579
|
}
|
|
6580
|
+
function isCancelledWebShare(node) {
|
|
6581
|
+
return node.type === AST_NODE_TYPES24.CallExpression && node.callee.type === AST_NODE_TYPES24.MemberExpression && !node.callee.computed && node.callee.object.type === AST_NODE_TYPES24.Identifier && node.callee.object.name === "navigator" && node.callee.property.type === AST_NODE_TYPES24.Identifier && node.callee.property.name === "share";
|
|
6582
|
+
}
|
|
6495
6583
|
function isSilentHandler(handler) {
|
|
6496
6584
|
const body2 = handler.body;
|
|
6497
6585
|
if (body2.type !== AST_NODE_TYPES24.BlockStatement) {
|
|
@@ -6541,7 +6629,7 @@ var no_silent_promise_catch_default = createRule({
|
|
|
6541
6629
|
},
|
|
6542
6630
|
defaultOptions: [],
|
|
6543
6631
|
create(context) {
|
|
6544
|
-
if (isTestFile(context.filename)) {
|
|
6632
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename)) {
|
|
6545
6633
|
return {};
|
|
6546
6634
|
}
|
|
6547
6635
|
const hasExplanatoryComment = (call, handler) => {
|
|
@@ -6574,7 +6662,10 @@ var no_silent_promise_catch_default = createRule({
|
|
|
6574
6662
|
if (isTeardownCall(node.callee.object)) {
|
|
6575
6663
|
return;
|
|
6576
6664
|
}
|
|
6577
|
-
if (node.
|
|
6665
|
+
if (isCancelledWebShare(node.callee.object)) {
|
|
6666
|
+
return;
|
|
6667
|
+
}
|
|
6668
|
+
if (node.parent.type === AST_NODE_TYPES24.MemberExpression && node.parent.object === node && !node.parent.computed && node.parent.property.type === AST_NODE_TYPES24.Identifier && node.parent.property.name === "then") {
|
|
6578
6669
|
return;
|
|
6579
6670
|
}
|
|
6580
6671
|
const expectedArguments = method === "catch" ? 1 : 2;
|
|
@@ -6736,22 +6827,25 @@ var noStorageInStatelessModulesDocumentation = {
|
|
|
6736
6827
|
rationale: "Private storage in a stateless workflow creates another source of truth that can silently diverge.",
|
|
6737
6828
|
remediation: "Read from the system of record or derive state from an artifact the workflow already produces.",
|
|
6738
6829
|
category: "architecture",
|
|
6739
|
-
limitations: ["The rule is disabled until module path patterns are configured
|
|
6830
|
+
limitations: ["The rule is disabled until module path patterns are configured, recognizes only configured storage method names, and requires storage-like receiver evidence for the overloaded `put` method."],
|
|
6740
6831
|
examples: [
|
|
6741
6832
|
{ id: "system-of-record", title: "Read from the system of record", outcome: "no-match", files: [{ path: "src/engineer-digest/post.ts", source: "const issues = await linear.listIssues();" }], focusPath: "src/engineer-digest/post.ts", expectedCount: 0, public: true },
|
|
6742
6833
|
{ id: "private-storage", title: "Do not write private state in a stateless module", outcome: "match", files: [{ path: "src/engineer-digest/post.ts", source: "await kv.put('digest:last', timestamp);" }], focusPath: "src/engineer-digest/post.ts", expectedCount: 1, public: true }
|
|
6743
6834
|
]
|
|
6744
6835
|
};
|
|
6745
6836
|
function compile2(patterns) {
|
|
6746
|
-
|
|
6747
|
-
for (const pattern of patterns) {
|
|
6748
|
-
try {
|
|
6749
|
-
compiled.push(new RegExp(pattern));
|
|
6750
|
-
} catch {
|
|
6751
|
-
}
|
|
6752
|
-
}
|
|
6753
|
-
return compiled;
|
|
6837
|
+
return patterns.map((pattern) => new RegExp(pattern));
|
|
6754
6838
|
}
|
|
6839
|
+
var STORAGE_RECEIVER_WORDS = /* @__PURE__ */ new Set([
|
|
6840
|
+
"bucket",
|
|
6841
|
+
"cache",
|
|
6842
|
+
"kv",
|
|
6843
|
+
"namespace",
|
|
6844
|
+
"r2",
|
|
6845
|
+
"redis",
|
|
6846
|
+
"storage",
|
|
6847
|
+
"store"
|
|
6848
|
+
]);
|
|
6755
6849
|
function storageMethodName(node, methods) {
|
|
6756
6850
|
const callee = node.callee;
|
|
6757
6851
|
if (callee.type !== AST_NODE_TYPES26.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES26.Identifier) {
|
|
@@ -6764,8 +6858,31 @@ function storageMethodName(node, methods) {
|
|
|
6764
6858
|
if (node.arguments.length < (MIN_ARGUMENTS.get(name) ?? 1)) {
|
|
6765
6859
|
return null;
|
|
6766
6860
|
}
|
|
6861
|
+
if (name === "put" && !isStorageLikeReceiver(callee.object)) {
|
|
6862
|
+
return null;
|
|
6863
|
+
}
|
|
6767
6864
|
return name;
|
|
6768
6865
|
}
|
|
6866
|
+
function isStorageLikeReceiver(node) {
|
|
6867
|
+
if (node.type === AST_NODE_TYPES26.Identifier) {
|
|
6868
|
+
return isStorageIdentifier(node.name);
|
|
6869
|
+
}
|
|
6870
|
+
if (node.type !== AST_NODE_TYPES26.MemberExpression) {
|
|
6871
|
+
return false;
|
|
6872
|
+
}
|
|
6873
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES26.Identifier && isStorageIdentifier(node.property.name)) {
|
|
6874
|
+
return true;
|
|
6875
|
+
}
|
|
6876
|
+
return isStorageLikeReceiver(node.object);
|
|
6877
|
+
}
|
|
6878
|
+
function isStorageIdentifier(name) {
|
|
6879
|
+
return identifierWords(name).some(
|
|
6880
|
+
(word) => STORAGE_RECEIVER_WORDS.has(word.toLowerCase())
|
|
6881
|
+
);
|
|
6882
|
+
}
|
|
6883
|
+
function identifierWords(name) {
|
|
6884
|
+
return name.match(/[A-Z]?[a-z]+|[A-Z]+(?=[A-Z]|$)|\d+/gu) ?? [name];
|
|
6885
|
+
}
|
|
6769
6886
|
var no_storage_in_stateless_modules_default = createRule({
|
|
6770
6887
|
name: "no-storage-in-stateless-modules",
|
|
6771
6888
|
documentation: noStorageInStatelessModulesDocumentation,
|
|
@@ -6886,7 +7003,20 @@ function isStringInitializedVariable(variable) {
|
|
|
6886
7003
|
if (declarator.type !== "VariableDeclarator") {
|
|
6887
7004
|
return false;
|
|
6888
7005
|
}
|
|
6889
|
-
|
|
7006
|
+
if (isStringLiteralInit(declarator.init)) return true;
|
|
7007
|
+
if (declarator.id.type === "Identifier" && declarator.id.typeAnnotation?.typeAnnotation.type === "TSStringKeyword") {
|
|
7008
|
+
return true;
|
|
7009
|
+
}
|
|
7010
|
+
return isTemplateStringsArrayElement(declarator.init, variable.scope);
|
|
7011
|
+
}
|
|
7012
|
+
function isTemplateStringsArrayElement(node, scope) {
|
|
7013
|
+
if (node?.type !== "MemberExpression" || !node.computed || node.object.type !== "Identifier") {
|
|
7014
|
+
return false;
|
|
7015
|
+
}
|
|
7016
|
+
const source = findVariable(scope, node.object.name);
|
|
7017
|
+
if (source?.defs.length !== 1) return false;
|
|
7018
|
+
const name = source.defs[0]?.name;
|
|
7019
|
+
return name?.type === "Identifier" && name.typeAnnotation?.typeAnnotation.type === "TSTypeReference" && name.typeAnnotation.typeAnnotation.typeName.type === "Identifier" && name.typeAnnotation.typeAnnotation.typeName.name === "TemplateStringsArray";
|
|
6890
7020
|
}
|
|
6891
7021
|
function isStringLiteralInit(node) {
|
|
6892
7022
|
if (node === null) {
|
|
@@ -6920,12 +7050,13 @@ function isConcatOperand(node, target) {
|
|
|
6920
7050
|
}
|
|
6921
7051
|
return false;
|
|
6922
7052
|
}
|
|
6923
|
-
function isDeclaredInsideLoop(variable,
|
|
7053
|
+
function isDeclaredInsideLoop(variable, repetition) {
|
|
6924
7054
|
const def = variable.defs[0];
|
|
6925
7055
|
if (def === void 0) {
|
|
6926
7056
|
return false;
|
|
6927
7057
|
}
|
|
6928
|
-
const body2 =
|
|
7058
|
+
const body2 = repetition.type === "CallExpression" ? repetition.arguments[0] : repetition.body;
|
|
7059
|
+
if (body2 === void 0 || body2.type === "SpreadElement") return false;
|
|
6929
7060
|
const [declStart, declEnd] = def.node.range;
|
|
6930
7061
|
const [bodyStart, bodyEnd] = body2.range;
|
|
6931
7062
|
return declStart >= bodyStart && declEnd <= bodyEnd;
|
|
@@ -6934,6 +7065,9 @@ function enclosingLoop(node) {
|
|
|
6934
7065
|
let child = node;
|
|
6935
7066
|
let parent = node.parent;
|
|
6936
7067
|
while (parent !== void 0 && parent !== null) {
|
|
7068
|
+
if ((parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression") && parent.parent.type === "CallExpression" && parent.parent.arguments[0] === parent && parent.parent.callee.type === "MemberExpression" && !parent.parent.callee.computed && parent.parent.callee.property.type === "Identifier" && parent.parent.callee.property.name === "forEach") {
|
|
7069
|
+
return parent.parent;
|
|
7070
|
+
}
|
|
6937
7071
|
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
6938
7072
|
const loop = parent;
|
|
6939
7073
|
if (loop.body === child) {
|
|
@@ -6945,6 +7079,17 @@ function enclosingLoop(node) {
|
|
|
6945
7079
|
}
|
|
6946
7080
|
return null;
|
|
6947
7081
|
}
|
|
7082
|
+
function isSmallStaticForLoop(node) {
|
|
7083
|
+
if (node.type !== "ForStatement" || node.init?.type !== "VariableDeclaration" || node.init.declarations.length !== 1 || node.test?.type !== "BinaryExpression" || node.test.operator !== "<" && node.test.operator !== "<=" || node.update?.type !== "UpdateExpression" || node.update.operator !== "++") {
|
|
7084
|
+
return false;
|
|
7085
|
+
}
|
|
7086
|
+
const declaration = node.init.declarations[0];
|
|
7087
|
+
if (declaration?.id.type !== "Identifier" || declaration.init?.type !== "Literal" || typeof declaration.init.value !== "number" || !Number.isInteger(declaration.init.value) || node.test.left.type !== "Identifier" || node.test.left.name !== declaration.id.name || node.test.right.type !== "Literal" || typeof node.test.right.value !== "number" || !Number.isInteger(node.test.right.value) || node.update.argument.type !== "Identifier" || node.update.argument.name !== declaration.id.name) {
|
|
7088
|
+
return false;
|
|
7089
|
+
}
|
|
7090
|
+
const iterations = node.test.right.value - declaration.init.value + (node.test.operator === "<=" ? 1 : 0);
|
|
7091
|
+
return iterations >= 0 && iterations <= 8;
|
|
7092
|
+
}
|
|
6948
7093
|
var no_string_concat_in_loop_default = createRule({
|
|
6949
7094
|
name: "no-string-concat-in-loop",
|
|
6950
7095
|
documentation: noStringConcatInLoopDocumentation,
|
|
@@ -6977,6 +7122,9 @@ var no_string_concat_in_loop_default = createRule({
|
|
|
6977
7122
|
if (loop === null) {
|
|
6978
7123
|
return;
|
|
6979
7124
|
}
|
|
7125
|
+
if (isSmallStaticForLoop(loop)) {
|
|
7126
|
+
return;
|
|
7127
|
+
}
|
|
6980
7128
|
const scope = context.sourceCode.getScope(node);
|
|
6981
7129
|
const variable = findVariable(scope, node.left.name);
|
|
6982
7130
|
if (variable === void 0) {
|
|
@@ -7136,12 +7284,86 @@ var no_tautological_expect_default = createRule({
|
|
|
7136
7284
|
});
|
|
7137
7285
|
|
|
7138
7286
|
// src/rules/no-typed-doc-sections.ts
|
|
7287
|
+
var TYPED_TAG_RE2 = /^\s*@(arg|argument|param|return|returns|yield|yields)\b(.*)$/iu;
|
|
7288
|
+
var PARAM_TAGS2 = /* @__PURE__ */ new Set(["arg", "argument", "param"]);
|
|
7289
|
+
var PARAMETER_FILLER = /* @__PURE__ */ new Set([
|
|
7290
|
+
"a",
|
|
7291
|
+
"an",
|
|
7292
|
+
"argument",
|
|
7293
|
+
"given",
|
|
7294
|
+
"input",
|
|
7295
|
+
"parameter",
|
|
7296
|
+
"passed",
|
|
7297
|
+
"provided",
|
|
7298
|
+
"the",
|
|
7299
|
+
"value"
|
|
7300
|
+
]);
|
|
7301
|
+
var RESULT_FILLER = /* @__PURE__ */ new Set([
|
|
7302
|
+
"a",
|
|
7303
|
+
"an",
|
|
7304
|
+
"array",
|
|
7305
|
+
"boolean",
|
|
7306
|
+
"generator",
|
|
7307
|
+
"number",
|
|
7308
|
+
"object",
|
|
7309
|
+
"output",
|
|
7310
|
+
"promise",
|
|
7311
|
+
"result",
|
|
7312
|
+
"return",
|
|
7313
|
+
"returned",
|
|
7314
|
+
"returns",
|
|
7315
|
+
"string",
|
|
7316
|
+
"the",
|
|
7317
|
+
"value"
|
|
7318
|
+
]);
|
|
7319
|
+
function hasVacuousTypedTag(text) {
|
|
7320
|
+
const tags = typedTags(text);
|
|
7321
|
+
return tags.length > 0 && tags.some(isVacuousTag);
|
|
7322
|
+
}
|
|
7323
|
+
function typedTags(text) {
|
|
7324
|
+
const tags = [];
|
|
7325
|
+
for (const raw of text.split("\n")) {
|
|
7326
|
+
const match = TYPED_TAG_RE2.exec(raw);
|
|
7327
|
+
if (match !== null) {
|
|
7328
|
+
tags.push({ kind: (match[1] ?? "").toLowerCase(), payload: (match[2] ?? "").trim() });
|
|
7329
|
+
} else if (tags.length > 0 && raw.trim().length > 0 && !raw.trim().startsWith("@")) {
|
|
7330
|
+
const last = tags.at(-1);
|
|
7331
|
+
last.payload = `${last.payload} ${raw.trim()}`.trim();
|
|
7332
|
+
}
|
|
7333
|
+
}
|
|
7334
|
+
return tags.map(({ kind, payload }) => {
|
|
7335
|
+
let rest = payload.replace(/^\{[^}\n]+\}\s*/u, "").trim();
|
|
7336
|
+
if (!PARAM_TAGS2.has(kind)) {
|
|
7337
|
+
return { kind, name: null, description: rest.replace(/^-\s*/u, "").trim() };
|
|
7338
|
+
}
|
|
7339
|
+
const match = /^(\[[^\]]+\]|[A-Za-z_$][\w$.[\]-]*)(?:\s+-\s*|\s+)?(.*)$/u.exec(rest);
|
|
7340
|
+
if (match === null) return { kind, name: null, description: "" };
|
|
7341
|
+
const rawName = (match[1] ?? "").replace(/^\[/u, "").replace(/\]$/u, "").split("=")[0] ?? "";
|
|
7342
|
+
rest = (match[2] ?? "").trim();
|
|
7343
|
+
return { kind, name: rawName, description: rest };
|
|
7344
|
+
});
|
|
7345
|
+
}
|
|
7346
|
+
function isVacuousTag(tag) {
|
|
7347
|
+
const description = words(tag.description).map(canonicalWord);
|
|
7348
|
+
if (description.length === 0) return true;
|
|
7349
|
+
if (tag.name === null) return description.every((word) => RESULT_FILLER.has(word));
|
|
7350
|
+
const nameWords2 = new Set(words(tag.name).map(canonicalWord));
|
|
7351
|
+
return description.every((word) => PARAMETER_FILLER.has(word) || nameWords2.has(word));
|
|
7352
|
+
}
|
|
7353
|
+
function words(text) {
|
|
7354
|
+
return text.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").toLowerCase().match(/[a-z][a-z0-9]*/gu) ?? [];
|
|
7355
|
+
}
|
|
7356
|
+
function canonicalWord(word) {
|
|
7357
|
+
if (["identifier", "identifiers", "ids"].includes(word)) return "id";
|
|
7358
|
+
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
7359
|
+
return word;
|
|
7360
|
+
}
|
|
7139
7361
|
var noTypedDocSectionsDocumentation = {
|
|
7140
7362
|
summary: "Reject typed-signature repetition while preserving behavior that types cannot express.",
|
|
7141
7363
|
rationale: "Parameter and return tags repeat typed signatures and can drift without adding runtime behavior or constraints.",
|
|
7142
7364
|
remediation: "Remove repeated parameter and return tags; retain documentation for behavior, failures, and external contracts.",
|
|
7143
7365
|
category: "maintainability",
|
|
7144
|
-
limitations: ["
|
|
7366
|
+
limitations: ["Description-free or name-restating parameter and return tags are reported only when the documented function has corresponding explicit TypeScript types."],
|
|
7145
7367
|
examples: [
|
|
7146
7368
|
{
|
|
7147
7369
|
id: "behavioral-documentation",
|
|
@@ -7179,7 +7401,7 @@ var no_typed_doc_sections_default = createRule({
|
|
|
7179
7401
|
return {
|
|
7180
7402
|
Program() {
|
|
7181
7403
|
for (const group of proseGroups(context.filename, context.sourceCode, true)) {
|
|
7182
|
-
if (group.hasTypedTags && documentsTypedFunction(context.sourceCode, group.comment)) {
|
|
7404
|
+
if (group.hasTypedTags && hasVacuousTypedTag(group.text) && documentsTypedFunction(context.sourceCode, group.comment)) {
|
|
7183
7405
|
context.report({ node: group.comment, messageId: "typedSection" });
|
|
7184
7406
|
}
|
|
7185
7407
|
}
|
|
@@ -7280,28 +7502,32 @@ var STOPWORDS3 = /* @__PURE__ */ new Set([
|
|
|
7280
7502
|
"with"
|
|
7281
7503
|
]);
|
|
7282
7504
|
var DIRECTIVE_RE5 = /^\s*(?:eslint\b|eslint-|sarj-noqa\b|@ts-|prettier|biome-|c8\b|v8\b|istanbul\b|todo\b|fixme\b|hack\b|xxx\b)/i;
|
|
7505
|
+
var UNIT_NAME_SUFFIX_RE = /(?:_(?:NS|US|MS|S|SEC|SECS|SECOND|SECONDS|MIN|MINS|MINUTE|MINUTES|HOUR|HOURS|DAY|DAYS|BYTE|BYTES|KB|MB|GB|HZ|KHZ|MHZ|PX)|(?:Ns|Us|Ms|Sec|Secs|Second|Seconds|Min|Mins|Minute|Minutes|Hour|Hours|Day|Days|Byte|Bytes|Kb|Mb|Gb|Hz|Khz|Mhz|Px))$/u;
|
|
7283
7506
|
function narratesValue(body2, code) {
|
|
7284
7507
|
if (body2.length === 0 || DIRECTIVE_RE5.test(body2) || hasExternalReference(body2)) return false;
|
|
7285
7508
|
const codeNumbers = numbersIn(code);
|
|
7286
7509
|
if (codeNumbers.size === 0) return false;
|
|
7287
|
-
const
|
|
7288
|
-
if (
|
|
7510
|
+
const words2 = (body2.match(WORD_RE3) ?? []).map((word) => word.toLowerCase());
|
|
7511
|
+
if (words2.length === 0) return false;
|
|
7289
7512
|
const commentNumbers = numbersIn(body2);
|
|
7290
7513
|
if (commentNumbers.size === 0) return false;
|
|
7291
7514
|
for (const number of commentNumbers) {
|
|
7292
7515
|
if (!codeNumbers.has(number)) return false;
|
|
7293
7516
|
}
|
|
7294
|
-
if (!
|
|
7517
|
+
if (!words2.some((word) => UNIT_WORDS.has(word))) return false;
|
|
7295
7518
|
const identifiers = codeTokens(code);
|
|
7296
7519
|
const stems = /* @__PURE__ */ new Set();
|
|
7297
7520
|
for (const token of identifiers) stems.add(stem(token));
|
|
7298
|
-
return
|
|
7521
|
+
return words2.every(
|
|
7299
7522
|
(word) => STOPWORDS3.has(word) || UNIT_WORDS.has(word) || commentNumbers.has(word) || identifiers.has(word) || stems.has(stem(word))
|
|
7300
7523
|
);
|
|
7301
7524
|
}
|
|
7302
7525
|
function numbersIn(text) {
|
|
7303
7526
|
return new Set(text.match(NUMBER_RE) ?? []);
|
|
7304
7527
|
}
|
|
7528
|
+
function nameAlreadyCarriesUnit(code) {
|
|
7529
|
+
return (code.match(/[A-Za-z_$][\w$]*/gu) ?? []).some((identifier) => UNIT_NAME_SUFFIX_RE.test(identifier));
|
|
7530
|
+
}
|
|
7305
7531
|
var no_trailing_value_narration_default = createRule({
|
|
7306
7532
|
name: "no-trailing-value-narration",
|
|
7307
7533
|
documentation: noTrailingValueNarrationDocumentation,
|
|
@@ -7312,6 +7538,7 @@ var no_trailing_value_narration_default = createRule({
|
|
|
7312
7538
|
},
|
|
7313
7539
|
schema: [],
|
|
7314
7540
|
messages: {
|
|
7541
|
+
deleteNarration: "Trailing comment restates the literal and the identifier already names its unit \u2014 delete the comment so it cannot drift.",
|
|
7315
7542
|
narratesValue: "Trailing comment restates the literal on this line \u2014 put the unit in the name (STALE_TIME_MS) so it cannot drift."
|
|
7316
7543
|
}
|
|
7317
7544
|
},
|
|
@@ -7344,7 +7571,10 @@ var no_trailing_value_narration_default = createRule({
|
|
|
7344
7571
|
const code = line.slice(0, comment.loc.start.column);
|
|
7345
7572
|
const body2 = comment.value.replace(/^\*+/, "").replace(/\*+$/, "").trim();
|
|
7346
7573
|
if (narratesValue(body2, code)) {
|
|
7347
|
-
context.report({
|
|
7574
|
+
context.report({
|
|
7575
|
+
node: comment,
|
|
7576
|
+
messageId: nameAlreadyCarriesUnit(code) ? "deleteNarration" : "narratesValue"
|
|
7577
|
+
});
|
|
7348
7578
|
}
|
|
7349
7579
|
}
|
|
7350
7580
|
}
|
|
@@ -8262,7 +8492,8 @@ var no_unsafe_mock_casting_default = createRule({
|
|
|
8262
8492
|
// src/rules/no-zod-native-enum.ts
|
|
8263
8493
|
import {
|
|
8264
8494
|
ESLintUtils as ESLintUtils2,
|
|
8265
|
-
AST_NODE_TYPES as AST_NODE_TYPES34
|
|
8495
|
+
AST_NODE_TYPES as AST_NODE_TYPES34,
|
|
8496
|
+
ASTUtils as ASTUtils8
|
|
8266
8497
|
} from "@typescript-eslint/utils";
|
|
8267
8498
|
import * as ts from "typescript";
|
|
8268
8499
|
var noZodNativeEnumDocumentation = {
|
|
@@ -8391,15 +8622,23 @@ var no_zod_native_enum_default = createRule({
|
|
|
8391
8622
|
} catch {
|
|
8392
8623
|
services = null;
|
|
8393
8624
|
}
|
|
8394
|
-
const
|
|
8395
|
-
const
|
|
8625
|
+
const zodImportedBindings = /* @__PURE__ */ new Map();
|
|
8626
|
+
const zodNamespaceBindings = /* @__PURE__ */ new Set();
|
|
8627
|
+
function resolvedBinding(identifier) {
|
|
8628
|
+
return ASTUtils8.findVariable(
|
|
8629
|
+
sourceCode.getScope(identifier),
|
|
8630
|
+
identifier.name
|
|
8631
|
+
);
|
|
8632
|
+
}
|
|
8396
8633
|
function isZodMemberCall(node, api) {
|
|
8397
8634
|
const callee = node.callee;
|
|
8398
|
-
if (callee.type === AST_NODE_TYPES34.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES34.Identifier &&
|
|
8399
|
-
|
|
8635
|
+
if (callee.type === AST_NODE_TYPES34.MemberExpression && !callee.computed && callee.object.type === AST_NODE_TYPES34.Identifier && callee.property.type === AST_NODE_TYPES34.Identifier) {
|
|
8636
|
+
const binding = resolvedBinding(callee.object);
|
|
8637
|
+
return binding !== null && zodNamespaceBindings.has(binding) && callee.property.name === api;
|
|
8400
8638
|
}
|
|
8401
8639
|
if (callee.type === AST_NODE_TYPES34.Identifier) {
|
|
8402
|
-
|
|
8640
|
+
const binding = resolvedBinding(callee);
|
|
8641
|
+
return binding !== null && zodImportedBindings.get(binding) === api;
|
|
8403
8642
|
}
|
|
8404
8643
|
return false;
|
|
8405
8644
|
}
|
|
@@ -8434,10 +8673,14 @@ var no_zod_native_enum_default = createRule({
|
|
|
8434
8673
|
}
|
|
8435
8674
|
for (const spec of node.specifiers) {
|
|
8436
8675
|
if (spec.type === AST_NODE_TYPES34.ImportNamespaceSpecifier || spec.type === AST_NODE_TYPES34.ImportDefaultSpecifier || spec.type === AST_NODE_TYPES34.ImportSpecifier && (spec.imported.type === AST_NODE_TYPES34.Identifier ? spec.imported.name === "z" : spec.imported.value === "z")) {
|
|
8437
|
-
|
|
8676
|
+
const binding = resolvedBinding(spec.local);
|
|
8677
|
+
if (binding !== null) zodNamespaceBindings.add(binding);
|
|
8438
8678
|
}
|
|
8439
8679
|
if (spec.type === AST_NODE_TYPES34.ImportSpecifier && spec.imported.type === AST_NODE_TYPES34.Identifier) {
|
|
8440
|
-
|
|
8680
|
+
const binding = resolvedBinding(spec.local);
|
|
8681
|
+
if (binding !== null) {
|
|
8682
|
+
zodImportedBindings.set(binding, spec.imported.name);
|
|
8683
|
+
}
|
|
8441
8684
|
}
|
|
8442
8685
|
}
|
|
8443
8686
|
},
|
|
@@ -8474,7 +8717,7 @@ var no_zod_native_enum_default = createRule({
|
|
|
8474
8717
|
});
|
|
8475
8718
|
|
|
8476
8719
|
// src/rules/test-loops-over-literal-cases.ts
|
|
8477
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES35, ASTUtils as
|
|
8720
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES35, ASTUtils as ASTUtils9 } from "@typescript-eslint/utils";
|
|
8478
8721
|
var testLoopsOverLiteralCasesDocumentation = {
|
|
8479
8722
|
summary: "Disallow assertions over an inline literal case loop in a test; parameterization reports and names every case independently.",
|
|
8480
8723
|
rationale: "A loop is reported as one test, so failures hide the individual case name and may stop later cases from running.",
|
|
@@ -8637,7 +8880,7 @@ var test_loops_over_literal_cases_default = createRule({
|
|
|
8637
8880
|
return {};
|
|
8638
8881
|
}
|
|
8639
8882
|
const isFrameworkIdentifier = (identifier, modules) => {
|
|
8640
|
-
const variable =
|
|
8883
|
+
const variable = ASTUtils9.findVariable(context.sourceCode.getScope(identifier), identifier.name);
|
|
8641
8884
|
if (variable === null || variable.defs.length === 0) return true;
|
|
8642
8885
|
return variable.defs.some((definition) => {
|
|
8643
8886
|
let current = definition.node;
|
|
@@ -8830,8 +9073,10 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
|
8830
9073
|
let statusMemberCount = 0;
|
|
8831
9074
|
let hasFailurePayload = false;
|
|
8832
9075
|
let hasSuccessPayload = false;
|
|
9076
|
+
let hasUnrecognizedMember = false;
|
|
8833
9077
|
for (const member of typeLiteral.members) {
|
|
8834
9078
|
if (member.type !== AST_NODE_TYPES37.TSPropertySignature) {
|
|
9079
|
+
hasUnrecognizedMember = true;
|
|
8835
9080
|
continue;
|
|
8836
9081
|
}
|
|
8837
9082
|
const name = getMemberName(member);
|
|
@@ -8840,15 +9085,18 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
|
8840
9085
|
continue;
|
|
8841
9086
|
}
|
|
8842
9087
|
if (!member.optional || isBooleanTyped(member) || name === null) {
|
|
9088
|
+
hasUnrecognizedMember = true;
|
|
8843
9089
|
continue;
|
|
8844
9090
|
}
|
|
8845
9091
|
if (FAILURE_MEMBER_NAMES.has(name)) {
|
|
8846
9092
|
hasFailurePayload = true;
|
|
8847
9093
|
} else if (SUCCESS_PAYLOAD_MEMBER_NAMES.has(name)) {
|
|
8848
9094
|
hasSuccessPayload = true;
|
|
9095
|
+
} else {
|
|
9096
|
+
hasUnrecognizedMember = true;
|
|
8849
9097
|
}
|
|
8850
9098
|
}
|
|
8851
|
-
return statusMemberCount === REQUIRED_STATUS_MEMBER_COUNT && hasFailurePayload && hasSuccessPayload;
|
|
9099
|
+
return statusMemberCount === REQUIRED_STATUS_MEMBER_COUNT && hasFailurePayload && (hasSuccessPayload || !hasUnrecognizedMember);
|
|
8852
9100
|
}
|
|
8853
9101
|
function getMemberName(member) {
|
|
8854
9102
|
if (member.type !== AST_NODE_TYPES37.TSPropertySignature) {
|
|
@@ -9069,7 +9317,7 @@ var prefer_input_group_search_default = createRule({
|
|
|
9069
9317
|
});
|
|
9070
9318
|
|
|
9071
9319
|
// src/rules/prefer-immutable-module-constant.ts
|
|
9072
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES39, ASTUtils as
|
|
9320
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES39, ASTUtils as ASTUtils10 } from "@typescript-eslint/utils";
|
|
9073
9321
|
var preferImmutableModuleConstantDocumentation = {
|
|
9074
9322
|
summary: "Require module-level constant collections to expose readonly state.",
|
|
9075
9323
|
rationale: "A const binding prevents reassignment but does not stop callers from mutating its array, object, Set, or Map contents.",
|
|
@@ -9222,7 +9470,7 @@ var prefer_immutable_module_constant_default = createRule({
|
|
|
9222
9470
|
create(context) {
|
|
9223
9471
|
const sourceCode = context.sourceCode;
|
|
9224
9472
|
const isUnshadowedGlobal = (identifier) => {
|
|
9225
|
-
const variable =
|
|
9473
|
+
const variable = ASTUtils10.findVariable(sourceCode.getScope(identifier), identifier.name);
|
|
9226
9474
|
return variable === null || variable.defs.length === 0;
|
|
9227
9475
|
};
|
|
9228
9476
|
if (JAVASCRIPT_FILE_RE.test(context.filename) || isTestFile(context.filename) || isGeneratedFile(context.filename, sourceCode.getText())) {
|
|
@@ -10139,7 +10387,7 @@ var prefer_module_level_schema_default = createRule({
|
|
|
10139
10387
|
});
|
|
10140
10388
|
|
|
10141
10389
|
// src/rules/prefer-native-random-uuid.ts
|
|
10142
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES43, ASTUtils as
|
|
10390
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES43, ASTUtils as ASTUtils11 } from "@typescript-eslint/utils";
|
|
10143
10391
|
var preferNativeRandomUuidDocumentation = {
|
|
10144
10392
|
summary: "Prefer `globalThis.crypto.randomUUID()` over resolved zero-argument UUID v4 bindings from the `uuid` package.",
|
|
10145
10393
|
rationale: "The platform implementation avoids an unnecessary dependency for standard random UUID generation.",
|
|
@@ -10175,7 +10423,7 @@ var prefer_native_random_uuid_default = createRule({
|
|
|
10175
10423
|
const directBindings = /* @__PURE__ */ new Set();
|
|
10176
10424
|
const namespaceBindings = /* @__PURE__ */ new Set();
|
|
10177
10425
|
function resolve(identifier) {
|
|
10178
|
-
return
|
|
10426
|
+
return ASTUtils11.findVariable(context.sourceCode.getScope(identifier), identifier.name);
|
|
10179
10427
|
}
|
|
10180
10428
|
function record(identifier, destination) {
|
|
10181
10429
|
const variable = resolve(identifier);
|
|
@@ -10238,7 +10486,7 @@ var prefer_native_random_uuid_default = createRule({
|
|
|
10238
10486
|
});
|
|
10239
10487
|
|
|
10240
10488
|
// src/rules/prefer-non-nullable-collection.ts
|
|
10241
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES44, ASTUtils as
|
|
10489
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES44, ASTUtils as ASTUtils12 } from "@typescript-eslint/utils";
|
|
10242
10490
|
var preferNonNullableCollectionDocumentation = {
|
|
10243
10491
|
summary: "Suggest non-null arrays only when local control flow proves the nullish state is equivalent to an empty collection.",
|
|
10244
10492
|
rationale: "A redundant nullish collection state spreads defaults and guards through consumers without carrying information.",
|
|
@@ -10368,14 +10616,14 @@ function directlyCoalesced(node) {
|
|
|
10368
10616
|
return parent?.type === AST_NODE_TYPES44.LogicalExpression && parent.left === node && (parent.operator === "??" || parent.operator === "||") && emptyArray(parent.right);
|
|
10369
10617
|
}
|
|
10370
10618
|
function identifierIsOnlyCoalesced(context, binding, fn) {
|
|
10371
|
-
const variable =
|
|
10619
|
+
const variable = ASTUtils12.findVariable(context.sourceCode.getScope(binding), binding.name);
|
|
10372
10620
|
if (variable === null || variable.references.length === 0) return false;
|
|
10373
10621
|
return variable.references.every(
|
|
10374
10622
|
(reference) => belongsToFunction(reference.identifier, fn) && directlyCoalesced(reference.identifier)
|
|
10375
10623
|
);
|
|
10376
10624
|
}
|
|
10377
10625
|
function memberIsOnlyCoalesced(context, object, property, fn) {
|
|
10378
|
-
const variable =
|
|
10626
|
+
const variable = ASTUtils12.findVariable(context.sourceCode.getScope(object), object.name);
|
|
10379
10627
|
if (variable === null) return false;
|
|
10380
10628
|
const accesses = variable.references.flatMap((reference) => {
|
|
10381
10629
|
if (!belongsToFunction(reference.identifier, fn)) return [null];
|
|
@@ -10749,7 +10997,7 @@ var prefer_schema_for_api_payload_default = createRule({
|
|
|
10749
10997
|
},
|
|
10750
10998
|
defaultOptions: [],
|
|
10751
10999
|
create(context) {
|
|
10752
|
-
if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
11000
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
10753
11001
|
return {};
|
|
10754
11002
|
}
|
|
10755
11003
|
const unvalidatedVariables = /* @__PURE__ */ new Set();
|
|
@@ -12217,6 +12465,9 @@ var requireAssertNeverDocumentation = {
|
|
|
12217
12465
|
};
|
|
12218
12466
|
var isRuntimeHandlingStatement = (statement) => {
|
|
12219
12467
|
if (statement.type === AST_NODE_TYPES49.EmptyStatement) return false;
|
|
12468
|
+
if (statement.type === AST_NODE_TYPES49.BreakStatement) {
|
|
12469
|
+
return statement.label !== null;
|
|
12470
|
+
}
|
|
12220
12471
|
if (statement.type === AST_NODE_TYPES49.TSTypeAliasDeclaration || statement.type === AST_NODE_TYPES49.TSInterfaceDeclaration) {
|
|
12221
12472
|
return false;
|
|
12222
12473
|
}
|
|
@@ -12246,7 +12497,12 @@ function isExhaustiveFiniteSwitch(node, services) {
|
|
|
12246
12497
|
const discriminant = services.esTreeNodeToTSNodeMap.get(node.discriminant);
|
|
12247
12498
|
const discriminantType = checker.getTypeAtLocation(discriminant);
|
|
12248
12499
|
const constituents = discriminantType.isUnion() ? discriminantType.types : [discriminantType];
|
|
12249
|
-
if (constituents.length
|
|
12500
|
+
if (!discriminantType.isUnion() || constituents.length < 2) return false;
|
|
12501
|
+
if (constituents.every(
|
|
12502
|
+
(constituent) => (constituent.flags & ts2.TypeFlags.BooleanLiteral) !== 0
|
|
12503
|
+
)) {
|
|
12504
|
+
return false;
|
|
12505
|
+
}
|
|
12250
12506
|
const expected = /* @__PURE__ */ new Set();
|
|
12251
12507
|
for (const constituent of constituents) {
|
|
12252
12508
|
const key = finiteTypeKey(constituent, checker);
|
|
@@ -12314,7 +12570,7 @@ var require_assert_never_default = createRule({
|
|
|
12314
12570
|
});
|
|
12315
12571
|
|
|
12316
12572
|
// src/rules/require-fetch-timeout.ts
|
|
12317
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES50, ASTUtils as
|
|
12573
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES50, ASTUtils as ASTUtils13 } from "@typescript-eslint/utils";
|
|
12318
12574
|
var requireFetchTimeoutDocumentation = {
|
|
12319
12575
|
summary: "Require an abort `signal` (e.g. `AbortSignal.timeout(ms)`) on global `fetch()` calls so stalled upstreams cannot hang the caller forever.",
|
|
12320
12576
|
rationale: "An unbounded request can occupy work indefinitely when an upstream stalls.",
|
|
@@ -12395,7 +12651,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
12395
12651
|
}
|
|
12396
12652
|
function resolvesToGlobal(identifier) {
|
|
12397
12653
|
const scope = context.sourceCode.getScope(identifier);
|
|
12398
|
-
const variable =
|
|
12654
|
+
const variable = ASTUtils13.findVariable(scope, identifier.name);
|
|
12399
12655
|
return variable === null || variable.defs.length === 0;
|
|
12400
12656
|
}
|
|
12401
12657
|
function isGlobalFetchCall2(callee) {
|
|
@@ -12404,6 +12660,26 @@ var require_fetch_timeout_default = createRule({
|
|
|
12404
12660
|
}
|
|
12405
12661
|
return callee.type === AST_NODE_TYPES50.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES50.Identifier && callee.property.name === "fetch" && callee.object.type === AST_NODE_TYPES50.Identifier && GLOBAL_OBJECTS2.has(callee.object.name) && resolvesToGlobal(callee.object);
|
|
12406
12662
|
}
|
|
12663
|
+
function localConstInitProvablyLacksSignal(identifier) {
|
|
12664
|
+
const variable = ASTUtils13.findVariable(
|
|
12665
|
+
context.sourceCode.getScope(identifier),
|
|
12666
|
+
identifier.name
|
|
12667
|
+
);
|
|
12668
|
+
if (variable?.defs.length !== 1) return false;
|
|
12669
|
+
const definition = variable.defs[0];
|
|
12670
|
+
if (definition?.type !== "Variable" || definition.parent.kind !== "const" || definition.node.init?.type !== AST_NODE_TYPES50.ObjectExpression || !initProvablyLacksSignal(definition.node.init)) {
|
|
12671
|
+
return false;
|
|
12672
|
+
}
|
|
12673
|
+
for (const reference of variable.references) {
|
|
12674
|
+
const ref = reference.identifier;
|
|
12675
|
+
if (ref === identifier || ref === definition.name) continue;
|
|
12676
|
+
const member = ref.parent;
|
|
12677
|
+
if (member.type !== AST_NODE_TYPES50.MemberExpression || member.object !== ref || member.computed || member.property.type !== AST_NODE_TYPES50.Identifier || member.property.name === "signal" || member.parent.type !== AST_NODE_TYPES50.AssignmentExpression || member.parent.left !== member) {
|
|
12678
|
+
return false;
|
|
12679
|
+
}
|
|
12680
|
+
}
|
|
12681
|
+
return true;
|
|
12682
|
+
}
|
|
12407
12683
|
return {
|
|
12408
12684
|
CallExpression(node) {
|
|
12409
12685
|
if (!isGlobalFetchCall2(node.callee)) {
|
|
@@ -12413,7 +12689,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
12413
12689
|
if (node.arguments.length === 1 && first !== void 0 && !isInlineUrl(first, resolvesToGlobal)) {
|
|
12414
12690
|
return;
|
|
12415
12691
|
}
|
|
12416
|
-
if (init === void 0 || initProvablyLacksSignal(init)) {
|
|
12692
|
+
if (init === void 0 || initProvablyLacksSignal(init) || init.type === AST_NODE_TYPES50.Identifier && localConstInitProvablyLacksSignal(init)) {
|
|
12417
12693
|
context.report({ node, messageId: "missingSignal" });
|
|
12418
12694
|
}
|
|
12419
12695
|
}
|
|
@@ -13027,34 +13303,35 @@ var require_static_next_matcher_default = createRule({
|
|
|
13027
13303
|
});
|
|
13028
13304
|
|
|
13029
13305
|
// src/rules/require-zod-form-validation.ts
|
|
13030
|
-
import {
|
|
13306
|
+
import {
|
|
13307
|
+
AST_NODE_TYPES as AST_NODE_TYPES53,
|
|
13308
|
+
ASTUtils as ASTUtils14
|
|
13309
|
+
} from "@typescript-eslint/utils";
|
|
13031
13310
|
var requireZodFormValidationDocumentation = {
|
|
13032
13311
|
summary: "Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object.",
|
|
13033
13312
|
rationale: "FormData values are untrusted strings or files and need runtime validation before use.",
|
|
13034
13313
|
remediation: "Read the value inside a Zod schema's `parse` or `safeParse` input.",
|
|
13035
13314
|
category: "security",
|
|
13315
|
+
limitations: [
|
|
13316
|
+
"Tests are excluded; imported schema-shaped names are trusted when their implementation is outside the linted file.",
|
|
13317
|
+
"Delayed raw-value use is accepted only after an unconditional successful parse in the same block; safeParse remains valid when the raw binding has no unvalidated consumer."
|
|
13318
|
+
],
|
|
13036
13319
|
examples: [
|
|
13037
13320
|
{ id: "validated-form-value", title: "Validate the form value", outcome: "no-match", files: [{ path: "src/action.ts", source: "const input = UserSchema.parse({ name: formData.get('name') });" }], focusPath: "src/action.ts", expectedCount: 0, public: true },
|
|
13038
13321
|
{ id: "raw-form-value", title: "Do not use a raw form value", outcome: "match", files: [{ path: "src/action.ts", source: "const name = formData.get('name');" }], focusPath: "src/action.ts", expectedCount: 1, public: true }
|
|
13039
13322
|
]
|
|
13040
13323
|
};
|
|
13041
|
-
var
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
if (method !== "parse" && method !== "safeParse" && method !== "parseAsync" && method !== "safeParseAsync") {
|
|
13049
|
-
return false;
|
|
13050
|
-
}
|
|
13051
|
-
return looksLikeZodSchema(callee.object);
|
|
13052
|
-
};
|
|
13053
|
-
var looksLikeZodSchema = (node) => {
|
|
13324
|
+
var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
|
|
13325
|
+
"parse",
|
|
13326
|
+
"safeParse",
|
|
13327
|
+
"parseAsync",
|
|
13328
|
+
"safeParseAsync"
|
|
13329
|
+
]);
|
|
13330
|
+
var zodReceiverRoot = (node) => {
|
|
13054
13331
|
let current = node;
|
|
13055
13332
|
while (true) {
|
|
13056
13333
|
if (current.type === AST_NODE_TYPES53.Identifier) {
|
|
13057
|
-
return current
|
|
13334
|
+
return current;
|
|
13058
13335
|
}
|
|
13059
13336
|
if (current.type === AST_NODE_TYPES53.CallExpression) {
|
|
13060
13337
|
current = current.callee;
|
|
@@ -13064,7 +13341,7 @@ var looksLikeZodSchema = (node) => {
|
|
|
13064
13341
|
current = current.object;
|
|
13065
13342
|
continue;
|
|
13066
13343
|
}
|
|
13067
|
-
return
|
|
13344
|
+
return null;
|
|
13068
13345
|
}
|
|
13069
13346
|
};
|
|
13070
13347
|
var isFormDataMethodCall = (node) => {
|
|
@@ -13094,6 +13371,34 @@ var require_zod_form_validation_default = createRule({
|
|
|
13094
13371
|
if (isTestFile(context.filename)) {
|
|
13095
13372
|
return {};
|
|
13096
13373
|
}
|
|
13374
|
+
const zodBindings = /* @__PURE__ */ new Set();
|
|
13375
|
+
const resolvedBinding = (identifier) => ASTUtils14.findVariable(
|
|
13376
|
+
context.sourceCode.getScope(identifier),
|
|
13377
|
+
identifier.name
|
|
13378
|
+
);
|
|
13379
|
+
const isProvablyNonZodLocal = (identifier) => {
|
|
13380
|
+
const binding = resolvedBinding(identifier);
|
|
13381
|
+
if (binding === null || zodBindings.has(binding) || binding.defs.length !== 1) {
|
|
13382
|
+
return false;
|
|
13383
|
+
}
|
|
13384
|
+
const definition = binding.defs[0];
|
|
13385
|
+
if (definition?.type !== "Variable" || definition.node.type !== AST_NODE_TYPES53.VariableDeclarator) {
|
|
13386
|
+
return false;
|
|
13387
|
+
}
|
|
13388
|
+
const init = definition.node.init;
|
|
13389
|
+
return init?.type === AST_NODE_TYPES53.ObjectExpression || init?.type === AST_NODE_TYPES53.ArrayExpression || init?.type === AST_NODE_TYPES53.Literal || init?.type === AST_NODE_TYPES53.ArrowFunctionExpression || init?.type === AST_NODE_TYPES53.FunctionExpression;
|
|
13390
|
+
};
|
|
13391
|
+
const isZodParseCall = (node) => {
|
|
13392
|
+
if (node.type !== AST_NODE_TYPES53.CallExpression) return false;
|
|
13393
|
+
const callee = node.callee;
|
|
13394
|
+
if (callee.type !== AST_NODE_TYPES53.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES53.Identifier || !ZOD_PARSE_METHODS.has(callee.property.name)) {
|
|
13395
|
+
return false;
|
|
13396
|
+
}
|
|
13397
|
+
const root = zodReceiverRoot(callee.object);
|
|
13398
|
+
if (root === null) return false;
|
|
13399
|
+
const binding = resolvedBinding(root);
|
|
13400
|
+
return binding !== null && zodBindings.has(binding) || (root.name === "z" || ZOD_SCHEMA_NAME_RE.test(root.name)) && !isProvablyNonZodLocal(root);
|
|
13401
|
+
};
|
|
13097
13402
|
const isFormSourceIdentifier = (node) => {
|
|
13098
13403
|
if (node.type !== AST_NODE_TYPES53.Identifier) return false;
|
|
13099
13404
|
if (/formdata/i.test(node.name)) return true;
|
|
@@ -13119,14 +13424,15 @@ var require_zod_form_validation_default = createRule({
|
|
|
13119
13424
|
}
|
|
13120
13425
|
return isFormSourceIdentifier(callee.object);
|
|
13121
13426
|
};
|
|
13122
|
-
const
|
|
13427
|
+
const zodParseAncestor = (node) => {
|
|
13123
13428
|
let parent = node.parent;
|
|
13124
13429
|
while (parent !== null && parent !== void 0) {
|
|
13125
|
-
if (isZodParseCall(parent)) return
|
|
13430
|
+
if (isZodParseCall(parent)) return parent;
|
|
13126
13431
|
parent = parent.parent;
|
|
13127
13432
|
}
|
|
13128
|
-
return
|
|
13433
|
+
return null;
|
|
13129
13434
|
};
|
|
13435
|
+
const hasZodParseAncestor = (node) => zodParseAncestor(node) !== null;
|
|
13130
13436
|
const isInstanceofNarrowing = (node) => {
|
|
13131
13437
|
const parent = node.parent;
|
|
13132
13438
|
return parent !== null && parent !== void 0 && parent.type === AST_NODE_TYPES53.BinaryExpression && parent.operator === "instanceof" && parent.left === node && parent.right.type === AST_NODE_TYPES53.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
@@ -13143,14 +13449,111 @@ var require_zod_form_validation_default = createRule({
|
|
|
13143
13449
|
}
|
|
13144
13450
|
return null;
|
|
13145
13451
|
};
|
|
13452
|
+
const containingStatement = (node) => {
|
|
13453
|
+
let current = node;
|
|
13454
|
+
while (current.parent !== void 0) {
|
|
13455
|
+
const parent = current.parent;
|
|
13456
|
+
if (parent.type === AST_NODE_TYPES53.BlockStatement || parent.type === AST_NODE_TYPES53.Program) {
|
|
13457
|
+
return current;
|
|
13458
|
+
}
|
|
13459
|
+
current = parent;
|
|
13460
|
+
}
|
|
13461
|
+
return null;
|
|
13462
|
+
};
|
|
13463
|
+
const zodParseMethod = (call) => {
|
|
13464
|
+
const callee = call.callee;
|
|
13465
|
+
return callee.type === AST_NODE_TYPES53.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES53.Identifier ? callee.property.name : null;
|
|
13466
|
+
};
|
|
13467
|
+
const hasConditionalAncestorBeforeStatement = (node, statement) => {
|
|
13468
|
+
let current = node.parent;
|
|
13469
|
+
while (current !== void 0 && current !== statement) {
|
|
13470
|
+
if (current.type === AST_NODE_TYPES53.LogicalExpression || current.type === AST_NODE_TYPES53.ConditionalExpression) {
|
|
13471
|
+
return true;
|
|
13472
|
+
}
|
|
13473
|
+
current = current.parent;
|
|
13474
|
+
}
|
|
13475
|
+
return false;
|
|
13476
|
+
};
|
|
13477
|
+
const isAwaitedBeforeStatement = (node, statement) => {
|
|
13478
|
+
let current = node.parent;
|
|
13479
|
+
while (current !== void 0 && current !== statement) {
|
|
13480
|
+
if (current.type === AST_NODE_TYPES53.AwaitExpression) return true;
|
|
13481
|
+
current = current.parent;
|
|
13482
|
+
}
|
|
13483
|
+
return false;
|
|
13484
|
+
};
|
|
13485
|
+
const guaranteedValidationStatement = (declarator, reference) => {
|
|
13486
|
+
const parse2 = zodParseAncestor(reference);
|
|
13487
|
+
if (parse2 === null) return null;
|
|
13488
|
+
const declarationStatement = containingStatement(declarator);
|
|
13489
|
+
const validationStatement = containingStatement(parse2);
|
|
13490
|
+
if (declarationStatement === null || validationStatement === null || declarationStatement.parent !== validationStatement.parent || validationStatement.range[0] <= declarationStatement.range[1] || hasConditionalAncestorBeforeStatement(parse2, validationStatement)) {
|
|
13491
|
+
return null;
|
|
13492
|
+
}
|
|
13493
|
+
if (validationStatement.type !== AST_NODE_TYPES53.VariableDeclaration && validationStatement.type !== AST_NODE_TYPES53.ExpressionStatement) {
|
|
13494
|
+
return null;
|
|
13495
|
+
}
|
|
13496
|
+
const method = zodParseMethod(parse2);
|
|
13497
|
+
if (method === "parse") return validationStatement;
|
|
13498
|
+
if (method === "parseAsync" && isAwaitedBeforeStatement(parse2, validationStatement)) {
|
|
13499
|
+
return validationStatement;
|
|
13500
|
+
}
|
|
13501
|
+
return null;
|
|
13502
|
+
};
|
|
13503
|
+
const isSafePrevalidationInspection = (identifier) => {
|
|
13504
|
+
const parent = identifier.parent;
|
|
13505
|
+
if (parent.type === AST_NODE_TYPES53.UnaryExpression && parent.operator === "typeof") {
|
|
13506
|
+
return true;
|
|
13507
|
+
}
|
|
13508
|
+
if (parent.type !== AST_NODE_TYPES53.BinaryExpression || parent.left !== identifier) {
|
|
13509
|
+
return false;
|
|
13510
|
+
}
|
|
13511
|
+
if (parent.operator === "instanceof") {
|
|
13512
|
+
return parent.right.type === AST_NODE_TYPES53.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
13513
|
+
}
|
|
13514
|
+
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === AST_NODE_TYPES53.Literal && parent.right.value === null || parent.right.type === AST_NODE_TYPES53.Identifier && parent.right.name === "undefined");
|
|
13515
|
+
};
|
|
13516
|
+
const statementWithinBlock = (node, block) => {
|
|
13517
|
+
let current = node;
|
|
13518
|
+
while (current.parent !== void 0 && current.parent !== block) {
|
|
13519
|
+
current = current.parent;
|
|
13520
|
+
}
|
|
13521
|
+
return current.parent === block ? current : null;
|
|
13522
|
+
};
|
|
13146
13523
|
const bindingIsValidated = (declarator) => {
|
|
13147
13524
|
const variable = context.sourceCode.getDeclaredVariables(declarator)[0];
|
|
13148
13525
|
if (variable === void 0) return false;
|
|
13149
|
-
|
|
13150
|
-
(
|
|
13526
|
+
const references = variable.references.filter((reference) => !reference.isWriteOnly()).map((reference) => reference.identifier).filter(
|
|
13527
|
+
(identifier) => identifier.type === AST_NODE_TYPES53.Identifier
|
|
13528
|
+
);
|
|
13529
|
+
if (references.length === 0) return false;
|
|
13530
|
+
if (references.some(isInstanceofNarrowing)) return true;
|
|
13531
|
+
const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
|
|
13532
|
+
(statement) => statement !== null
|
|
13151
13533
|
);
|
|
13534
|
+
const declarationStatement = containingStatement(declarator);
|
|
13535
|
+
const declarationBlock = declarationStatement?.parent;
|
|
13536
|
+
return references.every((reference) => {
|
|
13537
|
+
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
|
|
13538
|
+
return true;
|
|
13539
|
+
}
|
|
13540
|
+
if (declarationBlock === void 0) return false;
|
|
13541
|
+
const useStatement = statementWithinBlock(reference, declarationBlock);
|
|
13542
|
+
return useStatement !== null && validationStatements.some(
|
|
13543
|
+
(statement) => statement.range[1] < useStatement.range[0]
|
|
13544
|
+
);
|
|
13545
|
+
});
|
|
13152
13546
|
};
|
|
13153
13547
|
return {
|
|
13548
|
+
ImportDeclaration(node) {
|
|
13549
|
+
if (!isZodModule(node.source.value)) return;
|
|
13550
|
+
for (const specifier of node.specifiers) {
|
|
13551
|
+
if (specifier.type === AST_NODE_TYPES53.ImportNamespaceSpecifier || specifier.type === AST_NODE_TYPES53.ImportDefaultSpecifier || specifier.type === AST_NODE_TYPES53.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES53.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z")) {
|
|
13552
|
+
const binding = resolvedBinding(specifier.local);
|
|
13553
|
+
if (binding !== null) zodBindings.add(binding);
|
|
13554
|
+
}
|
|
13555
|
+
}
|
|
13556
|
+
},
|
|
13154
13557
|
CallExpression(node) {
|
|
13155
13558
|
if (!isFormDataGetCall(node)) return;
|
|
13156
13559
|
if (hasZodParseAncestor(node) || isInstanceofNarrowing(node)) return;
|
|
@@ -13168,8 +13571,8 @@ var require_zod_form_validation_default = createRule({
|
|
|
13168
13571
|
// src/rules/store-insert-requires-on-conflict.ts
|
|
13169
13572
|
import "@typescript-eslint/utils";
|
|
13170
13573
|
var storeInsertRequiresOnConflictDocumentation = {
|
|
13171
|
-
summary: "Require
|
|
13172
|
-
rationale: "A
|
|
13574
|
+
summary: "Require embedded inserts in explicitly replayable callables to carry conflict handling.",
|
|
13575
|
+
rationale: "A callable named as an enqueue, seed, migration, schedule, ensure, or upsert promises replay safety.",
|
|
13173
13576
|
remediation: "Add an appropriate `ON CONFLICT` action or supported replay-safe insert form.",
|
|
13174
13577
|
category: "correctness",
|
|
13175
13578
|
examples: [
|
|
@@ -13178,7 +13581,25 @@ var storeInsertRequiresOnConflictDocumentation = {
|
|
|
13178
13581
|
]
|
|
13179
13582
|
};
|
|
13180
13583
|
var INSERT_WRITE = /\bINSERT\s+(?:OR\s+\w+\s+)?INTO\s+[\w."'`?$:@-]+\s*(?:\([^)]*\)\s*)?(?:VALUES|SELECT|DEFAULT\s+VALUES)\b/i;
|
|
13181
|
-
var CONFLICT_HANDLED = /\bON\s+CONFLICT\b|\bON\s+DUPLICATE\s+KEY\b|\bINSERT\s+OR\s+(?:IGNORE|REPLACE)\b/i;
|
|
13584
|
+
var CONFLICT_HANDLED = /\bON\s+CONFLICT\b|\bON\s+DUPLICATE\s+KEY\b|\bINSERT\s+OR\s+(?:IGNORE|REPLACE)\b|\bINSERT\b[\s\S]*?\bSELECT\b[\s\S]*?\bWHERE\s+NOT\s+EXISTS\b/i;
|
|
13585
|
+
var REPLAY_CONTRACT_NAME = /(?:enqueue|ensure|migrate|recordOnce|schedule|seed|upsert|getOrCreate|createIfAbsent|insertIfAbsent)/i;
|
|
13586
|
+
function owningCallableName(node) {
|
|
13587
|
+
for (let current = node.parent; current !== null && current !== void 0; current = current.parent) {
|
|
13588
|
+
if (current.type === "FunctionDeclaration") {
|
|
13589
|
+
return current.id?.name ?? null;
|
|
13590
|
+
}
|
|
13591
|
+
if (current.type === "MethodDefinition") {
|
|
13592
|
+
return current.key.type === "Identifier" ? current.key.name : null;
|
|
13593
|
+
}
|
|
13594
|
+
if ((current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") && current.parent.type === "VariableDeclarator" && current.parent.id.type === "Identifier") {
|
|
13595
|
+
return current.parent.id.name;
|
|
13596
|
+
}
|
|
13597
|
+
if ((current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") && current.parent.type === "Property" && current.parent.key.type === "Identifier") {
|
|
13598
|
+
return current.parent.key.name;
|
|
13599
|
+
}
|
|
13600
|
+
}
|
|
13601
|
+
return null;
|
|
13602
|
+
}
|
|
13182
13603
|
var INSERT_GATE = /insert/i;
|
|
13183
13604
|
var store_insert_requires_on_conflict_default = createRule({
|
|
13184
13605
|
name: "store-insert-requires-on-conflict",
|
|
@@ -13186,7 +13607,7 @@ var store_insert_requires_on_conflict_default = createRule({
|
|
|
13186
13607
|
meta: {
|
|
13187
13608
|
type: "problem",
|
|
13188
13609
|
docs: {
|
|
13189
|
-
description: "Require
|
|
13610
|
+
description: "Require embedded inserts in explicitly replayable callables to carry conflict handling."
|
|
13190
13611
|
},
|
|
13191
13612
|
schema: [],
|
|
13192
13613
|
messages: {
|
|
@@ -13202,13 +13623,17 @@ var store_insert_requires_on_conflict_default = createRule({
|
|
|
13202
13623
|
if (!INSERT_WRITE.test(sql) || CONFLICT_HANDLED.test(sql)) {
|
|
13203
13624
|
return;
|
|
13204
13625
|
}
|
|
13626
|
+
const owner = owningCallableName(node);
|
|
13627
|
+
if (owner !== null && !REPLAY_CONTRACT_NAME.test(owner)) {
|
|
13628
|
+
return;
|
|
13629
|
+
}
|
|
13205
13630
|
context.report({ node, messageId: "storeInsertRequiresOnConflict" });
|
|
13206
13631
|
});
|
|
13207
13632
|
}
|
|
13208
13633
|
});
|
|
13209
13634
|
|
|
13210
13635
|
// src/rules/stepdown.ts
|
|
13211
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES54, ASTUtils as
|
|
13636
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES54, ASTUtils as ASTUtils15 } from "@typescript-eslint/utils";
|
|
13212
13637
|
var stepdownDocumentation = {
|
|
13213
13638
|
summary: "Place a private helper below its sole direct same-scope caller.",
|
|
13214
13639
|
rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
|
|
@@ -13401,7 +13826,7 @@ function methodName(node) {
|
|
|
13401
13826
|
return !node.computed && node.key.type === AST_NODE_TYPES54.Identifier ? node.key.name : null;
|
|
13402
13827
|
}
|
|
13403
13828
|
function referencedMethod(context, node, classVariables) {
|
|
13404
|
-
const objectVariable = node.object.type === AST_NODE_TYPES54.Identifier ?
|
|
13829
|
+
const objectVariable = node.object.type === AST_NODE_TYPES54.Identifier ? ASTUtils15.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
|
|
13405
13830
|
const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
|
|
13406
13831
|
if (node.object.type !== AST_NODE_TYPES54.ThisExpression && !isClassReference) return null;
|
|
13407
13832
|
if (node.property.type === AST_NODE_TYPES54.PrivateIdentifier) return `#${node.property.name}`;
|
|
@@ -13454,11 +13879,11 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
13454
13879
|
const pinned = /* @__PURE__ */ new Set();
|
|
13455
13880
|
const classVariables = /* @__PURE__ */ new Set();
|
|
13456
13881
|
if (node.id !== null) {
|
|
13457
|
-
const internal =
|
|
13882
|
+
const internal = ASTUtils15.findVariable(context.sourceCode.getScope(node), node.id.name);
|
|
13458
13883
|
if (internal !== null) classVariables.add(internal);
|
|
13459
13884
|
}
|
|
13460
13885
|
if (node.type === AST_NODE_TYPES54.ClassExpression && node.parent.type === AST_NODE_TYPES54.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES54.Identifier) {
|
|
13461
|
-
const outer =
|
|
13886
|
+
const outer = ASTUtils15.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
|
|
13462
13887
|
if (outer !== null) classVariables.add(outer);
|
|
13463
13888
|
}
|
|
13464
13889
|
for (const method of methods) {
|
|
@@ -13494,7 +13919,7 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
13494
13919
|
return;
|
|
13495
13920
|
}
|
|
13496
13921
|
if (binding.type !== AST_NODE_TYPES54.Identifier) return;
|
|
13497
|
-
const variable =
|
|
13922
|
+
const variable = ASTUtils15.findVariable(context.sourceCode.getScope(binding), binding.name);
|
|
13498
13923
|
if (variable !== null) {
|
|
13499
13924
|
methodClassVariables.add(variable);
|
|
13500
13925
|
methodAliases.add(variable);
|
|
@@ -13524,7 +13949,7 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
13524
13949
|
return;
|
|
13525
13950
|
}
|
|
13526
13951
|
if (!privateNames.has(target)) return;
|
|
13527
|
-
const objectVariable = current.object.type === AST_NODE_TYPES54.Identifier ?
|
|
13952
|
+
const objectVariable = current.object.type === AST_NODE_TYPES54.Identifier ? ASTUtils15.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
|
|
13528
13953
|
if (objectVariable !== null && methodAliases.has(objectVariable)) {
|
|
13529
13954
|
pinned.add(target);
|
|
13530
13955
|
return;
|
|
@@ -13608,7 +14033,7 @@ var stepdown_default = createRule({
|
|
|
13608
14033
|
// src/rules/zod-naming-convention.ts
|
|
13609
14034
|
import {
|
|
13610
14035
|
AST_NODE_TYPES as AST_NODE_TYPES55,
|
|
13611
|
-
ASTUtils as
|
|
14036
|
+
ASTUtils as ASTUtils16
|
|
13612
14037
|
} from "@typescript-eslint/utils";
|
|
13613
14038
|
var zodNamingConventionDocumentation = {
|
|
13614
14039
|
summary: "Enforce a consistent Zod schema naming convention \u2014 a `Z` prefix (`ZUser`) or a `Schema` suffix (`userSchema`); both are accepted by default.",
|
|
@@ -13695,7 +14120,7 @@ var zod_naming_convention_default = createRule({
|
|
|
13695
14120
|
const acceptsSchemaWord = convention !== "prefix";
|
|
13696
14121
|
const zodBindings = /* @__PURE__ */ new Set();
|
|
13697
14122
|
function resolvedBinding(identifier) {
|
|
13698
|
-
return
|
|
14123
|
+
return ASTUtils16.findVariable(
|
|
13699
14124
|
context.sourceCode.getScope(identifier),
|
|
13700
14125
|
identifier.name
|
|
13701
14126
|
);
|
|
@@ -13891,7 +14316,7 @@ var rules = {
|
|
|
13891
14316
|
};
|
|
13892
14317
|
var meta = {
|
|
13893
14318
|
name: "@sarj/eslint-plugin",
|
|
13894
|
-
version: "15.
|
|
14319
|
+
version: "15.2.0"
|
|
13895
14320
|
};
|
|
13896
14321
|
var applicationOnlyRules = [
|
|
13897
14322
|
"no-restricted-library-load",
|