@sarj/eslint-plugin 2.11.0 → 2.12.1
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 +562 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -5
- package/dist/index.d.ts +45 -5
- package/dist/index.js +562 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,30 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
|
|
38
38
|
// src/rules/enforce-file-structure.ts
|
|
39
39
|
var import_utils = require("@typescript-eslint/utils");
|
|
40
|
+
|
|
41
|
+
// src/rules/_paths.ts
|
|
42
|
+
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
43
|
+
var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
44
|
+
var GENERATED_FILE_RE = /([\\/]generated[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)/;
|
|
45
|
+
function isTestFile(filename) {
|
|
46
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
47
|
+
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
48
|
+
if (/\.(test|spec|e2e|integration)\.[cm]?[jt]sx?$/.test(base)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return /(^|\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\//.test(normalized);
|
|
52
|
+
}
|
|
53
|
+
function isStoryFile(filename) {
|
|
54
|
+
return STORY_FILE_RE.test(filename);
|
|
55
|
+
}
|
|
56
|
+
function isGeneratedFile(filename, sourceText = "") {
|
|
57
|
+
return GENERATED_FILE_RE.test(filename.replaceAll("\\", "/")) || /@generated\b/.test(sourceText.slice(0, 1024));
|
|
58
|
+
}
|
|
59
|
+
function isScriptFile(filename) {
|
|
60
|
+
return SCRIPT_FILE_RE.test(filename);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/rules/enforce-file-structure.ts
|
|
40
64
|
var classifyStatement = (statement) => {
|
|
41
65
|
switch (statement.type) {
|
|
42
66
|
case import_utils.AST_NODE_TYPES.ImportDeclaration:
|
|
@@ -73,6 +97,9 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
|
|
|
73
97
|
},
|
|
74
98
|
defaultOptions: [],
|
|
75
99
|
create(context) {
|
|
100
|
+
if (isTestFile(context.filename)) {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
76
103
|
return {
|
|
77
104
|
Program(node) {
|
|
78
105
|
const body = node.body;
|
|
@@ -86,6 +113,7 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
|
|
|
86
113
|
});
|
|
87
114
|
}
|
|
88
115
|
let seenBody = false;
|
|
116
|
+
let inMisplacedRun = false;
|
|
89
117
|
for (const statement of body) {
|
|
90
118
|
if (isStringDirective(statement)) continue;
|
|
91
119
|
switch (classifyStatement(statement)) {
|
|
@@ -93,9 +121,11 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
|
|
|
93
121
|
continue;
|
|
94
122
|
case "body":
|
|
95
123
|
seenBody = true;
|
|
124
|
+
inMisplacedRun = false;
|
|
96
125
|
continue;
|
|
97
126
|
case "import":
|
|
98
|
-
if (seenBody) {
|
|
127
|
+
if (seenBody && !inMisplacedRun) {
|
|
128
|
+
inMisplacedRun = true;
|
|
99
129
|
context.report({
|
|
100
130
|
node: statement,
|
|
101
131
|
messageId: "importsFirst"
|
|
@@ -251,9 +281,13 @@ var STEP_NARRATION_RE = /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|a
|
|
|
251
281
|
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;
|
|
252
282
|
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;
|
|
253
283
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
284
|
+
var ENUMERATED_ITEM_RE = /^(?:\d+[.):]|[-*•])\s+\S/;
|
|
285
|
+
var ENUMERATED_ITEM_MIN_WORDS = 3;
|
|
286
|
+
var ENUMERATED_PREAMBLE_MIN_ITEMS = 2;
|
|
254
287
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
255
288
|
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
256
289
|
var REGION_RE = /^#?(?:end)?region\b/i;
|
|
290
|
+
var DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;
|
|
257
291
|
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\.)/;
|
|
258
292
|
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
259
293
|
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
@@ -267,7 +301,8 @@ function isDirective(text) {
|
|
|
267
301
|
function isBanner(text) {
|
|
268
302
|
const t = text.trim();
|
|
269
303
|
if (!t) return false;
|
|
270
|
-
|
|
304
|
+
if (BANNER_FULL_RE.test(t) || REGION_RE.test(t)) return true;
|
|
305
|
+
return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);
|
|
271
306
|
}
|
|
272
307
|
function looksLikeCode(text) {
|
|
273
308
|
const t = text.trim();
|
|
@@ -278,6 +313,10 @@ function looksLikeCode(text) {
|
|
|
278
313
|
function hasPseudocode(text) {
|
|
279
314
|
return PSEUDOCODE_RE.test(text);
|
|
280
315
|
}
|
|
316
|
+
function isEnumeratedProseItem(text) {
|
|
317
|
+
const t = text.trim();
|
|
318
|
+
return ENUMERATED_ITEM_RE.test(t) && t.split(/\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;
|
|
319
|
+
}
|
|
281
320
|
function isProse(text) {
|
|
282
321
|
const t = text.trim();
|
|
283
322
|
if (!t) return false;
|
|
@@ -390,13 +429,32 @@ function restatesNextLine(body, statement) {
|
|
|
390
429
|
const code = codeTokens(head);
|
|
391
430
|
return content.every((word) => code.has(word));
|
|
392
431
|
}
|
|
393
|
-
|
|
432
|
+
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;
|
|
433
|
+
function isRedundantNarration(body, statementBelow, standalone) {
|
|
394
434
|
const t = body.trim();
|
|
395
435
|
if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;
|
|
396
|
-
if (
|
|
397
|
-
|
|
436
|
+
if (standalone) {
|
|
437
|
+
if (STEP_NARRATION_RE.test(t)) return true;
|
|
438
|
+
if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;
|
|
439
|
+
}
|
|
398
440
|
return restatesNextLine(t, statementBelow);
|
|
399
441
|
}
|
|
442
|
+
function areAdjacentLineComments(a, b) {
|
|
443
|
+
return a !== void 0 && b !== void 0 && a.type === "Line" && b.type === "Line" && b.loc.start.line === a.loc.end.line + 1;
|
|
444
|
+
}
|
|
445
|
+
function isInsideCommentRun(comments, index) {
|
|
446
|
+
const comment = comments[index];
|
|
447
|
+
return areAdjacentLineComments(comments[index - 1], comment) || areAdjacentLineComments(comment, comments[index + 1]);
|
|
448
|
+
}
|
|
449
|
+
var LEAD_IN_SCAN_LIMIT = 24;
|
|
450
|
+
function hasIllustrationLeadInAbove(comments, index) {
|
|
451
|
+
for (let i = index - 1; i >= 0 && index - i <= LEAD_IN_SCAN_LIMIT; i--) {
|
|
452
|
+
if (!areAdjacentLineComments(comments[i], comments[i + 1])) return false;
|
|
453
|
+
const body = stripCommentMarker(comments[i]?.value ?? "");
|
|
454
|
+
if (body.length > 0 && body.endsWith(":")) return true;
|
|
455
|
+
}
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
400
458
|
function hasCommentedOutCode(texts, precedingProse) {
|
|
401
459
|
for (let i = 0; i < texts.length; i++) {
|
|
402
460
|
const line = texts[i];
|
|
@@ -456,6 +514,9 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
456
514
|
const bodies = leading.map((c) => stripCommentMarker(c.value));
|
|
457
515
|
if (bodies.some((body) => LICENSE_RE.test(body))) return;
|
|
458
516
|
if (bodies.some((body) => isProse(body))) return;
|
|
517
|
+
if (bodies.filter(isEnumeratedProseItem).length >= ENUMERATED_PREAMBLE_MIN_ITEMS) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
459
520
|
context.report({ node: first, messageId: "fileHeaderPreamble" });
|
|
460
521
|
}
|
|
461
522
|
return {
|
|
@@ -474,14 +535,15 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
474
535
|
}
|
|
475
536
|
const prev = comments[i - 1];
|
|
476
537
|
const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
|
|
477
|
-
if (hasCommentedOutCode(texts, precedingProse)) {
|
|
538
|
+
if (hasCommentedOutCode(texts, precedingProse) && !hasIllustrationLeadInAbove(comments, i)) {
|
|
478
539
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
479
540
|
continue;
|
|
480
541
|
}
|
|
481
542
|
if (comment.type === "Line" && texts.length === 1) {
|
|
482
543
|
const body = texts[0];
|
|
483
544
|
const statement = restatableStatementBelow(comment, sourceCode);
|
|
484
|
-
|
|
545
|
+
const standalone = !isInsideCommentRun(comments, i);
|
|
546
|
+
if (body !== void 0 && isRedundantNarration(body, statement, standalone)) {
|
|
485
547
|
context.report({ node: comment, messageId: "redundantNarration" });
|
|
486
548
|
}
|
|
487
549
|
}
|
|
@@ -720,6 +782,9 @@ var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
720
782
|
},
|
|
721
783
|
defaultOptions: [],
|
|
722
784
|
create(context) {
|
|
785
|
+
if (isTestFile(context.filename)) {
|
|
786
|
+
return {};
|
|
787
|
+
}
|
|
723
788
|
return {
|
|
724
789
|
CallExpression(node) {
|
|
725
790
|
if (!isMathRandomCall(node)) {
|
|
@@ -749,6 +814,22 @@ var import_utils6 = require("@typescript-eslint/utils");
|
|
|
749
814
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
750
815
|
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
751
816
|
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
817
|
+
var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
|
|
818
|
+
"data",
|
|
819
|
+
"status",
|
|
820
|
+
"statuscode",
|
|
821
|
+
"statustext",
|
|
822
|
+
"code",
|
|
823
|
+
"issues",
|
|
824
|
+
"details",
|
|
825
|
+
"body",
|
|
826
|
+
"payload",
|
|
827
|
+
"response",
|
|
828
|
+
"info",
|
|
829
|
+
"meta",
|
|
830
|
+
"metadata",
|
|
831
|
+
"context"
|
|
832
|
+
]);
|
|
752
833
|
function isCatchBinding(scope, name) {
|
|
753
834
|
let current = scope;
|
|
754
835
|
while (current) {
|
|
@@ -772,7 +853,11 @@ function memberSuggestsError(member, scope) {
|
|
|
772
853
|
const base = member.object;
|
|
773
854
|
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
774
855
|
if (baseSuggestsError) {
|
|
775
|
-
|
|
856
|
+
if (propName2 === null) {
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
const lowered = propName2.toLowerCase();
|
|
860
|
+
return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);
|
|
776
861
|
}
|
|
777
862
|
return false;
|
|
778
863
|
}
|
|
@@ -1059,13 +1144,17 @@ var no_log_only_catch_default = import_utils8.ESLintUtils.RuleCreator(
|
|
|
1059
1144
|
return {
|
|
1060
1145
|
CatchClause(node) {
|
|
1061
1146
|
const statements = node.body.body;
|
|
1147
|
+
const isDocumented = context.sourceCode.getCommentsInside(node.body).length > 0;
|
|
1062
1148
|
if (statements.length === 0) {
|
|
1063
|
-
if (
|
|
1149
|
+
if (isDocumented) {
|
|
1064
1150
|
return;
|
|
1065
1151
|
}
|
|
1066
1152
|
context.report({ node, messageId: "emptyCatch" });
|
|
1067
1153
|
return;
|
|
1068
1154
|
}
|
|
1155
|
+
if (isDocumented) {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1069
1158
|
const everyStatementIsLogging = statements.every(
|
|
1070
1159
|
(statement) => isLoggingCallStatement(statement)
|
|
1071
1160
|
);
|
|
@@ -1079,6 +1168,11 @@ var no_log_only_catch_default = import_utils8.ESLintUtils.RuleCreator(
|
|
|
1079
1168
|
|
|
1080
1169
|
// src/rules/no-raw-env.ts
|
|
1081
1170
|
var import_utils9 = require("@typescript-eslint/utils");
|
|
1171
|
+
var CONFIG_FILE_RE = /(^|[\\/])[\w.-]+\.config\.[cm]?[jt]sx?$/;
|
|
1172
|
+
var ENV_BOUNDARY_FILE_RE = /(^|[\\/])(?:env|client-env|server-env|client-settings|server-settings)\.[cm]?[jt]sx?$/;
|
|
1173
|
+
function isValidatedEnvBoundary(filename, sourceText) {
|
|
1174
|
+
return ENV_BOUNDARY_FILE_RE.test(filename.replaceAll("\\", "/")) && /\bz\.object\s*\(/.test(sourceText) && /\.parse\s*\(/.test(sourceText);
|
|
1175
|
+
}
|
|
1082
1176
|
function isProcessEnv(node) {
|
|
1083
1177
|
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
1084
1178
|
}
|
|
@@ -1096,6 +1190,21 @@ function isBuildTimeConstantAccess(node) {
|
|
|
1096
1190
|
const parent = node.parent;
|
|
1097
1191
|
return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
|
|
1098
1192
|
}
|
|
1193
|
+
function isWriteTarget(node) {
|
|
1194
|
+
const access = node.parent.type === "MemberExpression" && node.parent.object === node ? node.parent : node;
|
|
1195
|
+
const parent = access.parent;
|
|
1196
|
+
if (parent.type === "AssignmentExpression") {
|
|
1197
|
+
return parent.left === access;
|
|
1198
|
+
}
|
|
1199
|
+
if (parent.type === "UnaryExpression") {
|
|
1200
|
+
return parent.operator === "delete";
|
|
1201
|
+
}
|
|
1202
|
+
return false;
|
|
1203
|
+
}
|
|
1204
|
+
function isWholeEnvSpread(node) {
|
|
1205
|
+
const parent = node.parent;
|
|
1206
|
+
return parent.type === "SpreadElement" && parent.argument === node;
|
|
1207
|
+
}
|
|
1099
1208
|
var no_raw_env_default = import_utils9.ESLintUtils.RuleCreator(
|
|
1100
1209
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1101
1210
|
)({
|
|
@@ -1112,9 +1221,13 @@ var no_raw_env_default = import_utils9.ESLintUtils.RuleCreator(
|
|
|
1112
1221
|
},
|
|
1113
1222
|
defaultOptions: [],
|
|
1114
1223
|
create(context) {
|
|
1224
|
+
const filename = context.filename;
|
|
1225
|
+
if (isTestFile(filename) || isScriptFile(filename) || CONFIG_FILE_RE.test(filename.replaceAll("\\", "/")) || isValidatedEnvBoundary(filename, context.sourceCode.text)) {
|
|
1226
|
+
return {};
|
|
1227
|
+
}
|
|
1115
1228
|
return {
|
|
1116
1229
|
MemberExpression(node) {
|
|
1117
|
-
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
|
|
1230
|
+
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node) && !isWriteTarget(node) && !isWholeEnvSpread(node)) {
|
|
1118
1231
|
context.report({
|
|
1119
1232
|
node,
|
|
1120
1233
|
messageId: "noRawEnv"
|
|
@@ -1324,6 +1437,32 @@ function enclosingReturnTypeNode(node) {
|
|
|
1324
1437
|
}
|
|
1325
1438
|
return null;
|
|
1326
1439
|
}
|
|
1440
|
+
var PREDICATE_NAME_RE = /^(is|has|can|should|must|does|did|was|were|are)[A-Z]/;
|
|
1441
|
+
var PREDICATE_SUFFIX_RE = /(Exists?|Available|Enabled|Disabled)$/;
|
|
1442
|
+
function enclosingFunctionName(node) {
|
|
1443
|
+
let current = node.parent;
|
|
1444
|
+
while (current !== void 0 && current !== null) {
|
|
1445
|
+
if (isFunctionNode(current)) {
|
|
1446
|
+
if ("id" in current && isNode(current.id) && current.id.type === import_utils10.AST_NODE_TYPES.Identifier) {
|
|
1447
|
+
return current.id.name;
|
|
1448
|
+
}
|
|
1449
|
+
const parent = current.parent;
|
|
1450
|
+
if (parent?.type === import_utils10.AST_NODE_TYPES.VariableDeclarator && parent.id.type === import_utils10.AST_NODE_TYPES.Identifier) {
|
|
1451
|
+
return parent.id.name;
|
|
1452
|
+
}
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
current = current.parent;
|
|
1456
|
+
}
|
|
1457
|
+
return null;
|
|
1458
|
+
}
|
|
1459
|
+
function isNamedBooleanPredicate(catchNode, kind) {
|
|
1460
|
+
if (kind !== "boolean") {
|
|
1461
|
+
return false;
|
|
1462
|
+
}
|
|
1463
|
+
const name = enclosingFunctionName(catchNode);
|
|
1464
|
+
return name !== null && (PREDICATE_NAME_RE.test(name) || PREDICATE_SUFFIX_RE.test(name));
|
|
1465
|
+
}
|
|
1327
1466
|
function isDeclaredBooleanPredicate(catchNode, kind) {
|
|
1328
1467
|
if (kind !== "boolean") {
|
|
1329
1468
|
return false;
|
|
@@ -1367,9 +1506,32 @@ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
|
1367
1506
|
if (isWithin(current, catchNode.body)) {
|
|
1368
1507
|
return false;
|
|
1369
1508
|
}
|
|
1370
|
-
return
|
|
1509
|
+
return returnedSentinelKinds(current.argument).has(kind);
|
|
1371
1510
|
});
|
|
1372
1511
|
}
|
|
1512
|
+
function returnedSentinelKinds(arg) {
|
|
1513
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
1514
|
+
if (arg === null) {
|
|
1515
|
+
return kinds;
|
|
1516
|
+
}
|
|
1517
|
+
const direct = sentinelKind(arg);
|
|
1518
|
+
if (direct !== null) {
|
|
1519
|
+
kinds.add(direct);
|
|
1520
|
+
return kinds;
|
|
1521
|
+
}
|
|
1522
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.ConditionalExpression) {
|
|
1523
|
+
for (const branch of [arg.consequent, arg.alternate]) {
|
|
1524
|
+
for (const nested of returnedSentinelKinds(branch)) {
|
|
1525
|
+
kinds.add(nested);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
} else if (arg.type === import_utils10.AST_NODE_TYPES.LogicalExpression && (arg.operator === "??" || arg.operator === "||")) {
|
|
1529
|
+
for (const nested of returnedSentinelKinds(arg.right)) {
|
|
1530
|
+
kinds.add(nested);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
return kinds;
|
|
1534
|
+
}
|
|
1373
1535
|
function isWithin(node, ancestor) {
|
|
1374
1536
|
let current = node;
|
|
1375
1537
|
while (current !== void 0 && current !== null) {
|
|
@@ -1439,6 +1601,9 @@ var no_sentinel_return_on_catch_default = import_utils10.ESLintUtils.RuleCreator
|
|
|
1439
1601
|
return;
|
|
1440
1602
|
}
|
|
1441
1603
|
const kind = sentinelKind(last.argument);
|
|
1604
|
+
if (kind !== null && isNamedBooleanPredicate(node, kind)) {
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1442
1607
|
if (kind !== null && isDeclaredBooleanPredicate(node, kind)) {
|
|
1443
1608
|
return;
|
|
1444
1609
|
}
|
|
@@ -1457,7 +1622,8 @@ var no_sentinel_return_on_catch_default = import_utils10.ESLintUtils.RuleCreator
|
|
|
1457
1622
|
// src/rules/no-sequential-await.ts
|
|
1458
1623
|
var import_utils11 = require("@typescript-eslint/utils");
|
|
1459
1624
|
var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
|
|
1460
|
-
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
|
|
1625
|
+
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;
|
|
1626
|
+
var BENCH_FILE_RE = /(^|[\\/])bench(marks?)?[\\/]|\.bench\.[cm]?[jt]sx?$/i;
|
|
1461
1627
|
function isFunctionLike(node) {
|
|
1462
1628
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
1463
1629
|
}
|
|
@@ -1491,6 +1657,7 @@ function collectAwaits(root) {
|
|
|
1491
1657
|
});
|
|
1492
1658
|
return awaits;
|
|
1493
1659
|
}
|
|
1660
|
+
var ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;
|
|
1494
1661
|
function hasEarlyExit(root) {
|
|
1495
1662
|
let found = false;
|
|
1496
1663
|
visitScope(root, (node) => {
|
|
@@ -1514,6 +1681,9 @@ function isTimerYield(node) {
|
|
|
1514
1681
|
return true;
|
|
1515
1682
|
}
|
|
1516
1683
|
if (arg.type === "CallExpression") {
|
|
1684
|
+
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")) {
|
|
1685
|
+
return true;
|
|
1686
|
+
}
|
|
1517
1687
|
const name = calleeName2(arg.callee);
|
|
1518
1688
|
return name !== null && TIMER_HELPER_RE.test(name);
|
|
1519
1689
|
}
|
|
@@ -1524,6 +1694,25 @@ function isQueueDrain(node) {
|
|
|
1524
1694
|
const arg = node.argument;
|
|
1525
1695
|
return arg.type === "CallExpression" && arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.property.type === "Identifier" && QUEUE_DRAIN_METHODS.test(arg.callee.property.name);
|
|
1526
1696
|
}
|
|
1697
|
+
function hasAssertion(root) {
|
|
1698
|
+
let found = false;
|
|
1699
|
+
visitScope(root, (node) => {
|
|
1700
|
+
if (node.type !== "CallExpression") {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
let callee = node.callee;
|
|
1704
|
+
while (callee.type === "MemberExpression") {
|
|
1705
|
+
callee = callee.object;
|
|
1706
|
+
}
|
|
1707
|
+
if (callee.type === "CallExpression") {
|
|
1708
|
+
callee = callee.callee;
|
|
1709
|
+
}
|
|
1710
|
+
if (callee.type === "Identifier" && ASSERTION_CALLEE_RE.test(callee.name)) {
|
|
1711
|
+
found = true;
|
|
1712
|
+
}
|
|
1713
|
+
});
|
|
1714
|
+
return found;
|
|
1715
|
+
}
|
|
1527
1716
|
function referencesName(root, name) {
|
|
1528
1717
|
let found = false;
|
|
1529
1718
|
visitScope(root, (node) => {
|
|
@@ -1568,13 +1757,16 @@ function testStateIsAssignedInBody(test, body) {
|
|
|
1568
1757
|
});
|
|
1569
1758
|
return found;
|
|
1570
1759
|
}
|
|
1571
|
-
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1760
|
+
function shouldReport(awaits, earlyExit, iterableText, asserts = false) {
|
|
1572
1761
|
if (awaits.length === 0) {
|
|
1573
1762
|
return false;
|
|
1574
1763
|
}
|
|
1575
1764
|
if (earlyExit) {
|
|
1576
1765
|
return false;
|
|
1577
1766
|
}
|
|
1767
|
+
if (asserts) {
|
|
1768
|
+
return false;
|
|
1769
|
+
}
|
|
1578
1770
|
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1579
1771
|
return false;
|
|
1580
1772
|
}
|
|
@@ -1598,6 +1790,9 @@ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
1598
1790
|
},
|
|
1599
1791
|
defaultOptions: [],
|
|
1600
1792
|
create(context) {
|
|
1793
|
+
if (isTestFile(context.filename) || BENCH_FILE_RE.test(context.filename)) {
|
|
1794
|
+
return {};
|
|
1795
|
+
}
|
|
1601
1796
|
function loopParts(node) {
|
|
1602
1797
|
if (node.type === "ForStatement") {
|
|
1603
1798
|
return [node.body, node.test, node.update];
|
|
@@ -1619,6 +1814,7 @@ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
1619
1814
|
}
|
|
1620
1815
|
const awaits = [];
|
|
1621
1816
|
let earlyExit = false;
|
|
1817
|
+
let asserts = false;
|
|
1622
1818
|
for (const part of loopParts(node)) {
|
|
1623
1819
|
if (part === null || isLoop(part)) {
|
|
1624
1820
|
continue;
|
|
@@ -1627,8 +1823,11 @@ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
1627
1823
|
if (!earlyExit && hasEarlyExit(part)) {
|
|
1628
1824
|
earlyExit = true;
|
|
1629
1825
|
}
|
|
1826
|
+
if (!asserts && hasAssertion(part)) {
|
|
1827
|
+
asserts = true;
|
|
1828
|
+
}
|
|
1630
1829
|
}
|
|
1631
|
-
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1830
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node), asserts)) {
|
|
1632
1831
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1633
1832
|
}
|
|
1634
1833
|
}
|
|
@@ -1661,7 +1860,7 @@ var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
1661
1860
|
const awaits = collectAwaits(callback.body);
|
|
1662
1861
|
const earlyExit = hasEarlyExit(callback.body);
|
|
1663
1862
|
const iterableText = context.sourceCode.getText(callee.object);
|
|
1664
|
-
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1863
|
+
if (shouldReport(awaits, earlyExit, iterableText, hasAssertion(callback.body))) {
|
|
1665
1864
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1666
1865
|
}
|
|
1667
1866
|
}
|
|
@@ -1730,20 +1929,30 @@ function isConcatOntoTarget(rhs, target) {
|
|
|
1730
1929
|
}
|
|
1731
1930
|
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1732
1931
|
}
|
|
1733
|
-
function
|
|
1932
|
+
function isDeclaredInsideLoop(variable, loop) {
|
|
1933
|
+
const def = variable.defs[0];
|
|
1934
|
+
if (def === void 0) {
|
|
1935
|
+
return false;
|
|
1936
|
+
}
|
|
1937
|
+
const body = loop.body;
|
|
1938
|
+
const [declStart, declEnd] = def.node.range;
|
|
1939
|
+
const [bodyStart, bodyEnd] = body.range;
|
|
1940
|
+
return declStart >= bodyStart && declEnd <= bodyEnd;
|
|
1941
|
+
}
|
|
1942
|
+
function enclosingLoop(node) {
|
|
1734
1943
|
let child = node;
|
|
1735
1944
|
let parent = node.parent;
|
|
1736
1945
|
while (parent !== void 0 && parent !== null) {
|
|
1737
1946
|
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
1738
1947
|
const loop = parent;
|
|
1739
1948
|
if (loop.body === child) {
|
|
1740
|
-
return
|
|
1949
|
+
return loop;
|
|
1741
1950
|
}
|
|
1742
1951
|
}
|
|
1743
1952
|
child = parent;
|
|
1744
1953
|
parent = parent.parent;
|
|
1745
1954
|
}
|
|
1746
|
-
return
|
|
1955
|
+
return null;
|
|
1747
1956
|
}
|
|
1748
1957
|
var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
|
|
1749
1958
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -1761,6 +1970,7 @@ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1761
1970
|
},
|
|
1762
1971
|
defaultOptions: [],
|
|
1763
1972
|
create(context) {
|
|
1973
|
+
const reported = /* @__PURE__ */ new WeakMap();
|
|
1764
1974
|
return {
|
|
1765
1975
|
AssignmentExpression(node) {
|
|
1766
1976
|
if (node.left.type !== "Identifier") {
|
|
@@ -1770,7 +1980,8 @@ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1770
1980
|
if (!isAccumulation) {
|
|
1771
1981
|
return;
|
|
1772
1982
|
}
|
|
1773
|
-
|
|
1983
|
+
const loop = enclosingLoop(node);
|
|
1984
|
+
if (loop === null) {
|
|
1774
1985
|
return;
|
|
1775
1986
|
}
|
|
1776
1987
|
const scope = context.sourceCode.getScope(node);
|
|
@@ -1781,6 +1992,18 @@ var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1781
1992
|
if (!isStringInitializedVariable(variable)) {
|
|
1782
1993
|
return;
|
|
1783
1994
|
}
|
|
1995
|
+
if (isDeclaredInsideLoop(variable, loop)) {
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
let seen = reported.get(loop);
|
|
1999
|
+
if (seen === void 0) {
|
|
2000
|
+
seen = /* @__PURE__ */ new Set();
|
|
2001
|
+
reported.set(loop, seen);
|
|
2002
|
+
}
|
|
2003
|
+
if (seen.has(node.left.name)) {
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
seen.add(node.left.name);
|
|
1784
2007
|
context.report({
|
|
1785
2008
|
node,
|
|
1786
2009
|
messageId: "noStringConcatInLoop"
|
|
@@ -1812,7 +2035,31 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
1812
2035
|
"KeyboardEvent",
|
|
1813
2036
|
"TouchEvent"
|
|
1814
2037
|
]);
|
|
2038
|
+
var CLIENT_REQUIRED_MODULES = /* @__PURE__ */ new Set(["next/dynamic"]);
|
|
1815
2039
|
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\/)/;
|
|
2040
|
+
var isBareSpecifier = (source) => !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("@/") && !source.startsWith("~");
|
|
2041
|
+
var jsxRootName = (name) => {
|
|
2042
|
+
let current = name;
|
|
2043
|
+
while (current.type === import_utils13.AST_NODE_TYPES.JSXMemberExpression) {
|
|
2044
|
+
current = current.object;
|
|
2045
|
+
}
|
|
2046
|
+
return current.type === import_utils13.AST_NODE_TYPES.JSXIdentifier ? current.name : "";
|
|
2047
|
+
};
|
|
2048
|
+
var subtreeReadsImportedBinding = (node, imported) => {
|
|
2049
|
+
if (node.type === import_utils13.AST_NODE_TYPES.Identifier) {
|
|
2050
|
+
return imported.has(node.name);
|
|
2051
|
+
}
|
|
2052
|
+
for (const key of Object.keys(node)) {
|
|
2053
|
+
if (key === "parent") continue;
|
|
2054
|
+
const value = node[key];
|
|
2055
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2056
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string" && subtreeReadsImportedBinding(child, imported)) {
|
|
2057
|
+
return true;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
return false;
|
|
2062
|
+
};
|
|
1816
2063
|
var isUseClientDirective = (node) => {
|
|
1817
2064
|
return node.type === import_utils13.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils13.AST_NODE_TYPES.Literal && node.expression.value === "use client";
|
|
1818
2065
|
};
|
|
@@ -1862,6 +2109,8 @@ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
|
|
|
1862
2109
|
}
|
|
1863
2110
|
let directiveNode = null;
|
|
1864
2111
|
let hasClientIndicator = false;
|
|
2112
|
+
const importedLocals = /* @__PURE__ */ new Set();
|
|
2113
|
+
const externalLocals = /* @__PURE__ */ new Set();
|
|
1865
2114
|
const markIfHookOrContext = (callee) => {
|
|
1866
2115
|
if (callee.type === import_utils13.AST_NODE_TYPES.Identifier) {
|
|
1867
2116
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
@@ -1898,7 +2147,21 @@ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
|
|
|
1898
2147
|
},
|
|
1899
2148
|
ImportDeclaration(node) {
|
|
1900
2149
|
if (directiveNode === null) return;
|
|
1901
|
-
if (typeof node.source.value
|
|
2150
|
+
if (typeof node.source.value !== "string") return;
|
|
2151
|
+
const source = node.source.value;
|
|
2152
|
+
if (CLIENT_ONLY_PACKAGES_REGEX.test(source) || CLIENT_REQUIRED_MODULES.has(source)) {
|
|
2153
|
+
hasClientIndicator = true;
|
|
2154
|
+
}
|
|
2155
|
+
for (const specifier of node.specifiers) {
|
|
2156
|
+
importedLocals.add(specifier.local.name);
|
|
2157
|
+
if (isBareSpecifier(source)) {
|
|
2158
|
+
externalLocals.add(specifier.local.name);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
},
|
|
2162
|
+
JSXOpeningElement(node) {
|
|
2163
|
+
if (directiveNode === null) return;
|
|
2164
|
+
if (externalLocals.has(jsxRootName(node.name))) {
|
|
1902
2165
|
hasClientIndicator = true;
|
|
1903
2166
|
}
|
|
1904
2167
|
},
|
|
@@ -1906,6 +2169,10 @@ var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
|
|
|
1906
2169
|
if (directiveNode === null) return;
|
|
1907
2170
|
if (node.source !== null) {
|
|
1908
2171
|
hasClientIndicator = true;
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (node.declaration !== null && subtreeReadsImportedBinding(node.declaration, importedLocals)) {
|
|
2175
|
+
hasClientIndicator = true;
|
|
1909
2176
|
}
|
|
1910
2177
|
},
|
|
1911
2178
|
ExportAllDeclaration(node) {
|
|
@@ -1970,19 +2237,23 @@ function isBooleanTyped(member) {
|
|
|
1970
2237
|
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1971
2238
|
let hasStatusBoolean = false;
|
|
1972
2239
|
let optionalCount = 0;
|
|
2240
|
+
let optionalPayloadCount = 0;
|
|
1973
2241
|
for (const member of typeLiteral.members) {
|
|
1974
2242
|
if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
|
|
1975
2243
|
continue;
|
|
1976
2244
|
}
|
|
1977
2245
|
if (member.optional) {
|
|
1978
2246
|
optionalCount += 1;
|
|
2247
|
+
if (!isBooleanTyped(member)) {
|
|
2248
|
+
optionalPayloadCount += 1;
|
|
2249
|
+
}
|
|
1979
2250
|
}
|
|
1980
2251
|
const name = getMemberName(member);
|
|
1981
2252
|
if (name !== null && STATUS_MEMBER_NAMES.has(name) && isBooleanTyped(member)) {
|
|
1982
2253
|
hasStatusBoolean = true;
|
|
1983
2254
|
}
|
|
1984
2255
|
}
|
|
1985
|
-
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
2256
|
+
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS && optionalPayloadCount >= 1;
|
|
1986
2257
|
}
|
|
1987
2258
|
var prefer_discriminated_union_default = import_utils14.ESLintUtils.RuleCreator(
|
|
1988
2259
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -2060,7 +2331,51 @@ var isRawPayloadSource = (node) => {
|
|
|
2060
2331
|
return true;
|
|
2061
2332
|
}
|
|
2062
2333
|
const object = unwrap(callee.object);
|
|
2063
|
-
return property.name === "parse" && object !== null && object.type === import_utils16.AST_NODE_TYPES.Identifier && object.name === "JSON"
|
|
2334
|
+
return property.name === "parse" && object !== null && object.type === import_utils16.AST_NODE_TYPES.Identifier && object.name === "JSON" && // ...but not `JSON.parse(readFileSync(p, "utf8"))` — see isLocalFileRead.
|
|
2335
|
+
!isLocalFileRead(current.arguments[0]);
|
|
2336
|
+
};
|
|
2337
|
+
var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
|
|
2338
|
+
var isLocalFileRead = (node) => {
|
|
2339
|
+
let found = false;
|
|
2340
|
+
const visit = (current) => {
|
|
2341
|
+
if (found || current === null || current === void 0) return;
|
|
2342
|
+
if (current.type === import_utils16.AST_NODE_TYPES.CallExpression) {
|
|
2343
|
+
const callee = unwrap(current.callee);
|
|
2344
|
+
const name = callee?.type === import_utils16.AST_NODE_TYPES.Identifier ? callee.name : callee?.type === import_utils16.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils16.AST_NODE_TYPES.Identifier ? callee.property.name : null;
|
|
2345
|
+
if (name !== null && FILE_READ_RE.test(name)) {
|
|
2346
|
+
found = true;
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
for (const key of Object.keys(current)) {
|
|
2351
|
+
if (key === "parent") continue;
|
|
2352
|
+
const value = current[key];
|
|
2353
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2354
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string") {
|
|
2355
|
+
visit(child);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
2360
|
+
visit(node);
|
|
2361
|
+
return found;
|
|
2362
|
+
};
|
|
2363
|
+
var ASSERTION_CALLEE_RE2 = /^(expect|assert|should|invariant)$/;
|
|
2364
|
+
var isInsideAssertion = (node) => {
|
|
2365
|
+
for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
2366
|
+
if (current.type !== import_utils16.AST_NODE_TYPES.CallExpression) continue;
|
|
2367
|
+
let callee = current.callee;
|
|
2368
|
+
while (callee.type === import_utils16.AST_NODE_TYPES.MemberExpression) {
|
|
2369
|
+
callee = callee.object;
|
|
2370
|
+
}
|
|
2371
|
+
if (callee.type === import_utils16.AST_NODE_TYPES.CallExpression) {
|
|
2372
|
+
callee = callee.callee;
|
|
2373
|
+
}
|
|
2374
|
+
if (callee.type === import_utils16.AST_NODE_TYPES.Identifier && ASSERTION_CALLEE_RE2.test(callee.name)) {
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return false;
|
|
2064
2379
|
};
|
|
2065
2380
|
var findVariable2 = (scope, name) => {
|
|
2066
2381
|
let current = scope;
|
|
@@ -2119,6 +2434,9 @@ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreat
|
|
|
2119
2434
|
},
|
|
2120
2435
|
defaultOptions: [],
|
|
2121
2436
|
create(context) {
|
|
2437
|
+
if (isTestFile(context.filename)) {
|
|
2438
|
+
return {};
|
|
2439
|
+
}
|
|
2122
2440
|
const unvalidatedVariables = /* @__PURE__ */ new Set();
|
|
2123
2441
|
const trackInitializer = (declarator) => {
|
|
2124
2442
|
if (!isRawPayloadSource(declarator.init)) return;
|
|
@@ -2190,6 +2508,7 @@ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreat
|
|
|
2190
2508
|
}
|
|
2191
2509
|
},
|
|
2192
2510
|
MemberExpression(node) {
|
|
2511
|
+
if (isInsideAssertion(node)) return;
|
|
2193
2512
|
const scope = context.sourceCode.getScope(node);
|
|
2194
2513
|
const obj = unwrap(node.object);
|
|
2195
2514
|
if (isRawPayloadSource(obj)) {
|
|
@@ -2214,6 +2533,8 @@ var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreat
|
|
|
2214
2533
|
|
|
2215
2534
|
// src/rules/prefer-semantic-colors.ts
|
|
2216
2535
|
var import_utils17 = require("@typescript-eslint/utils");
|
|
2536
|
+
var import_fs = require("fs");
|
|
2537
|
+
var import_path = require("path");
|
|
2217
2538
|
|
|
2218
2539
|
// src/rules/_tailwind.ts
|
|
2219
2540
|
var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
|
|
@@ -2251,6 +2572,20 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
|
2251
2572
|
]);
|
|
2252
2573
|
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
2253
2574
|
var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
2575
|
+
var SEMANTIC_TOKEN_RE = /--(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b|(?:bg|text|border)-(?:background|foreground|primary|secondary|muted|accent|destructive|border|card|popover)\b/;
|
|
2576
|
+
var DETECTION_FILES = [
|
|
2577
|
+
"components.json",
|
|
2578
|
+
"tailwind.config.js",
|
|
2579
|
+
"tailwind.config.cjs",
|
|
2580
|
+
"tailwind.config.mjs",
|
|
2581
|
+
"tailwind.config.ts",
|
|
2582
|
+
"app/globals.css",
|
|
2583
|
+
"src/app/globals.css",
|
|
2584
|
+
"src/index.css",
|
|
2585
|
+
"src/styles/globals.css",
|
|
2586
|
+
"styles/globals.css"
|
|
2587
|
+
];
|
|
2588
|
+
var semanticTokenCache = /* @__PURE__ */ new Map();
|
|
2254
2589
|
var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
|
|
2255
2590
|
"mask",
|
|
2256
2591
|
"clipPath",
|
|
@@ -2279,6 +2614,34 @@ var isInsideSvg = (node) => {
|
|
|
2279
2614
|
}
|
|
2280
2615
|
return false;
|
|
2281
2616
|
};
|
|
2617
|
+
var hasSemanticTokenSystem = (filename) => {
|
|
2618
|
+
let dir = (0, import_path.dirname)(filename);
|
|
2619
|
+
const root = (0, import_path.parse)(dir).root;
|
|
2620
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
2621
|
+
const cached = semanticTokenCache.get(dir);
|
|
2622
|
+
if (cached !== void 0) return cached;
|
|
2623
|
+
let found = false;
|
|
2624
|
+
for (const rel of DETECTION_FILES) {
|
|
2625
|
+
const candidate = (0, import_path.join)(dir, rel);
|
|
2626
|
+
if (!(0, import_fs.existsSync)(candidate)) continue;
|
|
2627
|
+
if (rel === "components.json") {
|
|
2628
|
+
found = true;
|
|
2629
|
+
break;
|
|
2630
|
+
}
|
|
2631
|
+
try {
|
|
2632
|
+
if (SEMANTIC_TOKEN_RE.test((0, import_fs.readFileSync)(candidate, "utf8"))) {
|
|
2633
|
+
found = true;
|
|
2634
|
+
break;
|
|
2635
|
+
}
|
|
2636
|
+
} catch {
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
semanticTokenCache.set(dir, found);
|
|
2640
|
+
if (found || dir === root) return found;
|
|
2641
|
+
dir = (0, import_path.dirname)(dir);
|
|
2642
|
+
}
|
|
2643
|
+
return false;
|
|
2644
|
+
};
|
|
2282
2645
|
var propName = (key) => {
|
|
2283
2646
|
if (key.type === import_utils17.AST_NODE_TYPES.Identifier) return key.name;
|
|
2284
2647
|
if (key.type === import_utils17.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
|
|
@@ -2293,16 +2656,27 @@ var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
|
|
|
2293
2656
|
docs: {
|
|
2294
2657
|
description: "Enforce design-system semantic color tokens (bg-primary, text-destructive, \u2026) over raw Tailwind palette classes (text-red-500), arbitrary color values (bg-[#fff]), and inline color literals."
|
|
2295
2658
|
},
|
|
2296
|
-
schema: [
|
|
2659
|
+
schema: [
|
|
2660
|
+
{
|
|
2661
|
+
type: "object",
|
|
2662
|
+
additionalProperties: false,
|
|
2663
|
+
properties: {
|
|
2664
|
+
requireSemanticTokens: { type: "boolean" }
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
],
|
|
2297
2668
|
messages: {
|
|
2298
2669
|
rawPalette: "Raw palette class '{{class}}' \u2014 use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).",
|
|
2299
2670
|
arbitraryColor: "Hardcoded color '{{class}}' \u2014 use a semantic token, or var(--\u2026). For charts/brand add an eslint-disable with a reason.",
|
|
2300
2671
|
inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason."
|
|
2301
2672
|
}
|
|
2302
2673
|
},
|
|
2303
|
-
defaultOptions: [],
|
|
2304
|
-
create(context) {
|
|
2674
|
+
defaultOptions: [{}],
|
|
2675
|
+
create(context, [options]) {
|
|
2305
2676
|
if (STORIES_FILE_RE.test(context.filename)) return {};
|
|
2677
|
+
if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {
|
|
2678
|
+
return {};
|
|
2679
|
+
}
|
|
2306
2680
|
const reportClasses = (value, node) => {
|
|
2307
2681
|
for (const token of classTokens(value)) {
|
|
2308
2682
|
const base = tailwindBase(token);
|
|
@@ -2398,7 +2772,8 @@ var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
|
|
|
2398
2772
|
var import_utils18 = require("@typescript-eslint/utils");
|
|
2399
2773
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
2400
2774
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
2401
|
-
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
2775
|
+
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\/)/;
|
|
2776
|
+
var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
|
|
2402
2777
|
function getScope(context, node) {
|
|
2403
2778
|
return context.sourceCode.getScope(node);
|
|
2404
2779
|
}
|
|
@@ -2494,8 +2869,15 @@ var prefer_server_actions_default = import_utils18.ESLintUtils.RuleCreator(
|
|
|
2494
2869
|
if (SKIP_FILE_REGEX.test(filename)) {
|
|
2495
2870
|
return {};
|
|
2496
2871
|
}
|
|
2872
|
+
let isNonReactFramework = false;
|
|
2497
2873
|
return {
|
|
2874
|
+
ImportDeclaration(node) {
|
|
2875
|
+
if (typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(node.source.value)) {
|
|
2876
|
+
isNonReactFramework = true;
|
|
2877
|
+
}
|
|
2878
|
+
},
|
|
2498
2879
|
CallExpression(node) {
|
|
2880
|
+
if (isNonReactFramework) return;
|
|
2499
2881
|
let isMutation = false;
|
|
2500
2882
|
if (node.callee.type === "Identifier" && node.callee.name === "fetch") {
|
|
2501
2883
|
const urlArg = node.arguments[0];
|
|
@@ -2553,7 +2935,7 @@ var INPUT_TYPE_REPLACEMENTS = {
|
|
|
2553
2935
|
radio: "RadioGroup",
|
|
2554
2936
|
range: "Slider"
|
|
2555
2937
|
};
|
|
2556
|
-
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
|
|
2938
|
+
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["file", "hidden"]);
|
|
2557
2939
|
var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2558
2940
|
var literalTypeAttr = (node) => {
|
|
2559
2941
|
for (const attribute of node.attributes) {
|
|
@@ -2567,8 +2949,12 @@ var literalTypeAttr = (node) => {
|
|
|
2567
2949
|
}
|
|
2568
2950
|
return null;
|
|
2569
2951
|
};
|
|
2952
|
+
var hasFileAcceptAttr = (node) => node.attributes.some(
|
|
2953
|
+
(attribute) => attribute.type === import_utils19.AST_NODE_TYPES.JSXAttribute && attribute.name.type === import_utils19.AST_NODE_TYPES.JSXIdentifier && attribute.name.name === "accept"
|
|
2954
|
+
);
|
|
2570
2955
|
var resolveInputReplacement = (node) => {
|
|
2571
2956
|
const typeAttr = literalTypeAttr(node);
|
|
2957
|
+
if (hasFileAcceptAttr(node)) return null;
|
|
2572
2958
|
if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
|
|
2573
2959
|
if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
|
|
2574
2960
|
return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
|
|
@@ -2589,6 +2975,9 @@ var prefer_shadcn_default = import_utils19.ESLintUtils.RuleCreator(
|
|
|
2589
2975
|
},
|
|
2590
2976
|
defaultOptions: [],
|
|
2591
2977
|
create(context) {
|
|
2978
|
+
if (isTestFile(context.filename) || isStoryFile(context.filename)) {
|
|
2979
|
+
return {};
|
|
2980
|
+
}
|
|
2592
2981
|
return {
|
|
2593
2982
|
JSXOpeningElement(node) {
|
|
2594
2983
|
if (node.name.type !== "JSXIdentifier") {
|
|
@@ -2762,6 +3151,9 @@ var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator
|
|
|
2762
3151
|
},
|
|
2763
3152
|
defaultOptions: [],
|
|
2764
3153
|
create(context) {
|
|
3154
|
+
if (isTestFile(context.filename)) {
|
|
3155
|
+
return {};
|
|
3156
|
+
}
|
|
2765
3157
|
const isFormSourceIdentifier = (node) => {
|
|
2766
3158
|
if (node.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
|
|
2767
3159
|
if (/formdata/i.test(node.name)) return true;
|
|
@@ -2835,6 +3227,25 @@ var CONVENTIONS = {
|
|
|
2835
3227
|
suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
|
|
2836
3228
|
either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
|
|
2837
3229
|
};
|
|
3230
|
+
var CONTAINS_SCHEMA_RE = /schema/i;
|
|
3231
|
+
var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
|
|
3232
|
+
"parse",
|
|
3233
|
+
"parseAsync",
|
|
3234
|
+
"safeParse",
|
|
3235
|
+
"safeParseAsync",
|
|
3236
|
+
"encode",
|
|
3237
|
+
"decode",
|
|
3238
|
+
"encodeAsync",
|
|
3239
|
+
"decodeAsync",
|
|
3240
|
+
"safeEncode",
|
|
3241
|
+
"safeDecode",
|
|
3242
|
+
"safeEncodeAsync",
|
|
3243
|
+
"safeDecodeAsync",
|
|
3244
|
+
"toJSONSchema",
|
|
3245
|
+
"registry",
|
|
3246
|
+
"implement"
|
|
3247
|
+
]);
|
|
3248
|
+
var terminalMethodName = (callee) => !callee.computed && callee.property.type === import_utils22.AST_NODE_TYPES.Identifier ? callee.property.name : null;
|
|
2838
3249
|
var calleeChainStartsWithZ = (node) => {
|
|
2839
3250
|
let current = node;
|
|
2840
3251
|
while (current.type === import_utils22.AST_NODE_TYPES.MemberExpression) {
|
|
@@ -2879,7 +3290,12 @@ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
|
|
|
2879
3290
|
},
|
|
2880
3291
|
defaultOptions: [{}],
|
|
2881
3292
|
create(context, [optionsArg]) {
|
|
2882
|
-
const
|
|
3293
|
+
const convention = optionsArg?.convention ?? "either";
|
|
3294
|
+
const { test, messageId } = CONVENTIONS[convention];
|
|
3295
|
+
const acceptsSchemaWord = convention !== "prefix";
|
|
3296
|
+
if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
3297
|
+
return {};
|
|
3298
|
+
}
|
|
2883
3299
|
return {
|
|
2884
3300
|
VariableDeclarator(node) {
|
|
2885
3301
|
const init = node.init;
|
|
@@ -2888,8 +3304,11 @@ var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
|
|
|
2888
3304
|
const callee = init.callee;
|
|
2889
3305
|
if (callee.type !== import_utils22.AST_NODE_TYPES.MemberExpression) return;
|
|
2890
3306
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
3307
|
+
const terminal = terminalMethodName(callee);
|
|
3308
|
+
if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
|
|
2891
3309
|
if (node.id.type !== import_utils22.AST_NODE_TYPES.Identifier) return;
|
|
2892
3310
|
if (test.test(node.id.name)) return;
|
|
3311
|
+
if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
|
|
2893
3312
|
context.report({
|
|
2894
3313
|
node: node.id,
|
|
2895
3314
|
messageId
|
|
@@ -3111,25 +3530,24 @@ var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleC
|
|
|
3111
3530
|
|
|
3112
3531
|
// src/rules/no-silent-promise-catch.ts
|
|
3113
3532
|
var import_utils24 = require("@typescript-eslint/utils");
|
|
3114
|
-
|
|
3115
|
-
// src/rules/_paths.ts
|
|
3116
|
-
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
3117
|
-
function isTestFile(filename) {
|
|
3118
|
-
const normalized = filename.replaceAll("\\", "/");
|
|
3119
|
-
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
3120
|
-
if (/\.(test|spec)\.[cm]?[jt]sx?$/.test(base)) {
|
|
3121
|
-
return true;
|
|
3122
|
-
}
|
|
3123
|
-
return /(^|\/)(tests?|__tests__|__mocks__|fixtures)\//.test(normalized);
|
|
3124
|
-
}
|
|
3125
|
-
function isScriptFile(filename) {
|
|
3126
|
-
return SCRIPT_FILE_RE.test(filename);
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
// src/rules/no-silent-promise-catch.ts
|
|
3130
3533
|
function isBodyParseCall(node) {
|
|
3131
3534
|
return node.type === import_utils24.AST_NODE_TYPES.CallExpression && node.arguments.length === 0 && node.callee.type === import_utils24.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils24.AST_NODE_TYPES.Identifier && (node.callee.property.name === "json" || node.callee.property.name === "text");
|
|
3132
3535
|
}
|
|
3536
|
+
var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
|
|
3537
|
+
"cancel",
|
|
3538
|
+
"close",
|
|
3539
|
+
"abort",
|
|
3540
|
+
"destroy",
|
|
3541
|
+
"dispose",
|
|
3542
|
+
"release",
|
|
3543
|
+
"unlock",
|
|
3544
|
+
"disconnect"
|
|
3545
|
+
]);
|
|
3546
|
+
var DIRECTIVE_COMMENT_RE = /^\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;
|
|
3547
|
+
var isExplanatory = (comment) => !DIRECTIVE_COMMENT_RE.test(comment.value);
|
|
3548
|
+
function isTeardownCall(node) {
|
|
3549
|
+
return node.type === import_utils24.AST_NODE_TYPES.CallExpression && node.callee.type === import_utils24.AST_NODE_TYPES.MemberExpression && !node.callee.computed && node.callee.property.type === import_utils24.AST_NODE_TYPES.Identifier && TEARDOWN_METHODS.has(node.callee.property.name);
|
|
3550
|
+
}
|
|
3133
3551
|
function isSilentExpression(node) {
|
|
3134
3552
|
switch (node.type) {
|
|
3135
3553
|
case import_utils24.AST_NODE_TYPES.Literal:
|
|
@@ -3183,6 +3601,22 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
3183
3601
|
if (isTestFile(context.filename)) {
|
|
3184
3602
|
return {};
|
|
3185
3603
|
}
|
|
3604
|
+
const hasExplanatoryComment = (call, handler) => {
|
|
3605
|
+
const sourceCode = context.sourceCode;
|
|
3606
|
+
if (sourceCode.getCommentsInside(handler).some(isExplanatory)) {
|
|
3607
|
+
return true;
|
|
3608
|
+
}
|
|
3609
|
+
let statement = call;
|
|
3610
|
+
while (statement.parent !== void 0 && statement.parent !== null && !statement.type.endsWith("Statement") && statement.type !== import_utils24.AST_NODE_TYPES.VariableDeclaration) {
|
|
3611
|
+
statement = statement.parent;
|
|
3612
|
+
}
|
|
3613
|
+
if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {
|
|
3614
|
+
return true;
|
|
3615
|
+
}
|
|
3616
|
+
return sourceCode.getCommentsAfter(statement).some(
|
|
3617
|
+
(c) => isExplanatory(c) && c.loc.start.line === statement.loc.end.line
|
|
3618
|
+
);
|
|
3619
|
+
};
|
|
3186
3620
|
return {
|
|
3187
3621
|
CallExpression(node) {
|
|
3188
3622
|
if (node.callee.type !== import_utils24.AST_NODE_TYPES.MemberExpression || node.callee.computed || node.callee.property.type !== import_utils24.AST_NODE_TYPES.Identifier || node.callee.property.name !== "catch") {
|
|
@@ -3191,6 +3625,12 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
3191
3625
|
if (isBodyParseCall(node.callee.object)) {
|
|
3192
3626
|
return;
|
|
3193
3627
|
}
|
|
3628
|
+
if (isTeardownCall(node.callee.object)) {
|
|
3629
|
+
return;
|
|
3630
|
+
}
|
|
3631
|
+
if (node.parent.type === import_utils24.AST_NODE_TYPES.MemberExpression && node.parent.object === node) {
|
|
3632
|
+
return;
|
|
3633
|
+
}
|
|
3194
3634
|
if (node.arguments.length !== 1) {
|
|
3195
3635
|
return;
|
|
3196
3636
|
}
|
|
@@ -3198,6 +3638,9 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
3198
3638
|
if (handler === void 0 || handler.type !== import_utils24.AST_NODE_TYPES.ArrowFunctionExpression && handler.type !== import_utils24.AST_NODE_TYPES.FunctionExpression) {
|
|
3199
3639
|
return;
|
|
3200
3640
|
}
|
|
3641
|
+
if (hasExplanatoryComment(node, handler)) {
|
|
3642
|
+
return;
|
|
3643
|
+
}
|
|
3201
3644
|
if (isSilentHandler(handler)) {
|
|
3202
3645
|
context.report({ node, messageId: "silentCatch" });
|
|
3203
3646
|
}
|
|
@@ -3208,6 +3651,7 @@ var no_silent_promise_catch_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
3208
3651
|
|
|
3209
3652
|
// src/rules/require-fetch-timeout.ts
|
|
3210
3653
|
var import_utils25 = require("@typescript-eslint/utils");
|
|
3654
|
+
var CODEMOD_FIXTURE_RE = /[\\/]__testfixtures__[\\/]/;
|
|
3211
3655
|
var GLOBAL_OBJECTS = /* @__PURE__ */ new Set([
|
|
3212
3656
|
"globalThis",
|
|
3213
3657
|
"window",
|
|
@@ -3270,7 +3714,7 @@ var require_fetch_timeout_default = import_utils25.ESLintUtils.RuleCreator(
|
|
|
3270
3714
|
},
|
|
3271
3715
|
defaultOptions: [{}],
|
|
3272
3716
|
create(context, [optionsArg]) {
|
|
3273
|
-
if (isTestFile(context.filename) || isScriptFile(context.filename)) {
|
|
3717
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || CODEMOD_FIXTURE_RE.test(context.filename)) {
|
|
3274
3718
|
return {};
|
|
3275
3719
|
}
|
|
3276
3720
|
const allowIn = optionsArg?.allowIn ?? [];
|
|
@@ -3989,6 +4433,9 @@ var no_unsafe_cast_default = import_utils29.ESLintUtils.RuleCreator(
|
|
|
3989
4433
|
},
|
|
3990
4434
|
defaultOptions: [],
|
|
3991
4435
|
create(context) {
|
|
4436
|
+
if (isTestFile(context.filename)) {
|
|
4437
|
+
return {};
|
|
4438
|
+
}
|
|
3992
4439
|
function checkAssertion(node) {
|
|
3993
4440
|
if (isConstAssertion(node.typeAnnotation)) {
|
|
3994
4441
|
return;
|
|
@@ -4486,6 +4933,7 @@ var single_public_export_default = import_utils32.ESLintUtils.RuleCreator(
|
|
|
4486
4933
|
const base = basename(context.filename);
|
|
4487
4934
|
if (base.endsWith(".d.ts")) return {};
|
|
4488
4935
|
if (TEST_FILE_RE.test(base)) return {};
|
|
4936
|
+
if (isTestFile(context.filename)) return {};
|
|
4489
4937
|
const stem = stemOf(base);
|
|
4490
4938
|
if (!JUNK_DRAWER_STEMS.has(stem.toLowerCase())) return {};
|
|
4491
4939
|
return {
|
|
@@ -4686,6 +5134,7 @@ var no_offset_pagination_default = import_utils34.ESLintUtils.RuleCreator(
|
|
|
4686
5134
|
// src/rules/no-positional-tuple-return.ts
|
|
4687
5135
|
var import_utils35 = require("@typescript-eslint/utils");
|
|
4688
5136
|
var MIN_ELEMENTS = 2;
|
|
5137
|
+
var ACCESSOR_PAIR_LENGTH = 2;
|
|
4689
5138
|
var AWAITABLE_TYPES = /* @__PURE__ */ new Set(["Promise", "PromiseLike", "Awaited"]);
|
|
4690
5139
|
function tupleReturnType(node) {
|
|
4691
5140
|
if (node.type === import_utils35.AST_NODE_TYPES.TSTupleType) {
|
|
@@ -4714,6 +5163,9 @@ function isPermittedTuple(tuple, sourceCode) {
|
|
|
4714
5163
|
if (elements[0]?.type === import_utils35.AST_NODE_TYPES.TSLiteralType) {
|
|
4715
5164
|
return true;
|
|
4716
5165
|
}
|
|
5166
|
+
if (elements.length === ACCESSOR_PAIR_LENGTH && elements.some((element) => element.type === import_utils35.AST_NODE_TYPES.TSFunctionType)) {
|
|
5167
|
+
return true;
|
|
5168
|
+
}
|
|
4717
5169
|
const texts = new Set(elements.map((element) => normalizedText(sourceCode, element)));
|
|
4718
5170
|
return texts.size === 1;
|
|
4719
5171
|
}
|
|
@@ -4913,6 +5365,9 @@ var no_repeated_string_literal_default = import_utils36.ESLintUtils.RuleCreator(
|
|
|
4913
5365
|
}
|
|
4914
5366
|
},
|
|
4915
5367
|
TemplateLiteral(node) {
|
|
5368
|
+
if (node.parent.type === import_utils36.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
4916
5371
|
const [only] = node.quasis;
|
|
4917
5372
|
if (node.expressions.length === 0 && only !== void 0) {
|
|
4918
5373
|
record(only.value.cooked ?? only.value.raw, node);
|
|
@@ -5136,7 +5591,10 @@ var import_utils39 = require("@typescript-eslint/utils");
|
|
|
5136
5591
|
var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["===", "!==", "==", "!="]);
|
|
5137
5592
|
var SENTINEL_IDENTIFIERS = /* @__PURE__ */ new Set(["undefined", "NaN"]);
|
|
5138
5593
|
var SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;
|
|
5594
|
+
var SENTINEL_PREFIX_RE = /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;
|
|
5595
|
+
var AST_NODE_TYPE_RE = /^(?:TS|JSX)?[A-Z][A-Za-z]*(?:Signature|Keyword|Expression|Declaration|Element|Literal|Identifier)$/;
|
|
5139
5596
|
function isConstantReference(identifier) {
|
|
5597
|
+
if (AST_NODE_TYPE_RE.test(identifier)) return true;
|
|
5140
5598
|
if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;
|
|
5141
5599
|
return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);
|
|
5142
5600
|
}
|
|
@@ -5147,9 +5605,9 @@ function isExcludedOperand(node) {
|
|
|
5147
5605
|
case import_utils39.AST_NODE_TYPES.TemplateLiteral:
|
|
5148
5606
|
return node.expressions.length === 0;
|
|
5149
5607
|
case import_utils39.AST_NODE_TYPES.Identifier:
|
|
5150
|
-
return SENTINEL_IDENTIFIERS.has(node.name) || isConstantReference(node.name);
|
|
5608
|
+
return SENTINEL_IDENTIFIERS.has(node.name) || SENTINEL_PREFIX_RE.test(node.name) || isConstantReference(node.name);
|
|
5151
5609
|
case import_utils39.AST_NODE_TYPES.MemberExpression:
|
|
5152
|
-
return !node.computed && node.property.type === import_utils39.AST_NODE_TYPES.Identifier && isConstantReference(node.property.name);
|
|
5610
|
+
return !node.computed && node.property.type === import_utils39.AST_NODE_TYPES.Identifier && (SENTINEL_PREFIX_RE.test(node.property.name) || isConstantReference(node.property.name));
|
|
5153
5611
|
default:
|
|
5154
5612
|
return false;
|
|
5155
5613
|
}
|
|
@@ -5299,6 +5757,23 @@ function runtimeConcatOperands(node) {
|
|
|
5299
5757
|
(operand) => operand.type !== import_utils41.AST_NODE_TYPES.Literal && !isStaticFragment(operand)
|
|
5300
5758
|
);
|
|
5301
5759
|
}
|
|
5760
|
+
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;
|
|
5761
|
+
var RUNTIME_MARKER = " ? ";
|
|
5762
|
+
function staticStatementText(node) {
|
|
5763
|
+
if (node.type === import_utils41.AST_NODE_TYPES.TemplateLiteral) {
|
|
5764
|
+
return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);
|
|
5765
|
+
}
|
|
5766
|
+
if (node.type === import_utils41.AST_NODE_TYPES.Literal) {
|
|
5767
|
+
return typeof node.value === "string" ? node.value : RUNTIME_MARKER;
|
|
5768
|
+
}
|
|
5769
|
+
if (node.type === import_utils41.AST_NODE_TYPES.BinaryExpression && node.operator === "+") {
|
|
5770
|
+
return staticStatementText(node.left) + staticStatementText(node.right);
|
|
5771
|
+
}
|
|
5772
|
+
return RUNTIME_MARKER;
|
|
5773
|
+
}
|
|
5774
|
+
function looksLikeSql(node) {
|
|
5775
|
+
return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));
|
|
5776
|
+
}
|
|
5302
5777
|
function statementMethodName(node, methods) {
|
|
5303
5778
|
const callee = node.callee;
|
|
5304
5779
|
if (callee.type !== import_utils41.AST_NODE_TYPES.MemberExpression || callee.computed || callee.property.type !== import_utils41.AST_NODE_TYPES.Identifier) {
|
|
@@ -5343,7 +5818,7 @@ var no_dynamic_sql_default = import_utils41.ESLintUtils.RuleCreator(
|
|
|
5343
5818
|
return;
|
|
5344
5819
|
}
|
|
5345
5820
|
const statement = node.arguments[0];
|
|
5346
|
-
if (statement === void 0) {
|
|
5821
|
+
if (statement === void 0 || !looksLikeSql(statement)) {
|
|
5347
5822
|
return;
|
|
5348
5823
|
}
|
|
5349
5824
|
const offenders = statement.type === import_utils41.AST_NODE_TYPES.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
@@ -5365,16 +5840,28 @@ var DEFAULT_ALLOW = [
|
|
|
5365
5840
|
"[\\\\/]clients?[\\\\/]",
|
|
5366
5841
|
"-client\\.[cm]?[jt]sx?$",
|
|
5367
5842
|
"[\\\\/]http-client\\.[cm]?[jt]sx?$",
|
|
5843
|
+
// The `api` spelling of the same client-layer convention: an `api/` directory,
|
|
5844
|
+
// a bare `api.ts`, or a `*-api.ts` / `*.api.ts` module.
|
|
5845
|
+
"[\\\\/]api[\\\\/]",
|
|
5846
|
+
"[\\\\/]api\\.[cm]?[jt]sx?$",
|
|
5847
|
+
"[-.]api\\.[cm]?[jt]sx?$",
|
|
5368
5848
|
"\\.test\\.",
|
|
5369
5849
|
"\\.spec\\.",
|
|
5850
|
+
// `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.
|
|
5851
|
+
"\\.(test|spec)-d\\.[cm]?[jt]sx?$",
|
|
5852
|
+
"-(test|spec)\\.[cm]?[jt]sx?$",
|
|
5370
5853
|
"[\\\\/]__tests__[\\\\/]",
|
|
5371
|
-
"[\\\\/]__mocks__[\\\\/]"
|
|
5854
|
+
"[\\\\/]__mocks__[\\\\/]",
|
|
5855
|
+
"[\\\\/]tests?[\\\\/]",
|
|
5856
|
+
// jscodeshift input/output fixtures — text a codemod transforms, not code.
|
|
5857
|
+
"[\\\\/]__testfixtures__[\\\\/]"
|
|
5372
5858
|
];
|
|
5373
5859
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
|
|
5374
5860
|
"globalThis",
|
|
5375
5861
|
"window",
|
|
5376
5862
|
"self"
|
|
5377
5863
|
]);
|
|
5864
|
+
var PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;
|
|
5378
5865
|
function isGlobalFetchCall(node) {
|
|
5379
5866
|
const callee = node.callee;
|
|
5380
5867
|
if (callee.type === "Identifier") {
|
|
@@ -5385,6 +5872,23 @@ function isGlobalFetchCall(node) {
|
|
|
5385
5872
|
}
|
|
5386
5873
|
return false;
|
|
5387
5874
|
}
|
|
5875
|
+
function identifierLikeName(node) {
|
|
5876
|
+
if (node.type === "Identifier") {
|
|
5877
|
+
return node.name;
|
|
5878
|
+
}
|
|
5879
|
+
if (node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier") {
|
|
5880
|
+
return node.property.name;
|
|
5881
|
+
}
|
|
5882
|
+
return null;
|
|
5883
|
+
}
|
|
5884
|
+
function isPresignedUrlTransfer(node) {
|
|
5885
|
+
const first = node.arguments[0];
|
|
5886
|
+
if (first === void 0) {
|
|
5887
|
+
return false;
|
|
5888
|
+
}
|
|
5889
|
+
const name = identifierLikeName(first);
|
|
5890
|
+
return name !== null && PRESIGNED_URL_NAME_RE.test(name);
|
|
5891
|
+
}
|
|
5388
5892
|
function compile(patterns) {
|
|
5389
5893
|
const compiled = [];
|
|
5390
5894
|
for (const pattern of patterns) {
|
|
@@ -5431,6 +5935,9 @@ var no_raw_fetch_outside_clients_default = import_utils42.ESLintUtils.RuleCreato
|
|
|
5431
5935
|
}
|
|
5432
5936
|
return {
|
|
5433
5937
|
CallExpression(node) {
|
|
5938
|
+
if (isPresignedUrlTransfer(node)) {
|
|
5939
|
+
return;
|
|
5940
|
+
}
|
|
5434
5941
|
if (isGlobalFetchCall(node)) {
|
|
5435
5942
|
context.report({ node, messageId: "rawFetch" });
|
|
5436
5943
|
}
|
|
@@ -5620,6 +6127,9 @@ var no_zod_native_enum_default = import_utils44.ESLintUtils.RuleCreator(
|
|
|
5620
6127
|
if (isIgnoredFile2(context.filename, sourceCode.getText())) {
|
|
5621
6128
|
return {};
|
|
5622
6129
|
}
|
|
6130
|
+
if (isTestFile(context.filename)) {
|
|
6131
|
+
return {};
|
|
6132
|
+
}
|
|
5623
6133
|
let services;
|
|
5624
6134
|
try {
|
|
5625
6135
|
services = import_utils44.ESLintUtils.getParserServices(context);
|
|
@@ -6053,7 +6563,7 @@ var rules = {
|
|
|
6053
6563
|
var plugin = {
|
|
6054
6564
|
meta: {
|
|
6055
6565
|
name: "@sarj/eslint-plugin",
|
|
6056
|
-
version: "2.
|
|
6566
|
+
version: "2.12.1"
|
|
6057
6567
|
},
|
|
6058
6568
|
rules,
|
|
6059
6569
|
configs: {
|
|
@@ -6078,7 +6588,7 @@ var plugin = {
|
|
|
6078
6588
|
"@sarj/prefer-discriminated-union": "warn",
|
|
6079
6589
|
"@sarj/no-comment-cruft": "warn",
|
|
6080
6590
|
// Frontend / styling — distilled from frontend PR-review mining.
|
|
6081
|
-
"@sarj/prefer-semantic-colors": "warn",
|
|
6591
|
+
"@sarj/prefer-semantic-colors": ["warn", { requireSemanticTokens: true }],
|
|
6082
6592
|
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
6083
6593
|
"@sarj/no-fat-try-blocks": "warn",
|
|
6084
6594
|
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
@@ -6137,7 +6647,7 @@ var plugin = {
|
|
|
6137
6647
|
"@sarj/no-comment-cruft": "error",
|
|
6138
6648
|
// Frontend / styling — distilled from frontend PR-review mining. Stylistic,
|
|
6139
6649
|
// no autofix → warn (rollout should prove the FP rate before raising it).
|
|
6140
|
-
"@sarj/prefer-semantic-colors": "error",
|
|
6650
|
+
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
6141
6651
|
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
6142
6652
|
"@sarj/no-fat-try-blocks": "error",
|
|
6143
6653
|
"@sarj/no-cors-wildcard-with-credentials": "error",
|