@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.cjs
CHANGED
|
@@ -2555,12 +2555,12 @@ function exportedNextConfigProperty(sourceCode, path) {
|
|
|
2555
2555
|
// src/rules/no-dangerously-allow-svg.ts
|
|
2556
2556
|
var NEXT_CONFIG_RE = /(?:^|\/)next\.config\.[cm]?[jt]s$/;
|
|
2557
2557
|
var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
|
|
2558
|
-
summary: "Next.js image configuration enables
|
|
2558
|
+
summary: "Next.js image configuration enables SVG rendering without the required response hardening",
|
|
2559
2559
|
rationale: "SVG files can contain scripts and other active content; enabling dangerouslyAllowSVG makes the image optimizer serve that content from the application origin.",
|
|
2560
|
-
remediation: "Keep dangerouslyAllowSVG disabled. If SVG
|
|
2560
|
+
remediation: "Keep dangerouslyAllowSVG disabled. If SVG optimization is required, retain attachment disposition and set the image Content-Security-Policy to `script-src 'none'; sandbox;`.",
|
|
2561
2561
|
category: "security",
|
|
2562
2562
|
limitations: [
|
|
2563
|
-
"Only
|
|
2563
|
+
"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."
|
|
2564
2564
|
],
|
|
2565
2565
|
examples: [
|
|
2566
2566
|
{
|
|
@@ -2583,6 +2583,19 @@ var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
|
|
|
2583
2583
|
}
|
|
2584
2584
|
]
|
|
2585
2585
|
};
|
|
2586
|
+
function literalString(node) {
|
|
2587
|
+
if (node?.value.type === "Literal" && typeof node.value.value === "string") return node.value.value;
|
|
2588
|
+
if (node?.value.type === "TemplateLiteral" && node.value.expressions.length === 0) return node.value.quasis[0]?.value.cooked ?? null;
|
|
2589
|
+
return null;
|
|
2590
|
+
}
|
|
2591
|
+
function hasHardenedSvgPolicy(policy) {
|
|
2592
|
+
const directives = policy.split(";").map((part) => part.trim().split(/\s+/u).filter(Boolean)).filter((parts) => parts.length > 0);
|
|
2593
|
+
const scriptSources = directives.filter(([name]) => name?.toLowerCase() === "script-src");
|
|
2594
|
+
const sandboxes = directives.filter(([name]) => name?.toLowerCase() === "sandbox");
|
|
2595
|
+
const scriptSource = scriptSources[0];
|
|
2596
|
+
const sandbox = sandboxes[0];
|
|
2597
|
+
return scriptSources.length === 1 && sandboxes.length === 1 && scriptSource?.length === 2 && scriptSource[1]?.toLowerCase() === "'none'" && sandbox?.length === 1;
|
|
2598
|
+
}
|
|
2586
2599
|
var no_dangerously_allow_svg_default = createRule({
|
|
2587
2600
|
name: "no-dangerously-allow-svg",
|
|
2588
2601
|
documentation: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION,
|
|
@@ -2591,7 +2604,7 @@ var no_dangerously_allow_svg_default = createRule({
|
|
|
2591
2604
|
docs: { description: NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION.summary },
|
|
2592
2605
|
schema: [],
|
|
2593
2606
|
messages: {
|
|
2594
|
-
noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG. SVG can carry active content served from the application origin."
|
|
2607
|
+
noDangerouslyAllowSvg: "Do not enable dangerouslyAllowSVG without a script-blocking sandbox policy and attachment disposition. SVG can carry active content served from the application origin."
|
|
2595
2608
|
}
|
|
2596
2609
|
},
|
|
2597
2610
|
defaultOptions: [],
|
|
@@ -2601,6 +2614,12 @@ var no_dangerously_allow_svg_default = createRule({
|
|
|
2601
2614
|
"Program:exit"() {
|
|
2602
2615
|
const node = exportedNextConfigProperty(context.sourceCode, ["images", "dangerouslyAllowSVG"]);
|
|
2603
2616
|
if (node !== null && node.value.type === "Literal" && node.value.value === true) {
|
|
2617
|
+
const disposition = exportedNextConfigProperty(context.sourceCode, ["images", "contentDispositionType"]);
|
|
2618
|
+
const policy = literalString(
|
|
2619
|
+
exportedNextConfigProperty(context.sourceCode, ["images", "contentSecurityPolicy"])
|
|
2620
|
+
);
|
|
2621
|
+
const attachmentDisposition = disposition === null || literalString(disposition) === "attachment";
|
|
2622
|
+
if (attachmentDisposition && policy !== null && hasHardenedSvgPolicy(policy)) return;
|
|
2604
2623
|
context.report({ node, messageId: "noDangerouslyAllowSvg" });
|
|
2605
2624
|
}
|
|
2606
2625
|
}
|
|
@@ -3719,12 +3738,41 @@ var no_hand_rolled_sleep_default = createRule({
|
|
|
3719
3738
|
|
|
3720
3739
|
// src/rules/no-hand-rolled-spinner.ts
|
|
3721
3740
|
var import_utils17 = require("@typescript-eslint/utils");
|
|
3741
|
+
|
|
3742
|
+
// src/rules/_tailwind.ts
|
|
3743
|
+
var tailwindVariantPrefix = (token) => {
|
|
3744
|
+
let bracketDepth = 0;
|
|
3745
|
+
let parenthesisDepth = 0;
|
|
3746
|
+
let escaped = false;
|
|
3747
|
+
let end = 0;
|
|
3748
|
+
for (let index = 0; index < token.length; index += 1) {
|
|
3749
|
+
const character = token[index];
|
|
3750
|
+
if (escaped) {
|
|
3751
|
+
escaped = false;
|
|
3752
|
+
continue;
|
|
3753
|
+
}
|
|
3754
|
+
if (character === "\\") {
|
|
3755
|
+
escaped = true;
|
|
3756
|
+
continue;
|
|
3757
|
+
}
|
|
3758
|
+
if (character === "[") bracketDepth += 1;
|
|
3759
|
+
else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
3760
|
+
else if (character === "(") parenthesisDepth += 1;
|
|
3761
|
+
else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
|
|
3762
|
+
else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
|
|
3763
|
+
}
|
|
3764
|
+
return token.slice(0, end);
|
|
3765
|
+
};
|
|
3766
|
+
var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
|
|
3767
|
+
var classTokens = (value) => value.split(/\s+/).filter(Boolean);
|
|
3768
|
+
|
|
3769
|
+
// src/rules/no-hand-rolled-spinner.ts
|
|
3722
3770
|
var NO_HAND_ROLLED_SPINNER_DOCUMENTATION = {
|
|
3723
3771
|
summary: "Disallow intrinsic elements styled as Tailwind border-ring spinners outside the design-system implementation.",
|
|
3724
3772
|
rationale: "One-off loading indicators duplicate a shared primitive and let accessibility and styling diverge.",
|
|
3725
3773
|
remediation: "Render the design-system Spinner component instead.",
|
|
3726
3774
|
category: "maintainability",
|
|
3727
|
-
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."],
|
|
3775
|
+
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."],
|
|
3728
3776
|
examples: [
|
|
3729
3777
|
{ 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 },
|
|
3730
3778
|
{ 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 }
|
|
@@ -3750,6 +3798,10 @@ function isContrastingEdge(token) {
|
|
|
3750
3798
|
const match = DIRECTIONAL_BORDER.exec(token);
|
|
3751
3799
|
return match?.[2] !== void 0 && !isBorderWidthValue(match[2]);
|
|
3752
3800
|
}
|
|
3801
|
+
function hasSpinnerInVariant(classes, variant) {
|
|
3802
|
+
const effective = classes.filter((entry) => entry.variant === "" || entry.variant === variant).map((entry) => entry.base);
|
|
3803
|
+
return effective.includes("animate-spin") && effective.includes("rounded-full") && effective.some(isBorderWidth) && effective.some(isContrastingEdge);
|
|
3804
|
+
}
|
|
3753
3805
|
function staticClassName(attribute) {
|
|
3754
3806
|
const value = attribute.value;
|
|
3755
3807
|
if (value?.type === import_utils17.AST_NODE_TYPES.Literal && typeof value.value === "string") {
|
|
@@ -3792,8 +3844,12 @@ var no_hand_rolled_spinner_default = createRule({
|
|
|
3792
3844
|
if (classNameAttribute?.type !== import_utils17.AST_NODE_TYPES.JSXAttribute) return;
|
|
3793
3845
|
const className = staticClassName(classNameAttribute);
|
|
3794
3846
|
if (className === null) return;
|
|
3795
|
-
const classes = className.split(/\s+/u)
|
|
3796
|
-
|
|
3847
|
+
const classes = className.split(/\s+/u).filter(Boolean).map((token) => ({
|
|
3848
|
+
base: tailwindBase(token),
|
|
3849
|
+
variant: tailwindVariantPrefix(token)
|
|
3850
|
+
}));
|
|
3851
|
+
const variants = new Set(classes.map((entry) => entry.variant));
|
|
3852
|
+
if ([...variants].some((variant) => hasSpinnerInVariant(classes, variant))) {
|
|
3797
3853
|
context.report({ node, messageId: "handRolledSpinner" });
|
|
3798
3854
|
}
|
|
3799
3855
|
}
|
|
@@ -4036,31 +4092,31 @@ var no_insecure_random_id_default = createRule({
|
|
|
4036
4092
|
// src/rules/no-detached-global-fetch.ts
|
|
4037
4093
|
var import_utils19 = require("@typescript-eslint/utils");
|
|
4038
4094
|
var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
|
|
4039
|
-
summary: "
|
|
4040
|
-
rationale: "
|
|
4041
|
-
remediation: "
|
|
4095
|
+
summary: "Keep the ambient global fetch receiver-safe when storing or explicitly rebinding it.",
|
|
4096
|
+
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.",
|
|
4097
|
+
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.",
|
|
4042
4098
|
category: "correctness",
|
|
4043
4099
|
autofix: "none",
|
|
4044
4100
|
limitations: [
|
|
4045
|
-
"The rule
|
|
4046
|
-
"
|
|
4047
|
-
"
|
|
4101
|
+
"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.",
|
|
4102
|
+
"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.",
|
|
4103
|
+
"Interprocedural aliases, mutable aliases, collection storage, and host identity remain manual review boundaries."
|
|
4048
4104
|
],
|
|
4049
4105
|
examples: [
|
|
4050
4106
|
{
|
|
4051
4107
|
id: "forwarded-global-fetch",
|
|
4052
|
-
title: "
|
|
4108
|
+
title: "Store a receiver-safe forwarding wrapper",
|
|
4053
4109
|
outcome: "no-match",
|
|
4054
|
-
files: [{ path: "src/client.ts", source: "
|
|
4110
|
+
files: [{ path: "src/client.ts", source: "class Client { readonly request = (input: RequestInfo | URL, init?: RequestInit) => fetch(input, init); }" }],
|
|
4055
4111
|
focusPath: "src/client.ts",
|
|
4056
4112
|
expectedCount: 0,
|
|
4057
4113
|
public: true
|
|
4058
4114
|
},
|
|
4059
4115
|
{
|
|
4060
|
-
id: "
|
|
4061
|
-
title: "Do not
|
|
4116
|
+
id: "receiver-unsafe-global-fetch",
|
|
4117
|
+
title: "Do not store raw ambient fetch as an object method",
|
|
4062
4118
|
outcome: "match",
|
|
4063
|
-
files: [{ path: "src/client.ts", source: "class Client {
|
|
4119
|
+
files: [{ path: "src/client.ts", source: "class Client { readonly request = fetch; }" }],
|
|
4064
4120
|
focusPath: "src/client.ts",
|
|
4065
4121
|
expectedCount: 1,
|
|
4066
4122
|
public: true
|
|
@@ -4068,85 +4124,131 @@ var NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION = {
|
|
|
4068
4124
|
]
|
|
4069
4125
|
};
|
|
4070
4126
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set(["globalThis", "self", "window"]);
|
|
4071
|
-
var
|
|
4127
|
+
var EXPLICIT_RECEIVER_METHODS = /* @__PURE__ */ new Set(["apply", "bind", "call"]);
|
|
4128
|
+
var STORAGE_ASSIGNMENT_OPERATORS = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
|
|
4129
|
+
function unwrapExpression(node) {
|
|
4130
|
+
let current = node;
|
|
4131
|
+
while (current.type === import_utils19.AST_NODE_TYPES.ChainExpression || current.type === import_utils19.AST_NODE_TYPES.TSAsExpression || current.type === import_utils19.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils19.AST_NODE_TYPES.TSTypeAssertion) {
|
|
4132
|
+
current = current.expression;
|
|
4133
|
+
}
|
|
4134
|
+
return current;
|
|
4135
|
+
}
|
|
4072
4136
|
var no_detached_global_fetch_default = createRule({
|
|
4073
4137
|
name: "no-detached-global-fetch",
|
|
4074
4138
|
documentation: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION,
|
|
4075
4139
|
meta: {
|
|
4076
4140
|
type: "problem",
|
|
4077
|
-
docs: { description:
|
|
4141
|
+
docs: { description: NO_DETACHED_GLOBAL_FETCH_DOCUMENTATION.summary },
|
|
4078
4142
|
schema: [],
|
|
4079
4143
|
messages: {
|
|
4080
|
-
detachedGlobalFetch: "
|
|
4144
|
+
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."
|
|
4081
4145
|
}
|
|
4082
4146
|
},
|
|
4083
4147
|
defaultOptions: [],
|
|
4084
4148
|
create(context) {
|
|
4085
4149
|
const sourceCode = context.sourceCode;
|
|
4086
|
-
if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) {
|
|
4087
|
-
|
|
4150
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || isGeneratedFile(context.filename, sourceCode.text)) return {};
|
|
4151
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
4152
|
+
function bindingOf(identifier) {
|
|
4153
|
+
return import_utils19.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
|
|
4088
4154
|
}
|
|
4089
4155
|
function resolvesToGlobal(identifier) {
|
|
4090
|
-
const variable =
|
|
4156
|
+
const variable = bindingOf(identifier);
|
|
4091
4157
|
return variable === null || variable.defs.length === 0;
|
|
4092
4158
|
}
|
|
4093
4159
|
function isGlobalReceiver(node) {
|
|
4094
4160
|
return node.type === import_utils19.AST_NODE_TYPES.Identifier && GLOBAL_RECEIVERS.has(node.name) && resolvesToGlobal(node);
|
|
4095
4161
|
}
|
|
4096
|
-
function
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
if (!parent.type.startsWith("TS")) return false;
|
|
4101
|
-
parent = parent.parent;
|
|
4102
|
-
}
|
|
4103
|
-
return false;
|
|
4162
|
+
function isStableAlias(variable) {
|
|
4163
|
+
return !variable.references.some(
|
|
4164
|
+
(reference) => reference.isWrite() && reference.init !== true
|
|
4165
|
+
);
|
|
4104
4166
|
}
|
|
4105
|
-
function
|
|
4106
|
-
|
|
4107
|
-
|
|
4167
|
+
function recordAlias(identifier) {
|
|
4168
|
+
const variable = bindingOf(identifier);
|
|
4169
|
+
if (variable !== null && isStableAlias(variable)) aliases.add(variable);
|
|
4108
4170
|
}
|
|
4109
|
-
function
|
|
4110
|
-
|
|
4171
|
+
function isGlobalFetchMember(node) {
|
|
4172
|
+
if (!isGlobalReceiver(node.object)) return false;
|
|
4173
|
+
if (!node.computed) {
|
|
4174
|
+
return node.property.type === import_utils19.AST_NODE_TYPES.Identifier && node.property.name === "fetch";
|
|
4175
|
+
}
|
|
4176
|
+
return node.property.type === import_utils19.AST_NODE_TYPES.Literal && node.property.value === "fetch";
|
|
4111
4177
|
}
|
|
4112
|
-
function
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
return false;
|
|
4178
|
+
function staticMemberName9(node) {
|
|
4179
|
+
if (!node.computed) {
|
|
4180
|
+
return node.property.type === import_utils19.AST_NODE_TYPES.Identifier ? node.property.name : null;
|
|
4116
4181
|
}
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4182
|
+
return node.property.type === import_utils19.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
4183
|
+
}
|
|
4184
|
+
function mayBeRawFetch(node) {
|
|
4185
|
+
const expression = unwrapExpression(node);
|
|
4186
|
+
if (expression.type === import_utils19.AST_NODE_TYPES.Identifier) {
|
|
4187
|
+
if (expression.name === "fetch" && resolvesToGlobal(expression)) return true;
|
|
4188
|
+
const variable = bindingOf(expression);
|
|
4189
|
+
return variable !== null && aliases.has(variable) && isStableAlias(variable);
|
|
4190
|
+
}
|
|
4191
|
+
if (expression.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
|
|
4192
|
+
return isGlobalFetchMember(expression);
|
|
4120
4193
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
if (node.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
|
|
4124
|
-
const original = receiverOf(node);
|
|
4125
|
-
return original !== null && receiver.type === import_utils19.AST_NODE_TYPES.Identifier && receiver.name === original.name;
|
|
4194
|
+
if (expression.type === import_utils19.AST_NODE_TYPES.LogicalExpression) {
|
|
4195
|
+
return expression.operator === "&&" ? mayBeRawFetch(expression.right) : mayBeRawFetch(expression.left) || mayBeRawFetch(expression.right);
|
|
4126
4196
|
}
|
|
4127
|
-
|
|
4197
|
+
if (expression.type === import_utils19.AST_NODE_TYPES.ConditionalExpression) {
|
|
4198
|
+
return mayBeRawFetch(expression.consequent) || mayBeRawFetch(expression.alternate);
|
|
4199
|
+
}
|
|
4200
|
+
if (expression.type === import_utils19.AST_NODE_TYPES.SequenceExpression) {
|
|
4201
|
+
const last = expression.expressions.at(-1);
|
|
4202
|
+
return last !== void 0 && mayBeRawFetch(last);
|
|
4203
|
+
}
|
|
4204
|
+
return false;
|
|
4128
4205
|
}
|
|
4129
|
-
function
|
|
4130
|
-
if (
|
|
4131
|
-
|
|
4206
|
+
function recordAliasFromValue(identifier, value) {
|
|
4207
|
+
if (mayBeRawFetch(value)) recordAlias(identifier);
|
|
4208
|
+
}
|
|
4209
|
+
function reportStored(node) {
|
|
4210
|
+
if (mayBeRawFetch(node)) context.report({ node, messageId: "detachedGlobalFetch" });
|
|
4211
|
+
}
|
|
4212
|
+
function isCompatibleReceiver(node) {
|
|
4213
|
+
if (node === void 0 || node.type === import_utils19.AST_NODE_TYPES.SpreadElement) return false;
|
|
4214
|
+
if (node.type !== import_utils19.AST_NODE_TYPES.Identifier || !resolvesToGlobal(node)) return false;
|
|
4215
|
+
return GLOBAL_RECEIVERS.has(node.name) || node.name === "undefined";
|
|
4132
4216
|
}
|
|
4133
4217
|
return {
|
|
4134
|
-
|
|
4135
|
-
if (node.
|
|
4136
|
-
|
|
4137
|
-
if (parent.type === import_utils19.AST_NODE_TYPES.MemberExpression && parent.property === node) {
|
|
4138
|
-
return;
|
|
4139
|
-
}
|
|
4140
|
-
if (parent.type === import_utils19.AST_NODE_TYPES.Property && parent.key === node && !parent.computed && !parent.shorthand) {
|
|
4141
|
-
return;
|
|
4218
|
+
AssignmentExpression(node) {
|
|
4219
|
+
if (STORAGE_ASSIGNMENT_OPERATORS.has(node.operator) && node.left.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
|
|
4220
|
+
reportStored(node.right);
|
|
4142
4221
|
}
|
|
4143
|
-
reportIfDetached(node);
|
|
4144
4222
|
},
|
|
4145
|
-
|
|
4146
|
-
if (node.
|
|
4223
|
+
AssignmentPattern(node) {
|
|
4224
|
+
if (node.left.type !== import_utils19.AST_NODE_TYPES.Identifier) return;
|
|
4225
|
+
recordAliasFromValue(node.left, node.right);
|
|
4226
|
+
if (node.parent.type === import_utils19.AST_NODE_TYPES.TSParameterProperty) reportStored(node.right);
|
|
4227
|
+
},
|
|
4228
|
+
CallExpression(node) {
|
|
4229
|
+
const callee = unwrapExpression(node.callee);
|
|
4230
|
+
if (callee.type !== import_utils19.AST_NODE_TYPES.MemberExpression || !EXPLICIT_RECEIVER_METHODS.has(staticMemberName9(callee) ?? "") || !mayBeRawFetch(callee.object) || isCompatibleReceiver(node.arguments[0])) return;
|
|
4231
|
+
context.report({ node: callee.object, messageId: "detachedGlobalFetch" });
|
|
4232
|
+
},
|
|
4233
|
+
Property(node) {
|
|
4234
|
+
if (node.parent.type === import_utils19.AST_NODE_TYPES.ObjectPattern || node.method || node.value.type === import_utils19.AST_NODE_TYPES.AssignmentPattern || node.value.type === import_utils19.AST_NODE_TYPES.TSEmptyBodyFunctionExpression) return;
|
|
4235
|
+
reportStored(node.value);
|
|
4236
|
+
},
|
|
4237
|
+
PropertyDefinition(node) {
|
|
4238
|
+
if (node.value !== null) reportStored(node.value);
|
|
4239
|
+
},
|
|
4240
|
+
VariableDeclarator(node) {
|
|
4241
|
+
if (node.init === null) return;
|
|
4242
|
+
if (node.id.type === import_utils19.AST_NODE_TYPES.Identifier) {
|
|
4243
|
+
recordAliasFromValue(node.id, node.init);
|
|
4147
4244
|
return;
|
|
4148
4245
|
}
|
|
4149
|
-
|
|
4246
|
+
if (node.id.type !== import_utils19.AST_NODE_TYPES.ObjectPattern || !isGlobalReceiver(unwrapExpression(node.init))) return;
|
|
4247
|
+
for (const property of node.id.properties) {
|
|
4248
|
+
if (property.type !== import_utils19.AST_NODE_TYPES.Property || property.computed || property.key.type !== import_utils19.AST_NODE_TYPES.Identifier || property.key.name !== "fetch") continue;
|
|
4249
|
+
const value = property.value.type === import_utils19.AST_NODE_TYPES.AssignmentPattern ? property.value.left : property.value;
|
|
4250
|
+
if (value.type === import_utils19.AST_NODE_TYPES.Identifier) recordAlias(value);
|
|
4251
|
+
}
|
|
4150
4252
|
}
|
|
4151
4253
|
};
|
|
4152
4254
|
}
|
|
@@ -4408,7 +4510,7 @@ var NO_JSON_STRINGIFY_OBJECT_EQUALITY_DOCUMENTATION = {
|
|
|
4408
4510
|
};
|
|
4409
4511
|
var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["!=", "!==", "==", "==="]);
|
|
4410
4512
|
var PRIMITIVE_FLAGS = import_typescript.default.TypeFlags.BigIntLike | import_typescript.default.TypeFlags.BooleanLike | import_typescript.default.TypeFlags.ESSymbolLike | import_typescript.default.TypeFlags.Never | import_typescript.default.TypeFlags.Null | import_typescript.default.TypeFlags.NumberLike | import_typescript.default.TypeFlags.StringLike | import_typescript.default.TypeFlags.Undefined | import_typescript.default.TypeFlags.Void;
|
|
4411
|
-
function
|
|
4513
|
+
function unwrapExpression2(node) {
|
|
4412
4514
|
let current = node;
|
|
4413
4515
|
while (current.type === import_utils21.AST_NODE_TYPES.ChainExpression || current.type === import_utils21.AST_NODE_TYPES.TSAsExpression || current.type === import_utils21.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils21.AST_NODE_TYPES.TSTypeAssertion) {
|
|
4414
4516
|
current = current.expression;
|
|
@@ -4416,10 +4518,10 @@ function unwrapExpression(node) {
|
|
|
4416
4518
|
return current;
|
|
4417
4519
|
}
|
|
4418
4520
|
function jsonStringifyArgument(node, sourceCode) {
|
|
4419
|
-
const expression =
|
|
4521
|
+
const expression = unwrapExpression2(node);
|
|
4420
4522
|
if (expression.type !== import_utils21.AST_NODE_TYPES.CallExpression) return null;
|
|
4421
4523
|
const { callee } = expression;
|
|
4422
|
-
if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression || callee.
|
|
4524
|
+
if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression || callee.object.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.object.name !== "JSON" || (!callee.computed ? callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "stringify" : callee.property.type !== import_utils21.AST_NODE_TYPES.Literal || callee.property.value !== "stringify")) {
|
|
4423
4525
|
return null;
|
|
4424
4526
|
}
|
|
4425
4527
|
const variable = import_utils21.ASTUtils.findVariable(sourceCode.getScope(callee.object), "JSON");
|
|
@@ -4447,7 +4549,7 @@ function typeMayContainObject(type, checker) {
|
|
|
4447
4549
|
return true;
|
|
4448
4550
|
}
|
|
4449
4551
|
function syntaxMayContainObject(node) {
|
|
4450
|
-
const expression =
|
|
4552
|
+
const expression = unwrapExpression2(node);
|
|
4451
4553
|
if (expression.type === import_utils21.AST_NODE_TYPES.ObjectExpression) return true;
|
|
4452
4554
|
if (expression.type !== import_utils21.AST_NODE_TYPES.ArrayExpression) return null;
|
|
4453
4555
|
for (const element of expression.elements) {
|
|
@@ -5076,7 +5178,7 @@ var INTERFACE_CONTRACT_MEMBERS_PRIVATE_DOCUMENTATION = {
|
|
|
5076
5178
|
category: "architecture",
|
|
5077
5179
|
autofix: "none",
|
|
5078
5180
|
limitations: [
|
|
5079
|
-
"Only concrete classes with an explicit `implements` clause are checked; constructors
|
|
5181
|
+
"Only concrete classes with an explicit `implements` clause are checked; constructors, static members, protected extension hooks, and overrides are excluded.",
|
|
5080
5182
|
"Inherited interface members are resolved by TypeScript. Computed names are excluded because their contract identity is not stable syntax.",
|
|
5081
5183
|
"The rule abstains for the whole class when TypeScript cannot resolve any implemented contract, avoiding false positives for missing or unavailable package declarations.",
|
|
5082
5184
|
"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.",
|
|
@@ -5128,7 +5230,7 @@ function reportClass(context, services, owner) {
|
|
|
5128
5230
|
}
|
|
5129
5231
|
}
|
|
5130
5232
|
function candidate(member) {
|
|
5131
|
-
return member.type === import_utils26.AST_NODE_TYPES.MethodDefinition && member.kind !== "constructor" && !member.static && !member.computed && member.key.type === import_utils26.AST_NODE_TYPES.Identifier && member.value.body !== null;
|
|
5233
|
+
return member.type === import_utils26.AST_NODE_TYPES.MethodDefinition && member.kind !== "constructor" && !member.static && member.accessibility !== "protected" && !member.override && !member.computed && member.key.type === import_utils26.AST_NODE_TYPES.Identifier && member.value.body !== null;
|
|
5132
5234
|
}
|
|
5133
5235
|
function interfaceNames(services, owner) {
|
|
5134
5236
|
const tsOwner = services.esTreeNodeToTSNodeMap.get(owner);
|
|
@@ -5486,7 +5588,7 @@ var NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION = {
|
|
|
5486
5588
|
remediation: "Rethrow the error, assert on it, or use the runner's explicit skip mechanism when the capability is optional.",
|
|
5487
5589
|
category: "testing",
|
|
5488
5590
|
filePatterns: ["**/*.test.*", "**/*.spec.*", "**/tests/**", "**/__tests__/**"],
|
|
5489
|
-
limitations: ["Only bare returns owned by a direct supported test callback and followed lexically by a framework assertion are reported."],
|
|
5591
|
+
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."],
|
|
5490
5592
|
examples: [
|
|
5491
5593
|
{ 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 },
|
|
5492
5594
|
{ 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 }
|
|
@@ -5507,10 +5609,11 @@ function importedName4(identifier, context, modules) {
|
|
|
5507
5609
|
const variable = import_utils29.ASTUtils.findVariable(context.sourceCode.getScope(identifier), identifier.name);
|
|
5508
5610
|
if (variable === null || variable.defs.length === 0) return identifier.name;
|
|
5509
5611
|
for (const definition of variable.defs) {
|
|
5510
|
-
if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier) continue;
|
|
5612
|
+
if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier && definition.node.type !== import_utils29.AST_NODE_TYPES.ImportDefaultSpecifier && definition.node.type !== import_utils29.AST_NODE_TYPES.ImportNamespaceSpecifier) continue;
|
|
5511
5613
|
const declaration = definition.node.parent;
|
|
5512
5614
|
if (declaration.type !== import_utils29.AST_NODE_TYPES.ImportDeclaration || typeof declaration.source.value !== "string" || !modules.has(declaration.source.value)) continue;
|
|
5513
5615
|
if (declaration.source.value === "node:assert" || declaration.source.value === "node:assert/strict") return "assert";
|
|
5616
|
+
if (definition.node.type !== import_utils29.AST_NODE_TYPES.ImportSpecifier) continue;
|
|
5514
5617
|
const imported = definition.node.imported;
|
|
5515
5618
|
return imported.type === import_utils29.AST_NODE_TYPES.Identifier ? imported.name : String(imported.value);
|
|
5516
5619
|
}
|
|
@@ -5562,6 +5665,13 @@ function isExplicitSkip(node, context) {
|
|
|
5562
5665
|
const root = rootIdentifier2(node.callee.object);
|
|
5563
5666
|
return root !== null && TEST_NAMES.has(importedName4(root, context, TEST_MODULES2) ?? "");
|
|
5564
5667
|
}
|
|
5668
|
+
function hasDominatingExplicitSkip(node, context) {
|
|
5669
|
+
const block = node.parent;
|
|
5670
|
+
if (block?.type !== import_utils29.AST_NODE_TYPES.BlockStatement) return false;
|
|
5671
|
+
return block.body.some(
|
|
5672
|
+
(candidate2) => candidate2.range[1] <= node.range[0] && candidate2.type === import_utils29.AST_NODE_TYPES.ExpressionStatement && isExplicitSkip(candidate2.expression, context)
|
|
5673
|
+
);
|
|
5674
|
+
}
|
|
5565
5675
|
var no_bare_return_from_test_catch_default = createRule({
|
|
5566
5676
|
name: "no-bare-return-from-test-catch",
|
|
5567
5677
|
documentation: NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION,
|
|
@@ -5587,11 +5697,12 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
5587
5697
|
}
|
|
5588
5698
|
if (current === null || current === void 0) break;
|
|
5589
5699
|
}
|
|
5590
|
-
if (catchClause === null
|
|
5700
|
+
if (catchClause === null) return;
|
|
5591
5701
|
const parameter = catchClause.param;
|
|
5592
|
-
|
|
5702
|
+
const returnBlock = node.parent;
|
|
5703
|
+
if (parameter?.type === import_utils29.AST_NODE_TYPES.Identifier && returnBlock?.type === import_utils29.AST_NODE_TYPES.BlockStatement) {
|
|
5593
5704
|
const errorBinding = import_utils29.ASTUtils.findVariable(context.sourceCode.getScope(parameter), parameter.name);
|
|
5594
|
-
const assertedError =
|
|
5705
|
+
const assertedError = returnBlock.body.some((statement) => {
|
|
5595
5706
|
if (statement.range[1] >= node.range[0] || statement.type !== import_utils29.AST_NODE_TYPES.ExpressionStatement) return false;
|
|
5596
5707
|
const expression = statement.expression;
|
|
5597
5708
|
if (expression.type !== import_utils29.AST_NODE_TYPES.CallExpression || !isAssertion(expression, context)) return false;
|
|
@@ -5605,7 +5716,7 @@ var no_bare_return_from_test_catch_default = createRule({
|
|
|
5605
5716
|
});
|
|
5606
5717
|
if (assertedError) return;
|
|
5607
5718
|
}
|
|
5608
|
-
if (
|
|
5719
|
+
if (hasDominatingExplicitSkip(node, context)) return;
|
|
5609
5720
|
if (!walkOwnScope(owner.body, (current) => current.range[0] > node.range[1] && isAssertion(current, context))) return;
|
|
5610
5721
|
context.report({ node, messageId: "bareReturnFromTestCatch" });
|
|
5611
5722
|
}
|
|
@@ -5843,7 +5954,8 @@ var no_long_comment_default = createRule({
|
|
|
5843
5954
|
});
|
|
5844
5955
|
|
|
5845
5956
|
// src/rules/no-vague-suppression-description.ts
|
|
5846
|
-
var
|
|
5957
|
+
var ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE = /^eslint-(?:disable|disable-next-line|disable-line)\b[^:\n]*?\s*(?::|--)\s*(.+?)\s*$/iu;
|
|
5958
|
+
var TS_EXPECT_ERROR_WITH_DESCRIPTION_RE = /^@ts-expect-error\b(?:(?:\s*(?::|--)\s*)|\s+)(.+?)\s*$/iu;
|
|
5847
5959
|
var VAGUE_RE = /^(?:needed|required|intentional(?:ly)?|ignore(?:d)?|false positive|type error|typescript|to satisfy (?:the )?(?:linter|typescript|type checker))\.?$/iu;
|
|
5848
5960
|
var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
|
|
5849
5961
|
summary: "Require suppression descriptions to name the concrete mismatch or invariant instead of a generic non-reason.",
|
|
@@ -5886,6 +5998,9 @@ var NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION = {
|
|
|
5886
5998
|
}
|
|
5887
5999
|
]
|
|
5888
6000
|
};
|
|
6001
|
+
function suppressionDescription(text) {
|
|
6002
|
+
return (ESLINT_DIRECTIVE_WITH_DESCRIPTION_RE.exec(text)?.[1] ?? TS_EXPECT_ERROR_WITH_DESCRIPTION_RE.exec(text)?.[1])?.trim();
|
|
6003
|
+
}
|
|
5889
6004
|
var no_vague_suppression_description_default = createRule({
|
|
5890
6005
|
name: "no-vague-suppression-description",
|
|
5891
6006
|
documentation: NO_VAGUE_SUPPRESSION_DESCRIPTION_DOCUMENTATION,
|
|
@@ -5908,7 +6023,7 @@ var no_vague_suppression_description_default = createRule({
|
|
|
5908
6023
|
Program() {
|
|
5909
6024
|
for (const comment of context.sourceCode.getAllComments()) {
|
|
5910
6025
|
const text = comment.value.trim();
|
|
5911
|
-
const description =
|
|
6026
|
+
const description = suppressionDescription(text);
|
|
5912
6027
|
if (description === void 0 || !VAGUE_RE.test(description)) continue;
|
|
5913
6028
|
context.report({
|
|
5914
6029
|
loc: comment.loc,
|
|
@@ -6972,12 +7087,24 @@ var NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION = {
|
|
|
6972
7087
|
{ 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 }
|
|
6973
7088
|
]
|
|
6974
7089
|
};
|
|
6975
|
-
function
|
|
6976
|
-
|
|
7090
|
+
function staticModule(node) {
|
|
7091
|
+
if (node?.type === import_utils37.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
7092
|
+
return node.value;
|
|
7093
|
+
}
|
|
7094
|
+
if (node?.type === import_utils37.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0) {
|
|
7095
|
+
return node.quasis[0]?.value.cooked ?? null;
|
|
7096
|
+
}
|
|
7097
|
+
return null;
|
|
6977
7098
|
}
|
|
6978
7099
|
function matchesModule(source, module2) {
|
|
6979
7100
|
return source === module2 || source.startsWith(`${module2}/`);
|
|
6980
7101
|
}
|
|
7102
|
+
function staticMemberName4(node) {
|
|
7103
|
+
if (!node.computed && node.property.type === import_utils37.AST_NODE_TYPES.Identifier) {
|
|
7104
|
+
return node.property.name;
|
|
7105
|
+
}
|
|
7106
|
+
return node.computed && node.property.type === import_utils37.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
|
|
7107
|
+
}
|
|
6981
7108
|
var no_restricted_library_load_default = createRule({
|
|
6982
7109
|
name: "no-restricted-library-load",
|
|
6983
7110
|
documentation: NO_RESTRICTED_LIBRARY_LOAD_DOCUMENTATION,
|
|
@@ -7041,24 +7168,24 @@ var no_restricted_library_load_default = createRule({
|
|
|
7041
7168
|
}
|
|
7042
7169
|
return {
|
|
7043
7170
|
ImportExpression(node) {
|
|
7044
|
-
const source =
|
|
7171
|
+
const source = staticModule(node.source);
|
|
7045
7172
|
if (source !== null) report2(node.source, source);
|
|
7046
7173
|
},
|
|
7047
7174
|
CallExpression(node) {
|
|
7048
7175
|
let requireIdentifier = null;
|
|
7049
7176
|
if (node.callee.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.name === "require") {
|
|
7050
7177
|
requireIdentifier = node.callee;
|
|
7051
|
-
} else if (node.callee.type === import_utils37.AST_NODE_TYPES.MemberExpression &&
|
|
7178
|
+
} else if (node.callee.type === import_utils37.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils37.AST_NODE_TYPES.Identifier && node.callee.object.name === "require" && staticMemberName4(node.callee) === "resolve") {
|
|
7052
7179
|
requireIdentifier = node.callee.object;
|
|
7053
7180
|
}
|
|
7054
7181
|
if (requireIdentifier === null || !isUnshadowedRequire(requireIdentifier)) return;
|
|
7055
|
-
const source =
|
|
7182
|
+
const source = staticModule(node.arguments[0]);
|
|
7056
7183
|
if (source !== null) report2(node.arguments[0], source);
|
|
7057
7184
|
},
|
|
7058
7185
|
TSImportEqualsDeclaration(node) {
|
|
7059
7186
|
if (node.importKind === "type") return;
|
|
7060
7187
|
if (node.moduleReference.type !== import_utils37.AST_NODE_TYPES.TSExternalModuleReference) return;
|
|
7061
|
-
const source =
|
|
7188
|
+
const source = staticModule(node.moduleReference.expression);
|
|
7062
7189
|
if (source !== null) report2(node.moduleReference.expression, source);
|
|
7063
7190
|
}
|
|
7064
7191
|
};
|
|
@@ -11098,7 +11225,7 @@ function rootIdentifier3(callee) {
|
|
|
11098
11225
|
if (callee.type === import_utils60.AST_NODE_TYPES.TaggedTemplateExpression) return rootIdentifier3(callee.tag);
|
|
11099
11226
|
return null;
|
|
11100
11227
|
}
|
|
11101
|
-
function
|
|
11228
|
+
function staticMemberName5(member) {
|
|
11102
11229
|
if (!member.computed && member.property.type === import_utils60.AST_NODE_TYPES.Identifier) return member.property.name;
|
|
11103
11230
|
if (member.computed && member.property.type === import_utils60.AST_NODE_TYPES.Literal && typeof member.property.value === "string") {
|
|
11104
11231
|
return member.property.value;
|
|
@@ -11113,7 +11240,7 @@ function isTestBody2(node, isFrameworkTest) {
|
|
|
11113
11240
|
function isTestCaller(callee) {
|
|
11114
11241
|
if (callee.type === import_utils60.AST_NODE_TYPES.Identifier) return TEST_CALLERS3.has(callee.name);
|
|
11115
11242
|
if (callee.type !== import_utils60.AST_NODE_TYPES.MemberExpression) return false;
|
|
11116
|
-
const member =
|
|
11243
|
+
const member = staticMemberName5(callee);
|
|
11117
11244
|
return member !== null && TEST_MODIFIERS3.has(member) && isTestCaller(callee.object);
|
|
11118
11245
|
}
|
|
11119
11246
|
function nearestEnclosingFunction2(node) {
|
|
@@ -11194,7 +11321,7 @@ function opensSubtest(node, callbackParameters) {
|
|
|
11194
11321
|
return false;
|
|
11195
11322
|
}
|
|
11196
11323
|
const callee = node.callee;
|
|
11197
|
-
return callee.type === import_utils60.AST_NODE_TYPES.MemberExpression &&
|
|
11324
|
+
return callee.type === import_utils60.AST_NODE_TYPES.MemberExpression && staticMemberName5(callee) === "test" && callee.object.type === import_utils60.AST_NODE_TYPES.Identifier && callbackParameters.has(callee.object.name) && node.arguments.some(
|
|
11198
11325
|
(argument) => argument.type !== import_utils60.AST_NODE_TYPES.SpreadElement && FUNCTION_TYPES6.has(argument.type)
|
|
11199
11326
|
);
|
|
11200
11327
|
}
|
|
@@ -11244,7 +11371,7 @@ var test_loops_over_literal_cases_default = createRule({
|
|
|
11244
11371
|
for (let current = node; current !== void 0 && current !== enclosing; current = current.parent) {
|
|
11245
11372
|
if (current.parent?.type === import_utils60.AST_NODE_TYPES.BlockStatement && current.parent.body.at(-1) !== current) return;
|
|
11246
11373
|
}
|
|
11247
|
-
const cases =
|
|
11374
|
+
const cases = unwrapExpression3(node.right);
|
|
11248
11375
|
const callbackParameters = new Set(
|
|
11249
11376
|
enclosing.params.flatMap((parameter) => parameter.type === import_utils60.AST_NODE_TYPES.Identifier ? [parameter.name] : [])
|
|
11250
11377
|
);
|
|
@@ -11272,9 +11399,9 @@ var test_loops_over_literal_cases_default = createRule({
|
|
|
11272
11399
|
};
|
|
11273
11400
|
}
|
|
11274
11401
|
});
|
|
11275
|
-
function
|
|
11402
|
+
function unwrapExpression3(node) {
|
|
11276
11403
|
if (node.type === import_utils60.AST_NODE_TYPES.TSAsExpression || node.type === import_utils60.AST_NODE_TYPES.TSTypeAssertion || node.type === import_utils60.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils60.AST_NODE_TYPES.TSNonNullExpression) {
|
|
11277
|
-
return
|
|
11404
|
+
return unwrapExpression3(node.expression);
|
|
11278
11405
|
}
|
|
11279
11406
|
return node;
|
|
11280
11407
|
}
|
|
@@ -12061,14 +12188,14 @@ function isAsConst(node, sourceText) {
|
|
|
12061
12188
|
if (node.type !== import_utils68.AST_NODE_TYPES.TSAsExpression) return false;
|
|
12062
12189
|
return sourceText(node.typeAnnotation).trim() === "const";
|
|
12063
12190
|
}
|
|
12064
|
-
function
|
|
12191
|
+
function unwrapExpression4(node) {
|
|
12065
12192
|
if (node.type === import_utils68.AST_NODE_TYPES.TSAsExpression || node.type === import_utils68.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils68.AST_NODE_TYPES.TSNonNullExpression) {
|
|
12066
|
-
return
|
|
12193
|
+
return unwrapExpression4(node.expression);
|
|
12067
12194
|
}
|
|
12068
12195
|
return node;
|
|
12069
12196
|
}
|
|
12070
12197
|
function isObjectFreeze(node, isUnshadowedGlobal3) {
|
|
12071
|
-
const inner =
|
|
12198
|
+
const inner = unwrapExpression4(node);
|
|
12072
12199
|
if (inner.type === import_utils68.AST_NODE_TYPES.CallExpression && inner.callee.type === import_utils68.AST_NODE_TYPES.MemberExpression && !inner.callee.computed && inner.callee.object.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1) {
|
|
12073
12200
|
const argument = inner.arguments[0];
|
|
12074
12201
|
return argument !== void 0 && argument.type !== import_utils68.AST_NODE_TYPES.SpreadElement && collectionKind(argument, isUnshadowedGlobal3) === "literal";
|
|
@@ -12076,7 +12203,7 @@ function isObjectFreeze(node, isUnshadowedGlobal3) {
|
|
|
12076
12203
|
return false;
|
|
12077
12204
|
}
|
|
12078
12205
|
function collectionKind(node, isUnshadowedGlobal3) {
|
|
12079
|
-
const inner =
|
|
12206
|
+
const inner = unwrapExpression4(node);
|
|
12080
12207
|
if (inner.type === import_utils68.AST_NODE_TYPES.CallExpression && inner.callee.type === import_utils68.AST_NODE_TYPES.MemberExpression && !inner.callee.computed && inner.callee.object.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.object.name === "Object" && isUnshadowedGlobal3(inner.callee.object) && inner.callee.property.type === import_utils68.AST_NODE_TYPES.Identifier && inner.callee.property.name === "freeze" && inner.arguments.length === 1 && inner.arguments[0] !== void 0 && inner.arguments[0].type !== import_utils68.AST_NODE_TYPES.SpreadElement) {
|
|
12081
12208
|
return collectionKind(inner.arguments[0], isUnshadowedGlobal3);
|
|
12082
12209
|
}
|
|
@@ -15503,17 +15630,18 @@ var prefer_schema_for_api_payload_default = createRule({
|
|
|
15503
15630
|
// src/rules/prefer-shared-zod-enum.ts
|
|
15504
15631
|
var import_utils83 = require("@typescript-eslint/utils");
|
|
15505
15632
|
var PREFER_SHARED_ZOD_ENUM_DOCUMENTATION = {
|
|
15506
|
-
summary: "Give literal Zod enum domains one reusable module-level schema.",
|
|
15507
|
-
rationale: "
|
|
15633
|
+
summary: "Give repeated literal Zod enum domains one reusable module-level schema.",
|
|
15634
|
+
rationale: "Repeated literal domains hide a shared contract and allow equivalent fields to drift independently.",
|
|
15508
15635
|
remediation: "Declare a module-level named Zod enum schema and reuse it at each field or contract site.",
|
|
15509
15636
|
category: "maintainability",
|
|
15510
|
-
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
|
|
15637
|
+
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."],
|
|
15511
15638
|
examples: [
|
|
15512
15639
|
{ 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 },
|
|
15513
|
-
{ id: "inline-provider", title: "Do not inline enum
|
|
15640
|
+
{ 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 }
|
|
15514
15641
|
]
|
|
15515
15642
|
};
|
|
15516
15643
|
function literalDomain(node) {
|
|
15644
|
+
if (node.arguments.length !== 1) return null;
|
|
15517
15645
|
const [argument] = node.arguments;
|
|
15518
15646
|
if (argument?.type !== import_utils83.AST_NODE_TYPES.ArrayExpression || argument.elements.length < 2) return null;
|
|
15519
15647
|
const values = [];
|
|
@@ -15539,7 +15667,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15539
15667
|
documentation: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION,
|
|
15540
15668
|
meta: {
|
|
15541
15669
|
type: "suggestion",
|
|
15542
|
-
docs: { description:
|
|
15670
|
+
docs: { description: PREFER_SHARED_ZOD_ENUM_DOCUMENTATION.summary },
|
|
15543
15671
|
schema: [],
|
|
15544
15672
|
messages: {
|
|
15545
15673
|
shareEnumDomain: "Extract this literal Zod enum to one module-level named schema and reuse it."
|
|
@@ -15550,7 +15678,7 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15550
15678
|
if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
|
|
15551
15679
|
const zodBindings = /* @__PURE__ */ new Set();
|
|
15552
15680
|
const bindingOf = (node) => import_utils83.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
|
|
15553
|
-
const
|
|
15681
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
15554
15682
|
return {
|
|
15555
15683
|
ImportDeclaration(node) {
|
|
15556
15684
|
if (!isZodModule(node.source.value)) return;
|
|
@@ -15568,10 +15696,19 @@ var prefer_shared_zod_enum_default = createRule({
|
|
|
15568
15696
|
const domain = literalDomain(node);
|
|
15569
15697
|
if (domain === null) return;
|
|
15570
15698
|
const key = JSON.stringify(domain);
|
|
15571
|
-
const
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15699
|
+
const group = candidates.get(key) ?? [];
|
|
15700
|
+
group.push({ node, named: isModuleLevelNamedSchema(node) });
|
|
15701
|
+
candidates.set(key, group);
|
|
15702
|
+
},
|
|
15703
|
+
"Program:exit"() {
|
|
15704
|
+
for (const group of candidates.values()) {
|
|
15705
|
+
if (group.length < 2) continue;
|
|
15706
|
+
const canonical = group.find((candidate2) => candidate2.named);
|
|
15707
|
+
for (const candidate2 of group) {
|
|
15708
|
+
if (candidate2 === canonical) continue;
|
|
15709
|
+
context.report({ node: candidate2.node, messageId: "shareEnumDomain" });
|
|
15710
|
+
}
|
|
15711
|
+
}
|
|
15575
15712
|
}
|
|
15576
15713
|
};
|
|
15577
15714
|
}
|
|
@@ -15646,35 +15783,6 @@ var prefer_switch_for_repeated_equality_default = createRule({
|
|
|
15646
15783
|
var import_utils85 = require("@typescript-eslint/utils");
|
|
15647
15784
|
var import_fs = require("fs");
|
|
15648
15785
|
var import_path = require("path");
|
|
15649
|
-
|
|
15650
|
-
// src/rules/_tailwind.ts
|
|
15651
|
-
var tailwindVariantPrefix = (token) => {
|
|
15652
|
-
let bracketDepth = 0;
|
|
15653
|
-
let parenthesisDepth = 0;
|
|
15654
|
-
let escaped = false;
|
|
15655
|
-
let end = 0;
|
|
15656
|
-
for (let index = 0; index < token.length; index += 1) {
|
|
15657
|
-
const character = token[index];
|
|
15658
|
-
if (escaped) {
|
|
15659
|
-
escaped = false;
|
|
15660
|
-
continue;
|
|
15661
|
-
}
|
|
15662
|
-
if (character === "\\") {
|
|
15663
|
-
escaped = true;
|
|
15664
|
-
continue;
|
|
15665
|
-
}
|
|
15666
|
-
if (character === "[") bracketDepth += 1;
|
|
15667
|
-
else if (character === "]") bracketDepth = Math.max(0, bracketDepth - 1);
|
|
15668
|
-
else if (character === "(") parenthesisDepth += 1;
|
|
15669
|
-
else if (character === ")") parenthesisDepth = Math.max(0, parenthesisDepth - 1);
|
|
15670
|
-
else if (character === ":" && bracketDepth === 0 && parenthesisDepth === 0) end = index + 1;
|
|
15671
|
-
}
|
|
15672
|
-
return token.slice(0, end);
|
|
15673
|
-
};
|
|
15674
|
-
var tailwindBase = (token) => token.slice(tailwindVariantPrefix(token).length).replace(/^!/, "");
|
|
15675
|
-
var classTokens = (value) => value.split(/\s+/).filter(Boolean);
|
|
15676
|
-
|
|
15677
|
-
// src/rules/prefer-semantic-colors.ts
|
|
15678
15786
|
var PREFER_SEMANTIC_COLORS_DOCUMENTATION = {
|
|
15679
15787
|
summary: "Enforce semantic color tokens over raw Tailwind palette classes, arbitrary color values, and inline color literals.",
|
|
15680
15788
|
rationale: "Semantic tokens keep themes and product meaning consistent while raw colors couple components to a palette value.",
|
|
@@ -16728,7 +16836,7 @@ var TEST_MODIFIERS4 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "s
|
|
|
16728
16836
|
var EXPECT_MODIFIERS = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
|
|
16729
16837
|
var SNAPSHOT_MATCHERS = /snapshot/iu;
|
|
16730
16838
|
var MIN_CASES2 = 3;
|
|
16731
|
-
function
|
|
16839
|
+
function staticMemberName6(node) {
|
|
16732
16840
|
if (!node.computed && node.property.type === import_utils88.AST_NODE_TYPES.Identifier) return node.property.name;
|
|
16733
16841
|
if (node.computed && node.property.type === import_utils88.AST_NODE_TYPES.Literal && typeof node.property.value === "string") return node.property.value;
|
|
16734
16842
|
return null;
|
|
@@ -16755,7 +16863,7 @@ function isDirectTestCallback2(node, context) {
|
|
|
16755
16863
|
function testRoot2(callee) {
|
|
16756
16864
|
if (callee.type === import_utils88.AST_NODE_TYPES.Identifier) return callee;
|
|
16757
16865
|
if (callee.type !== import_utils88.AST_NODE_TYPES.MemberExpression) return null;
|
|
16758
|
-
const modifier =
|
|
16866
|
+
const modifier = staticMemberName6(callee);
|
|
16759
16867
|
return modifier !== null && TEST_MODIFIERS4.has(modifier) ? testRoot2(callee.object) : null;
|
|
16760
16868
|
}
|
|
16761
16869
|
function isStatic(node) {
|
|
@@ -16819,7 +16927,7 @@ function expectCallFromMatcher(node) {
|
|
|
16819
16927
|
const modifiers = [];
|
|
16820
16928
|
let receiver = node.object;
|
|
16821
16929
|
while (receiver.type === import_utils88.AST_NODE_TYPES.MemberExpression) {
|
|
16822
|
-
const modifier =
|
|
16930
|
+
const modifier = staticMemberName6(receiver);
|
|
16823
16931
|
if (modifier === null || !EXPECT_MODIFIERS.has(modifier)) return null;
|
|
16824
16932
|
modifiers.unshift(modifier);
|
|
16825
16933
|
receiver = receiver.object;
|
|
@@ -18407,8 +18515,8 @@ var FLUENT_BUILDER_NAME_RE = /Builder$/;
|
|
|
18407
18515
|
var FLUENT_RESULT_TYPE_RE = /(?:Builder|Base|Query|Without)(?:\W|$)/;
|
|
18408
18516
|
var ROUTER_FACTORY_NAME = "Router";
|
|
18409
18517
|
var FRAMEWORK_HTTP_TYPES = /* @__PURE__ */ new Set(["Request", "Response", "NextFunction"]);
|
|
18410
|
-
var
|
|
18411
|
-
var
|
|
18518
|
+
var STORAGE_ASSIGNMENT_OPERATORS2 = /* @__PURE__ */ new Set(["=", "&&=", "??=", "||="]);
|
|
18519
|
+
var staticMemberName7 = (member) => {
|
|
18412
18520
|
if (member.property.type === import_utils94.AST_NODE_TYPES.PrivateIdentifier) return `#${member.property.name}`;
|
|
18413
18521
|
if (!member.computed && member.property.type === import_utils94.AST_NODE_TYPES.Identifier) return member.property.name;
|
|
18414
18522
|
return member.computed && member.property.type === import_utils94.AST_NODE_TYPES.Literal && typeof member.property.value === "string" ? member.property.value : null;
|
|
@@ -18500,8 +18608,8 @@ var readConstructor = (ctor, declared, typeParameters) => {
|
|
|
18500
18608
|
if (current === void 0) break;
|
|
18501
18609
|
if (current.type === import_utils94.AST_NODE_TYPES.ArrowFunctionExpression || current.type === import_utils94.AST_NODE_TYPES.FunctionExpression || current.type === import_utils94.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils94.AST_NODE_TYPES.ClassExpression || current.type === import_utils94.AST_NODE_TYPES.ClassDeclaration) continue;
|
|
18502
18610
|
const expression = current.type === import_utils94.AST_NODE_TYPES.ExpressionStatement ? current.expression : null;
|
|
18503
|
-
const storedField = expression?.type === import_utils94.AST_NODE_TYPES.AssignmentExpression && expression.left.type === import_utils94.AST_NODE_TYPES.MemberExpression && expression.left.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ?
|
|
18504
|
-
if (expression?.type !== import_utils94.AST_NODE_TYPES.AssignmentExpression || !
|
|
18611
|
+
const storedField = expression?.type === import_utils94.AST_NODE_TYPES.AssignmentExpression && expression.left.type === import_utils94.AST_NODE_TYPES.MemberExpression && expression.left.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName7(expression.left) : null;
|
|
18612
|
+
if (expression?.type !== import_utils94.AST_NODE_TYPES.AssignmentExpression || !STORAGE_ASSIGNMENT_OPERATORS2.has(expression.operator) || expression.left.type !== import_utils94.AST_NODE_TYPES.MemberExpression || expression.left.object.type !== import_utils94.AST_NODE_TYPES.ThisExpression || storedField === null) {
|
|
18505
18613
|
for (const key of Object.keys(current)) {
|
|
18506
18614
|
if (key === "parent") continue;
|
|
18507
18615
|
const value = current[key];
|
|
@@ -18525,7 +18633,7 @@ var readConstructor = (ctor, declared, typeParameters) => {
|
|
|
18525
18633
|
const fields = storedFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Set();
|
|
18526
18634
|
fields.add(storedField);
|
|
18527
18635
|
storedFieldsFrom.set(source.object.name, fields);
|
|
18528
|
-
const member =
|
|
18636
|
+
const member = staticMemberName7(source);
|
|
18529
18637
|
if (member !== null) {
|
|
18530
18638
|
const members = storedMemberFieldsFrom.get(source.object.name) ?? /* @__PURE__ */ new Map();
|
|
18531
18639
|
const memberFields = members.get(member) ?? /* @__PURE__ */ new Set();
|
|
@@ -18657,7 +18765,7 @@ var invokedInstanceField = (call) => {
|
|
|
18657
18765
|
var instanceField = (candidate2) => {
|
|
18658
18766
|
let node = candidate2;
|
|
18659
18767
|
while (node.type === import_utils94.AST_NODE_TYPES.ChainExpression || node.type === import_utils94.AST_NODE_TYPES.TSAsExpression || node.type === import_utils94.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils94.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils94.AST_NODE_TYPES.TSTypeAssertion) node = node.expression;
|
|
18660
|
-
return node.type === import_utils94.AST_NODE_TYPES.MemberExpression && node.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ?
|
|
18768
|
+
return node.type === import_utils94.AST_NODE_TYPES.MemberExpression && node.object.type === import_utils94.AST_NODE_TYPES.ThisExpression ? staticMemberName7(node) : null;
|
|
18661
18769
|
};
|
|
18662
18770
|
var behaviorallyInvokedFields = (body2) => {
|
|
18663
18771
|
const invoked = /* @__PURE__ */ new Set();
|
|
@@ -19247,14 +19355,14 @@ var REQUIRE_STATIC_NEXT_MATCHER_DOCUMENTATION = {
|
|
|
19247
19355
|
]
|
|
19248
19356
|
};
|
|
19249
19357
|
var NEXT_ENTRY_FILE = /(?:^|[/\\])(?:middleware|proxy)\.[cm]?[jt]sx?$/u;
|
|
19250
|
-
function
|
|
19358
|
+
function unwrapExpression5(node) {
|
|
19251
19359
|
if (node.type === import_utils96.AST_NODE_TYPES.TSAsExpression || node.type === import_utils96.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils96.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils96.AST_NODE_TYPES.TSTypeAssertion) {
|
|
19252
|
-
return
|
|
19360
|
+
return unwrapExpression5(node.expression);
|
|
19253
19361
|
}
|
|
19254
19362
|
return node;
|
|
19255
19363
|
}
|
|
19256
19364
|
function isStaticValue(node) {
|
|
19257
|
-
const value =
|
|
19365
|
+
const value = unwrapExpression5(node);
|
|
19258
19366
|
if (value.type === import_utils96.AST_NODE_TYPES.Literal) {
|
|
19259
19367
|
return true;
|
|
19260
19368
|
}
|
|
@@ -19274,9 +19382,8 @@ function isStaticValue(node) {
|
|
|
19274
19382
|
return false;
|
|
19275
19383
|
}
|
|
19276
19384
|
function propertyName5(property) {
|
|
19277
|
-
if (property.computed) return
|
|
19278
|
-
|
|
19279
|
-
return typeof property.key.value === "string" ? property.key.value : null;
|
|
19385
|
+
if (!property.computed && property.key.type === import_utils96.AST_NODE_TYPES.Identifier) return property.key.name;
|
|
19386
|
+
return property.key.type === import_utils96.AST_NODE_TYPES.Literal && typeof property.key.value === "string" ? property.key.value : null;
|
|
19280
19387
|
}
|
|
19281
19388
|
var require_static_next_matcher_default = createRule({
|
|
19282
19389
|
name: "require-static-next-matcher",
|
|
@@ -19305,7 +19412,7 @@ var require_static_next_matcher_default = createRule({
|
|
|
19305
19412
|
if (declaration.id.type !== import_utils96.AST_NODE_TYPES.Identifier || declaration.id.name !== "config" || declaration.init === null) {
|
|
19306
19413
|
continue;
|
|
19307
19414
|
}
|
|
19308
|
-
const config =
|
|
19415
|
+
const config = unwrapExpression5(declaration.init);
|
|
19309
19416
|
if (config.type !== import_utils96.AST_NODE_TYPES.ObjectExpression) {
|
|
19310
19417
|
continue;
|
|
19311
19418
|
}
|
|
@@ -19326,38 +19433,44 @@ var require_static_next_matcher_default = createRule({
|
|
|
19326
19433
|
// src/rules/require-use-form-default-values.ts
|
|
19327
19434
|
var import_utils97 = require("@typescript-eslint/utils");
|
|
19328
19435
|
var REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION = {
|
|
19329
|
-
summary: "
|
|
19330
|
-
rationale: "
|
|
19331
|
-
remediation: "Provide
|
|
19436
|
+
summary: "Require explicit form-level initialization or a field default for directly bound Controller fields.",
|
|
19437
|
+
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.",
|
|
19438
|
+
remediation: "Provide a non-undefined defaultValues or values option to useForm, or a non-undefined defaultValue on the associated Controller/useController field.",
|
|
19332
19439
|
category: "correctness",
|
|
19333
19440
|
limitations: [
|
|
19334
|
-
"
|
|
19441
|
+
"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.",
|
|
19442
|
+
"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
19443
|
],
|
|
19336
19444
|
examples: [
|
|
19337
19445
|
{
|
|
19338
|
-
id: "
|
|
19339
|
-
title: "
|
|
19446
|
+
id: "controlled-field-with-form-defaults",
|
|
19447
|
+
title: "Initialize controlled fields at form level",
|
|
19340
19448
|
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 <
|
|
19449
|
+
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
19450
|
focusPath: "profile-form.tsx",
|
|
19343
19451
|
expectedCount: 0,
|
|
19344
19452
|
public: true
|
|
19345
19453
|
},
|
|
19346
19454
|
{
|
|
19347
|
-
id: "
|
|
19348
|
-
title: "Do not leave
|
|
19455
|
+
id: "controlled-field-without-default",
|
|
19456
|
+
title: "Do not leave a controlled field uninitialized",
|
|
19349
19457
|
outcome: "match",
|
|
19350
|
-
files: [{ path: "profile-form.tsx", source: "'use client'; import { useForm } from 'react-hook-form'; function ProfileForm() { const form = useForm(
|
|
19458
|
+
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
19459
|
focusPath: "profile-form.tsx",
|
|
19352
19460
|
expectedCount: 1,
|
|
19353
19461
|
public: true
|
|
19354
19462
|
}
|
|
19355
19463
|
]
|
|
19356
19464
|
};
|
|
19357
|
-
function
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
);
|
|
19465
|
+
function staticPropertyName(property) {
|
|
19466
|
+
if (property.computed) return null;
|
|
19467
|
+
if (property.key.type === import_utils97.AST_NODE_TYPES.Identifier) return property.key.name;
|
|
19468
|
+
if (property.key.type === import_utils97.AST_NODE_TYPES.Literal && typeof property.key.value === "string") return property.key.value;
|
|
19469
|
+
return null;
|
|
19470
|
+
}
|
|
19471
|
+
function unwrapExpression6(node) {
|
|
19472
|
+
if (node.type === import_utils97.AST_NODE_TYPES.TSAsExpression || node.type === import_utils97.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils97.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils97.AST_NODE_TYPES.TSTypeAssertion) return unwrapExpression6(node.expression);
|
|
19473
|
+
return node;
|
|
19361
19474
|
}
|
|
19362
19475
|
var require_use_form_default_values_default = createRule({
|
|
19363
19476
|
name: "require-use-form-default-values",
|
|
@@ -19366,27 +19479,127 @@ var require_use_form_default_values_default = createRule({
|
|
|
19366
19479
|
type: "problem",
|
|
19367
19480
|
docs: { description: REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION.summary },
|
|
19368
19481
|
schema: [],
|
|
19369
|
-
messages: {
|
|
19370
|
-
requireUseFormDefaultValues: "Provide defaultValues or reactive values to useForm so controlled fields have an explicit initial shape."
|
|
19371
|
-
}
|
|
19482
|
+
messages: { requireUseFormDefaultValues: "Add non-undefined defaultValues/values to the associated useForm call or non-undefined defaultValue to this field." }
|
|
19372
19483
|
},
|
|
19373
19484
|
defaultOptions: [],
|
|
19374
19485
|
create(context) {
|
|
19375
|
-
const
|
|
19486
|
+
const imported = /* @__PURE__ */ new Map();
|
|
19487
|
+
const uninitializedForms = /* @__PURE__ */ new Set();
|
|
19488
|
+
const uninitializedControls = /* @__PURE__ */ new Set();
|
|
19489
|
+
const bindingNamed = (node, name) => import_utils97.ASTUtils.findVariable(context.sourceCode.getScope(node), name);
|
|
19490
|
+
const bindingOf = (node) => bindingNamed(node, node.name);
|
|
19491
|
+
const stable = (variable) => !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
|
|
19492
|
+
const importedKind = (node) => {
|
|
19493
|
+
const variable = bindingOf(node);
|
|
19494
|
+
return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
|
|
19495
|
+
};
|
|
19496
|
+
const importedJsxKind = (node) => {
|
|
19497
|
+
const variable = bindingNamed(node, node.name);
|
|
19498
|
+
return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
|
|
19499
|
+
};
|
|
19500
|
+
const isDefinitelyUndefined = (node) => {
|
|
19501
|
+
const value = unwrapExpression6(node);
|
|
19502
|
+
return value.type === import_utils97.AST_NODE_TYPES.UnaryExpression && value.operator === "void" || value.type === import_utils97.AST_NODE_TYPES.Identifier && value.name === "undefined" && (bindingOf(value)?.defs.length ?? 0) === 0;
|
|
19503
|
+
};
|
|
19504
|
+
const initializationState = (node) => {
|
|
19505
|
+
if (node === void 0) return "uninitialized";
|
|
19506
|
+
if (node.type !== import_utils97.AST_NODE_TYPES.ObjectExpression) return "unknown";
|
|
19507
|
+
const initialization = /* @__PURE__ */ new Map();
|
|
19508
|
+
for (const property of node.properties) {
|
|
19509
|
+
if (property.type === import_utils97.AST_NODE_TYPES.SpreadElement || property.computed) return "unknown";
|
|
19510
|
+
if (property.type !== import_utils97.AST_NODE_TYPES.Property) continue;
|
|
19511
|
+
const name = staticPropertyName(property);
|
|
19512
|
+
if (name === "defaultValues" || name === "values") initialization.set(name, !isDefinitelyUndefined(property.value));
|
|
19513
|
+
}
|
|
19514
|
+
return [...initialization.values()].some(Boolean) ? "initialized" : "uninitialized";
|
|
19515
|
+
};
|
|
19516
|
+
const uninitializedUseFormCall = (node) => {
|
|
19517
|
+
if (node.type !== import_utils97.AST_NODE_TYPES.CallExpression || node.callee.type !== import_utils97.AST_NODE_TYPES.Identifier || importedKind(node.callee) !== "useForm") return false;
|
|
19518
|
+
const options = node.arguments[0];
|
|
19519
|
+
return options?.type !== import_utils97.AST_NODE_TYPES.SpreadElement && initializationState(options) === "uninitialized";
|
|
19520
|
+
};
|
|
19521
|
+
const isUninitializedForm = (node) => {
|
|
19522
|
+
if (node.type !== import_utils97.AST_NODE_TYPES.Identifier) return false;
|
|
19523
|
+
const variable = bindingOf(node);
|
|
19524
|
+
return variable !== null && stable(variable) && uninitializedForms.has(variable);
|
|
19525
|
+
};
|
|
19526
|
+
const isUninitializedControl = (node) => {
|
|
19527
|
+
if (node.type === import_utils97.AST_NODE_TYPES.Identifier) {
|
|
19528
|
+
const variable = bindingOf(node);
|
|
19529
|
+
return variable !== null && stable(variable) && uninitializedControls.has(variable);
|
|
19530
|
+
}
|
|
19531
|
+
return node.type === import_utils97.AST_NODE_TYPES.MemberExpression && !node.computed && node.property.type === import_utils97.AST_NODE_TYPES.Identifier && node.property.name === "control" && isUninitializedForm(node.object);
|
|
19532
|
+
};
|
|
19533
|
+
const fieldOptionsNeedDefault = (node) => {
|
|
19534
|
+
if (node?.type !== import_utils97.AST_NODE_TYPES.ObjectExpression) return false;
|
|
19535
|
+
let control = null;
|
|
19536
|
+
let hasDefault = false;
|
|
19537
|
+
for (const property of node.properties) {
|
|
19538
|
+
if (property.type === import_utils97.AST_NODE_TYPES.SpreadElement || property.computed) return false;
|
|
19539
|
+
if (property.type !== import_utils97.AST_NODE_TYPES.Property) continue;
|
|
19540
|
+
const name = staticPropertyName(property);
|
|
19541
|
+
if (name === "control") control = property.value;
|
|
19542
|
+
if (name === "defaultValue") hasDefault = !isDefinitelyUndefined(property.value);
|
|
19543
|
+
}
|
|
19544
|
+
return control !== null && isUninitializedControl(control) && !hasDefault;
|
|
19545
|
+
};
|
|
19546
|
+
const jsxAttribute = (node, name) => {
|
|
19547
|
+
let result = null;
|
|
19548
|
+
for (const attribute of node.attributes) {
|
|
19549
|
+
if (attribute.type === import_utils97.AST_NODE_TYPES.JSXSpreadAttribute) return null;
|
|
19550
|
+
if (attribute.name.type === import_utils97.AST_NODE_TYPES.JSXIdentifier && attribute.name.name === name) result = attribute;
|
|
19551
|
+
}
|
|
19552
|
+
return result;
|
|
19553
|
+
};
|
|
19554
|
+
const trackDestructuredControl = (pattern) => {
|
|
19555
|
+
for (const property of pattern.properties) {
|
|
19556
|
+
if (property.type !== import_utils97.AST_NODE_TYPES.Property || staticPropertyName(property) !== "control" || property.value.type !== import_utils97.AST_NODE_TYPES.Identifier) continue;
|
|
19557
|
+
const variable = bindingOf(property.value);
|
|
19558
|
+
if (variable !== null) uninitializedControls.add(variable);
|
|
19559
|
+
}
|
|
19560
|
+
};
|
|
19376
19561
|
return {
|
|
19377
19562
|
ImportDeclaration(node) {
|
|
19378
19563
|
if (node.source.value !== "react-hook-form") return;
|
|
19379
19564
|
for (const specifier of node.specifiers) {
|
|
19380
|
-
if (specifier.type !==
|
|
19381
|
-
const
|
|
19382
|
-
if (
|
|
19565
|
+
if (specifier.type !== import_utils97.AST_NODE_TYPES.ImportSpecifier) continue;
|
|
19566
|
+
const name = specifier.imported.type === import_utils97.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
19567
|
+
if (name !== "Controller" && name !== "useController" && name !== "useForm") continue;
|
|
19568
|
+
const variable = bindingOf(specifier.local);
|
|
19569
|
+
if (variable !== null) imported.set(variable, name);
|
|
19570
|
+
}
|
|
19571
|
+
},
|
|
19572
|
+
VariableDeclarator(node) {
|
|
19573
|
+
if (node.init === null) return;
|
|
19574
|
+
if (uninitializedUseFormCall(node.init)) {
|
|
19575
|
+
if (node.id.type === import_utils97.AST_NODE_TYPES.Identifier) {
|
|
19576
|
+
const variable = bindingOf(node.id);
|
|
19577
|
+
if (variable !== null) uninitializedForms.add(variable);
|
|
19578
|
+
} else if (node.id.type === import_utils97.AST_NODE_TYPES.ObjectPattern) {
|
|
19579
|
+
trackDestructuredControl(node.id);
|
|
19580
|
+
}
|
|
19581
|
+
return;
|
|
19582
|
+
}
|
|
19583
|
+
if (node.id.type === import_utils97.AST_NODE_TYPES.ObjectPattern && isUninitializedForm(node.init)) {
|
|
19584
|
+
trackDestructuredControl(node.id);
|
|
19585
|
+
return;
|
|
19586
|
+
}
|
|
19587
|
+
if (node.id.type === import_utils97.AST_NODE_TYPES.Identifier && node.init.type === import_utils97.AST_NODE_TYPES.MemberExpression && !node.init.computed && node.init.property.type === import_utils97.AST_NODE_TYPES.Identifier && node.init.property.name === "control" && isUninitializedForm(node.init.object)) {
|
|
19588
|
+
const variable = bindingOf(node.id);
|
|
19589
|
+
if (variable !== null) uninitializedControls.add(variable);
|
|
19383
19590
|
}
|
|
19384
19591
|
},
|
|
19385
19592
|
CallExpression(node) {
|
|
19386
|
-
if (node.callee.type !==
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19593
|
+
if (node.callee.type !== import_utils97.AST_NODE_TYPES.Identifier || importedKind(node.callee) !== "useController" || !fieldOptionsNeedDefault(node.arguments[0]?.type === import_utils97.AST_NODE_TYPES.SpreadElement ? void 0 : node.arguments[0])) return;
|
|
19594
|
+
context.report({ node, messageId: "requireUseFormDefaultValues" });
|
|
19595
|
+
},
|
|
19596
|
+
JSXOpeningElement(node) {
|
|
19597
|
+
if (node.name.type !== import_utils97.AST_NODE_TYPES.JSXIdentifier || importedJsxKind(node.name) !== "Controller") return;
|
|
19598
|
+
if (node.attributes.some((attribute) => attribute.type === import_utils97.AST_NODE_TYPES.JSXSpreadAttribute)) return;
|
|
19599
|
+
const control = jsxAttribute(node, "control");
|
|
19600
|
+
if (control?.value?.type !== import_utils97.AST_NODE_TYPES.JSXExpressionContainer || control.value.expression.type === import_utils97.AST_NODE_TYPES.JSXEmptyExpression || !isUninitializedControl(control.value.expression)) return;
|
|
19601
|
+
const defaultValue = jsxAttribute(node, "defaultValue");
|
|
19602
|
+
if (defaultValue !== null && (defaultValue.value === null || defaultValue.value.type !== import_utils97.AST_NODE_TYPES.JSXExpressionContainer || defaultValue.value.expression.type !== import_utils97.AST_NODE_TYPES.JSXEmptyExpression && !isDefinitelyUndefined(defaultValue.value.expression))) return;
|
|
19390
19603
|
context.report({ node, messageId: "requireUseFormDefaultValues" });
|
|
19391
19604
|
}
|
|
19392
19605
|
};
|
|
@@ -20260,7 +20473,7 @@ var stepdown_default = createRule({
|
|
|
20260
20473
|
|
|
20261
20474
|
// src/rules/source-coupled-test.ts
|
|
20262
20475
|
var import_utils102 = require("@typescript-eslint/utils");
|
|
20263
|
-
var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|py|[cm]?[jt]s)$/iu;
|
|
20476
|
+
var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc?|toml|py|[cm]?[jt]s)$/iu;
|
|
20264
20477
|
var FS_MODULES = /* @__PURE__ */ new Set(["fs", "node:fs", "fs/promises", "node:fs/promises"]);
|
|
20265
20478
|
var FS_READERS = /* @__PURE__ */ new Set(["readFile", "readFileSync"]);
|
|
20266
20479
|
var TEXT_TRANSFORMS = /* @__PURE__ */ new Set([
|
|
@@ -20297,6 +20510,8 @@ var EXPECT_MATCHERS = /* @__PURE__ */ new Set([
|
|
|
20297
20510
|
]);
|
|
20298
20511
|
var EXPECT_MODIFIERS2 = /* @__PURE__ */ new Set(["not", "rejects", "resolves"]);
|
|
20299
20512
|
var ASSERT_MATCHERS = /* @__PURE__ */ new Set(["deepEqual", "doesNotMatch", "equal", "match", "notDeepEqual", "notEqual", "notStrictEqual", "ok", "strictEqual"]);
|
|
20513
|
+
var EXPECT_MODULES = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "vitest"]);
|
|
20514
|
+
var ASSERT_MODULES = /* @__PURE__ */ new Set(["assert", "assert/strict", "node:assert", "node:assert/strict"]);
|
|
20300
20515
|
var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
20301
20516
|
summary: "Disallow raw repository source text as a test oracle; parse or execute the artifact instead.",
|
|
20302
20517
|
rationale: "Substring and regex checks can pass on comments or unreachable configuration and fail after behavior-preserving formatting changes.",
|
|
@@ -20304,6 +20519,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
20304
20519
|
category: "testing",
|
|
20305
20520
|
limitations: [
|
|
20306
20521
|
"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.",
|
|
20522
|
+
"Assertion roots are scope-resolved for supported test runners and Node assert imports; local or unknown helpers with assertion-like names are not inferred.",
|
|
20307
20523
|
"When raw representation is genuinely the contract (for example a golden or compatibility sentinel), use an exact line suppression with the reason."
|
|
20308
20524
|
],
|
|
20309
20525
|
examples: [
|
|
@@ -20327,7 +20543,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
20327
20543
|
}
|
|
20328
20544
|
]
|
|
20329
20545
|
};
|
|
20330
|
-
function
|
|
20546
|
+
function staticMemberName8(node) {
|
|
20331
20547
|
if (!node.computed && node.property.type === import_utils102.AST_NODE_TYPES.Identifier) return node.property.name;
|
|
20332
20548
|
if (node.computed && node.property.type === import_utils102.AST_NODE_TYPES.Literal && typeof node.property.value === "string") return node.property.value;
|
|
20333
20549
|
return null;
|
|
@@ -20377,6 +20593,27 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20377
20593
|
const reportedOrigins = /* @__PURE__ */ new Set();
|
|
20378
20594
|
const currentScope = () => scopes.at(-1) ?? scopes[0];
|
|
20379
20595
|
const bindingOf = (node) => import_utils102.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
|
|
20596
|
+
const assertionKind = (node) => {
|
|
20597
|
+
const binding = bindingOf(node);
|
|
20598
|
+
if (binding === null || binding.defs.length === 0) {
|
|
20599
|
+
return node.name === "assert" || node.name === "expect" ? node.name : null;
|
|
20600
|
+
}
|
|
20601
|
+
for (const definition of binding.defs) {
|
|
20602
|
+
const specifier = definition.node;
|
|
20603
|
+
if (specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier && specifier.type !== import_utils102.AST_NODE_TYPES.ImportDefaultSpecifier && specifier.type !== import_utils102.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.parent.type !== import_utils102.AST_NODE_TYPES.ImportDeclaration) continue;
|
|
20604
|
+
const source = importSource(specifier.parent);
|
|
20605
|
+
if (source !== null && ASSERT_MODULES.has(source)) {
|
|
20606
|
+
if (specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier) return "assert";
|
|
20607
|
+
const imported2 = specifier.imported.type === import_utils102.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
20608
|
+
if (imported2 === "strict" || ASSERT_MATCHERS.has(imported2)) return "assert";
|
|
20609
|
+
continue;
|
|
20610
|
+
}
|
|
20611
|
+
if (source === null || !EXPECT_MODULES.has(source) || specifier.type !== import_utils102.AST_NODE_TYPES.ImportSpecifier) continue;
|
|
20612
|
+
const imported = specifier.imported.type === import_utils102.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
|
|
20613
|
+
if (imported === "assert" || imported === "expect") return imported;
|
|
20614
|
+
}
|
|
20615
|
+
return null;
|
|
20616
|
+
};
|
|
20380
20617
|
const visible = (kind, node) => {
|
|
20381
20618
|
const name2 = bindingOf(node);
|
|
20382
20619
|
if (name2 === null || name2.references.some((reference) => reference.isWrite() && reference.init !== true)) return false;
|
|
@@ -20417,7 +20654,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20417
20654
|
return visible("fsReaders", callee) && sourcePath(current.arguments[0]);
|
|
20418
20655
|
}
|
|
20419
20656
|
if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return false;
|
|
20420
|
-
const name2 =
|
|
20657
|
+
const name2 = staticMemberName8(callee);
|
|
20421
20658
|
const object = unwrap7(callee.object);
|
|
20422
20659
|
return name2 !== null && FS_READERS.has(name2) && object.type === import_utils102.AST_NODE_TYPES.Identifier && visible("fsObjects", object) && sourcePath(current.arguments[0]);
|
|
20423
20660
|
};
|
|
@@ -20426,11 +20663,11 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20426
20663
|
if (current.type === import_utils102.AST_NODE_TYPES.Identifier) return visibleRawOrigins(current);
|
|
20427
20664
|
if (rawRead(current)) return /* @__PURE__ */ new Set([`${current.range[0]}:${current.range[1]}`]);
|
|
20428
20665
|
if (current.type === import_utils102.AST_NODE_TYPES.BinaryExpression && current.operator === "+") return /* @__PURE__ */ new Set([...rawOrigins(current.left), ...rawOrigins(current.right)]);
|
|
20429
|
-
if (current.type === import_utils102.AST_NODE_TYPES.MemberExpression &&
|
|
20666
|
+
if (current.type === import_utils102.AST_NODE_TYPES.MemberExpression && staticMemberName8(current) === "length") return rawOrigins(current.object);
|
|
20430
20667
|
if (current.type !== import_utils102.AST_NODE_TYPES.CallExpression) return /* @__PURE__ */ new Set();
|
|
20431
20668
|
const callee = unwrap7(current.callee);
|
|
20432
20669
|
if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20433
|
-
const name2 =
|
|
20670
|
+
const name2 = staticMemberName8(callee);
|
|
20434
20671
|
return name2 !== null && TEXT_TRANSFORMS.has(name2) ? rawOrigins(callee.object) : /* @__PURE__ */ new Set();
|
|
20435
20672
|
};
|
|
20436
20673
|
const evidenceOrigins = (node) => {
|
|
@@ -20442,26 +20679,26 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
20442
20679
|
if (current.type !== import_utils102.AST_NODE_TYPES.CallExpression) return /* @__PURE__ */ new Set();
|
|
20443
20680
|
const callee = unwrap7(current.callee);
|
|
20444
20681
|
if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20445
|
-
const name2 =
|
|
20682
|
+
const name2 = staticMemberName8(callee);
|
|
20446
20683
|
if (name2 !== null && TEXT_PREDICATES.has(name2)) return rawOrigins(callee.object);
|
|
20447
20684
|
if (name2 !== null && REGEXP_PREDICATES.has(name2)) return new Set(current.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...rawOrigins(argument)]));
|
|
20448
20685
|
return /* @__PURE__ */ new Set();
|
|
20449
20686
|
};
|
|
20450
20687
|
const rawAssertionOrigins = (node) => {
|
|
20451
20688
|
const callee = unwrap7(node.callee);
|
|
20452
|
-
if (callee.type === import_utils102.AST_NODE_TYPES.Identifier && callee
|
|
20689
|
+
if (callee.type === import_utils102.AST_NODE_TYPES.Identifier && assertionKind(callee) === "assert") {
|
|
20453
20690
|
return new Set(node.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20454
20691
|
}
|
|
20455
20692
|
if (callee.type !== import_utils102.AST_NODE_TYPES.MemberExpression) return /* @__PURE__ */ new Set();
|
|
20456
|
-
const matcher =
|
|
20693
|
+
const matcher = staticMemberName8(callee);
|
|
20457
20694
|
if (matcher === null) return /* @__PURE__ */ new Set();
|
|
20458
20695
|
let receiver = unwrap7(callee.object);
|
|
20459
|
-
while (receiver.type === import_utils102.AST_NODE_TYPES.MemberExpression && EXPECT_MODIFIERS2.has(
|
|
20460
|
-
if (receiver.type === import_utils102.AST_NODE_TYPES.CallExpression && receiver.callee.type === import_utils102.AST_NODE_TYPES.Identifier && receiver.callee
|
|
20696
|
+
while (receiver.type === import_utils102.AST_NODE_TYPES.MemberExpression && EXPECT_MODIFIERS2.has(staticMemberName8(receiver) ?? "")) receiver = unwrap7(receiver.object);
|
|
20697
|
+
if (receiver.type === import_utils102.AST_NODE_TYPES.CallExpression && receiver.callee.type === import_utils102.AST_NODE_TYPES.Identifier && assertionKind(receiver.callee) === "expect") {
|
|
20461
20698
|
if (!EXPECT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
|
|
20462
20699
|
return new Set([...receiver.arguments, ...node.arguments].flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20463
20700
|
}
|
|
20464
|
-
if (receiver.type !== import_utils102.AST_NODE_TYPES.Identifier || receiver
|
|
20701
|
+
if (receiver.type !== import_utils102.AST_NODE_TYPES.Identifier || assertionKind(receiver) !== "assert" || !ASSERT_MATCHERS.has(matcher)) return /* @__PURE__ */ new Set();
|
|
20465
20702
|
return new Set(node.arguments.flatMap((argument) => argument.type === import_utils102.AST_NODE_TYPES.SpreadElement ? [] : [...evidenceOrigins(argument)]));
|
|
20466
20703
|
};
|
|
20467
20704
|
const declare = (node, state) => {
|
|
@@ -20555,13 +20792,13 @@ var source_coupled_test_default = createSourceCoupledRule(
|
|
|
20555
20792
|
// src/rules/sole-export-matches-filename.ts
|
|
20556
20793
|
var import_utils103 = require("@typescript-eslint/utils");
|
|
20557
20794
|
var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
20558
|
-
summary: "
|
|
20795
|
+
summary: "Make a module filename reflect its sole named public runtime export.",
|
|
20559
20796
|
rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
|
|
20560
|
-
remediation: "
|
|
20797
|
+
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.",
|
|
20561
20798
|
category: "maintainability",
|
|
20562
20799
|
limitations: [
|
|
20563
20800
|
"Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, and re-exports are excluded.",
|
|
20564
|
-
"The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker.",
|
|
20801
|
+
"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.",
|
|
20565
20802
|
"Exported destructuring patterns are excluded rather than undercounted as public exports."
|
|
20566
20803
|
],
|
|
20567
20804
|
examples: [
|
|
@@ -20587,6 +20824,23 @@ var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
|
|
|
20587
20824
|
"util",
|
|
20588
20825
|
"utils"
|
|
20589
20826
|
]);
|
|
20827
|
+
var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
|
|
20828
|
+
"adapter",
|
|
20829
|
+
"client",
|
|
20830
|
+
"config",
|
|
20831
|
+
"controller",
|
|
20832
|
+
"factory",
|
|
20833
|
+
"handler",
|
|
20834
|
+
"manager",
|
|
20835
|
+
"provider",
|
|
20836
|
+
"record",
|
|
20837
|
+
"repository",
|
|
20838
|
+
"router",
|
|
20839
|
+
"schema",
|
|
20840
|
+
"service",
|
|
20841
|
+
"store",
|
|
20842
|
+
"worker"
|
|
20843
|
+
]);
|
|
20590
20844
|
function stem3(filename) {
|
|
20591
20845
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
20592
20846
|
return base.replace(/\.[cm]?[jt]sx?$/u, "").split(".")[0] ?? "";
|
|
@@ -20594,6 +20848,25 @@ function stem3(filename) {
|
|
|
20594
20848
|
function kebabCase(name) {
|
|
20595
20849
|
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();
|
|
20596
20850
|
}
|
|
20851
|
+
function reflectsExportName(fileStem, exportedStem) {
|
|
20852
|
+
if (fileStem.startsWith("_")) return false;
|
|
20853
|
+
const visibleFileStem = fileStem.toLowerCase();
|
|
20854
|
+
const fileTokens = visibleFileStem.split("-").filter(Boolean);
|
|
20855
|
+
const exportTokens = exportedStem.split("-").filter(Boolean);
|
|
20856
|
+
if (fileTokens.length === 0 || exportTokens.length === 0) return false;
|
|
20857
|
+
if (fileTokens.length === 1) {
|
|
20858
|
+
const [token] = fileTokens;
|
|
20859
|
+
return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
|
|
20860
|
+
}
|
|
20861
|
+
const compactFile = fileTokens.join("");
|
|
20862
|
+
if (compactFile.length < 6) return false;
|
|
20863
|
+
const boundaryPhrases = /* @__PURE__ */ new Set();
|
|
20864
|
+
for (let index = 1; index <= exportTokens.length; index += 1) {
|
|
20865
|
+
boundaryPhrases.add(exportTokens.slice(0, index).join(""));
|
|
20866
|
+
boundaryPhrases.add(exportTokens.slice(-index).join(""));
|
|
20867
|
+
}
|
|
20868
|
+
return boundaryPhrases.has(compactFile);
|
|
20869
|
+
}
|
|
20597
20870
|
function declarationExport(statement) {
|
|
20598
20871
|
const declaration = statement.declaration;
|
|
20599
20872
|
if (declaration === null || declaration.declare === true) return [];
|
|
@@ -20608,7 +20881,7 @@ var sole_export_matches_filename_default = createRule({
|
|
|
20608
20881
|
documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
|
|
20609
20882
|
meta: {
|
|
20610
20883
|
type: "suggestion",
|
|
20611
|
-
docs: { description:
|
|
20884
|
+
docs: { description: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION.summary },
|
|
20612
20885
|
schema: [],
|
|
20613
20886
|
messages: {
|
|
20614
20887
|
matchSoleExport: "This module's sole runtime export is `{{exported}}`; rename the file stem to `{{expected}}`."
|
|
@@ -20665,7 +20938,7 @@ var sole_export_matches_filename_default = createRule({
|
|
|
20665
20938
|
const exportedStem = kebabCase(only.name);
|
|
20666
20939
|
if (exportedStem === "") return;
|
|
20667
20940
|
const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
|
|
20668
|
-
if (expected === fileStem.toLowerCase()) return;
|
|
20941
|
+
if (expected === fileStem.toLowerCase() || reflectsExportName(fileStem, exportedStem)) return;
|
|
20669
20942
|
context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
|
|
20670
20943
|
}
|
|
20671
20944
|
};
|
|
@@ -20880,7 +21153,7 @@ var chainMemberNames2 = (node) => {
|
|
|
20880
21153
|
names.reverse();
|
|
20881
21154
|
return names;
|
|
20882
21155
|
};
|
|
20883
|
-
var
|
|
21156
|
+
var unwrapExpression7 = (node) => {
|
|
20884
21157
|
let current = node;
|
|
20885
21158
|
while (current.type === import_utils104.AST_NODE_TYPES.TSAsExpression || current.type === import_utils104.AST_NODE_TYPES.TSSatisfiesExpression || current.type === import_utils104.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils104.AST_NODE_TYPES.TSTypeAssertion) {
|
|
20886
21159
|
current = current.expression;
|
|
@@ -20931,7 +21204,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
20931
21204
|
return binding !== null && schemaBindings.has(binding);
|
|
20932
21205
|
}
|
|
20933
21206
|
function isConfirmedSchema(expression) {
|
|
20934
|
-
const init =
|
|
21207
|
+
const init = unwrapExpression7(expression);
|
|
20935
21208
|
if (init.type === import_utils104.AST_NODE_TYPES.Identifier) return isSchemaBinding(init);
|
|
20936
21209
|
if (init.type !== import_utils104.AST_NODE_TYPES.CallExpression || init.callee.type !== import_utils104.AST_NODE_TYPES.MemberExpression) {
|
|
20937
21210
|
return false;
|
|
@@ -21161,7 +21434,7 @@ var RULES = {
|
|
|
21161
21434
|
};
|
|
21162
21435
|
var meta = {
|
|
21163
21436
|
name: "@sarj/eslint-plugin",
|
|
21164
|
-
version: "15.17.
|
|
21437
|
+
version: "15.17.17"
|
|
21165
21438
|
};
|
|
21166
21439
|
var APPLICATION_ONLY_RULES = [];
|
|
21167
21440
|
var LIBRARY_IMPORT_POLICY = ["error", {
|