@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.js
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
// src/rules/enforce-file-structure.ts
|
|
2
2
|
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
|
+
|
|
4
|
+
// src/rules/_paths.ts
|
|
5
|
+
var SCRIPT_FILE_RE = /([\\/]scripts[\\/])|(\.mjs$)/;
|
|
6
|
+
var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
7
|
+
var GENERATED_FILE_RE = /([\\/]generated[\\/])|(\.gen\.[cm]?[jt]sx?$)|(\.generated\.[cm]?[jt]sx?$)|(\.d\.[cm]?ts$)/;
|
|
8
|
+
function isTestFile(filename) {
|
|
9
|
+
const normalized = filename.replaceAll("\\", "/");
|
|
10
|
+
const base = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
11
|
+
if (/\.(test|spec|e2e|integration)\.[cm]?[jt]sx?$/.test(base)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return /(^|\/)(tests?|__tests__|__mocks__|fixtures|e2e|integration)\//.test(normalized);
|
|
15
|
+
}
|
|
16
|
+
function isStoryFile(filename) {
|
|
17
|
+
return STORY_FILE_RE.test(filename);
|
|
18
|
+
}
|
|
19
|
+
function isGeneratedFile(filename, sourceText = "") {
|
|
20
|
+
return GENERATED_FILE_RE.test(filename.replaceAll("\\", "/")) || /@generated\b/.test(sourceText.slice(0, 1024));
|
|
21
|
+
}
|
|
22
|
+
function isScriptFile(filename) {
|
|
23
|
+
return SCRIPT_FILE_RE.test(filename);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/rules/enforce-file-structure.ts
|
|
3
27
|
var classifyStatement = (statement) => {
|
|
4
28
|
switch (statement.type) {
|
|
5
29
|
case AST_NODE_TYPES.ImportDeclaration:
|
|
@@ -36,6 +60,9 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
36
60
|
},
|
|
37
61
|
defaultOptions: [],
|
|
38
62
|
create(context) {
|
|
63
|
+
if (isTestFile(context.filename)) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
39
66
|
return {
|
|
40
67
|
Program(node) {
|
|
41
68
|
const body = node.body;
|
|
@@ -49,6 +76,7 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
49
76
|
});
|
|
50
77
|
}
|
|
51
78
|
let seenBody = false;
|
|
79
|
+
let inMisplacedRun = false;
|
|
52
80
|
for (const statement of body) {
|
|
53
81
|
if (isStringDirective(statement)) continue;
|
|
54
82
|
switch (classifyStatement(statement)) {
|
|
@@ -56,9 +84,11 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
56
84
|
continue;
|
|
57
85
|
case "body":
|
|
58
86
|
seenBody = true;
|
|
87
|
+
inMisplacedRun = false;
|
|
59
88
|
continue;
|
|
60
89
|
case "import":
|
|
61
|
-
if (seenBody) {
|
|
90
|
+
if (seenBody && !inMisplacedRun) {
|
|
91
|
+
inMisplacedRun = true;
|
|
62
92
|
context.report({
|
|
63
93
|
node: statement,
|
|
64
94
|
messageId: "importsFirst"
|
|
@@ -217,9 +247,13 @@ var STEP_NARRATION_RE = /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|a
|
|
|
217
247
|
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
248
|
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
249
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
250
|
+
var ENUMERATED_ITEM_RE = /^(?:\d+[.):]|[-*•])\s+\S/;
|
|
251
|
+
var ENUMERATED_ITEM_MIN_WORDS = 3;
|
|
252
|
+
var ENUMERATED_PREAMBLE_MIN_ITEMS = 2;
|
|
220
253
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
221
254
|
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
222
255
|
var REGION_RE = /^#?(?:end)?region\b/i;
|
|
256
|
+
var DIAGRAM_ARROW_RE = /[-=~]{2,}>|<[-=~]{2,}/;
|
|
223
257
|
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
258
|
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
225
259
|
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
@@ -233,7 +267,8 @@ function isDirective(text) {
|
|
|
233
267
|
function isBanner(text) {
|
|
234
268
|
const t = text.trim();
|
|
235
269
|
if (!t) return false;
|
|
236
|
-
|
|
270
|
+
if (BANNER_FULL_RE.test(t) || REGION_RE.test(t)) return true;
|
|
271
|
+
return BANNER_RUN_RE.test(t) && !DIAGRAM_ARROW_RE.test(t);
|
|
237
272
|
}
|
|
238
273
|
function looksLikeCode(text) {
|
|
239
274
|
const t = text.trim();
|
|
@@ -244,6 +279,10 @@ function looksLikeCode(text) {
|
|
|
244
279
|
function hasPseudocode(text) {
|
|
245
280
|
return PSEUDOCODE_RE.test(text);
|
|
246
281
|
}
|
|
282
|
+
function isEnumeratedProseItem(text) {
|
|
283
|
+
const t = text.trim();
|
|
284
|
+
return ENUMERATED_ITEM_RE.test(t) && t.split(/\s+/).length >= ENUMERATED_ITEM_MIN_WORDS;
|
|
285
|
+
}
|
|
247
286
|
function isProse(text) {
|
|
248
287
|
const t = text.trim();
|
|
249
288
|
if (!t) return false;
|
|
@@ -356,13 +395,32 @@ function restatesNextLine(body, statement) {
|
|
|
356
395
|
const code = codeTokens(head);
|
|
357
396
|
return content.every((word) => code.has(word));
|
|
358
397
|
}
|
|
359
|
-
|
|
398
|
+
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;
|
|
399
|
+
function isRedundantNarration(body, statementBelow, standalone) {
|
|
360
400
|
const t = body.trim();
|
|
361
401
|
if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;
|
|
362
|
-
if (
|
|
363
|
-
|
|
402
|
+
if (standalone) {
|
|
403
|
+
if (STEP_NARRATION_RE.test(t)) return true;
|
|
404
|
+
if (META_COMMENTARY_RE.test(t) && !JUSTIFICATION_RE.test(t)) return true;
|
|
405
|
+
}
|
|
364
406
|
return restatesNextLine(t, statementBelow);
|
|
365
407
|
}
|
|
408
|
+
function areAdjacentLineComments(a, b) {
|
|
409
|
+
return a !== void 0 && b !== void 0 && a.type === "Line" && b.type === "Line" && b.loc.start.line === a.loc.end.line + 1;
|
|
410
|
+
}
|
|
411
|
+
function isInsideCommentRun(comments, index) {
|
|
412
|
+
const comment = comments[index];
|
|
413
|
+
return areAdjacentLineComments(comments[index - 1], comment) || areAdjacentLineComments(comment, comments[index + 1]);
|
|
414
|
+
}
|
|
415
|
+
var LEAD_IN_SCAN_LIMIT = 24;
|
|
416
|
+
function hasIllustrationLeadInAbove(comments, index) {
|
|
417
|
+
for (let i = index - 1; i >= 0 && index - i <= LEAD_IN_SCAN_LIMIT; i--) {
|
|
418
|
+
if (!areAdjacentLineComments(comments[i], comments[i + 1])) return false;
|
|
419
|
+
const body = stripCommentMarker(comments[i]?.value ?? "");
|
|
420
|
+
if (body.length > 0 && body.endsWith(":")) return true;
|
|
421
|
+
}
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
366
424
|
function hasCommentedOutCode(texts, precedingProse) {
|
|
367
425
|
for (let i = 0; i < texts.length; i++) {
|
|
368
426
|
const line = texts[i];
|
|
@@ -422,6 +480,9 @@ var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
|
422
480
|
const bodies = leading.map((c) => stripCommentMarker(c.value));
|
|
423
481
|
if (bodies.some((body) => LICENSE_RE.test(body))) return;
|
|
424
482
|
if (bodies.some((body) => isProse(body))) return;
|
|
483
|
+
if (bodies.filter(isEnumeratedProseItem).length >= ENUMERATED_PREAMBLE_MIN_ITEMS) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
425
486
|
context.report({ node: first, messageId: "fileHeaderPreamble" });
|
|
426
487
|
}
|
|
427
488
|
return {
|
|
@@ -440,14 +501,15 @@ var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
|
440
501
|
}
|
|
441
502
|
const prev = comments[i - 1];
|
|
442
503
|
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)) {
|
|
504
|
+
if (hasCommentedOutCode(texts, precedingProse) && !hasIllustrationLeadInAbove(comments, i)) {
|
|
444
505
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
445
506
|
continue;
|
|
446
507
|
}
|
|
447
508
|
if (comment.type === "Line" && texts.length === 1) {
|
|
448
509
|
const body = texts[0];
|
|
449
510
|
const statement = restatableStatementBelow(comment, sourceCode);
|
|
450
|
-
|
|
511
|
+
const standalone = !isInsideCommentRun(comments, i);
|
|
512
|
+
if (body !== void 0 && isRedundantNarration(body, statement, standalone)) {
|
|
451
513
|
context.report({ node: comment, messageId: "redundantNarration" });
|
|
452
514
|
}
|
|
453
515
|
}
|
|
@@ -686,6 +748,9 @@ var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
|
686
748
|
},
|
|
687
749
|
defaultOptions: [],
|
|
688
750
|
create(context) {
|
|
751
|
+
if (isTestFile(context.filename)) {
|
|
752
|
+
return {};
|
|
753
|
+
}
|
|
689
754
|
return {
|
|
690
755
|
CallExpression(node) {
|
|
691
756
|
if (!isMathRandomCall(node)) {
|
|
@@ -715,6 +780,22 @@ import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
|
|
|
715
780
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
716
781
|
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
717
782
|
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
783
|
+
var PAYLOAD_PROPS = /* @__PURE__ */ new Set([
|
|
784
|
+
"data",
|
|
785
|
+
"status",
|
|
786
|
+
"statuscode",
|
|
787
|
+
"statustext",
|
|
788
|
+
"code",
|
|
789
|
+
"issues",
|
|
790
|
+
"details",
|
|
791
|
+
"body",
|
|
792
|
+
"payload",
|
|
793
|
+
"response",
|
|
794
|
+
"info",
|
|
795
|
+
"meta",
|
|
796
|
+
"metadata",
|
|
797
|
+
"context"
|
|
798
|
+
]);
|
|
718
799
|
function isCatchBinding(scope, name) {
|
|
719
800
|
let current = scope;
|
|
720
801
|
while (current) {
|
|
@@ -738,7 +819,11 @@ function memberSuggestsError(member, scope) {
|
|
|
738
819
|
const base = member.object;
|
|
739
820
|
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
740
821
|
if (baseSuggestsError) {
|
|
741
|
-
|
|
822
|
+
if (propName2 === null) {
|
|
823
|
+
return true;
|
|
824
|
+
}
|
|
825
|
+
const lowered = propName2.toLowerCase();
|
|
826
|
+
return !SAFE_STRING_PROPS.has(lowered) && !PAYLOAD_PROPS.has(lowered);
|
|
742
827
|
}
|
|
743
828
|
return false;
|
|
744
829
|
}
|
|
@@ -1025,13 +1110,17 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
1025
1110
|
return {
|
|
1026
1111
|
CatchClause(node) {
|
|
1027
1112
|
const statements = node.body.body;
|
|
1113
|
+
const isDocumented = context.sourceCode.getCommentsInside(node.body).length > 0;
|
|
1028
1114
|
if (statements.length === 0) {
|
|
1029
|
-
if (
|
|
1115
|
+
if (isDocumented) {
|
|
1030
1116
|
return;
|
|
1031
1117
|
}
|
|
1032
1118
|
context.report({ node, messageId: "emptyCatch" });
|
|
1033
1119
|
return;
|
|
1034
1120
|
}
|
|
1121
|
+
if (isDocumented) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1035
1124
|
const everyStatementIsLogging = statements.every(
|
|
1036
1125
|
(statement) => isLoggingCallStatement(statement)
|
|
1037
1126
|
);
|
|
@@ -1045,6 +1134,11 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
1045
1134
|
|
|
1046
1135
|
// src/rules/no-raw-env.ts
|
|
1047
1136
|
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
1137
|
+
var CONFIG_FILE_RE = /(^|[\\/])[\w.-]+\.config\.[cm]?[jt]sx?$/;
|
|
1138
|
+
var ENV_BOUNDARY_FILE_RE = /(^|[\\/])(?:env|client-env|server-env|client-settings|server-settings)\.[cm]?[jt]sx?$/;
|
|
1139
|
+
function isValidatedEnvBoundary(filename, sourceText) {
|
|
1140
|
+
return ENV_BOUNDARY_FILE_RE.test(filename.replaceAll("\\", "/")) && /\bz\.object\s*\(/.test(sourceText) && /\.parse\s*\(/.test(sourceText);
|
|
1141
|
+
}
|
|
1048
1142
|
function isProcessEnv(node) {
|
|
1049
1143
|
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
1050
1144
|
}
|
|
@@ -1062,6 +1156,21 @@ function isBuildTimeConstantAccess(node) {
|
|
|
1062
1156
|
const parent = node.parent;
|
|
1063
1157
|
return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
|
|
1064
1158
|
}
|
|
1159
|
+
function isWriteTarget(node) {
|
|
1160
|
+
const access = node.parent.type === "MemberExpression" && node.parent.object === node ? node.parent : node;
|
|
1161
|
+
const parent = access.parent;
|
|
1162
|
+
if (parent.type === "AssignmentExpression") {
|
|
1163
|
+
return parent.left === access;
|
|
1164
|
+
}
|
|
1165
|
+
if (parent.type === "UnaryExpression") {
|
|
1166
|
+
return parent.operator === "delete";
|
|
1167
|
+
}
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1170
|
+
function isWholeEnvSpread(node) {
|
|
1171
|
+
const parent = node.parent;
|
|
1172
|
+
return parent.type === "SpreadElement" && parent.argument === node;
|
|
1173
|
+
}
|
|
1065
1174
|
var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
1066
1175
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1067
1176
|
)({
|
|
@@ -1078,9 +1187,13 @@ var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
|
1078
1187
|
},
|
|
1079
1188
|
defaultOptions: [],
|
|
1080
1189
|
create(context) {
|
|
1190
|
+
const filename = context.filename;
|
|
1191
|
+
if (isTestFile(filename) || isScriptFile(filename) || CONFIG_FILE_RE.test(filename.replaceAll("\\", "/")) || isValidatedEnvBoundary(filename, context.sourceCode.text)) {
|
|
1192
|
+
return {};
|
|
1193
|
+
}
|
|
1081
1194
|
return {
|
|
1082
1195
|
MemberExpression(node) {
|
|
1083
|
-
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
|
|
1196
|
+
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node) && !isWriteTarget(node) && !isWholeEnvSpread(node)) {
|
|
1084
1197
|
context.report({
|
|
1085
1198
|
node,
|
|
1086
1199
|
messageId: "noRawEnv"
|
|
@@ -1293,6 +1406,32 @@ function enclosingReturnTypeNode(node) {
|
|
|
1293
1406
|
}
|
|
1294
1407
|
return null;
|
|
1295
1408
|
}
|
|
1409
|
+
var PREDICATE_NAME_RE = /^(is|has|can|should|must|does|did|was|were|are)[A-Z]/;
|
|
1410
|
+
var PREDICATE_SUFFIX_RE = /(Exists?|Available|Enabled|Disabled)$/;
|
|
1411
|
+
function enclosingFunctionName(node) {
|
|
1412
|
+
let current = node.parent;
|
|
1413
|
+
while (current !== void 0 && current !== null) {
|
|
1414
|
+
if (isFunctionNode(current)) {
|
|
1415
|
+
if ("id" in current && isNode(current.id) && current.id.type === AST_NODE_TYPES4.Identifier) {
|
|
1416
|
+
return current.id.name;
|
|
1417
|
+
}
|
|
1418
|
+
const parent = current.parent;
|
|
1419
|
+
if (parent?.type === AST_NODE_TYPES4.VariableDeclarator && parent.id.type === AST_NODE_TYPES4.Identifier) {
|
|
1420
|
+
return parent.id.name;
|
|
1421
|
+
}
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
current = current.parent;
|
|
1425
|
+
}
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
function isNamedBooleanPredicate(catchNode, kind) {
|
|
1429
|
+
if (kind !== "boolean") {
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1432
|
+
const name = enclosingFunctionName(catchNode);
|
|
1433
|
+
return name !== null && (PREDICATE_NAME_RE.test(name) || PREDICATE_SUFFIX_RE.test(name));
|
|
1434
|
+
}
|
|
1296
1435
|
function isDeclaredBooleanPredicate(catchNode, kind) {
|
|
1297
1436
|
if (kind !== "boolean") {
|
|
1298
1437
|
return false;
|
|
@@ -1336,9 +1475,32 @@ function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
|
1336
1475
|
if (isWithin(current, catchNode.body)) {
|
|
1337
1476
|
return false;
|
|
1338
1477
|
}
|
|
1339
|
-
return
|
|
1478
|
+
return returnedSentinelKinds(current.argument).has(kind);
|
|
1340
1479
|
});
|
|
1341
1480
|
}
|
|
1481
|
+
function returnedSentinelKinds(arg) {
|
|
1482
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
1483
|
+
if (arg === null) {
|
|
1484
|
+
return kinds;
|
|
1485
|
+
}
|
|
1486
|
+
const direct = sentinelKind(arg);
|
|
1487
|
+
if (direct !== null) {
|
|
1488
|
+
kinds.add(direct);
|
|
1489
|
+
return kinds;
|
|
1490
|
+
}
|
|
1491
|
+
if (arg.type === AST_NODE_TYPES4.ConditionalExpression) {
|
|
1492
|
+
for (const branch of [arg.consequent, arg.alternate]) {
|
|
1493
|
+
for (const nested of returnedSentinelKinds(branch)) {
|
|
1494
|
+
kinds.add(nested);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
} else if (arg.type === AST_NODE_TYPES4.LogicalExpression && (arg.operator === "??" || arg.operator === "||")) {
|
|
1498
|
+
for (const nested of returnedSentinelKinds(arg.right)) {
|
|
1499
|
+
kinds.add(nested);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
return kinds;
|
|
1503
|
+
}
|
|
1342
1504
|
function isWithin(node, ancestor) {
|
|
1343
1505
|
let current = node;
|
|
1344
1506
|
while (current !== void 0 && current !== null) {
|
|
@@ -1408,6 +1570,9 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
1408
1570
|
return;
|
|
1409
1571
|
}
|
|
1410
1572
|
const kind = sentinelKind(last.argument);
|
|
1573
|
+
if (kind !== null && isNamedBooleanPredicate(node, kind)) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1411
1576
|
if (kind !== null && isDeclaredBooleanPredicate(node, kind)) {
|
|
1412
1577
|
return;
|
|
1413
1578
|
}
|
|
@@ -1426,7 +1591,8 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
1426
1591
|
// src/rules/no-sequential-await.ts
|
|
1427
1592
|
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
1428
1593
|
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;
|
|
1594
|
+
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;
|
|
1595
|
+
var BENCH_FILE_RE = /(^|[\\/])bench(marks?)?[\\/]|\.bench\.[cm]?[jt]sx?$/i;
|
|
1430
1596
|
function isFunctionLike(node) {
|
|
1431
1597
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
1432
1598
|
}
|
|
@@ -1460,6 +1626,7 @@ function collectAwaits(root) {
|
|
|
1460
1626
|
});
|
|
1461
1627
|
return awaits;
|
|
1462
1628
|
}
|
|
1629
|
+
var ASSERTION_CALLEE_RE = /^(expect|assert|should|invariant)$/;
|
|
1463
1630
|
function hasEarlyExit(root) {
|
|
1464
1631
|
let found = false;
|
|
1465
1632
|
visitScope(root, (node) => {
|
|
@@ -1483,6 +1650,9 @@ function isTimerYield(node) {
|
|
|
1483
1650
|
return true;
|
|
1484
1651
|
}
|
|
1485
1652
|
if (arg.type === "CallExpression") {
|
|
1653
|
+
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")) {
|
|
1654
|
+
return true;
|
|
1655
|
+
}
|
|
1486
1656
|
const name = calleeName2(arg.callee);
|
|
1487
1657
|
return name !== null && TIMER_HELPER_RE.test(name);
|
|
1488
1658
|
}
|
|
@@ -1493,6 +1663,25 @@ function isQueueDrain(node) {
|
|
|
1493
1663
|
const arg = node.argument;
|
|
1494
1664
|
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
1665
|
}
|
|
1666
|
+
function hasAssertion(root) {
|
|
1667
|
+
let found = false;
|
|
1668
|
+
visitScope(root, (node) => {
|
|
1669
|
+
if (node.type !== "CallExpression") {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
let callee = node.callee;
|
|
1673
|
+
while (callee.type === "MemberExpression") {
|
|
1674
|
+
callee = callee.object;
|
|
1675
|
+
}
|
|
1676
|
+
if (callee.type === "CallExpression") {
|
|
1677
|
+
callee = callee.callee;
|
|
1678
|
+
}
|
|
1679
|
+
if (callee.type === "Identifier" && ASSERTION_CALLEE_RE.test(callee.name)) {
|
|
1680
|
+
found = true;
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1683
|
+
return found;
|
|
1684
|
+
}
|
|
1496
1685
|
function referencesName(root, name) {
|
|
1497
1686
|
let found = false;
|
|
1498
1687
|
visitScope(root, (node) => {
|
|
@@ -1537,13 +1726,16 @@ function testStateIsAssignedInBody(test, body) {
|
|
|
1537
1726
|
});
|
|
1538
1727
|
return found;
|
|
1539
1728
|
}
|
|
1540
|
-
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1729
|
+
function shouldReport(awaits, earlyExit, iterableText, asserts = false) {
|
|
1541
1730
|
if (awaits.length === 0) {
|
|
1542
1731
|
return false;
|
|
1543
1732
|
}
|
|
1544
1733
|
if (earlyExit) {
|
|
1545
1734
|
return false;
|
|
1546
1735
|
}
|
|
1736
|
+
if (asserts) {
|
|
1737
|
+
return false;
|
|
1738
|
+
}
|
|
1547
1739
|
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1548
1740
|
return false;
|
|
1549
1741
|
}
|
|
@@ -1567,6 +1759,9 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1567
1759
|
},
|
|
1568
1760
|
defaultOptions: [],
|
|
1569
1761
|
create(context) {
|
|
1762
|
+
if (isTestFile(context.filename) || BENCH_FILE_RE.test(context.filename)) {
|
|
1763
|
+
return {};
|
|
1764
|
+
}
|
|
1570
1765
|
function loopParts(node) {
|
|
1571
1766
|
if (node.type === "ForStatement") {
|
|
1572
1767
|
return [node.body, node.test, node.update];
|
|
@@ -1588,6 +1783,7 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1588
1783
|
}
|
|
1589
1784
|
const awaits = [];
|
|
1590
1785
|
let earlyExit = false;
|
|
1786
|
+
let asserts = false;
|
|
1591
1787
|
for (const part of loopParts(node)) {
|
|
1592
1788
|
if (part === null || isLoop(part)) {
|
|
1593
1789
|
continue;
|
|
@@ -1596,8 +1792,11 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1596
1792
|
if (!earlyExit && hasEarlyExit(part)) {
|
|
1597
1793
|
earlyExit = true;
|
|
1598
1794
|
}
|
|
1795
|
+
if (!asserts && hasAssertion(part)) {
|
|
1796
|
+
asserts = true;
|
|
1797
|
+
}
|
|
1599
1798
|
}
|
|
1600
|
-
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1799
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node), asserts)) {
|
|
1601
1800
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1602
1801
|
}
|
|
1603
1802
|
}
|
|
@@ -1630,7 +1829,7 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
1630
1829
|
const awaits = collectAwaits(callback.body);
|
|
1631
1830
|
const earlyExit = hasEarlyExit(callback.body);
|
|
1632
1831
|
const iterableText = context.sourceCode.getText(callee.object);
|
|
1633
|
-
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1832
|
+
if (shouldReport(awaits, earlyExit, iterableText, hasAssertion(callback.body))) {
|
|
1634
1833
|
context.report({ node, messageId: "noSequentialAwait" });
|
|
1635
1834
|
}
|
|
1636
1835
|
}
|
|
@@ -1699,20 +1898,30 @@ function isConcatOntoTarget(rhs, target) {
|
|
|
1699
1898
|
}
|
|
1700
1899
|
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1701
1900
|
}
|
|
1702
|
-
function
|
|
1901
|
+
function isDeclaredInsideLoop(variable, loop) {
|
|
1902
|
+
const def = variable.defs[0];
|
|
1903
|
+
if (def === void 0) {
|
|
1904
|
+
return false;
|
|
1905
|
+
}
|
|
1906
|
+
const body = loop.body;
|
|
1907
|
+
const [declStart, declEnd] = def.node.range;
|
|
1908
|
+
const [bodyStart, bodyEnd] = body.range;
|
|
1909
|
+
return declStart >= bodyStart && declEnd <= bodyEnd;
|
|
1910
|
+
}
|
|
1911
|
+
function enclosingLoop(node) {
|
|
1703
1912
|
let child = node;
|
|
1704
1913
|
let parent = node.parent;
|
|
1705
1914
|
while (parent !== void 0 && parent !== null) {
|
|
1706
1915
|
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
1707
1916
|
const loop = parent;
|
|
1708
1917
|
if (loop.body === child) {
|
|
1709
|
-
return
|
|
1918
|
+
return loop;
|
|
1710
1919
|
}
|
|
1711
1920
|
}
|
|
1712
1921
|
child = parent;
|
|
1713
1922
|
parent = parent.parent;
|
|
1714
1923
|
}
|
|
1715
|
-
return
|
|
1924
|
+
return null;
|
|
1716
1925
|
}
|
|
1717
1926
|
var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
1718
1927
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -1730,6 +1939,7 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1730
1939
|
},
|
|
1731
1940
|
defaultOptions: [],
|
|
1732
1941
|
create(context) {
|
|
1942
|
+
const reported = /* @__PURE__ */ new WeakMap();
|
|
1733
1943
|
return {
|
|
1734
1944
|
AssignmentExpression(node) {
|
|
1735
1945
|
if (node.left.type !== "Identifier") {
|
|
@@ -1739,7 +1949,8 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1739
1949
|
if (!isAccumulation) {
|
|
1740
1950
|
return;
|
|
1741
1951
|
}
|
|
1742
|
-
|
|
1952
|
+
const loop = enclosingLoop(node);
|
|
1953
|
+
if (loop === null) {
|
|
1743
1954
|
return;
|
|
1744
1955
|
}
|
|
1745
1956
|
const scope = context.sourceCode.getScope(node);
|
|
@@ -1750,6 +1961,18 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
1750
1961
|
if (!isStringInitializedVariable(variable)) {
|
|
1751
1962
|
return;
|
|
1752
1963
|
}
|
|
1964
|
+
if (isDeclaredInsideLoop(variable, loop)) {
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
let seen = reported.get(loop);
|
|
1968
|
+
if (seen === void 0) {
|
|
1969
|
+
seen = /* @__PURE__ */ new Set();
|
|
1970
|
+
reported.set(loop, seen);
|
|
1971
|
+
}
|
|
1972
|
+
if (seen.has(node.left.name)) {
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
seen.add(node.left.name);
|
|
1753
1976
|
context.report({
|
|
1754
1977
|
node,
|
|
1755
1978
|
messageId: "noStringConcatInLoop"
|
|
@@ -1784,7 +2007,31 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
1784
2007
|
"KeyboardEvent",
|
|
1785
2008
|
"TouchEvent"
|
|
1786
2009
|
]);
|
|
2010
|
+
var CLIENT_REQUIRED_MODULES = /* @__PURE__ */ new Set(["next/dynamic"]);
|
|
1787
2011
|
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\/)/;
|
|
2012
|
+
var isBareSpecifier = (source) => !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("@/") && !source.startsWith("~");
|
|
2013
|
+
var jsxRootName = (name) => {
|
|
2014
|
+
let current = name;
|
|
2015
|
+
while (current.type === AST_NODE_TYPES5.JSXMemberExpression) {
|
|
2016
|
+
current = current.object;
|
|
2017
|
+
}
|
|
2018
|
+
return current.type === AST_NODE_TYPES5.JSXIdentifier ? current.name : "";
|
|
2019
|
+
};
|
|
2020
|
+
var subtreeReadsImportedBinding = (node, imported) => {
|
|
2021
|
+
if (node.type === AST_NODE_TYPES5.Identifier) {
|
|
2022
|
+
return imported.has(node.name);
|
|
2023
|
+
}
|
|
2024
|
+
for (const key of Object.keys(node)) {
|
|
2025
|
+
if (key === "parent") continue;
|
|
2026
|
+
const value = node[key];
|
|
2027
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2028
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string" && subtreeReadsImportedBinding(child, imported)) {
|
|
2029
|
+
return true;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
return false;
|
|
2034
|
+
};
|
|
1788
2035
|
var isUseClientDirective = (node) => {
|
|
1789
2036
|
return node.type === AST_NODE_TYPES5.ExpressionStatement && node.expression.type === AST_NODE_TYPES5.Literal && node.expression.value === "use client";
|
|
1790
2037
|
};
|
|
@@ -1834,6 +2081,8 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1834
2081
|
}
|
|
1835
2082
|
let directiveNode = null;
|
|
1836
2083
|
let hasClientIndicator = false;
|
|
2084
|
+
const importedLocals = /* @__PURE__ */ new Set();
|
|
2085
|
+
const externalLocals = /* @__PURE__ */ new Set();
|
|
1837
2086
|
const markIfHookOrContext = (callee) => {
|
|
1838
2087
|
if (callee.type === AST_NODE_TYPES5.Identifier) {
|
|
1839
2088
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
@@ -1870,7 +2119,21 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1870
2119
|
},
|
|
1871
2120
|
ImportDeclaration(node) {
|
|
1872
2121
|
if (directiveNode === null) return;
|
|
1873
|
-
if (typeof node.source.value
|
|
2122
|
+
if (typeof node.source.value !== "string") return;
|
|
2123
|
+
const source = node.source.value;
|
|
2124
|
+
if (CLIENT_ONLY_PACKAGES_REGEX.test(source) || CLIENT_REQUIRED_MODULES.has(source)) {
|
|
2125
|
+
hasClientIndicator = true;
|
|
2126
|
+
}
|
|
2127
|
+
for (const specifier of node.specifiers) {
|
|
2128
|
+
importedLocals.add(specifier.local.name);
|
|
2129
|
+
if (isBareSpecifier(source)) {
|
|
2130
|
+
externalLocals.add(specifier.local.name);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
JSXOpeningElement(node) {
|
|
2135
|
+
if (directiveNode === null) return;
|
|
2136
|
+
if (externalLocals.has(jsxRootName(node.name))) {
|
|
1874
2137
|
hasClientIndicator = true;
|
|
1875
2138
|
}
|
|
1876
2139
|
},
|
|
@@ -1878,6 +2141,10 @@ var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
|
1878
2141
|
if (directiveNode === null) return;
|
|
1879
2142
|
if (node.source !== null) {
|
|
1880
2143
|
hasClientIndicator = true;
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
if (node.declaration !== null && subtreeReadsImportedBinding(node.declaration, importedLocals)) {
|
|
2147
|
+
hasClientIndicator = true;
|
|
1881
2148
|
}
|
|
1882
2149
|
},
|
|
1883
2150
|
ExportAllDeclaration(node) {
|
|
@@ -1942,19 +2209,23 @@ function isBooleanTyped(member) {
|
|
|
1942
2209
|
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1943
2210
|
let hasStatusBoolean = false;
|
|
1944
2211
|
let optionalCount = 0;
|
|
2212
|
+
let optionalPayloadCount = 0;
|
|
1945
2213
|
for (const member of typeLiteral.members) {
|
|
1946
2214
|
if (member.type !== AST_NODE_TYPES6.TSPropertySignature) {
|
|
1947
2215
|
continue;
|
|
1948
2216
|
}
|
|
1949
2217
|
if (member.optional) {
|
|
1950
2218
|
optionalCount += 1;
|
|
2219
|
+
if (!isBooleanTyped(member)) {
|
|
2220
|
+
optionalPayloadCount += 1;
|
|
2221
|
+
}
|
|
1951
2222
|
}
|
|
1952
2223
|
const name = getMemberName(member);
|
|
1953
2224
|
if (name !== null && STATUS_MEMBER_NAMES.has(name) && isBooleanTyped(member)) {
|
|
1954
2225
|
hasStatusBoolean = true;
|
|
1955
2226
|
}
|
|
1956
2227
|
}
|
|
1957
|
-
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
2228
|
+
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS && optionalPayloadCount >= 1;
|
|
1958
2229
|
}
|
|
1959
2230
|
var prefer_discriminated_union_default = ESLintUtils13.RuleCreator(
|
|
1960
2231
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -2035,7 +2306,51 @@ var isRawPayloadSource = (node) => {
|
|
|
2035
2306
|
return true;
|
|
2036
2307
|
}
|
|
2037
2308
|
const object = unwrap(callee.object);
|
|
2038
|
-
return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES7.Identifier && object.name === "JSON"
|
|
2309
|
+
return property.name === "parse" && object !== null && object.type === AST_NODE_TYPES7.Identifier && object.name === "JSON" && // ...but not `JSON.parse(readFileSync(p, "utf8"))` — see isLocalFileRead.
|
|
2310
|
+
!isLocalFileRead(current.arguments[0]);
|
|
2311
|
+
};
|
|
2312
|
+
var FILE_READ_RE = /^(readFile|readFileSync|readJson|readJsonSync|readJSON)$/;
|
|
2313
|
+
var isLocalFileRead = (node) => {
|
|
2314
|
+
let found = false;
|
|
2315
|
+
const visit = (current) => {
|
|
2316
|
+
if (found || current === null || current === void 0) return;
|
|
2317
|
+
if (current.type === AST_NODE_TYPES7.CallExpression) {
|
|
2318
|
+
const callee = unwrap(current.callee);
|
|
2319
|
+
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;
|
|
2320
|
+
if (name !== null && FILE_READ_RE.test(name)) {
|
|
2321
|
+
found = true;
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
for (const key of Object.keys(current)) {
|
|
2326
|
+
if (key === "parent") continue;
|
|
2327
|
+
const value = current[key];
|
|
2328
|
+
for (const child of Array.isArray(value) ? value : [value]) {
|
|
2329
|
+
if (child !== null && typeof child === "object" && typeof child.type === "string") {
|
|
2330
|
+
visit(child);
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
visit(node);
|
|
2336
|
+
return found;
|
|
2337
|
+
};
|
|
2338
|
+
var ASSERTION_CALLEE_RE2 = /^(expect|assert|should|invariant)$/;
|
|
2339
|
+
var isInsideAssertion = (node) => {
|
|
2340
|
+
for (let current = node.parent; current !== void 0 && current !== null; current = current.parent) {
|
|
2341
|
+
if (current.type !== AST_NODE_TYPES7.CallExpression) continue;
|
|
2342
|
+
let callee = current.callee;
|
|
2343
|
+
while (callee.type === AST_NODE_TYPES7.MemberExpression) {
|
|
2344
|
+
callee = callee.object;
|
|
2345
|
+
}
|
|
2346
|
+
if (callee.type === AST_NODE_TYPES7.CallExpression) {
|
|
2347
|
+
callee = callee.callee;
|
|
2348
|
+
}
|
|
2349
|
+
if (callee.type === AST_NODE_TYPES7.Identifier && ASSERTION_CALLEE_RE2.test(callee.name)) {
|
|
2350
|
+
return true;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return false;
|
|
2039
2354
|
};
|
|
2040
2355
|
var findVariable2 = (scope, name) => {
|
|
2041
2356
|
let current = scope;
|
|
@@ -2094,6 +2409,9 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
2094
2409
|
},
|
|
2095
2410
|
defaultOptions: [],
|
|
2096
2411
|
create(context) {
|
|
2412
|
+
if (isTestFile(context.filename)) {
|
|
2413
|
+
return {};
|
|
2414
|
+
}
|
|
2097
2415
|
const unvalidatedVariables = /* @__PURE__ */ new Set();
|
|
2098
2416
|
const trackInitializer = (declarator) => {
|
|
2099
2417
|
if (!isRawPayloadSource(declarator.init)) return;
|
|
@@ -2165,6 +2483,7 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
2165
2483
|
}
|
|
2166
2484
|
},
|
|
2167
2485
|
MemberExpression(node) {
|
|
2486
|
+
if (isInsideAssertion(node)) return;
|
|
2168
2487
|
const scope = context.sourceCode.getScope(node);
|
|
2169
2488
|
const obj = unwrap(node.object);
|
|
2170
2489
|
if (isRawPayloadSource(obj)) {
|
|
@@ -2189,6 +2508,8 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
2189
2508
|
|
|
2190
2509
|
// src/rules/prefer-semantic-colors.ts
|
|
2191
2510
|
import { AST_NODE_TYPES as AST_NODE_TYPES8, ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
|
|
2511
|
+
import { existsSync, readFileSync } from "fs";
|
|
2512
|
+
import { dirname, join, parse } from "path";
|
|
2192
2513
|
|
|
2193
2514
|
// src/rules/_tailwind.ts
|
|
2194
2515
|
var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
|
|
@@ -2226,6 +2547,20 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
|
2226
2547
|
]);
|
|
2227
2548
|
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
2228
2549
|
var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
2550
|
+
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/;
|
|
2551
|
+
var DETECTION_FILES = [
|
|
2552
|
+
"components.json",
|
|
2553
|
+
"tailwind.config.js",
|
|
2554
|
+
"tailwind.config.cjs",
|
|
2555
|
+
"tailwind.config.mjs",
|
|
2556
|
+
"tailwind.config.ts",
|
|
2557
|
+
"app/globals.css",
|
|
2558
|
+
"src/app/globals.css",
|
|
2559
|
+
"src/index.css",
|
|
2560
|
+
"src/styles/globals.css",
|
|
2561
|
+
"styles/globals.css"
|
|
2562
|
+
];
|
|
2563
|
+
var semanticTokenCache = /* @__PURE__ */ new Map();
|
|
2229
2564
|
var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
|
|
2230
2565
|
"mask",
|
|
2231
2566
|
"clipPath",
|
|
@@ -2254,6 +2589,34 @@ var isInsideSvg = (node) => {
|
|
|
2254
2589
|
}
|
|
2255
2590
|
return false;
|
|
2256
2591
|
};
|
|
2592
|
+
var hasSemanticTokenSystem = (filename) => {
|
|
2593
|
+
let dir = dirname(filename);
|
|
2594
|
+
const root = parse(dir).root;
|
|
2595
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
2596
|
+
const cached = semanticTokenCache.get(dir);
|
|
2597
|
+
if (cached !== void 0) return cached;
|
|
2598
|
+
let found = false;
|
|
2599
|
+
for (const rel of DETECTION_FILES) {
|
|
2600
|
+
const candidate = join(dir, rel);
|
|
2601
|
+
if (!existsSync(candidate)) continue;
|
|
2602
|
+
if (rel === "components.json") {
|
|
2603
|
+
found = true;
|
|
2604
|
+
break;
|
|
2605
|
+
}
|
|
2606
|
+
try {
|
|
2607
|
+
if (SEMANTIC_TOKEN_RE.test(readFileSync(candidate, "utf8"))) {
|
|
2608
|
+
found = true;
|
|
2609
|
+
break;
|
|
2610
|
+
}
|
|
2611
|
+
} catch {
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
semanticTokenCache.set(dir, found);
|
|
2615
|
+
if (found || dir === root) return found;
|
|
2616
|
+
dir = dirname(dir);
|
|
2617
|
+
}
|
|
2618
|
+
return false;
|
|
2619
|
+
};
|
|
2257
2620
|
var propName = (key) => {
|
|
2258
2621
|
if (key.type === AST_NODE_TYPES8.Identifier) return key.name;
|
|
2259
2622
|
if (key.type === AST_NODE_TYPES8.Literal && typeof key.value === "string") return key.value;
|
|
@@ -2268,16 +2631,27 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
2268
2631
|
docs: {
|
|
2269
2632
|
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."
|
|
2270
2633
|
},
|
|
2271
|
-
schema: [
|
|
2634
|
+
schema: [
|
|
2635
|
+
{
|
|
2636
|
+
type: "object",
|
|
2637
|
+
additionalProperties: false,
|
|
2638
|
+
properties: {
|
|
2639
|
+
requireSemanticTokens: { type: "boolean" }
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
],
|
|
2272
2643
|
messages: {
|
|
2273
2644
|
rawPalette: "Raw palette class '{{class}}' \u2014 use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).",
|
|
2274
2645
|
arbitraryColor: "Hardcoded color '{{class}}' \u2014 use a semantic token, or var(--\u2026). For charts/brand add an eslint-disable with a reason.",
|
|
2275
2646
|
inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason."
|
|
2276
2647
|
}
|
|
2277
2648
|
},
|
|
2278
|
-
defaultOptions: [],
|
|
2279
|
-
create(context) {
|
|
2649
|
+
defaultOptions: [{}],
|
|
2650
|
+
create(context, [options]) {
|
|
2280
2651
|
if (STORIES_FILE_RE.test(context.filename)) return {};
|
|
2652
|
+
if (options?.requireSemanticTokens === true && !hasSemanticTokenSystem(context.filename)) {
|
|
2653
|
+
return {};
|
|
2654
|
+
}
|
|
2281
2655
|
const reportClasses = (value, node) => {
|
|
2282
2656
|
for (const token of classTokens(value)) {
|
|
2283
2657
|
const base = tailwindBase(token);
|
|
@@ -2373,7 +2747,8 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
2373
2747
|
import { ESLintUtils as ESLintUtils16 } from "@typescript-eslint/utils";
|
|
2374
2748
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
2375
2749
|
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\/)/;
|
|
2750
|
+
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\/)/;
|
|
2751
|
+
var NON_REACT_FRAMEWORK_RE = /^(?:@angular\/|@nestjs\/|vue$|vue\/|svelte$|svelte\/|solid-js$|solid-js\/|@ember\/|rxjs$|rxjs\/)/;
|
|
2377
2752
|
function getScope(context, node) {
|
|
2378
2753
|
return context.sourceCode.getScope(node);
|
|
2379
2754
|
}
|
|
@@ -2469,8 +2844,15 @@ var prefer_server_actions_default = ESLintUtils16.RuleCreator(
|
|
|
2469
2844
|
if (SKIP_FILE_REGEX.test(filename)) {
|
|
2470
2845
|
return {};
|
|
2471
2846
|
}
|
|
2847
|
+
let isNonReactFramework = false;
|
|
2472
2848
|
return {
|
|
2849
|
+
ImportDeclaration(node) {
|
|
2850
|
+
if (typeof node.source.value === "string" && NON_REACT_FRAMEWORK_RE.test(node.source.value)) {
|
|
2851
|
+
isNonReactFramework = true;
|
|
2852
|
+
}
|
|
2853
|
+
},
|
|
2473
2854
|
CallExpression(node) {
|
|
2855
|
+
if (isNonReactFramework) return;
|
|
2474
2856
|
let isMutation = false;
|
|
2475
2857
|
if (node.callee.type === "Identifier" && node.callee.name === "fetch") {
|
|
2476
2858
|
const urlArg = node.arguments[0];
|
|
@@ -2531,7 +2913,7 @@ var INPUT_TYPE_REPLACEMENTS = {
|
|
|
2531
2913
|
radio: "RadioGroup",
|
|
2532
2914
|
range: "Slider"
|
|
2533
2915
|
};
|
|
2534
|
-
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
|
|
2916
|
+
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["file", "hidden"]);
|
|
2535
2917
|
var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2536
2918
|
var literalTypeAttr = (node) => {
|
|
2537
2919
|
for (const attribute of node.attributes) {
|
|
@@ -2545,8 +2927,12 @@ var literalTypeAttr = (node) => {
|
|
|
2545
2927
|
}
|
|
2546
2928
|
return null;
|
|
2547
2929
|
};
|
|
2930
|
+
var hasFileAcceptAttr = (node) => node.attributes.some(
|
|
2931
|
+
(attribute) => attribute.type === AST_NODE_TYPES9.JSXAttribute && attribute.name.type === AST_NODE_TYPES9.JSXIdentifier && attribute.name.name === "accept"
|
|
2932
|
+
);
|
|
2548
2933
|
var resolveInputReplacement = (node) => {
|
|
2549
2934
|
const typeAttr = literalTypeAttr(node);
|
|
2935
|
+
if (hasFileAcceptAttr(node)) return null;
|
|
2550
2936
|
if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
|
|
2551
2937
|
if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
|
|
2552
2938
|
return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
|
|
@@ -2567,6 +2953,9 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
2567
2953
|
},
|
|
2568
2954
|
defaultOptions: [],
|
|
2569
2955
|
create(context) {
|
|
2956
|
+
if (isTestFile(context.filename) || isStoryFile(context.filename)) {
|
|
2957
|
+
return {};
|
|
2958
|
+
}
|
|
2570
2959
|
return {
|
|
2571
2960
|
JSXOpeningElement(node) {
|
|
2572
2961
|
if (node.name.type !== "JSXIdentifier") {
|
|
@@ -2743,6 +3132,9 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
2743
3132
|
},
|
|
2744
3133
|
defaultOptions: [],
|
|
2745
3134
|
create(context) {
|
|
3135
|
+
if (isTestFile(context.filename)) {
|
|
3136
|
+
return {};
|
|
3137
|
+
}
|
|
2746
3138
|
const isFormSourceIdentifier = (node) => {
|
|
2747
3139
|
if (node.type !== AST_NODE_TYPES11.Identifier) return false;
|
|
2748
3140
|
if (/formdata/i.test(node.name)) return true;
|
|
@@ -2816,6 +3208,25 @@ var CONVENTIONS = {
|
|
|
2816
3208
|
suffix: { test: ZOD_SUFFIX_RE, messageId: "schemaSuffix" },
|
|
2817
3209
|
either: { test: ZOD_SCHEMA_NAME_RE, messageId: "zodSchemaName" }
|
|
2818
3210
|
};
|
|
3211
|
+
var CONTAINS_SCHEMA_RE = /schema/i;
|
|
3212
|
+
var NON_SCHEMA_TERMINALS = /* @__PURE__ */ new Set([
|
|
3213
|
+
"parse",
|
|
3214
|
+
"parseAsync",
|
|
3215
|
+
"safeParse",
|
|
3216
|
+
"safeParseAsync",
|
|
3217
|
+
"encode",
|
|
3218
|
+
"decode",
|
|
3219
|
+
"encodeAsync",
|
|
3220
|
+
"decodeAsync",
|
|
3221
|
+
"safeEncode",
|
|
3222
|
+
"safeDecode",
|
|
3223
|
+
"safeEncodeAsync",
|
|
3224
|
+
"safeDecodeAsync",
|
|
3225
|
+
"toJSONSchema",
|
|
3226
|
+
"registry",
|
|
3227
|
+
"implement"
|
|
3228
|
+
]);
|
|
3229
|
+
var terminalMethodName = (callee) => !callee.computed && callee.property.type === AST_NODE_TYPES12.Identifier ? callee.property.name : null;
|
|
2819
3230
|
var calleeChainStartsWithZ = (node) => {
|
|
2820
3231
|
let current = node;
|
|
2821
3232
|
while (current.type === AST_NODE_TYPES12.MemberExpression) {
|
|
@@ -2860,7 +3271,12 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
2860
3271
|
},
|
|
2861
3272
|
defaultOptions: [{}],
|
|
2862
3273
|
create(context, [optionsArg]) {
|
|
2863
|
-
const
|
|
3274
|
+
const convention = optionsArg?.convention ?? "either";
|
|
3275
|
+
const { test, messageId } = CONVENTIONS[convention];
|
|
3276
|
+
const acceptsSchemaWord = convention !== "prefix";
|
|
3277
|
+
if (isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) {
|
|
3278
|
+
return {};
|
|
3279
|
+
}
|
|
2864
3280
|
return {
|
|
2865
3281
|
VariableDeclarator(node) {
|
|
2866
3282
|
const init = node.init;
|
|
@@ -2869,8 +3285,11 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
2869
3285
|
const callee = init.callee;
|
|
2870
3286
|
if (callee.type !== AST_NODE_TYPES12.MemberExpression) return;
|
|
2871
3287
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
3288
|
+
const terminal = terminalMethodName(callee);
|
|
3289
|
+
if (terminal !== null && NON_SCHEMA_TERMINALS.has(terminal)) return;
|
|
2872
3290
|
if (node.id.type !== AST_NODE_TYPES12.Identifier) return;
|
|
2873
3291
|
if (test.test(node.id.name)) return;
|
|
3292
|
+
if (acceptsSchemaWord && CONTAINS_SCHEMA_RE.test(node.id.name)) return;
|
|
2874
3293
|
context.report({
|
|
2875
3294
|
node: node.id,
|
|
2876
3295
|
messageId
|
|
@@ -3092,25 +3511,24 @@ var no_cors_wildcard_with_credentials_default = ESLintUtils21.RuleCreator(
|
|
|
3092
3511
|
|
|
3093
3512
|
// src/rules/no-silent-promise-catch.ts
|
|
3094
3513
|
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
3514
|
function isBodyParseCall(node) {
|
|
3112
3515
|
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
3516
|
}
|
|
3517
|
+
var TEARDOWN_METHODS = /* @__PURE__ */ new Set([
|
|
3518
|
+
"cancel",
|
|
3519
|
+
"close",
|
|
3520
|
+
"abort",
|
|
3521
|
+
"destroy",
|
|
3522
|
+
"dispose",
|
|
3523
|
+
"release",
|
|
3524
|
+
"unlock",
|
|
3525
|
+
"disconnect"
|
|
3526
|
+
]);
|
|
3527
|
+
var DIRECTIVE_COMMENT_RE = /^\s*(eslint-|@ts-|prettier-ignore|biome-ignore|c8 |v8 |istanbul )/;
|
|
3528
|
+
var isExplanatory = (comment) => !DIRECTIVE_COMMENT_RE.test(comment.value);
|
|
3529
|
+
function isTeardownCall(node) {
|
|
3530
|
+
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);
|
|
3531
|
+
}
|
|
3114
3532
|
function isSilentExpression(node) {
|
|
3115
3533
|
switch (node.type) {
|
|
3116
3534
|
case AST_NODE_TYPES13.Literal:
|
|
@@ -3164,6 +3582,22 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3164
3582
|
if (isTestFile(context.filename)) {
|
|
3165
3583
|
return {};
|
|
3166
3584
|
}
|
|
3585
|
+
const hasExplanatoryComment = (call, handler) => {
|
|
3586
|
+
const sourceCode = context.sourceCode;
|
|
3587
|
+
if (sourceCode.getCommentsInside(handler).some(isExplanatory)) {
|
|
3588
|
+
return true;
|
|
3589
|
+
}
|
|
3590
|
+
let statement = call;
|
|
3591
|
+
while (statement.parent !== void 0 && statement.parent !== null && !statement.type.endsWith("Statement") && statement.type !== AST_NODE_TYPES13.VariableDeclaration) {
|
|
3592
|
+
statement = statement.parent;
|
|
3593
|
+
}
|
|
3594
|
+
if (sourceCode.getCommentsBefore(statement).some(isExplanatory)) {
|
|
3595
|
+
return true;
|
|
3596
|
+
}
|
|
3597
|
+
return sourceCode.getCommentsAfter(statement).some(
|
|
3598
|
+
(c) => isExplanatory(c) && c.loc.start.line === statement.loc.end.line
|
|
3599
|
+
);
|
|
3600
|
+
};
|
|
3167
3601
|
return {
|
|
3168
3602
|
CallExpression(node) {
|
|
3169
3603
|
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 +3606,12 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3172
3606
|
if (isBodyParseCall(node.callee.object)) {
|
|
3173
3607
|
return;
|
|
3174
3608
|
}
|
|
3609
|
+
if (isTeardownCall(node.callee.object)) {
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
if (node.parent.type === AST_NODE_TYPES13.MemberExpression && node.parent.object === node) {
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3175
3615
|
if (node.arguments.length !== 1) {
|
|
3176
3616
|
return;
|
|
3177
3617
|
}
|
|
@@ -3179,6 +3619,9 @@ var no_silent_promise_catch_default = ESLintUtils22.RuleCreator(
|
|
|
3179
3619
|
if (handler === void 0 || handler.type !== AST_NODE_TYPES13.ArrowFunctionExpression && handler.type !== AST_NODE_TYPES13.FunctionExpression) {
|
|
3180
3620
|
return;
|
|
3181
3621
|
}
|
|
3622
|
+
if (hasExplanatoryComment(node, handler)) {
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3182
3625
|
if (isSilentHandler(handler)) {
|
|
3183
3626
|
context.report({ node, messageId: "silentCatch" });
|
|
3184
3627
|
}
|
|
@@ -3193,6 +3636,7 @@ import {
|
|
|
3193
3636
|
ASTUtils,
|
|
3194
3637
|
ESLintUtils as ESLintUtils23
|
|
3195
3638
|
} from "@typescript-eslint/utils";
|
|
3639
|
+
var CODEMOD_FIXTURE_RE = /[\\/]__testfixtures__[\\/]/;
|
|
3196
3640
|
var GLOBAL_OBJECTS = /* @__PURE__ */ new Set([
|
|
3197
3641
|
"globalThis",
|
|
3198
3642
|
"window",
|
|
@@ -3255,7 +3699,7 @@ var require_fetch_timeout_default = ESLintUtils23.RuleCreator(
|
|
|
3255
3699
|
},
|
|
3256
3700
|
defaultOptions: [{}],
|
|
3257
3701
|
create(context, [optionsArg]) {
|
|
3258
|
-
if (isTestFile(context.filename) || isScriptFile(context.filename)) {
|
|
3702
|
+
if (isTestFile(context.filename) || isScriptFile(context.filename) || CODEMOD_FIXTURE_RE.test(context.filename)) {
|
|
3259
3703
|
return {};
|
|
3260
3704
|
}
|
|
3261
3705
|
const allowIn = optionsArg?.allowIn ?? [];
|
|
@@ -3977,6 +4421,9 @@ var no_unsafe_cast_default = ESLintUtils27.RuleCreator(
|
|
|
3977
4421
|
},
|
|
3978
4422
|
defaultOptions: [],
|
|
3979
4423
|
create(context) {
|
|
4424
|
+
if (isTestFile(context.filename)) {
|
|
4425
|
+
return {};
|
|
4426
|
+
}
|
|
3980
4427
|
function checkAssertion(node) {
|
|
3981
4428
|
if (isConstAssertion(node.typeAnnotation)) {
|
|
3982
4429
|
return;
|
|
@@ -4477,6 +4924,7 @@ var single_public_export_default = ESLintUtils29.RuleCreator(
|
|
|
4477
4924
|
const base = basename(context.filename);
|
|
4478
4925
|
if (base.endsWith(".d.ts")) return {};
|
|
4479
4926
|
if (TEST_FILE_RE.test(base)) return {};
|
|
4927
|
+
if (isTestFile(context.filename)) return {};
|
|
4480
4928
|
const stem = stemOf(base);
|
|
4481
4929
|
if (!JUNK_DRAWER_STEMS.has(stem.toLowerCase())) return {};
|
|
4482
4930
|
return {
|
|
@@ -4677,6 +5125,7 @@ var no_offset_pagination_default = ESLintUtils30.RuleCreator(
|
|
|
4677
5125
|
// src/rules/no-positional-tuple-return.ts
|
|
4678
5126
|
import { AST_NODE_TYPES as AST_NODE_TYPES21, ESLintUtils as ESLintUtils31 } from "@typescript-eslint/utils";
|
|
4679
5127
|
var MIN_ELEMENTS = 2;
|
|
5128
|
+
var ACCESSOR_PAIR_LENGTH = 2;
|
|
4680
5129
|
var AWAITABLE_TYPES = /* @__PURE__ */ new Set(["Promise", "PromiseLike", "Awaited"]);
|
|
4681
5130
|
function tupleReturnType(node) {
|
|
4682
5131
|
if (node.type === AST_NODE_TYPES21.TSTupleType) {
|
|
@@ -4705,6 +5154,9 @@ function isPermittedTuple(tuple, sourceCode) {
|
|
|
4705
5154
|
if (elements[0]?.type === AST_NODE_TYPES21.TSLiteralType) {
|
|
4706
5155
|
return true;
|
|
4707
5156
|
}
|
|
5157
|
+
if (elements.length === ACCESSOR_PAIR_LENGTH && elements.some((element) => element.type === AST_NODE_TYPES21.TSFunctionType)) {
|
|
5158
|
+
return true;
|
|
5159
|
+
}
|
|
4708
5160
|
const texts = new Set(elements.map((element) => normalizedText(sourceCode, element)));
|
|
4709
5161
|
return texts.size === 1;
|
|
4710
5162
|
}
|
|
@@ -4904,6 +5356,9 @@ var no_repeated_string_literal_default = ESLintUtils32.RuleCreator(
|
|
|
4904
5356
|
}
|
|
4905
5357
|
},
|
|
4906
5358
|
TemplateLiteral(node) {
|
|
5359
|
+
if (node.parent.type === AST_NODE_TYPES22.TaggedTemplateExpression) {
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
4907
5362
|
const [only] = node.quasis;
|
|
4908
5363
|
if (node.expressions.length === 0 && only !== void 0) {
|
|
4909
5364
|
record(only.value.cooked ?? only.value.raw, node);
|
|
@@ -5127,7 +5582,10 @@ import { AST_NODE_TYPES as AST_NODE_TYPES24, ESLintUtils as ESLintUtils35 } from
|
|
|
5127
5582
|
var EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["===", "!==", "==", "!="]);
|
|
5128
5583
|
var SENTINEL_IDENTIFIERS = /* @__PURE__ */ new Set(["undefined", "NaN"]);
|
|
5129
5584
|
var SENTINEL_WORDS = /(^|_)(SENTINEL|EMPTY|NONE|NULL|UNSET|MISSING|PLACEHOLDER|DUMMY|FAKE|EXAMPLE)(_|$)/;
|
|
5585
|
+
var SENTINEL_PREFIX_RE = /^(skip|sentinel|empty|none|missing|unset|placeholder|dummy|fake|example|noop)[A-Z]/;
|
|
5586
|
+
var AST_NODE_TYPE_RE = /^(?:TS|JSX)?[A-Z][A-Za-z]*(?:Signature|Keyword|Expression|Declaration|Element|Literal|Identifier)$/;
|
|
5130
5587
|
function isConstantReference(identifier) {
|
|
5588
|
+
if (AST_NODE_TYPE_RE.test(identifier)) return true;
|
|
5131
5589
|
if (isAuthSecretName(identifier) && !SENTINEL_WORDS.test(identifier)) return false;
|
|
5132
5590
|
return identifier === identifier.toUpperCase() && /[A-Za-z]/.test(identifier);
|
|
5133
5591
|
}
|
|
@@ -5138,9 +5596,9 @@ function isExcludedOperand(node) {
|
|
|
5138
5596
|
case AST_NODE_TYPES24.TemplateLiteral:
|
|
5139
5597
|
return node.expressions.length === 0;
|
|
5140
5598
|
case AST_NODE_TYPES24.Identifier:
|
|
5141
|
-
return SENTINEL_IDENTIFIERS.has(node.name) || isConstantReference(node.name);
|
|
5599
|
+
return SENTINEL_IDENTIFIERS.has(node.name) || SENTINEL_PREFIX_RE.test(node.name) || isConstantReference(node.name);
|
|
5142
5600
|
case AST_NODE_TYPES24.MemberExpression:
|
|
5143
|
-
return !node.computed && node.property.type === AST_NODE_TYPES24.Identifier && isConstantReference(node.property.name);
|
|
5601
|
+
return !node.computed && node.property.type === AST_NODE_TYPES24.Identifier && (SENTINEL_PREFIX_RE.test(node.property.name) || isConstantReference(node.property.name));
|
|
5144
5602
|
default:
|
|
5145
5603
|
return false;
|
|
5146
5604
|
}
|
|
@@ -5290,6 +5748,23 @@ function runtimeConcatOperands(node) {
|
|
|
5290
5748
|
(operand) => operand.type !== AST_NODE_TYPES25.Literal && !isStaticFragment(operand)
|
|
5291
5749
|
);
|
|
5292
5750
|
}
|
|
5751
|
+
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;
|
|
5752
|
+
var RUNTIME_MARKER = " ? ";
|
|
5753
|
+
function staticStatementText(node) {
|
|
5754
|
+
if (node.type === AST_NODE_TYPES25.TemplateLiteral) {
|
|
5755
|
+
return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);
|
|
5756
|
+
}
|
|
5757
|
+
if (node.type === AST_NODE_TYPES25.Literal) {
|
|
5758
|
+
return typeof node.value === "string" ? node.value : RUNTIME_MARKER;
|
|
5759
|
+
}
|
|
5760
|
+
if (node.type === AST_NODE_TYPES25.BinaryExpression && node.operator === "+") {
|
|
5761
|
+
return staticStatementText(node.left) + staticStatementText(node.right);
|
|
5762
|
+
}
|
|
5763
|
+
return RUNTIME_MARKER;
|
|
5764
|
+
}
|
|
5765
|
+
function looksLikeSql(node) {
|
|
5766
|
+
return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));
|
|
5767
|
+
}
|
|
5293
5768
|
function statementMethodName(node, methods) {
|
|
5294
5769
|
const callee = node.callee;
|
|
5295
5770
|
if (callee.type !== AST_NODE_TYPES25.MemberExpression || callee.computed || callee.property.type !== AST_NODE_TYPES25.Identifier) {
|
|
@@ -5334,7 +5809,7 @@ var no_dynamic_sql_default = ESLintUtils37.RuleCreator(
|
|
|
5334
5809
|
return;
|
|
5335
5810
|
}
|
|
5336
5811
|
const statement = node.arguments[0];
|
|
5337
|
-
if (statement === void 0) {
|
|
5812
|
+
if (statement === void 0 || !looksLikeSql(statement)) {
|
|
5338
5813
|
return;
|
|
5339
5814
|
}
|
|
5340
5815
|
const offenders = statement.type === AST_NODE_TYPES25.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
@@ -5356,16 +5831,28 @@ var DEFAULT_ALLOW = [
|
|
|
5356
5831
|
"[\\\\/]clients?[\\\\/]",
|
|
5357
5832
|
"-client\\.[cm]?[jt]sx?$",
|
|
5358
5833
|
"[\\\\/]http-client\\.[cm]?[jt]sx?$",
|
|
5834
|
+
// The `api` spelling of the same client-layer convention: an `api/` directory,
|
|
5835
|
+
// a bare `api.ts`, or a `*-api.ts` / `*.api.ts` module.
|
|
5836
|
+
"[\\\\/]api[\\\\/]",
|
|
5837
|
+
"[\\\\/]api\\.[cm]?[jt]sx?$",
|
|
5838
|
+
"[-.]api\\.[cm]?[jt]sx?$",
|
|
5359
5839
|
"\\.test\\.",
|
|
5360
5840
|
"\\.spec\\.",
|
|
5841
|
+
// `*.test-d.ts` type tests, and react-router's `single-fetch-test.ts` spelling.
|
|
5842
|
+
"\\.(test|spec)-d\\.[cm]?[jt]sx?$",
|
|
5843
|
+
"-(test|spec)\\.[cm]?[jt]sx?$",
|
|
5361
5844
|
"[\\\\/]__tests__[\\\\/]",
|
|
5362
|
-
"[\\\\/]__mocks__[\\\\/]"
|
|
5845
|
+
"[\\\\/]__mocks__[\\\\/]",
|
|
5846
|
+
"[\\\\/]tests?[\\\\/]",
|
|
5847
|
+
// jscodeshift input/output fixtures — text a codemod transforms, not code.
|
|
5848
|
+
"[\\\\/]__testfixtures__[\\\\/]"
|
|
5363
5849
|
];
|
|
5364
5850
|
var GLOBAL_RECEIVERS = /* @__PURE__ */ new Set([
|
|
5365
5851
|
"globalThis",
|
|
5366
5852
|
"window",
|
|
5367
5853
|
"self"
|
|
5368
5854
|
]);
|
|
5855
|
+
var PRESIGNED_URL_NAME_RE = /(?:pre-?signed|signed|upload|download)Url$/i;
|
|
5369
5856
|
function isGlobalFetchCall(node) {
|
|
5370
5857
|
const callee = node.callee;
|
|
5371
5858
|
if (callee.type === "Identifier") {
|
|
@@ -5376,6 +5863,23 @@ function isGlobalFetchCall(node) {
|
|
|
5376
5863
|
}
|
|
5377
5864
|
return false;
|
|
5378
5865
|
}
|
|
5866
|
+
function identifierLikeName(node) {
|
|
5867
|
+
if (node.type === "Identifier") {
|
|
5868
|
+
return node.name;
|
|
5869
|
+
}
|
|
5870
|
+
if (node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier") {
|
|
5871
|
+
return node.property.name;
|
|
5872
|
+
}
|
|
5873
|
+
return null;
|
|
5874
|
+
}
|
|
5875
|
+
function isPresignedUrlTransfer(node) {
|
|
5876
|
+
const first = node.arguments[0];
|
|
5877
|
+
if (first === void 0) {
|
|
5878
|
+
return false;
|
|
5879
|
+
}
|
|
5880
|
+
const name = identifierLikeName(first);
|
|
5881
|
+
return name !== null && PRESIGNED_URL_NAME_RE.test(name);
|
|
5882
|
+
}
|
|
5379
5883
|
function compile(patterns) {
|
|
5380
5884
|
const compiled = [];
|
|
5381
5885
|
for (const pattern of patterns) {
|
|
@@ -5422,6 +5926,9 @@ var no_raw_fetch_outside_clients_default = ESLintUtils38.RuleCreator(
|
|
|
5422
5926
|
}
|
|
5423
5927
|
return {
|
|
5424
5928
|
CallExpression(node) {
|
|
5929
|
+
if (isPresignedUrlTransfer(node)) {
|
|
5930
|
+
return;
|
|
5931
|
+
}
|
|
5425
5932
|
if (isGlobalFetchCall(node)) {
|
|
5426
5933
|
context.report({ node, messageId: "rawFetch" });
|
|
5427
5934
|
}
|
|
@@ -5614,6 +6121,9 @@ var no_zod_native_enum_default = ESLintUtils40.RuleCreator(
|
|
|
5614
6121
|
if (isIgnoredFile2(context.filename, sourceCode.getText())) {
|
|
5615
6122
|
return {};
|
|
5616
6123
|
}
|
|
6124
|
+
if (isTestFile(context.filename)) {
|
|
6125
|
+
return {};
|
|
6126
|
+
}
|
|
5617
6127
|
let services;
|
|
5618
6128
|
try {
|
|
5619
6129
|
services = ESLintUtils40.getParserServices(context);
|
|
@@ -6050,7 +6560,7 @@ var rules = {
|
|
|
6050
6560
|
var plugin = {
|
|
6051
6561
|
meta: {
|
|
6052
6562
|
name: "@sarj/eslint-plugin",
|
|
6053
|
-
version: "2.
|
|
6563
|
+
version: "2.12.1"
|
|
6054
6564
|
},
|
|
6055
6565
|
rules,
|
|
6056
6566
|
configs: {
|
|
@@ -6075,7 +6585,7 @@ var plugin = {
|
|
|
6075
6585
|
"@sarj/prefer-discriminated-union": "warn",
|
|
6076
6586
|
"@sarj/no-comment-cruft": "warn",
|
|
6077
6587
|
// Frontend / styling — distilled from frontend PR-review mining.
|
|
6078
|
-
"@sarj/prefer-semantic-colors": "warn",
|
|
6588
|
+
"@sarj/prefer-semantic-colors": ["warn", { requireSemanticTokens: true }],
|
|
6079
6589
|
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
6080
6590
|
"@sarj/no-fat-try-blocks": "warn",
|
|
6081
6591
|
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
@@ -6134,7 +6644,7 @@ var plugin = {
|
|
|
6134
6644
|
"@sarj/no-comment-cruft": "error",
|
|
6135
6645
|
// Frontend / styling — distilled from frontend PR-review mining. Stylistic,
|
|
6136
6646
|
// no autofix → warn (rollout should prove the FP rate before raising it).
|
|
6137
|
-
"@sarj/prefer-semantic-colors": "error",
|
|
6647
|
+
"@sarj/prefer-semantic-colors": ["error", { requireSemanticTokens: true }],
|
|
6138
6648
|
// Ported from sarj-python-lint (SARJ), corpus-validated FP~0.
|
|
6139
6649
|
"@sarj/no-fat-try-blocks": "error",
|
|
6140
6650
|
"@sarj/no-cors-wildcard-with-credentials": "error",
|