@sarj/eslint-plugin 2.11.0 → 2.12.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 +459 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +459 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49,6 +49,7 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
let seenBody = false;
|
|
52
|
+
let inMisplacedRun = false;
|
|
52
53
|
for (const statement of body) {
|
|
53
54
|
if (isStringDirective(statement)) continue;
|
|
54
55
|
switch (classifyStatement(statement)) {
|
|
@@ -56,9 +57,11 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
56
57
|
continue;
|
|
57
58
|
case "body":
|
|
58
59
|
seenBody = true;
|
|
60
|
+
inMisplacedRun = false;
|
|
59
61
|
continue;
|
|
60
62
|
case "import":
|
|
61
|
-
if (seenBody) {
|
|
63
|
+
if (seenBody && !inMisplacedRun) {
|
|
64
|
+
inMisplacedRun = true;
|
|
62
65
|
context.report({
|
|
63
66
|
node: statement,
|
|
64
67
|
messageId: "importsFirst"
|
|
@@ -217,9 +220,13 @@ var STEP_NARRATION_RE = /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|a
|
|
|
217
220
|
var META_COMMENTARY_RE = /\b(?:for now|keeping (?:it|this) simple|could be (?:refactored|improved|cleaned up|simplified)|refactor(?:ed|ing)? (?:later|this)|not sure (?:if|whether|why|how)|quick[- ](?:and[- ]dirty|fix)|(?:a |bit of a )?hacky|is a hack|temporary (?:solution|workaround|fix|hack)|revisit (?:this|later|below)|clean (?:this|it) up|not ideal|placeholder for now)\b/i;
|
|
218
221
|
var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|<amd|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
|
|
219
222
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
223
|
+
var ENUMERATED_ITEM_RE = /^(?:\d+[.):]|[-*•])\s+\S/;
|
|
224
|
+
var ENUMERATED_ITEM_MIN_WORDS = 3;
|
|
225
|
+
var ENUMERATED_PREAMBLE_MIN_ITEMS = 2;
|
|
220
226
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
221
227
|
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
222
228
|
var REGION_RE = /^#?(?:end)?region\b/i;
|
|
229
|
+
var DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;
|
|
223
230
|
var CODE_KEYWORD_RE = /^(import |export |const |let |var |function\b|class |interface |type \w|enum |return\b|throw |await |async |if\s*\(|for\s*\(|while\s*\(|switch\s*\(|new |console\.)/;
|
|
224
231
|
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
225
232
|
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
@@ -233,7 +240,8 @@ function isDirective(text) {
|
|
|
233
240
|
function isBanner(text) {
|
|
234
241
|
const t = text.trim();
|
|
235
242
|
if (!t) return false;
|
|
236
|
-
|
|
243
|
+
if (BANNER_FULL_RE.test(t) || REGION_RE.test(t)) return true;
|
|
244
|
+
return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);
|
|
237
245
|
}
|
|
238
246
|
function looksLikeCode(text) {
|
|
239
247
|
const t = text.trim();
|
|
@@ -244,6 +252,10 @@ function looksLikeCode(text) {
|
|
|
244
252
|
function hasPseudocode(text) {
|
|
245
253
|
return PSEUDOCODE_RE.test(text);
|
|
246
254
|
}
|
|
255
|
+
function isEnumeratedProseItem(text) {
|
|
256
|
+
const t = text.trim();
|
|
257
|
+
return ENUMERATED_ITEM_RE.test(t) && t.split(/\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;
|
|
258
|
+
}
|
|
247
259
|
function isProse(text) {
|
|
248
260
|
const t = text.trim();
|
|
249
261
|
if (!t) return false;
|
|
@@ -356,13 +368,32 @@ function restatesNextLine(body, statement) {
|
|
|
356
368
|
const code = codeTokens(head);
|
|
357
369
|
return content.every((word) => code.has(word));
|
|
358
370
|
}
|
|
359
|
-
|
|
371
|
+
var JUSTIFICATION_RE = /\b(?:because|since|until|due to|so that|otherwise|which is why|in order to|to avoid|to work around|to prevent)\b/i;
|
|
372
|
+
function isRedundantNarration(body, statementBelow, standalone) {
|
|
360
373
|
const t = body.trim();
|
|
361
374
|
if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;
|
|
362
|
-
if (
|
|
363
|
-
|
|
375
|
+
if (standalone) {
|
|
376
|
+
if (STEP_NARRATION_RE.test(t)) return true;
|
|
377
|
+
if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;
|
|
378
|
+
}
|
|
364
379
|
return restatesNextLine(t, statementBelow);
|
|
365
380
|
}
|
|
381
|
+
function areAdjacentLineComments(a, b) {
|
|
382
|
+
return a !== void 0 && b !== void 0 && a.type === "Line" && b.type === "Line" && b.loc.start.line === a.loc.end.line + 1;
|
|
383
|
+
}
|
|
384
|
+
function isInsideCommentRun(comments, index) {
|
|
385
|
+
const comment = comments[index];
|
|
386
|
+
return areAdjacentLineComments(comments[index - 1], comment) || areAdjacentLineComments(comment, comments[index + 1]);
|
|
387
|
+
}
|
|
388
|
+
var LEAD_IN_SCAN_LIMIT = 24;
|
|
389
|
+
function hasIllustrationLeadInAbove(comments, index) {
|
|
390
|
+
for (let i = index - 1; i >= 0 && index - i <= LEAD_IN_SCAN_LIMIT; i--) {
|
|
391
|
+
if (!areAdjacentLineComments(comments[i], comments[i + 1])) return false;
|
|
392
|
+
const body = stripCommentMarker(comments[i]?.value ?? "");
|
|
393
|
+
if (body.length > 0 && body.endsWith(":")) return true;
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
366
397
|
function hasCommentedOutCode(texts, precedingProse) {
|
|
367
398
|
for (let i = 0; i < texts.length; i++) {
|
|
368
399
|
const line = texts[i];
|
|
@@ -422,6 +453,9 @@ var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
|
422
453
|
const bodies = leading.map((c) => stripCommentMarker(c.value));
|
|
423
454
|
if (bodies.some((body) => LICENSE_RE.test(body))) return;
|
|
424
455
|
if (bodies.some((body) => isProse(body))) return;
|
|
456
|
+
if (bodies.filter(isEnumeratedProseItem).length >= ENUMERATED_PREAMBLE_MIN_ITEMS) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
425
459
|
context.report({ node: first, messageId: "fileHeaderPreamble" });
|
|
426
460
|
}
|
|
427
461
|
return {
|
|
@@ -440,14 +474,15 @@ var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
|
440
474
|
}
|
|
441
475
|
const prev = comments[i - 1];
|
|
442
476
|
const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
|
|
443
|
-
if (hasCommentedOutCode(texts, precedingProse)) {
|
|
477
|
+
if (hasCommentedOutCode(texts, precedingProse) && !hasIllustrationLeadInAbove(comments, i)) {
|
|
444
478
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
445
479
|
continue;
|
|
446
480
|
}
|
|
447
481
|
if (comment.type === "Line" && texts.length === 1) {
|
|
448
482
|
const body = texts[0];
|
|
449
483
|
const statement = restatableStatementBelow(comment, sourceCode);
|
|
450
|
-
|
|
484
|
+
const standalone = !isInsideCommentRun(comments, i);
|
|
485
|
+
if (body !== void 0 && isRedundantNarration(body, statement, standalone)) {
|
|
451
486
|
context.report({ node: comment, messageId: "redundantNarration" });
|
|
452
487
|
}
|
|
453
488
|
}
|
|
@@ -530,6 +565,22 @@ var no_enum_default = ESLintUtils4.RuleCreator(
|
|
|
530
565
|
|
|
531
566
|
// src/rules/no-insecure-random-id.ts
|
|
532
567
|
import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
|
|
568
|
+
|
|
569
|
+
// src/rules/_paths.ts
|
|
570
|
+
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
571
|
+
function isTestFile(filename) {
|
|
572
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
573
|
+
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
574
|
+
if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(base)) {
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
return /(^|\/)(tests?|__tests__|__mocks__|fixtures)\//.test(normalized);
|
|
578
|
+
}
|
|
579
|
+
function isScriptFile(filename) {
|
|
580
|
+
return SCRIPT_FILE_RE.test(filename);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/rules/no-insecure-random-id.ts
|
|
533
584
|
var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
|
|
534
585
|
var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
|
|
535
586
|
var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
|
|
@@ -686,6 +737,9 @@ var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
|
686
737
|
},
|
|
687
738
|
defaultOptions: [],
|
|
688
739
|
create(context) {
|
|
740
|
+
if (isTestFile(context.filename)) {
|
|
741
|
+
return {};
|
|
742
|
+
}
|
|
689
743
|
return {
|
|
690
744
|
CallExpression(node) {
|
|
691
745
|
if (!isMathRandomCall(node)) {
|
|
@@ -715,6 +769,22 @@ import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
|
|
|
715
769
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
716
770
|
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
717
771
|
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
772
|
+
var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
|
|
773
|
+
"data",
|
|
774
|
+
"status",
|
|
775
|
+
"statuscode",
|
|
776
|
+
"statustext",
|
|
777
|
+
"code",
|
|
778
|
+
"issues",
|
|
779
|
+
"details",
|
|
780
|
+
"body",
|
|
781
|
+
"payload",
|
|
782
|
+
"response",
|
|
783
|
+
"info",
|
|
784
|
+
"meta",
|
|
785
|
+
"metadata",
|
|
786
|
+
"context"
|
|
787
|
+
]);
|
|
718
788
|
function isCatchBinding(scope, name) {
|
|
719
789
|
let current = scope;
|
|
720
790
|
while (current) {
|
|
@@ -738,7 +808,11 @@ function memberSuggestsError(member, scope) {
|
|
|
738
808
|
const base = member.object;
|
|
739
809
|
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
740
810
|
if (baseSuggestsError) {
|
|
741
|
-
|
|
811
|
+
if (propName2 === null) {
|
|
812
|
+
return true;
|
|
813
|
+
}
|
|
814
|
+
const lowered = propName2.toLowerCase();
|
|
815
|
+
return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);
|
|
742
816
|
}
|
|
743
817
|
return false;
|
|
744
818
|
}
|
|
@@ -1025,13 +1099,17 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
1025
1099
|
return {
|
|
1026
1100
|
CatchClause(node) {
|
|
1027
1101
|
const statements = node.body.body;
|
|
1102
|
+
const isDocumented = context.sourceCode.getCommentsInside(node.body).length > 0;
|
|
1028
1103
|
if (statements.length === 0) {
|
|
1029
|
-
if (
|
|
1104
|
+
if (isDocumented) {
|
|
1030
1105
|
return;
|
|
1031
1106
|
}
|
|
1032
1107
|
context.report({ node, messageId: "emptyCatch" });
|
|
1033
1108
|
return;
|
|
1034
1109
|
}
|
|
1110
|
+
if (isDocumented) {
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1035
1113
|
const everyStatementIsLogging = statements.every(
|
|
1036
1114
|
(statement) => isLoggingCallStatement(statement)
|
|
1037
1115
|
);
|
|
@@ -1045,6 +1123,7 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
1045
1123
|
|
|
1046
1124
|
// src/rules/no-raw-env.ts
|
|
1047
1125
|
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
1126
|
+
var CONFIG_FILE_RE = /(^|[\\/])[\w.-]+\.config\.[cm]?[jt]sx?$/;
|
|
1048
1127
|
function isProcessEnv(node) {
|
|
1049
1128
|
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
1050
1129
|
}
|
|
@@ -1062,6 +1141,21 @@ function isBuildTimeConstantAccess(node) {
|
|
|
1062
1141
|
const parent = node.parent;
|
|
1063
1142
|
return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
|
|
1064
1143
|
}
|
|
1144
|
+
function isWriteTarget(node) {
|
|
1145
|
+
const access = node.parent.type === "MemberExpression" && node.parent.object === node ? node.parent : node;
|
|
1146
|
+
const parent = access.parent;
|
|
1147
|
+
if (parent.type === "AssignmentExpression") {
|
|
1148
|
+
return parent.left === access;
|
|
1149
|
+
}
|
|
1150
|
+
if (parent.type === "UnaryExpression") {
|
|
1151
|
+
return parent.operator === "delete";
|
|
1152
|
+
}
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
function isWholeEnvSpread(node) {
|
|
1156
|
+
const parent = node.parent;
|
|
1157
|
+
return parent.type === "SpreadElement" && parent.argument === node;
|
|
1158
|
+
}
|
|
1065
1159
|
var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
1066
1160
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1067
1161
|
)({
|
|
@@ -1078,9 +1172,13 @@ var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
|
1078
1172
|
},
|
|
1079
1173
|
defaultOptions: [],
|
|
1080
1174
|
create(context) {
|
|
1175
|
+
const filename = context.filename;
|
|
1176
|
+
if (isTestFile(filename) || isScriptFile(filename) || CONFIG_FILE_RE.test(filename.replaceAll("\\", "/"))) {
|
|
1177
|
+
return {};
|
|
1178
|
+
}
|
|
1081
1179
|
return {
|
|
1082
1180
|
MemberExpression(node) {
|
|
1083
|
-
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
|
|
1181
|
+
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node) && !isWriteTarget(node) && !isWholeEnvSpread(node)) {
|
|
1084
1182
|
context.report({
|
|
1085
1183
|
node,
|
|
1086
1184
|
messageId: "noRawEnv"
|
|
@@ -1293,6 +1391,32 @@ function enclosingReturnTypeNode(node) {
|
|
|
1293
1391
|
}
|
|
1294
1392
|
return null;
|
|
1295
1393
|
}
|
|
1394
|
+
var PREDICATE_NAME_RE = /^(is|has|can|should|must|does|did|was|were|are)[A-Z]/;
|
|
1395
|
+
var PREDICATE_SUFFIX_RE = /(Exists?|Available|Enabled|Disabled)$/;
|
|
1396
|
+
function enclosingFunctionName(node) {
|
|
1397
|
+
let current = node.parent;
|
|
1398
|
+
while (current !== void 0 && current !== null) {
|
|
1399
|
+
if (isFunctionNode(current)) {
|
|
1400
|
+
if ("id" in current && isNode(current.id) && current.id.type === AST_NODE_TYPES4.Identifier) {
|
|
1401
|
+
return current.id.name;
|
|
1402
|
+
}
|
|
1403
|
+
const parent = current.parent;
|
|
1404
|
+
if (parent?.type === AST_NODE_TYPES4.VariableDeclarator && parent.id.type === AST_NODE_TYPES4.Identifier) {
|
|
1405
|
+
return parent.id.name;
|
|
1406
|
+
}
|
|
1407
|
+
return null;
|
|
1408
|
+
}
|
|
1409
|
+
current = current.parent;
|
|
1410
|
+
}
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
function isNamedBooleanPredicate(catchNode, kind) {
|
|
1414
|
+
if (kind !== "boolean") {
|
|
1415
|
+
return false;
|
|
1416
|
+
}
|
|
1417
|
+
const name = enclosingFunctionName(catchNode);
|
|
1418
|
+
return name !== null && (PREDICATE_NAME_RE.test(name) || PREDICATE_SUFFIX_RE.test(name));
|
|
1419
|
+
}
|
|
1296
1420
|
function isDeclaredBooleanPredicate(catchNode, kind) {
|
|
1297
1421
|
if (kind !== "boolean") {
|
|
1298
1422
|
return false;
|
|
@@ -1336,9 +1460,32 @@ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
|
1336
1460
|
if (isWithin(current, catchNode.body)) {
|
|
1337
1461
|
return false;
|
|
1338
1462
|
}
|
|
1339
|
-
return
|
|
1463
|
+
return returnedSentinelKinds(current.argument).has(kind);
|
|
1340
1464
|
});
|
|
1341
1465
|
}
|
|
1466
|
+
function returnedSentinelKinds(arg) {
|
|
1467
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
1468
|
+
if (arg === null) {
|
|
1469
|
+
return kinds;
|
|
1470
|
+
}
|
|
1471
|
+
const direct = sentinelKind(arg);
|
|
1472
|
+
if (direct !== null) {
|
|
1473
|
+
kinds.add(direct);
|
|
1474
|
+
return kinds;
|
|
1475
|
+
}
|
|
1476
|
+
if (arg.type === AST_NODE_TYPES4.ConditionalExpression) {
|
|
1477
|
+
for (const branch of [arg.consequent, arg.alternate]) {
|
|
1478
|
+
for (const nested of returnedSentinelKinds(branch)) {
|
|
1479
|
+
kinds.add(nested);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
} else if (arg.type === AST_NODE_TYPES4.LogicalExpression && (arg.operator === "??" || arg.operator === "||")) {
|
|
1483
|
+
for (const nested of returnedSentinelKinds(arg.right)) {
|
|
1484
|
+
kinds.add(nested);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return kinds;
|
|
1488
|
+
}
|
|
1342
1489
|
function isWithin(node, ancestor) {
|
|
1343
1490
|
let current = node;
|
|
1344
1491
|
while (current !== void 0 && current !== null) {
|
|
@@ -1408,6 +1555,9 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
1408
1555
|
return;
|
|
1409
1556
|
}
|
|
1410
1557
|
const kind = sentinelKind(last.argument);
|
|
1558
|
+
if (kind !== null && isNamedBooleanPredicate(node, kind)) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1411
1561
|
if (kind !== null && isDeclaredBooleanPredicate(node, kind)) {
|
|
1412
1562
|
return;
|
|
1413
1563
|
}
|
|
@@ -1426,7 +1576,8 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
1426
1576
|
// src/rules/no-sequential-await.ts
|
|
1427
1577
|
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
1428
1578
|
var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
|
|
1429
|
-
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
|
|
1579
|
+
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|preset|plugin|extension|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
|
|
1580
|
+
var BENCH_FILE_RE = /(^|[\\/])bench(marks?)?[\\/]|\.bench\.[cm]?[jt]sx?$/i;
|
|
1430
1581
|
function isFunctionLike(node) {
|
|
1431
1582
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
1432
1583
|
}
|
|
@@ -1460,6 +1611,7 @@ function collectAwaits(root) {
|
|
|
1460
1611
|
});
|
|
1461
1612
|
return awaits;
|
|
1462
1613
|
}
|
|
1614
|
+
var ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;
|
|
1463
1615
|
function hasEarlyExit(root) {
|
|
1464
1616
|
let found = false;
|
|
1465
1617
|
visitScope(root, (node) => {
|
|
@@ -1483,6 +1635,9 @@ function isTimerYield(node) {
|
|
|
1483
1635
|
return true;
|
|
1484
1636
|
}
|
|
1485
1637
|
if (arg.type === "CallExpression") {
|
|
1638
|
+
if (arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.object.type === "Identifier" && arg.callee.object.name === "Promise" && arg.callee.property.type === "Identifier" && (arg.callee.property.name === "resolve" || arg.callee.property.name === "reject")) {
|
|
1639
|
+
return true;
|
|
1640
|
+
}
|
|
1486
1641
|
const name = calleeName2(arg.callee);
|
|
1487
1642
|
return name !== null && TIMER_HELPER_RE.test(name);
|
|
1488
1643
|
}
|
|
@@ -1493,6 +1648,25 @@ function isQueueDrain(node) {
|
|
|
1493
1648
|
const arg = node.argument;
|
|
1494
1649
|
return arg.type === "CallExpression" && arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.property.type === "Identifier" && QUEUE_DRAIN_METHODS.test(arg.callee.property.name);
|
|
1495
1650
|
}
|
|
1651
|
+
function hasAssertion(root) {
|
|
1652
|
+
let found = false;
|
|
1653
|
+
visitScope(root, (node) => {
|
|
1654
|
+
if (node.type !== "CallExpression") {
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
let callee = node.callee;
|
|
1658
|
+
while (callee.type === "MemberExpression") {
|
|
1659
|
+
callee = callee.object;
|
|
1660
|
+
}
|
|
1661
|
+
if (callee.type === "CallExpression") {
|
|
1662
|
+
callee = callee.callee;
|
|
1663
|
+
}
|
|
1664
|
+
if (callee.type === "Identifier" && ASSERTION_CALLEE_RE.test(callee.name)) {
|
|
1665
|
+
found = true;
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
return found;
|
|
1669
|
+
}
|
|
1496
1670
|
function referencesName(root, name) {
|
|
1497
1671
|
let found = false;
|
|
1498
1672
|
visitScope(root, (node) => {
|
|
@@ -1537,13 +1711,16 @@ function testStateIsAssignedInBody(test, body) {
|
|
|
1537
1711
|
});
|
|
1538
1712
|
return found;
|
|
1539
1713
|
}
|
|
1540
|
-
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1714
|
+
function shouldReport(awaits, earlyExit, iterableText, asserts = false) {
|
|
1541
1715
|
if (awaits.length === 0) {
|
|
1542
1716
|
return false;
|
|
1543
1717
|
}
|
|
1544
1718
|
if (earlyExit) {
|
|
1545
1719
|
return false;
|
|
1546
1720
|
}
|
|
1721
|
+
if (asserts) {
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1547
1724
|
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1548
1725
|
return false;
|
|
1549
1726
|
}
|
|
@@ -1567,6 +1744,9 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1567
1744
|
},
|
|
1568
1745
|
defaultOptions: [],
|
|
1569
1746
|
create(context) {
|
|
1747
|
+
if (isTestFile(context.filename) || BENCH_FILE_RE.test(context.filename)) {
|
|
1748
|
+
return {};
|
|
1749
|
+
}
|
|
1570
1750
|
function loopParts(node) {
|
|
1571
1751
|
if (node.type === "ForStatement") {
|
|
1572
1752
|
return [node.body, node.test, node.update];
|
|
@@ -1588,6 +1768,7 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1588
1768
|
}
|
|
1589
1769
|
const awaits = [];
|
|
1590
1770
|
let earlyExit = false;
|
|
1771
|
+
let asserts = false;
|
|
1591
1772
|
for (const part of loopParts(node)) {
|
|
1592
1773
|
if (part === null || isLoop(part)) {
|
|
1593
1774
|
continue;
|
|
@@ -1596,8 +1777,11 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1596
1777
|
if (!earlyExit && hasEarlyExit(part)) {
|
|
1597
1778
|
earlyExit = true;
|
|
1598
1779
|
}
|
|
1780
|
+
if (!asserts && hasAssertion(part)) {
|
|
1781
|
+
asserts = true;
|
|
1782
|
+
}
|
|
1599
1783
|
}
|
|
1600
|
-
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1784
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node), asserts)) {
|
|
1601
1785
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1602
1786
|
}
|
|
1603
1787
|
}
|
|
@@ -1630,7 +1814,7 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1630
1814
|
const awaits = collectAwaits(callback.body);
|
|
1631
1815
|
const earlyExit = hasEarlyExit(callback.body);
|
|
1632
1816
|
const iterableText = context.sourceCode.getText(callee.object);
|
|
1633
|
-
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1817
|
+
if (shouldReport(awaits, earlyExit, iterableText, hasAssertion(callback.body))) {
|
|
1634
1818
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1635
1819
|
}
|
|
1636
1820
|
}
|
|
@@ -1699,20 +1883,30 @@ function isConcatOntoTarget(rhs, target) {
|
|
|
1699
1883
|
}
|
|
1700
1884
|
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1701
1885
|
}
|
|
1702
|
-
function
|
|
1886
|
+
function isDeclaredInsideLoop(variable, loop) {
|
|
1887
|
+
const def = variable.defs[0];
|
|
1888
|
+
if (def === void 0) {
|
|
1889
|
+
return false;
|
|
1890
|
+
}
|
|
1891
|
+
const body = loop.body;
|
|
1892
|
+
const [declStart, declEnd] = def.node.range;
|
|
1893
|
+
const [bodyStart, bodyEnd] = body.range;
|
|
1894
|
+
return declStart >= bodyStart && declEnd <= bodyEnd;
|
|
1895
|
+
}
|
|
1896
|
+
function enclosingLoop(node) {
|
|
1703
1897
|
let child = node;
|
|
1704
1898
|
let parent = node.parent;
|
|
1705
1899
|
while (parent !== void 0 && parent !== null) {
|
|
1706
1900
|
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
1707
1901
|
const loop = parent;
|
|
1708
1902
|
if (loop.body === child) {
|
|
1709
|
-
return
|
|
1903
|
+
return loop;
|
|
1710
1904
|
}
|
|
1711
1905
|
}
|
|
1712
1906
|
child = parent;
|
|
1713
1907
|
parent = parent.parent;
|
|
1714
1908
|
}
|
|
1715
|
-
return
|
|
1909
|
+
return null;
|
|
1716
1910
|
}
|
|
1717
1911
|
var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
1718
1912
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -1730,6 +1924,7 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1730
1924
|
},
|
|
1731
1925
|
defaultOptions: [],
|
|
1732
1926
|
create(context) {
|
|
1927
|
+
const reported = /* @__PURE__ */ new WeakMap();
|
|
1733
1928
|
return {
|
|
1734
1929
|
AssignmentExpression(node) {
|
|
1735
1930
|
if (node.left.type !== "Identifier") {
|
|
@@ -1739,7 +1934,8 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1739
1934
|
if (!isAccumulation) {
|
|
1740
1935
|
return;
|
|
1741
1936
|
}
|
|
1742
|
-
|
|
1937
|
+
const loop = enclosingLoop(node);
|
|
1938
|
+
if (loop === null) {
|
|
1743
1939
|
return;
|
|
1744
1940
|
}
|
|
1745
1941
|
const scope = context.sourceCode.getScope(node);
|
|
@@ -1750,6 +1946,18 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1750
1946
|
if (!isStringInitializedVariable(variable)) {
|
|
1751
1947
|
return;
|
|
1752
1948
|
}
|
|
1949
|
+
if (isDeclaredInsideLoop(variable, loop)) {
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
let seen = reported.get(loop);
|
|
1953
|
+
if (seen === void 0) {
|
|
1954
|
+
seen = /* @__PURE__ */ new Set();
|
|
1955
|
+
reported.set(loop, seen);
|
|
1956
|
+
}
|
|
1957
|
+
if (seen.has(node.left.name)) {
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
seen.add(node.left.name);
|
|
1753
1961
|
context.report({
|
|
1754
1962
|
node,
|
|
1755
1963
|
messageId: "noStringConcatInLoop"
|
|
@@ -1784,7 +1992,31 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
1784
1992
|
"KeyboardEvent",
|
|
1785
1993
|
"TouchEvent"
|
|
1786
1994
|
]);
|
|
1995
|
+
var CLIENT_REQUIRED_MODULES = /* @__PURE__ */ new Set(["next/dynamic"]);
|
|
1787
1996
|
var CLIENT_ONLY_PACKAGES_REGEX = /^(?:@radix-ui\/|framer-motion|react-dom|react-day-picker|@floating-ui\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\/dnd|react-virtualized|react-window|@tanstack\/react-table|@tanstack\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\/)/;
|
|
1997
|
+
var isBareSpecifier = (source) => !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("@/") && !source.startsWith("~");
|
|
1998
|
+
var jsxRootName = (name) => {
|
|
1999
|
+
let current = name;
|
|
2000
|
+
while (current.type === AST_NODE_TYPES5.JSXMemberExpression) {
|
|
2001
|
+
current = current.object;
|
|
2002
|
+
}
|
|
2003
|
+
return current.type === AST_NODE_TYPES5.JSXIdentifier ? current.name : "";
|
|
2004
|
+
};
|
|
2005
|
+
var subtreeReadsImportedBinding = (node, imported) => {
|
|
2006
|
+
if (node.type === AST_NODE_TYPES5.Identifier) {
|
|
2007
|
+
return imported.has(node.name);
|
|
2008
|
+
}
|
|
2009
|
+
for (const key of Object.keys(node)) {
|
|
2010
|
+
if (key === "parent") continue;
|
|
2011
|
+
const value = node[key];
|
|
2012
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2013
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string" && subtreeReadsImportedBinding(child, imported)) {
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return false;
|
|
2019
|
+
};
|
|
1788
2020
|
var isUseClientDirective = (node) => {
|
|
1789
2021
|
return node.type === AST_NODE_TYPES5.ExpressionStatement && node.expression.type === AST_NODE_TYPES5.Literal && node.expression.value === "use client";
|
|
1790
2022
|
};
|
|
@@ -1834,6 +2066,8 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1834
2066
|
}
|
|
1835
2067
|
let directiveNode = null;
|
|
1836
2068
|
let hasClientIndicator = false;
|
|
2069
|
+
const importedLocals = /* @__PURE__ */ new Set();
|
|
2070
|
+
const externalLocals = /* @__PURE__ */ new Set();
|
|
1837
2071
|
const markIfHookOrContext = (callee) => {
|
|
1838
2072
|
if (callee.type === AST_NODE_TYPES5.Identifier) {
|
|
1839
2073
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
@@ -1870,7 +2104,21 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1870
2104
|
},
|
|
1871
2105
|
ImportDeclaration(node) {
|
|
1872
2106
|
if (directiveNode === null) return;
|
|
1873
|
-
if (typeof node.source.value
|
|
2107
|
+
if (typeof node.source.value !== "string") return;
|
|
2108
|
+
const source = node.source.value;
|
|
2109
|
+
if (CLIENT_ONLY_PACKAGES_REGEX.test(source) || CLIENT_REQUIRED_MODULES.has(source)) {
|
|
2110
|
+
hasClientIndicator = true;
|
|
2111
|
+
}
|
|
2112
|
+
for (const specifier of node.specifiers) {
|
|
2113
|
+
importedLocals.add(specifier.local.name);
|
|
2114
|
+
if (isBareSpecifier(source)) {
|
|
2115
|
+
externalLocals.add(specifier.local.name);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
},
|
|
2119
|
+
JSXOpeningElement(node) {
|
|
2120
|
+
if (directiveNode === null) return;
|
|
2121
|
+
if (externalLocals.has(jsxRootName(node.name))) {
|
|
1874
2122
|
hasClientIndicator = true;
|
|
1875
2123
|
}
|
|
1876
2124
|
},
|
|
@@ -1878,6 +2126,10 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1878
2126
|
if (directiveNode === null) return;
|
|
1879
2127
|
if (node.source !== null) {
|
|
1880
2128
|
hasClientIndicator = true;
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
if (node.declaration !== null && subtreeReadsImportedBinding(node.declaration, importedLocals)) {
|
|
2132
|
+
hasClientIndicator = true;
|
|
1881
2133
|
}
|
|
1882
2134
|
},
|
|
1883
2135
|
ExportAllDeclaration(node) {
|
|
@@ -1942,19 +2194,23 @@ function isBooleanTyped(member) {
|
|
|
1942
2194
|
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1943
2195
|
let hasStatusBoolean = false;
|
|
1944
2196
|
let optionalCount = 0;
|
|
2197
|
+
let optionalPayloadCount = 0;
|
|
1945
2198
|
for (const member of typeLiteral.members) {
|
|
1946
2199
|
if (member.type !== AST_NODE_TYPES6.TSPropertySignature) {
|
|
1947
2200
|
continue;
|
|
1948
2201
|
}
|
|
1949
2202
|
if (member.optional) {
|
|
1950
2203
|
optionalCount += 1;
|
|
2204
|
+
if (!isBooleanTyped(member)) {
|
|
2205
|
+
optionalPayloadCount += 1;
|
|
2206
|
+
}
|
|
1951
2207
|
}
|
|
1952
2208
|
const name = getMemberName(member);
|
|
1953
2209
|
if (name !== null && STATUS_MEMBER_NAMES.has(name) && isBooleanTyped(member)) {
|
|
1954
2210
|
hasStatusBoolean = true;
|
|
1955
2211
|
}
|
|
1956
2212
|
}
|
|
1957
|
-
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
2213
|
+
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS && optionalPayloadCount >= 1;
|
|
1958
2214
|
}
|
|
1959
2215
|
var prefer_discriminated_union_default = ESLintUtils13.RuleCreator(
|
|
1960
2216
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -2035,7 +2291,51 @@ var isRawPayloadSource = (node) => {
|
|
|
2035
2291
|
return true;
|
|
2036
2292
|
}
|
|
2037
2293
|
const object = unwrap(callee.object);
|
|
2038
|
-
return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES7.Identifier && object.name === "JSON"
|
|
2294
|
+
return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES7.Identifier && object.name === "JSON" && // ...but not `JSON.parse(readFileSync(p, "utf8"))` — see isLocalFileRead.
|
|
2295
|
+
!isLocalFileRead(current.arguments[0]);
|
|
2296
|
+
};
|
|
2297
|
+
var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
|
|
2298
|
+
var isLocalFileRead = (node) => {
|
|
2299
|
+
let found = false;
|
|
2300
|
+
const visit = (current) => {
|
|
2301
|
+
if (found || current === null || current === void 0) return;
|
|
2302
|
+
if (current.type === AST_NODE_TYPES7.CallExpression) {
|
|
2303
|
+
const callee = unwrap(current.callee);
|
|
2304
|
+
const name = callee?.type === AST_NODE_TYPES7.Identifier ? callee.name : callee?.type === AST_NODE_TYPES7.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES7.Identifier ? callee.property.name : null;
|
|
2305
|
+
if (name !== null && FILE_READ_RE.test(name)) {
|
|
2306
|
+
found = true;
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
for (const key of Object.keys(current)) {
|
|
2311
|
+
if (key === "parent") continue;
|
|
2312
|
+
const value = current[key];
|
|
2313
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2314
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string") {
|
|
2315
|
+
visit(child);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
visit(node);
|
|
2321
|
+
return found;
|
|
2322
|
+
};
|
|
2323
|
+
var ASSERTION_CALLEE_RE2 = /^(expect|assert|should|invariant)$/;
|
|
2324
|
+
var isInsideAssertion = (node) => {
|
|
2325
|
+
for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
2326
|
+
if (current.type !== AST_NODE_TYPES7.CallExpression) continue;
|
|
2327
|
+
let callee = current.callee;
|
|
2328
|
+
while (callee.type === AST_NODE_TYPES7.MemberExpression) {
|
|
2329
|
+
callee = callee.object;
|
|
2330
|
+
}
|
|
2331
|
+
if (callee.type === AST_NODE_TYPES7.CallExpression) {
|
|
2332
|
+
callee = callee.callee;
|
|
2333
|
+
}
|
|
2334
|
+
if (callee.type === AST_NODE_TYPES7.Identifier && ASSERTION_CALLEE_RE2.test(callee.name)) {
|
|
2335
|
+
return true;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
return false;
|
|
2039
2339
|
};
|
|
2040
2340
|
var findVariable2 = (scope, name) => {
|
|
2041
2341
|
let current = scope;
|
|
@@ -2094,6 +2394,9 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
2094
2394
|
},
|
|
2095
2395
|
defaultOptions: [],
|
|
2096
2396
|
create(context) {
|
|
2397
|
+
if (isTestFile(context.filename)) {
|
|
2398
|
+
return {};
|
|
2399
|
+
}
|
|
2097
2400
|
const unvalidatedVariables = /* @__PURE__ */ new Set();
|
|
2098
2401
|
const trackInitializer = (declarator) => {
|
|
2099
2402
|
if (!isRawPayloadSource(declarator.init)) return;
|
|
@@ -2165,6 +2468,7 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
2165
2468
|
}
|
|
2166
2469
|
},
|
|
2167
2470
|
MemberExpression(node) {
|
|
2471
|
+
if (isInsideAssertion(node)) return;
|
|
2168
2472
|
const scope = context.sourceCode.getScope(node);
|
|
2169
2473
|
const obj = unwrap(node.object);
|
|
2170
2474
|
if (isRawPayloadSource(obj)) {
|
|
@@ -2373,7 +2677,8 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
2373
2677
|
import { ESLintUtils as ESLintUtils16 } from "@typescript-eslint/utils";
|
|
2374
2678
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
2375
2679
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
2376
|
-
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
2680
|
+
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|-(?:test|spec)\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/__testfixtures__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
2681
|
+
var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
|
|
2377
2682
|
function getScope(context, node) {
|
|
2378
2683
|
return context.sourceCode.getScope(node);
|
|
2379
2684
|
}
|
|
@@ -2469,8 +2774,15 @@ var prefer_server_actions_default = ESLintUtils16.RuleCreator(
|
|
|
2469
2774
|
if (SKIP_FILE_REGEX.test(filename)) {
|
|
2470
2775
|
return {};
|
|
2471
2776
|
}
|
|
2777
|
+
let isNonReactFramework = false;
|
|
2472
2778
|
return {
|
|
2779
|
+
ImportDeclaration(node) {
|
|
2780
|
+
if (typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(node.source.value)) {
|
|
2781
|
+
isNonReactFramework = true;
|
|
2782
|
+
}
|
|
2783
|
+
},
|
|
2473
2784
|
CallExpression(node) {
|
|
2785
|
+
if (isNonReactFramework) return;
|
|
2474
2786
|
let isMutation = false;
|
|
2475
2787
|
if (node.callee.type === "Identifier" && node.callee.name === "fetch") {
|
|
2476
2788
|
const urlArg = node.arguments[0];
|
|
@@ -2567,6 +2879,9 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
2567
2879
|
},
|
|
2568
2880
|
defaultOptions: [],
|
|
2569
2881
|
create(context) {
|
|
2882
|
+
if (isTestFile(context.filename)) {
|
|
2883
|
+
return {};
|
|
2884
|
+
}
|
|
2570
2885
|
return {
|
|
2571
2886
|
JSXOpeningElement(node) {
|
|
2572
2887
|
if (node.name.type !== "JSXIdentifier") {
|
|
@@ -2743,6 +3058,9 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
2743
3058
|
},
|
|
2744
3059
|
defaultOptions: [],
|
|
2745
3060
|
create(context) {
|
|
3061
|
+
if (isTestFile(context.filename)) {
|
|
3062
|
+
return {};
|
|
3063
|
+
}
|
|
2746
3064
|
const isFormSourceIdentifier = (node) => {
|
|
2747
3065
|
if (node.type !== AST_NODE_TYPES11.Identifier) return false;
|
|
2748
3066
|
if (/formdata/i.test(node.name)) return true;
|
|
@@ -2816,6 +3134,25 @@ var CONVENTIONS = {
|
|
|
2816
3134
|
suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
|
|
2817
3135
|
either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
|
|
2818
3136
|
};
|
|
3137
|
+
var CONTAINS_SCHEMA_RE = /schema/i;
|
|
3138
|
+
var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
|
|
3139
|
+
"parse",
|
|
3140
|
+
"parseAsync",
|
|
3141
|
+
"safeParse",
|
|
3142
|
+
"safeParseAsync",
|
|
3143
|
+
"encode",
|
|
3144
|
+
"decode",
|
|
3145
|
+
"encodeAsync",
|
|
3146
|
+
"decodeAsync",
|
|
3147
|
+
"safeEncode",
|
|
3148
|
+
"safeDecode",
|
|
3149
|
+
"safeEncodeAsync",
|
|
3150
|
+
"safeDecodeAsync",
|
|
3151
|
+
"toJSONSchema",
|
|
3152
|
+
"registry",
|
|
3153
|
+
"implement"
|
|
3154
|
+
]);
|
|
3155
|
+
var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES12.Identifier ? callee.property.name : null;
|
|
2819
3156
|
var calleeChainStartsWithZ = (node) => {
|
|
2820
3157
|
let current = node;
|
|
2821
3158
|
while (current.type === AST_NODE_TYPES12.MemberExpression) {
|
|
@@ -2860,7 +3197,12 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
2860
3197
|
},
|
|
2861
3198
|
defaultOptions: [{}],
|
|
2862
3199
|
create(context, [optionsArg]) {
|
|
2863
|
-
const
|
|
3200
|
+
const convention = optionsArg?.convention ?? "either";
|
|
3201
|
+
const { test, messageId } = CONVENTIONS[convention];
|
|
3202
|
+
const acceptsSchemaWord = convention !== "prefix";
|
|
3203
|
+
if (isTestFile(context.filename)) {
|
|
3204
|
+
return {};
|
|
3205
|
+
}
|
|
2864
3206
|
return {
|
|
2865
3207
|
VariableDeclarator(node) {
|
|
2866
3208
|
const init = node.init;
|
|
@@ -2869,8 +3211,11 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
2869
3211
|
const callee = init.callee;
|
|
2870
3212
|
if (callee.type !== AST_NODE_TYPES12.MemberExpression) return;
|
|
2871
3213
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
3214
|
+
const terminal = terminalMethodName(callee);
|
|
3215
|
+
if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
|
|
2872
3216
|
if (node.id.type !== AST_NODE_TYPES12.Identifier) return;
|
|
2873
3217
|
if (test.test(node.id.name)) return;
|
|
3218
|
+
if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
|
|
2874
3219
|
context.report({
|
|
2875
3220
|
node: node.id,
|
|
2876
3221
|
messageId
|
|
@@ -3092,25 +3437,24 @@ var no_cors_wildcard_with_credentials_default = ESLintUtils21.RuleCreator(
|
|
|
3092
3437
|
|
|
3093
3438
|
// src/rules/no-silent-promise-catch.ts
|
|
3094
3439
|
import { AST_NODE_TYPES as AST_NODE_TYPES13, ESLintUtils as ESLintUtils22 } from "@typescript-eslint/utils";
|
|
3095
|
-
|
|
3096
|
-
// src/rules/_paths.ts
|
|
3097
|
-
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
3098
|
-
function isTestFile(filename) {
|
|
3099
|
-
const normalized = filename.replaceAll("\\", "/");
|
|
3100
|
-
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
3101
|
-
if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(base)) {
|
|
3102
|
-
return true;
|
|
3103
|
-
}
|
|
3104
|
-
return /(^|\/)(tests?|__tests__|__mocks__|fixtures)\//.test(normalized);
|
|
3105
|
-
}
|
|
3106
|
-
function isScriptFile(filename) {
|
|
3107
|
-
return SCRIPT_FILE_RE.test(filename);
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
// src/rules/no-silent-promise-catch.ts
|
|
3111
3440
|
function isBodyParseCall(node) {
|
|
3112
3441
|
return node.type === AST_NODE_TYPES13.CallExpression && node.arguments.length === 0 && node.callee.type === AST_NODE_TYPES13.MemberExpression && !node.callee.computed && node.callee.property.type === AST_NODE_TYPES13.Identifier && (node.callee.property.name === "json" || node.callee.property.name === "text");
|
|
3113
3442
|
}
|
|
3443
|
+
var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
|
|
3444
|
+
"cancel",
|
|
3445
|
+
"close",
|
|
3446
|
+
"abort",
|
|
3447
|
+
"destroy",
|
|
3448
|
+
"dispose",
|
|
3449
|
+
"release",
|
|
3450
|
+
"unlock",
|
|
3451
|
+
"disconnect"
|
|
3452
|
+
]);
|
|
3453
|
+
var DIRECTIVE_COMMENT_RE = /^\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;
|
|
3454
|
+
var isExplanatory = (comment) => !DIRECTIVE_COMMENT_RE.test(comment.value);
|
|
3455
|
+
function isTeardownCall(node) {
|
|
3456
|
+
return node.type === AST_NODE_TYPES13.CallExpression && node.callee.type === AST_NODE_TYPES13.MemberExpression && !node.callee.computed && node.callee.property.type === AST_NODE_TYPES13.Identifier && TEARDOWN_METHODS.has(node.callee.property.name);
|
|
3457
|
+
}
|
|
3114
3458
|
function isSilentExpression(node) {
|
|
3115
3459
|
switch (node.type) {
|
|
3116
3460
|
case AST_NODE_TYPES13.Literal:
|
|
@@ -3164,6 +3508,22 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3164
3508
|
if (isTestFile(context.filename)) {
|
|
3165
3509
|
return {};
|
|
3166
3510
|
}
|
|
3511
|
+
const hasExplanatoryComment = (call, handler) => {
|
|
3512
|
+
const sourceCode = context.sourceCode;
|
|
3513
|
+
if (sourceCode.getCommentsInside(handler).some(isExplanatory)) {
|
|
3514
|
+
return true;
|
|
3515
|
+
}
|
|
3516
|
+
let statement = call;
|
|
3517
|
+
while (statement.parent !== void 0 && statement.parent !== null && !statement.type.endsWith("Statement") && statement.type !== AST_NODE_TYPES13.VariableDeclaration) {
|
|
3518
|
+
statement = statement.parent;
|
|
3519
|
+
}
|
|
3520
|
+
if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {
|
|
3521
|
+
return true;
|
|
3522
|
+
}
|
|
3523
|
+
return sourceCode.getCommentsAfter(statement).some(
|
|
3524
|
+
(c) => isExplanatory(c) && c.loc.start.line === statement.loc.end.line
|
|
3525
|
+
);
|
|
3526
|
+
};
|
|
3167
3527
|
return {
|
|
3168
3528
|
CallExpression(node) {
|
|
3169
3529
|
if (node.callee.type !== AST_NODE_TYPES13.MemberExpression || node.callee.computed || node.callee.property.type !== AST_NODE_TYPES13.Identifier || node.callee.property.name !== "catch") {
|
|
@@ -3172,6 +3532,12 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3172
3532
|
if (isBodyParseCall(node.callee.object)) {
|
|
3173
3533
|
return;
|
|
3174
3534
|
}
|
|
3535
|
+
if (isTeardownCall(node.callee.object)) {
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
if (node.parent.type === AST_NODE_TYPES13.MemberExpression && node.parent.object === node) {
|
|
3539
|
+
return;
|
|
3540
|
+
}
|
|
3175
3541
|
if (node.arguments.length !== 1) {
|
|
3176
3542
|
return;
|
|
3177
3543
|
}
|
|
@@ -3179,6 +3545,9 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3179
3545
|
if (handler === void 0 || handler.type !== AST_NODE_TYPES13.ArrowFunctionExpression && handler.type !== AST_NODE_TYPES13.FunctionExpression) {
|
|
3180
3546
|
return;
|
|
3181
3547
|
}
|
|
3548
|
+
if (hasExplanatoryComment(node, handler)) {
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3182
3551
|
if (isSilentHandler(handler)) {
|
|
3183
3552
|
context.report({ node, messageId: "silentCatch" });
|
|
3184
3553
|
}
|
|
@@ -3193,6 +3562,7 @@ import {
|
|
|
3193
3562
|
ASTUtils,
|
|
3194
3563
|
ESLintUtils as ESLintUtils23
|
|
3195
3564
|
} from "@typescript-eslint/utils";
|
|
3565
|
+
var CODEMOD_FIXTURE_RE = /[\\/]__testfixtures__[\\/]/;
|
|
3196
3566
|
var GLOBAL_OBJECTS = /* @__PURE__ */ new Set([
|
|
3197
3567
|
"globalThis",
|
|
3198
3568
|
"window",
|
|
@@ -3255,7 +3625,7 @@ var require_fetch_timeout_default = ESLintUtils23.RuleCreator(
|
|
|
3255
3625
|
},
|
|
3256
3626
|
defaultOptions: [{}],
|
|
3257
3627
|
create(context, [optionsArg]) {
|
|
3258
|
-
if (isTestFile(context.filename) || isScriptFile(context.filename)) {
|
|
3628
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || CODEMOD_FIXTURE_RE.test(context.filename)) {
|
|
3259
3629
|
return {};
|
|
3260
3630
|
}
|
|
3261
3631
|
const allowIn = optionsArg?.allowIn ?? [];
|
|
@@ -3977,6 +4347,9 @@ var no_unsafe_cast_default = ESLintUtils27.RuleCreator(
|
|
|
3977
4347
|
},
|
|
3978
4348
|
defaultOptions: [],
|
|
3979
4349
|
create(context) {
|
|
4350
|
+
if (isTestFile(context.filename)) {
|
|
4351
|
+
return {};
|
|
4352
|
+
}
|
|
3980
4353
|
function checkAssertion(node) {
|
|
3981
4354
|
if (isConstAssertion(node.typeAnnotation)) {
|
|
3982
4355
|
return;
|
|
@@ -4477,6 +4850,7 @@ var single_public_export_default = ESLintUtils29.RuleCreator(
|
|
|
4477
4850
|
const base = basename(context.filename);
|
|
4478
4851
|
if (base.endsWith(".d.ts")) return {};
|
|
4479
4852
|
if (TEST_FILE_RE.test(base)) return {};
|
|
4853
|
+
if (isTestFile(context.filename)) return {};
|
|
4480
4854
|
const stem = stemOf(base);
|
|
4481
4855
|
if (!JUNK_DRAWER_STEMS.has(stem.toLowerCase())) return {};
|
|
4482
4856
|
return {
|
|
@@ -4677,6 +5051,7 @@ var no_offset_pagination_default = ESLintUtils30.RuleCreator(
|
|
|
4677
5051
|
// src/rules/no-positional-tuple-return.ts
|
|
4678
5052
|
import { AST_NODE_TYPES as AST_NODE_TYPES21, ESLintUtils as ESLintUtils31 } from "@typescript-eslint/utils";
|
|
4679
5053
|
var MIN_ELEMENTS = 2;
|
|
5054
|
+
var ACCESSOR_PAIR_LENGTH = 2;
|
|
4680
5055
|
var AWAITABLE_TYPES = /* @__PURE__ */ new Set(["Promise", "PromiseLike", "Awaited"]);
|
|
4681
5056
|
function tupleReturnType(node) {
|
|
4682
5057
|
if (node.type === AST_NODE_TYPES21.TSTupleType) {
|
|
@@ -4705,6 +5080,9 @@ function isPermittedTuple(tuple, sourceCode) {
|
|
|
4705
5080
|
if (elements[0]?.type === AST_NODE_TYPES21.TSLiteralType) {
|
|
4706
5081
|
return true;
|
|
4707
5082
|
}
|
|
5083
|
+
if (elements.length === ACCESSOR_PAIR_LENGTH && elements.some((element) => element.type === AST_NODE_TYPES21.TSFunctionType)) {
|
|
5084
|
+
return true;
|
|
5085
|
+
}
|
|
4708
5086
|
const texts = new Set(elements.map((element) => normalizedText(sourceCode, element)));
|
|
4709
5087
|
return texts.size === 1;
|
|
4710
5088
|
}
|
|
@@ -4904,6 +5282,9 @@ var no_repeated_string_literal_default = ESLintUtils32.RuleCreator(
|
|
|
4904
5282
|
}
|
|
4905
5283
|
},
|
|
4906
5284
|
TemplateLiteral(node) {
|
|
5285
|
+
if (node.parent.type === AST_NODE_TYPES22.TaggedTemplateExpression) {
|
|
5286
|
+
return;
|
|
5287
|
+
}
|
|
4907
5288
|
const [only] = node.quasis;
|
|
4908
5289
|
if (node.expressions.length === 0 && only !== void 0) {
|
|
4909
5290
|
record(only.value.cooked ?? only.value.raw, node);
|
|
@@ -5127,6 +5508,7 @@ import { AST_NODE_TYPES as AST_NODE_TYPES24, ESLintUtils as ESLintUtils35 } from
|
|
|
5127
5508
|
var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["===", "!==", "==", "!="]);
|
|
5128
5509
|
var SENTINEL_IDENTIFIERS = /* @__PURE__ */ new Set(["undefined", "NaN"]);
|
|
5129
5510
|
var SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;
|
|
5511
|
+
var SENTINEL_PREFIX_RE = /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;
|
|
5130
5512
|
function isConstantReference(identifier) {
|
|
5131
5513
|
if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;
|
|
5132
5514
|
return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);
|
|
@@ -5138,9 +5520,9 @@ function isExcludedOperand(node) {
|
|
|
5138
5520
|
case AST_NODE_TYPES24.TemplateLiteral:
|
|
5139
5521
|
return node.expressions.length === 0;
|
|
5140
5522
|
case AST_NODE_TYPES24.Identifier:
|
|
5141
|
-
return SENTINEL_IDENTIFIERS.has(node.name) || isConstantReference(node.name);
|
|
5523
|
+
return SENTINEL_IDENTIFIERS.has(node.name) || SENTINEL_PREFIX_RE.test(node.name) || isConstantReference(node.name);
|
|
5142
5524
|
case AST_NODE_TYPES24.MemberExpression:
|
|
5143
|
-
return !node.computed && node.property.type === AST_NODE_TYPES24.Identifier && isConstantReference(node.property.name);
|
|
5525
|
+
return !node.computed && node.property.type === AST_NODE_TYPES24.Identifier && (SENTINEL_PREFIX_RE.test(node.property.name) || isConstantReference(node.property.name));
|
|
5144
5526
|
default:
|
|
5145
5527
|
return false;
|
|
5146
5528
|
}
|
|
@@ -5290,6 +5672,23 @@ function runtimeConcatOperands(node) {
|
|
|
5290
5672
|
(operand) => operand.type !== AST_NODE_TYPES25.Literal && !isStaticFragment(operand)
|
|
5291
5673
|
);
|
|
5292
5674
|
}
|
|
5675
|
+
var SQL_STATEMENT_RE = /\b(?:select\s|insert\s+into\b|insert\s+or\b|update\s+\w|delete\s+from\b|replace\s+into\b|merge\s+into\b|upsert\s+into\b|create\s+(?:temp(?:orary)?\s+)?(?:table|index|view|trigger|schema|database)\b|alter\s+table\b|drop\s+(?:table|index|view|trigger)\b|truncate\s+table\b|pragma\s+\w|with\s+\w+\s+as\s*\(|from\s+\w+\s+where\b)/i;
|
|
5676
|
+
var RUNTIME_MARKER = " ? ";
|
|
5677
|
+
function staticStatementText(node) {
|
|
5678
|
+
if (node.type === AST_NODE_TYPES25.TemplateLiteral) {
|
|
5679
|
+
return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);
|
|
5680
|
+
}
|
|
5681
|
+
if (node.type === AST_NODE_TYPES25.Literal) {
|
|
5682
|
+
return typeof node.value === "string" ? node.value : RUNTIME_MARKER;
|
|
5683
|
+
}
|
|
5684
|
+
if (node.type === AST_NODE_TYPES25.BinaryExpression && node.operator === "+") {
|
|
5685
|
+
return staticStatementText(node.left) + staticStatementText(node.right);
|
|
5686
|
+
}
|
|
5687
|
+
return RUNTIME_MARKER;
|
|
5688
|
+
}
|
|
5689
|
+
function looksLikeSql(node) {
|
|
5690
|
+
return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));
|
|
5691
|
+
}
|
|
5293
5692
|
function statementMethodName(node, methods) {
|
|
5294
5693
|
const callee = node.callee;
|
|
5295
5694
|
if (callee.type !== AST_NODE_TYPES25.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES25.Identifier) {
|
|
@@ -5334,7 +5733,7 @@ var no_dynamic_sql_default = ESLintUtils37.RuleCreator(
|
|
|
5334
5733
|
return;
|
|
5335
5734
|
}
|
|
5336
5735
|
const statement = node.arguments[0];
|
|
5337
|
-
if (statement === void 0) {
|
|
5736
|
+
if (statement === void 0 || !looksLikeSql(statement)) {
|
|
5338
5737
|
return;
|
|
5339
5738
|
}
|
|
5340
5739
|
const offenders = statement.type === AST_NODE_TYPES25.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
@@ -5356,10 +5755,21 @@ var DEFAULT_ALLOW = [
|
|
|
5356
5755
|
"[\\\\/]clients?[\\\\/]",
|
|
5357
5756
|
"-client\\.[cm]?[jt]sx?$",
|
|
5358
5757
|
"[\\\\/]http-client\\.[cm]?[jt]sx?$",
|
|
5758
|
+
// The `api` spelling of the same client-layer convention: an `api/` directory,
|
|
5759
|
+
// a bare `api.ts`, or a `*-api.ts` / `*.api.ts` module.
|
|
5760
|
+
"[\\\\/]api[\\\\/]",
|
|
5761
|
+
"[\\\\/]api\\.[cm]?[jt]sx?$",
|
|
5762
|
+
"[-.]api\\.[cm]?[jt]sx?$",
|
|
5359
5763
|
"\\.test\\.",
|
|
5360
5764
|
"\\.spec\\.",
|
|
5765
|
+
// `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.
|
|
5766
|
+
"\\.(test|spec)-d\\.[cm]?[jt]sx?$",
|
|
5767
|
+
"-(test|spec)\\.[cm]?[jt]sx?$",
|
|
5361
5768
|
"[\\\\/]__tests__[\\\\/]",
|
|
5362
|
-
"[\\\\/]__mocks__[\\\\/]"
|
|
5769
|
+
"[\\\\/]__mocks__[\\\\/]",
|
|
5770
|
+
"[\\\\/]tests?[\\\\/]",
|
|
5771
|
+
// jscodeshift input/output fixtures — text a codemod transforms, not code.
|
|
5772
|
+
"[\\\\/]__testfixtures__[\\\\/]"
|
|
5363
5773
|
];
|
|
5364
5774
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
|
|
5365
5775
|
"globalThis",
|
|
@@ -5614,6 +6024,9 @@ var no_zod_native_enum_default = ESLintUtils40.RuleCreator(
|
|
|
5614
6024
|
if (isIgnoredFile2(context.filename, sourceCode.getText())) {
|
|
5615
6025
|
return {};
|
|
5616
6026
|
}
|
|
6027
|
+
if (isTestFile(context.filename)) {
|
|
6028
|
+
return {};
|
|
6029
|
+
}
|
|
5617
6030
|
let services;
|
|
5618
6031
|
try {
|
|
5619
6032
|
services = ESLintUtils40.getParserServices(context);
|
|
@@ -6050,7 +6463,7 @@ var rules = {
|
|
|
6050
6463
|
var plugin = {
|
|
6051
6464
|
meta: {
|
|
6052
6465
|
name: "@sarj/eslint-plugin",
|
|
6053
|
-
version: "2.
|
|
6466
|
+
version: "2.12.0"
|
|
6054
6467
|
},
|
|
6055
6468
|
rules,
|
|
6056
6469
|
configs: {
|