@sarj/eslint-plugin 2.3.4 → 2.4.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 +941 -330
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +867 -250
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -37,43 +37,20 @@ 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
|
-
var
|
|
41
|
-
declarations: 0,
|
|
42
|
-
functions: 1,
|
|
43
|
-
exports: 2
|
|
44
|
-
};
|
|
45
|
-
var SECTION_NAMES = ["declarations", "functions", "exports"];
|
|
46
|
-
var sectionName = (ordinal) => {
|
|
47
|
-
const name = SECTION_NAMES[ordinal];
|
|
48
|
-
return name ?? "unknown";
|
|
49
|
-
};
|
|
50
|
-
var SERVER_ACTION_FILE_RE = /(?:^|\/)actions\/|\.action\.[jt]sx?$|(?:^|\/)actions\.[jt]sx?$/;
|
|
51
|
-
var isFunctionExpression = (node) => node.type === import_utils.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils.AST_NODE_TYPES.FunctionExpression;
|
|
52
|
-
var isFunctionLikeVariable = (statement) => statement.declarations.length > 0 && statement.declarations.every(
|
|
53
|
-
(decl) => decl.init !== null && isFunctionExpression(decl.init)
|
|
54
|
-
);
|
|
55
|
-
var getStatementSection = (statement) => {
|
|
40
|
+
var classifyStatement = (statement) => {
|
|
56
41
|
switch (statement.type) {
|
|
57
42
|
case import_utils.AST_NODE_TYPES.ImportDeclaration:
|
|
58
|
-
|
|
59
|
-
case import_utils.AST_NODE_TYPES.TSInterfaceDeclaration:
|
|
60
|
-
case import_utils.AST_NODE_TYPES.TSEnumDeclaration:
|
|
61
|
-
case import_utils.AST_NODE_TYPES.ClassDeclaration:
|
|
62
|
-
return SECTION.declarations;
|
|
63
|
-
case import_utils.AST_NODE_TYPES.VariableDeclaration:
|
|
64
|
-
return isFunctionLikeVariable(statement) ? SECTION.functions : SECTION.declarations;
|
|
65
|
-
case import_utils.AST_NODE_TYPES.FunctionDeclaration:
|
|
66
|
-
return SECTION.functions;
|
|
67
|
-
case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
|
|
68
|
-
case import_utils.AST_NODE_TYPES.ExportDefaultDeclaration:
|
|
43
|
+
return "import";
|
|
69
44
|
case import_utils.AST_NODE_TYPES.ExportAllDeclaration:
|
|
70
|
-
return
|
|
45
|
+
return "reexport";
|
|
46
|
+
case import_utils.AST_NODE_TYPES.ExportNamedDeclaration:
|
|
47
|
+
return statement.declaration === null ? "reexport" : "body";
|
|
71
48
|
default:
|
|
72
|
-
return
|
|
49
|
+
return "body";
|
|
73
50
|
}
|
|
74
51
|
};
|
|
52
|
+
var isStringDirective = (statement) => statement.type === import_utils.AST_NODE_TYPES.ExpressionStatement && statement.expression.type === import_utils.AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ");
|
|
75
53
|
var isUseServerDirective = (statement) => {
|
|
76
|
-
if (statement === void 0) return false;
|
|
77
54
|
if (statement.type !== import_utils.AST_NODE_TYPES.ExpressionStatement) return false;
|
|
78
55
|
const expr = statement.expression;
|
|
79
56
|
if (expr.type !== import_utils.AST_NODE_TYPES.Literal) return false;
|
|
@@ -86,47 +63,45 @@ var enforce_file_structure_default = import_utils.ESLintUtils.RuleCreator(
|
|
|
86
63
|
meta: {
|
|
87
64
|
type: "suggestion",
|
|
88
65
|
docs: {
|
|
89
|
-
description: "
|
|
66
|
+
description: "Require `import` statements to come first, then allow step-down ordering (public API first, private helpers below) for the rest of the file. Exported statements are classified by WHAT they export \u2014 an exported interface is a declaration, an exported function is a function \u2014 so a public exported function followed by a private helper, or an exported interface among declarations, is allowed. Re-exports (`export { \u2026 } from`, `export *`, `export { \u2026 }`) are a neutral group, so generated namespace barrels pass. When a module contains a `use server` directive, it must be the first statement in the file."
|
|
90
67
|
},
|
|
91
68
|
schema: [],
|
|
92
69
|
messages: {
|
|
93
|
-
|
|
94
|
-
useServerDirective: "
|
|
70
|
+
importsFirst: "File structure violation: import statements must come before other declarations",
|
|
71
|
+
useServerDirective: "A 'use server' directive must be the first statement in the file"
|
|
95
72
|
}
|
|
96
73
|
},
|
|
97
74
|
defaultOptions: [],
|
|
98
75
|
create(context) {
|
|
99
|
-
const filename = context.filename;
|
|
100
|
-
const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
|
|
101
76
|
return {
|
|
102
77
|
Program(node) {
|
|
103
78
|
const body = node.body;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
79
|
+
const misplacedUseServer = body.find(
|
|
80
|
+
(statement, index) => index > 0 && isUseServerDirective(statement)
|
|
81
|
+
);
|
|
82
|
+
if (misplacedUseServer !== void 0) {
|
|
83
|
+
context.report({
|
|
84
|
+
node: misplacedUseServer,
|
|
85
|
+
messageId: "useServerDirective"
|
|
86
|
+
});
|
|
112
87
|
}
|
|
113
|
-
let
|
|
88
|
+
let seenBody = false;
|
|
114
89
|
for (const statement of body) {
|
|
115
|
-
if (statement
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
90
|
+
if (isStringDirective(statement)) continue;
|
|
91
|
+
switch (classifyStatement(statement)) {
|
|
92
|
+
case "reexport":
|
|
93
|
+
continue;
|
|
94
|
+
case "body":
|
|
95
|
+
seenBody = true;
|
|
96
|
+
continue;
|
|
97
|
+
case "import":
|
|
98
|
+
if (seenBody) {
|
|
99
|
+
context.report({
|
|
100
|
+
node: statement,
|
|
101
|
+
messageId: "importsFirst"
|
|
102
|
+
});
|
|
126
103
|
}
|
|
127
|
-
|
|
128
|
-
} else if (statementSection > currentSection) {
|
|
129
|
-
currentSection = statementSection;
|
|
104
|
+
continue;
|
|
130
105
|
}
|
|
131
106
|
}
|
|
132
107
|
}
|
|
@@ -272,7 +247,7 @@ var no_client_side_data_fetching_default = import_utils2.ESLintUtils.RuleCreator
|
|
|
272
247
|
// src/rules/no-comment-cruft.ts
|
|
273
248
|
var import_utils3 = require("@typescript-eslint/utils");
|
|
274
249
|
var LEADING_PREAMBLE_MIN = 4;
|
|
275
|
-
var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
|
|
250
|
+
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;
|
|
276
251
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
277
252
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
278
253
|
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
@@ -280,8 +255,9 @@ var REGION_RE = /^#?(?:end)?region\b/i;
|
|
|
280
255
|
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\.)/;
|
|
281
256
|
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
282
257
|
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
258
|
+
var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|…|\.\.\./;
|
|
283
259
|
function stripCommentMarker(line) {
|
|
284
|
-
return line.replace(/^\s
|
|
260
|
+
return line.replace(/^\s*\/{1,2}/, "").replace(/^\s*\*+/, "").trim();
|
|
285
261
|
}
|
|
286
262
|
function isDirective(text) {
|
|
287
263
|
return DIRECTIVE_RE.test(text.trim());
|
|
@@ -297,6 +273,30 @@ function looksLikeCode(text) {
|
|
|
297
273
|
if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
|
|
298
274
|
return CALL_OR_ASSIGN_RE.test(t);
|
|
299
275
|
}
|
|
276
|
+
function hasPseudocode(text) {
|
|
277
|
+
return PSEUDOCODE_RE.test(text);
|
|
278
|
+
}
|
|
279
|
+
function isProse(text) {
|
|
280
|
+
const t = text.trim();
|
|
281
|
+
if (!t) return false;
|
|
282
|
+
if (t.endsWith(":")) return true;
|
|
283
|
+
if (/[.!?]$/.test(t) && /\s/.test(t) && /[a-z]/.test(t) && !looksLikeCode(t) && t.split(/\s+/).length >= 3) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
function hasCommentedOutCode(texts, precedingProse) {
|
|
289
|
+
for (let i = 0; i < texts.length; i++) {
|
|
290
|
+
const line = texts[i];
|
|
291
|
+
if (line === void 0 || !looksLikeCode(line) || hasPseudocode(line)) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const prev = i > 0 ? texts[i - 1] : void 0;
|
|
295
|
+
if (prev !== void 0 ? isProse(prev) : precedingProse) continue;
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
300
|
var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
301
301
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
302
302
|
)({
|
|
@@ -351,12 +351,19 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
351
351
|
Program() {
|
|
352
352
|
const comments = sourceCode.getAllComments();
|
|
353
353
|
const firstCodeLine = sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;
|
|
354
|
-
for (
|
|
354
|
+
for (let i = 0; i < comments.length; i++) {
|
|
355
|
+
const comment = comments[i];
|
|
356
|
+
if (comment === void 0) continue;
|
|
355
357
|
if (isJsDoc(comment) || !isStandalone(comment)) continue;
|
|
358
|
+
if (LICENSE_RE.test(comment.value)) continue;
|
|
356
359
|
const texts = comment.value.split("\n").map(stripCommentMarker).filter((l) => l.length > 0 && !isDirective(l));
|
|
357
360
|
if (texts.some(isBanner)) {
|
|
358
361
|
context.report({ node: comment, messageId: "sectionBanner" });
|
|
359
|
-
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const prev = comments[i - 1];
|
|
365
|
+
const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
|
|
366
|
+
if (hasCommentedOutCode(texts, precedingProse)) {
|
|
360
367
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
361
368
|
}
|
|
362
369
|
}
|
|
@@ -438,7 +445,9 @@ var no_enum_default = import_utils4.ESLintUtils.RuleCreator(
|
|
|
438
445
|
|
|
439
446
|
// src/rules/no-insecure-random-id.ts
|
|
440
447
|
var import_utils5 = require("@typescript-eslint/utils");
|
|
441
|
-
var
|
|
448
|
+
var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
|
|
449
|
+
var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
|
|
450
|
+
var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
|
|
442
451
|
function isMathRandomCall(node) {
|
|
443
452
|
if (node.type !== "CallExpression") {
|
|
444
453
|
return false;
|
|
@@ -450,6 +459,24 @@ function isMathRandomCall(node) {
|
|
|
450
459
|
const { object, property } = callee;
|
|
451
460
|
return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
|
|
452
461
|
}
|
|
462
|
+
function climbValueChain(node) {
|
|
463
|
+
let current = node;
|
|
464
|
+
let parent = current.parent;
|
|
465
|
+
while (parent) {
|
|
466
|
+
if (parent.type === "MemberExpression" && parent.object === current && !parent.computed) {
|
|
467
|
+
current = parent;
|
|
468
|
+
parent = current.parent;
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
472
|
+
current = parent;
|
|
473
|
+
parent = current.parent;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
return current;
|
|
479
|
+
}
|
|
453
480
|
function isPartOfToString36Chain(node) {
|
|
454
481
|
let current = node;
|
|
455
482
|
let parent = current.parent;
|
|
@@ -515,6 +542,49 @@ function findEnclosingName(node) {
|
|
|
515
542
|
}
|
|
516
543
|
return void 0;
|
|
517
544
|
}
|
|
545
|
+
function collectStaticStringParts(node, out) {
|
|
546
|
+
if (node.type === "Literal" && typeof node.value === "string") {
|
|
547
|
+
out.push(node.value);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (node.type === "TemplateLiteral") {
|
|
551
|
+
for (const quasi of node.quasis) {
|
|
552
|
+
out.push(quasi.value.cooked ?? quasi.value.raw);
|
|
553
|
+
}
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
557
|
+
collectStaticStringParts(node.left, out);
|
|
558
|
+
collectStaticStringParts(node.right, out);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function isConcatenatedIntoPathOrDomId(node) {
|
|
562
|
+
const valueNode = climbValueChain(node);
|
|
563
|
+
let current = valueNode;
|
|
564
|
+
let parent = current.parent;
|
|
565
|
+
let top;
|
|
566
|
+
while (parent) {
|
|
567
|
+
if (parent.type === "BinaryExpression" && parent.operator === "+" && (parent.left === current || parent.right === current)) {
|
|
568
|
+
top = parent;
|
|
569
|
+
current = parent;
|
|
570
|
+
parent = current.parent;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
if (parent.type === "TemplateLiteral") {
|
|
574
|
+
top = parent;
|
|
575
|
+
current = parent;
|
|
576
|
+
parent = current.parent;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
if (!top) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
const parts = [];
|
|
585
|
+
collectStaticStringParts(top, parts);
|
|
586
|
+
return parts.some((part) => PATH_OR_DOM_MARKER.test(part));
|
|
587
|
+
}
|
|
518
588
|
var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
|
|
519
589
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
520
590
|
)({
|
|
@@ -536,12 +606,18 @@ var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
536
606
|
if (!isMathRandomCall(node)) {
|
|
537
607
|
return;
|
|
538
608
|
}
|
|
539
|
-
|
|
609
|
+
const name = findEnclosingName(node);
|
|
610
|
+
if (name !== void 0 && STRONG_SECURITY_PATTERN.test(name)) {
|
|
540
611
|
context.report({ node, messageId: "insecureRandomId" });
|
|
541
612
|
return;
|
|
542
613
|
}
|
|
543
|
-
|
|
544
|
-
|
|
614
|
+
if (name !== void 0 && NON_SECURITY_ID_PATTERN.test(name)) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (isConcatenatedIntoPathOrDomId(node)) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (isPartOfToString36Chain(node)) {
|
|
545
621
|
context.report({ node, messageId: "insecureRandomId" });
|
|
546
622
|
}
|
|
547
623
|
}
|
|
@@ -552,6 +628,8 @@ var no_insecure_random_id_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
552
628
|
// src/rules/no-json-stringify-error.ts
|
|
553
629
|
var import_utils6 = require("@typescript-eslint/utils");
|
|
554
630
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
631
|
+
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
632
|
+
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
555
633
|
function isCatchBinding(scope, name) {
|
|
556
634
|
let current = scope;
|
|
557
635
|
while (current) {
|
|
@@ -567,6 +645,98 @@ function isCatchBinding(scope, name) {
|
|
|
567
645
|
}
|
|
568
646
|
return false;
|
|
569
647
|
}
|
|
648
|
+
function memberSuggestsError(member, scope) {
|
|
649
|
+
const propName2 = !member.computed && member.property.type === "Identifier" ? member.property.name : null;
|
|
650
|
+
if (propName2 !== null && ERROR_PROP_PATTERN.test(propName2)) {
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
const base = member.object;
|
|
654
|
+
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
655
|
+
if (baseSuggestsError) {
|
|
656
|
+
return propName2 === null || !SAFE_STRING_PROPS.has(propName2.toLowerCase());
|
|
657
|
+
}
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
function instanceofErrorSubject(test) {
|
|
661
|
+
if (test.type === "BinaryExpression" && test.operator === "instanceof" && test.right.type === "Identifier" && test.right.name === "Error") {
|
|
662
|
+
return test.left;
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
var TYPE_GUARD_PATTERN = /^(is|has)[A-Z]/;
|
|
667
|
+
function typeGuardSubject(test) {
|
|
668
|
+
const arg = test.type === "CallExpression" ? test.arguments[0] : void 0;
|
|
669
|
+
if (test.type === "CallExpression" && test.callee.type === "Identifier" && TYPE_GUARD_PATTERN.test(test.callee.name) && test.arguments.length === 1 && arg !== void 0 && arg.type !== "SpreadElement") {
|
|
670
|
+
return arg;
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
function positiveErrorSubject(test) {
|
|
675
|
+
return instanceofErrorSubject(test) ?? typeGuardSubject(test);
|
|
676
|
+
}
|
|
677
|
+
function negatedInstanceofErrorSubject(test) {
|
|
678
|
+
if (test.type === "UnaryExpression" && test.operator === "!") {
|
|
679
|
+
return positiveErrorSubject(test.argument);
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
function branchTerminates(branch) {
|
|
684
|
+
const body = branch.type === "BlockStatement" ? branch.body : [branch];
|
|
685
|
+
const last = body[body.length - 1];
|
|
686
|
+
return last !== void 0 && (last.type === "ReturnStatement" || last.type === "ThrowStatement");
|
|
687
|
+
}
|
|
688
|
+
function isNarrowedByEarlyReturn(node, argExpr, sourceCode) {
|
|
689
|
+
const argText = sourceCode.getText(argExpr);
|
|
690
|
+
let current = node.parent;
|
|
691
|
+
while (current) {
|
|
692
|
+
if (current.type === "BlockStatement" || current.type === "Program") {
|
|
693
|
+
for (const stmt of current.body) {
|
|
694
|
+
if (stmt.range[0] >= node.range[0]) {
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
if (stmt.type === "IfStatement" && stmt.alternate === null && branchTerminates(stmt.consequent)) {
|
|
698
|
+
const subject = positiveErrorSubject(stmt.test);
|
|
699
|
+
if (subject && sourceCode.getText(subject) === argText) {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
current = current.parent;
|
|
706
|
+
}
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
function nodeWithin(node, container) {
|
|
710
|
+
return container !== null && node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
|
|
711
|
+
}
|
|
712
|
+
function isGuardedByInstanceofError(node, argExpr, sourceCode) {
|
|
713
|
+
const argText = sourceCode.getText(argExpr);
|
|
714
|
+
const sameSubject = (subject) => sourceCode.getText(subject) === argText;
|
|
715
|
+
let current = node.parent;
|
|
716
|
+
while (current) {
|
|
717
|
+
if (current.type === "ConditionalExpression") {
|
|
718
|
+
const subject = positiveErrorSubject(current.test);
|
|
719
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
723
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
} else if (current.type === "IfStatement") {
|
|
727
|
+
const subject = positiveErrorSubject(current.test);
|
|
728
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
732
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
current = current.parent;
|
|
737
|
+
}
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
570
740
|
function isJsonStringify(callee) {
|
|
571
741
|
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
572
742
|
}
|
|
@@ -592,72 +762,124 @@ var no_json_stringify_error_default = import_utils6.ESLintUtils.RuleCreator(
|
|
|
592
762
|
return;
|
|
593
763
|
}
|
|
594
764
|
const firstArg = node.arguments[0];
|
|
595
|
-
if (!firstArg
|
|
765
|
+
if (!firstArg) {
|
|
596
766
|
return;
|
|
597
767
|
}
|
|
598
|
-
const name = firstArg.name;
|
|
599
768
|
const scope = context.sourceCode.getScope(firstArg);
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
769
|
+
let suggestsError;
|
|
770
|
+
if (firstArg.type === "Identifier") {
|
|
771
|
+
suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
|
|
772
|
+
} else if (firstArg.type === "MemberExpression") {
|
|
773
|
+
suggestsError = memberSuggestsError(firstArg, scope);
|
|
774
|
+
} else {
|
|
775
|
+
return;
|
|
605
776
|
}
|
|
777
|
+
if (!suggestsError) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
if (isGuardedByInstanceofError(node, firstArg, context.sourceCode) || isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
context.report({
|
|
784
|
+
node,
|
|
785
|
+
messageId: "noJsonStringifyError"
|
|
786
|
+
});
|
|
606
787
|
}
|
|
607
788
|
};
|
|
608
789
|
}
|
|
609
790
|
});
|
|
610
791
|
|
|
611
792
|
// src/rules/no-log-only-catch.ts
|
|
793
|
+
var import_utils8 = require("@typescript-eslint/utils");
|
|
794
|
+
|
|
795
|
+
// src/rules/_logging.ts
|
|
612
796
|
var import_utils7 = require("@typescript-eslint/utils");
|
|
613
|
-
var
|
|
614
|
-
|
|
615
|
-
/\.spec\./,
|
|
616
|
-
/[\\/]__tests__[\\/]/
|
|
617
|
-
];
|
|
618
|
-
var CONSOLE_METHODS = /* @__PURE__ */ new Set([
|
|
619
|
-
"log",
|
|
620
|
-
"error",
|
|
621
|
-
"warn",
|
|
797
|
+
var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
798
|
+
"debug",
|
|
622
799
|
"info",
|
|
623
|
-
"
|
|
800
|
+
"warn",
|
|
801
|
+
"warning",
|
|
802
|
+
"error",
|
|
803
|
+
"exception",
|
|
804
|
+
"critical",
|
|
805
|
+
"trace",
|
|
806
|
+
"log",
|
|
807
|
+
"fatal",
|
|
808
|
+
"success"
|
|
624
809
|
]);
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
810
|
+
var LOGGER_NAMES = /* @__PURE__ */ new Set([
|
|
811
|
+
"logger",
|
|
812
|
+
"log",
|
|
813
|
+
"logging",
|
|
814
|
+
"loguru",
|
|
815
|
+
"console",
|
|
816
|
+
"_logger",
|
|
817
|
+
"_log"
|
|
818
|
+
]);
|
|
819
|
+
var REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;
|
|
820
|
+
function isLoggerReceiver(expr) {
|
|
821
|
+
switch (expr.type) {
|
|
822
|
+
case "Identifier":
|
|
823
|
+
return LOGGER_NAMES.has(expr.name.toLowerCase());
|
|
824
|
+
case "MemberExpression": {
|
|
825
|
+
const { property, object } = expr;
|
|
826
|
+
if (!expr.computed && property.type === "Identifier" && LOGGER_NAMES.has(property.name.toLowerCase())) {
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
return isLoggerReceiver(object);
|
|
830
|
+
}
|
|
831
|
+
default:
|
|
832
|
+
return false;
|
|
628
833
|
}
|
|
629
|
-
|
|
834
|
+
}
|
|
835
|
+
function isLoggingCall(expr) {
|
|
630
836
|
if (expr.type !== "CallExpression") {
|
|
631
837
|
return false;
|
|
632
838
|
}
|
|
633
839
|
const callee = expr.callee;
|
|
634
|
-
if (callee.type !== "MemberExpression") {
|
|
840
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier") {
|
|
635
841
|
return false;
|
|
636
842
|
}
|
|
637
|
-
|
|
638
|
-
if (object.type !== "Identifier" || object.name !== "console") {
|
|
843
|
+
if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {
|
|
639
844
|
return false;
|
|
640
845
|
}
|
|
641
|
-
|
|
642
|
-
|
|
846
|
+
return isLoggerReceiver(callee.object);
|
|
847
|
+
}
|
|
848
|
+
function calleeName(callee) {
|
|
849
|
+
if (callee.type === "Identifier") {
|
|
850
|
+
return callee.name;
|
|
851
|
+
}
|
|
852
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
853
|
+
return callee.property.name;
|
|
643
854
|
}
|
|
644
|
-
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/rules/no-log-only-catch.ts
|
|
859
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
860
|
+
/\.test\./,
|
|
861
|
+
/\.spec\./,
|
|
862
|
+
/[\\/]__tests__[\\/]/
|
|
863
|
+
];
|
|
864
|
+
function isLoggingCallStatement(statement) {
|
|
865
|
+
if (statement.type !== "ExpressionStatement") {
|
|
645
866
|
return false;
|
|
646
867
|
}
|
|
647
|
-
return
|
|
868
|
+
return isLoggingCall(statement.expression);
|
|
648
869
|
}
|
|
649
|
-
var no_log_only_catch_default =
|
|
870
|
+
var no_log_only_catch_default = import_utils8.ESLintUtils.RuleCreator(
|
|
650
871
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
651
872
|
)({
|
|
652
873
|
name: "no-log-only-catch",
|
|
653
874
|
meta: {
|
|
654
875
|
type: "problem",
|
|
655
876
|
docs: {
|
|
656
|
-
description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
|
|
877
|
+
description: "Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead."
|
|
657
878
|
},
|
|
658
879
|
schema: [],
|
|
659
880
|
messages: {
|
|
660
|
-
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
|
|
881
|
+
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real.",
|
|
882
|
+
emptyCatch: "Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore."
|
|
661
883
|
}
|
|
662
884
|
},
|
|
663
885
|
defaultOptions: [],
|
|
@@ -673,13 +895,16 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
|
|
|
673
895
|
CatchClause(node) {
|
|
674
896
|
const statements = node.body.body;
|
|
675
897
|
if (statements.length === 0) {
|
|
676
|
-
context.
|
|
898
|
+
if (context.sourceCode.getCommentsInside(node.body).length > 0) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
context.report({ node, messageId: "emptyCatch" });
|
|
677
902
|
return;
|
|
678
903
|
}
|
|
679
|
-
const
|
|
680
|
-
(statement) =>
|
|
904
|
+
const everyStatementIsLogging = statements.every(
|
|
905
|
+
(statement) => isLoggingCallStatement(statement)
|
|
681
906
|
);
|
|
682
|
-
if (
|
|
907
|
+
if (everyStatementIsLogging) {
|
|
683
908
|
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
684
909
|
}
|
|
685
910
|
}
|
|
@@ -688,8 +913,25 @@ var no_log_only_catch_default = import_utils7.ESLintUtils.RuleCreator(
|
|
|
688
913
|
});
|
|
689
914
|
|
|
690
915
|
// src/rules/no-raw-env.ts
|
|
691
|
-
var
|
|
692
|
-
|
|
916
|
+
var import_utils9 = require("@typescript-eslint/utils");
|
|
917
|
+
function isProcessEnv(node) {
|
|
918
|
+
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
919
|
+
}
|
|
920
|
+
function isImportMetaEnv(node) {
|
|
921
|
+
return !node.computed && node.property.type === "Identifier" && node.property.name === "env" && node.object.type === "MetaProperty" && node.object.meta.name === "import" && node.object.property.name === "meta";
|
|
922
|
+
}
|
|
923
|
+
var BUILD_TIME_CONSTANTS = /* @__PURE__ */ new Set([
|
|
924
|
+
"NODE_ENV",
|
|
925
|
+
"MODE",
|
|
926
|
+
"DEV",
|
|
927
|
+
"PROD",
|
|
928
|
+
"SSR"
|
|
929
|
+
]);
|
|
930
|
+
function isBuildTimeConstantAccess(node) {
|
|
931
|
+
const parent = node.parent;
|
|
932
|
+
return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
|
|
933
|
+
}
|
|
934
|
+
var no_raw_env_default = import_utils9.ESLintUtils.RuleCreator(
|
|
693
935
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
694
936
|
)({
|
|
695
937
|
name: "no-raw-env",
|
|
@@ -707,10 +949,7 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
|
|
|
707
949
|
create(context) {
|
|
708
950
|
return {
|
|
709
951
|
MemberExpression(node) {
|
|
710
|
-
if (node
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
if (node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env") {
|
|
952
|
+
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
|
|
714
953
|
context.report({
|
|
715
954
|
node,
|
|
716
955
|
messageId: "noRawEnv"
|
|
@@ -722,39 +961,72 @@ var no_raw_env_default = import_utils8.ESLintUtils.RuleCreator(
|
|
|
722
961
|
});
|
|
723
962
|
|
|
724
963
|
// src/rules/no-sentinel-return-on-catch.ts
|
|
725
|
-
var
|
|
964
|
+
var import_utils10 = require("@typescript-eslint/utils");
|
|
965
|
+
function sentinelKind(arg) {
|
|
966
|
+
if (arg === null) {
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
969
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.Literal) {
|
|
970
|
+
if (arg.value === null) {
|
|
971
|
+
return "nullish";
|
|
972
|
+
}
|
|
973
|
+
if (typeof arg.value === "boolean") {
|
|
974
|
+
return "boolean";
|
|
975
|
+
}
|
|
976
|
+
if (typeof arg.value === "string") {
|
|
977
|
+
return "string";
|
|
978
|
+
}
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
|
|
982
|
+
return "nullish";
|
|
983
|
+
}
|
|
984
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression) {
|
|
985
|
+
return "array";
|
|
986
|
+
}
|
|
987
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression) {
|
|
988
|
+
return "object";
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
726
992
|
function isSentinelArgument(arg) {
|
|
727
993
|
if (arg === null) {
|
|
728
994
|
return false;
|
|
729
995
|
}
|
|
730
|
-
if (arg.type ===
|
|
996
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === null) {
|
|
731
997
|
return true;
|
|
732
998
|
}
|
|
733
|
-
if (arg.type ===
|
|
999
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.Literal && arg.value === false) {
|
|
734
1000
|
return true;
|
|
735
1001
|
}
|
|
736
|
-
if (arg.type ===
|
|
1002
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
|
|
737
1003
|
return true;
|
|
738
1004
|
}
|
|
739
|
-
if (arg.type ===
|
|
1005
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
|
|
740
1006
|
return true;
|
|
741
1007
|
}
|
|
742
|
-
if (arg.type ===
|
|
1008
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
|
|
743
1009
|
return true;
|
|
744
1010
|
}
|
|
745
1011
|
return false;
|
|
746
1012
|
}
|
|
747
|
-
function
|
|
1013
|
+
function isFunctionNode(node) {
|
|
1014
|
+
return node.type === import_utils10.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils10.AST_NODE_TYPES.FunctionExpression || node.type === import_utils10.AST_NODE_TYPES.ArrowFunctionExpression;
|
|
1015
|
+
}
|
|
1016
|
+
function isNode(value) {
|
|
1017
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1018
|
+
}
|
|
1019
|
+
function walkWithinScope(node, visit) {
|
|
748
1020
|
let found = false;
|
|
749
|
-
const
|
|
1021
|
+
const recurse = (current) => {
|
|
750
1022
|
if (found) {
|
|
751
1023
|
return;
|
|
752
1024
|
}
|
|
753
|
-
if (current
|
|
1025
|
+
if (visit(current)) {
|
|
754
1026
|
found = true;
|
|
755
1027
|
return;
|
|
756
1028
|
}
|
|
757
|
-
if (current
|
|
1029
|
+
if (isFunctionNode(current)) {
|
|
758
1030
|
return;
|
|
759
1031
|
}
|
|
760
1032
|
for (const key of Object.keys(current)) {
|
|
@@ -765,32 +1037,111 @@ function containsThrow(node) {
|
|
|
765
1037
|
if (Array.isArray(value)) {
|
|
766
1038
|
for (const child of value) {
|
|
767
1039
|
if (isNode(child)) {
|
|
768
|
-
|
|
1040
|
+
recurse(child);
|
|
769
1041
|
}
|
|
770
1042
|
}
|
|
771
1043
|
} else if (isNode(value)) {
|
|
772
|
-
|
|
1044
|
+
recurse(value);
|
|
773
1045
|
}
|
|
774
1046
|
}
|
|
775
1047
|
};
|
|
776
|
-
|
|
1048
|
+
recurse(node);
|
|
777
1049
|
return found;
|
|
778
1050
|
}
|
|
779
|
-
function
|
|
780
|
-
return
|
|
1051
|
+
function containsThrow(node) {
|
|
1052
|
+
return walkWithinScope(
|
|
1053
|
+
node,
|
|
1054
|
+
(current) => current.type === import_utils10.AST_NODE_TYPES.ThrowStatement
|
|
1055
|
+
);
|
|
781
1056
|
}
|
|
782
|
-
|
|
1057
|
+
function argsIncludeBinding(args, caughtName) {
|
|
1058
|
+
if (caughtName === null) {
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
return args.some(
|
|
1062
|
+
(arg) => arg.type === import_utils10.AST_NODE_TYPES.Identifier && arg.name === caughtName
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
function logsOrReportsError(catchBody, caughtName) {
|
|
1066
|
+
return walkWithinScope(catchBody, (current) => {
|
|
1067
|
+
if (current.type !== import_utils10.AST_NODE_TYPES.CallExpression) {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
if (isLoggingCall(current)) {
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
const name = calleeName(current.callee);
|
|
1074
|
+
return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
function tryBlockOf(catchNode) {
|
|
1078
|
+
return catchNode.parent.block;
|
|
1079
|
+
}
|
|
1080
|
+
function isSafeParseExpression(arg) {
|
|
1081
|
+
if (arg === null) {
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.CallExpression && arg.callee.type === import_utils10.AST_NODE_TYPES.MemberExpression && !arg.callee.computed && arg.callee.property.type === import_utils10.AST_NODE_TYPES.Identifier && arg.callee.property.name === "parse") {
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
if (arg.type === import_utils10.AST_NODE_TYPES.NewExpression && arg.callee.type === import_utils10.AST_NODE_TYPES.Identifier) {
|
|
1088
|
+
return arg.callee.name === "RegExp" || arg.callee.name === "URL";
|
|
1089
|
+
}
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
function tryReturnsSafeParse(catchNode) {
|
|
1093
|
+
return walkWithinScope(
|
|
1094
|
+
tryBlockOf(catchNode),
|
|
1095
|
+
(current) => current.type === import_utils10.AST_NODE_TYPES.ReturnStatement && isSafeParseExpression(current.argument)
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
function enclosingFunctionBody(node) {
|
|
1099
|
+
let current = node.parent;
|
|
1100
|
+
while (current !== void 0 && current !== null) {
|
|
1101
|
+
if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === import_utils10.AST_NODE_TYPES.BlockStatement) {
|
|
1102
|
+
return current.body;
|
|
1103
|
+
}
|
|
1104
|
+
current = current.parent;
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
1109
|
+
const functionBody = enclosingFunctionBody(catchNode);
|
|
1110
|
+
if (functionBody === null) {
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
return walkWithinScope(functionBody, (current) => {
|
|
1114
|
+
if (current.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
if (isWithin(current, catchNode.body)) {
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1120
|
+
return sentinelKind(current.argument) === kind;
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function isWithin(node, ancestor) {
|
|
1124
|
+
let current = node;
|
|
1125
|
+
while (current !== void 0 && current !== null) {
|
|
1126
|
+
if (current === ancestor) {
|
|
1127
|
+
return true;
|
|
1128
|
+
}
|
|
1129
|
+
current = current.parent;
|
|
1130
|
+
}
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
var no_sentinel_return_on_catch_default = import_utils10.ESLintUtils.RuleCreator(
|
|
783
1134
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
784
1135
|
)({
|
|
785
1136
|
name: "no-sentinel-return-on-catch",
|
|
786
1137
|
meta: {
|
|
787
1138
|
type: "problem",
|
|
788
1139
|
docs: {
|
|
789
|
-
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
|
|
1140
|
+
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block, unless the error is logged/reported or the sentinel is the declared safe-parse/predicate contract."
|
|
790
1141
|
},
|
|
791
1142
|
schema: [],
|
|
792
1143
|
messages: {
|
|
793
|
-
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it,
|
|
1144
|
+
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel without logging it. Rethrow it, log/report it, or return a typed Result."
|
|
794
1145
|
}
|
|
795
1146
|
},
|
|
796
1147
|
defaultOptions: [],
|
|
@@ -802,7 +1153,7 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
|
|
|
802
1153
|
return;
|
|
803
1154
|
}
|
|
804
1155
|
const last = body[body.length - 1];
|
|
805
|
-
if (last === void 0 || last.type !==
|
|
1156
|
+
if (last === void 0 || last.type !== import_utils10.AST_NODE_TYPES.ReturnStatement) {
|
|
806
1157
|
return;
|
|
807
1158
|
}
|
|
808
1159
|
if (!isSentinelArgument(last.argument)) {
|
|
@@ -811,6 +1162,17 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
|
|
|
811
1162
|
if (containsThrow(node.body)) {
|
|
812
1163
|
return;
|
|
813
1164
|
}
|
|
1165
|
+
const caughtName = node.param?.type === import_utils10.AST_NODE_TYPES.Identifier ? node.param.name : null;
|
|
1166
|
+
if (logsOrReportsError(node.body, caughtName)) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
if (tryReturnsSafeParse(node)) {
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
const kind = sentinelKind(last.argument);
|
|
1173
|
+
if (kind !== null && functionReturnsSameSentinelKindElsewhere(node, kind)) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
814
1176
|
context.report({
|
|
815
1177
|
node: last,
|
|
816
1178
|
messageId: "noSentinelReturn"
|
|
@@ -821,14 +1183,112 @@ var no_sentinel_return_on_catch_default = import_utils9.ESLintUtils.RuleCreator(
|
|
|
821
1183
|
});
|
|
822
1184
|
|
|
823
1185
|
// src/rules/no-sequential-await.ts
|
|
824
|
-
var
|
|
1186
|
+
var import_utils11 = require("@typescript-eslint/utils");
|
|
1187
|
+
var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
|
|
1188
|
+
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
|
|
825
1189
|
function isFunctionLike(node) {
|
|
826
1190
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
827
1191
|
}
|
|
828
1192
|
function isLoop(node) {
|
|
829
1193
|
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
830
1194
|
}
|
|
831
|
-
|
|
1195
|
+
function isNode2(value) {
|
|
1196
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1197
|
+
}
|
|
1198
|
+
function visitScope(root, visit) {
|
|
1199
|
+
visit(root);
|
|
1200
|
+
for (const key of Object.keys(root)) {
|
|
1201
|
+
if (key === "parent") {
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
const value = root[key];
|
|
1205
|
+
const children = Array.isArray(value) ? value : [value];
|
|
1206
|
+
for (const child of children) {
|
|
1207
|
+
if (isNode2(child) && !isFunctionLike(child) && !isLoop(child)) {
|
|
1208
|
+
visitScope(child, visit);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function collectAwaits(root) {
|
|
1214
|
+
const awaits = [];
|
|
1215
|
+
visitScope(root, (node) => {
|
|
1216
|
+
if (node.type === "AwaitExpression") {
|
|
1217
|
+
awaits.push(node);
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
return awaits;
|
|
1221
|
+
}
|
|
1222
|
+
function hasEarlyExit(root) {
|
|
1223
|
+
let found = false;
|
|
1224
|
+
visitScope(root, (node) => {
|
|
1225
|
+
if (node.type === "ReturnStatement" || node.type === "BreakStatement" || node.type === "ContinueStatement") {
|
|
1226
|
+
found = true;
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
return found;
|
|
1230
|
+
}
|
|
1231
|
+
var TIMER_HELPER_RE = /^(sleep|timeout|delay|wait|pause|tick)$/i;
|
|
1232
|
+
function calleeName2(callee) {
|
|
1233
|
+
if (callee.type === "Identifier") return callee.name;
|
|
1234
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
1235
|
+
return callee.property.name;
|
|
1236
|
+
}
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
function isTimerYield(node) {
|
|
1240
|
+
const arg = node.argument;
|
|
1241
|
+
if (arg.type === "NewExpression" && arg.callee.type === "Identifier" && arg.callee.name === "Promise") {
|
|
1242
|
+
return true;
|
|
1243
|
+
}
|
|
1244
|
+
if (arg.type === "CallExpression") {
|
|
1245
|
+
const name = calleeName2(arg.callee);
|
|
1246
|
+
return name !== null && TIMER_HELPER_RE.test(name);
|
|
1247
|
+
}
|
|
1248
|
+
return false;
|
|
1249
|
+
}
|
|
1250
|
+
var QUEUE_DRAIN_METHODS = /^(shift|pop|dequeue|next|poll)$/;
|
|
1251
|
+
function isQueueDrain(node) {
|
|
1252
|
+
const arg = node.argument;
|
|
1253
|
+
return arg.type === "CallExpression" && arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.property.type === "Identifier" && QUEUE_DRAIN_METHODS.test(arg.callee.property.name);
|
|
1254
|
+
}
|
|
1255
|
+
function referencesName(root, name) {
|
|
1256
|
+
let found = false;
|
|
1257
|
+
visitScope(root, (node) => {
|
|
1258
|
+
if (node.type === "Identifier" && node.name === name) {
|
|
1259
|
+
found = true;
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
return found;
|
|
1263
|
+
}
|
|
1264
|
+
function isThreadedAccumulator(node) {
|
|
1265
|
+
const parent = node.parent;
|
|
1266
|
+
let target = null;
|
|
1267
|
+
if (parent.type === "AssignmentExpression" && parent.operator === "=" && parent.right === node && parent.left.type === "Identifier") {
|
|
1268
|
+
target = parent.left.name;
|
|
1269
|
+
} else if (parent.type === "VariableDeclarator" && parent.init === node && parent.id.type === "Identifier") {
|
|
1270
|
+
target = parent.id.name;
|
|
1271
|
+
}
|
|
1272
|
+
if (target === null) {
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
return referencesName(node.argument, target);
|
|
1276
|
+
}
|
|
1277
|
+
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1278
|
+
if (awaits.length === 0) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
if (earlyExit) {
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
return awaits.some(
|
|
1288
|
+
(node) => !isTimerYield(node) && !isThreadedAccumulator(node) && !isQueueDrain(node)
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
var no_sequential_await_default = import_utils11.ESLintUtils.RuleCreator(
|
|
832
1292
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
833
1293
|
)({
|
|
834
1294
|
name: "no-sequential-await",
|
|
@@ -844,53 +1304,35 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
|
|
|
844
1304
|
},
|
|
845
1305
|
defaultOptions: [],
|
|
846
1306
|
create(context) {
|
|
847
|
-
function
|
|
848
|
-
if (node.type === "
|
|
849
|
-
return node;
|
|
1307
|
+
function loopParts(node) {
|
|
1308
|
+
if (node.type === "ForStatement") {
|
|
1309
|
+
return [node.body, node.test, node.update];
|
|
850
1310
|
}
|
|
851
|
-
if (
|
|
852
|
-
return
|
|
1311
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1312
|
+
return [node.body];
|
|
853
1313
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (Array.isArray(value)) {
|
|
860
|
-
for (const child of value) {
|
|
861
|
-
if (isNode4(child) && !isLoop(child)) {
|
|
862
|
-
const found = findAwaitInScope(child);
|
|
863
|
-
if (found) {
|
|
864
|
-
return found;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
} else if (isNode4(value) && !isLoop(value)) {
|
|
869
|
-
const found = findAwaitInScope(value);
|
|
870
|
-
if (found) {
|
|
871
|
-
return found;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
1314
|
+
return [node.body, node.test];
|
|
1315
|
+
}
|
|
1316
|
+
function iterableTextOf(node) {
|
|
1317
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1318
|
+
return context.sourceCode.getText(node.right);
|
|
874
1319
|
}
|
|
875
1320
|
return null;
|
|
876
1321
|
}
|
|
877
|
-
function isNode4(value) {
|
|
878
|
-
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
879
|
-
}
|
|
880
1322
|
function checkLoop(node) {
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
} else {
|
|
887
|
-
parts.push(node.test);
|
|
888
|
-
}
|
|
889
|
-
for (const part of parts) {
|
|
890
|
-
if (part && !isLoop(part) && findAwaitInScope(part)) {
|
|
891
|
-
context.report({ node, messageId: "noSequentialAwait" });
|
|
892
|
-
return;
|
|
1323
|
+
const awaits = [];
|
|
1324
|
+
let earlyExit = false;
|
|
1325
|
+
for (const part of loopParts(node)) {
|
|
1326
|
+
if (part === null || isLoop(part)) {
|
|
1327
|
+
continue;
|
|
893
1328
|
}
|
|
1329
|
+
awaits.push(...collectAwaits(part));
|
|
1330
|
+
if (!earlyExit && hasEarlyExit(part)) {
|
|
1331
|
+
earlyExit = true;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1335
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
894
1336
|
}
|
|
895
1337
|
}
|
|
896
1338
|
return {
|
|
@@ -903,13 +1345,35 @@ var no_sequential_await_default = import_utils10.ESLintUtils.RuleCreator(
|
|
|
903
1345
|
return;
|
|
904
1346
|
}
|
|
905
1347
|
checkLoop(node);
|
|
1348
|
+
},
|
|
1349
|
+
CallExpression(node) {
|
|
1350
|
+
const callee = node.callee;
|
|
1351
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
if (callee.property.type !== "Identifier" || !ARRAY_ITERATION_METHODS.has(callee.property.name)) {
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
const callback = node.arguments[0];
|
|
1358
|
+
if (callback === void 0 || !isFunctionLike(callback) || !("async" in callback && callback.async)) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
if (callee.property.name !== "forEach" && node.parent.type !== "ExpressionStatement") {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
const awaits = collectAwaits(callback.body);
|
|
1365
|
+
const earlyExit = hasEarlyExit(callback.body);
|
|
1366
|
+
const iterableText = context.sourceCode.getText(callee.object);
|
|
1367
|
+
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1368
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
1369
|
+
}
|
|
906
1370
|
}
|
|
907
1371
|
};
|
|
908
1372
|
}
|
|
909
1373
|
});
|
|
910
1374
|
|
|
911
1375
|
// src/rules/no-string-concat-in-loop.ts
|
|
912
|
-
var
|
|
1376
|
+
var import_utils12 = require("@typescript-eslint/utils");
|
|
913
1377
|
var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
914
1378
|
"ForStatement",
|
|
915
1379
|
"ForOfStatement",
|
|
@@ -954,6 +1418,21 @@ function isStringInitializedVariable(variable) {
|
|
|
954
1418
|
}
|
|
955
1419
|
return isStringLiteralInit(declarator.init);
|
|
956
1420
|
}
|
|
1421
|
+
function isConcatOperand(node, target) {
|
|
1422
|
+
if (node.type === "Identifier") {
|
|
1423
|
+
return node.name === target;
|
|
1424
|
+
}
|
|
1425
|
+
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
1426
|
+
return isConcatOperand(node.left, target) || isConcatOperand(node.right, target);
|
|
1427
|
+
}
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
function isConcatOntoTarget(rhs, target) {
|
|
1431
|
+
if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1435
|
+
}
|
|
957
1436
|
function isInsideLoopBody(node) {
|
|
958
1437
|
let child = node;
|
|
959
1438
|
let parent = node.parent;
|
|
@@ -969,7 +1448,7 @@ function isInsideLoopBody(node) {
|
|
|
969
1448
|
}
|
|
970
1449
|
return false;
|
|
971
1450
|
}
|
|
972
|
-
var no_string_concat_in_loop_default =
|
|
1451
|
+
var no_string_concat_in_loop_default = import_utils12.ESLintUtils.RuleCreator(
|
|
973
1452
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
974
1453
|
)({
|
|
975
1454
|
name: "no-string-concat-in-loop",
|
|
@@ -987,10 +1466,11 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
987
1466
|
create(context) {
|
|
988
1467
|
return {
|
|
989
1468
|
AssignmentExpression(node) {
|
|
990
|
-
if (node.
|
|
1469
|
+
if (node.left.type !== "Identifier") {
|
|
991
1470
|
return;
|
|
992
1471
|
}
|
|
993
|
-
|
|
1472
|
+
const isAccumulation = node.operator === "+=" || node.operator === "=" && isConcatOntoTarget(node.right, node.left.name);
|
|
1473
|
+
if (!isAccumulation) {
|
|
994
1474
|
return;
|
|
995
1475
|
}
|
|
996
1476
|
if (!isInsideLoopBody(node)) {
|
|
@@ -1014,7 +1494,7 @@ var no_string_concat_in_loop_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
1014
1494
|
});
|
|
1015
1495
|
|
|
1016
1496
|
// src/rules/no-unnecessary-use-client.ts
|
|
1017
|
-
var
|
|
1497
|
+
var import_utils13 = require("@typescript-eslint/utils");
|
|
1018
1498
|
var HOOK_REGEX = /^use([A-Z]|$)/;
|
|
1019
1499
|
var EVENT_PROP_REGEX = /^on[A-Z]/;
|
|
1020
1500
|
var ERROR_FILE_REGEX = /\b(?:global-)?error\.[jt]sx?$/;
|
|
@@ -1037,16 +1517,16 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
1037
1517
|
]);
|
|
1038
1518
|
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\/)/;
|
|
1039
1519
|
var isUseClientDirective = (node) => {
|
|
1040
|
-
return node.type ===
|
|
1520
|
+
return node.type === import_utils13.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils13.AST_NODE_TYPES.Literal && node.expression.value === "use client";
|
|
1041
1521
|
};
|
|
1042
1522
|
var isGlobalReference = (node, context) => {
|
|
1043
1523
|
if (!BROWSER_GLOBALS.has(node.name)) return false;
|
|
1044
1524
|
const parent = node.parent;
|
|
1045
1525
|
if (parent !== void 0) {
|
|
1046
|
-
if (parent.type ===
|
|
1526
|
+
if (parent.type === import_utils13.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
|
|
1047
1527
|
return false;
|
|
1048
1528
|
}
|
|
1049
|
-
if (parent.type ===
|
|
1529
|
+
if (parent.type === import_utils13.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
|
|
1050
1530
|
return false;
|
|
1051
1531
|
}
|
|
1052
1532
|
if (parent.type.startsWith("TS")) {
|
|
@@ -1063,7 +1543,7 @@ var isGlobalReference = (node, context) => {
|
|
|
1063
1543
|
}
|
|
1064
1544
|
return true;
|
|
1065
1545
|
};
|
|
1066
|
-
var no_unnecessary_use_client_default =
|
|
1546
|
+
var no_unnecessary_use_client_default = import_utils13.ESLintUtils.RuleCreator(
|
|
1067
1547
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1068
1548
|
)({
|
|
1069
1549
|
name: "no-unnecessary-use-client",
|
|
@@ -1086,13 +1566,13 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1086
1566
|
let directiveNode = null;
|
|
1087
1567
|
let hasClientIndicator = false;
|
|
1088
1568
|
const markIfHookOrContext = (callee) => {
|
|
1089
|
-
if (callee.type ===
|
|
1569
|
+
if (callee.type === import_utils13.AST_NODE_TYPES.Identifier) {
|
|
1090
1570
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
1091
1571
|
hasClientIndicator = true;
|
|
1092
1572
|
}
|
|
1093
1573
|
return;
|
|
1094
1574
|
}
|
|
1095
|
-
if (callee.type ===
|
|
1575
|
+
if (callee.type === import_utils13.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils13.AST_NODE_TYPES.Identifier) {
|
|
1096
1576
|
const name = callee.property.name;
|
|
1097
1577
|
if (HOOK_REGEX.test(name) || name === "createContext") {
|
|
1098
1578
|
hasClientIndicator = true;
|
|
@@ -1102,7 +1582,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1102
1582
|
return {
|
|
1103
1583
|
Program(node) {
|
|
1104
1584
|
for (const stmt of node.body) {
|
|
1105
|
-
if (stmt.type !==
|
|
1585
|
+
if (stmt.type !== import_utils13.AST_NODE_TYPES.ExpressionStatement) break;
|
|
1106
1586
|
if (isUseClientDirective(stmt)) {
|
|
1107
1587
|
directiveNode = stmt;
|
|
1108
1588
|
break;
|
|
@@ -1115,7 +1595,7 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1115
1595
|
},
|
|
1116
1596
|
JSXAttribute(node) {
|
|
1117
1597
|
if (directiveNode === null) return;
|
|
1118
|
-
if (node.name.type ===
|
|
1598
|
+
if (node.name.type === import_utils13.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
|
|
1119
1599
|
hasClientIndicator = true;
|
|
1120
1600
|
}
|
|
1121
1601
|
},
|
|
@@ -1164,8 +1644,8 @@ var no_unnecessary_use_client_default = import_utils12.ESLintUtils.RuleCreator(
|
|
|
1164
1644
|
});
|
|
1165
1645
|
|
|
1166
1646
|
// src/rules/prefer-discriminated-union.ts
|
|
1167
|
-
var import_utils13 = require("@typescript-eslint/utils");
|
|
1168
1647
|
var import_utils14 = require("@typescript-eslint/utils");
|
|
1648
|
+
var import_utils15 = require("@typescript-eslint/utils");
|
|
1169
1649
|
var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
1170
1650
|
"success",
|
|
1171
1651
|
"ok",
|
|
@@ -1175,26 +1655,26 @@ var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
|
1175
1655
|
]);
|
|
1176
1656
|
var MIN_OPTIONAL_MEMBERS = 2;
|
|
1177
1657
|
function getMemberName(member) {
|
|
1178
|
-
if (member.type !==
|
|
1658
|
+
if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
|
|
1179
1659
|
return null;
|
|
1180
1660
|
}
|
|
1181
1661
|
const { key } = member;
|
|
1182
|
-
if (key.type ===
|
|
1662
|
+
if (key.type === import_utils15.AST_NODE_TYPES.Identifier) {
|
|
1183
1663
|
return key.name;
|
|
1184
1664
|
}
|
|
1185
|
-
if (key.type ===
|
|
1665
|
+
if (key.type === import_utils15.AST_NODE_TYPES.Literal && typeof key.value === "string") {
|
|
1186
1666
|
return key.value;
|
|
1187
1667
|
}
|
|
1188
1668
|
return null;
|
|
1189
1669
|
}
|
|
1190
1670
|
function isBooleanTyped(member) {
|
|
1191
|
-
return member.typeAnnotation?.typeAnnotation.type ===
|
|
1671
|
+
return member.typeAnnotation?.typeAnnotation.type === import_utils15.AST_NODE_TYPES.TSBooleanKeyword;
|
|
1192
1672
|
}
|
|
1193
1673
|
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1194
1674
|
let hasStatusBoolean = false;
|
|
1195
1675
|
let optionalCount = 0;
|
|
1196
1676
|
for (const member of typeLiteral.members) {
|
|
1197
|
-
if (member.type !==
|
|
1677
|
+
if (member.type !== import_utils15.AST_NODE_TYPES.TSPropertySignature) {
|
|
1198
1678
|
continue;
|
|
1199
1679
|
}
|
|
1200
1680
|
if (member.optional) {
|
|
@@ -1207,7 +1687,7 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
|
1207
1687
|
}
|
|
1208
1688
|
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
1209
1689
|
}
|
|
1210
|
-
var prefer_discriminated_union_default =
|
|
1690
|
+
var prefer_discriminated_union_default = import_utils14.ESLintUtils.RuleCreator(
|
|
1211
1691
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1212
1692
|
)({
|
|
1213
1693
|
name: "prefer-discriminated-union",
|
|
@@ -1235,7 +1715,7 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
|
|
|
1235
1715
|
TSInterfaceDeclaration(node) {
|
|
1236
1716
|
const synthetic = {
|
|
1237
1717
|
...node.body,
|
|
1238
|
-
type:
|
|
1718
|
+
type: import_utils15.AST_NODE_TYPES.TSTypeLiteral,
|
|
1239
1719
|
members: node.body.body
|
|
1240
1720
|
};
|
|
1241
1721
|
checkTypeLiteral(synthetic, node);
|
|
@@ -1248,13 +1728,13 @@ var prefer_discriminated_union_default = import_utils13.ESLintUtils.RuleCreator(
|
|
|
1248
1728
|
});
|
|
1249
1729
|
|
|
1250
1730
|
// src/rules/prefer-schema-for-api-payload.ts
|
|
1251
|
-
var
|
|
1731
|
+
var import_utils16 = require("@typescript-eslint/utils");
|
|
1252
1732
|
var unwrap = (node) => {
|
|
1253
1733
|
let current = node;
|
|
1254
1734
|
while (current !== null && current !== void 0) {
|
|
1255
|
-
if (current.type ===
|
|
1735
|
+
if (current.type === import_utils16.AST_NODE_TYPES.TSAsExpression || current.type === import_utils16.AST_NODE_TYPES.TSTypeAssertion || current.type === import_utils16.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils16.AST_NODE_TYPES.TSSatisfiesExpression) {
|
|
1256
1736
|
current = current.expression;
|
|
1257
|
-
} else if (current.type ===
|
|
1737
|
+
} else if (current.type === import_utils16.AST_NODE_TYPES.ChainExpression) {
|
|
1258
1738
|
current = current.expression;
|
|
1259
1739
|
} else {
|
|
1260
1740
|
break;
|
|
@@ -1265,18 +1745,18 @@ var unwrap = (node) => {
|
|
|
1265
1745
|
var isJsonCall = (node) => {
|
|
1266
1746
|
let current = unwrap(node);
|
|
1267
1747
|
if (current === null) return false;
|
|
1268
|
-
if (current.type ===
|
|
1748
|
+
if (current.type === import_utils16.AST_NODE_TYPES.AwaitExpression) {
|
|
1269
1749
|
current = unwrap(current.argument);
|
|
1270
1750
|
}
|
|
1271
|
-
if (current === null || current.type !==
|
|
1751
|
+
if (current === null || current.type !== import_utils16.AST_NODE_TYPES.CallExpression) {
|
|
1272
1752
|
return false;
|
|
1273
1753
|
}
|
|
1274
1754
|
const callee = unwrap(current.callee);
|
|
1275
|
-
if (callee === null || callee.type !==
|
|
1755
|
+
if (callee === null || callee.type !== import_utils16.AST_NODE_TYPES.MemberExpression) {
|
|
1276
1756
|
return false;
|
|
1277
1757
|
}
|
|
1278
1758
|
const property = unwrap(callee.property);
|
|
1279
|
-
return property !== null && property.type ===
|
|
1759
|
+
return property !== null && property.type === import_utils16.AST_NODE_TYPES.Identifier && property.name === "json";
|
|
1280
1760
|
};
|
|
1281
1761
|
var findVariable2 = (scope, name) => {
|
|
1282
1762
|
let current = scope;
|
|
@@ -1287,15 +1767,39 @@ var findVariable2 = (scope, name) => {
|
|
|
1287
1767
|
}
|
|
1288
1768
|
return null;
|
|
1289
1769
|
};
|
|
1770
|
+
var GUARD_NAME_RE = /^is[A-Z]/;
|
|
1771
|
+
var isGuardTestPosition = (node) => {
|
|
1772
|
+
let current = node;
|
|
1773
|
+
let parent = current.parent;
|
|
1774
|
+
while (parent !== void 0 && parent !== null) {
|
|
1775
|
+
switch (parent.type) {
|
|
1776
|
+
case import_utils16.AST_NODE_TYPES.UnaryExpression:
|
|
1777
|
+
case import_utils16.AST_NODE_TYPES.LogicalExpression:
|
|
1778
|
+
case import_utils16.AST_NODE_TYPES.ChainExpression:
|
|
1779
|
+
current = parent;
|
|
1780
|
+
parent = parent.parent;
|
|
1781
|
+
continue;
|
|
1782
|
+
case import_utils16.AST_NODE_TYPES.IfStatement:
|
|
1783
|
+
case import_utils16.AST_NODE_TYPES.ConditionalExpression:
|
|
1784
|
+
case import_utils16.AST_NODE_TYPES.WhileStatement:
|
|
1785
|
+
case import_utils16.AST_NODE_TYPES.DoWhileStatement:
|
|
1786
|
+
case import_utils16.AST_NODE_TYPES.ForStatement:
|
|
1787
|
+
return parent.test === current;
|
|
1788
|
+
default:
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
return false;
|
|
1793
|
+
};
|
|
1290
1794
|
var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
1291
1795
|
const unwrapped = unwrap(node);
|
|
1292
|
-
if (unwrapped === null || unwrapped.type !==
|
|
1796
|
+
if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
|
|
1293
1797
|
return false;
|
|
1294
1798
|
}
|
|
1295
1799
|
const variable = findVariable2(scope, unwrapped.name);
|
|
1296
1800
|
return variable !== null && tracked.has(variable);
|
|
1297
1801
|
};
|
|
1298
|
-
var prefer_schema_for_api_payload_default =
|
|
1802
|
+
var prefer_schema_for_api_payload_default = import_utils16.ESLintUtils.RuleCreator(
|
|
1299
1803
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1300
1804
|
)({
|
|
1301
1805
|
name: "prefer-schema-for-api-payload",
|
|
@@ -1323,11 +1827,11 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
|
|
|
1323
1827
|
return {
|
|
1324
1828
|
VariableDeclarator(node) {
|
|
1325
1829
|
const scope = context.sourceCode.getScope(node);
|
|
1326
|
-
if (node.id.type ===
|
|
1830
|
+
if (node.id.type === import_utils16.AST_NODE_TYPES.Identifier) {
|
|
1327
1831
|
trackInitializer(node);
|
|
1328
1832
|
return;
|
|
1329
1833
|
}
|
|
1330
|
-
if (node.id.type ===
|
|
1834
|
+
if (node.id.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
|
|
1331
1835
|
if (isJsonCall(node.init)) {
|
|
1332
1836
|
context.report({ node: node.id, messageId: "unparsedJsonAccess" });
|
|
1333
1837
|
return;
|
|
@@ -1339,7 +1843,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
|
|
|
1339
1843
|
},
|
|
1340
1844
|
AssignmentExpression(node) {
|
|
1341
1845
|
const scope = context.sourceCode.getScope(node);
|
|
1342
|
-
if (node.left.type ===
|
|
1846
|
+
if (node.left.type === import_utils16.AST_NODE_TYPES.Identifier) {
|
|
1343
1847
|
const variable = findVariable2(scope, node.left.name);
|
|
1344
1848
|
if (variable === null) return;
|
|
1345
1849
|
if (isJsonCall(node.right)) {
|
|
@@ -1349,7 +1853,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
|
|
|
1349
1853
|
}
|
|
1350
1854
|
return;
|
|
1351
1855
|
}
|
|
1352
|
-
if (node.left.type ===
|
|
1856
|
+
if (node.left.type === import_utils16.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils16.AST_NODE_TYPES.ArrayPattern) {
|
|
1353
1857
|
if (isJsonCall(node.right)) {
|
|
1354
1858
|
context.report({
|
|
1355
1859
|
node: node.left,
|
|
@@ -1365,18 +1869,34 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
|
|
|
1365
1869
|
}
|
|
1366
1870
|
}
|
|
1367
1871
|
},
|
|
1872
|
+
CallExpression(node) {
|
|
1873
|
+
if (node.callee.type !== import_utils16.AST_NODE_TYPES.Identifier) return;
|
|
1874
|
+
if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
const scope = context.sourceCode.getScope(node);
|
|
1878
|
+
for (const arg of node.arguments) {
|
|
1879
|
+
if (arg.type === import_utils16.AST_NODE_TYPES.SpreadElement) continue;
|
|
1880
|
+
const unwrapped = unwrap(arg);
|
|
1881
|
+
if (unwrapped === null || unwrapped.type !== import_utils16.AST_NODE_TYPES.Identifier) {
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
const variable = findVariable2(scope, unwrapped.name);
|
|
1885
|
+
if (variable !== null) unvalidatedVariables.delete(variable);
|
|
1886
|
+
}
|
|
1887
|
+
},
|
|
1368
1888
|
MemberExpression(node) {
|
|
1369
1889
|
const scope = context.sourceCode.getScope(node);
|
|
1370
1890
|
const obj = unwrap(node.object);
|
|
1371
1891
|
if (isJsonCall(obj)) {
|
|
1372
1892
|
const parent = node.parent;
|
|
1373
|
-
if (parent.type ===
|
|
1893
|
+
if (parent.type === import_utils16.AST_NODE_TYPES.CallExpression && parent.callee === node && node.property.type === import_utils16.AST_NODE_TYPES.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
|
|
1374
1894
|
return;
|
|
1375
1895
|
}
|
|
1376
1896
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
1377
1897
|
return;
|
|
1378
1898
|
}
|
|
1379
|
-
if (obj !== null && obj.type ===
|
|
1899
|
+
if (obj !== null && obj.type === import_utils16.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
|
|
1380
1900
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
1381
1901
|
const variable = findVariable2(scope, obj.name);
|
|
1382
1902
|
if (variable !== null) {
|
|
@@ -1389,7 +1909,7 @@ var prefer_schema_for_api_payload_default = import_utils15.ESLintUtils.RuleCreat
|
|
|
1389
1909
|
});
|
|
1390
1910
|
|
|
1391
1911
|
// src/rules/prefer-semantic-colors.ts
|
|
1392
|
-
var
|
|
1912
|
+
var import_utils17 = require("@typescript-eslint/utils");
|
|
1393
1913
|
|
|
1394
1914
|
// src/rules/_tailwind.ts
|
|
1395
1915
|
var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
|
|
@@ -1426,12 +1946,41 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
|
1426
1946
|
"lightingColor"
|
|
1427
1947
|
]);
|
|
1428
1948
|
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
1949
|
+
var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
1950
|
+
var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
|
|
1951
|
+
"mask",
|
|
1952
|
+
"clipPath",
|
|
1953
|
+
"defs",
|
|
1954
|
+
"pattern",
|
|
1955
|
+
"linearGradient",
|
|
1956
|
+
"radialGradient"
|
|
1957
|
+
]);
|
|
1958
|
+
var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
|
|
1959
|
+
"#fff",
|
|
1960
|
+
"#ffffff",
|
|
1961
|
+
"#000",
|
|
1962
|
+
"#000000",
|
|
1963
|
+
"transparent",
|
|
1964
|
+
"none",
|
|
1965
|
+
"currentcolor",
|
|
1966
|
+
"inherit"
|
|
1967
|
+
]);
|
|
1968
|
+
var isInsideSvg = (node) => {
|
|
1969
|
+
let current = node.parent;
|
|
1970
|
+
while (current !== void 0 && current !== null) {
|
|
1971
|
+
if (current.type === import_utils17.AST_NODE_TYPES.JSXElement && current.openingElement.name.type === import_utils17.AST_NODE_TYPES.JSXIdentifier && (current.openingElement.name.name === "svg" || SVG_DEFS_CONTAINERS.has(current.openingElement.name.name))) {
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
current = current.parent;
|
|
1975
|
+
}
|
|
1976
|
+
return false;
|
|
1977
|
+
};
|
|
1429
1978
|
var propName = (key) => {
|
|
1430
|
-
if (key.type ===
|
|
1431
|
-
if (key.type ===
|
|
1979
|
+
if (key.type === import_utils17.AST_NODE_TYPES.Identifier) return key.name;
|
|
1980
|
+
if (key.type === import_utils17.AST_NODE_TYPES.Literal && typeof key.value === "string") return key.value;
|
|
1432
1981
|
return null;
|
|
1433
1982
|
};
|
|
1434
|
-
var prefer_semantic_colors_default =
|
|
1983
|
+
var prefer_semantic_colors_default = import_utils17.ESLintUtils.RuleCreator(
|
|
1435
1984
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1436
1985
|
)({
|
|
1437
1986
|
name: "prefer-semantic-colors",
|
|
@@ -1449,6 +1998,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
|
|
|
1449
1998
|
},
|
|
1450
1999
|
defaultOptions: [],
|
|
1451
2000
|
create(context) {
|
|
2001
|
+
if (STORIES_FILE_RE.test(context.filename)) return {};
|
|
1452
2002
|
const reportClasses = (value, node) => {
|
|
1453
2003
|
for (const token of classTokens(value)) {
|
|
1454
2004
|
const base = tailwindBase(token);
|
|
@@ -1462,27 +2012,27 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
|
|
|
1462
2012
|
const checkClassNode = (node) => {
|
|
1463
2013
|
if (node === null) return;
|
|
1464
2014
|
switch (node.type) {
|
|
1465
|
-
case
|
|
2015
|
+
case import_utils17.AST_NODE_TYPES.Literal:
|
|
1466
2016
|
if (typeof node.value === "string") reportClasses(node.value, node);
|
|
1467
2017
|
break;
|
|
1468
|
-
case
|
|
2018
|
+
case import_utils17.AST_NODE_TYPES.TemplateLiteral:
|
|
1469
2019
|
for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? "", quasi);
|
|
1470
2020
|
break;
|
|
1471
|
-
case
|
|
2021
|
+
case import_utils17.AST_NODE_TYPES.ArrayExpression:
|
|
1472
2022
|
for (const element of node.elements) {
|
|
1473
|
-
if (element !== null && element.type !==
|
|
2023
|
+
if (element !== null && element.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(element);
|
|
1474
2024
|
}
|
|
1475
2025
|
break;
|
|
1476
|
-
case
|
|
2026
|
+
case import_utils17.AST_NODE_TYPES.ObjectExpression:
|
|
1477
2027
|
for (const property of node.properties) {
|
|
1478
|
-
if (property.type ===
|
|
2028
|
+
if (property.type === import_utils17.AST_NODE_TYPES.Property) checkClassNode(property.value);
|
|
1479
2029
|
}
|
|
1480
2030
|
break;
|
|
1481
|
-
case
|
|
2031
|
+
case import_utils17.AST_NODE_TYPES.ConditionalExpression:
|
|
1482
2032
|
checkClassNode(node.consequent);
|
|
1483
2033
|
checkClassNode(node.alternate);
|
|
1484
2034
|
break;
|
|
1485
|
-
case
|
|
2035
|
+
case import_utils17.AST_NODE_TYPES.LogicalExpression:
|
|
1486
2036
|
checkClassNode(node.right);
|
|
1487
2037
|
break;
|
|
1488
2038
|
default:
|
|
@@ -1490,29 +2040,29 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
|
|
|
1490
2040
|
}
|
|
1491
2041
|
};
|
|
1492
2042
|
const checkColorValueNode = (node) => {
|
|
1493
|
-
if (node.type ===
|
|
2043
|
+
if (node.type === import_utils17.AST_NODE_TYPES.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
|
|
1494
2044
|
context.report({ node, messageId: "inlineColor", data: { value: node.value } });
|
|
1495
2045
|
}
|
|
1496
2046
|
};
|
|
1497
2047
|
return {
|
|
1498
2048
|
"JSXAttribute[name.name='className']"(node) {
|
|
1499
2049
|
if (node.value === null) return;
|
|
1500
|
-
if (node.value.type ===
|
|
1501
|
-
else if (node.value.type ===
|
|
1502
|
-
if (node.value.expression.type !==
|
|
2050
|
+
if (node.value.type === import_utils17.AST_NODE_TYPES.Literal) checkClassNode(node.value);
|
|
2051
|
+
else if (node.value.type === import_utils17.AST_NODE_TYPES.JSXExpressionContainer) {
|
|
2052
|
+
if (node.value.expression.type !== import_utils17.AST_NODE_TYPES.JSXEmptyExpression) {
|
|
1503
2053
|
checkClassNode(node.value.expression);
|
|
1504
2054
|
}
|
|
1505
2055
|
}
|
|
1506
2056
|
},
|
|
1507
2057
|
CallExpression(node) {
|
|
1508
|
-
if (node.callee.type ===
|
|
2058
|
+
if (node.callee.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_FNS.has(node.callee.name)) {
|
|
1509
2059
|
for (const arg of node.arguments) {
|
|
1510
|
-
if (arg.type !==
|
|
2060
|
+
if (arg.type !== import_utils17.AST_NODE_TYPES.SpreadElement) checkClassNode(arg);
|
|
1511
2061
|
}
|
|
1512
2062
|
}
|
|
1513
2063
|
},
|
|
1514
2064
|
VariableDeclarator(node) {
|
|
1515
|
-
if (node.id.type ===
|
|
2065
|
+
if (node.id.type === import_utils17.AST_NODE_TYPES.Identifier && CLASS_NAME_RE.test(node.id.name)) {
|
|
1516
2066
|
checkClassNode(node.init);
|
|
1517
2067
|
}
|
|
1518
2068
|
},
|
|
@@ -1520,9 +2070,16 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
|
|
|
1520
2070
|
const name = propName(node.key);
|
|
1521
2071
|
if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
|
|
1522
2072
|
},
|
|
1523
|
-
// SVG presentation attributes: <path fill="#
|
|
2073
|
+
// SVG presentation attributes: <path fill="#7c3aed" stroke="#7c3aed" />.
|
|
2074
|
+
// Neutral drawing literals and anything inside an SVG defs container are
|
|
2075
|
+
// structural, not UI tokens, so they never fire.
|
|
1524
2076
|
"JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
|
|
1525
|
-
if (node.value?.type
|
|
2077
|
+
if (node.value?.type !== import_utils17.AST_NODE_TYPES.Literal) return;
|
|
2078
|
+
if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
if (isInsideSvg(node)) return;
|
|
2082
|
+
checkColorValueNode(node.value);
|
|
1526
2083
|
},
|
|
1527
2084
|
// Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
|
|
1528
2085
|
"JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
|
|
@@ -1534,7 +2091,7 @@ var prefer_semantic_colors_default = import_utils16.ESLintUtils.RuleCreator(
|
|
|
1534
2091
|
});
|
|
1535
2092
|
|
|
1536
2093
|
// src/rules/prefer-server-actions.ts
|
|
1537
|
-
var
|
|
2094
|
+
var import_utils18 = require("@typescript-eslint/utils");
|
|
1538
2095
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
1539
2096
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
1540
2097
|
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
@@ -1613,7 +2170,7 @@ function getPropertyNode(objNode, propName2) {
|
|
|
1613
2170
|
}
|
|
1614
2171
|
return null;
|
|
1615
2172
|
}
|
|
1616
|
-
var prefer_server_actions_default =
|
|
2173
|
+
var prefer_server_actions_default = import_utils18.ESLintUtils.RuleCreator(
|
|
1617
2174
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1618
2175
|
)({
|
|
1619
2176
|
name: "prefer-server-actions",
|
|
@@ -1681,14 +2238,38 @@ var prefer_server_actions_default = import_utils17.ESLintUtils.RuleCreator(
|
|
|
1681
2238
|
});
|
|
1682
2239
|
|
|
1683
2240
|
// src/rules/prefer-shadcn.ts
|
|
1684
|
-
var
|
|
2241
|
+
var import_utils19 = require("@typescript-eslint/utils");
|
|
1685
2242
|
var REPLACEMENTS = {
|
|
1686
|
-
input: "Input",
|
|
1687
2243
|
select: "Select",
|
|
1688
2244
|
textarea: "Textarea",
|
|
1689
2245
|
dialog: "Dialog"
|
|
1690
2246
|
};
|
|
1691
|
-
var
|
|
2247
|
+
var INPUT_TYPE_REPLACEMENTS = {
|
|
2248
|
+
checkbox: "Checkbox",
|
|
2249
|
+
radio: "RadioGroup",
|
|
2250
|
+
range: "Slider"
|
|
2251
|
+
};
|
|
2252
|
+
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
|
|
2253
|
+
var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2254
|
+
var literalTypeAttr = (node) => {
|
|
2255
|
+
for (const attribute of node.attributes) {
|
|
2256
|
+
if (attribute.type !== import_utils19.AST_NODE_TYPES.JSXAttribute || attribute.name.type !== import_utils19.AST_NODE_TYPES.JSXIdentifier || attribute.name.name !== "type") {
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
if (attribute.value?.type === import_utils19.AST_NODE_TYPES.Literal && typeof attribute.value.value === "string") {
|
|
2260
|
+
return { kind: "literal", value: attribute.value.value.toLowerCase() };
|
|
2261
|
+
}
|
|
2262
|
+
return { kind: "dynamic" };
|
|
2263
|
+
}
|
|
2264
|
+
return null;
|
|
2265
|
+
};
|
|
2266
|
+
var resolveInputReplacement = (node) => {
|
|
2267
|
+
const typeAttr = literalTypeAttr(node);
|
|
2268
|
+
if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
|
|
2269
|
+
if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
|
|
2270
|
+
return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
|
|
2271
|
+
};
|
|
2272
|
+
var prefer_shadcn_default = import_utils19.ESLintUtils.RuleCreator(
|
|
1692
2273
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1693
2274
|
)({
|
|
1694
2275
|
name: "prefer-shadcn",
|
|
@@ -1710,8 +2291,8 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
|
|
|
1710
2291
|
return;
|
|
1711
2292
|
}
|
|
1712
2293
|
const elementName = node.name.name;
|
|
1713
|
-
const replacement = REPLACEMENTS[elementName];
|
|
1714
|
-
if (replacement === void 0) {
|
|
2294
|
+
const replacement = elementName === "input" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];
|
|
2295
|
+
if (replacement === void 0 || replacement === null) {
|
|
1715
2296
|
return;
|
|
1716
2297
|
}
|
|
1717
2298
|
context.report({
|
|
@@ -1720,7 +2301,7 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
|
|
|
1720
2301
|
data: {
|
|
1721
2302
|
element: elementName,
|
|
1722
2303
|
replacement,
|
|
1723
|
-
lowercase:
|
|
2304
|
+
lowercase: kebabCase(replacement)
|
|
1724
2305
|
}
|
|
1725
2306
|
});
|
|
1726
2307
|
}
|
|
@@ -1729,41 +2310,57 @@ var prefer_shadcn_default = import_utils18.ESLintUtils.RuleCreator(
|
|
|
1729
2310
|
});
|
|
1730
2311
|
|
|
1731
2312
|
// src/rules/require-assert-never.ts
|
|
1732
|
-
var
|
|
2313
|
+
var import_utils20 = require("@typescript-eslint/utils");
|
|
1733
2314
|
var isAssertNeverCall = (expression) => {
|
|
1734
|
-
if (expression.type !==
|
|
2315
|
+
if (expression.type !== import_utils20.AST_NODE_TYPES.CallExpression) return false;
|
|
1735
2316
|
const callee = expression.callee;
|
|
1736
|
-
if (callee.type ===
|
|
2317
|
+
if (callee.type === import_utils20.AST_NODE_TYPES.Identifier) {
|
|
1737
2318
|
return callee.name === "assertNever";
|
|
1738
2319
|
}
|
|
1739
|
-
if (callee.type ===
|
|
2320
|
+
if (callee.type === import_utils20.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils20.AST_NODE_TYPES.Identifier) {
|
|
1740
2321
|
return callee.property.name === "assertNever";
|
|
1741
2322
|
}
|
|
1742
2323
|
return false;
|
|
1743
2324
|
};
|
|
1744
2325
|
var statementContainsAssertNever = (statement) => {
|
|
1745
|
-
if (statement.type ===
|
|
2326
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.ExpressionStatement) {
|
|
1746
2327
|
return isAssertNeverCall(statement.expression);
|
|
1747
2328
|
}
|
|
1748
|
-
if (statement.type ===
|
|
2329
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.ThrowStatement) {
|
|
1749
2330
|
return isAssertNeverCall(statement.argument);
|
|
1750
2331
|
}
|
|
1751
|
-
if (statement.type ===
|
|
2332
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.ReturnStatement) {
|
|
1752
2333
|
return statement.argument !== null && isAssertNeverCall(statement.argument);
|
|
1753
2334
|
}
|
|
1754
|
-
if (statement.type ===
|
|
2335
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
|
|
1755
2336
|
return statement.body.some(statementContainsAssertNever);
|
|
1756
2337
|
}
|
|
1757
2338
|
return false;
|
|
1758
2339
|
};
|
|
1759
2340
|
var isRuntimeHandlingStatement = (statement) => {
|
|
1760
|
-
if (statement.type ===
|
|
1761
|
-
if (statement.type ===
|
|
2341
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.EmptyStatement) return false;
|
|
2342
|
+
if (statement.type === import_utils20.AST_NODE_TYPES.BlockStatement) {
|
|
1762
2343
|
return statement.body.some(isRuntimeHandlingStatement);
|
|
1763
2344
|
}
|
|
1764
2345
|
return true;
|
|
1765
2346
|
};
|
|
1766
|
-
var
|
|
2347
|
+
var isFallthroughDefault = (node, defaultIndex) => {
|
|
2348
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2349
|
+
return defaultCase !== void 0 && defaultCase.consequent.length === 0 && defaultIndex < node.cases.length - 1;
|
|
2350
|
+
};
|
|
2351
|
+
var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
|
|
2352
|
+
if (defaultCase.consequent.length === 0) {
|
|
2353
|
+
const defaultToken = sourceCode.getFirstToken(defaultCase);
|
|
2354
|
+
const colonToken = defaultToken ? sourceCode.getTokenAfter(defaultToken) : null;
|
|
2355
|
+
return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
|
|
2356
|
+
}
|
|
2357
|
+
const only = defaultCase.consequent[0];
|
|
2358
|
+
if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === import_utils20.AST_NODE_TYPES.BlockStatement && only.body.length === 0) {
|
|
2359
|
+
return sourceCode.getCommentsInside(only).length > 0;
|
|
2360
|
+
}
|
|
2361
|
+
return false;
|
|
2362
|
+
};
|
|
2363
|
+
var require_assert_never_default = import_utils20.ESLintUtils.RuleCreator(
|
|
1767
2364
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1768
2365
|
)({
|
|
1769
2366
|
name: "require-assert-never",
|
|
@@ -1781,12 +2378,16 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
|
|
|
1781
2378
|
create(context) {
|
|
1782
2379
|
return {
|
|
1783
2380
|
SwitchStatement(node) {
|
|
1784
|
-
const
|
|
2381
|
+
const defaultIndex = node.cases.findIndex(
|
|
1785
2382
|
(caseNode) => caseNode.test === null
|
|
1786
2383
|
);
|
|
1787
|
-
if (
|
|
2384
|
+
if (defaultIndex === -1) return;
|
|
2385
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2386
|
+
if (defaultCase === void 0) return;
|
|
1788
2387
|
if (defaultCase.consequent.some(statementContainsAssertNever)) return;
|
|
1789
2388
|
if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
|
|
2389
|
+
if (isFallthroughDefault(node, defaultIndex)) return;
|
|
2390
|
+
if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;
|
|
1790
2391
|
context.report({
|
|
1791
2392
|
node: defaultCase,
|
|
1792
2393
|
messageId: "missingAssertNever"
|
|
@@ -1797,19 +2398,19 @@ var require_assert_never_default = import_utils19.ESLintUtils.RuleCreator(
|
|
|
1797
2398
|
});
|
|
1798
2399
|
|
|
1799
2400
|
// src/rules/require-zod-form-validation.ts
|
|
1800
|
-
var
|
|
2401
|
+
var import_utils21 = require("@typescript-eslint/utils");
|
|
1801
2402
|
var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
|
|
1802
2403
|
var looksLikeZodSchema = (node) => {
|
|
1803
2404
|
let current = node;
|
|
1804
2405
|
while (true) {
|
|
1805
|
-
if (current.type ===
|
|
2406
|
+
if (current.type === import_utils21.AST_NODE_TYPES.Identifier) {
|
|
1806
2407
|
return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
|
|
1807
2408
|
}
|
|
1808
|
-
if (current.type ===
|
|
2409
|
+
if (current.type === import_utils21.AST_NODE_TYPES.CallExpression) {
|
|
1809
2410
|
current = current.callee;
|
|
1810
2411
|
continue;
|
|
1811
2412
|
}
|
|
1812
|
-
if (current.type ===
|
|
2413
|
+
if (current.type === import_utils21.AST_NODE_TYPES.MemberExpression) {
|
|
1813
2414
|
current = current.object;
|
|
1814
2415
|
continue;
|
|
1815
2416
|
}
|
|
@@ -1817,25 +2418,25 @@ var looksLikeZodSchema = (node) => {
|
|
|
1817
2418
|
}
|
|
1818
2419
|
};
|
|
1819
2420
|
var isZodParseCall = (node) => {
|
|
1820
|
-
if (node.type !==
|
|
2421
|
+
if (node.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
|
|
1821
2422
|
const callee = node.callee;
|
|
1822
|
-
if (callee.type !==
|
|
2423
|
+
if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
|
|
1823
2424
|
if (callee.computed) return false;
|
|
1824
|
-
if (callee.property.type !==
|
|
2425
|
+
if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
|
|
1825
2426
|
const method = callee.property.name;
|
|
1826
2427
|
if (method !== "parse" && method !== "safeParse") return false;
|
|
1827
2428
|
return looksLikeZodSchema(callee.object);
|
|
1828
2429
|
};
|
|
1829
2430
|
var isFormDataMethodCall = (node) => {
|
|
1830
2431
|
let current = node;
|
|
1831
|
-
if (current.type ===
|
|
2432
|
+
if (current.type === import_utils21.AST_NODE_TYPES.AwaitExpression) {
|
|
1832
2433
|
current = current.argument;
|
|
1833
2434
|
}
|
|
1834
|
-
if (current.type !==
|
|
2435
|
+
if (current.type !== import_utils21.AST_NODE_TYPES.CallExpression) return false;
|
|
1835
2436
|
const callee = current.callee;
|
|
1836
|
-
return callee.type ===
|
|
2437
|
+
return callee.type === import_utils21.AST_NODE_TYPES.MemberExpression && !callee.computed && callee.property.type === import_utils21.AST_NODE_TYPES.Identifier && callee.property.name === "formData";
|
|
1837
2438
|
};
|
|
1838
|
-
var require_zod_form_validation_default =
|
|
2439
|
+
var require_zod_form_validation_default = import_utils21.ESLintUtils.RuleCreator(
|
|
1839
2440
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1840
2441
|
)({
|
|
1841
2442
|
name: "require-zod-form-validation",
|
|
@@ -1852,14 +2453,14 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
|
|
|
1852
2453
|
defaultOptions: [],
|
|
1853
2454
|
create(context) {
|
|
1854
2455
|
const isFormSourceIdentifier = (node) => {
|
|
1855
|
-
if (node.type !==
|
|
2456
|
+
if (node.type !== import_utils21.AST_NODE_TYPES.Identifier) return false;
|
|
1856
2457
|
if (/formdata/i.test(node.name)) return true;
|
|
1857
2458
|
let scope = context.sourceCode.getScope(node);
|
|
1858
2459
|
while (scope !== null) {
|
|
1859
2460
|
const variable = scope.set.get(node.name);
|
|
1860
2461
|
if (variable !== void 0 && variable.defs.length === 1) {
|
|
1861
2462
|
const def = variable.defs[0];
|
|
1862
|
-
if (def !== void 0 && def.type === "Variable" && def.node.type ===
|
|
2463
|
+
if (def !== void 0 && def.type === "Variable" && def.node.type === import_utils21.AST_NODE_TYPES.VariableDeclarator && def.node.init !== null) {
|
|
1863
2464
|
return isFormDataMethodCall(def.node.init);
|
|
1864
2465
|
}
|
|
1865
2466
|
return false;
|
|
@@ -1870,8 +2471,8 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
|
|
|
1870
2471
|
};
|
|
1871
2472
|
const isFormDataGetCall = (node) => {
|
|
1872
2473
|
const callee = node.callee;
|
|
1873
|
-
if (callee.type !==
|
|
1874
|
-
if (callee.property.type !==
|
|
2474
|
+
if (callee.type !== import_utils21.AST_NODE_TYPES.MemberExpression) return false;
|
|
2475
|
+
if (callee.property.type !== import_utils21.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
|
|
1875
2476
|
return false;
|
|
1876
2477
|
}
|
|
1877
2478
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -1894,15 +2495,15 @@ var require_zod_form_validation_default = import_utils20.ESLintUtils.RuleCreator
|
|
|
1894
2495
|
});
|
|
1895
2496
|
|
|
1896
2497
|
// src/rules/zod-naming-convention.ts
|
|
1897
|
-
var
|
|
2498
|
+
var import_utils22 = require("@typescript-eslint/utils");
|
|
1898
2499
|
var calleeChainStartsWithZ = (node) => {
|
|
1899
2500
|
let current = node;
|
|
1900
|
-
while (current.type ===
|
|
2501
|
+
while (current.type === import_utils22.AST_NODE_TYPES.MemberExpression) {
|
|
1901
2502
|
const receiver = current.object;
|
|
1902
|
-
if (receiver.type ===
|
|
2503
|
+
if (receiver.type === import_utils22.AST_NODE_TYPES.Identifier && receiver.name === "z") {
|
|
1903
2504
|
return true;
|
|
1904
2505
|
}
|
|
1905
|
-
if (receiver.type ===
|
|
2506
|
+
if (receiver.type === import_utils22.AST_NODE_TYPES.CallExpression) {
|
|
1906
2507
|
current = receiver.callee;
|
|
1907
2508
|
continue;
|
|
1908
2509
|
}
|
|
@@ -1910,7 +2511,7 @@ var calleeChainStartsWithZ = (node) => {
|
|
|
1910
2511
|
}
|
|
1911
2512
|
return false;
|
|
1912
2513
|
};
|
|
1913
|
-
var zod_naming_convention_default =
|
|
2514
|
+
var zod_naming_convention_default = import_utils22.ESLintUtils.RuleCreator(
|
|
1914
2515
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1915
2516
|
)({
|
|
1916
2517
|
name: "zod-naming-convention",
|
|
@@ -1930,11 +2531,11 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
|
|
|
1930
2531
|
VariableDeclarator(node) {
|
|
1931
2532
|
const init = node.init;
|
|
1932
2533
|
if (init === null || init === void 0) return;
|
|
1933
|
-
if (init.type !==
|
|
2534
|
+
if (init.type !== import_utils22.AST_NODE_TYPES.CallExpression) return;
|
|
1934
2535
|
const callee = init.callee;
|
|
1935
|
-
if (callee.type !==
|
|
2536
|
+
if (callee.type !== import_utils22.AST_NODE_TYPES.MemberExpression) return;
|
|
1936
2537
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
1937
|
-
if (node.id.type !==
|
|
2538
|
+
if (node.id.type !== import_utils22.AST_NODE_TYPES.Identifier) return;
|
|
1938
2539
|
const variableName = node.id.name;
|
|
1939
2540
|
if (variableName.startsWith("Z")) return;
|
|
1940
2541
|
context.report({
|
|
@@ -1947,7 +2548,7 @@ var zod_naming_convention_default = import_utils21.ESLintUtils.RuleCreator(
|
|
|
1947
2548
|
});
|
|
1948
2549
|
|
|
1949
2550
|
// src/rules/no-cors-wildcard-with-credentials.ts
|
|
1950
|
-
var
|
|
2551
|
+
var import_utils23 = require("@typescript-eslint/utils");
|
|
1951
2552
|
var ACAO_HEADER = "access-control-allow-origin";
|
|
1952
2553
|
var ACAC_HEADER = "access-control-allow-credentials";
|
|
1953
2554
|
var HEADER_SET_METHODS = /* @__PURE__ */ new Set(["setheader", "set", "append"]);
|
|
@@ -1979,17 +2580,17 @@ function subtreeContainsStarLiteral(node) {
|
|
|
1979
2580
|
const value = node[key];
|
|
1980
2581
|
if (Array.isArray(value)) {
|
|
1981
2582
|
for (const child of value) {
|
|
1982
|
-
if (
|
|
2583
|
+
if (isNode3(child) && subtreeContainsStarLiteral(child)) {
|
|
1983
2584
|
return true;
|
|
1984
2585
|
}
|
|
1985
2586
|
}
|
|
1986
|
-
} else if (
|
|
2587
|
+
} else if (isNode3(value) && subtreeContainsStarLiteral(value)) {
|
|
1987
2588
|
return true;
|
|
1988
2589
|
}
|
|
1989
2590
|
}
|
|
1990
2591
|
return false;
|
|
1991
2592
|
}
|
|
1992
|
-
function
|
|
2593
|
+
function isNode3(value) {
|
|
1993
2594
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1994
2595
|
}
|
|
1995
2596
|
function propertyKeyName(prop) {
|
|
@@ -2005,7 +2606,7 @@ function propertyKeyName(prop) {
|
|
|
2005
2606
|
}
|
|
2006
2607
|
return void 0;
|
|
2007
2608
|
}
|
|
2008
|
-
function
|
|
2609
|
+
function calleeName3(node) {
|
|
2009
2610
|
const callee = node.callee;
|
|
2010
2611
|
if (callee.type === "Identifier") {
|
|
2011
2612
|
return callee.name;
|
|
@@ -2016,7 +2617,7 @@ function calleeName(node) {
|
|
|
2016
2617
|
return void 0;
|
|
2017
2618
|
}
|
|
2018
2619
|
function isCorsWildcardCredentialsCall(node) {
|
|
2019
|
-
const name =
|
|
2620
|
+
const name = calleeName3(node);
|
|
2020
2621
|
if (name === void 0 || name.toLowerCase() !== "cors") {
|
|
2021
2622
|
return false;
|
|
2022
2623
|
}
|
|
@@ -2089,7 +2690,7 @@ function enclosingScope(node) {
|
|
|
2089
2690
|
}
|
|
2090
2691
|
return void 0;
|
|
2091
2692
|
}
|
|
2092
|
-
var no_cors_wildcard_with_credentials_default =
|
|
2693
|
+
var no_cors_wildcard_with_credentials_default = import_utils23.ESLintUtils.RuleCreator(
|
|
2093
2694
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
2094
2695
|
)({
|
|
2095
2696
|
name: "no-cors-wildcard-with-credentials",
|
|
@@ -2157,12 +2758,12 @@ var no_cors_wildcard_with_credentials_default = import_utils22.ESLintUtils.RuleC
|
|
|
2157
2758
|
});
|
|
2158
2759
|
|
|
2159
2760
|
// src/rules/no-fat-try-blocks.ts
|
|
2160
|
-
var
|
|
2761
|
+
var import_utils24 = require("@typescript-eslint/utils");
|
|
2161
2762
|
var MAX_TRY_BODY_STATEMENTS = 3;
|
|
2162
2763
|
var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2764
|
+
import_utils24.AST_NODE_TYPES.FunctionDeclaration,
|
|
2765
|
+
import_utils24.AST_NODE_TYPES.FunctionExpression,
|
|
2766
|
+
import_utils24.AST_NODE_TYPES.ArrowFunctionExpression
|
|
2166
2767
|
]);
|
|
2167
2768
|
var PURE_METHODS = /* @__PURE__ */ new Set([
|
|
2168
2769
|
"map",
|
|
@@ -2255,25 +2856,25 @@ var PURE_CONSTRUCTORS = /* @__PURE__ */ new Set([
|
|
|
2255
2856
|
"Response",
|
|
2256
2857
|
"AbortController"
|
|
2257
2858
|
]);
|
|
2258
|
-
function
|
|
2859
|
+
function isNode4(value) {
|
|
2259
2860
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
2260
2861
|
}
|
|
2261
2862
|
function isPureCall(node) {
|
|
2262
2863
|
const callee = node.callee;
|
|
2263
|
-
if (callee.type !==
|
|
2864
|
+
if (callee.type !== import_utils24.AST_NODE_TYPES.MemberExpression) {
|
|
2264
2865
|
return false;
|
|
2265
2866
|
}
|
|
2266
2867
|
const property = callee.property;
|
|
2267
|
-
if (property.type !==
|
|
2868
|
+
if (property.type !== import_utils24.AST_NODE_TYPES.Identifier) {
|
|
2268
2869
|
return false;
|
|
2269
2870
|
}
|
|
2270
|
-
if (callee.object.type ===
|
|
2871
|
+
if (callee.object.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
|
|
2271
2872
|
return true;
|
|
2272
2873
|
}
|
|
2273
2874
|
return PURE_METHODS.has(property.name);
|
|
2274
2875
|
}
|
|
2275
2876
|
function isPureNew(node) {
|
|
2276
|
-
return node.callee.type ===
|
|
2877
|
+
return node.callee.type === import_utils24.AST_NODE_TYPES.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
|
|
2277
2878
|
}
|
|
2278
2879
|
function subtreeMatches(stmt, predicate) {
|
|
2279
2880
|
let found = false;
|
|
@@ -2295,11 +2896,11 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2295
2896
|
const value = current[key];
|
|
2296
2897
|
if (Array.isArray(value)) {
|
|
2297
2898
|
for (const child of value) {
|
|
2298
|
-
if (
|
|
2899
|
+
if (isNode4(child)) {
|
|
2299
2900
|
visit(child);
|
|
2300
2901
|
}
|
|
2301
2902
|
}
|
|
2302
|
-
} else if (
|
|
2903
|
+
} else if (isNode4(value)) {
|
|
2303
2904
|
visit(value);
|
|
2304
2905
|
}
|
|
2305
2906
|
if (found) {
|
|
@@ -2310,14 +2911,14 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2310
2911
|
visit(stmt);
|
|
2311
2912
|
return found;
|
|
2312
2913
|
}
|
|
2313
|
-
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type ===
|
|
2914
|
+
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === import_utils24.AST_NODE_TYPES.AwaitExpression);
|
|
2314
2915
|
var hasThrowingCallOrNew = (stmt) => subtreeMatches(
|
|
2315
2916
|
stmt,
|
|
2316
|
-
(n) => n.type ===
|
|
2917
|
+
(n) => n.type === import_utils24.AST_NODE_TYPES.CallExpression && !isPureCall(n) || n.type === import_utils24.AST_NODE_TYPES.NewExpression && !isPureNew(n)
|
|
2317
2918
|
);
|
|
2318
2919
|
function unwrap2(expr) {
|
|
2319
2920
|
let current = expr;
|
|
2320
|
-
while (current.type ===
|
|
2921
|
+
while (current.type === import_utils24.AST_NODE_TYPES.ChainExpression || current.type === import_utils24.AST_NODE_TYPES.TSNonNullExpression) {
|
|
2321
2922
|
current = current.expression;
|
|
2322
2923
|
}
|
|
2323
2924
|
return current;
|
|
@@ -2326,7 +2927,7 @@ function canThrow(stmt) {
|
|
|
2326
2927
|
if (hasAwait(stmt)) {
|
|
2327
2928
|
return true;
|
|
2328
2929
|
}
|
|
2329
|
-
if (stmt.type ===
|
|
2930
|
+
if (stmt.type === import_utils24.AST_NODE_TYPES.ExpressionStatement && unwrap2(stmt.expression).type === import_utils24.AST_NODE_TYPES.CallExpression) {
|
|
2330
2931
|
return false;
|
|
2331
2932
|
}
|
|
2332
2933
|
return hasThrowingCallOrNew(stmt);
|
|
@@ -2337,9 +2938,9 @@ function handlerRethrows(handler) {
|
|
|
2337
2938
|
}
|
|
2338
2939
|
const body = handler.body.body;
|
|
2339
2940
|
const last = body[body.length - 1];
|
|
2340
|
-
return last !== void 0 && last.type ===
|
|
2941
|
+
return last !== void 0 && last.type === import_utils24.AST_NODE_TYPES.ThrowStatement;
|
|
2341
2942
|
}
|
|
2342
|
-
var no_fat_try_blocks_default =
|
|
2943
|
+
var no_fat_try_blocks_default = import_utils24.ESLintUtils.RuleCreator(
|
|
2343
2944
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
2344
2945
|
)({
|
|
2345
2946
|
name: "no-fat-try-blocks",
|
|
@@ -2380,8 +2981,8 @@ var no_fat_try_blocks_default = import_utils23.ESLintUtils.RuleCreator(
|
|
|
2380
2981
|
});
|
|
2381
2982
|
|
|
2382
2983
|
// src/rules/no-secret-in-log.ts
|
|
2383
|
-
var
|
|
2384
|
-
var
|
|
2984
|
+
var import_utils25 = require("@typescript-eslint/utils");
|
|
2985
|
+
var LOG_METHODS2 = /* @__PURE__ */ new Set([
|
|
2385
2986
|
"debug",
|
|
2386
2987
|
"info",
|
|
2387
2988
|
"warn",
|
|
@@ -2394,7 +2995,7 @@ var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
|
2394
2995
|
"fatal",
|
|
2395
2996
|
"success"
|
|
2396
2997
|
]);
|
|
2397
|
-
var
|
|
2998
|
+
var LOGGER_NAMES2 = /* @__PURE__ */ new Set([
|
|
2398
2999
|
"logger",
|
|
2399
3000
|
"log",
|
|
2400
3001
|
"logging",
|
|
@@ -2535,12 +3136,12 @@ function isSecretKeyword(name) {
|
|
|
2535
3136
|
function isLoggerExpr(expr) {
|
|
2536
3137
|
switch (expr.type) {
|
|
2537
3138
|
case "Identifier":
|
|
2538
|
-
return
|
|
3139
|
+
return LOGGER_NAMES2.has(expr.name.toLowerCase());
|
|
2539
3140
|
case "MemberExpression": {
|
|
2540
3141
|
const { property, object } = expr;
|
|
2541
3142
|
if (!expr.computed && property.type === "Identifier") {
|
|
2542
3143
|
const lowered = property.name.toLowerCase();
|
|
2543
|
-
if (
|
|
3144
|
+
if (LOGGER_NAMES2.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
|
|
2544
3145
|
return true;
|
|
2545
3146
|
}
|
|
2546
3147
|
}
|
|
@@ -2578,7 +3179,7 @@ function propertyKeyName2(prop) {
|
|
|
2578
3179
|
}
|
|
2579
3180
|
return null;
|
|
2580
3181
|
}
|
|
2581
|
-
var no_secret_in_log_default =
|
|
3182
|
+
var no_secret_in_log_default = import_utils25.ESLintUtils.RuleCreator(
|
|
2582
3183
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
2583
3184
|
)({
|
|
2584
3185
|
name: "no-secret-in-log",
|
|
@@ -2597,7 +3198,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
2597
3198
|
return {
|
|
2598
3199
|
CallExpression(node) {
|
|
2599
3200
|
const callee = node.callee;
|
|
2600
|
-
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !
|
|
3201
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS2.has(callee.property.name)) {
|
|
2601
3202
|
return;
|
|
2602
3203
|
}
|
|
2603
3204
|
if (!isLoggerExpr(callee.object)) {
|
|
@@ -2614,6 +3215,16 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
2614
3215
|
}
|
|
2615
3216
|
continue;
|
|
2616
3217
|
}
|
|
3218
|
+
if (arg.type === "MemberExpression") {
|
|
3219
|
+
if (!arg.computed && arg.property.type === "Identifier" && isSecretKeyword(arg.property.name)) {
|
|
3220
|
+
context.report({
|
|
3221
|
+
node: arg,
|
|
3222
|
+
messageId: "noSecretInLog",
|
|
3223
|
+
data: { name: arg.property.name }
|
|
3224
|
+
});
|
|
3225
|
+
}
|
|
3226
|
+
continue;
|
|
3227
|
+
}
|
|
2617
3228
|
if (arg.type === "ObjectExpression") {
|
|
2618
3229
|
for (const prop of arg.properties) {
|
|
2619
3230
|
if (prop.type !== "Property") {
|
|
@@ -2636,7 +3247,7 @@ var no_secret_in_log_default = import_utils24.ESLintUtils.RuleCreator(
|
|
|
2636
3247
|
});
|
|
2637
3248
|
|
|
2638
3249
|
// src/rules/prefer-string-literal-union.ts
|
|
2639
|
-
var
|
|
3250
|
+
var import_utils26 = require("@typescript-eslint/utils");
|
|
2640
3251
|
var ts = __toESM(require("typescript"), 1);
|
|
2641
3252
|
var CHOICE_TOKENS = /* @__PURE__ */ new Set([
|
|
2642
3253
|
"status",
|
|
@@ -2679,19 +3290,19 @@ function isChoiceLikeName(name) {
|
|
|
2679
3290
|
return CHOICE_TOKENS.has(lastWord(name));
|
|
2680
3291
|
}
|
|
2681
3292
|
function keyName(key) {
|
|
2682
|
-
if (key.type ===
|
|
3293
|
+
if (key.type === import_utils26.AST_NODE_TYPES.Identifier) {
|
|
2683
3294
|
return key.name;
|
|
2684
3295
|
}
|
|
2685
|
-
if (key.type ===
|
|
3296
|
+
if (key.type === import_utils26.AST_NODE_TYPES.Literal && typeof key.value === "string") {
|
|
2686
3297
|
return key.value;
|
|
2687
3298
|
}
|
|
2688
3299
|
return null;
|
|
2689
3300
|
}
|
|
2690
3301
|
function isStringLiteralMember(t) {
|
|
2691
|
-
return t.type ===
|
|
3302
|
+
return t.type === import_utils26.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils26.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
|
|
2692
3303
|
}
|
|
2693
3304
|
function isStringLiteralUnion(node) {
|
|
2694
|
-
if (node?.type !==
|
|
3305
|
+
if (node?.type !== import_utils26.AST_NODE_TYPES.TSUnionType) {
|
|
2695
3306
|
return false;
|
|
2696
3307
|
}
|
|
2697
3308
|
return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
|
|
@@ -2720,12 +3331,12 @@ function bindingSourceExpression(decl) {
|
|
|
2720
3331
|
return ts.isForOfStatement(node) ? node.expression : node.initializer;
|
|
2721
3332
|
}
|
|
2722
3333
|
function refKey(node) {
|
|
2723
|
-
if (node.type ===
|
|
3334
|
+
if (node.type === import_utils26.AST_NODE_TYPES.Identifier) {
|
|
2724
3335
|
return node.name;
|
|
2725
3336
|
}
|
|
2726
|
-
if (node.type ===
|
|
3337
|
+
if (node.type === import_utils26.AST_NODE_TYPES.MemberExpression && !node.computed) {
|
|
2727
3338
|
const inner = refKey(node.object);
|
|
2728
|
-
if (inner === null || node.property.type !==
|
|
3339
|
+
if (inner === null || node.property.type !== import_utils26.AST_NODE_TYPES.Identifier) {
|
|
2729
3340
|
return null;
|
|
2730
3341
|
}
|
|
2731
3342
|
return `${inner}.${node.property.name}`;
|
|
@@ -2733,12 +3344,12 @@ function refKey(node) {
|
|
|
2733
3344
|
return null;
|
|
2734
3345
|
}
|
|
2735
3346
|
function strLiteral(node) {
|
|
2736
|
-
if (node.type ===
|
|
3347
|
+
if (node.type === import_utils26.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
2737
3348
|
return node.value;
|
|
2738
3349
|
}
|
|
2739
3350
|
return null;
|
|
2740
3351
|
}
|
|
2741
|
-
var prefer_string_literal_union_default =
|
|
3352
|
+
var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator(
|
|
2742
3353
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
2743
3354
|
)({
|
|
2744
3355
|
name: "prefer-string-literal-union",
|
|
@@ -2762,7 +3373,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
|
|
|
2762
3373
|
}
|
|
2763
3374
|
let services;
|
|
2764
3375
|
try {
|
|
2765
|
-
services =
|
|
3376
|
+
services = import_utils26.ESLintUtils.getParserServices(context);
|
|
2766
3377
|
} catch {
|
|
2767
3378
|
services = null;
|
|
2768
3379
|
}
|
|
@@ -2846,7 +3457,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
|
|
|
2846
3457
|
containersWithUnion.add(container);
|
|
2847
3458
|
return;
|
|
2848
3459
|
}
|
|
2849
|
-
if (typeNode?.type !==
|
|
3460
|
+
if (typeNode?.type !== import_utils26.AST_NODE_TYPES.TSStringKeyword) {
|
|
2850
3461
|
return;
|
|
2851
3462
|
}
|
|
2852
3463
|
const name = keyName(key);
|
|
@@ -2934,10 +3545,10 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
|
|
|
2934
3545
|
}
|
|
2935
3546
|
};
|
|
2936
3547
|
function refKeyText(node) {
|
|
2937
|
-
if (node.type ===
|
|
3548
|
+
if (node.type === import_utils26.AST_NODE_TYPES.BinaryExpression) {
|
|
2938
3549
|
return refKey(node.left) ?? refKey(node.right) ?? "value";
|
|
2939
3550
|
}
|
|
2940
|
-
if (node.type ===
|
|
3551
|
+
if (node.type === import_utils26.AST_NODE_TYPES.SwitchStatement) {
|
|
2941
3552
|
return refKey(node.discriminant) ?? "value";
|
|
2942
3553
|
}
|
|
2943
3554
|
return "value";
|
|
@@ -2946,7 +3557,7 @@ var prefer_string_literal_union_default = import_utils25.ESLintUtils.RuleCreator
|
|
|
2946
3557
|
});
|
|
2947
3558
|
|
|
2948
3559
|
// src/rules/single-public-export.ts
|
|
2949
|
-
var
|
|
3560
|
+
var import_utils27 = require("@typescript-eslint/utils");
|
|
2950
3561
|
var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
|
|
2951
3562
|
"util",
|
|
2952
3563
|
"utils",
|
|
@@ -2973,20 +3584,20 @@ var TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
|
|
|
2973
3584
|
var SCRIPT_EXT_RE = /\.[cm]?[jt]sx?$/i;
|
|
2974
3585
|
var basename = (filename) => filename.split(/[/\\]/).pop() ?? filename;
|
|
2975
3586
|
var stemOf = (base) => base.replace(SCRIPT_EXT_RE, "");
|
|
2976
|
-
var
|
|
3587
|
+
var kebabCase2 = (name) => {
|
|
2977
3588
|
let normalized = name;
|
|
2978
3589
|
for (const [pattern, replacement] of ACRONYM_OVERRIDES) {
|
|
2979
3590
|
normalized = normalized.replace(pattern, replacement);
|
|
2980
3591
|
}
|
|
2981
3592
|
return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
|
|
2982
3593
|
};
|
|
2983
|
-
var
|
|
3594
|
+
var isFunctionExpression = (node) => node !== null && (node.type === import_utils27.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils27.AST_NODE_TYPES.FunctionExpression);
|
|
2984
3595
|
var functionConstName = (decl) => {
|
|
2985
3596
|
if (decl.declarations.length !== 1) return null;
|
|
2986
3597
|
const [declarator] = decl.declarations;
|
|
2987
3598
|
if (declarator === void 0) return null;
|
|
2988
|
-
if (declarator.id.type !==
|
|
2989
|
-
if (!
|
|
3599
|
+
if (declarator.id.type !== import_utils27.AST_NODE_TYPES.Identifier) return null;
|
|
3600
|
+
if (!isFunctionExpression(declarator.init)) return null;
|
|
2990
3601
|
return declarator.id.name;
|
|
2991
3602
|
};
|
|
2992
3603
|
var summarizeExports = (body) => {
|
|
@@ -2999,20 +3610,20 @@ var summarizeExports = (body) => {
|
|
|
2999
3610
|
};
|
|
3000
3611
|
for (const statement of body) {
|
|
3001
3612
|
switch (statement.type) {
|
|
3002
|
-
case
|
|
3613
|
+
case import_utils27.AST_NODE_TYPES.ExportAllDeclaration:
|
|
3003
3614
|
hasReExport = true;
|
|
3004
3615
|
break;
|
|
3005
|
-
case
|
|
3616
|
+
case import_utils27.AST_NODE_TYPES.ExportDefaultDeclaration: {
|
|
3006
3617
|
names += 1;
|
|
3007
3618
|
const decl = statement.declaration;
|
|
3008
|
-
if (decl.type ===
|
|
3619
|
+
if (decl.type === import_utils27.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
|
|
3009
3620
|
candidate = { name: decl.id.name, node: statement };
|
|
3010
|
-
} else if (decl.type ===
|
|
3621
|
+
} else if (decl.type === import_utils27.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
|
|
3011
3622
|
candidate = { name: decl.id.name, node: statement };
|
|
3012
3623
|
}
|
|
3013
3624
|
break;
|
|
3014
3625
|
}
|
|
3015
|
-
case
|
|
3626
|
+
case import_utils27.AST_NODE_TYPES.ExportNamedDeclaration: {
|
|
3016
3627
|
if (statement.source !== null) {
|
|
3017
3628
|
hasReExport = true;
|
|
3018
3629
|
break;
|
|
@@ -3023,15 +3634,15 @@ var summarizeExports = (body) => {
|
|
|
3023
3634
|
break;
|
|
3024
3635
|
}
|
|
3025
3636
|
switch (decl.type) {
|
|
3026
|
-
case
|
|
3637
|
+
case import_utils27.AST_NODE_TYPES.FunctionDeclaration:
|
|
3027
3638
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3028
3639
|
else names += 1;
|
|
3029
3640
|
break;
|
|
3030
|
-
case
|
|
3641
|
+
case import_utils27.AST_NODE_TYPES.ClassDeclaration:
|
|
3031
3642
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3032
3643
|
else names += 1;
|
|
3033
3644
|
break;
|
|
3034
|
-
case
|
|
3645
|
+
case import_utils27.AST_NODE_TYPES.VariableDeclaration: {
|
|
3035
3646
|
const fnName = functionConstName(decl);
|
|
3036
3647
|
if (fnName !== null && decl.declarations.length === 1) {
|
|
3037
3648
|
addCandidate(fnName, statement);
|
|
@@ -3051,7 +3662,7 @@ var summarizeExports = (body) => {
|
|
|
3051
3662
|
}
|
|
3052
3663
|
return { names, hasReExport, candidate };
|
|
3053
3664
|
};
|
|
3054
|
-
var single_public_export_default =
|
|
3665
|
+
var single_public_export_default = import_utils27.ESLintUtils.RuleCreator(
|
|
3055
3666
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
3056
3667
|
)({
|
|
3057
3668
|
name: "single-public-export",
|
|
@@ -3078,7 +3689,7 @@ var single_public_export_default = import_utils26.ESLintUtils.RuleCreator(
|
|
|
3078
3689
|
if (hasReExport) return;
|
|
3079
3690
|
if (names !== 1 || candidate === null) return;
|
|
3080
3691
|
if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;
|
|
3081
|
-
const expected =
|
|
3692
|
+
const expected = kebabCase2(candidate.name);
|
|
3082
3693
|
if (stem === expected) return;
|
|
3083
3694
|
context.report({
|
|
3084
3695
|
node: candidate.node,
|
|
@@ -3121,7 +3732,7 @@ var rules = {
|
|
|
3121
3732
|
var plugin = {
|
|
3122
3733
|
meta: {
|
|
3123
3734
|
name: "@sarj/eslint-plugin",
|
|
3124
|
-
version: "2.
|
|
3735
|
+
version: "2.4.1"
|
|
3125
3736
|
},
|
|
3126
3737
|
rules,
|
|
3127
3738
|
configs: {
|