@sarj/eslint-plugin 15.3.0 → 15.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +182 -28
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +184 -30
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -104,6 +104,7 @@ ${missing.join("\n")}`);
|
|
|
104
104
|
function publicExample(example) {
|
|
105
105
|
return {
|
|
106
106
|
id: example.id,
|
|
107
|
+
scenarioId: example.scenarioId ?? "primary",
|
|
107
108
|
title: example.title,
|
|
108
109
|
outcome: example.outcome,
|
|
109
110
|
files: example.files.map(publicFile),
|
|
@@ -148,9 +149,14 @@ function nativeSpec(config, documentation) {
|
|
|
148
149
|
const examples = [...documentation.examples ?? []];
|
|
149
150
|
examples.forEach(validateExample);
|
|
150
151
|
assertUnique(examples.map((example) => example.id), "rule example IDs");
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const publicExamples = examples.filter((example) => example.public === true);
|
|
153
|
+
const scenarios = new Set(publicExamples.map((example) => example.scenarioId ?? "primary"));
|
|
154
|
+
for (const scenario of scenarios) {
|
|
155
|
+
const pair = publicExamples.filter((example) => (example.scenarioId ?? "primary") === scenario);
|
|
156
|
+
const outcomes = new Set(pair.map((example) => example.outcome));
|
|
157
|
+
if (pair.length !== 2 || !outcomes.has("match") || !outcomes.has("no-match")) {
|
|
158
|
+
throw new TypeError(`published example scenario ${scenario} must contain both matching and non-matching cases exactly once`);
|
|
159
|
+
}
|
|
154
160
|
}
|
|
155
161
|
const messageIds = Object.keys(meta2.messages).sort();
|
|
156
162
|
const schema = optionsSchema(meta2.schema);
|
|
@@ -171,7 +177,7 @@ function nativeSpec(config, documentation) {
|
|
|
171
177
|
references,
|
|
172
178
|
since: documentation.since ?? null,
|
|
173
179
|
examples,
|
|
174
|
-
publicExamples
|
|
180
|
+
publicExamples,
|
|
175
181
|
messageIds,
|
|
176
182
|
optionsSchema: schema
|
|
177
183
|
};
|
|
@@ -191,6 +197,9 @@ function validateExample(example) {
|
|
|
191
197
|
if (!KEBAB_CASE.test(example.id)) {
|
|
192
198
|
throw new TypeError("example ID must be lowercase kebab-case");
|
|
193
199
|
}
|
|
200
|
+
if (!KEBAB_CASE.test(example.scenarioId ?? "primary")) {
|
|
201
|
+
throw new TypeError("example scenario must be lowercase kebab-case");
|
|
202
|
+
}
|
|
194
203
|
if (example.title.trim().length === 0) {
|
|
195
204
|
throw new TypeError("example title must not be empty");
|
|
196
205
|
}
|
|
@@ -598,6 +607,9 @@ function isAssertionStatement(statement) {
|
|
|
598
607
|
const expression = statement.expression;
|
|
599
608
|
return expression.type === import_utils3.AST_NODE_TYPES.CallExpression && isAssertionCall(expression);
|
|
600
609
|
}
|
|
610
|
+
function isTypeOnlyContractStatement(statement) {
|
|
611
|
+
return statement.type === import_utils3.AST_NODE_TYPES.TSTypeAliasDeclaration || statement.type === import_utils3.AST_NODE_TYPES.TSInterfaceDeclaration;
|
|
612
|
+
}
|
|
601
613
|
function normalizedLiteral(node) {
|
|
602
614
|
if ("regex" in node) {
|
|
603
615
|
return ["Literal", "regex", node.regex.pattern, node.regex.flags];
|
|
@@ -655,7 +667,7 @@ var duplicate_test_body_default = createRule({
|
|
|
655
667
|
return;
|
|
656
668
|
}
|
|
657
669
|
const body2 = candidate.body;
|
|
658
|
-
if (body2.body.type !== import_utils3.AST_NODE_TYPES.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement)) {
|
|
670
|
+
if (body2.body.type !== import_utils3.AST_NODE_TYPES.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement) || body2.body.body.some(isTypeOnlyContractStatement)) {
|
|
659
671
|
return;
|
|
660
672
|
}
|
|
661
673
|
const comments = context.sourceCode.getCommentsInside(body2.body).map((comment) => [comment.type, comment.value]);
|
|
@@ -3550,7 +3562,11 @@ var NUMBER_METHODS = /* @__PURE__ */ new Set([
|
|
|
3550
3562
|
"lt",
|
|
3551
3563
|
"lte",
|
|
3552
3564
|
"max",
|
|
3553
|
-
"min"
|
|
3565
|
+
"min",
|
|
3566
|
+
"negative",
|
|
3567
|
+
"nonnegative",
|
|
3568
|
+
"nonpositive",
|
|
3569
|
+
"positive"
|
|
3554
3570
|
]);
|
|
3555
3571
|
var LENGTH_METHODS = /* @__PURE__ */ new Set(["length", "max", "min"]);
|
|
3556
3572
|
var RESHAPING_METHODS = /* @__PURE__ */ new Set([
|
|
@@ -3687,13 +3703,29 @@ var no_impossible_zod_literal_bounds_default = createRule({
|
|
|
3687
3703
|
let upper = null;
|
|
3688
3704
|
for (const { method, node } of chain.calls) {
|
|
3689
3705
|
if (!allowed.has(method)) return null;
|
|
3690
|
-
const
|
|
3706
|
+
const semantic = method === "positive" ? { exclusive: true, lower: true, value: 0 } : method === "nonnegative" ? { exclusive: false, lower: true, value: 0 } : method === "negative" ? { exclusive: true, lower: false, value: 0 } : method === "nonpositive" ? { exclusive: false, lower: false, value: 0 } : null;
|
|
3707
|
+
const value = semantic?.value ?? finiteNumber(node.arguments[0]);
|
|
3691
3708
|
if (value === null) return null;
|
|
3692
3709
|
if (chain.kind !== "number" && (!Number.isInteger(value) || value < 0)) {
|
|
3693
3710
|
return null;
|
|
3694
3711
|
}
|
|
3695
3712
|
const label = `${method}(${String(value)})`;
|
|
3696
|
-
if (
|
|
3713
|
+
if (semantic !== null) {
|
|
3714
|
+
if (node.arguments.length !== 0) return null;
|
|
3715
|
+
if (semantic.lower) {
|
|
3716
|
+
lower = strongerLower(lower, {
|
|
3717
|
+
exclusive: semantic.exclusive,
|
|
3718
|
+
label: `${method}()`,
|
|
3719
|
+
value
|
|
3720
|
+
});
|
|
3721
|
+
} else {
|
|
3722
|
+
upper = strongerUpper(upper, {
|
|
3723
|
+
exclusive: semantic.exclusive,
|
|
3724
|
+
label: `${method}()`,
|
|
3725
|
+
value
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
} else if (method === "length") {
|
|
3697
3729
|
lower = strongerLower(lower, { exclusive: false, label, value });
|
|
3698
3730
|
upper = strongerUpper(upper, { exclusive: false, label, value });
|
|
3699
3731
|
} else if (method === "gt" || method === "gte" || method === "min") {
|
|
@@ -6806,6 +6838,13 @@ function nearestEnclosingFunction(node) {
|
|
|
6806
6838
|
}
|
|
6807
6839
|
return null;
|
|
6808
6840
|
}
|
|
6841
|
+
function isImmediatelyConsumedSleep(node) {
|
|
6842
|
+
const parent = node.parent;
|
|
6843
|
+
if (parent?.type === import_utils34.AST_NODE_TYPES.AwaitExpression || parent?.type === import_utils34.AST_NODE_TYPES.ReturnStatement || parent?.type === import_utils34.AST_NODE_TYPES.ExpressionStatement) {
|
|
6844
|
+
return true;
|
|
6845
|
+
}
|
|
6846
|
+
return parent?.type === import_utils34.AST_NODE_TYPES.ArrowFunctionExpression && parent.body === node;
|
|
6847
|
+
}
|
|
6809
6848
|
function isTestBody(fn) {
|
|
6810
6849
|
const call = fn.parent;
|
|
6811
6850
|
if (call?.type !== import_utils34.AST_NODE_TYPES.CallExpression || !call.arguments.some((argument) => argument === fn)) {
|
|
@@ -6848,6 +6887,9 @@ var no_sleep_in_test_body_default = createRule({
|
|
|
6848
6887
|
return {};
|
|
6849
6888
|
}
|
|
6850
6889
|
const report = (node) => {
|
|
6890
|
+
if (!isImmediatelyConsumedSleep(node)) {
|
|
6891
|
+
return;
|
|
6892
|
+
}
|
|
6851
6893
|
const enclosing = nearestEnclosingFunction(node);
|
|
6852
6894
|
if (enclosing === null || !isTestBody(enclosing)) {
|
|
6853
6895
|
return;
|
|
@@ -7145,6 +7187,27 @@ function isSmallStaticForLoop(node) {
|
|
|
7145
7187
|
const iterations = node.test.right.value - declaration.init.value + (node.test.operator === "<=" ? 1 : 0);
|
|
7146
7188
|
return iterations >= 0 && iterations <= 8;
|
|
7147
7189
|
}
|
|
7190
|
+
function isStringSeededReduce(node) {
|
|
7191
|
+
if (node.callee.type !== "MemberExpression" || node.callee.computed || node.callee.property.type !== "Identifier" || node.callee.property.name !== "reduce" || node.arguments.length !== 2) {
|
|
7192
|
+
return false;
|
|
7193
|
+
}
|
|
7194
|
+
const [callback, initial] = node.arguments;
|
|
7195
|
+
if (node.callee.object.type === "ArrayExpression" && node.callee.object.elements.length <= 8) {
|
|
7196
|
+
return false;
|
|
7197
|
+
}
|
|
7198
|
+
if (callback === void 0 || initial === void 0 || callback.type === "SpreadElement" || initial.type === "SpreadElement" || callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression" || callback.params[0]?.type !== "Identifier" || !isStringLiteralInit(initial)) {
|
|
7199
|
+
return false;
|
|
7200
|
+
}
|
|
7201
|
+
const accumulator = callback.params[0].name;
|
|
7202
|
+
if (callback.body.type !== "BlockStatement") {
|
|
7203
|
+
return isConcatOntoTarget(callback.body, accumulator);
|
|
7204
|
+
}
|
|
7205
|
+
if (callback.body.body.length !== 1 || callback.body.body[0]?.type !== "ReturnStatement") {
|
|
7206
|
+
return false;
|
|
7207
|
+
}
|
|
7208
|
+
const returned = callback.body.body[0].argument;
|
|
7209
|
+
return returned !== null && isConcatOntoTarget(returned, accumulator);
|
|
7210
|
+
}
|
|
7148
7211
|
var no_string_concat_in_loop_default = createRule({
|
|
7149
7212
|
name: "no-string-concat-in-loop",
|
|
7150
7213
|
documentation: noStringConcatInLoopDocumentation,
|
|
@@ -7155,7 +7218,8 @@ var no_string_concat_in_loop_default = createRule({
|
|
|
7155
7218
|
},
|
|
7156
7219
|
schema: [],
|
|
7157
7220
|
messages: {
|
|
7158
|
-
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.'
|
|
7221
|
+
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.',
|
|
7222
|
+
noStringReduce: "Avoid concatenating a growing string in `reduce` \u2014 this is O(n^2). Map the fragments and join them once instead."
|
|
7159
7223
|
}
|
|
7160
7224
|
},
|
|
7161
7225
|
defaultOptions: [],
|
|
@@ -7165,6 +7229,11 @@ var no_string_concat_in_loop_default = createRule({
|
|
|
7165
7229
|
}
|
|
7166
7230
|
const reported = /* @__PURE__ */ new WeakMap();
|
|
7167
7231
|
return {
|
|
7232
|
+
CallExpression(node) {
|
|
7233
|
+
if (isStringSeededReduce(node)) {
|
|
7234
|
+
context.report({ node, messageId: "noStringReduce" });
|
|
7235
|
+
}
|
|
7236
|
+
},
|
|
7168
7237
|
AssignmentExpression(node) {
|
|
7169
7238
|
if (node.left.type !== "Identifier") {
|
|
7170
7239
|
return;
|
|
@@ -7269,6 +7338,9 @@ function isLiteral(node) {
|
|
|
7269
7338
|
return false;
|
|
7270
7339
|
}
|
|
7271
7340
|
}
|
|
7341
|
+
function isStructuralLiteral(node) {
|
|
7342
|
+
return node.type === import_utils37.AST_NODE_TYPES.ArrayExpression || node.type === import_utils37.AST_NODE_TYPES.ObjectExpression;
|
|
7343
|
+
}
|
|
7272
7344
|
function expectOperand(callee) {
|
|
7273
7345
|
const receiver = callee.object;
|
|
7274
7346
|
if (receiver.type !== import_utils37.AST_NODE_TYPES.CallExpression || receiver.callee.type !== import_utils37.AST_NODE_TYPES.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
|
|
@@ -7325,6 +7397,9 @@ var no_tautological_expect_default = createRule({
|
|
|
7325
7397
|
if (!EQUALITY_MATCHERS.has(matcher) || node.arguments.length !== 1 || expected === void 0 || !isLiteral(expected)) {
|
|
7326
7398
|
return;
|
|
7327
7399
|
}
|
|
7400
|
+
if (matcher === "toBe" && (isStructuralLiteral(operand) || isStructuralLiteral(expected))) {
|
|
7401
|
+
return;
|
|
7402
|
+
}
|
|
7328
7403
|
if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {
|
|
7329
7404
|
return;
|
|
7330
7405
|
}
|
|
@@ -7838,7 +7913,13 @@ var no_declaration_comment_wall_default = createRule({
|
|
|
7838
7913
|
const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
|
|
7839
7914
|
if (lead !== void 0) {
|
|
7840
7915
|
const before = sourceCode.getTokenBefore(lead, { includeComments: false });
|
|
7841
|
-
if (before === null || before.loc.end.line < lead.loc.start.line)
|
|
7916
|
+
if (before === null || before.loc.end.line < lead.loc.start.line) {
|
|
7917
|
+
const previousLine = endingOn.get(lead.loc.start.line - 1);
|
|
7918
|
+
if (lead.type === import_utils40.AST_TOKEN_TYPES.Line && previousLine?.type === import_utils40.AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
|
|
7919
|
+
return void 0;
|
|
7920
|
+
}
|
|
7921
|
+
return lead;
|
|
7922
|
+
}
|
|
7842
7923
|
}
|
|
7843
7924
|
const trail = startingOn.get(member.loc.end.line);
|
|
7844
7925
|
return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
|
|
@@ -8140,7 +8221,13 @@ var no_type_member_comment_wall_default = createRule({
|
|
|
8140
8221
|
const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
|
|
8141
8222
|
if (lead !== void 0) {
|
|
8142
8223
|
const before = sourceCode.getTokenBefore(lead, { includeComments: false });
|
|
8143
|
-
if (before === null || before.loc.end.line < lead.loc.start.line)
|
|
8224
|
+
if (before === null || before.loc.end.line < lead.loc.start.line) {
|
|
8225
|
+
const previousLine = endingOn.get(lead.loc.start.line - 1);
|
|
8226
|
+
if (lead.type === import_utils42.AST_TOKEN_TYPES.Line && previousLine?.type === import_utils42.AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
|
|
8227
|
+
return void 0;
|
|
8228
|
+
}
|
|
8229
|
+
return lead;
|
|
8230
|
+
}
|
|
8144
8231
|
}
|
|
8145
8232
|
const trail = startingOn.get(member.loc.end.line);
|
|
8146
8233
|
return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
|
|
@@ -8437,6 +8524,10 @@ var no_unnecessary_use_client_default = createRule({
|
|
|
8437
8524
|
var import_utils44 = require("@typescript-eslint/utils");
|
|
8438
8525
|
var MOCK_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
8439
8526
|
"Mock",
|
|
8527
|
+
"Mocked",
|
|
8528
|
+
"MockedClass",
|
|
8529
|
+
"MockedFunction",
|
|
8530
|
+
"MockedObject",
|
|
8440
8531
|
"MockInstance",
|
|
8441
8532
|
"SpyInstance"
|
|
8442
8533
|
]);
|
|
@@ -8680,9 +8771,9 @@ var no_zod_native_enum_default = createRule({
|
|
|
8680
8771
|
}
|
|
8681
8772
|
function isZodMemberCall(node, api) {
|
|
8682
8773
|
const callee = node.callee;
|
|
8683
|
-
if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression &&
|
|
8774
|
+
if (callee.type === import_utils45.AST_NODE_TYPES.MemberExpression && callee.object.type === import_utils45.AST_NODE_TYPES.Identifier && (callee.property.type === import_utils45.AST_NODE_TYPES.Identifier && !callee.computed && callee.property.name === api || callee.computed && callee.property.type === import_utils45.AST_NODE_TYPES.Literal && callee.property.value === api)) {
|
|
8684
8775
|
const binding = resolvedBinding(callee.object);
|
|
8685
|
-
return binding !== null && zodNamespaceBindings.has(binding)
|
|
8776
|
+
return binding !== null && zodNamespaceBindings.has(binding);
|
|
8686
8777
|
}
|
|
8687
8778
|
if (callee.type === import_utils45.AST_NODE_TYPES.Identifier) {
|
|
8688
8779
|
const binding = resolvedBinding(callee);
|
|
@@ -9526,6 +9617,27 @@ var prefer_immutable_module_constant_default = createRule({
|
|
|
9526
9617
|
}
|
|
9527
9618
|
const exportedNames2 = /* @__PURE__ */ new Set();
|
|
9528
9619
|
const typeAliases2 = /* @__PURE__ */ new Map();
|
|
9620
|
+
const mutatesThroughConstAlias = (root) => {
|
|
9621
|
+
const pending = [root];
|
|
9622
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9623
|
+
while (pending.length > 0) {
|
|
9624
|
+
const variable = pending.pop();
|
|
9625
|
+
if (variable === void 0 || seen.has(variable)) continue;
|
|
9626
|
+
seen.add(variable);
|
|
9627
|
+
for (const reference of variable.references) {
|
|
9628
|
+
const identifier = reference.identifier;
|
|
9629
|
+
if (identifier.type !== import_utils51.AST_NODE_TYPES.Identifier) continue;
|
|
9630
|
+
if (referenceMutates(identifier, isUnshadowedGlobal)) return true;
|
|
9631
|
+
const declarator = identifier.parent;
|
|
9632
|
+
if (declarator.type !== import_utils51.AST_NODE_TYPES.VariableDeclarator || declarator.init !== identifier || declarator.id.type !== import_utils51.AST_NODE_TYPES.Identifier || declarator.parent.type !== import_utils51.AST_NODE_TYPES.VariableDeclaration || declarator.parent.kind !== "const") {
|
|
9633
|
+
continue;
|
|
9634
|
+
}
|
|
9635
|
+
const alias = sourceCode.getDeclaredVariables(declarator)[0];
|
|
9636
|
+
if (alias !== void 0) pending.push(alias);
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
return false;
|
|
9640
|
+
};
|
|
9529
9641
|
return {
|
|
9530
9642
|
Program(node) {
|
|
9531
9643
|
for (const statement of node.body) {
|
|
@@ -9566,9 +9678,7 @@ var prefer_immutable_module_constant_default = createRule({
|
|
|
9566
9678
|
return;
|
|
9567
9679
|
}
|
|
9568
9680
|
const variable = sourceCode.getDeclaredVariables(node)[0];
|
|
9569
|
-
if (!directlyExported && !exportedNames2.has(node.id.name) && variable
|
|
9570
|
-
(reference) => reference.identifier.type === import_utils51.AST_NODE_TYPES.Identifier && referenceMutates(reference.identifier, isUnshadowedGlobal)
|
|
9571
|
-
) === true) {
|
|
9681
|
+
if (!directlyExported && !exportedNames2.has(node.id.name) && variable !== void 0 && mutatesThroughConstAlias(variable)) {
|
|
9572
9682
|
return;
|
|
9573
9683
|
}
|
|
9574
9684
|
context.report({
|
|
@@ -10347,6 +10457,10 @@ var prefer_module_level_schema_default = createRule({
|
|
|
10347
10457
|
}
|
|
10348
10458
|
for (const definition of resolved.defs) {
|
|
10349
10459
|
if (definition.type === "ImportBinding") {
|
|
10460
|
+
const parent = reference.identifier.parent;
|
|
10461
|
+
if (parent?.type === import_utils54.AST_NODE_TYPES.CallExpression && parent.callee === reference.identifier && !zodNamespaces.has(reference.identifier.name)) {
|
|
10462
|
+
return false;
|
|
10463
|
+
}
|
|
10350
10464
|
continue;
|
|
10351
10465
|
}
|
|
10352
10466
|
if (definition.node.type === import_utils54.AST_NODE_TYPES.VariableDeclarator && definition.node.parent.type === import_utils54.AST_NODE_TYPES.VariableDeclaration && definition.node.parent.kind !== "const") {
|
|
@@ -12178,10 +12292,7 @@ var SHAPE_PRESERVING_METHODS = /* @__PURE__ */ new Set([
|
|
|
12178
12292
|
]);
|
|
12179
12293
|
var OPTIONAL_MODIFIERS = /* @__PURE__ */ new Set([
|
|
12180
12294
|
"optional",
|
|
12181
|
-
"nullish"
|
|
12182
|
-
"default",
|
|
12183
|
-
"prefault",
|
|
12184
|
-
"catch"
|
|
12295
|
+
"nullish"
|
|
12185
12296
|
]);
|
|
12186
12297
|
var NULLABLE_MODIFIERS = /* @__PURE__ */ new Set(["nullable", "nullish"]);
|
|
12187
12298
|
var RESHAPING_MODIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -13450,6 +13561,7 @@ var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
|
|
|
13450
13561
|
"parseAsync",
|
|
13451
13562
|
"safeParseAsync"
|
|
13452
13563
|
]);
|
|
13564
|
+
var FORM_VALUE_METHODS = /* @__PURE__ */ new Set(["get", "getAll"]);
|
|
13453
13565
|
var zodReceiverRoot = (node) => {
|
|
13454
13566
|
let current = node;
|
|
13455
13567
|
while (true) {
|
|
@@ -13524,7 +13636,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
13524
13636
|
};
|
|
13525
13637
|
const isFormSourceIdentifier = (node) => {
|
|
13526
13638
|
if (node.type !== import_utils66.AST_NODE_TYPES.Identifier) return false;
|
|
13527
|
-
|
|
13639
|
+
const conventionalName = /formdata/i.test(node.name);
|
|
13528
13640
|
let scope = context.sourceCode.getScope(node);
|
|
13529
13641
|
while (scope !== null) {
|
|
13530
13642
|
const variable = scope.set.get(node.name);
|
|
@@ -13533,16 +13645,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
13533
13645
|
if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils66.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
|
|
13534
13646
|
return isFormDataMethodCall(def.node.init);
|
|
13535
13647
|
}
|
|
13536
|
-
return
|
|
13648
|
+
return def?.type === "Parameter" && conventionalName;
|
|
13537
13649
|
}
|
|
13538
13650
|
scope = scope.upper;
|
|
13539
13651
|
}
|
|
13540
|
-
return
|
|
13652
|
+
return conventionalName;
|
|
13541
13653
|
};
|
|
13542
13654
|
const isFormDataGetCall = (node) => {
|
|
13543
13655
|
const callee = node.callee;
|
|
13544
13656
|
if (callee.type !== import_utils66.AST_NODE_TYPES.MemberExpression) return false;
|
|
13545
|
-
if (callee.property.type !== import_utils66.AST_NODE_TYPES.Identifier || callee.property.name
|
|
13657
|
+
if (callee.property.type !== import_utils66.AST_NODE_TYPES.Identifier || !FORM_VALUE_METHODS.has(callee.property.name)) {
|
|
13546
13658
|
return false;
|
|
13547
13659
|
}
|
|
13548
13660
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -13636,6 +13748,40 @@ var require_zod_form_validation_default = createRule({
|
|
|
13636
13748
|
}
|
|
13637
13749
|
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === import_utils66.AST_NODE_TYPES.Literal && parent.right.value === null || parent.right.type === import_utils66.AST_NODE_TYPES.Identifier && parent.right.name === "undefined");
|
|
13638
13750
|
};
|
|
13751
|
+
const isDescendantOf = (node, ancestor) => {
|
|
13752
|
+
let current = node;
|
|
13753
|
+
while (current !== void 0 && current !== null) {
|
|
13754
|
+
if (current === ancestor) return true;
|
|
13755
|
+
current = current.parent;
|
|
13756
|
+
}
|
|
13757
|
+
return false;
|
|
13758
|
+
};
|
|
13759
|
+
const blockTerminates = (node) => {
|
|
13760
|
+
if (node.type === import_utils66.AST_NODE_TYPES.ReturnStatement || node.type === import_utils66.AST_NODE_TYPES.ThrowStatement) {
|
|
13761
|
+
return true;
|
|
13762
|
+
}
|
|
13763
|
+
if (node.type !== import_utils66.AST_NODE_TYPES.BlockStatement || node.body.length === 0) return false;
|
|
13764
|
+
const last = node.body.at(-1);
|
|
13765
|
+
return last !== void 0 && blockTerminates(last);
|
|
13766
|
+
};
|
|
13767
|
+
const narrowingIf = (identifier) => {
|
|
13768
|
+
const comparison = identifier.parent;
|
|
13769
|
+
if (comparison?.type !== import_utils66.AST_NODE_TYPES.BinaryExpression || comparison.operator !== "instanceof" || comparison.left !== identifier || comparison.right.type !== import_utils66.AST_NODE_TYPES.Identifier || comparison.right.name !== "File" && comparison.right.name !== "Blob") {
|
|
13770
|
+
return null;
|
|
13771
|
+
}
|
|
13772
|
+
const maybeNegation = comparison.parent;
|
|
13773
|
+
const negated = maybeNegation?.type === import_utils66.AST_NODE_TYPES.UnaryExpression && maybeNegation.operator === "!";
|
|
13774
|
+
const test = negated ? maybeNegation : comparison;
|
|
13775
|
+
const branch = test.parent;
|
|
13776
|
+
return branch?.type === import_utils66.AST_NODE_TYPES.IfStatement && branch.test === test ? { branch, positive: !negated } : null;
|
|
13777
|
+
};
|
|
13778
|
+
const useDominatedByNarrowing = (use, narrowings) => narrowings.some(({ branch, positive }) => {
|
|
13779
|
+
if (positive) return isDescendantOf(use, branch.consequent);
|
|
13780
|
+
if (!blockTerminates(branch.consequent)) return false;
|
|
13781
|
+
const branchStatement = containingStatement(branch);
|
|
13782
|
+
const useStatement = containingStatement(use);
|
|
13783
|
+
return branchStatement !== null && useStatement !== null && branchStatement.parent === useStatement.parent && branchStatement.range[1] < useStatement.range[0];
|
|
13784
|
+
});
|
|
13639
13785
|
const statementWithinBlock = (node, block) => {
|
|
13640
13786
|
let current = node;
|
|
13641
13787
|
while (current.parent !== void 0 && current.parent !== block) {
|
|
@@ -13650,14 +13796,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
13650
13796
|
(identifier) => identifier.type === import_utils66.AST_NODE_TYPES.Identifier
|
|
13651
13797
|
);
|
|
13652
13798
|
if (references.length === 0) return false;
|
|
13653
|
-
|
|
13799
|
+
const narrowings = references.map(narrowingIf).filter(
|
|
13800
|
+
(value) => value !== null
|
|
13801
|
+
);
|
|
13654
13802
|
const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
|
|
13655
13803
|
(statement) => statement !== null
|
|
13656
13804
|
);
|
|
13657
13805
|
const declarationStatement = containingStatement(declarator);
|
|
13658
13806
|
const declarationBlock = declarationStatement?.parent;
|
|
13659
13807
|
return references.every((reference) => {
|
|
13660
|
-
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
|
|
13808
|
+
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference) || useDominatedByNarrowing(reference, narrowings)) {
|
|
13661
13809
|
return true;
|
|
13662
13810
|
}
|
|
13663
13811
|
if (declarationBlock === void 0) return false;
|
|
@@ -14187,7 +14335,13 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
|
|
|
14187
14335
|
"safeDecodeAsync",
|
|
14188
14336
|
"toJSONSchema",
|
|
14189
14337
|
"registry",
|
|
14190
|
-
"implement"
|
|
14338
|
+
"implement",
|
|
14339
|
+
"flattenError",
|
|
14340
|
+
"formatError",
|
|
14341
|
+
"isNullable",
|
|
14342
|
+
"isOptional",
|
|
14343
|
+
"prettifyError",
|
|
14344
|
+
"treeifyError"
|
|
14191
14345
|
]);
|
|
14192
14346
|
var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils69.AST_NODE_TYPES.Identifier ? callee.property.name : null;
|
|
14193
14347
|
var calleeChainRoot = (node) => {
|
|
@@ -14436,7 +14590,7 @@ var rules = {
|
|
|
14436
14590
|
};
|
|
14437
14591
|
var meta = {
|
|
14438
14592
|
name: "@sarj/eslint-plugin",
|
|
14439
|
-
version: "15.
|
|
14593
|
+
version: "15.5.0"
|
|
14440
14594
|
};
|
|
14441
14595
|
var applicationOnlyRules = [
|
|
14442
14596
|
"no-restricted-library-load",
|
package/dist/index.d.cts
CHANGED
|
@@ -25,6 +25,7 @@ interface ExampleFile {
|
|
|
25
25
|
/** A reviewed example. It remains private unless `public: true` is explicit. */
|
|
26
26
|
interface RuleExample {
|
|
27
27
|
readonly id: string;
|
|
28
|
+
readonly scenarioId?: string;
|
|
28
29
|
readonly title: string;
|
|
29
30
|
readonly outcome: ExampleOutcome;
|
|
30
31
|
readonly files: readonly ExampleFile[];
|
|
@@ -224,7 +225,7 @@ declare const rules: {
|
|
|
224
225
|
readonly "no-silent-promise-catch": DocumentedRule<readonly [], "silentCatch">;
|
|
225
226
|
readonly "no-sleep-in-test-body": DocumentedRule<readonly [], "noSleepInTestBody">;
|
|
226
227
|
readonly "no-storage-in-stateless-modules": DocumentedRule<readonly [RuleOptions$3?], "storageInStatelessModule">;
|
|
227
|
-
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop">;
|
|
228
|
+
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop" | "noStringReduce">;
|
|
228
229
|
readonly "no-tautological-expect": DocumentedRule<readonly [], "tautologicalComparison" | "tautologicalMatcher">;
|
|
229
230
|
readonly "no-typed-doc-sections": DocumentedRule<readonly [], "typedSection">;
|
|
230
231
|
readonly "no-trailing-value-narration": DocumentedRule<readonly [], "deleteNarration" | "narratesValue">;
|
|
@@ -414,7 +415,7 @@ type FlatPreset = {
|
|
|
414
415
|
declare const plugin: {
|
|
415
416
|
readonly meta: {
|
|
416
417
|
readonly name: "@sarj/eslint-plugin";
|
|
417
|
-
readonly version: "15.
|
|
418
|
+
readonly version: "15.5.0";
|
|
418
419
|
};
|
|
419
420
|
readonly rules: {
|
|
420
421
|
readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
|
|
@@ -454,7 +455,7 @@ declare const plugin: {
|
|
|
454
455
|
readonly "no-silent-promise-catch": DocumentedRule<readonly [], "silentCatch">;
|
|
455
456
|
readonly "no-sleep-in-test-body": DocumentedRule<readonly [], "noSleepInTestBody">;
|
|
456
457
|
readonly "no-storage-in-stateless-modules": DocumentedRule<readonly [RuleOptions$3?], "storageInStatelessModule">;
|
|
457
|
-
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop">;
|
|
458
|
+
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop" | "noStringReduce">;
|
|
458
459
|
readonly "no-tautological-expect": DocumentedRule<readonly [], "tautologicalComparison" | "tautologicalMatcher">;
|
|
459
460
|
readonly "no-typed-doc-sections": DocumentedRule<readonly [], "typedSection">;
|
|
460
461
|
readonly "no-trailing-value-narration": DocumentedRule<readonly [], "deleteNarration" | "narratesValue">;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ interface ExampleFile {
|
|
|
25
25
|
/** A reviewed example. It remains private unless `public: true` is explicit. */
|
|
26
26
|
interface RuleExample {
|
|
27
27
|
readonly id: string;
|
|
28
|
+
readonly scenarioId?: string;
|
|
28
29
|
readonly title: string;
|
|
29
30
|
readonly outcome: ExampleOutcome;
|
|
30
31
|
readonly files: readonly ExampleFile[];
|
|
@@ -224,7 +225,7 @@ declare const rules: {
|
|
|
224
225
|
readonly "no-silent-promise-catch": DocumentedRule<readonly [], "silentCatch">;
|
|
225
226
|
readonly "no-sleep-in-test-body": DocumentedRule<readonly [], "noSleepInTestBody">;
|
|
226
227
|
readonly "no-storage-in-stateless-modules": DocumentedRule<readonly [RuleOptions$3?], "storageInStatelessModule">;
|
|
227
|
-
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop">;
|
|
228
|
+
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop" | "noStringReduce">;
|
|
228
229
|
readonly "no-tautological-expect": DocumentedRule<readonly [], "tautologicalComparison" | "tautologicalMatcher">;
|
|
229
230
|
readonly "no-typed-doc-sections": DocumentedRule<readonly [], "typedSection">;
|
|
230
231
|
readonly "no-trailing-value-narration": DocumentedRule<readonly [], "deleteNarration" | "narratesValue">;
|
|
@@ -414,7 +415,7 @@ type FlatPreset = {
|
|
|
414
415
|
declare const plugin: {
|
|
415
416
|
readonly meta: {
|
|
416
417
|
readonly name: "@sarj/eslint-plugin";
|
|
417
|
-
readonly version: "15.
|
|
418
|
+
readonly version: "15.5.0";
|
|
418
419
|
};
|
|
419
420
|
readonly rules: {
|
|
420
421
|
readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
|
|
@@ -454,7 +455,7 @@ declare const plugin: {
|
|
|
454
455
|
readonly "no-silent-promise-catch": DocumentedRule<readonly [], "silentCatch">;
|
|
455
456
|
readonly "no-sleep-in-test-body": DocumentedRule<readonly [], "noSleepInTestBody">;
|
|
456
457
|
readonly "no-storage-in-stateless-modules": DocumentedRule<readonly [RuleOptions$3?], "storageInStatelessModule">;
|
|
457
|
-
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop">;
|
|
458
|
+
readonly "no-string-concat-in-loop": DocumentedRule<readonly [], "noStringConcatInLoop" | "noStringReduce">;
|
|
458
459
|
readonly "no-tautological-expect": DocumentedRule<readonly [], "tautologicalComparison" | "tautologicalMatcher">;
|
|
459
460
|
readonly "no-typed-doc-sections": DocumentedRule<readonly [], "typedSection">;
|
|
460
461
|
readonly "no-trailing-value-narration": DocumentedRule<readonly [], "deleteNarration" | "narratesValue">;
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,7 @@ ${missing.join("\n")}`);
|
|
|
61
61
|
function publicExample(example) {
|
|
62
62
|
return {
|
|
63
63
|
id: example.id,
|
|
64
|
+
scenarioId: example.scenarioId ?? "primary",
|
|
64
65
|
title: example.title,
|
|
65
66
|
outcome: example.outcome,
|
|
66
67
|
files: example.files.map(publicFile),
|
|
@@ -105,9 +106,14 @@ function nativeSpec(config, documentation) {
|
|
|
105
106
|
const examples = [...documentation.examples ?? []];
|
|
106
107
|
examples.forEach(validateExample);
|
|
107
108
|
assertUnique(examples.map((example) => example.id), "rule example IDs");
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const publicExamples = examples.filter((example) => example.public === true);
|
|
110
|
+
const scenarios = new Set(publicExamples.map((example) => example.scenarioId ?? "primary"));
|
|
111
|
+
for (const scenario of scenarios) {
|
|
112
|
+
const pair = publicExamples.filter((example) => (example.scenarioId ?? "primary") === scenario);
|
|
113
|
+
const outcomes = new Set(pair.map((example) => example.outcome));
|
|
114
|
+
if (pair.length !== 2 || !outcomes.has("match") || !outcomes.has("no-match")) {
|
|
115
|
+
throw new TypeError(`published example scenario ${scenario} must contain both matching and non-matching cases exactly once`);
|
|
116
|
+
}
|
|
111
117
|
}
|
|
112
118
|
const messageIds = Object.keys(meta2.messages).sort();
|
|
113
119
|
const schema = optionsSchema(meta2.schema);
|
|
@@ -128,7 +134,7 @@ function nativeSpec(config, documentation) {
|
|
|
128
134
|
references,
|
|
129
135
|
since: documentation.since ?? null,
|
|
130
136
|
examples,
|
|
131
|
-
publicExamples
|
|
137
|
+
publicExamples,
|
|
132
138
|
messageIds,
|
|
133
139
|
optionsSchema: schema
|
|
134
140
|
};
|
|
@@ -148,6 +154,9 @@ function validateExample(example) {
|
|
|
148
154
|
if (!KEBAB_CASE.test(example.id)) {
|
|
149
155
|
throw new TypeError("example ID must be lowercase kebab-case");
|
|
150
156
|
}
|
|
157
|
+
if (!KEBAB_CASE.test(example.scenarioId ?? "primary")) {
|
|
158
|
+
throw new TypeError("example scenario must be lowercase kebab-case");
|
|
159
|
+
}
|
|
151
160
|
if (example.title.trim().length === 0) {
|
|
152
161
|
throw new TypeError("example title must not be empty");
|
|
153
162
|
}
|
|
@@ -555,6 +564,9 @@ function isAssertionStatement(statement) {
|
|
|
555
564
|
const expression = statement.expression;
|
|
556
565
|
return expression.type === AST_NODE_TYPES2.CallExpression && isAssertionCall(expression);
|
|
557
566
|
}
|
|
567
|
+
function isTypeOnlyContractStatement(statement) {
|
|
568
|
+
return statement.type === AST_NODE_TYPES2.TSTypeAliasDeclaration || statement.type === AST_NODE_TYPES2.TSInterfaceDeclaration;
|
|
569
|
+
}
|
|
558
570
|
function normalizedLiteral(node) {
|
|
559
571
|
if ("regex" in node) {
|
|
560
572
|
return ["Literal", "regex", node.regex.pattern, node.regex.flags];
|
|
@@ -612,7 +624,7 @@ var duplicate_test_body_default = createRule({
|
|
|
612
624
|
return;
|
|
613
625
|
}
|
|
614
626
|
const body2 = candidate.body;
|
|
615
|
-
if (body2.body.type !== AST_NODE_TYPES2.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement)) {
|
|
627
|
+
if (body2.body.type !== AST_NODE_TYPES2.BlockStatement || body2.body.body.length < MIN_STATEMENTS || body2.body.body.every(isAssertionStatement) || body2.body.body.some(isTypeOnlyContractStatement)) {
|
|
616
628
|
return;
|
|
617
629
|
}
|
|
618
630
|
const comments = context.sourceCode.getCommentsInside(body2.body).map((comment) => [comment.type, comment.value]);
|
|
@@ -3512,7 +3524,11 @@ var NUMBER_METHODS = /* @__PURE__ */ new Set([
|
|
|
3512
3524
|
"lt",
|
|
3513
3525
|
"lte",
|
|
3514
3526
|
"max",
|
|
3515
|
-
"min"
|
|
3527
|
+
"min",
|
|
3528
|
+
"negative",
|
|
3529
|
+
"nonnegative",
|
|
3530
|
+
"nonpositive",
|
|
3531
|
+
"positive"
|
|
3516
3532
|
]);
|
|
3517
3533
|
var LENGTH_METHODS = /* @__PURE__ */ new Set(["length", "max", "min"]);
|
|
3518
3534
|
var RESHAPING_METHODS = /* @__PURE__ */ new Set([
|
|
@@ -3649,13 +3665,29 @@ var no_impossible_zod_literal_bounds_default = createRule({
|
|
|
3649
3665
|
let upper = null;
|
|
3650
3666
|
for (const { method, node } of chain.calls) {
|
|
3651
3667
|
if (!allowed.has(method)) return null;
|
|
3652
|
-
const
|
|
3668
|
+
const semantic = method === "positive" ? { exclusive: true, lower: true, value: 0 } : method === "nonnegative" ? { exclusive: false, lower: true, value: 0 } : method === "negative" ? { exclusive: true, lower: false, value: 0 } : method === "nonpositive" ? { exclusive: false, lower: false, value: 0 } : null;
|
|
3669
|
+
const value = semantic?.value ?? finiteNumber(node.arguments[0]);
|
|
3653
3670
|
if (value === null) return null;
|
|
3654
3671
|
if (chain.kind !== "number" && (!Number.isInteger(value) || value < 0)) {
|
|
3655
3672
|
return null;
|
|
3656
3673
|
}
|
|
3657
3674
|
const label = `${method}(${String(value)})`;
|
|
3658
|
-
if (
|
|
3675
|
+
if (semantic !== null) {
|
|
3676
|
+
if (node.arguments.length !== 0) return null;
|
|
3677
|
+
if (semantic.lower) {
|
|
3678
|
+
lower = strongerLower(lower, {
|
|
3679
|
+
exclusive: semantic.exclusive,
|
|
3680
|
+
label: `${method}()`,
|
|
3681
|
+
value
|
|
3682
|
+
});
|
|
3683
|
+
} else {
|
|
3684
|
+
upper = strongerUpper(upper, {
|
|
3685
|
+
exclusive: semantic.exclusive,
|
|
3686
|
+
label: `${method}()`,
|
|
3687
|
+
value
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3690
|
+
} else if (method === "length") {
|
|
3659
3691
|
lower = strongerLower(lower, { exclusive: false, label, value });
|
|
3660
3692
|
upper = strongerUpper(upper, { exclusive: false, label, value });
|
|
3661
3693
|
} else if (method === "gt" || method === "gte" || method === "min") {
|
|
@@ -6768,6 +6800,13 @@ function nearestEnclosingFunction(node) {
|
|
|
6768
6800
|
}
|
|
6769
6801
|
return null;
|
|
6770
6802
|
}
|
|
6803
|
+
function isImmediatelyConsumedSleep(node) {
|
|
6804
|
+
const parent = node.parent;
|
|
6805
|
+
if (parent?.type === AST_NODE_TYPES25.AwaitExpression || parent?.type === AST_NODE_TYPES25.ReturnStatement || parent?.type === AST_NODE_TYPES25.ExpressionStatement) {
|
|
6806
|
+
return true;
|
|
6807
|
+
}
|
|
6808
|
+
return parent?.type === AST_NODE_TYPES25.ArrowFunctionExpression && parent.body === node;
|
|
6809
|
+
}
|
|
6771
6810
|
function isTestBody(fn) {
|
|
6772
6811
|
const call = fn.parent;
|
|
6773
6812
|
if (call?.type !== AST_NODE_TYPES25.CallExpression || !call.arguments.some((argument) => argument === fn)) {
|
|
@@ -6810,6 +6849,9 @@ var no_sleep_in_test_body_default = createRule({
|
|
|
6810
6849
|
return {};
|
|
6811
6850
|
}
|
|
6812
6851
|
const report = (node) => {
|
|
6852
|
+
if (!isImmediatelyConsumedSleep(node)) {
|
|
6853
|
+
return;
|
|
6854
|
+
}
|
|
6813
6855
|
const enclosing = nearestEnclosingFunction(node);
|
|
6814
6856
|
if (enclosing === null || !isTestBody(enclosing)) {
|
|
6815
6857
|
return;
|
|
@@ -7107,6 +7149,27 @@ function isSmallStaticForLoop(node) {
|
|
|
7107
7149
|
const iterations = node.test.right.value - declaration.init.value + (node.test.operator === "<=" ? 1 : 0);
|
|
7108
7150
|
return iterations >= 0 && iterations <= 8;
|
|
7109
7151
|
}
|
|
7152
|
+
function isStringSeededReduce(node) {
|
|
7153
|
+
if (node.callee.type !== "MemberExpression" || node.callee.computed || node.callee.property.type !== "Identifier" || node.callee.property.name !== "reduce" || node.arguments.length !== 2) {
|
|
7154
|
+
return false;
|
|
7155
|
+
}
|
|
7156
|
+
const [callback, initial] = node.arguments;
|
|
7157
|
+
if (node.callee.object.type === "ArrayExpression" && node.callee.object.elements.length <= 8) {
|
|
7158
|
+
return false;
|
|
7159
|
+
}
|
|
7160
|
+
if (callback === void 0 || initial === void 0 || callback.type === "SpreadElement" || initial.type === "SpreadElement" || callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression" || callback.params[0]?.type !== "Identifier" || !isStringLiteralInit(initial)) {
|
|
7161
|
+
return false;
|
|
7162
|
+
}
|
|
7163
|
+
const accumulator = callback.params[0].name;
|
|
7164
|
+
if (callback.body.type !== "BlockStatement") {
|
|
7165
|
+
return isConcatOntoTarget(callback.body, accumulator);
|
|
7166
|
+
}
|
|
7167
|
+
if (callback.body.body.length !== 1 || callback.body.body[0]?.type !== "ReturnStatement") {
|
|
7168
|
+
return false;
|
|
7169
|
+
}
|
|
7170
|
+
const returned = callback.body.body[0].argument;
|
|
7171
|
+
return returned !== null && isConcatOntoTarget(returned, accumulator);
|
|
7172
|
+
}
|
|
7110
7173
|
var no_string_concat_in_loop_default = createRule({
|
|
7111
7174
|
name: "no-string-concat-in-loop",
|
|
7112
7175
|
documentation: noStringConcatInLoopDocumentation,
|
|
@@ -7117,7 +7180,8 @@ var no_string_concat_in_loop_default = createRule({
|
|
|
7117
7180
|
},
|
|
7118
7181
|
schema: [],
|
|
7119
7182
|
messages: {
|
|
7120
|
-
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.'
|
|
7183
|
+
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.',
|
|
7184
|
+
noStringReduce: "Avoid concatenating a growing string in `reduce` \u2014 this is O(n^2). Map the fragments and join them once instead."
|
|
7121
7185
|
}
|
|
7122
7186
|
},
|
|
7123
7187
|
defaultOptions: [],
|
|
@@ -7127,6 +7191,11 @@ var no_string_concat_in_loop_default = createRule({
|
|
|
7127
7191
|
}
|
|
7128
7192
|
const reported = /* @__PURE__ */ new WeakMap();
|
|
7129
7193
|
return {
|
|
7194
|
+
CallExpression(node) {
|
|
7195
|
+
if (isStringSeededReduce(node)) {
|
|
7196
|
+
context.report({ node, messageId: "noStringReduce" });
|
|
7197
|
+
}
|
|
7198
|
+
},
|
|
7130
7199
|
AssignmentExpression(node) {
|
|
7131
7200
|
if (node.left.type !== "Identifier") {
|
|
7132
7201
|
return;
|
|
@@ -7231,6 +7300,9 @@ function isLiteral(node) {
|
|
|
7231
7300
|
return false;
|
|
7232
7301
|
}
|
|
7233
7302
|
}
|
|
7303
|
+
function isStructuralLiteral(node) {
|
|
7304
|
+
return node.type === AST_NODE_TYPES27.ArrayExpression || node.type === AST_NODE_TYPES27.ObjectExpression;
|
|
7305
|
+
}
|
|
7234
7306
|
function expectOperand(callee) {
|
|
7235
7307
|
const receiver = callee.object;
|
|
7236
7308
|
if (receiver.type !== AST_NODE_TYPES27.CallExpression || receiver.callee.type !== AST_NODE_TYPES27.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
|
|
@@ -7287,6 +7359,9 @@ var no_tautological_expect_default = createRule({
|
|
|
7287
7359
|
if (!EQUALITY_MATCHERS.has(matcher) || node.arguments.length !== 1 || expected === void 0 || !isLiteral(expected)) {
|
|
7288
7360
|
return;
|
|
7289
7361
|
}
|
|
7362
|
+
if (matcher === "toBe" && (isStructuralLiteral(operand) || isStructuralLiteral(expected))) {
|
|
7363
|
+
return;
|
|
7364
|
+
}
|
|
7290
7365
|
if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {
|
|
7291
7366
|
return;
|
|
7292
7367
|
}
|
|
@@ -7600,7 +7675,7 @@ var no_trailing_value_narration_default = createRule({
|
|
|
7600
7675
|
});
|
|
7601
7676
|
|
|
7602
7677
|
// src/rules/no-declaration-comment-wall.ts
|
|
7603
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES29 } from "@typescript-eslint/utils";
|
|
7678
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES29, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
|
|
7604
7679
|
|
|
7605
7680
|
// src/rules/_comment-wall.ts
|
|
7606
7681
|
import { AST_NODE_TYPES as AST_NODE_TYPES28 } from "@typescript-eslint/utils";
|
|
@@ -7800,7 +7875,13 @@ var no_declaration_comment_wall_default = createRule({
|
|
|
7800
7875
|
const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
|
|
7801
7876
|
if (lead !== void 0) {
|
|
7802
7877
|
const before = sourceCode.getTokenBefore(lead, { includeComments: false });
|
|
7803
|
-
if (before === null || before.loc.end.line < lead.loc.start.line)
|
|
7878
|
+
if (before === null || before.loc.end.line < lead.loc.start.line) {
|
|
7879
|
+
const previousLine = endingOn.get(lead.loc.start.line - 1);
|
|
7880
|
+
if (lead.type === AST_TOKEN_TYPES.Line && previousLine?.type === AST_TOKEN_TYPES.Line && previousLine.loc.start.column === lead.loc.start.column) {
|
|
7881
|
+
return void 0;
|
|
7882
|
+
}
|
|
7883
|
+
return lead;
|
|
7884
|
+
}
|
|
7804
7885
|
}
|
|
7805
7886
|
const trail = startingOn.get(member.loc.end.line);
|
|
7806
7887
|
return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
|
|
@@ -8039,7 +8120,7 @@ var no_union_in_comment_default = createRule({
|
|
|
8039
8120
|
});
|
|
8040
8121
|
|
|
8041
8122
|
// src/rules/no-type-member-comment-wall.ts
|
|
8042
|
-
import { AST_NODE_TYPES as AST_NODE_TYPES31 } from "@typescript-eslint/utils";
|
|
8123
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES31, AST_TOKEN_TYPES as AST_TOKEN_TYPES2 } from "@typescript-eslint/utils";
|
|
8043
8124
|
var noTypeMemberCommentWallDocumentation = {
|
|
8044
8125
|
summary: "Flag an object type whose member comments mostly re-spell the members' own names and types.",
|
|
8045
8126
|
rationale: "Repetitive member comments add scanning cost while hiding the comments that describe facts absent from the type.",
|
|
@@ -8102,7 +8183,13 @@ var no_type_member_comment_wall_default = createRule({
|
|
|
8102
8183
|
const lead = ownsItsLine ? endingOn.get(member.loc.start.line - 1) : void 0;
|
|
8103
8184
|
if (lead !== void 0) {
|
|
8104
8185
|
const before = sourceCode.getTokenBefore(lead, { includeComments: false });
|
|
8105
|
-
if (before === null || before.loc.end.line < lead.loc.start.line)
|
|
8186
|
+
if (before === null || before.loc.end.line < lead.loc.start.line) {
|
|
8187
|
+
const previousLine = endingOn.get(lead.loc.start.line - 1);
|
|
8188
|
+
if (lead.type === AST_TOKEN_TYPES2.Line && previousLine?.type === AST_TOKEN_TYPES2.Line && previousLine.loc.start.column === lead.loc.start.column) {
|
|
8189
|
+
return void 0;
|
|
8190
|
+
}
|
|
8191
|
+
return lead;
|
|
8192
|
+
}
|
|
8106
8193
|
}
|
|
8107
8194
|
const trail = startingOn.get(member.loc.end.line);
|
|
8108
8195
|
return trail !== void 0 && trail.range[0] > member.range[0] ? trail : void 0;
|
|
@@ -8402,6 +8489,10 @@ import {
|
|
|
8402
8489
|
} from "@typescript-eslint/utils";
|
|
8403
8490
|
var MOCK_TYPE_NAMES = /* @__PURE__ */ new Set([
|
|
8404
8491
|
"Mock",
|
|
8492
|
+
"Mocked",
|
|
8493
|
+
"MockedClass",
|
|
8494
|
+
"MockedFunction",
|
|
8495
|
+
"MockedObject",
|
|
8405
8496
|
"MockInstance",
|
|
8406
8497
|
"SpyInstance"
|
|
8407
8498
|
]);
|
|
@@ -8649,9 +8740,9 @@ var no_zod_native_enum_default = createRule({
|
|
|
8649
8740
|
}
|
|
8650
8741
|
function isZodMemberCall(node, api) {
|
|
8651
8742
|
const callee = node.callee;
|
|
8652
|
-
if (callee.type === AST_NODE_TYPES34.MemberExpression &&
|
|
8743
|
+
if (callee.type === AST_NODE_TYPES34.MemberExpression && callee.object.type === AST_NODE_TYPES34.Identifier && (callee.property.type === AST_NODE_TYPES34.Identifier && !callee.computed && callee.property.name === api || callee.computed && callee.property.type === AST_NODE_TYPES34.Literal && callee.property.value === api)) {
|
|
8653
8744
|
const binding = resolvedBinding(callee.object);
|
|
8654
|
-
return binding !== null && zodNamespaceBindings.has(binding)
|
|
8745
|
+
return binding !== null && zodNamespaceBindings.has(binding);
|
|
8655
8746
|
}
|
|
8656
8747
|
if (callee.type === AST_NODE_TYPES34.Identifier) {
|
|
8657
8748
|
const binding = resolvedBinding(callee);
|
|
@@ -9495,6 +9586,27 @@ var prefer_immutable_module_constant_default = createRule({
|
|
|
9495
9586
|
}
|
|
9496
9587
|
const exportedNames2 = /* @__PURE__ */ new Set();
|
|
9497
9588
|
const typeAliases2 = /* @__PURE__ */ new Map();
|
|
9589
|
+
const mutatesThroughConstAlias = (root) => {
|
|
9590
|
+
const pending = [root];
|
|
9591
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9592
|
+
while (pending.length > 0) {
|
|
9593
|
+
const variable = pending.pop();
|
|
9594
|
+
if (variable === void 0 || seen.has(variable)) continue;
|
|
9595
|
+
seen.add(variable);
|
|
9596
|
+
for (const reference of variable.references) {
|
|
9597
|
+
const identifier = reference.identifier;
|
|
9598
|
+
if (identifier.type !== AST_NODE_TYPES39.Identifier) continue;
|
|
9599
|
+
if (referenceMutates(identifier, isUnshadowedGlobal)) return true;
|
|
9600
|
+
const declarator = identifier.parent;
|
|
9601
|
+
if (declarator.type !== AST_NODE_TYPES39.VariableDeclarator || declarator.init !== identifier || declarator.id.type !== AST_NODE_TYPES39.Identifier || declarator.parent.type !== AST_NODE_TYPES39.VariableDeclaration || declarator.parent.kind !== "const") {
|
|
9602
|
+
continue;
|
|
9603
|
+
}
|
|
9604
|
+
const alias = sourceCode.getDeclaredVariables(declarator)[0];
|
|
9605
|
+
if (alias !== void 0) pending.push(alias);
|
|
9606
|
+
}
|
|
9607
|
+
}
|
|
9608
|
+
return false;
|
|
9609
|
+
};
|
|
9498
9610
|
return {
|
|
9499
9611
|
Program(node) {
|
|
9500
9612
|
for (const statement of node.body) {
|
|
@@ -9535,9 +9647,7 @@ var prefer_immutable_module_constant_default = createRule({
|
|
|
9535
9647
|
return;
|
|
9536
9648
|
}
|
|
9537
9649
|
const variable = sourceCode.getDeclaredVariables(node)[0];
|
|
9538
|
-
if (!directlyExported && !exportedNames2.has(node.id.name) && variable
|
|
9539
|
-
(reference) => reference.identifier.type === AST_NODE_TYPES39.Identifier && referenceMutates(reference.identifier, isUnshadowedGlobal)
|
|
9540
|
-
) === true) {
|
|
9650
|
+
if (!directlyExported && !exportedNames2.has(node.id.name) && variable !== void 0 && mutatesThroughConstAlias(variable)) {
|
|
9541
9651
|
return;
|
|
9542
9652
|
}
|
|
9543
9653
|
context.report({
|
|
@@ -10316,6 +10426,10 @@ var prefer_module_level_schema_default = createRule({
|
|
|
10316
10426
|
}
|
|
10317
10427
|
for (const definition of resolved.defs) {
|
|
10318
10428
|
if (definition.type === "ImportBinding") {
|
|
10429
|
+
const parent = reference.identifier.parent;
|
|
10430
|
+
if (parent?.type === AST_NODE_TYPES42.CallExpression && parent.callee === reference.identifier && !zodNamespaces.has(reference.identifier.name)) {
|
|
10431
|
+
return false;
|
|
10432
|
+
}
|
|
10319
10433
|
continue;
|
|
10320
10434
|
}
|
|
10321
10435
|
if (definition.node.type === AST_NODE_TYPES42.VariableDeclarator && definition.node.parent.type === AST_NODE_TYPES42.VariableDeclaration && definition.node.parent.kind !== "const") {
|
|
@@ -12147,10 +12261,7 @@ var SHAPE_PRESERVING_METHODS = /* @__PURE__ */ new Set([
|
|
|
12147
12261
|
]);
|
|
12148
12262
|
var OPTIONAL_MODIFIERS = /* @__PURE__ */ new Set([
|
|
12149
12263
|
"optional",
|
|
12150
|
-
"nullish"
|
|
12151
|
-
"default",
|
|
12152
|
-
"prefault",
|
|
12153
|
-
"catch"
|
|
12264
|
+
"nullish"
|
|
12154
12265
|
]);
|
|
12155
12266
|
var NULLABLE_MODIFIERS = /* @__PURE__ */ new Set(["nullable", "nullish"]);
|
|
12156
12267
|
var RESHAPING_MODIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -13425,6 +13536,7 @@ var ZOD_PARSE_METHODS = /* @__PURE__ */ new Set([
|
|
|
13425
13536
|
"parseAsync",
|
|
13426
13537
|
"safeParseAsync"
|
|
13427
13538
|
]);
|
|
13539
|
+
var FORM_VALUE_METHODS = /* @__PURE__ */ new Set(["get", "getAll"]);
|
|
13428
13540
|
var zodReceiverRoot = (node) => {
|
|
13429
13541
|
let current = node;
|
|
13430
13542
|
while (true) {
|
|
@@ -13499,7 +13611,7 @@ var require_zod_form_validation_default = createRule({
|
|
|
13499
13611
|
};
|
|
13500
13612
|
const isFormSourceIdentifier = (node) => {
|
|
13501
13613
|
if (node.type !== AST_NODE_TYPES53.Identifier) return false;
|
|
13502
|
-
|
|
13614
|
+
const conventionalName = /formdata/i.test(node.name);
|
|
13503
13615
|
let scope = context.sourceCode.getScope(node);
|
|
13504
13616
|
while (scope !== null) {
|
|
13505
13617
|
const variable = scope.set.get(node.name);
|
|
@@ -13508,16 +13620,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
13508
13620
|
if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES53.VariableDeclarator && def.node.init !== null) {
|
|
13509
13621
|
return isFormDataMethodCall(def.node.init);
|
|
13510
13622
|
}
|
|
13511
|
-
return
|
|
13623
|
+
return def?.type === "Parameter" && conventionalName;
|
|
13512
13624
|
}
|
|
13513
13625
|
scope = scope.upper;
|
|
13514
13626
|
}
|
|
13515
|
-
return
|
|
13627
|
+
return conventionalName;
|
|
13516
13628
|
};
|
|
13517
13629
|
const isFormDataGetCall = (node) => {
|
|
13518
13630
|
const callee = node.callee;
|
|
13519
13631
|
if (callee.type !== AST_NODE_TYPES53.MemberExpression) return false;
|
|
13520
|
-
if (callee.property.type !== AST_NODE_TYPES53.Identifier || callee.property.name
|
|
13632
|
+
if (callee.property.type !== AST_NODE_TYPES53.Identifier || !FORM_VALUE_METHODS.has(callee.property.name)) {
|
|
13521
13633
|
return false;
|
|
13522
13634
|
}
|
|
13523
13635
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -13611,6 +13723,40 @@ var require_zod_form_validation_default = createRule({
|
|
|
13611
13723
|
}
|
|
13612
13724
|
return ["===", "!==", "==", "!="].includes(parent.operator) && (parent.right.type === AST_NODE_TYPES53.Literal && parent.right.value === null || parent.right.type === AST_NODE_TYPES53.Identifier && parent.right.name === "undefined");
|
|
13613
13725
|
};
|
|
13726
|
+
const isDescendantOf = (node, ancestor) => {
|
|
13727
|
+
let current = node;
|
|
13728
|
+
while (current !== void 0 && current !== null) {
|
|
13729
|
+
if (current === ancestor) return true;
|
|
13730
|
+
current = current.parent;
|
|
13731
|
+
}
|
|
13732
|
+
return false;
|
|
13733
|
+
};
|
|
13734
|
+
const blockTerminates = (node) => {
|
|
13735
|
+
if (node.type === AST_NODE_TYPES53.ReturnStatement || node.type === AST_NODE_TYPES53.ThrowStatement) {
|
|
13736
|
+
return true;
|
|
13737
|
+
}
|
|
13738
|
+
if (node.type !== AST_NODE_TYPES53.BlockStatement || node.body.length === 0) return false;
|
|
13739
|
+
const last = node.body.at(-1);
|
|
13740
|
+
return last !== void 0 && blockTerminates(last);
|
|
13741
|
+
};
|
|
13742
|
+
const narrowingIf = (identifier) => {
|
|
13743
|
+
const comparison = identifier.parent;
|
|
13744
|
+
if (comparison?.type !== AST_NODE_TYPES53.BinaryExpression || comparison.operator !== "instanceof" || comparison.left !== identifier || comparison.right.type !== AST_NODE_TYPES53.Identifier || comparison.right.name !== "File" && comparison.right.name !== "Blob") {
|
|
13745
|
+
return null;
|
|
13746
|
+
}
|
|
13747
|
+
const maybeNegation = comparison.parent;
|
|
13748
|
+
const negated = maybeNegation?.type === AST_NODE_TYPES53.UnaryExpression && maybeNegation.operator === "!";
|
|
13749
|
+
const test = negated ? maybeNegation : comparison;
|
|
13750
|
+
const branch = test.parent;
|
|
13751
|
+
return branch?.type === AST_NODE_TYPES53.IfStatement && branch.test === test ? { branch, positive: !negated } : null;
|
|
13752
|
+
};
|
|
13753
|
+
const useDominatedByNarrowing = (use, narrowings) => narrowings.some(({ branch, positive }) => {
|
|
13754
|
+
if (positive) return isDescendantOf(use, branch.consequent);
|
|
13755
|
+
if (!blockTerminates(branch.consequent)) return false;
|
|
13756
|
+
const branchStatement = containingStatement(branch);
|
|
13757
|
+
const useStatement = containingStatement(use);
|
|
13758
|
+
return branchStatement !== null && useStatement !== null && branchStatement.parent === useStatement.parent && branchStatement.range[1] < useStatement.range[0];
|
|
13759
|
+
});
|
|
13614
13760
|
const statementWithinBlock = (node, block) => {
|
|
13615
13761
|
let current = node;
|
|
13616
13762
|
while (current.parent !== void 0 && current.parent !== block) {
|
|
@@ -13625,14 +13771,16 @@ var require_zod_form_validation_default = createRule({
|
|
|
13625
13771
|
(identifier) => identifier.type === AST_NODE_TYPES53.Identifier
|
|
13626
13772
|
);
|
|
13627
13773
|
if (references.length === 0) return false;
|
|
13628
|
-
|
|
13774
|
+
const narrowings = references.map(narrowingIf).filter(
|
|
13775
|
+
(value) => value !== null
|
|
13776
|
+
);
|
|
13629
13777
|
const validationStatements = references.map((reference) => guaranteedValidationStatement(declarator, reference)).filter(
|
|
13630
13778
|
(statement) => statement !== null
|
|
13631
13779
|
);
|
|
13632
13780
|
const declarationStatement = containingStatement(declarator);
|
|
13633
13781
|
const declarationBlock = declarationStatement?.parent;
|
|
13634
13782
|
return references.every((reference) => {
|
|
13635
|
-
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference)) {
|
|
13783
|
+
if (zodParseAncestor(reference) !== null || isSafePrevalidationInspection(reference) || useDominatedByNarrowing(reference, narrowings)) {
|
|
13636
13784
|
return true;
|
|
13637
13785
|
}
|
|
13638
13786
|
if (declarationBlock === void 0) return false;
|
|
@@ -14165,7 +14313,13 @@ var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
|
|
|
14165
14313
|
"safeDecodeAsync",
|
|
14166
14314
|
"toJSONSchema",
|
|
14167
14315
|
"registry",
|
|
14168
|
-
"implement"
|
|
14316
|
+
"implement",
|
|
14317
|
+
"flattenError",
|
|
14318
|
+
"formatError",
|
|
14319
|
+
"isNullable",
|
|
14320
|
+
"isOptional",
|
|
14321
|
+
"prettifyError",
|
|
14322
|
+
"treeifyError"
|
|
14169
14323
|
]);
|
|
14170
14324
|
var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES55.Identifier ? callee.property.name : null;
|
|
14171
14325
|
var calleeChainRoot = (node) => {
|
|
@@ -14414,7 +14568,7 @@ var rules = {
|
|
|
14414
14568
|
};
|
|
14415
14569
|
var meta = {
|
|
14416
14570
|
name: "@sarj/eslint-plugin",
|
|
14417
|
-
version: "15.
|
|
14571
|
+
version: "15.5.0"
|
|
14418
14572
|
};
|
|
14419
14573
|
var applicationOnlyRules = [
|
|
14420
14574
|
"no-restricted-library-load",
|