@sarj/eslint-plugin 15.17.16 → 15.17.17
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 +467 -194
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +638 -362
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2507,12 +2507,12 @@ function exportedNextConfigProperty(sourceCode, path) {
|
|
|
2507
2507
|
// src/rules/no-dangerously-allow-svg.ts
|
|
2508
2508
|
var NEXT_CONFIG_RE = /(?:^|\/)next\.config\.[cm]?[jt]s$/;
|
|
2509
2509
|
var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
|
|
2510
|
-
summary: "Next.js image configuration enables
|
|
2510
|
+
summary: "Next.js image configuration enables SVG rendering without the required response hardening",
|
|
2511
2511
|
rationale: "SVG files can contain scripts and other active content; enabling dangerouslyAllowSVG makes the image optimizer serve that content from the application origin.",
|
|
2512
|
-
remediation: "Keep dangerouslyAllowSVG disabled. If SVG
|
|
2512
|
+
remediation: "Keep dangerouslyAllowSVG disabled. If SVG optimization is required, retain attachment disposition and set the image Content-Security-Policy to `script-src 'none'; sandbox;`.",
|
|
2513
2513
|
category: "security",
|
|
2514
2514
|
limitations: [
|
|
2515
|
-
"Only
|
|
2515
|
+
"Only literal effective properties of a directly exported object, unescaped const alias, or isolated module.exports object are analyzed. Wrappers, factories, spreads, computed keys, dynamic policies and mutations are not inferred."
|
|
2516
2516
|
],
|
|
2517
2517
|
examples: [
|
|
2518
2518
|
{
|
|
@@ -2535,6 +2535,19 @@ var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
|
|
|
2535
2535
|
}
|
|
2536
2536
|
]
|
|
2537
2537
|
};
|
|
2538
|
+
function literalString(node) {
|
|
2539
|
+
if (node?.value.type === "Literal" && typeof node.value.value === "string") return node.value.value;
|
|
2540
|
+
if (node?.value.type === "TemplateLiteral" && node.value.expressions.length === 0) return node.value.quasis[0]?.value.cooked ?? null;
|
|
2541
|
+
return null;
|
|
2542
|
+
}
|
|
2543
|
+
function hasHardenedSvgPolicy(policy) {
|
|
2544
|
+
const directives = policy.split(";").map((part) => part.trim().split(/\s+/u).filter(Boolean)).filter((parts) => parts.length > 0);
|
|
2545
|
+
const scriptSources = directives.filter(([name]) => name?.toLowerCase() === "script-src");
|
|
2546
|
+
const sandboxes = directives.filter(([name]) => name?.toLowerCase() === "sandbox");
|
|
2547
|
+
const scriptSource = scriptSources[0];
|
|
2548
|
+
const sandbox = sandboxes[0];
|
|
2549
|
+
return scriptSources.length === 1 && sandboxes.length === 1 && scriptSource?.length === 2 && scriptSource[1]?.toLowerCase() === "'none'" && sandbox?.length === 1;
|
|
2550
|
+
}
|
|
2538
2551
|
var no_dangerously_allow_svg_default = createRule({
|
|
2539
2552
|
name: "no-dangerously-allow-svg",
|
|
2540
2553
|
documentation: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION,
|
|
@@ -2543,7 +2556,7 @@ var no_dangerously_allow_svg_default = createRule({
|
|
|
2543
2556
|
docs: { description: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION.summary },
|
|
2544
2557
|
schema: [],
|
|
2545
2558
|
messages: {
|
|
2546
|
-
noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG. SVG can carry active content served from the application origin."
|
|
2559
|
+
noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG without a script-blocking sandbox policy and attachment disposition. SVG can carry active content served from the application origin."
|
|
2547
2560
|
}
|
|
2548
2561
|
},
|
|
2549
2562
|
defaultOptions: [],
|
|
@@ -2553,6 +2566,12 @@ var no_dangerously_allow_svg_default = createRule({
|
|
|
2553
2566
|
"Program:exit"() {
|
|
2554
2567
|
const node = exportedNextConfigProperty(context.sourceCode, ["images", "dangerouslyAllowSVG"]);
|
|
2555
2568
|
if (node !== null && node.value.type === "Literal" && node.value.value === true) {
|
|
2569
|
+
const disposition = exportedNextConfigProperty(context.sourceCode, ["images", "contentDispositionType"]);
|
|
2570
|
+
const policy = literalString(
|
|
2571
|
+
exportedNextConfigProperty(context.sourceCode, ["images", "contentSecurityPolicy"])
|
|
2572
|
+
);
|
|
2573
|
+
const attachmentDisposition = disposition === null || literalString(disposition) === "attachment";
|
|
2574
|
+
if (attachmentDisposition && policy !== null && hasHardenedSvgPolicy(policy)) return;
|
|
2556
2575
|
context.report({ node, messageId: "noDangerouslyAllowSvg" });
|
|
2557
2576
|
}
|
|
2558
2577
|
}
|
|
@@ -3671,12 +3690,41 @@ var no_hand_rolled_sleep_default = createRule({
|
|
|
3671
3690
|
|
|
3672
3691
|
// src/rules/no-hand-rolled-spinner.ts
|
|
3673
3692
|
import { AST_NODE_TYPES as AST_NODE_TYPES14 } from "@typescript-eslint/utils";
|
|
3693
|
+
|
|
3694
|
+
// src/rules/_tailwind.ts
|
|
3695
|
+
var tailwindVariantPrefix = (token) => {
|
|
3696
|
+
let bracketDepth = 0;
|
|
3697
|
+
let parenthesisDepth = 0;
|
|
3698
|
+
let escaped = false;
|
|
3699
|
+
let end = 0;
|
|
3700
|
+
for (let index = 0; index < token.length; index += 1) {
|
|
3701
|
+
const character = token[index];
|
|
3702
|
+
if (escaped) {
|
|
3703
|
+
escaped = false;
|
|
3704
|
+
continue;
|
|
3705
|
+
}
|
|
3706
|
+
if (character === "\\") {
|
|
3707
|
+
escaped = true;
|
|
3708
|
+
continue;
|
|
3709
|
+
}
|
|
3710
|
+
if (character === "[") bracketDepth += 1;
|
|
3711
|
+
else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
3712
|
+
else if (character === "(") parenthesisDepth += 1;
|
|
3713
|
+
else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
|
|
3714
|
+
else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
|
|
3715
|
+
}
|
|
3716
|
+
return token.slice(0, end);
|
|
3717
|
+
};
|
|
3718
|
+
var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
|
|
3719
|
+
var classTokens = (value) => value.split(/\s+/).filter(Boolean);
|
|
3720
|
+
|
|
3721
|
+
// src/rules/no-hand-rolled-spinner.ts
|
|
3674
3722
|
var NO_HAND_ROLLED_SPINNER_DOCUMENTATION = {
|
|
3675
3723
|
summary: "Disallow intrinsic elements styled as Tailwind border-ring spinners outside the design-system implementation.",
|
|
3676
3724
|
rationale: "One-off loading indicators duplicate a shared primitive and let accessibility and styling diverge.",
|
|
3677
3725
|
remediation: "Render the design-system Spinner component instead.",
|
|
3678
3726
|
category: "maintainability",
|
|
3679
|
-
limitations: ["Only effective static className values on div and span elements are inspected; a later spread makes the value unknown. Tests, stories, generated files, and the design-system implementation are excluded."],
|
|
3727
|
+
limitations: ["Only effective static className values on div and span elements are inspected; Tailwind utilities are combined only when they are unprefixed or share one exact variant context, while a later spread makes the value unknown. Tests, stories, generated files, and the design-system implementation are excluded."],
|
|
3680
3728
|
examples: [
|
|
3681
3729
|
{ 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 },
|
|
3682
3730
|
{ 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 }
|
|
@@ -3702,6 +3750,10 @@ function isContrastingEdge(token) {
|
|
|
3702
3750
|
const match = DIRECTIONAL_BORDER.exec(token);
|
|
3703
3751
|
return match?.[2] !== void 0 && !isBorderWidthValue(match[2]);
|
|
3704
3752
|
}
|
|
3753
|
+
function hasSpinnerInVariant(classes, variant) {
|
|
3754
|
+
const effective = classes.filter((entry) => entry.variant === "" || entry.variant === variant).map((entry) => entry.base);
|
|
3755
|
+
return effective.includes("animate-spin") && effective.includes("rounded-full") && effective.some(isBorderWidth) && effective.some(isContrastingEdge);
|
|
3756
|
+
}
|
|
3705
3757
|
function staticClassName(attribute) {
|
|
3706
3758
|
const value = attribute.value;
|
|
3707
3759
|
if (value?.type === AST_NODE_TYPES14.Literal && typeof value.value === "string") {
|
|
@@ -3744,8 +3796,12 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
3744
3796
|
if (classNameAttribute?.type !== AST_NODE_TYPES14.JSXAttribute) return;
|
|
3745
3797
|
const className = staticClassName(classNameAttribute);
|
|
3746
3798
|
if (className === null) return;
|
|
3747
|
-
const classes = className.split(/\s+/u)
|
|
3748
|
-
|
|
3799
|
+
const classes = className.split(/\s+/u).filter(Boolean).map((token) => ({
|
|
3800
|
+
base: tailwindBase(token),
|
|
3801
|
+
variant: tailwindVariantPrefix(token)
|
|
3802
|
+
}));
|
|
3803
|
+
const variants = new Set(classes.map((entry) => entry.variant));
|
|
3804
|
+
if ([...variants].some((variant) => hasSpinnerInVariant(classes, variant))) {
|
|
3749
3805
|
context.report({ node, messageId: "handRolledSpinner" });
|
|
3750
3806
|
}
|
|
3751
3807
|
}
|
|
@@ -3986,33 +4042,36 @@ var no_insecure_random_id_default = createRule({
|
|
|
3986
4042
|
});
|
|
3987
4043
|
|
|
3988
4044
|
// src/rules/no-detached-global-fetch.ts
|
|
3989
|
-
import {
|
|
4045
|
+
import {
|
|
4046
|
+
AST_NODE_TYPES as AST_NODE_TYPES15,
|
|
4047
|
+
ASTUtils as ASTUtils7
|
|
4048
|
+
} from "@typescript-eslint/utils";
|
|
3990
4049
|
var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
|
|
3991
|
-
summary: "
|
|
3992
|
-
rationale: "
|
|
3993
|
-
remediation: "
|
|
4050
|
+
summary: "Keep the ambient global fetch receiver-safe when storing or explicitly rebinding it.",
|
|
4051
|
+
rationale: "Calling a raw ambient fetch through a class or object property supplies that object as `this`; receiver-sensitive hosts can reject that invocation only after deployment.",
|
|
4052
|
+
remediation: "Store a forwarding wrapper such as `(input, init) => fetch(input, init)`, or bind the callable to a compatible global or undefined receiver before storing it.",
|
|
3994
4053
|
category: "correctness",
|
|
3995
4054
|
autofix: "none",
|
|
3996
4055
|
limitations: [
|
|
3997
|
-
"The rule
|
|
3998
|
-
"
|
|
3999
|
-
"
|
|
4056
|
+
"The rule follows stable local variables and defaulted parameters from an unshadowed ambient fetch or globalThis/self/window fetch into direct class, object, or member storage.",
|
|
4057
|
+
"Bare local aliases, callback arguments, returns, direct calls, compatible global or undefined bind/call/apply receivers, tests, scripts, generated files, and locally defined fetch implementations are excluded.",
|
|
4058
|
+
"Interprocedural aliases, mutable aliases, collection storage, and host identity remain manual review boundaries."
|
|
4000
4059
|
],
|
|
4001
4060
|
examples: [
|
|
4002
4061
|
{
|
|
4003
4062
|
id: "forwarded-global-fetch",
|
|
4004
|
-
title: "
|
|
4063
|
+
title: "Store a receiver-safe forwarding wrapper",
|
|
4005
4064
|
outcome: "no-match",
|
|
4006
|
-
files: [{ path: "src/client.ts", source: "
|
|
4065
|
+
files: [{ path: "src/client.ts", source: "class Client { readonly request = (input: RequestInfo | URL, init?: RequestInit) => fetch(input, init); }" }],
|
|
4007
4066
|
focusPath: "src/client.ts",
|
|
4008
4067
|
expectedCount: 0,
|
|
4009
4068
|
public: true
|
|
4010
4069
|
},
|
|
4011
4070
|
{
|
|
4012
|
-
id: "
|
|
4013
|
-
title: "Do not
|
|
4071
|
+
id: "receiver-unsafe-global-fetch",
|
|
4072
|
+
title: "Do not store raw ambient fetch as an object method",
|
|
4014
4073
|
outcome: "match",
|
|
4015
|
-
files: [{ path: "src/client.ts", source: "class Client {
|
|
4074
|
+
files: [{ path: "src/client.ts", source: "class Client { readonly request = fetch; }" }],
|
|
4016
4075
|
focusPath: "src/client.ts",
|
|
4017
4076
|
expectedCount: 1,
|
|
4018
4077
|
public: true
|
|
@@ -4020,85 +4079,131 @@ var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
|
|
|
4020
4079
|
]
|
|
4021
4080
|
};
|
|
4022
4081
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set(["globalThis", "self", "window"]);
|
|
4023
|
-
var
|
|
4082
|
+
var EXPLICIT_RECEIVER_METHODS = /* @__PURE__ */ new Set(["apply", "bind", "call"]);
|
|
4083
|
+
var STORAGE_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
|
|
4084
|
+
function unwrapExpression(node) {
|
|
4085
|
+
let current = node;
|
|
4086
|
+
while (current.type === AST_NODE_TYPES15.ChainExpression || current.type === AST_NODE_TYPES15.TSAsExpression || current.type === AST_NODE_TYPES15.TSNonNullExpression || current.type === AST_NODE_TYPES15.TSTypeAssertion) {
|
|
4087
|
+
current = current.expression;
|
|
4088
|
+
}
|
|
4089
|
+
return current;
|
|
4090
|
+
}
|
|
4024
4091
|
var no_detached_global_fetch_default = createRule({
|
|
4025
4092
|
name: "no-detached-global-fetch",
|
|
4026
4093
|
documentation: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION,
|
|
4027
4094
|
meta: {
|
|
4028
4095
|
type: "problem",
|
|
4029
|
-
docs: { description:
|
|
4096
|
+
docs: { description: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION.summary },
|
|
4030
4097
|
schema: [],
|
|
4031
4098
|
messages: {
|
|
4032
|
-
detachedGlobalFetch: "
|
|
4099
|
+
detachedGlobalFetch: "Raw ambient `fetch` becomes receiver-unsafe when stored or rebound this way. Store a forwarding wrapper or bind it to a compatible global or undefined receiver."
|
|
4033
4100
|
}
|
|
4034
4101
|
},
|
|
4035
4102
|
defaultOptions: [],
|
|
4036
4103
|
create(context) {
|
|
4037
4104
|
const sourceCode = context.sourceCode;
|
|
4038
|
-
if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) {
|
|
4039
|
-
|
|
4105
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) return {};
|
|
4106
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
4107
|
+
function bindingOf(identifier) {
|
|
4108
|
+
return ASTUtils7.findVariable(sourceCode.getScope(identifier), identifier.name);
|
|
4040
4109
|
}
|
|
4041
4110
|
function resolvesToGlobal(identifier) {
|
|
4042
|
-
const variable =
|
|
4111
|
+
const variable = bindingOf(identifier);
|
|
4043
4112
|
return variable === null || variable.defs.length === 0;
|
|
4044
4113
|
}
|
|
4045
4114
|
function isGlobalReceiver(node) {
|
|
4046
4115
|
return node.type === AST_NODE_TYPES15.Identifier && GLOBAL_RECEIVERS.has(node.name) && resolvesToGlobal(node);
|
|
4047
4116
|
}
|
|
4048
|
-
function
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
if (!parent.type.startsWith("TS")) return false;
|
|
4053
|
-
parent = parent.parent;
|
|
4054
|
-
}
|
|
4055
|
-
return false;
|
|
4117
|
+
function isStableAlias(variable) {
|
|
4118
|
+
return !variable.references.some(
|
|
4119
|
+
(reference) => reference.isWrite() && reference.init !== true
|
|
4120
|
+
);
|
|
4056
4121
|
}
|
|
4057
|
-
function
|
|
4058
|
-
|
|
4059
|
-
|
|
4122
|
+
function recordAlias(identifier) {
|
|
4123
|
+
const variable = bindingOf(identifier);
|
|
4124
|
+
if (variable !== null && isStableAlias(variable)) aliases.add(variable);
|
|
4060
4125
|
}
|
|
4061
|
-
function
|
|
4062
|
-
|
|
4126
|
+
function isGlobalFetchMember(node) {
|
|
4127
|
+
if (!isGlobalReceiver(node.object)) return false;
|
|
4128
|
+
if (!node.computed) {
|
|
4129
|
+
return node.property.type === AST_NODE_TYPES15.Identifier && node.property.name === "fetch";
|
|
4130
|
+
}
|
|
4131
|
+
return node.property.type === AST_NODE_TYPES15.Literal && node.property.value === "fetch";
|
|
4063
4132
|
}
|
|
4064
|
-
function
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
return false;
|
|
4133
|
+
function staticMemberName9(node) {
|
|
4134
|
+
if (!node.computed) {
|
|
4135
|
+
return node.property.type === AST_NODE_TYPES15.Identifier ? node.property.name : null;
|
|
4068
4136
|
}
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4137
|
+
return node.property.type === AST_NODE_TYPES15.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
4138
|
+
}
|
|
4139
|
+
function mayBeRawFetch(node) {
|
|
4140
|
+
const expression = unwrapExpression(node);
|
|
4141
|
+
if (expression.type === AST_NODE_TYPES15.Identifier) {
|
|
4142
|
+
if (expression.name === "fetch" && resolvesToGlobal(expression)) return true;
|
|
4143
|
+
const variable = bindingOf(expression);
|
|
4144
|
+
return variable !== null && aliases.has(variable) && isStableAlias(variable);
|
|
4072
4145
|
}
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
if (node.type === AST_NODE_TYPES15.MemberExpression) {
|
|
4076
|
-
const original = receiverOf(node);
|
|
4077
|
-
return original !== null && receiver.type === AST_NODE_TYPES15.Identifier && receiver.name === original.name;
|
|
4146
|
+
if (expression.type === AST_NODE_TYPES15.MemberExpression) {
|
|
4147
|
+
return isGlobalFetchMember(expression);
|
|
4078
4148
|
}
|
|
4079
|
-
|
|
4149
|
+
if (expression.type === AST_NODE_TYPES15.LogicalExpression) {
|
|
4150
|
+
return expression.operator === "&&" ? mayBeRawFetch(expression.right) : mayBeRawFetch(expression.left) || mayBeRawFetch(expression.right);
|
|
4151
|
+
}
|
|
4152
|
+
if (expression.type === AST_NODE_TYPES15.ConditionalExpression) {
|
|
4153
|
+
return mayBeRawFetch(expression.consequent) || mayBeRawFetch(expression.alternate);
|
|
4154
|
+
}
|
|
4155
|
+
if (expression.type === AST_NODE_TYPES15.SequenceExpression) {
|
|
4156
|
+
const last = expression.expressions.at(-1);
|
|
4157
|
+
return last !== void 0 && mayBeRawFetch(last);
|
|
4158
|
+
}
|
|
4159
|
+
return false;
|
|
4160
|
+
}
|
|
4161
|
+
function recordAliasFromValue(identifier, value) {
|
|
4162
|
+
if (mayBeRawFetch(value)) recordAlias(identifier);
|
|
4163
|
+
}
|
|
4164
|
+
function reportStored(node) {
|
|
4165
|
+
if (mayBeRawFetch(node)) context.report({ node, messageId: "detachedGlobalFetch" });
|
|
4080
4166
|
}
|
|
4081
|
-
function
|
|
4082
|
-
if (
|
|
4083
|
-
|
|
4167
|
+
function isCompatibleReceiver(node) {
|
|
4168
|
+
if (node === void 0 || node.type === AST_NODE_TYPES15.SpreadElement) return false;
|
|
4169
|
+
if (node.type !== AST_NODE_TYPES15.Identifier || !resolvesToGlobal(node)) return false;
|
|
4170
|
+
return GLOBAL_RECEIVERS.has(node.name) || node.name === "undefined";
|
|
4084
4171
|
}
|
|
4085
4172
|
return {
|
|
4086
|
-
|
|
4087
|
-
if (node.
|
|
4088
|
-
|
|
4089
|
-
if (parent.type === AST_NODE_TYPES15.MemberExpression && parent.property === node) {
|
|
4090
|
-
return;
|
|
4091
|
-
}
|
|
4092
|
-
if (parent.type === AST_NODE_TYPES15.Property && parent.key === node && !parent.computed && !parent.shorthand) {
|
|
4093
|
-
return;
|
|
4173
|
+
AssignmentExpression(node) {
|
|
4174
|
+
if (STORAGE_ASSIGNMENT_OPERATORS.has(node.operator) && node.left.type === AST_NODE_TYPES15.MemberExpression) {
|
|
4175
|
+
reportStored(node.right);
|
|
4094
4176
|
}
|
|
4095
|
-
reportIfDetached(node);
|
|
4096
4177
|
},
|
|
4097
|
-
|
|
4098
|
-
if (node.
|
|
4178
|
+
AssignmentPattern(node) {
|
|
4179
|
+
if (node.left.type !== AST_NODE_TYPES15.Identifier) return;
|
|
4180
|
+
recordAliasFromValue(node.left, node.right);
|
|
4181
|
+
if (node.parent.type === AST_NODE_TYPES15.TSParameterProperty) reportStored(node.right);
|
|
4182
|
+
},
|
|
4183
|
+
CallExpression(node) {
|
|
4184
|
+
const callee = unwrapExpression(node.callee);
|
|
4185
|
+
if (callee.type !== AST_NODE_TYPES15.MemberExpression || !EXPLICIT_RECEIVER_METHODS.has(staticMemberName9(callee) ?? "") || !mayBeRawFetch(callee.object) || isCompatibleReceiver(node.arguments[0])) return;
|
|
4186
|
+
context.report({ node: callee.object, messageId: "detachedGlobalFetch" });
|
|
4187
|
+
},
|
|
4188
|
+
Property(node) {
|
|
4189
|
+
if (node.parent.type === AST_NODE_TYPES15.ObjectPattern || node.method || node.value.type === AST_NODE_TYPES15.AssignmentPattern || node.value.type === AST_NODE_TYPES15.TSEmptyBodyFunctionExpression) return;
|
|
4190
|
+
reportStored(node.value);
|
|
4191
|
+
},
|
|
4192
|
+
PropertyDefinition(node) {
|
|
4193
|
+
if (node.value !== null) reportStored(node.value);
|
|
4194
|
+
},
|
|
4195
|
+
VariableDeclarator(node) {
|
|
4196
|
+
if (node.init === null) return;
|
|
4197
|
+
if (node.id.type === AST_NODE_TYPES15.Identifier) {
|
|
4198
|
+
recordAliasFromValue(node.id, node.init);
|
|
4099
4199
|
return;
|
|
4100
4200
|
}
|
|
4101
|
-
|
|
4201
|
+
if (node.id.type !== AST_NODE_TYPES15.ObjectPattern || !isGlobalReceiver(unwrapExpression(node.init))) return;
|
|
4202
|
+
for (const property of node.id.properties) {
|
|
4203
|
+
if (property.type !== AST_NODE_TYPES15.Property || property.computed || property.key.type !== AST_NODE_TYPES15.Identifier || property.key.name !== "fetch") continue;
|
|
4204
|
+
const value = property.value.type === AST_NODE_TYPES15.AssignmentPattern ? property.value.left : property.value;
|
|
4205
|
+
if (value.type === AST_NODE_TYPES15.Identifier) recordAlias(value);
|
|
4206
|
+
}
|
|
4102
4207
|
}
|
|
4103
4208
|
};
|
|
4104
4209
|
}
|
|
@@ -4364,7 +4469,7 @@ var NO_JSON_STRINGIFY_OBJECT_EQUALITY_DOCUMENTATION = {
|
|
|
4364
4469
|
};
|
|
4365
4470
|
var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["!=", "!==", "==", "==="]);
|
|
4366
4471
|
var PRIMITIVE_FLAGS = ts.TypeFlags.BigIntLike | ts.TypeFlags.BooleanLike | ts.TypeFlags.ESSymbolLike | ts.TypeFlags.Never | ts.TypeFlags.Null | ts.TypeFlags.NumberLike | ts.TypeFlags.StringLike | ts.TypeFlags.Undefined | ts.TypeFlags.Void;
|
|
4367
|
-
function
|
|
4472
|
+
function unwrapExpression2(node) {
|
|
4368
4473
|
let current = node;
|
|
4369
4474
|
while (current.type === AST_NODE_TYPES16.ChainExpression || current.type === AST_NODE_TYPES16.TSAsExpression || current.type === AST_NODE_TYPES16.TSNonNullExpression || current.type === AST_NODE_TYPES16.TSTypeAssertion) {
|
|
4370
4475
|
current = current.expression;
|
|
@@ -4372,10 +4477,10 @@ function unwrapExpression(node) {
|
|
|
4372
4477
|
return current;
|
|
4373
4478
|
}
|
|
4374
4479
|
function jsonStringifyArgument(node, sourceCode) {
|
|
4375
|
-
const expression =
|
|
4480
|
+
const expression = unwrapExpression2(node);
|
|
4376
4481
|
if (expression.type !== AST_NODE_TYPES16.CallExpression) return null;
|
|
4377
4482
|
const { callee } = expression;
|
|
4378
|
-
if (callee.type !== AST_NODE_TYPES16.MemberExpression || callee.
|
|
4483
|
+
if (callee.type !== AST_NODE_TYPES16.MemberExpression || callee.object.type !== AST_NODE_TYPES16.Identifier || callee.object.name !== "JSON" || (!callee.computed ? callee.property.type !== AST_NODE_TYPES16.Identifier || callee.property.name !== "stringify" : callee.property.type !== AST_NODE_TYPES16.Literal || callee.property.value !== "stringify")) {
|
|
4379
4484
|
return null;
|
|
4380
4485
|
}
|
|
4381
4486
|
const variable = ASTUtils9.findVariable(sourceCode.getScope(callee.object), "JSON");
|
|
@@ -4403,7 +4508,7 @@ function typeMayContainObject(type, checker) {
|
|
|
4403
4508
|
return true;
|
|
4404
4509
|
}
|
|
4405
4510
|
function syntaxMayContainObject(node) {
|
|
4406
|
-
const expression =
|
|
4511
|
+
const expression = unwrapExpression2(node);
|
|
4407
4512
|
if (expression.type === AST_NODE_TYPES16.ObjectExpression) return true;
|
|
4408
4513
|
if (expression.type !== AST_NODE_TYPES16.ArrayExpression) return null;
|
|
4409
4514
|
for (const element of expression.elements) {
|
|
@@ -5043,7 +5148,7 @@ var INTERFACE_CONTRACT_MEMBERS_PRIVATE_DOCUMENTATION = {
|
|
|
5043
5148
|
category: "architecture",
|
|
5044
5149
|
autofix: "none",
|
|
5045
5150
|
limitations: [
|
|
5046
|
-
"Only concrete classes with an explicit `implements` clause are checked; constructors
|
|
5151
|
+
"Only concrete classes with an explicit `implements` clause are checked; constructors, static members, protected extension hooks, and overrides are excluded.",
|
|
5047
5152
|
"Inherited interface members are resolved by TypeScript. Computed names are excluded because their contract identity is not stable syntax.",
|
|
5048
5153
|
"The rule abstains for the whole class when TypeScript cannot resolve any implemented contract, avoiding false positives for missing or unavailable package declarations.",
|
|
5049
5154
|
"The rule is report-only because a public member can have consumers in another source file; the developer must choose whether to extend the interface or privatize it.",
|
|
@@ -5095,7 +5200,7 @@ function reportClass(context, services, owner) {
|
|
|
5095
5200
|
}
|
|
5096
5201
|
}
|
|
5097
5202
|
function candidate(member) {
|
|
5098
|
-
return member.type === AST_NODE_TYPES20.MethodDefinition && member.kind !== "constructor" && !member.static && !member.computed && member.key.type === AST_NODE_TYPES20.Identifier && member.value.body !== null;
|
|
5203
|
+
return member.type === AST_NODE_TYPES20.MethodDefinition && member.kind !== "constructor" && !member.static && member.accessibility !== "protected" && !member.override && !member.computed && member.key.type === AST_NODE_TYPES20.Identifier && member.value.body !== null;
|
|
5099
5204
|
}
|
|
5100
5205
|
function interfaceNames(services, owner) {
|
|
5101
5206
|
const tsOwner = services.esTreeNodeToTSNodeMap.get(owner);
|
|
@@ -5453,7 +5558,7 @@ var NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION = {
|
|
|
5453
5558
|
remediation: "Rethrow the error, assert on it, or use the runner's explicit skip mechanism when the capability is optional.",
|
|
5454
5559
|
category: "testing",
|
|
5455
5560
|
filePatterns: ["**/*.test.*", "**/*.spec.*", "**/tests/**", "**/__tests__/**"],
|
|
5456
|
-
limitations: ["Only bare returns owned by a direct supported test callback and followed lexically by a framework assertion are reported."],
|
|
5561
|
+
limitations: ["Only bare returns owned by a direct supported test callback and followed lexically by a framework assertion are reported. A runner skip suppresses the finding only when it is an unconditional earlier statement in the return's block."],
|
|
5457
5562
|
examples: [
|
|
5458
5563
|
{ id: "rethrow", title: "Preserve the failure", outcome: "no-match", files: [{ path: "src/codec.test.ts", source: "test('decodes', () => { try { decode(); } catch (error) { throw error; } expect(result()).toBe('ok'); });" }], focusPath: "src/codec.test.ts", expectedCount: 0, public: true },
|
|
5459
5564
|
{ id: "bare-return", title: "Do not silently pass", outcome: "match", files: [{ path: "src/codec.test.ts", source: "test('decodes', () => { try { decode(); } catch { return; } expect(result()).toBe('ok'); });" }], focusPath: "src/codec.test.ts", expectedCount: 1, public: true }
|
|
@@ -5474,10 +5579,11 @@ function importedName4(identifier, context, modules) {
|
|
|
5474
5579
|
const variable = ASTUtils11.findVariable(context.sourceCode.getScope(identifier), identifier.name);
|
|
5475
5580
|
if (variable === null || variable.defs.length === 0) return identifier.name;
|
|
5476
5581
|
for (const definition of variable.defs) {
|
|
5477
|
-
if (definition.node.type !== AST_NODE_TYPES22.ImportSpecifier) continue;
|
|
5582
|
+
if (definition.node.type !== AST_NODE_TYPES22.ImportSpecifier && definition.node.type !== AST_NODE_TYPES22.ImportDefaultSpecifier && definition.node.type !== AST_NODE_TYPES22.ImportNamespaceSpecifier) continue;
|
|
5478
5583
|
const declaration = definition.node.parent;
|
|
5479
5584
|
if (declaration.type !== AST_NODE_TYPES22.ImportDeclaration || typeof declaration.source.value !== "string" || !modules.has(declaration.source.value)) continue;
|
|
5480
5585
|
if (declaration.source.value === "node:assert" || declaration.source.value === "node:assert/strict") return "assert";
|
|
5586
|
+
if (definition.node.type !== AST_NODE_TYPES22.ImportSpecifier) continue;
|
|
5481
5587
|
const imported = definition.node.imported;
|
|
5482
5588
|
return imported.type === AST_NODE_TYPES22.Identifier ? imported.name : String(imported.value);
|
|
5483
5589
|
}
|
|
@@ -5529,6 +5635,13 @@ function isExplicitSkip(node, context) {
|
|
|
5529
5635
|
const root = rootIdentifier2(node.callee.object);
|
|
5530
5636
|
return root !== null && TEST_NAMES.has(importedName4(root, context, TEST_MODULES2) ?? "");
|
|
5531
5637
|
}
|
|
5638
|
+
function hasDominatingExplicitSkip(node, context) {
|
|
5639
|
+
const block = node.parent;
|
|
5640
|
+
if (block?.type !== AST_NODE_TYPES22.BlockStatement) return false;
|
|
5641
|
+
return block.body.some(
|
|
5642
|
+
(candidate2) => candidate2.range[1] <= node.range[0] && candidate2.type === AST_NODE_TYPES22.ExpressionStatement && isExplicitSkip(candidate2.expression, context)
|
|
5643
|
+
);
|
|
5644
|
+
}
|
|
5532
5645
|
var no_bare_return_from_test_catch_default = createRule({
|
|
5533
5646
|
name: "no-bare-return-from-test-catch",
|
|
5534
5647
|
documentation: NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION,
|
|
@@ -5554,11 +5667,12 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
5554
5667
|
}
|
|
5555
5668
|
if (current === null || current === void 0) break;
|
|
5556
5669
|
}
|
|
5557
|
-
if (catchClause === null
|
|
5670
|
+
if (catchClause === null) return;
|
|
5558
5671
|
const parameter = catchClause.param;
|
|
5559
|
-
|
|
5672
|
+
const returnBlock = node.parent;
|
|
5673
|
+
if (parameter?.type === AST_NODE_TYPES22.Identifier && returnBlock?.type === AST_NODE_TYPES22.BlockStatement) {
|
|
5560
5674
|
const errorBinding = ASTUtils11.findVariable(context.sourceCode.getScope(parameter), parameter.name);
|
|
5561
|
-
const assertedError =
|
|
5675
|
+
const assertedError = returnBlock.body.some((statement) => {
|
|
5562
5676
|
if (statement.range[1] >= node.range[0] || statement.type !== AST_NODE_TYPES22.ExpressionStatement) return false;
|
|
5563
5677
|
const expression = statement.expression;
|
|
5564
5678
|
if (expression.type !== AST_NODE_TYPES22.CallExpression || !isAssertion(expression, context)) return false;
|
|
@@ -5572,7 +5686,7 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
5572
5686
|
});
|
|
5573
5687
|
if (assertedError) return;
|
|
5574
5688
|
}
|
|
5575
|
-
if (
|
|
5689
|
+
if (hasDominatingExplicitSkip(node, context)) return;
|
|
5576
5690
|
if (!walkOwnScope(owner.body, (current) => current.range[0] > node.range[1] && isAssertion(current, context))) return;
|
|
5577
5691
|
context.report({ node, messageId: "bareReturnFromTestCatch" });
|
|
5578
5692
|
}
|
|
@@ -5810,7 +5924,8 @@ var no_long_comment_default = createRule({
|
|
|
5810
5924
|
});
|
|
5811
5925
|
|
|
5812
5926
|
// src/rules/no-vague-suppression-description.ts
|
|
5813
|
-
var
|
|
5927
|
+
var ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE = /^eslint-(?:disable|disable-next-line|disable-line)\b[^:\n]*?\s*(?::|--)\s*(.+?)\s*$/iu;
|
|
5928
|
+
var TS_EXPECT_ERROR_WITH_DESCRIPTION_RE = /^@ts-expect-error\b(?:(?:\s*(?::|--)\s*)|\s+)(.+?)\s*$/iu;
|
|
5814
5929
|
var VAGUE_RE = /^(?:needed|required|intentional(?:ly)?|ignore(?:d)?|false positive|type error|typescript|to satisfy (?:the )?(?:linter|typescript|type checker))\.?$/iu;
|
|
5815
5930
|
var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
|
|
5816
5931
|
summary: "Require suppression descriptions to name the concrete mismatch or invariant instead of a generic non-reason.",
|
|
@@ -5853,6 +5968,9 @@ var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
|
|
|
5853
5968
|
}
|
|
5854
5969
|
]
|
|
5855
5970
|
};
|
|
5971
|
+
function suppressionDescription(text) {
|
|
5972
|
+
return (ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE.exec(text)?.[1] ?? TS_EXPECT_ERROR_WITH_DESCRIPTION_RE.exec(text)?.[1])?.trim();
|
|
5973
|
+
}
|
|
5856
5974
|
var no_vague_suppression_description_default = createRule({
|
|
5857
5975
|
name: "no-vague-suppression-description",
|
|
5858
5976
|
documentation: NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION,
|
|
@@ -5875,7 +5993,7 @@ var no_vague_suppression_description_default = createRule({
|
|
|
5875
5993
|
Program() {
|
|
5876
5994
|
for (const comment of context.sourceCode.getAllComments()) {
|
|
5877
5995
|
const text = comment.value.trim();
|
|
5878
|
-
const description =
|
|
5996
|
+
const description = suppressionDescription(text);
|
|
5879
5997
|
if (description === void 0 || !VAGUE_RE.test(description)) continue;
|
|
5880
5998
|
context.report({
|
|
5881
5999
|
loc: comment.loc,
|
|
@@ -6939,12 +7057,24 @@ var NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION = {
|
|
|
6939
7057
|
{ id: "runtime-load", title: "Do not load a restricted library at runtime", outcome: "match", files: [{ path: "src/client.ts", source: "const client = require('axios');" }], focusPath: "src/client.ts", expectedCount: 1, public: true }
|
|
6940
7058
|
]
|
|
6941
7059
|
};
|
|
6942
|
-
function
|
|
6943
|
-
|
|
7060
|
+
function staticModule(node) {
|
|
7061
|
+
if (node?.type === AST_NODE_TYPES28.Literal && typeof node.value === "string") {
|
|
7062
|
+
return node.value;
|
|
7063
|
+
}
|
|
7064
|
+
if (node?.type === AST_NODE_TYPES28.TemplateLiteral && node.expressions.length === 0) {
|
|
7065
|
+
return node.quasis[0]?.value.cooked ?? null;
|
|
7066
|
+
}
|
|
7067
|
+
return null;
|
|
6944
7068
|
}
|
|
6945
7069
|
function matchesModule(source, module) {
|
|
6946
7070
|
return source === module || source.startsWith(`${module}/`);
|
|
6947
7071
|
}
|
|
7072
|
+
function staticMemberName4(node) {
|
|
7073
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES28.Identifier) {
|
|
7074
|
+
return node.property.name;
|
|
7075
|
+
}
|
|
7076
|
+
return node.computed && node.property.type === AST_NODE_TYPES28.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
7077
|
+
}
|
|
6948
7078
|
var no_restricted_library_load_default = createRule({
|
|
6949
7079
|
name: "no-restricted-library-load",
|
|
6950
7080
|
documentation: NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION,
|
|
@@ -7008,24 +7138,24 @@ var no_restricted_library_load_default = createRule({
|
|
|
7008
7138
|
}
|
|
7009
7139
|
return {
|
|
7010
7140
|
ImportExpression(node) {
|
|
7011
|
-
const source =
|
|
7141
|
+
const source = staticModule(node.source);
|
|
7012
7142
|
if (source !== null) report2(node.source, source);
|
|
7013
7143
|
},
|
|
7014
7144
|
CallExpression(node) {
|
|
7015
7145
|
let requireIdentifier = null;
|
|
7016
7146
|
if (node.callee.type === AST_NODE_TYPES28.Identifier && node.callee.name === "require") {
|
|
7017
7147
|
requireIdentifier = node.callee;
|
|
7018
|
-
} else if (node.callee.type === AST_NODE_TYPES28.MemberExpression &&
|
|
7148
|
+
} else if (node.callee.type === AST_NODE_TYPES28.MemberExpression && node.callee.object.type === AST_NODE_TYPES28.Identifier && node.callee.object.name === "require" && staticMemberName4(node.callee) === "resolve") {
|
|
7019
7149
|
requireIdentifier = node.callee.object;
|
|
7020
7150
|
}
|
|
7021
7151
|
if (requireIdentifier === null || !isUnshadowedRequire(requireIdentifier)) return;
|
|
7022
|
-
const source =
|
|
7152
|
+
const source = staticModule(node.arguments[0]);
|
|
7023
7153
|
if (source !== null) report2(node.arguments[0], source);
|
|
7024
7154
|
},
|
|
7025
7155
|
TSImportEqualsDeclaration(node) {
|
|
7026
7156
|
if (node.importKind === "type") return;
|
|
7027
7157
|
if (node.moduleReference.type !== AST_NODE_TYPES28.TSExternalModuleReference) return;
|
|
7028
|
-
const source =
|
|
7158
|
+
const source = staticModule(node.moduleReference.expression);
|
|
7029
7159
|
if (source !== null) report2(node.moduleReference.expression, source);
|
|
7030
7160
|
}
|
|
7031
7161
|
};
|
|
@@ -11072,7 +11202,7 @@ function rootIdentifier3(callee) {
|
|
|
11072
11202
|
if (callee.type === AST_NODE_TYPES45.TaggedTemplateExpression) return rootIdentifier3(callee.tag);
|
|
11073
11203
|
return null;
|
|
11074
11204
|
}
|
|
11075
|
-
function
|
|
11205
|
+
function staticMemberName5(member) {
|
|
11076
11206
|
if (!member.computed && member.property.type === AST_NODE_TYPES45.Identifier) return member.property.name;
|
|
11077
11207
|
if (member.computed && member.property.type === AST_NODE_TYPES45.Literal && typeof member.property.value === "string") {
|
|
11078
11208
|
return member.property.value;
|
|
@@ -11087,7 +11217,7 @@ function isTestBody2(node, isFrameworkTest) {
|
|
|
11087
11217
|
function isTestCaller(callee) {
|
|
11088
11218
|
if (callee.type === AST_NODE_TYPES45.Identifier) return TEST_CALLERS3.has(callee.name);
|
|
11089
11219
|
if (callee.type !== AST_NODE_TYPES45.MemberExpression) return false;
|
|
11090
|
-
const member =
|
|
11220
|
+
const member = staticMemberName5(callee);
|
|
11091
11221
|
return member !== null && TEST_MODIFIERS3.has(member) && isTestCaller(callee.object);
|
|
11092
11222
|
}
|
|
11093
11223
|
function nearestEnclosingFunction2(node) {
|
|
@@ -11168,7 +11298,7 @@ function opensSubtest(node, callbackParameters) {
|
|
|
11168
11298
|
return false;
|
|
11169
11299
|
}
|
|
11170
11300
|
const callee = node.callee;
|
|
11171
|
-
return callee.type === AST_NODE_TYPES45.MemberExpression &&
|
|
11301
|
+
return callee.type === AST_NODE_TYPES45.MemberExpression && staticMemberName5(callee) === "test" && callee.object.type === AST_NODE_TYPES45.Identifier && callbackParameters.has(callee.object.name) && node.arguments.some(
|
|
11172
11302
|
(argument) => argument.type !== AST_NODE_TYPES45.SpreadElement && FUNCTION_TYPES6.has(argument.type)
|
|
11173
11303
|
);
|
|
11174
11304
|
}
|
|
@@ -11218,7 +11348,7 @@ var test_loops_over_literal_cases_default = createRule({
|
|
|
11218
11348
|
for (let current = node; current !== void 0 && current !== enclosing; current = current.parent) {
|
|
11219
11349
|
if (current.parent?.type === AST_NODE_TYPES45.BlockStatement && current.parent.body.at(-1) !== current) return;
|
|
11220
11350
|
}
|
|
11221
|
-
const cases =
|
|
11351
|
+
const cases = unwrapExpression3(node.right);
|
|
11222
11352
|
const callbackParameters = new Set(
|
|
11223
11353
|
enclosing.params.flatMap((parameter) => parameter.type === AST_NODE_TYPES45.Identifier ? [parameter.name] : [])
|
|
11224
11354
|
);
|
|
@@ -11246,9 +11376,9 @@ var test_loops_over_literal_cases_default = createRule({
|
|
|
11246
11376
|
};
|
|
11247
11377
|
}
|
|
11248
11378
|
});
|
|
11249
|
-
function
|
|
11379
|
+
function unwrapExpression3(node) {
|
|
11250
11380
|
if (node.type === AST_NODE_TYPES45.TSAsExpression || node.type === AST_NODE_TYPES45.TSTypeAssertion || node.type === AST_NODE_TYPES45.TSSatisfiesExpression || node.type === AST_NODE_TYPES45.TSNonNullExpression) {
|
|
11251
|
-
return
|
|
11381
|
+
return unwrapExpression3(node.expression);
|
|
11252
11382
|
}
|
|
11253
11383
|
return node;
|
|
11254
11384
|
}
|
|
@@ -12041,14 +12171,14 @@ function isAsConst(node, sourceText) {
|
|
|
12041
12171
|
if (node.type !== AST_NODE_TYPES52.TSAsExpression) return false;
|
|
12042
12172
|
return sourceText(node.typeAnnotation).trim() === "const";
|
|
12043
12173
|
}
|
|
12044
|
-
function
|
|
12174
|
+
function unwrapExpression4(node) {
|
|
12045
12175
|
if (node.type === AST_NODE_TYPES52.TSAsExpression || node.type === AST_NODE_TYPES52.TSSatisfiesExpression || node.type === AST_NODE_TYPES52.TSNonNullExpression) {
|
|
12046
|
-
return
|
|
12176
|
+
return unwrapExpression4(node.expression);
|
|
12047
12177
|
}
|
|
12048
12178
|
return node;
|
|
12049
12179
|
}
|
|
12050
12180
|
function isObjectFreeze(node, isUnshadowedGlobal3) {
|
|
12051
|
-
const inner =
|
|
12181
|
+
const inner = unwrapExpression4(node);
|
|
12052
12182
|
if (inner.type === AST_NODE_TYPES52.CallExpression && inner.callee.type === AST_NODE_TYPES52.MemberExpression && !inner.callee.computed && inner.callee.object.type === AST_NODE_TYPES52.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === AST_NODE_TYPES52.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1) {
|
|
12053
12183
|
const argument = inner.arguments[0];
|
|
12054
12184
|
return argument !== void 0 && argument.type !== AST_NODE_TYPES52.SpreadElement && collectionKind(argument, isUnshadowedGlobal3) === "literal";
|
|
@@ -12056,7 +12186,7 @@ function isObjectFreeze(node, isUnshadowedGlobal3) {
|
|
|
12056
12186
|
return false;
|
|
12057
12187
|
}
|
|
12058
12188
|
function collectionKind(node, isUnshadowedGlobal3) {
|
|
12059
|
-
const inner =
|
|
12189
|
+
const inner = unwrapExpression4(node);
|
|
12060
12190
|
if (inner.type === AST_NODE_TYPES52.CallExpression && inner.callee.type === AST_NODE_TYPES52.MemberExpression && !inner.callee.computed && inner.callee.object.type === AST_NODE_TYPES52.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === AST_NODE_TYPES52.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1 && inner.arguments[0] !== void 0 && inner.arguments[0].type !== AST_NODE_TYPES52.SpreadElement) {
|
|
12061
12191
|
return collectionKind(inner.arguments[0], isUnshadowedGlobal3);
|
|
12062
12192
|
}
|
|
@@ -15497,17 +15627,18 @@ var prefer_schema_for_api_payload_default = createRule({
|
|
|
15497
15627
|
// src/rules/prefer-shared-zod-enum.ts
|
|
15498
15628
|
import { AST_NODE_TYPES as AST_NODE_TYPES67, ASTUtils as ASTUtils38 } from "@typescript-eslint/utils";
|
|
15499
15629
|
var PREFER_SHARED_ZOD_ENUM_DOCUMENTATION = {
|
|
15500
|
-
summary: "Give literal Zod enum domains one reusable module-level schema.",
|
|
15501
|
-
rationale: "
|
|
15630
|
+
summary: "Give repeated literal Zod enum domains one reusable module-level schema.",
|
|
15631
|
+
rationale: "Repeated literal domains hide a shared contract and allow equivalent fields to drift independently.",
|
|
15502
15632
|
remediation: "Declare a module-level named Zod enum schema and reuse it at each field or contract site.",
|
|
15503
15633
|
category: "maintainability",
|
|
15504
|
-
limitations: ["Only direct z.enum calls with string-literal arrays are inspected; computed domains require review. Equal values do not prove a shared business domain: retain local schemas when ownership
|
|
15634
|
+
limitations: ["Only two or more exact, same-order direct z.enum calls with string-literal arrays and no customization argument in one module are inspected; one-off, computed, and customized domains require review. Equal values do not prove a shared business domain: retain local schemas when ownership or future evolution differs, and review initialization order before extraction."],
|
|
15505
15635
|
examples: [
|
|
15506
15636
|
{ id: "shared-provider", title: "Reuse a named enum schema", outcome: "no-match", files: [{ path: "src/provider.ts", source: "import { z } from 'zod'; const ProviderSchema = z.enum(['agy', 'claude', 'sol']); const JobSchema = z.object({ provider: ProviderSchema }); const StatusSchema = z.object({ provider: ProviderSchema.optional() });" }], focusPath: "src/provider.ts", expectedCount: 0, public: true },
|
|
15507
|
-
{ id: "inline-provider", title: "Do not inline enum
|
|
15637
|
+
{ id: "inline-provider", title: "Do not repeat an inline enum domain", outcome: "match", files: [{ path: "src/provider.ts", source: "import { z } from 'zod'; const JobSchema = z.object({ provider: z.enum(['agy', 'claude', 'sol']) }); const StatusSchema = z.object({ provider: z.enum(['agy', 'claude', 'sol']).optional() });" }], focusPath: "src/provider.ts", expectedCount: 2, public: true }
|
|
15508
15638
|
]
|
|
15509
15639
|
};
|
|
15510
15640
|
function literalDomain(node) {
|
|
15641
|
+
if (node.arguments.length !== 1) return null;
|
|
15511
15642
|
const [argument] = node.arguments;
|
|
15512
15643
|
if (argument?.type !== AST_NODE_TYPES67.ArrayExpression || argument.elements.length < 2) return null;
|
|
15513
15644
|
const values = [];
|
|
@@ -15533,7 +15664,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15533
15664
|
documentation: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION,
|
|
15534
15665
|
meta: {
|
|
15535
15666
|
type: "suggestion",
|
|
15536
|
-
docs: { description:
|
|
15667
|
+
docs: { description: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION.summary },
|
|
15537
15668
|
schema: [],
|
|
15538
15669
|
messages: {
|
|
15539
15670
|
shareEnumDomain: "Extract this literal Zod enum to one module-level named schema and reuse it."
|
|
@@ -15544,7 +15675,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15544
15675
|
if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
|
|
15545
15676
|
const zodBindings = /* @__PURE__ */ new Set();
|
|
15546
15677
|
const bindingOf = (node) => ASTUtils38.findVariable(context.sourceCode.getScope(node), node.name);
|
|
15547
|
-
const
|
|
15678
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
15548
15679
|
return {
|
|
15549
15680
|
ImportDeclaration(node) {
|
|
15550
15681
|
if (!isZodModule(node.source.value)) return;
|
|
@@ -15562,10 +15693,19 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15562
15693
|
const domain = literalDomain(node);
|
|
15563
15694
|
if (domain === null) return;
|
|
15564
15695
|
const key = JSON.stringify(domain);
|
|
15565
|
-
const
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15696
|
+
const group = candidates.get(key) ?? [];
|
|
15697
|
+
group.push({ node, named: isModuleLevelNamedSchema(node) });
|
|
15698
|
+
candidates.set(key, group);
|
|
15699
|
+
},
|
|
15700
|
+
"Program:exit"() {
|
|
15701
|
+
for (const group of candidates.values()) {
|
|
15702
|
+
if (group.length < 2) continue;
|
|
15703
|
+
const canonical = group.find((candidate2) => candidate2.named);
|
|
15704
|
+
for (const candidate2 of group) {
|
|
15705
|
+
if (candidate2 === canonical) continue;
|
|
15706
|
+
context.report({ node: candidate2.node, messageId: "shareEnumDomain" });
|
|
15707
|
+
}
|
|
15708
|
+
}
|
|
15569
15709
|
}
|
|
15570
15710
|
};
|
|
15571
15711
|
}
|
|
@@ -15640,35 +15780,6 @@ var prefer_switch_for_repeated_equality_default = createRule({
|
|
|
15640
15780
|
import { AST_NODE_TYPES as AST_NODE_TYPES69 } from "@typescript-eslint/utils";
|
|
15641
15781
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
|
|
15642
15782
|
import { dirname as dirname2, join as join2, parse } from "path";
|
|
15643
|
-
|
|
15644
|
-
// src/rules/_tailwind.ts
|
|
15645
|
-
var tailwindVariantPrefix = (token) => {
|
|
15646
|
-
let bracketDepth = 0;
|
|
15647
|
-
let parenthesisDepth = 0;
|
|
15648
|
-
let escaped = false;
|
|
15649
|
-
let end = 0;
|
|
15650
|
-
for (let index = 0; index < token.length; index += 1) {
|
|
15651
|
-
const character = token[index];
|
|
15652
|
-
if (escaped) {
|
|
15653
|
-
escaped = false;
|
|
15654
|
-
continue;
|
|
15655
|
-
}
|
|
15656
|
-
if (character === "\\") {
|
|
15657
|
-
escaped = true;
|
|
15658
|
-
continue;
|
|
15659
|
-
}
|
|
15660
|
-
if (character === "[") bracketDepth += 1;
|
|
15661
|
-
else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
15662
|
-
else if (character === "(") parenthesisDepth += 1;
|
|
15663
|
-
else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
|
|
15664
|
-
else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
|
|
15665
|
-
}
|
|
15666
|
-
return token.slice(0, end);
|
|
15667
|
-
};
|
|
15668
|
-
var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
|
|
15669
|
-
var classTokens = (value) => value.split(/\s+/).filter(Boolean);
|
|
15670
|
-
|
|
15671
|
-
// src/rules/prefer-semantic-colors.ts
|
|
15672
15783
|
var PREFER_SEMANTIC_COLORS_DOCUMENTATION = {
|
|
15673
15784
|
summary: "Enforce semantic color tokens over raw Tailwind palette classes, arbitrary color values, and inline color literals.",
|
|
15674
15785
|
rationale: "Semantic tokens keep themes and product meaning consistent while raw colors couple components to a palette value.",
|
|
@@ -16722,7 +16833,7 @@ var TEST_MODIFIERS4 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "s
|
|
|
16722
16833
|
var EXPECT_MODIFIERS = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
|
|
16723
16834
|
var SNAPSHOT_MATCHERS = /snapshot/iu;
|
|
16724
16835
|
var MIN_CASES2 = 3;
|
|
16725
|
-
function
|
|
16836
|
+
function staticMemberName6(node) {
|
|
16726
16837
|
if (!node.computed && node.property.type === AST_NODE_TYPES71.Identifier) return node.property.name;
|
|
16727
16838
|
if (node.computed && node.property.type === AST_NODE_TYPES71.Literal && typeof node.property.value === "string") return node.property.value;
|
|
16728
16839
|
return null;
|
|
@@ -16749,7 +16860,7 @@ function isDirectTestCallback2(node, context) {
|
|
|
16749
16860
|
function testRoot2(callee) {
|
|
16750
16861
|
if (callee.type === AST_NODE_TYPES71.Identifier) return callee;
|
|
16751
16862
|
if (callee.type !== AST_NODE_TYPES71.MemberExpression) return null;
|
|
16752
|
-
const modifier =
|
|
16863
|
+
const modifier = staticMemberName6(callee);
|
|
16753
16864
|
return modifier !== null && TEST_MODIFIERS4.has(modifier) ? testRoot2(callee.object) : null;
|
|
16754
16865
|
}
|
|
16755
16866
|
function isStatic(node) {
|
|
@@ -16813,7 +16924,7 @@ function expectCallFromMatcher(node) {
|
|
|
16813
16924
|
const modifiers = [];
|
|
16814
16925
|
let receiver = node.object;
|
|
16815
16926
|
while (receiver.type === AST_NODE_TYPES71.MemberExpression) {
|
|
16816
|
-
const modifier =
|
|
16927
|
+
const modifier = staticMemberName6(receiver);
|
|
16817
16928
|
if (modifier === null || !EXPECT_MODIFIERS.has(modifier)) return null;
|
|
16818
16929
|
modifiers.unshift(modifier);
|
|
16819
16930
|
receiver = receiver.object;
|
|
@@ -18407,8 +18518,8 @@ var FLUENT_BUILDER_NAME_RE = /Builder$/;
|
|
|
18407
18518
|
var FLUENT_RESULT_TYPE_RE = /(?:Builder|Base|Query|Without)(?:\W|$)/;
|
|
18408
18519
|
var ROUTER_FACTORY_NAME = "Router";
|
|
18409
18520
|
var FRAMEWORK_HTTP_TYPES = /* @__PURE__ */ new Set(["Request", "Response", "NextFunction"]);
|
|
18410
|
-
var
|
|
18411
|
-
var
|
|
18521
|
+
var STORAGE_ASSIGNMENT_OPERATORS2 = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
|
|
18522
|
+
var staticMemberName7 = (member) => {
|
|
18412
18523
|
if (member.property.type === AST_NODE_TYPES77.PrivateIdentifier) return `#${member.property.name}`;
|
|
18413
18524
|
if (!member.computed && member.property.type === AST_NODE_TYPES77.Identifier) return member.property.name;
|
|
18414
18525
|
return member.computed && member.property.type === AST_NODE_TYPES77.Literal && typeof member.property.value === "string" ? member.property.value : null;
|
|
@@ -18500,8 +18611,8 @@ var readConstructor = (ctor, declared, typeParameters) => {
|
|
|
18500
18611
|
if (current === void 0) break;
|
|
18501
18612
|
if (current.type === AST_NODE_TYPES77.ArrowFunctionExpression || current.type === AST_NODE_TYPES77.FunctionExpression || current.type === AST_NODE_TYPES77.FunctionDeclaration || current.type === AST_NODE_TYPES77.ClassExpression || current.type === AST_NODE_TYPES77.ClassDeclaration) continue;
|
|
18502
18613
|
const expression = current.type === AST_NODE_TYPES77.ExpressionStatement ? current.expression : null;
|
|
18503
|
-
const storedField = expression?.type === AST_NODE_TYPES77.AssignmentExpression && expression.left.type === AST_NODE_TYPES77.MemberExpression && expression.left.object.type === AST_NODE_TYPES77.ThisExpression ?
|
|
18504
|
-
if (expression?.type !== AST_NODE_TYPES77.AssignmentExpression || !
|
|
18614
|
+
const storedField = expression?.type === AST_NODE_TYPES77.AssignmentExpression && expression.left.type === AST_NODE_TYPES77.MemberExpression && expression.left.object.type === AST_NODE_TYPES77.ThisExpression ? staticMemberName7(expression.left) : null;
|
|
18615
|
+
if (expression?.type !== AST_NODE_TYPES77.AssignmentExpression || !STORAGE_ASSIGNMENT_OPERATORS2.has(expression.operator) || expression.left.type !== AST_NODE_TYPES77.MemberExpression || expression.left.object.type !== AST_NODE_TYPES77.ThisExpression || storedField === null) {
|
|
18505
18616
|
for (const key of Object.keys(current)) {
|
|
18506
18617
|
if (key === "parent") continue;
|
|
18507
18618
|
const value = current[key];
|
|
@@ -18525,7 +18636,7 @@ var readConstructor = (ctor, declared, typeParameters) => {
|
|
|
18525
18636
|
const fields = storedFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Set();
|
|
18526
18637
|
fields.add(storedField);
|
|
18527
18638
|
storedFieldsFrom.set(source.object.name, fields);
|
|
18528
|
-
const member =
|
|
18639
|
+
const member = staticMemberName7(source);
|
|
18529
18640
|
if (member !== null) {
|
|
18530
18641
|
const members = storedMemberFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Map();
|
|
18531
18642
|
const memberFields = members.get(member) ?? /* @__PURE__ */ new Set();
|
|
@@ -18657,7 +18768,7 @@ var invokedInstanceField = (call) => {
|
|
|
18657
18768
|
var instanceField = (candidate2) => {
|
|
18658
18769
|
let node = candidate2;
|
|
18659
18770
|
while (node.type === AST_NODE_TYPES77.ChainExpression || node.type === AST_NODE_TYPES77.TSAsExpression || node.type === AST_NODE_TYPES77.TSNonNullExpression || node.type === AST_NODE_TYPES77.TSSatisfiesExpression || node.type === AST_NODE_TYPES77.TSTypeAssertion) node = node.expression;
|
|
18660
|
-
return node.type === AST_NODE_TYPES77.MemberExpression && node.object.type === AST_NODE_TYPES77.ThisExpression ?
|
|
18771
|
+
return node.type === AST_NODE_TYPES77.MemberExpression && node.object.type === AST_NODE_TYPES77.ThisExpression ? staticMemberName7(node) : null;
|
|
18661
18772
|
};
|
|
18662
18773
|
var behaviorallyInvokedFields = (body2) => {
|
|
18663
18774
|
const invoked = /* @__PURE__ */ new Set();
|
|
@@ -19247,14 +19358,14 @@ var REQUIRE_STATIC_NEXT_MATCHER_DOCUMENTATION = {
|
|
|
19247
19358
|
]
|
|
19248
19359
|
};
|
|
19249
19360
|
var NEXT_ENTRY_FILE = /(?:^|[/\\])(?:middleware|proxy)\.[cm]?[jt]sx?$/u;
|
|
19250
|
-
function
|
|
19361
|
+
function unwrapExpression5(node) {
|
|
19251
19362
|
if (node.type === AST_NODE_TYPES79.TSAsExpression || node.type === AST_NODE_TYPES79.TSSatisfiesExpression || node.type === AST_NODE_TYPES79.TSNonNullExpression || node.type === AST_NODE_TYPES79.TSTypeAssertion) {
|
|
19252
|
-
return
|
|
19363
|
+
return unwrapExpression5(node.expression);
|
|
19253
19364
|
}
|
|
19254
19365
|
return node;
|
|
19255
19366
|
}
|
|
19256
19367
|
function isStaticValue(node) {
|
|
19257
|
-
const value =
|
|
19368
|
+
const value = unwrapExpression5(node);
|
|
19258
19369
|
if (value.type === AST_NODE_TYPES79.Literal) {
|
|
19259
19370
|
return true;
|
|
19260
19371
|
}
|
|
@@ -19274,9 +19385,8 @@ function isStaticValue(node) {
|
|
|
19274
19385
|
return false;
|
|
19275
19386
|
}
|
|
19276
19387
|
function propertyName5(property) {
|
|
19277
|
-
if (property.computed) return
|
|
19278
|
-
|
|
19279
|
-
return typeof property.key.value === "string" ? property.key.value : null;
|
|
19388
|
+
if (!property.computed && property.key.type === AST_NODE_TYPES79.Identifier) return property.key.name;
|
|
19389
|
+
return property.key.type === AST_NODE_TYPES79.Literal && typeof property.key.value === "string" ? property.key.value : null;
|
|
19280
19390
|
}
|
|
19281
19391
|
var require_static_next_matcher_default = createRule({
|
|
19282
19392
|
name: "require-static-next-matcher",
|
|
@@ -19305,7 +19415,7 @@ var require_static_next_matcher_default = createRule({
|
|
|
19305
19415
|
if (declaration.id.type !== AST_NODE_TYPES79.Identifier || declaration.id.name !== "config" || declaration.init === null) {
|
|
19306
19416
|
continue;
|
|
19307
19417
|
}
|
|
19308
|
-
const config =
|
|
19418
|
+
const config = unwrapExpression5(declaration.init);
|
|
19309
19419
|
if (config.type !== AST_NODE_TYPES79.ObjectExpression) {
|
|
19310
19420
|
continue;
|
|
19311
19421
|
}
|
|
@@ -19324,40 +19434,46 @@ var require_static_next_matcher_default = createRule({
|
|
|
19324
19434
|
});
|
|
19325
19435
|
|
|
19326
19436
|
// src/rules/require-use-form-default-values.ts
|
|
19327
|
-
import { ASTUtils as ASTUtils44 } from "@typescript-eslint/utils";
|
|
19437
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES80, ASTUtils as ASTUtils44 } from "@typescript-eslint/utils";
|
|
19328
19438
|
var REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION = {
|
|
19329
|
-
summary: "
|
|
19330
|
-
rationale: "
|
|
19331
|
-
remediation: "Provide
|
|
19439
|
+
summary: "Require explicit form-level initialization or a field default for directly bound Controller fields.",
|
|
19440
|
+
rationale: "React Hook Form exposes form-level defaultValues/values and field-level defaultValue as its explicit initialization mechanisms. A directly bound controlled field should not omit both mechanisms.",
|
|
19441
|
+
remediation: "Provide a non-undefined defaultValues or values option to useForm, or a non-undefined defaultValue on the associated Controller/useController field.",
|
|
19332
19442
|
category: "correctness",
|
|
19333
19443
|
limitations: [
|
|
19334
|
-
"
|
|
19444
|
+
"The rule proves only that an explicit form-level initialization option or field default is present; it does not prove that a particular field path occurs inside a defaultValues/values object.",
|
|
19445
|
+
"The rule reports only scope-resolved Controller/useController fields explicitly bound to a directly created useForm control. FormProvider context, wrapper hooks, dynamic options, spreads, computed properties, and interprocedural flows are intentionally left unreported."
|
|
19335
19446
|
],
|
|
19336
19447
|
examples: [
|
|
19337
19448
|
{
|
|
19338
|
-
id: "
|
|
19339
|
-
title: "
|
|
19449
|
+
id: "controlled-field-with-form-defaults",
|
|
19450
|
+
title: "Initialize controlled fields at form level",
|
|
19340
19451
|
outcome: "no-match",
|
|
19341
|
-
files: [{ path: "profile-form.tsx", source: "'use client'; import { useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm({ defaultValues: { name: '' } }); return <
|
|
19452
|
+
files: [{ path: "profile-form.tsx", source: "'use client'; import { Controller, useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm({ defaultValues: { name: '' } }); return <Controller control={form.control} name='name' render={() => null} />; }" }],
|
|
19342
19453
|
focusPath: "profile-form.tsx",
|
|
19343
19454
|
expectedCount: 0,
|
|
19344
19455
|
public: true
|
|
19345
19456
|
},
|
|
19346
19457
|
{
|
|
19347
|
-
id: "
|
|
19348
|
-
title: "Do not leave
|
|
19458
|
+
id: "controlled-field-without-default",
|
|
19459
|
+
title: "Do not leave a controlled field uninitialized",
|
|
19349
19460
|
outcome: "match",
|
|
19350
|
-
files: [{ path: "profile-form.tsx", source: "'use client'; import { useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm(
|
|
19461
|
+
files: [{ path: "profile-form.tsx", source: "'use client'; import { Controller, useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm(); return <Controller control={form.control} name='name' render={() => null} />; }" }],
|
|
19351
19462
|
focusPath: "profile-form.tsx",
|
|
19352
19463
|
expectedCount: 1,
|
|
19353
19464
|
public: true
|
|
19354
19465
|
}
|
|
19355
19466
|
]
|
|
19356
19467
|
};
|
|
19357
|
-
function
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
);
|
|
19468
|
+
function staticPropertyName(property) {
|
|
19469
|
+
if (property.computed) return null;
|
|
19470
|
+
if (property.key.type === AST_NODE_TYPES80.Identifier) return property.key.name;
|
|
19471
|
+
if (property.key.type === AST_NODE_TYPES80.Literal && typeof property.key.value === "string") return property.key.value;
|
|
19472
|
+
return null;
|
|
19473
|
+
}
|
|
19474
|
+
function unwrapExpression6(node) {
|
|
19475
|
+
if (node.type === AST_NODE_TYPES80.TSAsExpression || node.type === AST_NODE_TYPES80.TSSatisfiesExpression || node.type === AST_NODE_TYPES80.TSNonNullExpression || node.type === AST_NODE_TYPES80.TSTypeAssertion) return unwrapExpression6(node.expression);
|
|
19476
|
+
return node;
|
|
19361
19477
|
}
|
|
19362
19478
|
var require_use_form_default_values_default = createRule({
|
|
19363
19479
|
name: "require-use-form-default-values",
|
|
@@ -19366,27 +19482,127 @@ var require_use_form_default_values_default = createRule({
|
|
|
19366
19482
|
type: "problem",
|
|
19367
19483
|
docs: { description: REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION.summary },
|
|
19368
19484
|
schema: [],
|
|
19369
|
-
messages: {
|
|
19370
|
-
requireUseFormDefaultValues: "Provide defaultValues or reactive values to useForm so controlled fields have an explicit initial shape."
|
|
19371
|
-
}
|
|
19485
|
+
messages: { requireUseFormDefaultValues: "Add non-undefined defaultValues/values to the associated useForm call or non-undefined defaultValue to this field." }
|
|
19372
19486
|
},
|
|
19373
19487
|
defaultOptions: [],
|
|
19374
19488
|
create(context) {
|
|
19375
|
-
const
|
|
19489
|
+
const imported = /* @__PURE__ */ new Map();
|
|
19490
|
+
const uninitializedForms = /* @__PURE__ */ new Set();
|
|
19491
|
+
const uninitializedControls = /* @__PURE__ */ new Set();
|
|
19492
|
+
const bindingNamed = (node, name) => ASTUtils44.findVariable(context.sourceCode.getScope(node), name);
|
|
19493
|
+
const bindingOf = (node) => bindingNamed(node, node.name);
|
|
19494
|
+
const stable = (variable) => !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
|
|
19495
|
+
const importedKind = (node) => {
|
|
19496
|
+
const variable = bindingOf(node);
|
|
19497
|
+
return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
|
|
19498
|
+
};
|
|
19499
|
+
const importedJsxKind = (node) => {
|
|
19500
|
+
const variable = bindingNamed(node, node.name);
|
|
19501
|
+
return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
|
|
19502
|
+
};
|
|
19503
|
+
const isDefinitelyUndefined = (node) => {
|
|
19504
|
+
const value = unwrapExpression6(node);
|
|
19505
|
+
return value.type === AST_NODE_TYPES80.UnaryExpression && value.operator === "void" || value.type === AST_NODE_TYPES80.Identifier && value.name === "undefined" && (bindingOf(value)?.defs.length ?? 0) === 0;
|
|
19506
|
+
};
|
|
19507
|
+
const initializationState = (node) => {
|
|
19508
|
+
if (node === void 0) return "uninitialized";
|
|
19509
|
+
if (node.type !== AST_NODE_TYPES80.ObjectExpression) return "unknown";
|
|
19510
|
+
const initialization = /* @__PURE__ */ new Map();
|
|
19511
|
+
for (const property of node.properties) {
|
|
19512
|
+
if (property.type === AST_NODE_TYPES80.SpreadElement || property.computed) return "unknown";
|
|
19513
|
+
if (property.type !== AST_NODE_TYPES80.Property) continue;
|
|
19514
|
+
const name = staticPropertyName(property);
|
|
19515
|
+
if (name === "defaultValues" || name === "values") initialization.set(name, !isDefinitelyUndefined(property.value));
|
|
19516
|
+
}
|
|
19517
|
+
return [...initialization.values()].some(Boolean) ? "initialized" : "uninitialized";
|
|
19518
|
+
};
|
|
19519
|
+
const uninitializedUseFormCall = (node) => {
|
|
19520
|
+
if (node.type !== AST_NODE_TYPES80.CallExpression || node.callee.type !== AST_NODE_TYPES80.Identifier || importedKind(node.callee) !== "useForm") return false;
|
|
19521
|
+
const options = node.arguments[0];
|
|
19522
|
+
return options?.type !== AST_NODE_TYPES80.SpreadElement && initializationState(options) === "uninitialized";
|
|
19523
|
+
};
|
|
19524
|
+
const isUninitializedForm = (node) => {
|
|
19525
|
+
if (node.type !== AST_NODE_TYPES80.Identifier) return false;
|
|
19526
|
+
const variable = bindingOf(node);
|
|
19527
|
+
return variable !== null && stable(variable) && uninitializedForms.has(variable);
|
|
19528
|
+
};
|
|
19529
|
+
const isUninitializedControl = (node) => {
|
|
19530
|
+
if (node.type === AST_NODE_TYPES80.Identifier) {
|
|
19531
|
+
const variable = bindingOf(node);
|
|
19532
|
+
return variable !== null && stable(variable) && uninitializedControls.has(variable);
|
|
19533
|
+
}
|
|
19534
|
+
return node.type === AST_NODE_TYPES80.MemberExpression && !node.computed && node.property.type === AST_NODE_TYPES80.Identifier && node.property.name === "control" && isUninitializedForm(node.object);
|
|
19535
|
+
};
|
|
19536
|
+
const fieldOptionsNeedDefault = (node) => {
|
|
19537
|
+
if (node?.type !== AST_NODE_TYPES80.ObjectExpression) return false;
|
|
19538
|
+
let control = null;
|
|
19539
|
+
let hasDefault = false;
|
|
19540
|
+
for (const property of node.properties) {
|
|
19541
|
+
if (property.type === AST_NODE_TYPES80.SpreadElement || property.computed) return false;
|
|
19542
|
+
if (property.type !== AST_NODE_TYPES80.Property) continue;
|
|
19543
|
+
const name = staticPropertyName(property);
|
|
19544
|
+
if (name === "control") control = property.value;
|
|
19545
|
+
if (name === "defaultValue") hasDefault = !isDefinitelyUndefined(property.value);
|
|
19546
|
+
}
|
|
19547
|
+
return control !== null && isUninitializedControl(control) && !hasDefault;
|
|
19548
|
+
};
|
|
19549
|
+
const jsxAttribute = (node, name) => {
|
|
19550
|
+
let result = null;
|
|
19551
|
+
for (const attribute of node.attributes) {
|
|
19552
|
+
if (attribute.type === AST_NODE_TYPES80.JSXSpreadAttribute) return null;
|
|
19553
|
+
if (attribute.name.type === AST_NODE_TYPES80.JSXIdentifier && attribute.name.name === name) result = attribute;
|
|
19554
|
+
}
|
|
19555
|
+
return result;
|
|
19556
|
+
};
|
|
19557
|
+
const trackDestructuredControl = (pattern) => {
|
|
19558
|
+
for (const property of pattern.properties) {
|
|
19559
|
+
if (property.type !== AST_NODE_TYPES80.Property || staticPropertyName(property) !== "control" || property.value.type !== AST_NODE_TYPES80.Identifier) continue;
|
|
19560
|
+
const variable = bindingOf(property.value);
|
|
19561
|
+
if (variable !== null) uninitializedControls.add(variable);
|
|
19562
|
+
}
|
|
19563
|
+
};
|
|
19376
19564
|
return {
|
|
19377
19565
|
ImportDeclaration(node) {
|
|
19378
19566
|
if (node.source.value !== "react-hook-form") return;
|
|
19379
19567
|
for (const specifier of node.specifiers) {
|
|
19380
|
-
if (specifier.type !==
|
|
19381
|
-
const
|
|
19382
|
-
if (
|
|
19568
|
+
if (specifier.type !== AST_NODE_TYPES80.ImportSpecifier) continue;
|
|
19569
|
+
const name = specifier.imported.type === AST_NODE_TYPES80.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
19570
|
+
if (name !== "Controller" && name !== "useController" && name !== "useForm") continue;
|
|
19571
|
+
const variable = bindingOf(specifier.local);
|
|
19572
|
+
if (variable !== null) imported.set(variable, name);
|
|
19573
|
+
}
|
|
19574
|
+
},
|
|
19575
|
+
VariableDeclarator(node) {
|
|
19576
|
+
if (node.init === null) return;
|
|
19577
|
+
if (uninitializedUseFormCall(node.init)) {
|
|
19578
|
+
if (node.id.type === AST_NODE_TYPES80.Identifier) {
|
|
19579
|
+
const variable = bindingOf(node.id);
|
|
19580
|
+
if (variable !== null) uninitializedForms.add(variable);
|
|
19581
|
+
} else if (node.id.type === AST_NODE_TYPES80.ObjectPattern) {
|
|
19582
|
+
trackDestructuredControl(node.id);
|
|
19583
|
+
}
|
|
19584
|
+
return;
|
|
19585
|
+
}
|
|
19586
|
+
if (node.id.type === AST_NODE_TYPES80.ObjectPattern && isUninitializedForm(node.init)) {
|
|
19587
|
+
trackDestructuredControl(node.id);
|
|
19588
|
+
return;
|
|
19589
|
+
}
|
|
19590
|
+
if (node.id.type === AST_NODE_TYPES80.Identifier && node.init.type === AST_NODE_TYPES80.MemberExpression && !node.init.computed && node.init.property.type === AST_NODE_TYPES80.Identifier && node.init.property.name === "control" && isUninitializedForm(node.init.object)) {
|
|
19591
|
+
const variable = bindingOf(node.id);
|
|
19592
|
+
if (variable !== null) uninitializedControls.add(variable);
|
|
19383
19593
|
}
|
|
19384
19594
|
},
|
|
19385
19595
|
CallExpression(node) {
|
|
19386
|
-
if (node.callee.type !==
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19596
|
+
if (node.callee.type !== AST_NODE_TYPES80.Identifier || importedKind(node.callee) !== "useController" || !fieldOptionsNeedDefault(node.arguments[0]?.type === AST_NODE_TYPES80.SpreadElement ? void 0 : node.arguments[0])) return;
|
|
19597
|
+
context.report({ node, messageId: "requireUseFormDefaultValues" });
|
|
19598
|
+
},
|
|
19599
|
+
JSXOpeningElement(node) {
|
|
19600
|
+
if (node.name.type !== AST_NODE_TYPES80.JSXIdentifier || importedJsxKind(node.name) !== "Controller") return;
|
|
19601
|
+
if (node.attributes.some((attribute) => attribute.type === AST_NODE_TYPES80.JSXSpreadAttribute)) return;
|
|
19602
|
+
const control = jsxAttribute(node, "control");
|
|
19603
|
+
if (control?.value?.type !== AST_NODE_TYPES80.JSXExpressionContainer || control.value.expression.type === AST_NODE_TYPES80.JSXEmptyExpression || !isUninitializedControl(control.value.expression)) return;
|
|
19604
|
+
const defaultValue = jsxAttribute(node, "defaultValue");
|
|
19605
|
+
if (defaultValue !== null && (defaultValue.value === null || defaultValue.value.type !== AST_NODE_TYPES80.JSXExpressionContainer || defaultValue.value.expression.type !== AST_NODE_TYPES80.JSXEmptyExpression && !isDefinitelyUndefined(defaultValue.value.expression))) return;
|
|
19390
19606
|
context.report({ node, messageId: "requireUseFormDefaultValues" });
|
|
19391
19607
|
}
|
|
19392
19608
|
};
|
|
@@ -19462,7 +19678,7 @@ var require_use_server_in_actions_file_default = createRule({
|
|
|
19462
19678
|
|
|
19463
19679
|
// src/rules/require-zod-form-validation.ts
|
|
19464
19680
|
import {
|
|
19465
|
-
AST_NODE_TYPES as
|
|
19681
|
+
AST_NODE_TYPES as AST_NODE_TYPES81,
|
|
19466
19682
|
ASTUtils as ASTUtils45
|
|
19467
19683
|
} from "@typescript-eslint/utils";
|
|
19468
19684
|
var REQUIRE_ZOD_FORM_VALIDATION_DOCUMENTATION = {
|
|
@@ -19490,14 +19706,14 @@ var FORM_VALUE_METHODS = /* @__PURE__ */ new Set(["get", "getAll"]);
|
|
|
19490
19706
|
var zodReceiverRoot = (node) => {
|
|
19491
19707
|
let current = node;
|
|
19492
19708
|
while (true) {
|
|
19493
|
-
if (current.type ===
|
|
19709
|
+
if (current.type === AST_NODE_TYPES81.Identifier) {
|
|
19494
19710
|
return current;
|
|
19495
19711
|
}
|
|
19496
|
-
if (current.type ===
|
|
19712
|
+
if (current.type === AST_NODE_TYPES81.CallExpression) {
|
|
19497
19713
|
current = current.callee;
|
|
19498
19714
|
continue;
|
|
19499
19715
|
}
|
|
19500
|
-
if (current.type ===
|
|
19716
|
+
if (current.type === AST_NODE_TYPES81.MemberExpression) {
|
|
19501
19717
|
current = current.object;
|
|
19502
19718
|
continue;
|
|
19503
19719
|
}
|
|
@@ -19506,12 +19722,12 @@ var zodReceiverRoot = (node) => {
|
|
|
19506
19722
|
};
|
|
19507
19723
|
var isFormDataMethodCall = (node) => {
|
|
19508
19724
|
let current = node;
|
|
19509
|
-
if (current.type ===
|
|
19725
|
+
if (current.type === AST_NODE_TYPES81.AwaitExpression) {
|
|
19510
19726
|
current = current.argument;
|
|
19511
19727
|
}
|
|
19512
|
-
if (current.type !==
|
|
19728
|
+
if (current.type !== AST_NODE_TYPES81.CallExpression) return false;
|
|
19513
19729
|
const callee = current.callee;
|
|
19514
|
-
return callee.type ===
|
|
19730
|
+
return callee.type === AST_NODE_TYPES81.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES81.Identifier && callee.property.name === "formData";
|
|
19515
19731
|
};
|
|
19516
19732
|
var require_zod_form_validation_default = createRule({
|
|
19517
19733
|
name: "require-zod-form-validation",
|
|
@@ -19542,16 +19758,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
19542
19758
|
return false;
|
|
19543
19759
|
}
|
|
19544
19760
|
const definition = binding.defs[0];
|
|
19545
|
-
if (definition?.type !== "Variable" || definition.node.type !==
|
|
19761
|
+
if (definition?.type !== "Variable" || definition.node.type !== AST_NODE_TYPES81.VariableDeclarator) {
|
|
19546
19762
|
return false;
|
|
19547
19763
|
}
|
|
19548
19764
|
const init = definition.node.init;
|
|
19549
|
-
return init?.type ===
|
|
19765
|
+
return init?.type === AST_NODE_TYPES81.ObjectExpression || init?.type === AST_NODE_TYPES81.ArrayExpression || init?.type === AST_NODE_TYPES81.Literal || init?.type === AST_NODE_TYPES81.ArrowFunctionExpression || init?.type === AST_NODE_TYPES81.FunctionExpression;
|
|
19550
19766
|
};
|
|
19551
19767
|
const isZodParseCall = (node) => {
|
|
19552
|
-
if (node.type !==
|
|
19768
|
+
if (node.type !== AST_NODE_TYPES81.CallExpression) return false;
|
|
19553
19769
|
const callee = node.callee;
|
|
19554
|
-
if (callee.type !==
|
|
19770
|
+
if (callee.type !== AST_NODE_TYPES81.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES81.Identifier || !ZOD_PARSE_METHODS.has(callee.property.name)) {
|
|
19555
19771
|
return false;
|
|
19556
19772
|
}
|
|
19557
19773
|
const root = zodReceiverRoot(callee.object);
|
|
@@ -19560,14 +19776,14 @@ var require_zod_form_validation_default = createRule({
|
|
|
19560
19776
|
return binding !== null && zodBindings.has(binding) || (root.name === "z" || ZOD_SCHEMA_NAME_RE.test(root.name)) && !isProvablyNonZodLocal(root);
|
|
19561
19777
|
};
|
|
19562
19778
|
const isFormSourceIdentifier = (node) => {
|
|
19563
|
-
if (node.type !==
|
|
19779
|
+
if (node.type !== AST_NODE_TYPES81.Identifier) return false;
|
|
19564
19780
|
const conventionalName = /formdata/i.test(node.name);
|
|
19565
19781
|
let scope = context.sourceCode.getScope(node);
|
|
19566
19782
|
while (scope !== null) {
|
|
19567
19783
|
const variable = scope.set.get(node.name);
|
|
19568
19784
|
if (variable !== void 0 && variable.defs.length === 1) {
|
|
19569
19785
|
const def = variable.defs[0];
|
|
19570
|
-
if (def !== void 0 && def.type === "Variable" && def.node.type ===
|
|
19786
|
+
if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES81.VariableDeclarator && def.node.init !== null) {
|
|
19571
19787
|
return isFormDataMethodCall(def.node.init);
|
|
19572
19788
|
}
|
|
19573
19789
|
return def?.type === "Parameter" && conventionalName;
|
|
@@ -19578,8 +19794,8 @@ var require_zod_form_validation_default = createRule({
|
|
|
19578
19794
|
};
|
|
19579
19795
|
const isFormDataGetCall = (node) => {
|
|
19580
19796
|
const callee = node.callee;
|
|
19581
|
-
if (callee.type !==
|
|
19582
|
-
if (callee.property.type !==
|
|
19797
|
+
if (callee.type !== AST_NODE_TYPES81.MemberExpression) return false;
|
|
19798
|
+
if (callee.property.type !== AST_NODE_TYPES81.Identifier || !FORM_VALUE_METHODS.has(callee.property.name)) {
|
|
19583
19799
|
return false;
|
|
19584
19800
|
}
|
|
19585
19801
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -19588,15 +19804,15 @@ var require_zod_form_validation_default = createRule({
|
|
|
19588
19804
|
let parent = node.parent;
|
|
19589
19805
|
while (parent !== null && parent !== void 0) {
|
|
19590
19806
|
if (isZodParseCall(parent)) return parent;
|
|
19591
|
-
if (parent.type ===
|
|
19807
|
+
if (parent.type === AST_NODE_TYPES81.CallExpression && parent.callee.type === AST_NODE_TYPES81.Identifier && ["Number", "String", "Boolean"].includes(parent.callee.name) && parent.arguments.length === 1 && (resolvedBinding(parent.callee)?.defs.length ?? 0) === 0) {
|
|
19592
19808
|
parent = parent.parent;
|
|
19593
19809
|
continue;
|
|
19594
19810
|
}
|
|
19595
|
-
if (parent.type ===
|
|
19811
|
+
if (parent.type === AST_NODE_TYPES81.CallExpression && parent.callee.type === AST_NODE_TYPES81.MemberExpression && !parent.callee.computed && parent.callee.object.type === AST_NODE_TYPES81.Identifier && parent.callee.object.name === "Object" && parent.callee.property.type === AST_NODE_TYPES81.Identifier && parent.callee.property.name === "fromEntries" && (resolvedBinding(parent.callee.object)?.defs.length ?? 0) === 0) {
|
|
19596
19812
|
parent = parent.parent;
|
|
19597
19813
|
continue;
|
|
19598
19814
|
}
|
|
19599
|
-
if (parent.type ===
|
|
19815
|
+
if (parent.type === AST_NODE_TYPES81.CallExpression || parent.type === AST_NODE_TYPES81.NewExpression || parent.type === AST_NODE_TYPES81.TaggedTemplateExpression || parent.type === AST_NODE_TYPES81.ArrowFunctionExpression || parent.type === AST_NODE_TYPES81.FunctionExpression || parent.type === AST_NODE_TYPES81.FunctionDeclaration) return null;
|
|
19600
19816
|
parent = parent.parent;
|
|
19601
19817
|
}
|
|
19602
19818
|
return null;
|
|
@@ -19604,16 +19820,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
19604
19820
|
const hasZodParseAncestor = (node) => zodParseAncestor(node) !== null;
|
|
19605
19821
|
const isInstanceofNarrowing = (node) => {
|
|
19606
19822
|
const parent = node.parent;
|
|
19607
|
-
return parent !== null && parent !== void 0 && parent.type ===
|
|
19823
|
+
return parent !== null && parent !== void 0 && parent.type === AST_NODE_TYPES81.BinaryExpression && parent.operator === "instanceof" && parent.left === node && parent.right.type === AST_NODE_TYPES81.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
19608
19824
|
};
|
|
19609
19825
|
const boundDeclarator = (node) => {
|
|
19610
19826
|
let current = node;
|
|
19611
19827
|
let parent = current.parent;
|
|
19612
|
-
while ((parent.type ===
|
|
19828
|
+
while ((parent.type === AST_NODE_TYPES81.TSAsExpression || parent.type === AST_NODE_TYPES81.TSSatisfiesExpression || parent.type === AST_NODE_TYPES81.TSNonNullExpression || parent.type === AST_NODE_TYPES81.ChainExpression) && parent.expression === current) {
|
|
19613
19829
|
current = parent;
|
|
19614
19830
|
parent = current.parent;
|
|
19615
19831
|
}
|
|
19616
|
-
if (parent.type ===
|
|
19832
|
+
if (parent.type === AST_NODE_TYPES81.VariableDeclarator && parent.init === current && parent.id.type === AST_NODE_TYPES81.Identifier) {
|
|
19617
19833
|
return parent;
|
|
19618
19834
|
}
|
|
19619
19835
|
return null;
|
|
@@ -19622,7 +19838,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
19622
19838
|
let current = node;
|
|
19623
19839
|
while (current.parent !== void 0) {
|
|
19624
19840
|
const parent = current.parent;
|
|
19625
|
-
if (parent.type ===
|
|
19841
|
+
if (parent.type === AST_NODE_TYPES81.BlockStatement || parent.type === AST_NODE_TYPES81.Program) {
|
|
19626
19842
|
return current;
|
|
19627
19843
|
}
|
|
19628
19844
|
current = parent;
|
|
@@ -19631,12 +19847,12 @@ var require_zod_form_validation_default = createRule({
|
|
|
19631
19847
|
};
|
|
19632
19848
|
const zodParseMethod = (call) => {
|
|
19633
19849
|
const callee = call.callee;
|
|
19634
|
-
return callee.type ===
|
|
19850
|
+
return callee.type === AST_NODE_TYPES81.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES81.Identifier ? callee.property.name : null;
|
|
19635
19851
|
};
|
|
19636
19852
|
const hasConditionalAncestorBeforeStatement = (node, statement) => {
|
|
19637
19853
|
let current = node.parent;
|
|
19638
19854
|
while (current !== void 0 && current !== statement) {
|
|
19639
|
-
if (current.type ===
|
|
19855
|
+
if (current.type === AST_NODE_TYPES81.LogicalExpression || current.type === AST_NODE_TYPES81.ConditionalExpression) {
|
|
19640
19856
|
return true;
|
|
19641
19857
|
}
|
|
19642
19858
|
current = current.parent;
|
|
@@ -19646,7 +19862,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
19646
19862
|
const isAwaitedBeforeStatement = (node, statement) => {
|
|
19647
19863
|
let current = node.parent;
|
|
19648
19864
|
while (current !== void 0 && current !== statement) {
|
|
19649
|
-
if (current.type ===
|
|
19865
|
+
if (current.type === AST_NODE_TYPES81.AwaitExpression) return true;
|
|
19650
19866
|
current = current.parent;
|
|
19651
19867
|
}
|
|
19652
19868
|
return false;
|
|
@@ -19659,7 +19875,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
19659
19875
|
if (declarationStatement === null || validationStatement === null || declarationStatement.parent !== validationStatement.parent || validationStatement.range[0] <= declarationStatement.range[1] || hasConditionalAncestorBeforeStatement(parse2, validationStatement)) {
|
|
19660
19876
|
return null;
|
|
19661
19877
|
}
|
|
19662
|
-
if (validationStatement.type !==
|
|
19878
|
+
if (validationStatement.type !== AST_NODE_TYPES81.VariableDeclaration && validationStatement.type !== AST_NODE_TYPES81.ExpressionStatement) {
|
|
19663
19879
|
return null;
|
|
19664
19880
|
}
|
|
19665
19881
|
const method = zodParseMethod(parse2);
|
|
@@ -19671,16 +19887,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
19671
19887
|
};
|
|
19672
19888
|
const isSafePrevalidationInspection = (identifier) => {
|
|
19673
19889
|
const parent = identifier.parent;
|
|
19674
|
-
if (parent.type ===
|
|
19890
|
+
if (parent.type === AST_NODE_TYPES81.UnaryExpression && parent.operator === "typeof") {
|
|
19675
19891
|
return true;
|
|
19676
19892
|
}
|
|
19677
|
-
if (parent.type !==
|
|
19893
|
+
if (parent.type !== AST_NODE_TYPES81.BinaryExpression || parent.left !== identifier) {
|
|
19678
19894
|
return false;
|
|
19679
19895
|
}
|
|
19680
19896
|
if (parent.operator === "instanceof") {
|
|
19681
|
-
return parent.right.type ===
|
|
19897
|
+
return parent.right.type === AST_NODE_TYPES81.Identifier && (parent.right.name === "File" || parent.right.name === "Blob");
|
|
19682
19898
|
}
|
|
19683
|
-
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type ===
|
|
19899
|
+
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === AST_NODE_TYPES81.Literal && parent.right.value === null || parent.right.type === AST_NODE_TYPES81.Identifier && parent.right.name === "undefined");
|
|
19684
19900
|
};
|
|
19685
19901
|
const isDescendantOf = (node, ancestor) => {
|
|
19686
19902
|
let current = node;
|
|
@@ -19691,23 +19907,23 @@ var require_zod_form_validation_default = createRule({
|
|
|
19691
19907
|
return false;
|
|
19692
19908
|
};
|
|
19693
19909
|
const blockTerminates = (node) => {
|
|
19694
|
-
if (node.type ===
|
|
19910
|
+
if (node.type === AST_NODE_TYPES81.ReturnStatement || node.type === AST_NODE_TYPES81.ThrowStatement) {
|
|
19695
19911
|
return true;
|
|
19696
19912
|
}
|
|
19697
|
-
if (node.type !==
|
|
19913
|
+
if (node.type !== AST_NODE_TYPES81.BlockStatement || node.body.length === 0) return false;
|
|
19698
19914
|
const last = node.body.at(-1);
|
|
19699
19915
|
return last !== void 0 && blockTerminates(last);
|
|
19700
19916
|
};
|
|
19701
19917
|
const narrowingIf = (identifier) => {
|
|
19702
19918
|
const comparison = identifier.parent;
|
|
19703
|
-
if (comparison?.type !==
|
|
19919
|
+
if (comparison?.type !== AST_NODE_TYPES81.BinaryExpression || comparison.operator !== "instanceof" || comparison.left !== identifier || comparison.right.type !== AST_NODE_TYPES81.Identifier || comparison.right.name !== "File" && comparison.right.name !== "Blob") {
|
|
19704
19920
|
return null;
|
|
19705
19921
|
}
|
|
19706
19922
|
const maybeNegation = comparison.parent;
|
|
19707
|
-
const negated = maybeNegation?.type ===
|
|
19923
|
+
const negated = maybeNegation?.type === AST_NODE_TYPES81.UnaryExpression && maybeNegation.operator === "!";
|
|
19708
19924
|
const test = negated ? maybeNegation : comparison;
|
|
19709
19925
|
const branch = test.parent;
|
|
19710
|
-
return branch?.type ===
|
|
19926
|
+
return branch?.type === AST_NODE_TYPES81.IfStatement && branch.test === test ? { branch, positive: !negated } : null;
|
|
19711
19927
|
};
|
|
19712
19928
|
const useDominatedByNarrowing = (use, narrowings) => narrowings.some(({ branch, positive }) => {
|
|
19713
19929
|
if (positive) return isDescendantOf(use, branch.consequent);
|
|
@@ -19727,7 +19943,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
19727
19943
|
const variable = context.sourceCode.getDeclaredVariables(declarator)[0];
|
|
19728
19944
|
if (variable === void 0) return false;
|
|
19729
19945
|
const references = variable.references.filter((reference) => !reference.isWriteOnly()).map((reference) => reference.identifier).filter(
|
|
19730
|
-
(identifier) => identifier.type ===
|
|
19946
|
+
(identifier) => identifier.type === AST_NODE_TYPES81.Identifier
|
|
19731
19947
|
);
|
|
19732
19948
|
if (references.length === 0) return false;
|
|
19733
19949
|
const narrowings = references.map(narrowingIf).filter(
|
|
@@ -19753,7 +19969,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
19753
19969
|
ImportDeclaration(node) {
|
|
19754
19970
|
if (!isZodModule(node.source.value)) return;
|
|
19755
19971
|
for (const specifier of node.specifiers) {
|
|
19756
|
-
if (specifier.type ===
|
|
19972
|
+
if (specifier.type === AST_NODE_TYPES81.ImportNamespaceSpecifier || specifier.type === AST_NODE_TYPES81.ImportDefaultSpecifier || specifier.type === AST_NODE_TYPES81.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES81.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z")) {
|
|
19757
19973
|
const binding = resolvedBinding(specifier.local);
|
|
19758
19974
|
if (binding !== null) zodBindings.add(binding);
|
|
19759
19975
|
}
|
|
@@ -19843,7 +20059,7 @@ var store_insert_requires_on_conflict_default = createRule({
|
|
|
19843
20059
|
});
|
|
19844
20060
|
|
|
19845
20061
|
// src/rules/stepdown.ts
|
|
19846
|
-
import { AST_NODE_TYPES as
|
|
20062
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES82, ASTUtils as ASTUtils46 } from "@typescript-eslint/utils";
|
|
19847
20063
|
var STEPDOWN_DOCUMENTATION = {
|
|
19848
20064
|
summary: "Place a private helper below its sole direct same-scope caller.",
|
|
19849
20065
|
rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
|
|
@@ -19861,7 +20077,7 @@ var STEPDOWN_DOCUMENTATION = {
|
|
|
19861
20077
|
]
|
|
19862
20078
|
};
|
|
19863
20079
|
function isFunction(node) {
|
|
19864
|
-
return node.type ===
|
|
20080
|
+
return node.type === AST_NODE_TYPES82.ArrowFunctionExpression || node.type === AST_NODE_TYPES82.FunctionDeclaration || node.type === AST_NODE_TYPES82.FunctionExpression;
|
|
19865
20081
|
}
|
|
19866
20082
|
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
|
|
19867
20083
|
const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
|
|
@@ -19956,8 +20172,8 @@ function moduleScope(context, program) {
|
|
|
19956
20172
|
for (const node of declarations) counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
19957
20173
|
const overloadNames = new Set(
|
|
19958
20174
|
program.body.flatMap((statement) => {
|
|
19959
|
-
const node = statement.type ===
|
|
19960
|
-
return node?.type ===
|
|
20175
|
+
const node = statement.type === AST_NODE_TYPES82.ExportNamedDeclaration ? statement.declaration : statement;
|
|
20176
|
+
return node?.type === AST_NODE_TYPES82.TSDeclareFunction && node.id !== null ? [node.id.name] : [];
|
|
19961
20177
|
})
|
|
19962
20178
|
);
|
|
19963
20179
|
const exported = exportedNames(program);
|
|
@@ -19981,7 +20197,7 @@ function moduleScope(context, program) {
|
|
|
19981
20197
|
const nearestFunction2 = [...ancestors].reverse().find(isFunction);
|
|
19982
20198
|
const parent = identifier.parent;
|
|
19983
20199
|
const callerDefinition = nearestFunction2 === void 0 ? void 0 : byFunction.get(nearestFunction2);
|
|
19984
|
-
if (callerDefinition === void 0 || parent.type !==
|
|
20200
|
+
if (callerDefinition === void 0 || parent.type !== AST_NODE_TYPES82.CallExpression || parent.callee !== identifier) {
|
|
19985
20201
|
pinned.add(definition.name);
|
|
19986
20202
|
continue;
|
|
19987
20203
|
}
|
|
@@ -19996,38 +20212,38 @@ function moduleScope(context, program) {
|
|
|
19996
20212
|
function exportedNames(program) {
|
|
19997
20213
|
const names = /* @__PURE__ */ new Set();
|
|
19998
20214
|
for (const statement of program.body) {
|
|
19999
|
-
if (statement.type !==
|
|
20000
|
-
if (statement.declaration?.type ===
|
|
20215
|
+
if (statement.type !== AST_NODE_TYPES82.ExportNamedDeclaration || statement.exportKind === "type" || statement.source !== null) continue;
|
|
20216
|
+
if (statement.declaration?.type === AST_NODE_TYPES82.FunctionDeclaration && statement.declaration.id !== null) {
|
|
20001
20217
|
names.add(statement.declaration.id.name);
|
|
20002
20218
|
}
|
|
20003
|
-
if (statement.declaration?.type ===
|
|
20219
|
+
if (statement.declaration?.type === AST_NODE_TYPES82.VariableDeclaration) {
|
|
20004
20220
|
for (const declarator of statement.declaration.declarations) {
|
|
20005
|
-
if (declarator.id.type ===
|
|
20221
|
+
if (declarator.id.type === AST_NODE_TYPES82.Identifier) names.add(declarator.id.name);
|
|
20006
20222
|
}
|
|
20007
20223
|
}
|
|
20008
20224
|
for (const specifier of statement.specifiers) {
|
|
20009
|
-
if (specifier.exportKind !== "type" && specifier.local.type ===
|
|
20225
|
+
if (specifier.exportKind !== "type" && specifier.local.type === AST_NODE_TYPES82.Identifier) {
|
|
20010
20226
|
names.add(specifier.local.name);
|
|
20011
20227
|
}
|
|
20012
20228
|
}
|
|
20013
20229
|
}
|
|
20014
20230
|
for (const statement of program.body) {
|
|
20015
|
-
if (statement.type ===
|
|
20016
|
-
if (statement.type ===
|
|
20231
|
+
if (statement.type === AST_NODE_TYPES82.ExportDefaultDeclaration && statement.declaration.type === AST_NODE_TYPES82.Identifier) names.add(statement.declaration.name);
|
|
20232
|
+
if (statement.type === AST_NODE_TYPES82.ExportDefaultDeclaration && statement.declaration.type === AST_NODE_TYPES82.FunctionDeclaration && statement.declaration.id !== null) names.add(statement.declaration.id.name);
|
|
20017
20233
|
}
|
|
20018
20234
|
return names;
|
|
20019
20235
|
}
|
|
20020
20236
|
function moduleDefinitions(program) {
|
|
20021
20237
|
const definitions = [];
|
|
20022
20238
|
for (const statement of program.body) {
|
|
20023
|
-
const node = statement.type ===
|
|
20024
|
-
if (node?.type ===
|
|
20239
|
+
const node = statement.type === AST_NODE_TYPES82.ExportNamedDeclaration || statement.type === AST_NODE_TYPES82.ExportDefaultDeclaration ? statement.declaration : statement;
|
|
20240
|
+
if (node?.type === AST_NODE_TYPES82.FunctionDeclaration && node.id !== null && node.body !== null) {
|
|
20025
20241
|
definitions.push({ name: node.id.name, node, functionNode: node, bindingNode: node });
|
|
20026
20242
|
continue;
|
|
20027
20243
|
}
|
|
20028
|
-
if (node?.type !==
|
|
20244
|
+
if (node?.type !== AST_NODE_TYPES82.VariableDeclaration || node.kind !== "const") continue;
|
|
20029
20245
|
for (const declarator of node.declarations) {
|
|
20030
|
-
if (declarator.id.type ===
|
|
20246
|
+
if (declarator.id.type === AST_NODE_TYPES82.Identifier && declarator.init !== null && isFunction(declarator.init)) {
|
|
20031
20247
|
definitions.push({
|
|
20032
20248
|
name: declarator.id.name,
|
|
20033
20249
|
node: declarator,
|
|
@@ -20040,21 +20256,21 @@ function moduleDefinitions(program) {
|
|
|
20040
20256
|
return definitions;
|
|
20041
20257
|
}
|
|
20042
20258
|
function methodName(node) {
|
|
20043
|
-
if (node.key.type ===
|
|
20044
|
-
return !node.computed && node.key.type ===
|
|
20259
|
+
if (node.key.type === AST_NODE_TYPES82.PrivateIdentifier) return `#${node.key.name}`;
|
|
20260
|
+
return !node.computed && node.key.type === AST_NODE_TYPES82.Identifier ? node.key.name : null;
|
|
20045
20261
|
}
|
|
20046
20262
|
function referencedMethod(context, node, classVariables) {
|
|
20047
|
-
const objectVariable = node.object.type ===
|
|
20263
|
+
const objectVariable = node.object.type === AST_NODE_TYPES82.Identifier ? ASTUtils46.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
|
|
20048
20264
|
const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
|
|
20049
|
-
if (node.object.type !==
|
|
20050
|
-
if (node.property.type ===
|
|
20051
|
-
if (!node.computed && node.property.type ===
|
|
20052
|
-
return node.computed && node.property.type ===
|
|
20265
|
+
if (node.object.type !== AST_NODE_TYPES82.ThisExpression && !isClassReference) return null;
|
|
20266
|
+
if (node.property.type === AST_NODE_TYPES82.PrivateIdentifier) return `#${node.property.name}`;
|
|
20267
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES82.Identifier) return node.property.name;
|
|
20268
|
+
return node.computed && node.property.type === AST_NODE_TYPES82.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
20053
20269
|
}
|
|
20054
20270
|
function referencedPropertyName(node) {
|
|
20055
|
-
if (node.property.type ===
|
|
20056
|
-
if (!node.computed && node.property.type ===
|
|
20057
|
-
return node.computed && node.property.type ===
|
|
20271
|
+
if (node.property.type === AST_NODE_TYPES82.PrivateIdentifier) return `#${node.property.name}`;
|
|
20272
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES82.Identifier) return node.property.name;
|
|
20273
|
+
return node.computed && node.property.type === AST_NODE_TYPES82.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
20058
20274
|
}
|
|
20059
20275
|
function walk(node, visitorKeys, visit, nestedFunction = false) {
|
|
20060
20276
|
visit(node, nestedFunction);
|
|
@@ -20070,7 +20286,7 @@ function walk(node, visitorKeys, visit, nestedFunction = false) {
|
|
|
20070
20286
|
}
|
|
20071
20287
|
function classScope(context, node, computedReferenceNames) {
|
|
20072
20288
|
const methods = node.body.body.filter(
|
|
20073
|
-
(member) => member.type ===
|
|
20289
|
+
(member) => member.type === AST_NODE_TYPES82.MethodDefinition
|
|
20074
20290
|
);
|
|
20075
20291
|
const counts = /* @__PURE__ */ new Map();
|
|
20076
20292
|
for (const method of methods) {
|
|
@@ -20078,8 +20294,8 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20078
20294
|
if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
20079
20295
|
}
|
|
20080
20296
|
for (const member of node.body.body) {
|
|
20081
|
-
if (member.type !==
|
|
20082
|
-
const name = !member.computed && member.key.type ===
|
|
20297
|
+
if (member.type !== AST_NODE_TYPES82.TSAbstractMethodDefinition) continue;
|
|
20298
|
+
const name = !member.computed && member.key.type === AST_NODE_TYPES82.Identifier ? member.key.name : null;
|
|
20083
20299
|
if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
20084
20300
|
}
|
|
20085
20301
|
const scopeDefinitions = methods.flatMap((method) => {
|
|
@@ -20088,7 +20304,7 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20088
20304
|
});
|
|
20089
20305
|
const definitions = methods.flatMap((method) => {
|
|
20090
20306
|
const name = methodName(method);
|
|
20091
|
-
const isPrivate = method.accessibility === "private" || method.key.type ===
|
|
20307
|
+
const isPrivate = method.accessibility === "private" || method.key.type === AST_NODE_TYPES82.PrivateIdentifier;
|
|
20092
20308
|
return name !== null && isPrivate && counts.get(name) === 1 && method.decorators.length === 0 ? [{ name, node: method }] : [];
|
|
20093
20309
|
});
|
|
20094
20310
|
if (definitions.length === 0) return;
|
|
@@ -20100,7 +20316,7 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20100
20316
|
const internal = ASTUtils46.findVariable(context.sourceCode.getScope(node), node.id.name);
|
|
20101
20317
|
if (internal !== null) classVariables.add(internal);
|
|
20102
20318
|
}
|
|
20103
|
-
if (node.type ===
|
|
20319
|
+
if (node.type === AST_NODE_TYPES82.ClassExpression && node.parent.type === AST_NODE_TYPES82.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES82.Identifier) {
|
|
20104
20320
|
const outer = ASTUtils46.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
|
|
20105
20321
|
if (outer !== null) classVariables.add(outer);
|
|
20106
20322
|
}
|
|
@@ -20117,26 +20333,26 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20117
20333
|
}
|
|
20118
20334
|
const thisValue = (value) => {
|
|
20119
20335
|
let current = value;
|
|
20120
|
-
while (current?.type ===
|
|
20121
|
-
return current?.type ===
|
|
20336
|
+
while (current?.type === AST_NODE_TYPES82.TSAsExpression || current?.type === AST_NODE_TYPES82.TSSatisfiesExpression || current?.type === AST_NODE_TYPES82.TSNonNullExpression) current = current.expression;
|
|
20337
|
+
return current?.type === AST_NODE_TYPES82.ThisExpression;
|
|
20122
20338
|
};
|
|
20123
20339
|
const collectAlias = (current, nestedFunction) => {
|
|
20124
|
-
if (nestedFunction || current.type !==
|
|
20125
|
-
if (current.type ===
|
|
20126
|
-
const binding = current.type ===
|
|
20127
|
-
const value = current.type ===
|
|
20340
|
+
if (nestedFunction || current.type !== AST_NODE_TYPES82.VariableDeclarator && current.type !== AST_NODE_TYPES82.AssignmentPattern) return;
|
|
20341
|
+
if (current.type === AST_NODE_TYPES82.VariableDeclarator && (current.parent.type !== AST_NODE_TYPES82.VariableDeclaration || current.parent.kind !== "const")) return;
|
|
20342
|
+
const binding = current.type === AST_NODE_TYPES82.VariableDeclarator ? current.id : current.left;
|
|
20343
|
+
const value = current.type === AST_NODE_TYPES82.VariableDeclarator ? current.init : current.right;
|
|
20128
20344
|
if (!thisValue(value)) return;
|
|
20129
|
-
if (binding.type ===
|
|
20345
|
+
if (binding.type === AST_NODE_TYPES82.ObjectPattern) {
|
|
20130
20346
|
for (const property of binding.properties) {
|
|
20131
|
-
if (property.type ===
|
|
20347
|
+
if (property.type === AST_NODE_TYPES82.RestElement) {
|
|
20132
20348
|
for (const name of privateNames) pinned.add(name);
|
|
20133
|
-
} else if (property.key.type ===
|
|
20349
|
+
} else if (property.key.type === AST_NODE_TYPES82.Identifier && privateNames.has(property.key.name)) {
|
|
20134
20350
|
pinned.add(property.key.name);
|
|
20135
20351
|
}
|
|
20136
20352
|
}
|
|
20137
20353
|
return;
|
|
20138
20354
|
}
|
|
20139
|
-
if (binding.type !==
|
|
20355
|
+
if (binding.type !== AST_NODE_TYPES82.Identifier) return;
|
|
20140
20356
|
const variable = ASTUtils46.findVariable(context.sourceCode.getScope(binding), binding.name);
|
|
20141
20357
|
if (variable !== null) {
|
|
20142
20358
|
methodClassVariables.add(variable);
|
|
@@ -20150,16 +20366,16 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20150
20366
|
walk(statement, context.sourceCode.visitorKeys, collectAlias);
|
|
20151
20367
|
}
|
|
20152
20368
|
const visitCall = (current, nestedFunction) => {
|
|
20153
|
-
if (current.type ===
|
|
20369
|
+
if (current.type === AST_NODE_TYPES82.VariableDeclarator && current.id.type === AST_NODE_TYPES82.ObjectPattern && thisValue(current.init)) {
|
|
20154
20370
|
for (const property of current.id.properties) {
|
|
20155
|
-
if (property.type ===
|
|
20371
|
+
if (property.type === AST_NODE_TYPES82.RestElement) {
|
|
20156
20372
|
for (const name of privateNames) pinned.add(name);
|
|
20157
20373
|
continue;
|
|
20158
20374
|
}
|
|
20159
|
-
if (property.type ===
|
|
20375
|
+
if (property.type === AST_NODE_TYPES82.Property && property.key.type === AST_NODE_TYPES82.Identifier && privateNames.has(property.key.name)) pinned.add(property.key.name);
|
|
20160
20376
|
}
|
|
20161
20377
|
}
|
|
20162
|
-
if (current.type !==
|
|
20378
|
+
if (current.type !== AST_NODE_TYPES82.MemberExpression) return;
|
|
20163
20379
|
const target = referencedMethod(context, current, methodClassVariables);
|
|
20164
20380
|
if (target === null) {
|
|
20165
20381
|
const possibleTarget = referencedPropertyName(current);
|
|
@@ -20167,12 +20383,12 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20167
20383
|
return;
|
|
20168
20384
|
}
|
|
20169
20385
|
if (!privateNames.has(target)) return;
|
|
20170
|
-
const objectVariable = current.object.type ===
|
|
20386
|
+
const objectVariable = current.object.type === AST_NODE_TYPES82.Identifier ? ASTUtils46.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
|
|
20171
20387
|
if (objectVariable !== null && methodAliases.has(objectVariable)) {
|
|
20172
20388
|
pinned.add(target);
|
|
20173
20389
|
return;
|
|
20174
20390
|
}
|
|
20175
|
-
if (current.computed || nestedFunction || parameterDecoratorNodes.has(current) || current.parent.type !==
|
|
20391
|
+
if (current.computed || nestedFunction || parameterDecoratorNodes.has(current) || current.parent.type !== AST_NODE_TYPES82.CallExpression || current.parent.callee !== current) {
|
|
20176
20392
|
pinned.add(target);
|
|
20177
20393
|
return;
|
|
20178
20394
|
}
|
|
@@ -20192,9 +20408,9 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20192
20408
|
}
|
|
20193
20409
|
}
|
|
20194
20410
|
for (const member of node.body.body) {
|
|
20195
|
-
if (member.type ===
|
|
20411
|
+
if (member.type === AST_NODE_TYPES82.MethodDefinition || member.type === AST_NODE_TYPES82.TSAbstractMethodDefinition) continue;
|
|
20196
20412
|
walk(member, context.sourceCode.visitorKeys, (current) => {
|
|
20197
|
-
if (current.type !==
|
|
20413
|
+
if (current.type !== AST_NODE_TYPES82.MemberExpression) return;
|
|
20198
20414
|
const target = referencedMethod(context, current, classVariables);
|
|
20199
20415
|
const possibleTarget = target ?? referencedPropertyName(current);
|
|
20200
20416
|
if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
|
|
@@ -20216,12 +20432,12 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
20216
20432
|
}
|
|
20217
20433
|
function isClassRuntimeBarrier(member) {
|
|
20218
20434
|
switch (member.type) {
|
|
20219
|
-
case
|
|
20435
|
+
case AST_NODE_TYPES82.StaticBlock:
|
|
20220
20436
|
return true;
|
|
20221
|
-
case
|
|
20222
|
-
case
|
|
20437
|
+
case AST_NODE_TYPES82.PropertyDefinition:
|
|
20438
|
+
case AST_NODE_TYPES82.AccessorProperty:
|
|
20223
20439
|
return member.static || member.computed || member.decorators.length > 0 || member.value !== null;
|
|
20224
|
-
case
|
|
20440
|
+
case AST_NODE_TYPES82.MethodDefinition:
|
|
20225
20441
|
return member.computed || member.decorators.length > 0;
|
|
20226
20442
|
default:
|
|
20227
20443
|
return false;
|
|
@@ -20253,7 +20469,7 @@ var stepdown_default = createRule({
|
|
|
20253
20469
|
moduleScope(context, program);
|
|
20254
20470
|
const computedReferenceNames = /* @__PURE__ */ new Set();
|
|
20255
20471
|
walk(program, context.sourceCode.visitorKeys, (node) => {
|
|
20256
|
-
if (node.type ===
|
|
20472
|
+
if (node.type === AST_NODE_TYPES82.MemberExpression && node.computed && node.property.type === AST_NODE_TYPES82.Literal && typeof node.property.value === "string") computedReferenceNames.add(node.property.value);
|
|
20257
20473
|
});
|
|
20258
20474
|
for (const node of classes) classScope(context, node, computedReferenceNames);
|
|
20259
20475
|
}
|
|
@@ -20262,8 +20478,8 @@ var stepdown_default = createRule({
|
|
|
20262
20478
|
});
|
|
20263
20479
|
|
|
20264
20480
|
// src/rules/source-coupled-test.ts
|
|
20265
|
-
import { AST_NODE_TYPES as
|
|
20266
|
-
var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|py|[cm]?[jt]s)$/iu;
|
|
20481
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES83, ASTUtils as ASTUtils47 } from "@typescript-eslint/utils";
|
|
20482
|
+
var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc?|toml|py|[cm]?[jt]s)$/iu;
|
|
20267
20483
|
var FS_MODULES = /* @__PURE__ */ new Set(["fs", "node:fs", "fs/promises", "node:fs/promises"]);
|
|
20268
20484
|
var FS_READERS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
|
|
20269
20485
|
var TEXT_TRANSFORMS = /* @__PURE__ */ new Set([
|
|
@@ -20300,6 +20516,8 @@ var EXPECT_MATCHERS = /* @__PURE__ */ new Set([
|
|
|
20300
20516
|
]);
|
|
20301
20517
|
var EXPECT_MODIFIERS2 = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
|
|
20302
20518
|
var ASSERT_MATCHERS = /* @__PURE__ */ new Set(["deepEqual", "doesNotMatch", "equal", "match", "notDeepEqual", "notEqual", "notStrictEqual", "ok", "strictEqual"]);
|
|
20519
|
+
var EXPECT_MODULES = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "vitest"]);
|
|
20520
|
+
var ASSERT_MODULES = /* @__PURE__ */ new Set(["assert", "assert/strict", "node:assert", "node:assert/strict"]);
|
|
20303
20521
|
var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
20304
20522
|
summary: "Disallow raw repository source text as a test oracle; parse or execute the artifact instead.",
|
|
20305
20523
|
rationale: "Substring and regex checks can pass on comments or unreachable configuration and fail after behavior-preserving formatting changes.",
|
|
@@ -20307,6 +20525,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
20307
20525
|
category: "testing",
|
|
20308
20526
|
limitations: [
|
|
20309
20527
|
"The rule follows stable lexical bindings, static source paths, awaited reads, and common text operations. Reassigned bindings, dynamic paths, unknown path wrappers, iterator pipelines, and interprocedural flows remain unreported.",
|
|
20528
|
+
"Assertion roots are scope-resolved for supported test runners and Node assert imports; local or unknown helpers with assertion-like names are not inferred.",
|
|
20310
20529
|
"When raw representation is genuinely the contract (for example a golden or compatibility sentinel), use an exact line suppression with the reason."
|
|
20311
20530
|
],
|
|
20312
20531
|
examples: [
|
|
@@ -20330,22 +20549,22 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
20330
20549
|
}
|
|
20331
20550
|
]
|
|
20332
20551
|
};
|
|
20333
|
-
function
|
|
20334
|
-
if (!node.computed && node.property.type ===
|
|
20335
|
-
if (node.computed && node.property.type ===
|
|
20552
|
+
function staticMemberName8(node) {
|
|
20553
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES83.Identifier) return node.property.name;
|
|
20554
|
+
if (node.computed && node.property.type === AST_NODE_TYPES83.Literal && typeof node.property.value === "string") return node.property.value;
|
|
20336
20555
|
return null;
|
|
20337
20556
|
}
|
|
20338
20557
|
function unwrap7(node) {
|
|
20339
|
-
if (node.type ===
|
|
20340
|
-
if (node.type ===
|
|
20341
|
-
if (node.type ===
|
|
20558
|
+
if (node.type === AST_NODE_TYPES83.AwaitExpression) return unwrap7(node.argument);
|
|
20559
|
+
if (node.type === AST_NODE_TYPES83.ChainExpression) return unwrap7(node.expression);
|
|
20560
|
+
if (node.type === AST_NODE_TYPES83.TSAsExpression || node.type === AST_NODE_TYPES83.TSNonNullExpression || node.type === AST_NODE_TYPES83.TSTypeAssertion) return unwrap7(node.expression);
|
|
20342
20561
|
return node;
|
|
20343
20562
|
}
|
|
20344
20563
|
function stringValue(node) {
|
|
20345
20564
|
const current = unwrap7(node);
|
|
20346
|
-
if (current.type ===
|
|
20347
|
-
if (current.type ===
|
|
20348
|
-
if (current.type ===
|
|
20565
|
+
if (current.type === AST_NODE_TYPES83.Literal && typeof current.value === "string") return current.value;
|
|
20566
|
+
if (current.type === AST_NODE_TYPES83.TemplateLiteral && current.expressions.length === 0) return current.quasis[0]?.value.cooked ?? null;
|
|
20567
|
+
if (current.type === AST_NODE_TYPES83.BinaryExpression && current.operator === "+") {
|
|
20349
20568
|
const left = stringValue(current.left);
|
|
20350
20569
|
const right = stringValue(current.right);
|
|
20351
20570
|
return left === null || right === null ? null : left + right;
|
|
@@ -20357,7 +20576,7 @@ function importSource(node) {
|
|
|
20357
20576
|
}
|
|
20358
20577
|
function requireSource(node) {
|
|
20359
20578
|
const current = unwrap7(node);
|
|
20360
|
-
if (current.type !==
|
|
20579
|
+
if (current.type !== AST_NODE_TYPES83.CallExpression || current.callee.type !== AST_NODE_TYPES83.Identifier || current.callee.name !== "require" || current.arguments.length !== 1 || current.arguments[0]?.type === AST_NODE_TYPES83.SpreadElement) return null;
|
|
20361
20580
|
return stringValue(current.arguments[0]);
|
|
20362
20581
|
}
|
|
20363
20582
|
function newScope() {
|
|
@@ -20380,6 +20599,27 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20380
20599
|
const reportedOrigins = /* @__PURE__ */ new Set();
|
|
20381
20600
|
const currentScope = () => scopes.at(-1) ?? scopes[0];
|
|
20382
20601
|
const bindingOf = (node) => ASTUtils47.findVariable(context.sourceCode.getScope(node), node.name);
|
|
20602
|
+
const assertionKind = (node) => {
|
|
20603
|
+
const binding = bindingOf(node);
|
|
20604
|
+
if (binding === null || binding.defs.length === 0) {
|
|
20605
|
+
return node.name === "assert" || node.name === "expect" ? node.name : null;
|
|
20606
|
+
}
|
|
20607
|
+
for (const definition of binding.defs) {
|
|
20608
|
+
const specifier = definition.node;
|
|
20609
|
+
if (specifier.type !== AST_NODE_TYPES83.ImportSpecifier && specifier.type !== AST_NODE_TYPES83.ImportDefaultSpecifier && specifier.type !== AST_NODE_TYPES83.ImportNamespaceSpecifier || specifier.parent.type !== AST_NODE_TYPES83.ImportDeclaration) continue;
|
|
20610
|
+
const source = importSource(specifier.parent);
|
|
20611
|
+
if (source !== null && ASSERT_MODULES.has(source)) {
|
|
20612
|
+
if (specifier.type !== AST_NODE_TYPES83.ImportSpecifier) return "assert";
|
|
20613
|
+
const imported2 = specifier.imported.type === AST_NODE_TYPES83.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
20614
|
+
if (imported2 === "strict" || ASSERT_MATCHERS.has(imported2)) return "assert";
|
|
20615
|
+
continue;
|
|
20616
|
+
}
|
|
20617
|
+
if (source === null || !EXPECT_MODULES.has(source) || specifier.type !== AST_NODE_TYPES83.ImportSpecifier) continue;
|
|
20618
|
+
const imported = specifier.imported.type === AST_NODE_TYPES83.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
20619
|
+
if (imported === "assert" || imported === "expect") return imported;
|
|
20620
|
+
}
|
|
20621
|
+
return null;
|
|
20622
|
+
};
|
|
20383
20623
|
const visible = (kind, node) => {
|
|
20384
20624
|
const name2 = bindingOf(node);
|
|
20385
20625
|
if (name2 === null || name2.references.some((reference) => reference.isWrite() && reference.init !== true)) return false;
|
|
@@ -20402,70 +20642,70 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20402
20642
|
const current = unwrap7(node);
|
|
20403
20643
|
const value = stringValue(current);
|
|
20404
20644
|
if (value !== null) return sourceSuffixRe.test(value);
|
|
20405
|
-
if (current.type ===
|
|
20406
|
-
if (current.type ===
|
|
20645
|
+
if (current.type === AST_NODE_TYPES83.Identifier) return visible("paths", current);
|
|
20646
|
+
if (current.type === AST_NODE_TYPES83.CallExpression || current.type === AST_NODE_TYPES83.NewExpression) {
|
|
20407
20647
|
const callee = current.callee;
|
|
20408
20648
|
const first = current.arguments[0];
|
|
20409
|
-
if (first === void 0 || first.type ===
|
|
20410
|
-
if (current.type ===
|
|
20411
|
-
if (callee.type ===
|
|
20649
|
+
if (first === void 0 || first.type === AST_NODE_TYPES83.SpreadElement) return false;
|
|
20650
|
+
if (current.type === AST_NODE_TYPES83.NewExpression && callee.type === AST_NODE_TYPES83.Identifier && callee.name === "URL" && (bindingOf(callee)?.defs.length ?? 0) === 0) return sourcePath(first);
|
|
20651
|
+
if (callee.type === AST_NODE_TYPES83.Identifier && bindingOf(callee)?.defs.some((definition) => definition.node.type === AST_NODE_TYPES83.ImportSpecifier && definition.node.imported.type === AST_NODE_TYPES83.Identifier && definition.node.imported.name === "fileURLToPath" && definition.node.parent.type === AST_NODE_TYPES83.ImportDeclaration && ["node:url", "url"].includes(String(definition.node.parent.source.value)))) return sourcePath(first);
|
|
20412
20652
|
}
|
|
20413
20653
|
return false;
|
|
20414
20654
|
};
|
|
20415
20655
|
const rawRead = (node) => {
|
|
20416
20656
|
const current = unwrap7(node);
|
|
20417
|
-
if (current.type !==
|
|
20657
|
+
if (current.type !== AST_NODE_TYPES83.CallExpression || current.arguments.length === 0) return false;
|
|
20418
20658
|
const callee = unwrap7(current.callee);
|
|
20419
|
-
if (callee.type ===
|
|
20659
|
+
if (callee.type === AST_NODE_TYPES83.Identifier) {
|
|
20420
20660
|
return visible("fsReaders", callee) && sourcePath(current.arguments[0]);
|
|
20421
20661
|
}
|
|
20422
|
-
if (callee.type !==
|
|
20423
|
-
const name2 =
|
|
20662
|
+
if (callee.type !== AST_NODE_TYPES83.MemberExpression) return false;
|
|
20663
|
+
const name2 = staticMemberName8(callee);
|
|
20424
20664
|
const object = unwrap7(callee.object);
|
|
20425
|
-
return name2 !== null && FS_READERS.has(name2) && object.type ===
|
|
20665
|
+
return name2 !== null && FS_READERS.has(name2) && object.type === AST_NODE_TYPES83.Identifier && visible("fsObjects", object) && sourcePath(current.arguments[0]);
|
|
20426
20666
|
};
|
|
20427
20667
|
const rawOrigins = (node) => {
|
|
20428
20668
|
const current = unwrap7(node);
|
|
20429
|
-
if (current.type ===
|
|
20669
|
+
if (current.type === AST_NODE_TYPES83.Identifier) return visibleRawOrigins(current);
|
|
20430
20670
|
if (rawRead(current)) return /* @__PURE__ */ new Set([`${current.range[0]}:${current.range[1]}`]);
|
|
20431
|
-
if (current.type ===
|
|
20432
|
-
if (current.type ===
|
|
20433
|
-
if (current.type !==
|
|
20671
|
+
if (current.type === AST_NODE_TYPES83.BinaryExpression && current.operator === "+") return /* @__PURE__ */ new Set([...rawOrigins(current.left), ...rawOrigins(current.right)]);
|
|
20672
|
+
if (current.type === AST_NODE_TYPES83.MemberExpression && staticMemberName8(current) === "length") return rawOrigins(current.object);
|
|
20673
|
+
if (current.type !== AST_NODE_TYPES83.CallExpression) return /* @__PURE__ */ new Set();
|
|
20434
20674
|
const callee = unwrap7(current.callee);
|
|
20435
|
-
if (callee.type !==
|
|
20436
|
-
const name2 =
|
|
20675
|
+
if (callee.type !== AST_NODE_TYPES83.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20676
|
+
const name2 = staticMemberName8(callee);
|
|
20437
20677
|
return name2 !== null && TEXT_TRANSFORMS.has(name2) ? rawOrigins(callee.object) : /* @__PURE__ */ new Set();
|
|
20438
20678
|
};
|
|
20439
20679
|
const evidenceOrigins = (node) => {
|
|
20440
20680
|
const current = unwrap7(node);
|
|
20441
20681
|
const direct = rawOrigins(current);
|
|
20442
20682
|
if (direct.size > 0) return direct;
|
|
20443
|
-
if (current.type ===
|
|
20444
|
-
if (current.type ===
|
|
20445
|
-
if (current.type !==
|
|
20683
|
+
if (current.type === AST_NODE_TYPES83.BinaryExpression || current.type === AST_NODE_TYPES83.LogicalExpression) return /* @__PURE__ */ new Set([...evidenceOrigins(current.left), ...evidenceOrigins(current.right)]);
|
|
20684
|
+
if (current.type === AST_NODE_TYPES83.UnaryExpression) return evidenceOrigins(current.argument);
|
|
20685
|
+
if (current.type !== AST_NODE_TYPES83.CallExpression) return /* @__PURE__ */ new Set();
|
|
20446
20686
|
const callee = unwrap7(current.callee);
|
|
20447
|
-
if (callee.type !==
|
|
20448
|
-
const name2 =
|
|
20687
|
+
if (callee.type !== AST_NODE_TYPES83.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20688
|
+
const name2 = staticMemberName8(callee);
|
|
20449
20689
|
if (name2 !== null && TEXT_PREDICATES.has(name2)) return rawOrigins(callee.object);
|
|
20450
|
-
if (name2 !== null && REGEXP_PREDICATES.has(name2)) return new Set(current.arguments.flatMap((argument) => argument.type ===
|
|
20690
|
+
if (name2 !== null && REGEXP_PREDICATES.has(name2)) return new Set(current.arguments.flatMap((argument) => argument.type === AST_NODE_TYPES83.SpreadElement ? [] : [...rawOrigins(argument)]));
|
|
20451
20691
|
return /* @__PURE__ */ new Set();
|
|
20452
20692
|
};
|
|
20453
20693
|
const rawAssertionOrigins = (node) => {
|
|
20454
20694
|
const callee = unwrap7(node.callee);
|
|
20455
|
-
if (callee.type ===
|
|
20456
|
-
return new Set(node.arguments.flatMap((argument) => argument.type ===
|
|
20695
|
+
if (callee.type === AST_NODE_TYPES83.Identifier && assertionKind(callee) === "assert") {
|
|
20696
|
+
return new Set(node.arguments.flatMap((argument) => argument.type === AST_NODE_TYPES83.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20457
20697
|
}
|
|
20458
|
-
if (callee.type !==
|
|
20459
|
-
const matcher =
|
|
20698
|
+
if (callee.type !== AST_NODE_TYPES83.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20699
|
+
const matcher = staticMemberName8(callee);
|
|
20460
20700
|
if (matcher === null) return /* @__PURE__ */ new Set();
|
|
20461
20701
|
let receiver = unwrap7(callee.object);
|
|
20462
|
-
while (receiver.type ===
|
|
20463
|
-
if (receiver.type ===
|
|
20702
|
+
while (receiver.type === AST_NODE_TYPES83.MemberExpression && EXPECT_MODIFIERS2.has(staticMemberName8(receiver) ?? "")) receiver = unwrap7(receiver.object);
|
|
20703
|
+
if (receiver.type === AST_NODE_TYPES83.CallExpression && receiver.callee.type === AST_NODE_TYPES83.Identifier && assertionKind(receiver.callee) === "expect") {
|
|
20464
20704
|
if (!EXPECT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
|
|
20465
|
-
return new Set([...receiver.arguments, ...node.arguments].flatMap((argument) => argument.type ===
|
|
20705
|
+
return new Set([...receiver.arguments, ...node.arguments].flatMap((argument) => argument.type === AST_NODE_TYPES83.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20466
20706
|
}
|
|
20467
|
-
if (receiver.type !==
|
|
20468
|
-
return new Set(node.arguments.flatMap((argument) => argument.type ===
|
|
20707
|
+
if (receiver.type !== AST_NODE_TYPES83.Identifier || assertionKind(receiver) !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
|
|
20708
|
+
return new Set(node.arguments.flatMap((argument) => argument.type === AST_NODE_TYPES83.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20469
20709
|
};
|
|
20470
20710
|
const declare = (node, state) => {
|
|
20471
20711
|
const name2 = bindingOf(node);
|
|
@@ -20487,7 +20727,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20487
20727
|
};
|
|
20488
20728
|
const sourceCollection = (node) => {
|
|
20489
20729
|
const current = unwrap7(node);
|
|
20490
|
-
return current.type ===
|
|
20730
|
+
return current.type === AST_NODE_TYPES83.ArrayExpression && current.elements.length > 0 && current.elements.every((element) => element !== null && element.type !== AST_NODE_TYPES83.SpreadElement && sourcePath(element));
|
|
20491
20731
|
};
|
|
20492
20732
|
const enterFunction = () => {
|
|
20493
20733
|
scopes.push(newScope());
|
|
@@ -20500,8 +20740,8 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20500
20740
|
const source = importSource(node);
|
|
20501
20741
|
if (source === null || !FS_MODULES.has(source)) return;
|
|
20502
20742
|
for (const specifier of node.specifiers) {
|
|
20503
|
-
if (specifier.type ===
|
|
20504
|
-
const imported = specifier.imported.type ===
|
|
20743
|
+
if (specifier.type === AST_NODE_TYPES83.ImportSpecifier) {
|
|
20744
|
+
const imported = specifier.imported.type === AST_NODE_TYPES83.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
20505
20745
|
if (FS_READERS.has(imported)) declare(specifier.local, { fsReader: true });
|
|
20506
20746
|
} else {
|
|
20507
20747
|
declare(specifier.local, { fsObject: true });
|
|
@@ -20514,30 +20754,30 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20514
20754
|
if (node.init === null) return;
|
|
20515
20755
|
const required = requireSource(node.init);
|
|
20516
20756
|
const initializer = unwrap7(node.init);
|
|
20517
|
-
if (required !== null && initializer.type ===
|
|
20518
|
-
if (required !== null && FS_MODULES.has(required) && node.id.type ===
|
|
20757
|
+
if (required !== null && initializer.type === AST_NODE_TYPES83.CallExpression && initializer.callee.type === AST_NODE_TYPES83.Identifier && (bindingOf(initializer.callee)?.defs.length ?? 0) > 0) return;
|
|
20758
|
+
if (required !== null && FS_MODULES.has(required) && node.id.type === AST_NODE_TYPES83.Identifier) {
|
|
20519
20759
|
declare(node.id, { fsObject: true });
|
|
20520
20760
|
return;
|
|
20521
20761
|
}
|
|
20522
|
-
if (node.id.type ===
|
|
20762
|
+
if (node.id.type === AST_NODE_TYPES83.ObjectPattern && required !== null && FS_MODULES.has(required)) {
|
|
20523
20763
|
for (const property of node.id.properties) {
|
|
20524
|
-
if (property.type !==
|
|
20525
|
-
const key = property.key.type ===
|
|
20764
|
+
if (property.type !== AST_NODE_TYPES83.Property || property.value.type !== AST_NODE_TYPES83.Identifier) continue;
|
|
20765
|
+
const key = property.key.type === AST_NODE_TYPES83.Identifier ? property.key.name : property.key.type === AST_NODE_TYPES83.Literal ? String(property.key.value) : "";
|
|
20526
20766
|
if (FS_READERS.has(key)) declare(property.value, { fsReader: true });
|
|
20527
20767
|
}
|
|
20528
20768
|
return;
|
|
20529
20769
|
}
|
|
20530
|
-
if (node.id.type !==
|
|
20770
|
+
if (node.id.type !== AST_NODE_TYPES83.Identifier) return;
|
|
20531
20771
|
declare(node.id, { collection: sourceCollection(node.init), path: sourcePath(node.init), rawOrigins: rawOrigins(node.init) });
|
|
20532
20772
|
},
|
|
20533
20773
|
AssignmentExpression(node) {
|
|
20534
|
-
if (node.left.type ===
|
|
20774
|
+
if (node.left.type === AST_NODE_TYPES83.Identifier) declare(node.left, {});
|
|
20535
20775
|
},
|
|
20536
20776
|
ForOfStatement(node) {
|
|
20537
20777
|
const right = unwrap7(node.right);
|
|
20538
|
-
const collection = right.type ===
|
|
20539
|
-
const left = node.left.type ===
|
|
20540
|
-
if (collection && left?.type ===
|
|
20778
|
+
const collection = right.type === AST_NODE_TYPES83.Identifier && visible("collections", right);
|
|
20779
|
+
const left = node.left.type === AST_NODE_TYPES83.VariableDeclaration ? node.left.declarations[0]?.id : node.left;
|
|
20780
|
+
if (collection && left?.type === AST_NODE_TYPES83.Identifier) declare(left, { path: true });
|
|
20541
20781
|
},
|
|
20542
20782
|
CallExpression(node) {
|
|
20543
20783
|
const origins = rawAssertionOrigins(node);
|
|
@@ -20556,15 +20796,15 @@ var source_coupled_test_default = createSourceCoupledRule(
|
|
|
20556
20796
|
);
|
|
20557
20797
|
|
|
20558
20798
|
// src/rules/sole-export-matches-filename.ts
|
|
20559
|
-
import { AST_NODE_TYPES as
|
|
20799
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES84 } from "@typescript-eslint/utils";
|
|
20560
20800
|
var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
20561
|
-
summary: "
|
|
20801
|
+
summary: "Make a module filename reflect its sole named public runtime export.",
|
|
20562
20802
|
rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
|
|
20563
|
-
remediation: "
|
|
20803
|
+
remediation: "Name the module for the exported responsibility, using either the full export name or a clear leading or trailing domain phrase; otherwise colocate genuinely related exports.",
|
|
20564
20804
|
category: "maintainability",
|
|
20565
20805
|
limitations: [
|
|
20566
20806
|
"Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, and re-exports are excluded.",
|
|
20567
|
-
"The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker.",
|
|
20807
|
+
"The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker. A leading or trailing export-name phrase is accepted only at token boundaries; multi-token stems also tolerate established acronym spelling such as github versus GitHub.",
|
|
20568
20808
|
"Exported destructuring patterns are excluded rather than undercounted as public exports."
|
|
20569
20809
|
],
|
|
20570
20810
|
examples: [
|
|
@@ -20590,6 +20830,23 @@ var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
|
|
|
20590
20830
|
"util",
|
|
20591
20831
|
"utils"
|
|
20592
20832
|
]);
|
|
20833
|
+
var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
|
|
20834
|
+
"adapter",
|
|
20835
|
+
"client",
|
|
20836
|
+
"config",
|
|
20837
|
+
"controller",
|
|
20838
|
+
"factory",
|
|
20839
|
+
"handler",
|
|
20840
|
+
"manager",
|
|
20841
|
+
"provider",
|
|
20842
|
+
"record",
|
|
20843
|
+
"repository",
|
|
20844
|
+
"router",
|
|
20845
|
+
"schema",
|
|
20846
|
+
"service",
|
|
20847
|
+
"store",
|
|
20848
|
+
"worker"
|
|
20849
|
+
]);
|
|
20593
20850
|
function stem3(filename) {
|
|
20594
20851
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
20595
20852
|
return base.replace(/\.[cm]?[jt]sx?$/u, "").split(".")[0] ?? "";
|
|
@@ -20597,13 +20854,32 @@ function stem3(filename) {
|
|
|
20597
20854
|
function kebabCase(name) {
|
|
20598
20855
|
return name.replace(/([A-Z]{2,})([A-Z][a-z])/gu, "$1-$2").replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[^a-z0-9]+/giu, "-").replaceAll(/^-+|-+$/gu, "").toLowerCase();
|
|
20599
20856
|
}
|
|
20857
|
+
function reflectsExportName(fileStem, exportedStem) {
|
|
20858
|
+
if (fileStem.startsWith("_")) return false;
|
|
20859
|
+
const visibleFileStem = fileStem.toLowerCase();
|
|
20860
|
+
const fileTokens = visibleFileStem.split("-").filter(Boolean);
|
|
20861
|
+
const exportTokens = exportedStem.split("-").filter(Boolean);
|
|
20862
|
+
if (fileTokens.length === 0 || exportTokens.length === 0) return false;
|
|
20863
|
+
if (fileTokens.length === 1) {
|
|
20864
|
+
const [token] = fileTokens;
|
|
20865
|
+
return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
|
|
20866
|
+
}
|
|
20867
|
+
const compactFile = fileTokens.join("");
|
|
20868
|
+
if (compactFile.length < 6) return false;
|
|
20869
|
+
const boundaryPhrases = /* @__PURE__ */ new Set();
|
|
20870
|
+
for (let index = 1; index <= exportTokens.length; index += 1) {
|
|
20871
|
+
boundaryPhrases.add(exportTokens.slice(0, index).join(""));
|
|
20872
|
+
boundaryPhrases.add(exportTokens.slice(-index).join(""));
|
|
20873
|
+
}
|
|
20874
|
+
return boundaryPhrases.has(compactFile);
|
|
20875
|
+
}
|
|
20600
20876
|
function declarationExport(statement) {
|
|
20601
20877
|
const declaration = statement.declaration;
|
|
20602
20878
|
if (declaration === null || declaration.declare === true) return [];
|
|
20603
|
-
if (declaration.type ===
|
|
20604
|
-
if (declaration.type !==
|
|
20879
|
+
if (declaration.type === AST_NODE_TYPES84.ClassDeclaration || declaration.type === AST_NODE_TYPES84.FunctionDeclaration || declaration.type === AST_NODE_TYPES84.TSEnumDeclaration) return declaration.id === null ? [] : [{ name: declaration.id.name, node: declaration }];
|
|
20880
|
+
if (declaration.type !== AST_NODE_TYPES84.VariableDeclaration) return [];
|
|
20605
20881
|
return declaration.declarations.flatMap(
|
|
20606
|
-
(item) => item.id.type ===
|
|
20882
|
+
(item) => item.id.type === AST_NODE_TYPES84.Identifier ? [{ name: item.id.name, node: item }] : []
|
|
20607
20883
|
);
|
|
20608
20884
|
}
|
|
20609
20885
|
var sole_export_matches_filename_default = createRule({
|
|
@@ -20611,7 +20887,7 @@ var sole_export_matches_filename_default = createRule({
|
|
|
20611
20887
|
documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
|
|
20612
20888
|
meta: {
|
|
20613
20889
|
type: "suggestion",
|
|
20614
|
-
docs: { description:
|
|
20890
|
+
docs: { description: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION.summary },
|
|
20615
20891
|
schema: [],
|
|
20616
20892
|
messages: {
|
|
20617
20893
|
matchSoleExport: "This module's sole runtime export is `{{exported}}`; rename the file stem to `{{expected}}`."
|
|
@@ -20627,32 +20903,32 @@ var sole_export_matches_filename_default = createRule({
|
|
|
20627
20903
|
const exports = [];
|
|
20628
20904
|
const publicExports = /* @__PURE__ */ new Set();
|
|
20629
20905
|
for (const statement of program.body) {
|
|
20630
|
-
if (statement.type ===
|
|
20631
|
-
if (statement.type ===
|
|
20906
|
+
if (statement.type === AST_NODE_TYPES84.ExportAllDeclaration || statement.type === AST_NODE_TYPES84.ExportNamedDeclaration && statement.source !== null) return;
|
|
20907
|
+
if (statement.type === AST_NODE_TYPES84.ExportDefaultDeclaration) {
|
|
20632
20908
|
publicExports.add("default");
|
|
20633
20909
|
const declaration2 = statement.declaration;
|
|
20634
|
-
if ((declaration2.type ===
|
|
20910
|
+
if ((declaration2.type === AST_NODE_TYPES84.ClassDeclaration || declaration2.type === AST_NODE_TYPES84.FunctionDeclaration) && declaration2.id !== null) exports.push({ name: declaration2.id.name, node: declaration2 });
|
|
20635
20911
|
else return;
|
|
20636
20912
|
}
|
|
20637
|
-
if (statement.type !==
|
|
20913
|
+
if (statement.type !== AST_NODE_TYPES84.ExportNamedDeclaration) continue;
|
|
20638
20914
|
const declaration = statement.declaration;
|
|
20639
|
-
if (declaration !== null && "id" in declaration && declaration.id?.type ===
|
|
20915
|
+
if (declaration !== null && "id" in declaration && declaration.id?.type === AST_NODE_TYPES84.Identifier) {
|
|
20640
20916
|
publicExports.add(declaration.id.name);
|
|
20641
20917
|
}
|
|
20642
|
-
if (declaration?.type ===
|
|
20643
|
-
if (declaration.declarations.some((item) => item.id.type !==
|
|
20918
|
+
if (declaration?.type === AST_NODE_TYPES84.VariableDeclaration) {
|
|
20919
|
+
if (declaration.declarations.some((item) => item.id.type !== AST_NODE_TYPES84.Identifier)) return;
|
|
20644
20920
|
for (const item of declaration.declarations) {
|
|
20645
|
-
if (item.id.type ===
|
|
20921
|
+
if (item.id.type === AST_NODE_TYPES84.Identifier) publicExports.add(item.id.name);
|
|
20646
20922
|
}
|
|
20647
20923
|
}
|
|
20648
20924
|
for (const specifier of statement.specifiers) {
|
|
20649
|
-
const exported = specifier.exported.type ===
|
|
20925
|
+
const exported = specifier.exported.type === AST_NODE_TYPES84.Identifier ? specifier.exported.name : specifier.exported.value;
|
|
20650
20926
|
publicExports.add(String(exported));
|
|
20651
20927
|
}
|
|
20652
20928
|
if (statement.exportKind === "type") continue;
|
|
20653
20929
|
exports.push(...declarationExport(statement));
|
|
20654
20930
|
for (const specifier of statement.specifiers) {
|
|
20655
|
-
const exported = specifier.exported.type ===
|
|
20931
|
+
const exported = specifier.exported.type === AST_NODE_TYPES84.Identifier ? specifier.exported.name : specifier.exported.value;
|
|
20656
20932
|
publicExports.add(String(exported));
|
|
20657
20933
|
if (specifier.exportKind === "type") continue;
|
|
20658
20934
|
if (exported === "default") exports.push({ name: specifier.local.name, node: specifier });
|
|
@@ -20664,11 +20940,11 @@ var sole_export_matches_filename_default = createRule({
|
|
|
20664
20940
|
const only = [...unique.values()][0];
|
|
20665
20941
|
if (only === void 0) return;
|
|
20666
20942
|
if (only.name === "onRouterTransitionStart" && /(?:^|\/)instrumentation-client\.[jt]s$/u.test(normalizedFilename)) return;
|
|
20667
|
-
if (only.name === "collections" && /(?:^|\/)src\/content\.config\.(?:ts|js|mjs)$/u.test(normalizedFilename) && program.body.some((statement) => statement.type ===
|
|
20943
|
+
if (only.name === "collections" && /(?:^|\/)src\/content\.config\.(?:ts|js|mjs)$/u.test(normalizedFilename) && program.body.some((statement) => statement.type === AST_NODE_TYPES84.ImportDeclaration && statement.source.value === "astro:content")) return;
|
|
20668
20944
|
const exportedStem = kebabCase(only.name);
|
|
20669
20945
|
if (exportedStem === "") return;
|
|
20670
20946
|
const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
|
|
20671
|
-
if (expected === fileStem.toLowerCase()) return;
|
|
20947
|
+
if (expected === fileStem.toLowerCase() || reflectsExportName(fileStem, exportedStem)) return;
|
|
20672
20948
|
context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
|
|
20673
20949
|
}
|
|
20674
20950
|
};
|
|
@@ -20715,7 +20991,7 @@ var iac_source_coupled_test_default = createSourceCoupledRule(
|
|
|
20715
20991
|
|
|
20716
20992
|
// src/rules/require-pascal-case-zod-schema-name.ts
|
|
20717
20993
|
import {
|
|
20718
|
-
AST_NODE_TYPES as
|
|
20994
|
+
AST_NODE_TYPES as AST_NODE_TYPES85,
|
|
20719
20995
|
ASTUtils as ASTUtils48
|
|
20720
20996
|
} from "@typescript-eslint/utils";
|
|
20721
20997
|
var REQUIRE_PASCAL_CASE_ZOD_SCHEMA_NAME_DOCUMENTATION = {
|
|
@@ -20849,18 +21125,18 @@ var SCHEMA_RETURNING_METHODS = /* @__PURE__ */ new Set([
|
|
|
20849
21125
|
"superRefine",
|
|
20850
21126
|
"transform"
|
|
20851
21127
|
]);
|
|
20852
|
-
var terminalMethodName = (callee) => !callee.computed && callee.property.type ===
|
|
21128
|
+
var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES85.Identifier ? callee.property.name : null;
|
|
20853
21129
|
var calleeChainRoot2 = (node) => {
|
|
20854
21130
|
let current = node;
|
|
20855
21131
|
for (; ; ) {
|
|
20856
|
-
if (current.type ===
|
|
21132
|
+
if (current.type === AST_NODE_TYPES85.Identifier) {
|
|
20857
21133
|
return current;
|
|
20858
21134
|
}
|
|
20859
|
-
if (current.type ===
|
|
21135
|
+
if (current.type === AST_NODE_TYPES85.MemberExpression) {
|
|
20860
21136
|
current = current.object;
|
|
20861
21137
|
continue;
|
|
20862
21138
|
}
|
|
20863
|
-
if (current.type ===
|
|
21139
|
+
if (current.type === AST_NODE_TYPES85.CallExpression) {
|
|
20864
21140
|
current = current.callee;
|
|
20865
21141
|
continue;
|
|
20866
21142
|
}
|
|
@@ -20871,13 +21147,13 @@ var chainMemberNames2 = (node) => {
|
|
|
20871
21147
|
const names = [];
|
|
20872
21148
|
let current = node;
|
|
20873
21149
|
for (; ; ) {
|
|
20874
|
-
if (current.type ===
|
|
20875
|
-
if (current.computed || current.property.type !==
|
|
21150
|
+
if (current.type === AST_NODE_TYPES85.MemberExpression) {
|
|
21151
|
+
if (current.computed || current.property.type !== AST_NODE_TYPES85.Identifier) return [];
|
|
20876
21152
|
names.push(current.property.name);
|
|
20877
21153
|
current = current.object;
|
|
20878
21154
|
continue;
|
|
20879
21155
|
}
|
|
20880
|
-
if (current.type ===
|
|
21156
|
+
if (current.type === AST_NODE_TYPES85.CallExpression) {
|
|
20881
21157
|
current = current.callee;
|
|
20882
21158
|
continue;
|
|
20883
21159
|
}
|
|
@@ -20886,18 +21162,18 @@ var chainMemberNames2 = (node) => {
|
|
|
20886
21162
|
names.reverse();
|
|
20887
21163
|
return names;
|
|
20888
21164
|
};
|
|
20889
|
-
var
|
|
21165
|
+
var unwrapExpression7 = (node) => {
|
|
20890
21166
|
let current = node;
|
|
20891
|
-
while (current.type ===
|
|
21167
|
+
while (current.type === AST_NODE_TYPES85.TSAsExpression || current.type === AST_NODE_TYPES85.TSSatisfiesExpression || current.type === AST_NODE_TYPES85.TSNonNullExpression || current.type === AST_NODE_TYPES85.TSTypeAssertion) {
|
|
20892
21168
|
current = current.expression;
|
|
20893
21169
|
}
|
|
20894
21170
|
return current;
|
|
20895
21171
|
};
|
|
20896
21172
|
var isModuleDeclarator = (node) => {
|
|
20897
21173
|
const declaration = node.parent;
|
|
20898
|
-
if (declaration.type !==
|
|
21174
|
+
if (declaration.type !== AST_NODE_TYPES85.VariableDeclaration) return false;
|
|
20899
21175
|
const owner = declaration.parent;
|
|
20900
|
-
return owner.type ===
|
|
21176
|
+
return owner.type === AST_NODE_TYPES85.Program || owner.type === AST_NODE_TYPES85.ExportNamedDeclaration && owner.parent.type === AST_NODE_TYPES85.Program;
|
|
20901
21177
|
};
|
|
20902
21178
|
var require_pascal_case_zod_schema_name_default = createRule({
|
|
20903
21179
|
name: "require-pascal-case-zod-schema-name",
|
|
@@ -20937,9 +21213,9 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
20937
21213
|
return binding !== null && schemaBindings.has(binding);
|
|
20938
21214
|
}
|
|
20939
21215
|
function isConfirmedSchema(expression) {
|
|
20940
|
-
const init =
|
|
20941
|
-
if (init.type ===
|
|
20942
|
-
if (init.type !==
|
|
21216
|
+
const init = unwrapExpression7(expression);
|
|
21217
|
+
if (init.type === AST_NODE_TYPES85.Identifier) return isSchemaBinding(init);
|
|
21218
|
+
if (init.type !== AST_NODE_TYPES85.CallExpression || init.callee.type !== AST_NODE_TYPES85.MemberExpression) {
|
|
20943
21219
|
return false;
|
|
20944
21220
|
}
|
|
20945
21221
|
const terminal = terminalMethodName(init.callee);
|
|
@@ -20959,7 +21235,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
20959
21235
|
ImportDeclaration(node) {
|
|
20960
21236
|
if (!isZodModule(node.source.value)) return;
|
|
20961
21237
|
for (const specifier of node.specifiers) {
|
|
20962
|
-
if (specifier.type ===
|
|
21238
|
+
if (specifier.type === AST_NODE_TYPES85.ImportNamespaceSpecifier || specifier.type === AST_NODE_TYPES85.ImportDefaultSpecifier || specifier.type === AST_NODE_TYPES85.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES85.Identifier ? specifier.imported.name === "z" : specifier.imported.value === "z")) {
|
|
20963
21239
|
recordZodBinding(specifier.local);
|
|
20964
21240
|
}
|
|
20965
21241
|
}
|
|
@@ -20968,7 +21244,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
20968
21244
|
if (!isModuleDeclarator(node)) return;
|
|
20969
21245
|
const init = node.init;
|
|
20970
21246
|
if (init === null || init === void 0) return;
|
|
20971
|
-
if (node.id.type !==
|
|
21247
|
+
if (node.id.type !== AST_NODE_TYPES85.Identifier) return;
|
|
20972
21248
|
if (!isConfirmedSchema(init)) return;
|
|
20973
21249
|
const binding = resolvedBinding(node.id);
|
|
20974
21250
|
if (binding !== null) schemaBindings.add(binding);
|
|
@@ -21167,7 +21443,7 @@ var RULES = {
|
|
|
21167
21443
|
};
|
|
21168
21444
|
var meta = {
|
|
21169
21445
|
name: "@sarj/eslint-plugin",
|
|
21170
|
-
version: "15.17.
|
|
21446
|
+
version: "15.17.17"
|
|
21171
21447
|
};
|
|
21172
21448
|
var APPLICATION_ONLY_RULES = [];
|
|
21173
21449
|
var LIBRARY_IMPORT_POLICY = ["error", {
|