@sarj/eslint-plugin 15.1.0 → 15.3.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 +472 -105
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +498 -127
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1010,11 +1010,11 @@ function isTrivialInitializer(node) {
|
|
|
1010
1010
|
}
|
|
1011
1011
|
function restatesStatementHead(body2, statement) {
|
|
1012
1012
|
if (statement === null) return false;
|
|
1013
|
-
const
|
|
1014
|
-
const opener =
|
|
1015
|
-
if (opener === void 0 ||
|
|
1013
|
+
const words2 = body2.match(/[A-Za-z][\w$]*/g) ?? [];
|
|
1014
|
+
const opener = words2[0];
|
|
1015
|
+
if (opener === void 0 || words2.length > NARRATION_MAX_WORDS) return false;
|
|
1016
1016
|
if (!NARRATION_VERB_RE.test(opener)) return false;
|
|
1017
|
-
const content =
|
|
1017
|
+
const content = words2.slice(1).map(normalizeToken).filter((word) => !NARRATION_STOPWORDS.has(word));
|
|
1018
1018
|
if (content.length < NARRATION_MIN_CONTENT) return false;
|
|
1019
1019
|
const head = statement.split("(")[0] ?? statement;
|
|
1020
1020
|
const code = headTokens(head);
|
|
@@ -1282,8 +1282,8 @@ function isRedundantNarration(body2, statementBelow, standalone, isolatedEnumera
|
|
|
1282
1282
|
if (META_COMMENTARY_RE.test(t) && !justified) return true;
|
|
1283
1283
|
if (isBareDeferral(t) && !justified) return true;
|
|
1284
1284
|
if (HELPER_OPENER_RE.test(t) || LETS_RE.test(t)) return true;
|
|
1285
|
-
const
|
|
1286
|
-
if (
|
|
1285
|
+
const words2 = t.split(/\s+/);
|
|
1286
|
+
if (words2.length > 1 && words2.length <= 4 && DUMMY_TRANSLATION_RE.test(t) && !/[():=]/.test(t)) {
|
|
1287
1287
|
const lowerT = t.toLowerCase();
|
|
1288
1288
|
if (!RATIONALE_WORDS.some((word) => lowerT.includes(word)) && restatesWholeStatement(t, statementBelow)) {
|
|
1289
1289
|
return true;
|
|
@@ -1365,8 +1365,8 @@ function isWeakWalkthroughComment(body2, statement) {
|
|
|
1365
1365
|
if (normalized.length === 0 || normalized.endsWith("?") || normalized.split(/\s+/).length > WALL_MAX_WORDS || isDirective(normalized) || isProtected(normalized) || !WALL_NARRATION_RE.test(normalized)) {
|
|
1366
1366
|
return false;
|
|
1367
1367
|
}
|
|
1368
|
-
const
|
|
1369
|
-
const described =
|
|
1368
|
+
const words2 = contentTokens(normalized);
|
|
1369
|
+
const described = words2.slice(1);
|
|
1370
1370
|
if (described.length === 0) return false;
|
|
1371
1371
|
const code = codeTokens(statement);
|
|
1372
1372
|
const matched = described.filter((word) => restates([word], code)).length;
|
|
@@ -2710,7 +2710,7 @@ var no_fat_try_blocks_default = createRule({
|
|
|
2710
2710
|
const sourceCode = context.sourceCode;
|
|
2711
2711
|
return {
|
|
2712
2712
|
TryStatement(node) {
|
|
2713
|
-
if (node.finalizer !== null) {
|
|
2713
|
+
if (node.finalizer !== null && node.handler === null) {
|
|
2714
2714
|
return;
|
|
2715
2715
|
}
|
|
2716
2716
|
if (handlerRethrows(node.handler)) {
|
|
@@ -2950,15 +2950,32 @@ var noHandRolledSpinnerDocumentation = {
|
|
|
2950
2950
|
rationale: "One-off loading indicators duplicate a shared primitive and let accessibility and styling diverge.",
|
|
2951
2951
|
remediation: "Render the design-system Spinner component instead.",
|
|
2952
2952
|
category: "maintainability",
|
|
2953
|
-
limitations: ["Only static className values on div and span elements are inspected."],
|
|
2953
|
+
limitations: ["Only static className values on div and span elements are inspected; tests, stories, generated files, and the design-system implementation are excluded."],
|
|
2954
2954
|
examples: [
|
|
2955
2955
|
{ 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 },
|
|
2956
2956
|
{ 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 }
|
|
2957
2957
|
]
|
|
2958
2958
|
};
|
|
2959
2959
|
var DESIGN_SYSTEM_PATH = /(?:^|[/\\])components[/\\]ui[/\\]/u;
|
|
2960
|
-
var
|
|
2961
|
-
var
|
|
2960
|
+
var DIRECTIONAL_BORDER = /^border-([trblsexy])-(.+)$/u;
|
|
2961
|
+
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;
|
|
2962
|
+
var ARBITRARY_LENGTH_FUNCTION = /^(?:calc|clamp|max|min)\(.+\)$/u;
|
|
2963
|
+
function isBorderWidthValue(value) {
|
|
2964
|
+
if (/^\d+$/u.test(value)) return true;
|
|
2965
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
2966
|
+
const arbitrary = value.slice(1, -1);
|
|
2967
|
+
const length = arbitrary.startsWith("length:") ? arbitrary.slice("length:".length) : arbitrary;
|
|
2968
|
+
return CSS_LENGTH.test(length) || ARBITRARY_LENGTH_FUNCTION.test(length) || arbitrary.startsWith("length:") && /^var\(.+\)$/u.test(length);
|
|
2969
|
+
}
|
|
2970
|
+
return value.startsWith("(length:") && value.endsWith(")") && value.length > "(length:)".length;
|
|
2971
|
+
}
|
|
2972
|
+
function isBorderWidth(token) {
|
|
2973
|
+
return token === "border" || token.startsWith("border-") && isBorderWidthValue(token.slice("border-".length));
|
|
2974
|
+
}
|
|
2975
|
+
function isContrastingEdge(token) {
|
|
2976
|
+
const match = DIRECTIONAL_BORDER.exec(token);
|
|
2977
|
+
return match?.[2] !== void 0 && !isBorderWidthValue(match[2]);
|
|
2978
|
+
}
|
|
2962
2979
|
function staticClassName(attribute) {
|
|
2963
2980
|
const value = attribute.value;
|
|
2964
2981
|
if (value?.type === import_utils13.AST_NODE_TYPES.Literal && typeof value.value === "string") {
|
|
@@ -2987,7 +3004,7 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
2987
3004
|
},
|
|
2988
3005
|
defaultOptions: [],
|
|
2989
3006
|
create(context) {
|
|
2990
|
-
if (DESIGN_SYSTEM_PATH.test(context.filename)) {
|
|
3007
|
+
if (DESIGN_SYSTEM_PATH.test(context.filename) || isTestFile(context.filename) || isStoryFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
2991
3008
|
return {};
|
|
2992
3009
|
}
|
|
2993
3010
|
return {
|
|
@@ -3002,7 +3019,7 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
3002
3019
|
const className = staticClassName(classNameAttribute);
|
|
3003
3020
|
if (className === null) return;
|
|
3004
3021
|
const classes = className.split(/\s+/u);
|
|
3005
|
-
if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some(
|
|
3022
|
+
if (classes.includes("animate-spin") && classes.includes("rounded-full") && classes.some(isBorderWidth) && classes.some(isContrastingEdge)) {
|
|
3006
3023
|
context.report({ node, messageId: "handRolledSpinner" });
|
|
3007
3024
|
}
|
|
3008
3025
|
}
|
|
@@ -3068,9 +3085,9 @@ function nameWords(name) {
|
|
|
3068
3085
|
return name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").split(/[^A-Za-z0-9]+/u).filter(Boolean).map((word) => word.toLowerCase());
|
|
3069
3086
|
}
|
|
3070
3087
|
function isStrongSecurityName(name) {
|
|
3071
|
-
const
|
|
3072
|
-
return
|
|
3073
|
-
(word, index) => word === "api" &&
|
|
3088
|
+
const words2 = nameWords(name);
|
|
3089
|
+
return words2.some((word) => STRONG_SECURITY_WORDS.has(word)) || words2.some(
|
|
3090
|
+
(word, index) => word === "api" && words2[index + 1] === "key" || word === "auth" && words2[index + 1] === "id" || word === "verification" && words2[index + 1] === "code"
|
|
3074
3091
|
);
|
|
3075
3092
|
}
|
|
3076
3093
|
function isNonSecurityName(name) {
|
|
@@ -3247,14 +3264,12 @@ var noJsonStringifyErrorDocumentation = {
|
|
|
3247
3264
|
rationale: "Native Error details are non-enumerable, so generic JSON serialization discards diagnostic information.",
|
|
3248
3265
|
remediation: "Serialize explicit error fields or use an error-aware serializer.",
|
|
3249
3266
|
category: "correctness",
|
|
3250
|
-
limitations: ["The rule uses local
|
|
3267
|
+
limitations: ["The rule uses local catch-binding and constructor provenance rather than type information."],
|
|
3251
3268
|
examples: [
|
|
3252
3269
|
{ id: "explicit-error-message", title: "Serialize an enumerable error field", outcome: "no-match", files: [{ path: "src/report.ts", source: "try { f(); } catch (err) { JSON.stringify({ error: err.message }); }" }], focusPath: "src/report.ts", expectedCount: 0, public: true },
|
|
3253
3270
|
{ id: "stringified-error", title: "Do not stringify an Error object", outcome: "match", files: [{ path: "src/report.ts", source: "try { f(); } catch (err) { JSON.stringify({ error: err }); }" }], focusPath: "src/report.ts", expectedCount: 1, public: true }
|
|
3254
3271
|
]
|
|
3255
3272
|
};
|
|
3256
|
-
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
3257
|
-
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
3258
3273
|
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
3259
3274
|
var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
|
|
3260
3275
|
"data",
|
|
@@ -3282,6 +3297,21 @@ var BUILTIN_ERROR_CONSTRUCTORS = /* @__PURE__ */ new Set([
|
|
|
3282
3297
|
"TypeError",
|
|
3283
3298
|
"URIError"
|
|
3284
3299
|
]);
|
|
3300
|
+
function identifierIsProvenError(identifier, scope) {
|
|
3301
|
+
if (isCatchBinding(scope, identifier.name)) return true;
|
|
3302
|
+
let current = scope;
|
|
3303
|
+
while (current !== null && !current.set.has(identifier.name)) {
|
|
3304
|
+
current = current.upper;
|
|
3305
|
+
}
|
|
3306
|
+
const variable = current?.set.get(identifier.name);
|
|
3307
|
+
if (variable === void 0 || variable.defs.length !== 1) return false;
|
|
3308
|
+
const definition = variable.defs[0];
|
|
3309
|
+
if (definition?.type !== "Variable") return false;
|
|
3310
|
+
const initializer = definition.node.init;
|
|
3311
|
+
return initializer?.type === "NewExpression" && initializer.callee.type === "Identifier" && BUILTIN_ERROR_CONSTRUCTORS.has(initializer.callee.name) && variable.references.every(
|
|
3312
|
+
(reference) => !reference.isWrite() || reference.init === true
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3285
3315
|
function isCatchBinding(scope, name) {
|
|
3286
3316
|
let current = scope;
|
|
3287
3317
|
while (current) {
|
|
@@ -3297,22 +3327,6 @@ function isCatchBinding(scope, name) {
|
|
|
3297
3327
|
}
|
|
3298
3328
|
return false;
|
|
3299
3329
|
}
|
|
3300
|
-
function memberSuggestsError(member, scope) {
|
|
3301
|
-
const propName2 = !member.computed && member.property.type === "Identifier" ? member.property.name : null;
|
|
3302
|
-
if (propName2 !== null && ERROR_PROP_PATTERN.test(propName2)) {
|
|
3303
|
-
return true;
|
|
3304
|
-
}
|
|
3305
|
-
const base = member.object;
|
|
3306
|
-
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
3307
|
-
if (baseSuggestsError) {
|
|
3308
|
-
if (propName2 === null) {
|
|
3309
|
-
return true;
|
|
3310
|
-
}
|
|
3311
|
-
const lowered = propName2.toLowerCase();
|
|
3312
|
-
return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);
|
|
3313
|
-
}
|
|
3314
|
-
return false;
|
|
3315
|
-
}
|
|
3316
3330
|
function positiveErrorSubject(test) {
|
|
3317
3331
|
return instanceofErrorSubject(test) ?? typeGuardSubject(test);
|
|
3318
3332
|
}
|
|
@@ -3416,27 +3430,25 @@ function directLiteralValues(argument) {
|
|
|
3416
3430
|
}
|
|
3417
3431
|
function expressionSuggestsError(expression, scope) {
|
|
3418
3432
|
if (expression.type === "Identifier") {
|
|
3419
|
-
return
|
|
3433
|
+
return identifierIsProvenError(expression, scope);
|
|
3434
|
+
}
|
|
3435
|
+
if (expression.type === "NewExpression" && expression.callee.type === "Identifier") {
|
|
3436
|
+
return BUILTIN_ERROR_CONSTRUCTORS.has(expression.callee.name);
|
|
3420
3437
|
}
|
|
3421
3438
|
return expression.type === "MemberExpression" && memberSuggestsError(expression, scope);
|
|
3422
3439
|
}
|
|
3423
|
-
function
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
if (definition?.type !== "Variable") return false;
|
|
3434
|
-
const initializer = definition.node.init;
|
|
3435
|
-
return initializer?.type === "NewExpression" && initializer.callee.type === "Identifier" && BUILTIN_ERROR_CONSTRUCTORS.has(initializer.callee.name) && variable.references.every(
|
|
3436
|
-
(reference) => !reference.isWrite() || reference.init === true
|
|
3437
|
-
);
|
|
3440
|
+
function memberSuggestsError(member, scope) {
|
|
3441
|
+
const propName2 = !member.computed && member.property.type === "Identifier" ? member.property.name : null;
|
|
3442
|
+
const base = member.object;
|
|
3443
|
+
const baseSuggestsError = base.type === "Identifier" && identifierIsProvenError(base, scope);
|
|
3444
|
+
if (baseSuggestsError) {
|
|
3445
|
+
if (propName2 === null) {
|
|
3446
|
+
return true;
|
|
3447
|
+
}
|
|
3448
|
+
const lowered = propName2.toLowerCase();
|
|
3449
|
+
return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);
|
|
3438
3450
|
}
|
|
3439
|
-
return
|
|
3451
|
+
return false;
|
|
3440
3452
|
}
|
|
3441
3453
|
var no_json_stringify_error_default = createRule({
|
|
3442
3454
|
name: "no-json-stringify-error",
|
|
@@ -3463,9 +3475,8 @@ var no_json_stringify_error_default = createRule({
|
|
|
3463
3475
|
return;
|
|
3464
3476
|
}
|
|
3465
3477
|
const scope = context.sourceCode.getScope(firstArg);
|
|
3466
|
-
const isNestedLiteral = firstArg.type === "ObjectExpression" || firstArg.type === "ArrayExpression";
|
|
3467
3478
|
const unsafeValue = directLiteralValues(firstArg).find(
|
|
3468
|
-
(value) =>
|
|
3479
|
+
(value) => expressionSuggestsError(value, scope) && !isGuardedByInstanceofError(node, value, context.sourceCode) && !isNarrowedByEarlyReturn(node, value, context.sourceCode)
|
|
3469
3480
|
);
|
|
3470
3481
|
if (unsafeValue === void 0) {
|
|
3471
3482
|
return;
|
|
@@ -4032,7 +4043,7 @@ var VALUE_TAG_RE = /@(example|deprecated|see|remarks|throws|internal|public|alph
|
|
|
4032
4043
|
var BOUNDARY_RE = /(?<=[.!?])["'`)\]]*\s+(?=[A-Z0-9`])/;
|
|
4033
4044
|
var BULLET_RE = /^\s*(?:[-*+] |\d+[.)] )/;
|
|
4034
4045
|
var HEADING_RE = /^[A-Za-z][A-Za-z ]+:$/;
|
|
4035
|
-
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;
|
|
4046
|
+
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;
|
|
4036
4047
|
function body(comment) {
|
|
4037
4048
|
return comment.value.replace(/^\*/, "").split("\n").map((line) => line.replace(/^\s*\*?\s?/, "")).join("\n").trim();
|
|
4038
4049
|
}
|
|
@@ -4924,6 +4935,7 @@ var ANALYTICS_SEGMENTS2 = /* @__PURE__ */ new Set([
|
|
|
4924
4935
|
]);
|
|
4925
4936
|
var SERVER_ACTION_SKIP_FILE_RE = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
4926
4937
|
var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
|
|
4938
|
+
var NEXT_MODULE_PATH_RE = /(?:^|[/\\])(?:app|pages)[/\\]/u;
|
|
4927
4939
|
function isGlobalFetchCall(node, resolvesToGlobal) {
|
|
4928
4940
|
const callee = node.callee;
|
|
4929
4941
|
if (callee.type === "Identifier") {
|
|
@@ -5039,6 +5051,13 @@ var no_raw_fetch_outside_clients_default = createRule({
|
|
|
5039
5051
|
const nonReactFramework = context.sourceCode.ast.body.some(
|
|
5040
5052
|
(statement) => statement.type === import_utils25.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(statement.source.value)
|
|
5041
5053
|
);
|
|
5054
|
+
const hasUseClientDirective = context.sourceCode.ast.body.some(
|
|
5055
|
+
(statement) => statement.type === import_utils25.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils25.AST_NODE_TYPES.Literal && statement.expression.value === "use client"
|
|
5056
|
+
);
|
|
5057
|
+
const hasNextImport = context.sourceCode.ast.body.some(
|
|
5058
|
+
(statement) => statement.type === import_utils25.AST_NODE_TYPES.ImportDeclaration && typeof statement.source.value === "string" && (statement.source.value === "next" || statement.source.value.startsWith("next/"))
|
|
5059
|
+
);
|
|
5060
|
+
const hasNextEvidence = hasNextImport || hasUseClientDirective && NEXT_MODULE_PATH_RE.test(filename);
|
|
5042
5061
|
function resolvesToGlobal(identifier) {
|
|
5043
5062
|
const variable = import_utils25.ASTUtils.findVariable(
|
|
5044
5063
|
context.sourceCode.getScope(identifier),
|
|
@@ -5099,7 +5118,7 @@ var no_raw_fetch_outside_clients_default = createRule({
|
|
|
5099
5118
|
return resolved?.type === import_utils25.AST_NODE_TYPES.LogicalExpression && resolved.operator === "||" && (isMutationMethod2(resolved.left) || isMutationMethod2(resolved.right));
|
|
5100
5119
|
}
|
|
5101
5120
|
function serverActionOwns(node) {
|
|
5102
|
-
if (node.callee.type !== import_utils25.AST_NODE_TYPES.Identifier || SERVER_ACTION_SKIP_FILE_RE.test(filename) || nonReactFramework) {
|
|
5121
|
+
if (node.callee.type !== import_utils25.AST_NODE_TYPES.Identifier || !hasNextEvidence || SERVER_ACTION_SKIP_FILE_RE.test(filename) || nonReactFramework) {
|
|
5103
5122
|
return false;
|
|
5104
5123
|
}
|
|
5105
5124
|
const url = node.arguments[0];
|
|
@@ -6318,6 +6337,20 @@ var SAFE_PARSE_CONSTRUCTORS = /* @__PURE__ */ new Set([
|
|
|
6318
6337
|
function isBodyDecodeNode(node) {
|
|
6319
6338
|
return node.type === import_utils32.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils32.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils32.AST_NODE_TYPES.Identifier && BODY_DECODE_METHODS.has(node.callee.property.name);
|
|
6320
6339
|
}
|
|
6340
|
+
function isSafeParseSupportCall(node) {
|
|
6341
|
+
const callee = node.callee;
|
|
6342
|
+
if (callee.type !== import_utils32.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils32.AST_NODE_TYPES.Identifier) {
|
|
6343
|
+
return false;
|
|
6344
|
+
}
|
|
6345
|
+
if (callee.property.name === "isArray" && callee.object.type === import_utils32.AST_NODE_TYPES.Identifier && callee.object.name === "Array") {
|
|
6346
|
+
return true;
|
|
6347
|
+
}
|
|
6348
|
+
if (callee.property.name !== "getItem") return false;
|
|
6349
|
+
if (callee.object.type === import_utils32.AST_NODE_TYPES.Identifier && (callee.object.name === "localStorage" || callee.object.name === "sessionStorage")) {
|
|
6350
|
+
return true;
|
|
6351
|
+
}
|
|
6352
|
+
return callee.object.type === import_utils32.AST_NODE_TYPES.MemberExpression && !callee.object.computed && callee.object.object.type === import_utils32.AST_NODE_TYPES.Identifier && (callee.object.object.name === "window" || callee.object.object.name === "globalThis") && callee.object.property.type === import_utils32.AST_NODE_TYPES.Identifier && (callee.object.property.name === "localStorage" || callee.object.property.name === "sessionStorage");
|
|
6353
|
+
}
|
|
6321
6354
|
var BODY_DECODE_METHODS = /* @__PURE__ */ new Set([
|
|
6322
6355
|
"json",
|
|
6323
6356
|
"text",
|
|
@@ -6386,6 +6419,7 @@ function tryReturnsSafeParse(catchNode) {
|
|
|
6386
6419
|
if (current.type === import_utils32.AST_NODE_TYPES.CallExpression || current.type === import_utils32.AST_NODE_TYPES.NewExpression) {
|
|
6387
6420
|
if (isParseShapedNode(current) || isBodyDecodeNode(current)) {
|
|
6388
6421
|
sawSafeParse = true;
|
|
6422
|
+
} else if (current.type === import_utils32.AST_NODE_TYPES.CallExpression && isSafeParseSupportCall(current)) {
|
|
6389
6423
|
} else {
|
|
6390
6424
|
sawUnsafeOperation = true;
|
|
6391
6425
|
return;
|
|
@@ -6848,22 +6882,25 @@ var noStorageInStatelessModulesDocumentation = {
|
|
|
6848
6882
|
rationale: "Private storage in a stateless workflow creates another source of truth that can silently diverge.",
|
|
6849
6883
|
remediation: "Read from the system of record or derive state from an artifact the workflow already produces.",
|
|
6850
6884
|
category: "architecture",
|
|
6851
|
-
limitations: ["The rule is disabled until module path patterns are configured
|
|
6885
|
+
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."],
|
|
6852
6886
|
examples: [
|
|
6853
6887
|
{ 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 },
|
|
6854
6888
|
{ 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 }
|
|
6855
6889
|
]
|
|
6856
6890
|
};
|
|
6857
6891
|
function compile2(patterns) {
|
|
6858
|
-
|
|
6859
|
-
for (const pattern of patterns) {
|
|
6860
|
-
try {
|
|
6861
|
-
compiled.push(new RegExp(pattern));
|
|
6862
|
-
} catch {
|
|
6863
|
-
}
|
|
6864
|
-
}
|
|
6865
|
-
return compiled;
|
|
6892
|
+
return patterns.map((pattern) => new RegExp(pattern));
|
|
6866
6893
|
}
|
|
6894
|
+
var STORAGE_RECEIVER_WORDS = /* @__PURE__ */ new Set([
|
|
6895
|
+
"bucket",
|
|
6896
|
+
"cache",
|
|
6897
|
+
"kv",
|
|
6898
|
+
"namespace",
|
|
6899
|
+
"r2",
|
|
6900
|
+
"redis",
|
|
6901
|
+
"storage",
|
|
6902
|
+
"store"
|
|
6903
|
+
]);
|
|
6867
6904
|
function storageMethodName(node, methods) {
|
|
6868
6905
|
const callee = node.callee;
|
|
6869
6906
|
if (callee.type !== import_utils35.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils35.AST_NODE_TYPES.Identifier) {
|
|
@@ -6876,8 +6913,31 @@ function storageMethodName(node, methods) {
|
|
|
6876
6913
|
if (node.arguments.length < (MIN_ARGUMENTS.get(name) ?? 1)) {
|
|
6877
6914
|
return null;
|
|
6878
6915
|
}
|
|
6916
|
+
if (name === "put" && !isStorageLikeReceiver(callee.object)) {
|
|
6917
|
+
return null;
|
|
6918
|
+
}
|
|
6879
6919
|
return name;
|
|
6880
6920
|
}
|
|
6921
|
+
function isStorageLikeReceiver(node) {
|
|
6922
|
+
if (node.type === import_utils35.AST_NODE_TYPES.Identifier) {
|
|
6923
|
+
return isStorageIdentifier(node.name);
|
|
6924
|
+
}
|
|
6925
|
+
if (node.type !== import_utils35.AST_NODE_TYPES.MemberExpression) {
|
|
6926
|
+
return false;
|
|
6927
|
+
}
|
|
6928
|
+
if (!node.computed && node.property.type === import_utils35.AST_NODE_TYPES.Identifier && isStorageIdentifier(node.property.name)) {
|
|
6929
|
+
return true;
|
|
6930
|
+
}
|
|
6931
|
+
return isStorageLikeReceiver(node.object);
|
|
6932
|
+
}
|
|
6933
|
+
function isStorageIdentifier(name) {
|
|
6934
|
+
return identifierWords(name).some(
|
|
6935
|
+
(word) => STORAGE_RECEIVER_WORDS.has(word.toLowerCase())
|
|
6936
|
+
);
|
|
6937
|
+
}
|
|
6938
|
+
function identifierWords(name) {
|
|
6939
|
+
return name.match(/[A-Z]?[a-z]+|[A-Z]+(?=[A-Z]|$)|\d+/gu) ?? [name];
|
|
6940
|
+
}
|
|
6881
6941
|
var no_storage_in_stateless_modules_default = createRule({
|
|
6882
6942
|
name: "no-storage-in-stateless-modules",
|
|
6883
6943
|
documentation: noStorageInStatelessModulesDocumentation,
|
|
@@ -7279,12 +7339,86 @@ var no_tautological_expect_default = createRule({
|
|
|
7279
7339
|
});
|
|
7280
7340
|
|
|
7281
7341
|
// src/rules/no-typed-doc-sections.ts
|
|
7342
|
+
var TYPED_TAG_RE2 = /^\s*@(arg|argument|param|return|returns|yield|yields)\b(.*)$/iu;
|
|
7343
|
+
var PARAM_TAGS2 = /* @__PURE__ */ new Set(["arg", "argument", "param"]);
|
|
7344
|
+
var PARAMETER_FILLER = /* @__PURE__ */ new Set([
|
|
7345
|
+
"a",
|
|
7346
|
+
"an",
|
|
7347
|
+
"argument",
|
|
7348
|
+
"given",
|
|
7349
|
+
"input",
|
|
7350
|
+
"parameter",
|
|
7351
|
+
"passed",
|
|
7352
|
+
"provided",
|
|
7353
|
+
"the",
|
|
7354
|
+
"value"
|
|
7355
|
+
]);
|
|
7356
|
+
var RESULT_FILLER = /* @__PURE__ */ new Set([
|
|
7357
|
+
"a",
|
|
7358
|
+
"an",
|
|
7359
|
+
"array",
|
|
7360
|
+
"boolean",
|
|
7361
|
+
"generator",
|
|
7362
|
+
"number",
|
|
7363
|
+
"object",
|
|
7364
|
+
"output",
|
|
7365
|
+
"promise",
|
|
7366
|
+
"result",
|
|
7367
|
+
"return",
|
|
7368
|
+
"returned",
|
|
7369
|
+
"returns",
|
|
7370
|
+
"string",
|
|
7371
|
+
"the",
|
|
7372
|
+
"value"
|
|
7373
|
+
]);
|
|
7374
|
+
function hasVacuousTypedTag(text) {
|
|
7375
|
+
const tags = typedTags(text);
|
|
7376
|
+
return tags.length > 0 && tags.some(isVacuousTag);
|
|
7377
|
+
}
|
|
7378
|
+
function typedTags(text) {
|
|
7379
|
+
const tags = [];
|
|
7380
|
+
for (const raw of text.split("\n")) {
|
|
7381
|
+
const match = TYPED_TAG_RE2.exec(raw);
|
|
7382
|
+
if (match !== null) {
|
|
7383
|
+
tags.push({ kind: (match[1] ?? "").toLowerCase(), payload: (match[2] ?? "").trim() });
|
|
7384
|
+
} else if (tags.length > 0 && raw.trim().length > 0 && !raw.trim().startsWith("@")) {
|
|
7385
|
+
const last = tags.at(-1);
|
|
7386
|
+
last.payload = `${last.payload} ${raw.trim()}`.trim();
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
return tags.map(({ kind, payload }) => {
|
|
7390
|
+
let rest = payload.replace(/^\{[^}\n]+\}\s*/u, "").trim();
|
|
7391
|
+
if (!PARAM_TAGS2.has(kind)) {
|
|
7392
|
+
return { kind, name: null, description: rest.replace(/^-\s*/u, "").trim() };
|
|
7393
|
+
}
|
|
7394
|
+
const match = /^(\[[^\]]+\]|[A-Za-z_$][\w$.[\]-]*)(?:\s+-\s*|\s+)?(.*)$/u.exec(rest);
|
|
7395
|
+
if (match === null) return { kind, name: null, description: "" };
|
|
7396
|
+
const rawName = (match[1] ?? "").replace(/^\[/u, "").replace(/\]$/u, "").split("=")[0] ?? "";
|
|
7397
|
+
rest = (match[2] ?? "").trim();
|
|
7398
|
+
return { kind, name: rawName, description: rest };
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
function isVacuousTag(tag) {
|
|
7402
|
+
const description = words(tag.description).map(canonicalWord);
|
|
7403
|
+
if (description.length === 0) return true;
|
|
7404
|
+
if (tag.name === null) return description.every((word) => RESULT_FILLER.has(word));
|
|
7405
|
+
const nameWords2 = new Set(words(tag.name).map(canonicalWord));
|
|
7406
|
+
return description.every((word) => PARAMETER_FILLER.has(word) || nameWords2.has(word));
|
|
7407
|
+
}
|
|
7408
|
+
function words(text) {
|
|
7409
|
+
return text.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").toLowerCase().match(/[a-z][a-z0-9]*/gu) ?? [];
|
|
7410
|
+
}
|
|
7411
|
+
function canonicalWord(word) {
|
|
7412
|
+
if (["identifier", "identifiers", "ids"].includes(word)) return "id";
|
|
7413
|
+
if (word.endsWith("s") && word.length > 3) return word.slice(0, -1);
|
|
7414
|
+
return word;
|
|
7415
|
+
}
|
|
7282
7416
|
var noTypedDocSectionsDocumentation = {
|
|
7283
7417
|
summary: "Reject typed-signature repetition while preserving behavior that types cannot express.",
|
|
7284
7418
|
rationale: "Parameter and return tags repeat typed signatures and can drift without adding runtime behavior or constraints.",
|
|
7285
7419
|
remediation: "Remove repeated parameter and return tags; retain documentation for behavior, failures, and external contracts.",
|
|
7286
7420
|
category: "maintainability",
|
|
7287
|
-
limitations: ["
|
|
7421
|
+
limitations: ["Description-free or name-restating parameter and return tags are reported only when the documented function has corresponding explicit TypeScript types."],
|
|
7288
7422
|
examples: [
|
|
7289
7423
|
{
|
|
7290
7424
|
id: "behavioral-documentation",
|
|
@@ -7322,7 +7456,7 @@ var no_typed_doc_sections_default = createRule({
|
|
|
7322
7456
|
return {
|
|
7323
7457
|
Program() {
|
|
7324
7458
|
for (const group of proseGroups(context.filename, context.sourceCode, true)) {
|
|
7325
|
-
if (group.hasTypedTags && documentsTypedFunction(context.sourceCode, group.comment)) {
|
|
7459
|
+
if (group.hasTypedTags && hasVacuousTypedTag(group.text) && documentsTypedFunction(context.sourceCode, group.comment)) {
|
|
7326
7460
|
context.report({ node: group.comment, messageId: "typedSection" });
|
|
7327
7461
|
}
|
|
7328
7462
|
}
|
|
@@ -7423,28 +7557,32 @@ var STOPWORDS3 = /* @__PURE__ */ new Set([
|
|
|
7423
7557
|
"with"
|
|
7424
7558
|
]);
|
|
7425
7559
|
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;
|
|
7560
|
+
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;
|
|
7426
7561
|
function narratesValue(body2, code) {
|
|
7427
7562
|
if (body2.length === 0 || DIRECTIVE_RE5.test(body2) || hasExternalReference(body2)) return false;
|
|
7428
7563
|
const codeNumbers = numbersIn(code);
|
|
7429
7564
|
if (codeNumbers.size === 0) return false;
|
|
7430
|
-
const
|
|
7431
|
-
if (
|
|
7565
|
+
const words2 = (body2.match(WORD_RE3) ?? []).map((word) => word.toLowerCase());
|
|
7566
|
+
if (words2.length === 0) return false;
|
|
7432
7567
|
const commentNumbers = numbersIn(body2);
|
|
7433
7568
|
if (commentNumbers.size === 0) return false;
|
|
7434
7569
|
for (const number of commentNumbers) {
|
|
7435
7570
|
if (!codeNumbers.has(number)) return false;
|
|
7436
7571
|
}
|
|
7437
|
-
if (!
|
|
7572
|
+
if (!words2.some((word) => UNIT_WORDS.has(word))) return false;
|
|
7438
7573
|
const identifiers = codeTokens(code);
|
|
7439
7574
|
const stems = /* @__PURE__ */ new Set();
|
|
7440
7575
|
for (const token of identifiers) stems.add(stem(token));
|
|
7441
|
-
return
|
|
7576
|
+
return words2.every(
|
|
7442
7577
|
(word) => STOPWORDS3.has(word) || UNIT_WORDS.has(word) || commentNumbers.has(word) || identifiers.has(word) || stems.has(stem(word))
|
|
7443
7578
|
);
|
|
7444
7579
|
}
|
|
7445
7580
|
function numbersIn(text) {
|
|
7446
7581
|
return new Set(text.match(NUMBER_RE) ?? []);
|
|
7447
7582
|
}
|
|
7583
|
+
function nameAlreadyCarriesUnit(code) {
|
|
7584
|
+
return (code.match(/[A-Za-z_$][\w$]*/gu) ?? []).some((identifier) => UNIT_NAME_SUFFIX_RE.test(identifier));
|
|
7585
|
+
}
|
|
7448
7586
|
var no_trailing_value_narration_default = createRule({
|
|
7449
7587
|
name: "no-trailing-value-narration",
|
|
7450
7588
|
documentation: noTrailingValueNarrationDocumentation,
|
|
@@ -7455,6 +7593,7 @@ var no_trailing_value_narration_default = createRule({
|
|
|
7455
7593
|
},
|
|
7456
7594
|
schema: [],
|
|
7457
7595
|
messages: {
|
|
7596
|
+
deleteNarration: "Trailing comment restates the literal and the identifier already names its unit \u2014 delete the comment so it cannot drift.",
|
|
7458
7597
|
narratesValue: "Trailing comment restates the literal on this line \u2014 put the unit in the name (STALE_TIME_MS) so it cannot drift."
|
|
7459
7598
|
}
|
|
7460
7599
|
},
|
|
@@ -7487,7 +7626,10 @@ var no_trailing_value_narration_default = createRule({
|
|
|
7487
7626
|
const code = line.slice(0, comment.loc.start.column);
|
|
7488
7627
|
const body2 = comment.value.replace(/^\*+/, "").replace(/\*+$/, "").trim();
|
|
7489
7628
|
if (narratesValue(body2, code)) {
|
|
7490
|
-
context.report({
|
|
7629
|
+
context.report({
|
|
7630
|
+
node: comment,
|
|
7631
|
+
messageId: nameAlreadyCarriesUnit(code) ? "deleteNarration" : "narratesValue"
|
|
7632
|
+
});
|
|
7491
7633
|
}
|
|
7492
7634
|
}
|
|
7493
7635
|
}
|
|
@@ -8528,15 +8670,23 @@ var no_zod_native_enum_default = createRule({
|
|
|
8528
8670
|
} catch {
|
|
8529
8671
|
services = null;
|
|
8530
8672
|
}
|
|
8531
|
-
const
|
|
8532
|
-
const
|
|
8673
|
+
const zodImportedBindings = /* @__PURE__ */ new Map();
|
|
8674
|
+
const zodNamespaceBindings = /* @__PURE__ */ new Set();
|
|
8675
|
+
function resolvedBinding(identifier) {
|
|
8676
|
+
return import_utils45.ASTUtils.findVariable(
|
|
8677
|
+
sourceCode.getScope(identifier),
|
|
8678
|
+
identifier.name
|
|
8679
|
+
);
|
|
8680
|
+
}
|
|
8533
8681
|
function isZodMemberCall(node, api) {
|
|
8534
8682
|
const callee = node.callee;
|
|
8535
|
-
if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils45.AST_NODE_TYPES.Identifier &&
|
|
8536
|
-
|
|
8683
|
+
if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.object.type === import_utils45.AST_NODE_TYPES.Identifier && callee.property.type === import_utils45.AST_NODE_TYPES.Identifier) {
|
|
8684
|
+
const binding = resolvedBinding(callee.object);
|
|
8685
|
+
return binding !== null && zodNamespaceBindings.has(binding) && callee.property.name === api;
|
|
8537
8686
|
}
|
|
8538
8687
|
if (callee.type === import_utils45.AST_NODE_TYPES.Identifier) {
|
|
8539
|
-
|
|
8688
|
+
const binding = resolvedBinding(callee);
|
|
8689
|
+
return binding !== null && zodImportedBindings.get(binding) === api;
|
|
8540
8690
|
}
|
|
8541
8691
|
return false;
|
|
8542
8692
|
}
|
|
@@ -8571,10 +8721,14 @@ var no_zod_native_enum_default = createRule({
|
|
|
8571
8721
|
}
|
|
8572
8722
|
for (const spec of node.specifiers) {
|
|
8573
8723
|
if (spec.type === import_utils45.AST_NODE_TYPES.ImportNamespaceSpecifier || spec.type === import_utils45.AST_NODE_TYPES.ImportDefaultSpecifier || spec.type === import_utils45.AST_NODE_TYPES.ImportSpecifier && (spec.imported.type === import_utils45.AST_NODE_TYPES.Identifier ? spec.imported.name === "z" : spec.imported.value === "z")) {
|
|
8574
|
-
|
|
8724
|
+
const binding = resolvedBinding(spec.local);
|
|
8725
|
+
if (binding !== null) zodNamespaceBindings.add(binding);
|
|
8575
8726
|
}
|
|
8576
8727
|
if (spec.type === import_utils45.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === import_utils45.AST_NODE_TYPES.Identifier) {
|
|
8577
|
-
|
|
8728
|
+
const binding = resolvedBinding(spec.local);
|
|
8729
|
+
if (binding !== null) {
|
|
8730
|
+
zodImportedBindings.set(binding, spec.imported.name);
|
|
8731
|
+
}
|
|
8578
8732
|
}
|
|
8579
8733
|
}
|
|
8580
8734
|
},
|
|
@@ -10789,7 +10943,70 @@ var bindingValidationPolarity = (test, bindingName) => {
|
|
|
10789
10943
|
}
|
|
10790
10944
|
return test.type === import_utils57.AST_NODE_TYPES.CallExpression && test.arguments.length === 1 && test.arguments[0]?.type === import_utils57.AST_NODE_TYPES.Identifier && test.arguments[0].name === bindingName && test.callee.type === import_utils57.AST_NODE_TYPES.MemberExpression && !test.callee.computed && test.callee.object.type === import_utils57.AST_NODE_TYPES.Identifier && test.callee.object.name === "Array" && test.callee.property.type === import_utils57.AST_NODE_TYPES.Identifier && test.callee.property.name === "isArray" ? "valid-when-true" : null;
|
|
10791
10945
|
};
|
|
10946
|
+
var plainMemberAccess = (node) => node.type === import_utils57.AST_NODE_TYPES.MemberExpression && !node.computed && node.object.type === import_utils57.AST_NODE_TYPES.Identifier && node.property.type === import_utils57.AST_NODE_TYPES.Identifier ? { object: node.object.name, property: node.property.name } : null;
|
|
10947
|
+
var isSamePlainMember = (node, access) => {
|
|
10948
|
+
const candidate = plainMemberAccess(node);
|
|
10949
|
+
return candidate !== null && candidate.object === access.object && candidate.property === access.property;
|
|
10950
|
+
};
|
|
10792
10951
|
var nodeWithin2 = (node, container) => node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
|
|
10952
|
+
var isUseWithinValidatedBranch = (node, bindingName) => {
|
|
10953
|
+
for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
10954
|
+
if (current.type === import_utils57.AST_NODE_TYPES.ConditionalExpression) {
|
|
10955
|
+
const polarity = bindingValidationPolarity(current.test, bindingName);
|
|
10956
|
+
if (polarity === "valid-when-true" && nodeWithin2(node, current.consequent) || polarity === "valid-when-false" && nodeWithin2(node, current.alternate)) {
|
|
10957
|
+
return true;
|
|
10958
|
+
}
|
|
10959
|
+
}
|
|
10960
|
+
if (current.type === import_utils57.AST_NODE_TYPES.IfStatement) {
|
|
10961
|
+
const polarity = bindingValidationPolarity(current.test, bindingName);
|
|
10962
|
+
if (polarity === "valid-when-true" && nodeWithin2(node, current.consequent) || polarity === "valid-when-false" && current.alternate !== null && nodeWithin2(node, current.alternate)) {
|
|
10963
|
+
return true;
|
|
10964
|
+
}
|
|
10965
|
+
}
|
|
10966
|
+
if (current.type === import_utils57.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils57.AST_NODE_TYPES.FunctionExpression || current.type === import_utils57.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
10967
|
+
return false;
|
|
10968
|
+
}
|
|
10969
|
+
}
|
|
10970
|
+
return false;
|
|
10971
|
+
};
|
|
10972
|
+
var isMemberUseWithinValidatedBranch = (node, access) => {
|
|
10973
|
+
for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
10974
|
+
if (current.type === import_utils57.AST_NODE_TYPES.ConditionalExpression) {
|
|
10975
|
+
const polarity = memberValidationPolarity(current.test, access);
|
|
10976
|
+
if (polarity === "valid-when-true" && nodeWithin2(node, current.consequent) || polarity === "valid-when-false" && nodeWithin2(node, current.alternate)) {
|
|
10977
|
+
return true;
|
|
10978
|
+
}
|
|
10979
|
+
}
|
|
10980
|
+
if (current.type === import_utils57.AST_NODE_TYPES.IfStatement) {
|
|
10981
|
+
const polarity = memberValidationPolarity(current.test, access);
|
|
10982
|
+
if (polarity === "valid-when-true" && nodeWithin2(node, current.consequent) || polarity === "valid-when-false" && current.alternate !== null && nodeWithin2(node, current.alternate)) {
|
|
10983
|
+
return true;
|
|
10984
|
+
}
|
|
10985
|
+
}
|
|
10986
|
+
if (current.type === import_utils57.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils57.AST_NODE_TYPES.FunctionExpression || current.type === import_utils57.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
10987
|
+
return false;
|
|
10988
|
+
}
|
|
10989
|
+
}
|
|
10990
|
+
return false;
|
|
10991
|
+
};
|
|
10992
|
+
var memberValidationPolarity = (test, access) => {
|
|
10993
|
+
if (test.type === import_utils57.AST_NODE_TYPES.UnaryExpression && test.operator === "!") {
|
|
10994
|
+
const inner = memberValidationPolarity(test.argument, access);
|
|
10995
|
+
return inner === "valid-when-true" ? "valid-when-false" : inner === "valid-when-false" ? "valid-when-true" : null;
|
|
10996
|
+
}
|
|
10997
|
+
if (test.type === import_utils57.AST_NODE_TYPES.BinaryExpression) {
|
|
10998
|
+
const isMatchingTypeof = (node) => node.type === import_utils57.AST_NODE_TYPES.UnaryExpression && node.operator === "typeof" && isSamePlainMember(node.argument, access);
|
|
10999
|
+
const isPrimitiveType = (node) => node.type === import_utils57.AST_NODE_TYPES.Literal && typeof node.value === "string" && PRIMITIVE_TYPEOF_RESULTS.has(node.value);
|
|
11000
|
+
if (!(isMatchingTypeof(test.left) && isPrimitiveType(test.right) || isMatchingTypeof(test.right) && isPrimitiveType(test.left))) {
|
|
11001
|
+
return null;
|
|
11002
|
+
}
|
|
11003
|
+
if (test.operator === "===" || test.operator === "==") {
|
|
11004
|
+
return "valid-when-true";
|
|
11005
|
+
}
|
|
11006
|
+
return test.operator === "!==" || test.operator === "!=" ? "valid-when-false" : null;
|
|
11007
|
+
}
|
|
11008
|
+
return test.type === import_utils57.AST_NODE_TYPES.CallExpression && test.arguments.length === 1 && test.arguments[0] !== void 0 && test.arguments[0].type !== import_utils57.AST_NODE_TYPES.SpreadElement && isSamePlainMember(test.arguments[0], access) && test.callee.type === import_utils57.AST_NODE_TYPES.MemberExpression && !test.callee.computed && test.callee.object.type === import_utils57.AST_NODE_TYPES.Identifier && test.callee.object.name === "Array" && test.callee.property.type === import_utils57.AST_NODE_TYPES.Identifier && test.callee.property.name === "isArray" ? "valid-when-true" : null;
|
|
11009
|
+
};
|
|
10793
11010
|
var isFullyValidatedExtractedBinding = (member, source, context) => {
|
|
10794
11011
|
const isValidationReference = (identifier) => {
|
|
10795
11012
|
for (let current = identifier.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
@@ -11059,7 +11276,14 @@ var prefer_schema_for_api_payload_default = createRule({
|
|
|
11059
11276
|
return;
|
|
11060
11277
|
}
|
|
11061
11278
|
const variable = obj?.type === import_utils57.AST_NODE_TYPES.Identifier ? unvalidatedVariableRef(obj, scope, unvalidatedVariables) : null;
|
|
11062
|
-
if (variable !== null) {
|
|
11279
|
+
if (variable !== null && obj?.type === import_utils57.AST_NODE_TYPES.Identifier) {
|
|
11280
|
+
if (isUseWithinValidatedBranch(node, obj.name)) {
|
|
11281
|
+
return;
|
|
11282
|
+
}
|
|
11283
|
+
const access = plainMemberAccess(node);
|
|
11284
|
+
if (access !== null && isMemberUseWithinValidatedBranch(node, access)) {
|
|
11285
|
+
return;
|
|
11286
|
+
}
|
|
11063
11287
|
if (isFullyValidatedExtractedBinding(node, variable, context)) {
|
|
11064
11288
|
return;
|
|
11065
11289
|
}
|
|
@@ -11511,16 +11735,17 @@ var preferServerActionsDocumentation = {
|
|
|
11511
11735
|
rationale: "Server Actions preserve typed application calls and avoid an internal JSON request-response boundary.",
|
|
11512
11736
|
remediation: "Move the mutation into a Server Action and invoke that action from the React client.",
|
|
11513
11737
|
category: "architecture",
|
|
11514
|
-
limitations: ["Only statically recognizable /api/ mutations in
|
|
11738
|
+
limitations: ["Only statically recognizable /api/ mutations in modules with positive Next.js evidence are reported: an explicit next import, or an app/pages path with a top-level use-client directive."],
|
|
11515
11739
|
examples: [
|
|
11516
11740
|
{ id: "server-action-call", title: "Call a Server Action", outcome: "no-match", files: [{ path: "app/tasks/page.tsx", source: "import { createTask } from './actions'; await createTask(input);" }], focusPath: "app/tasks/page.tsx", expectedCount: 0, public: true },
|
|
11517
|
-
{ id: "api-mutation", title: "Do not mutate through an API route", outcome: "match", files: [{ path: "app/tasks/page.tsx", source: "await fetch('/api/tasks', { method: 'POST', body });" }], focusPath: "app/tasks/page.tsx", expectedCount: 1, public: true }
|
|
11741
|
+
{ id: "api-mutation", title: "Do not mutate through an API route", outcome: "match", files: [{ path: "app/tasks/page.tsx", source: "'use client'; await fetch('/api/tasks', { method: 'POST', body });" }], focusPath: "app/tasks/page.tsx", expectedCount: 1, public: true }
|
|
11518
11742
|
]
|
|
11519
11743
|
};
|
|
11520
11744
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
11521
11745
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
11522
11746
|
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
11523
11747
|
var NON_REACT_FRAMEWORK_RE2 = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
|
|
11748
|
+
var NEXT_MODULE_PATH_RE2 = /(?:^|[/\\])(?:app|pages)[/\\]/u;
|
|
11524
11749
|
function getScope(context, node) {
|
|
11525
11750
|
return context.sourceCode.getScope(node);
|
|
11526
11751
|
}
|
|
@@ -11634,6 +11859,16 @@ var prefer_server_actions_default = createRule({
|
|
|
11634
11859
|
const isNonReactFramework = context.sourceCode.ast.body.some(
|
|
11635
11860
|
(node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE2.test(node.source.value)
|
|
11636
11861
|
);
|
|
11862
|
+
const hasUseClientDirective = context.sourceCode.ast.body.some(
|
|
11863
|
+
(node) => node.type === "ExpressionStatement" && node.expression.type === "Literal" && node.expression.value === "use client"
|
|
11864
|
+
);
|
|
11865
|
+
const hasNextImport = context.sourceCode.ast.body.some(
|
|
11866
|
+
(node) => node.type === "ImportDeclaration" && typeof node.source.value === "string" && (node.source.value === "next" || node.source.value.startsWith("next/"))
|
|
11867
|
+
);
|
|
11868
|
+
const hasNextEvidence = hasNextImport || hasUseClientDirective && NEXT_MODULE_PATH_RE2.test(filename);
|
|
11869
|
+
if (!hasNextEvidence) {
|
|
11870
|
+
return {};
|
|
11871
|
+
}
|
|
11637
11872
|
return {
|
|
11638
11873
|
CallExpression(node) {
|
|
11639
11874
|
if (isNonReactFramework) return;
|
|
@@ -12356,6 +12591,9 @@ var requireAssertNeverDocumentation = {
|
|
|
12356
12591
|
};
|
|
12357
12592
|
var isRuntimeHandlingStatement = (statement) => {
|
|
12358
12593
|
if (statement.type === import_utils62.AST_NODE_TYPES.EmptyStatement) return false;
|
|
12594
|
+
if (statement.type === import_utils62.AST_NODE_TYPES.BreakStatement) {
|
|
12595
|
+
return statement.label !== null;
|
|
12596
|
+
}
|
|
12359
12597
|
if (statement.type === import_utils62.AST_NODE_TYPES.TSTypeAliasDeclaration || statement.type === import_utils62.AST_NODE_TYPES.TSInterfaceDeclaration) {
|
|
12360
12598
|
return false;
|
|
12361
12599
|
}
|
|
@@ -12385,7 +12623,12 @@ function isExhaustiveFiniteSwitch(node, services) {
|
|
|
12385
12623
|
const discriminant = services.esTreeNodeToTSNodeMap.get(node.discriminant);
|
|
12386
12624
|
const discriminantType = checker.getTypeAtLocation(discriminant);
|
|
12387
12625
|
const constituents = discriminantType.isUnion() ? discriminantType.types : [discriminantType];
|
|
12388
|
-
if (constituents.length
|
|
12626
|
+
if (!discriminantType.isUnion() || constituents.length < 2) return false;
|
|
12627
|
+
if (constituents.every(
|
|
12628
|
+
(constituent) => (constituent.flags & import_typescript.default.TypeFlags.BooleanLiteral) !== 0
|
|
12629
|
+
)) {
|
|
12630
|
+
return false;
|
|
12631
|
+
}
|
|
12389
12632
|
const expected = /* @__PURE__ */ new Set();
|
|
12390
12633
|
for (const constituent of constituents) {
|
|
12391
12634
|
const key = finiteTypeKey(constituent, checker);
|
|
@@ -13192,28 +13435,26 @@ var requireZodFormValidationDocumentation = {
|
|
|
13192
13435
|
rationale: "FormData values are untrusted strings or files and need runtime validation before use.",
|
|
13193
13436
|
remediation: "Read the value inside a Zod schema's `parse` or `safeParse` input.",
|
|
13194
13437
|
category: "security",
|
|
13438
|
+
limitations: [
|
|
13439
|
+
"Tests are excluded; imported schema-shaped names are trusted when their implementation is outside the linted file.",
|
|
13440
|
+
"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."
|
|
13441
|
+
],
|
|
13195
13442
|
examples: [
|
|
13196
13443
|
{ 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 },
|
|
13197
13444
|
{ 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 }
|
|
13198
13445
|
]
|
|
13199
13446
|
};
|
|
13200
|
-
var
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
if (method !== "parse" && method !== "safeParse" && method !== "parseAsync" && method !== "safeParseAsync") {
|
|
13208
|
-
return false;
|
|
13209
|
-
}
|
|
13210
|
-
return looksLikeZodSchema(callee.object);
|
|
13211
|
-
};
|
|
13212
|
-
var looksLikeZodSchema = (node) => {
|
|
13447
|
+
var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
|
|
13448
|
+
"parse",
|
|
13449
|
+
"safeParse",
|
|
13450
|
+
"parseAsync",
|
|
13451
|
+
"safeParseAsync"
|
|
13452
|
+
]);
|
|
13453
|
+
var zodReceiverRoot = (node) => {
|
|
13213
13454
|
let current = node;
|
|
13214
13455
|
while (true) {
|
|
13215
13456
|
if (current.type === import_utils66.AST_NODE_TYPES.Identifier) {
|
|
13216
|
-
return current
|
|
13457
|
+
return current;
|
|
13217
13458
|
}
|
|
13218
13459
|
if (current.type === import_utils66.AST_NODE_TYPES.CallExpression) {
|
|
13219
13460
|
current = current.callee;
|
|
@@ -13223,7 +13464,7 @@ var looksLikeZodSchema = (node) => {
|
|
|
13223
13464
|
current = current.object;
|
|
13224
13465
|
continue;
|
|
13225
13466
|
}
|
|
13226
|
-
return
|
|
13467
|
+
return null;
|
|
13227
13468
|
}
|
|
13228
13469
|
};
|
|
13229
13470
|
var isFormDataMethodCall = (node) => {
|
|
@@ -13253,6 +13494,34 @@ var require_zod_form_validation_default = createRule({
|
|
|
13253
13494
|
if (isTestFile(context.filename)) {
|
|
13254
13495
|
return {};
|
|
13255
13496
|
}
|
|
13497
|
+
const zodBindings = /* @__PURE__ */ new Set();
|
|
13498
|
+
const resolvedBinding = (identifier) => import_utils66.ASTUtils.findVariable(
|
|
13499
|
+
context.sourceCode.getScope(identifier),
|
|
13500
|
+
identifier.name
|
|
13501
|
+
);
|
|
13502
|
+
const isProvablyNonZodLocal = (identifier) => {
|
|
13503
|
+
const binding = resolvedBinding(identifier);
|
|
13504
|
+
if (binding === null || zodBindings.has(binding) || binding.defs.length !== 1) {
|
|
13505
|
+
return false;
|
|
13506
|
+
}
|
|
13507
|
+
const definition = binding.defs[0];
|
|
13508
|
+
if (definition?.type !== "Variable" || definition.node.type !== import_utils66.AST_NODE_TYPES.VariableDeclarator) {
|
|
13509
|
+
return false;
|
|
13510
|
+
}
|
|
13511
|
+
const init = definition.node.init;
|
|
13512
|
+
return init?.type === import_utils66.AST_NODE_TYPES.ObjectExpression || init?.type === import_utils66.AST_NODE_TYPES.ArrayExpression || init?.type === import_utils66.AST_NODE_TYPES.Literal || init?.type === import_utils66.AST_NODE_TYPES.ArrowFunctionExpression || init?.type === import_utils66.AST_NODE_TYPES.FunctionExpression;
|
|
13513
|
+
};
|
|
13514
|
+
const isZodParseCall = (node) => {
|
|
13515
|
+
if (node.type !== import_utils66.AST_NODE_TYPES.CallExpression) return false;
|
|
13516
|
+
const callee = node.callee;
|
|
13517
|
+
if (callee.type !== import_utils66.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils66.AST_NODE_TYPES.Identifier || !ZOD_PARSE_METHODS.has(callee.property.name)) {
|
|
13518
|
+
return false;
|
|
13519
|
+
}
|
|
13520
|
+
const root = zodReceiverRoot(callee.object);
|
|
13521
|
+
if (root === null) return false;
|
|
13522
|
+
const binding = resolvedBinding(root);
|
|
13523
|
+
return binding !== null && zodBindings.has(binding) || (root.name === "z" || ZOD_SCHEMA_NAME_RE.test(root.name)) && !isProvablyNonZodLocal(root);
|
|
13524
|
+
};
|
|
13256
13525
|
const isFormSourceIdentifier = (node) => {
|
|
13257
13526
|
if (node.type !== import_utils66.AST_NODE_TYPES.Identifier) return false;
|
|
13258
13527
|
if (/formdata/i.test(node.name)) return true;
|
|
@@ -13278,14 +13547,15 @@ var require_zod_form_validation_default = createRule({
|
|
|
13278
13547
|
}
|
|
13279
13548
|
return isFormSourceIdentifier(callee.object);
|
|
13280
13549
|
};
|
|
13281
|
-
const
|
|
13550
|
+
const zodParseAncestor = (node) => {
|
|
13282
13551
|
let parent = node.parent;
|
|
13283
13552
|
while (parent !== null && parent !== void 0) {
|
|
13284
|
-
if (isZodParseCall(parent)) return
|
|
13553
|
+
if (isZodParseCall(parent)) return parent;
|
|
13285
13554
|
parent = parent.parent;
|
|
13286
13555
|
}
|
|
13287
|
-
return
|
|
13556
|
+
return null;
|
|
13288
13557
|
};
|
|
13558
|
+
const hasZodParseAncestor = (node) => zodParseAncestor(node) !== null;
|
|
13289
13559
|
const isInstanceofNarrowing = (node) => {
|
|
13290
13560
|
const parent = node.parent;
|
|
13291
13561
|
return parent !== null && parent !== void 0 && parent.type === import_utils66.AST_NODE_TYPES.BinaryExpression && parent.operator === "instanceof" && parent.left === node && parent.right.type === import_utils66.AST_NODE_TYPES.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
@@ -13302,14 +13572,111 @@ var require_zod_form_validation_default = createRule({
|
|
|
13302
13572
|
}
|
|
13303
13573
|
return null;
|
|
13304
13574
|
};
|
|
13575
|
+
const containingStatement = (node) => {
|
|
13576
|
+
let current = node;
|
|
13577
|
+
while (current.parent !== void 0) {
|
|
13578
|
+
const parent = current.parent;
|
|
13579
|
+
if (parent.type === import_utils66.AST_NODE_TYPES.BlockStatement || parent.type === import_utils66.AST_NODE_TYPES.Program) {
|
|
13580
|
+
return current;
|
|
13581
|
+
}
|
|
13582
|
+
current = parent;
|
|
13583
|
+
}
|
|
13584
|
+
return null;
|
|
13585
|
+
};
|
|
13586
|
+
const zodParseMethod = (call) => {
|
|
13587
|
+
const callee = call.callee;
|
|
13588
|
+
return callee.type === import_utils66.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils66.AST_NODE_TYPES.Identifier ? callee.property.name : null;
|
|
13589
|
+
};
|
|
13590
|
+
const hasConditionalAncestorBeforeStatement = (node, statement) => {
|
|
13591
|
+
let current = node.parent;
|
|
13592
|
+
while (current !== void 0 && current !== statement) {
|
|
13593
|
+
if (current.type === import_utils66.AST_NODE_TYPES.LogicalExpression || current.type === import_utils66.AST_NODE_TYPES.ConditionalExpression) {
|
|
13594
|
+
return true;
|
|
13595
|
+
}
|
|
13596
|
+
current = current.parent;
|
|
13597
|
+
}
|
|
13598
|
+
return false;
|
|
13599
|
+
};
|
|
13600
|
+
const isAwaitedBeforeStatement = (node, statement) => {
|
|
13601
|
+
let current = node.parent;
|
|
13602
|
+
while (current !== void 0 && current !== statement) {
|
|
13603
|
+
if (current.type === import_utils66.AST_NODE_TYPES.AwaitExpression) return true;
|
|
13604
|
+
current = current.parent;
|
|
13605
|
+
}
|
|
13606
|
+
return false;
|
|
13607
|
+
};
|
|
13608
|
+
const guaranteedValidationStatement = (declarator, reference) => {
|
|
13609
|
+
const parse2 = zodParseAncestor(reference);
|
|
13610
|
+
if (parse2 === null) return null;
|
|
13611
|
+
const declarationStatement = containingStatement(declarator);
|
|
13612
|
+
const validationStatement = containingStatement(parse2);
|
|
13613
|
+
if (declarationStatement === null || validationStatement === null || declarationStatement.parent !== validationStatement.parent || validationStatement.range[0] <= declarationStatement.range[1] || hasConditionalAncestorBeforeStatement(parse2, validationStatement)) {
|
|
13614
|
+
return null;
|
|
13615
|
+
}
|
|
13616
|
+
if (validationStatement.type !== import_utils66.AST_NODE_TYPES.VariableDeclaration && validationStatement.type !== import_utils66.AST_NODE_TYPES.ExpressionStatement) {
|
|
13617
|
+
return null;
|
|
13618
|
+
}
|
|
13619
|
+
const method = zodParseMethod(parse2);
|
|
13620
|
+
if (method === "parse") return validationStatement;
|
|
13621
|
+
if (method === "parseAsync" && isAwaitedBeforeStatement(parse2, validationStatement)) {
|
|
13622
|
+
return validationStatement;
|
|
13623
|
+
}
|
|
13624
|
+
return null;
|
|
13625
|
+
};
|
|
13626
|
+
const isSafePrevalidationInspection = (identifier) => {
|
|
13627
|
+
const parent = identifier.parent;
|
|
13628
|
+
if (parent.type === import_utils66.AST_NODE_TYPES.UnaryExpression && parent.operator === "typeof") {
|
|
13629
|
+
return true;
|
|
13630
|
+
}
|
|
13631
|
+
if (parent.type !== import_utils66.AST_NODE_TYPES.BinaryExpression || parent.left !== identifier) {
|
|
13632
|
+
return false;
|
|
13633
|
+
}
|
|
13634
|
+
if (parent.operator === "instanceof") {
|
|
13635
|
+
return parent.right.type === import_utils66.AST_NODE_TYPES.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
13636
|
+
}
|
|
13637
|
+
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === import_utils66.AST_NODE_TYPES.Literal && parent.right.value === null || parent.right.type === import_utils66.AST_NODE_TYPES.Identifier && parent.right.name === "undefined");
|
|
13638
|
+
};
|
|
13639
|
+
const statementWithinBlock = (node, block) => {
|
|
13640
|
+
let current = node;
|
|
13641
|
+
while (current.parent !== void 0 && current.parent !== block) {
|
|
13642
|
+
current = current.parent;
|
|
13643
|
+
}
|
|
13644
|
+
return current.parent === block ? current : null;
|
|
13645
|
+
};
|
|
13305
13646
|
const bindingIsValidated = (declarator) => {
|
|
13306
13647
|
const variable = context.sourceCode.getDeclaredVariables(declarator)[0];
|
|
13307
13648
|
if (variable === void 0) return false;
|
|
13308
|
-
|
|
13309
|
-
(
|
|
13649
|
+
const references = variable.references.filter((reference) => !reference.isWriteOnly()).map((reference) => reference.identifier).filter(
|
|
13650
|
+
(identifier) => identifier.type === import_utils66.AST_NODE_TYPES.Identifier
|
|
13651
|
+
);
|
|
13652
|
+
if (references.length === 0) return false;
|
|
13653
|
+
if (references.some(isInstanceofNarrowing)) return true;
|
|
13654
|
+
const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
|
|
13655
|
+
(statement) => statement !== null
|
|
13310
13656
|
);
|
|
13657
|
+
const declarationStatement = containingStatement(declarator);
|
|
13658
|
+
const declarationBlock = declarationStatement?.parent;
|
|
13659
|
+
return references.every((reference) => {
|
|
13660
|
+
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
|
|
13661
|
+
return true;
|
|
13662
|
+
}
|
|
13663
|
+
if (declarationBlock === void 0) return false;
|
|
13664
|
+
const useStatement = statementWithinBlock(reference, declarationBlock);
|
|
13665
|
+
return useStatement !== null && validationStatements.some(
|
|
13666
|
+
(statement) => statement.range[1] < useStatement.range[0]
|
|
13667
|
+
);
|
|
13668
|
+
});
|
|
13311
13669
|
};
|
|
13312
13670
|
return {
|
|
13671
|
+
ImportDeclaration(node) {
|
|
13672
|
+
if (!isZodModule(node.source.value)) return;
|
|
13673
|
+
for (const specifier of node.specifiers) {
|
|
13674
|
+
if (specifier.type === import_utils66.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils66.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils66.AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === import_utils66.AST_NODE_TYPES.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z")) {
|
|
13675
|
+
const binding = resolvedBinding(specifier.local);
|
|
13676
|
+
if (binding !== null) zodBindings.add(binding);
|
|
13677
|
+
}
|
|
13678
|
+
}
|
|
13679
|
+
},
|
|
13313
13680
|
CallExpression(node) {
|
|
13314
13681
|
if (!isFormDataGetCall(node)) return;
|
|
13315
13682
|
if (hasZodParseAncestor(node) || isInstanceofNarrowing(node)) return;
|
|
@@ -14069,7 +14436,7 @@ var rules = {
|
|
|
14069
14436
|
};
|
|
14070
14437
|
var meta = {
|
|
14071
14438
|
name: "@sarj/eslint-plugin",
|
|
14072
|
-
version: "15.
|
|
14439
|
+
version: "15.3.0"
|
|
14073
14440
|
};
|
|
14074
14441
|
var applicationOnlyRules = [
|
|
14075
14442
|
"no-restricted-library-load",
|