@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.js
CHANGED
|
@@ -1,42 +1,19 @@
|
|
|
1
1
|
// src/rules/enforce-file-structure.ts
|
|
2
2
|
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
|
-
var
|
|
4
|
-
declarations: 0,
|
|
5
|
-
functions: 1,
|
|
6
|
-
exports: 2
|
|
7
|
-
};
|
|
8
|
-
var SECTION_NAMES = ["declarations", "functions", "exports"];
|
|
9
|
-
var sectionName = (ordinal) => {
|
|
10
|
-
const name = SECTION_NAMES[ordinal];
|
|
11
|
-
return name ?? "unknown";
|
|
12
|
-
};
|
|
13
|
-
var SERVER_ACTION_FILE_RE = /(?:^|\/)actions\/|\.action\.[jt]sx?$|(?:^|\/)actions\.[jt]sx?$/;
|
|
14
|
-
var isFunctionExpression = (node) => node.type === AST_NODE_TYPES.ArrowFunctionExpression || node.type === AST_NODE_TYPES.FunctionExpression;
|
|
15
|
-
var isFunctionLikeVariable = (statement) => statement.declarations.length > 0 && statement.declarations.every(
|
|
16
|
-
(decl) => decl.init !== null && isFunctionExpression(decl.init)
|
|
17
|
-
);
|
|
18
|
-
var getStatementSection = (statement) => {
|
|
3
|
+
var classifyStatement = (statement) => {
|
|
19
4
|
switch (statement.type) {
|
|
20
5
|
case AST_NODE_TYPES.ImportDeclaration:
|
|
21
|
-
|
|
22
|
-
case AST_NODE_TYPES.TSInterfaceDeclaration:
|
|
23
|
-
case AST_NODE_TYPES.TSEnumDeclaration:
|
|
24
|
-
case AST_NODE_TYPES.ClassDeclaration:
|
|
25
|
-
return SECTION.declarations;
|
|
26
|
-
case AST_NODE_TYPES.VariableDeclaration:
|
|
27
|
-
return isFunctionLikeVariable(statement) ? SECTION.functions : SECTION.declarations;
|
|
28
|
-
case AST_NODE_TYPES.FunctionDeclaration:
|
|
29
|
-
return SECTION.functions;
|
|
30
|
-
case AST_NODE_TYPES.ExportNamedDeclaration:
|
|
31
|
-
case AST_NODE_TYPES.ExportDefaultDeclaration:
|
|
6
|
+
return "import";
|
|
32
7
|
case AST_NODE_TYPES.ExportAllDeclaration:
|
|
33
|
-
return
|
|
8
|
+
return "reexport";
|
|
9
|
+
case AST_NODE_TYPES.ExportNamedDeclaration:
|
|
10
|
+
return statement.declaration === null ? "reexport" : "body";
|
|
34
11
|
default:
|
|
35
|
-
return
|
|
12
|
+
return "body";
|
|
36
13
|
}
|
|
37
14
|
};
|
|
15
|
+
var isStringDirective = (statement) => statement.type === AST_NODE_TYPES.ExpressionStatement && statement.expression.type === AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ");
|
|
38
16
|
var isUseServerDirective = (statement) => {
|
|
39
|
-
if (statement === void 0) return false;
|
|
40
17
|
if (statement.type !== AST_NODE_TYPES.ExpressionStatement) return false;
|
|
41
18
|
const expr = statement.expression;
|
|
42
19
|
if (expr.type !== AST_NODE_TYPES.Literal) return false;
|
|
@@ -49,47 +26,45 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
49
26
|
meta: {
|
|
50
27
|
type: "suggestion",
|
|
51
28
|
docs: {
|
|
52
|
-
description: "
|
|
29
|
+
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."
|
|
53
30
|
},
|
|
54
31
|
schema: [],
|
|
55
32
|
messages: {
|
|
56
|
-
|
|
57
|
-
useServerDirective: "
|
|
33
|
+
importsFirst: "File structure violation: import statements must come before other declarations",
|
|
34
|
+
useServerDirective: "A 'use server' directive must be the first statement in the file"
|
|
58
35
|
}
|
|
59
36
|
},
|
|
60
37
|
defaultOptions: [],
|
|
61
38
|
create(context) {
|
|
62
|
-
const filename = context.filename;
|
|
63
|
-
const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
|
|
64
39
|
return {
|
|
65
40
|
Program(node) {
|
|
66
41
|
const body = node.body;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
42
|
+
const misplacedUseServer = body.find(
|
|
43
|
+
(statement, index) => index > 0 && isUseServerDirective(statement)
|
|
44
|
+
);
|
|
45
|
+
if (misplacedUseServer !== void 0) {
|
|
46
|
+
context.report({
|
|
47
|
+
node: misplacedUseServer,
|
|
48
|
+
messageId: "useServerDirective"
|
|
49
|
+
});
|
|
75
50
|
}
|
|
76
|
-
let
|
|
51
|
+
let seenBody = false;
|
|
77
52
|
for (const statement of body) {
|
|
78
|
-
if (statement
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
53
|
+
if (isStringDirective(statement)) continue;
|
|
54
|
+
switch (classifyStatement(statement)) {
|
|
55
|
+
case "reexport":
|
|
56
|
+
continue;
|
|
57
|
+
case "body":
|
|
58
|
+
seenBody = true;
|
|
59
|
+
continue;
|
|
60
|
+
case "import":
|
|
61
|
+
if (seenBody) {
|
|
62
|
+
context.report({
|
|
63
|
+
node: statement,
|
|
64
|
+
messageId: "importsFirst"
|
|
65
|
+
});
|
|
89
66
|
}
|
|
90
|
-
|
|
91
|
-
} else if (statementSection > currentSection) {
|
|
92
|
-
currentSection = statementSection;
|
|
67
|
+
continue;
|
|
93
68
|
}
|
|
94
69
|
}
|
|
95
70
|
}
|
|
@@ -238,7 +213,7 @@ var no_client_side_data_fetching_default = ESLintUtils2.RuleCreator(
|
|
|
238
213
|
// src/rules/no-comment-cruft.ts
|
|
239
214
|
import { ESLintUtils as ESLintUtils3 } from "@typescript-eslint/utils";
|
|
240
215
|
var LEADING_PREAMBLE_MIN = 4;
|
|
241
|
-
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;
|
|
216
|
+
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;
|
|
242
217
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
243
218
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
244
219
|
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
@@ -246,8 +221,9 @@ var REGION_RE = /^#?(?:end)?region\b/i;
|
|
|
246
221
|
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\.)/;
|
|
247
222
|
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
248
223
|
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
224
|
+
var PSEUDOCODE_RE = /%\w+%|\[opt\]|(?:^|\s)<[A-Za-z]\w*>|…|\.\.\./;
|
|
249
225
|
function stripCommentMarker(line) {
|
|
250
|
-
return line.replace(/^\s
|
|
226
|
+
return line.replace(/^\s*\/{1,2}/, "").replace(/^\s*\*+/, "").trim();
|
|
251
227
|
}
|
|
252
228
|
function isDirective(text) {
|
|
253
229
|
return DIRECTIVE_RE.test(text.trim());
|
|
@@ -263,6 +239,30 @@ function looksLikeCode(text) {
|
|
|
263
239
|
if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
|
|
264
240
|
return CALL_OR_ASSIGN_RE.test(t);
|
|
265
241
|
}
|
|
242
|
+
function hasPseudocode(text) {
|
|
243
|
+
return PSEUDOCODE_RE.test(text);
|
|
244
|
+
}
|
|
245
|
+
function isProse(text) {
|
|
246
|
+
const t = text.trim();
|
|
247
|
+
if (!t) return false;
|
|
248
|
+
if (t.endsWith(":")) return true;
|
|
249
|
+
if (/[.!?]$/.test(t) && /\s/.test(t) && /[a-z]/.test(t) && !looksLikeCode(t) && t.split(/\s+/).length >= 3) {
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
function hasCommentedOutCode(texts, precedingProse) {
|
|
255
|
+
for (let i = 0; i < texts.length; i++) {
|
|
256
|
+
const line = texts[i];
|
|
257
|
+
if (line === void 0 || !looksLikeCode(line) || hasPseudocode(line)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const prev = i > 0 ? texts[i - 1] : void 0;
|
|
261
|
+
if (prev !== void 0 ? isProse(prev) : precedingProse) continue;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
266
|
var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
267
267
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
268
268
|
)({
|
|
@@ -317,12 +317,19 @@ var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
|
317
317
|
Program() {
|
|
318
318
|
const comments = sourceCode.getAllComments();
|
|
319
319
|
const firstCodeLine = sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;
|
|
320
|
-
for (
|
|
320
|
+
for (let i = 0; i < comments.length; i++) {
|
|
321
|
+
const comment = comments[i];
|
|
322
|
+
if (comment === void 0) continue;
|
|
321
323
|
if (isJsDoc(comment) || !isStandalone(comment)) continue;
|
|
324
|
+
if (LICENSE_RE.test(comment.value)) continue;
|
|
322
325
|
const texts = comment.value.split("\n").map(stripCommentMarker).filter((l) => l.length > 0 && !isDirective(l));
|
|
323
326
|
if (texts.some(isBanner)) {
|
|
324
327
|
context.report({ node: comment, messageId: "sectionBanner" });
|
|
325
|
-
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const prev = comments[i - 1];
|
|
331
|
+
const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
|
|
332
|
+
if (hasCommentedOutCode(texts, precedingProse)) {
|
|
326
333
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
327
334
|
}
|
|
328
335
|
}
|
|
@@ -404,7 +411,9 @@ var no_enum_default = ESLintUtils4.RuleCreator(
|
|
|
404
411
|
|
|
405
412
|
// src/rules/no-insecure-random-id.ts
|
|
406
413
|
import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
|
|
407
|
-
var
|
|
414
|
+
var STRONG_SECURITY_PATTERN = /token|secret|csrf|password|passwd|apikey|api[-_]?key|nonce|salt|uuid|authid/i;
|
|
415
|
+
var NON_SECURITY_ID_PATTERN = /temp|tmp|cache|correlation|request|req|trace|execution|dev|hmr|mock|test|perf|marker/i;
|
|
416
|
+
var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
|
|
408
417
|
function isMathRandomCall(node) {
|
|
409
418
|
if (node.type !== "CallExpression") {
|
|
410
419
|
return false;
|
|
@@ -416,6 +425,24 @@ function isMathRandomCall(node) {
|
|
|
416
425
|
const { object, property } = callee;
|
|
417
426
|
return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
|
|
418
427
|
}
|
|
428
|
+
function climbValueChain(node) {
|
|
429
|
+
let current = node;
|
|
430
|
+
let parent = current.parent;
|
|
431
|
+
while (parent) {
|
|
432
|
+
if (parent.type === "MemberExpression" && parent.object === current && !parent.computed) {
|
|
433
|
+
current = parent;
|
|
434
|
+
parent = current.parent;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
438
|
+
current = parent;
|
|
439
|
+
parent = current.parent;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
return current;
|
|
445
|
+
}
|
|
419
446
|
function isPartOfToString36Chain(node) {
|
|
420
447
|
let current = node;
|
|
421
448
|
let parent = current.parent;
|
|
@@ -481,6 +508,49 @@ function findEnclosingName(node) {
|
|
|
481
508
|
}
|
|
482
509
|
return void 0;
|
|
483
510
|
}
|
|
511
|
+
function collectStaticStringParts(node, out) {
|
|
512
|
+
if (node.type === "Literal" && typeof node.value === "string") {
|
|
513
|
+
out.push(node.value);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (node.type === "TemplateLiteral") {
|
|
517
|
+
for (const quasi of node.quasis) {
|
|
518
|
+
out.push(quasi.value.cooked ?? quasi.value.raw);
|
|
519
|
+
}
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
523
|
+
collectStaticStringParts(node.left, out);
|
|
524
|
+
collectStaticStringParts(node.right, out);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function isConcatenatedIntoPathOrDomId(node) {
|
|
528
|
+
const valueNode = climbValueChain(node);
|
|
529
|
+
let current = valueNode;
|
|
530
|
+
let parent = current.parent;
|
|
531
|
+
let top;
|
|
532
|
+
while (parent) {
|
|
533
|
+
if (parent.type === "BinaryExpression" && parent.operator === "+" && (parent.left === current || parent.right === current)) {
|
|
534
|
+
top = parent;
|
|
535
|
+
current = parent;
|
|
536
|
+
parent = current.parent;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
if (parent.type === "TemplateLiteral") {
|
|
540
|
+
top = parent;
|
|
541
|
+
current = parent;
|
|
542
|
+
parent = current.parent;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
if (!top) {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
const parts = [];
|
|
551
|
+
collectStaticStringParts(top, parts);
|
|
552
|
+
return parts.some((part) => PATH_OR_DOM_MARKER.test(part));
|
|
553
|
+
}
|
|
484
554
|
var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
485
555
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
486
556
|
)({
|
|
@@ -502,12 +572,18 @@ var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
|
502
572
|
if (!isMathRandomCall(node)) {
|
|
503
573
|
return;
|
|
504
574
|
}
|
|
505
|
-
|
|
575
|
+
const name = findEnclosingName(node);
|
|
576
|
+
if (name !== void 0 && STRONG_SECURITY_PATTERN.test(name)) {
|
|
506
577
|
context.report({ node, messageId: "insecureRandomId" });
|
|
507
578
|
return;
|
|
508
579
|
}
|
|
509
|
-
|
|
510
|
-
|
|
580
|
+
if (name !== void 0 && NON_SECURITY_ID_PATTERN.test(name)) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (isConcatenatedIntoPathOrDomId(node)) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (isPartOfToString36Chain(node)) {
|
|
511
587
|
context.report({ node, messageId: "insecureRandomId" });
|
|
512
588
|
}
|
|
513
589
|
}
|
|
@@ -518,6 +594,8 @@ var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
|
518
594
|
// src/rules/no-json-stringify-error.ts
|
|
519
595
|
import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
|
|
520
596
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
597
|
+
var ERROR_PROP_PATTERN = /^(cause|lastError|error|err|exception|originalError|innerError)$/i;
|
|
598
|
+
var SAFE_STRING_PROPS = /* @__PURE__ */ new Set(["message", "stack", "name"]);
|
|
521
599
|
function isCatchBinding(scope, name) {
|
|
522
600
|
let current = scope;
|
|
523
601
|
while (current) {
|
|
@@ -533,6 +611,98 @@ function isCatchBinding(scope, name) {
|
|
|
533
611
|
}
|
|
534
612
|
return false;
|
|
535
613
|
}
|
|
614
|
+
function memberSuggestsError(member, scope) {
|
|
615
|
+
const propName2 = !member.computed && member.property.type === "Identifier" ? member.property.name : null;
|
|
616
|
+
if (propName2 !== null && ERROR_PROP_PATTERN.test(propName2)) {
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
619
|
+
const base = member.object;
|
|
620
|
+
const baseSuggestsError = base.type === "Identifier" && (ERROR_NAME_PATTERN.test(base.name) || isCatchBinding(scope, base.name));
|
|
621
|
+
if (baseSuggestsError) {
|
|
622
|
+
return propName2 === null || !SAFE_STRING_PROPS.has(propName2.toLowerCase());
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
function instanceofErrorSubject(test) {
|
|
627
|
+
if (test.type === "BinaryExpression" && test.operator === "instanceof" && test.right.type === "Identifier" && test.right.name === "Error") {
|
|
628
|
+
return test.left;
|
|
629
|
+
}
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
var TYPE_GUARD_PATTERN = /^(is|has)[A-Z]/;
|
|
633
|
+
function typeGuardSubject(test) {
|
|
634
|
+
const arg = test.type === "CallExpression" ? test.arguments[0] : void 0;
|
|
635
|
+
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") {
|
|
636
|
+
return arg;
|
|
637
|
+
}
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
function positiveErrorSubject(test) {
|
|
641
|
+
return instanceofErrorSubject(test) ?? typeGuardSubject(test);
|
|
642
|
+
}
|
|
643
|
+
function negatedInstanceofErrorSubject(test) {
|
|
644
|
+
if (test.type === "UnaryExpression" && test.operator === "!") {
|
|
645
|
+
return positiveErrorSubject(test.argument);
|
|
646
|
+
}
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
function branchTerminates(branch) {
|
|
650
|
+
const body = branch.type === "BlockStatement" ? branch.body : [branch];
|
|
651
|
+
const last = body[body.length - 1];
|
|
652
|
+
return last !== void 0 && (last.type === "ReturnStatement" || last.type === "ThrowStatement");
|
|
653
|
+
}
|
|
654
|
+
function isNarrowedByEarlyReturn(node, argExpr, sourceCode) {
|
|
655
|
+
const argText = sourceCode.getText(argExpr);
|
|
656
|
+
let current = node.parent;
|
|
657
|
+
while (current) {
|
|
658
|
+
if (current.type === "BlockStatement" || current.type === "Program") {
|
|
659
|
+
for (const stmt of current.body) {
|
|
660
|
+
if (stmt.range[0] >= node.range[0]) {
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
if (stmt.type === "IfStatement" && stmt.alternate === null && branchTerminates(stmt.consequent)) {
|
|
664
|
+
const subject = positiveErrorSubject(stmt.test);
|
|
665
|
+
if (subject && sourceCode.getText(subject) === argText) {
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
current = current.parent;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
function nodeWithin(node, container) {
|
|
676
|
+
return container !== null && node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
|
|
677
|
+
}
|
|
678
|
+
function isGuardedByInstanceofError(node, argExpr, sourceCode) {
|
|
679
|
+
const argText = sourceCode.getText(argExpr);
|
|
680
|
+
const sameSubject = (subject) => sourceCode.getText(subject) === argText;
|
|
681
|
+
let current = node.parent;
|
|
682
|
+
while (current) {
|
|
683
|
+
if (current.type === "ConditionalExpression") {
|
|
684
|
+
const subject = positiveErrorSubject(current.test);
|
|
685
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
689
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
} else if (current.type === "IfStatement") {
|
|
693
|
+
const subject = positiveErrorSubject(current.test);
|
|
694
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
698
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
current = current.parent;
|
|
703
|
+
}
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
536
706
|
function isJsonStringify(callee) {
|
|
537
707
|
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
538
708
|
}
|
|
@@ -558,17 +728,28 @@ var no_json_stringify_error_default = ESLintUtils6.RuleCreator(
|
|
|
558
728
|
return;
|
|
559
729
|
}
|
|
560
730
|
const firstArg = node.arguments[0];
|
|
561
|
-
if (!firstArg
|
|
731
|
+
if (!firstArg) {
|
|
562
732
|
return;
|
|
563
733
|
}
|
|
564
|
-
const name = firstArg.name;
|
|
565
734
|
const scope = context.sourceCode.getScope(firstArg);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
735
|
+
let suggestsError;
|
|
736
|
+
if (firstArg.type === "Identifier") {
|
|
737
|
+
suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
|
|
738
|
+
} else if (firstArg.type === "MemberExpression") {
|
|
739
|
+
suggestsError = memberSuggestsError(firstArg, scope);
|
|
740
|
+
} else {
|
|
741
|
+
return;
|
|
571
742
|
}
|
|
743
|
+
if (!suggestsError) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (isGuardedByInstanceofError(node, firstArg, context.sourceCode) || isNarrowedByEarlyReturn(node, firstArg, context.sourceCode)) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
context.report({
|
|
750
|
+
node,
|
|
751
|
+
messageId: "noJsonStringifyError"
|
|
752
|
+
});
|
|
572
753
|
}
|
|
573
754
|
};
|
|
574
755
|
}
|
|
@@ -576,41 +757,81 @@ var no_json_stringify_error_default = ESLintUtils6.RuleCreator(
|
|
|
576
757
|
|
|
577
758
|
// src/rules/no-log-only-catch.ts
|
|
578
759
|
import { ESLintUtils as ESLintUtils7 } from "@typescript-eslint/utils";
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
var CONSOLE_METHODS = /* @__PURE__ */ new Set([
|
|
585
|
-
"log",
|
|
586
|
-
"error",
|
|
587
|
-
"warn",
|
|
760
|
+
|
|
761
|
+
// src/rules/_logging.ts
|
|
762
|
+
import "@typescript-eslint/utils";
|
|
763
|
+
var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
764
|
+
"debug",
|
|
588
765
|
"info",
|
|
589
|
-
"
|
|
766
|
+
"warn",
|
|
767
|
+
"warning",
|
|
768
|
+
"error",
|
|
769
|
+
"exception",
|
|
770
|
+
"critical",
|
|
771
|
+
"trace",
|
|
772
|
+
"log",
|
|
773
|
+
"fatal",
|
|
774
|
+
"success"
|
|
590
775
|
]);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
776
|
+
var LOGGER_NAMES = /* @__PURE__ */ new Set([
|
|
777
|
+
"logger",
|
|
778
|
+
"log",
|
|
779
|
+
"logging",
|
|
780
|
+
"loguru",
|
|
781
|
+
"console",
|
|
782
|
+
"_logger",
|
|
783
|
+
"_log"
|
|
784
|
+
]);
|
|
785
|
+
var REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;
|
|
786
|
+
function isLoggerReceiver(expr) {
|
|
787
|
+
switch (expr.type) {
|
|
788
|
+
case "Identifier":
|
|
789
|
+
return LOGGER_NAMES.has(expr.name.toLowerCase());
|
|
790
|
+
case "MemberExpression": {
|
|
791
|
+
const { property, object } = expr;
|
|
792
|
+
if (!expr.computed && property.type === "Identifier" && LOGGER_NAMES.has(property.name.toLowerCase())) {
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
return isLoggerReceiver(object);
|
|
796
|
+
}
|
|
797
|
+
default:
|
|
798
|
+
return false;
|
|
594
799
|
}
|
|
595
|
-
|
|
800
|
+
}
|
|
801
|
+
function isLoggingCall(expr) {
|
|
596
802
|
if (expr.type !== "CallExpression") {
|
|
597
803
|
return false;
|
|
598
804
|
}
|
|
599
805
|
const callee = expr.callee;
|
|
600
|
-
if (callee.type !== "MemberExpression") {
|
|
806
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier") {
|
|
601
807
|
return false;
|
|
602
808
|
}
|
|
603
|
-
|
|
604
|
-
if (object.type !== "Identifier" || object.name !== "console") {
|
|
809
|
+
if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {
|
|
605
810
|
return false;
|
|
606
811
|
}
|
|
607
|
-
|
|
608
|
-
|
|
812
|
+
return isLoggerReceiver(callee.object);
|
|
813
|
+
}
|
|
814
|
+
function calleeName(callee) {
|
|
815
|
+
if (callee.type === "Identifier") {
|
|
816
|
+
return callee.name;
|
|
817
|
+
}
|
|
818
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
819
|
+
return callee.property.name;
|
|
609
820
|
}
|
|
610
|
-
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/rules/no-log-only-catch.ts
|
|
825
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
826
|
+
/\.test\./,
|
|
827
|
+
/\.spec\./,
|
|
828
|
+
/[\\/]__tests__[\\/]/
|
|
829
|
+
];
|
|
830
|
+
function isLoggingCallStatement(statement) {
|
|
831
|
+
if (statement.type !== "ExpressionStatement") {
|
|
611
832
|
return false;
|
|
612
833
|
}
|
|
613
|
-
return
|
|
834
|
+
return isLoggingCall(statement.expression);
|
|
614
835
|
}
|
|
615
836
|
var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
616
837
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -619,11 +840,12 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
619
840
|
meta: {
|
|
620
841
|
type: "problem",
|
|
621
842
|
docs: {
|
|
622
|
-
description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
|
|
843
|
+
description: "Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead."
|
|
623
844
|
},
|
|
624
845
|
schema: [],
|
|
625
846
|
messages: {
|
|
626
|
-
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
|
|
847
|
+
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real.",
|
|
848
|
+
emptyCatch: "Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore."
|
|
627
849
|
}
|
|
628
850
|
},
|
|
629
851
|
defaultOptions: [],
|
|
@@ -639,13 +861,16 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
639
861
|
CatchClause(node) {
|
|
640
862
|
const statements = node.body.body;
|
|
641
863
|
if (statements.length === 0) {
|
|
642
|
-
context.
|
|
864
|
+
if (context.sourceCode.getCommentsInside(node.body).length > 0) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
context.report({ node, messageId: "emptyCatch" });
|
|
643
868
|
return;
|
|
644
869
|
}
|
|
645
|
-
const
|
|
646
|
-
(statement) =>
|
|
870
|
+
const everyStatementIsLogging = statements.every(
|
|
871
|
+
(statement) => isLoggingCallStatement(statement)
|
|
647
872
|
);
|
|
648
|
-
if (
|
|
873
|
+
if (everyStatementIsLogging) {
|
|
649
874
|
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
650
875
|
}
|
|
651
876
|
}
|
|
@@ -655,6 +880,23 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
655
880
|
|
|
656
881
|
// src/rules/no-raw-env.ts
|
|
657
882
|
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
883
|
+
function isProcessEnv(node) {
|
|
884
|
+
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
885
|
+
}
|
|
886
|
+
function isImportMetaEnv(node) {
|
|
887
|
+
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";
|
|
888
|
+
}
|
|
889
|
+
var BUILD_TIME_CONSTANTS = /* @__PURE__ */ new Set([
|
|
890
|
+
"NODE_ENV",
|
|
891
|
+
"MODE",
|
|
892
|
+
"DEV",
|
|
893
|
+
"PROD",
|
|
894
|
+
"SSR"
|
|
895
|
+
]);
|
|
896
|
+
function isBuildTimeConstantAccess(node) {
|
|
897
|
+
const parent = node.parent;
|
|
898
|
+
return parent.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier" && BUILD_TIME_CONSTANTS.has(parent.property.name);
|
|
899
|
+
}
|
|
658
900
|
var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
659
901
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
660
902
|
)({
|
|
@@ -673,10 +915,7 @@ var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
|
673
915
|
create(context) {
|
|
674
916
|
return {
|
|
675
917
|
MemberExpression(node) {
|
|
676
|
-
if (node
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
if (node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env") {
|
|
918
|
+
if ((isProcessEnv(node) || isImportMetaEnv(node)) && !isBuildTimeConstantAccess(node)) {
|
|
680
919
|
context.report({
|
|
681
920
|
node,
|
|
682
921
|
messageId: "noRawEnv"
|
|
@@ -692,6 +931,33 @@ import {
|
|
|
692
931
|
ESLintUtils as ESLintUtils9,
|
|
693
932
|
AST_NODE_TYPES as AST_NODE_TYPES3
|
|
694
933
|
} from "@typescript-eslint/utils";
|
|
934
|
+
function sentinelKind(arg) {
|
|
935
|
+
if (arg === null) {
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
if (arg.type === AST_NODE_TYPES3.Literal) {
|
|
939
|
+
if (arg.value === null) {
|
|
940
|
+
return "nullish";
|
|
941
|
+
}
|
|
942
|
+
if (typeof arg.value === "boolean") {
|
|
943
|
+
return "boolean";
|
|
944
|
+
}
|
|
945
|
+
if (typeof arg.value === "string") {
|
|
946
|
+
return "string";
|
|
947
|
+
}
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
if (arg.type === AST_NODE_TYPES3.Identifier && arg.name === "undefined") {
|
|
951
|
+
return "nullish";
|
|
952
|
+
}
|
|
953
|
+
if (arg.type === AST_NODE_TYPES3.ArrayExpression) {
|
|
954
|
+
return "array";
|
|
955
|
+
}
|
|
956
|
+
if (arg.type === AST_NODE_TYPES3.ObjectExpression) {
|
|
957
|
+
return "object";
|
|
958
|
+
}
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
695
961
|
function isSentinelArgument(arg) {
|
|
696
962
|
if (arg === null) {
|
|
697
963
|
return false;
|
|
@@ -713,17 +979,23 @@ function isSentinelArgument(arg) {
|
|
|
713
979
|
}
|
|
714
980
|
return false;
|
|
715
981
|
}
|
|
716
|
-
function
|
|
982
|
+
function isFunctionNode(node) {
|
|
983
|
+
return node.type === AST_NODE_TYPES3.FunctionDeclaration || node.type === AST_NODE_TYPES3.FunctionExpression || node.type === AST_NODE_TYPES3.ArrowFunctionExpression;
|
|
984
|
+
}
|
|
985
|
+
function isNode(value) {
|
|
986
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
987
|
+
}
|
|
988
|
+
function walkWithinScope(node, visit) {
|
|
717
989
|
let found = false;
|
|
718
|
-
const
|
|
990
|
+
const recurse = (current) => {
|
|
719
991
|
if (found) {
|
|
720
992
|
return;
|
|
721
993
|
}
|
|
722
|
-
if (current
|
|
994
|
+
if (visit(current)) {
|
|
723
995
|
found = true;
|
|
724
996
|
return;
|
|
725
997
|
}
|
|
726
|
-
if (current
|
|
998
|
+
if (isFunctionNode(current)) {
|
|
727
999
|
return;
|
|
728
1000
|
}
|
|
729
1001
|
for (const key of Object.keys(current)) {
|
|
@@ -734,19 +1006,98 @@ function containsThrow(node) {
|
|
|
734
1006
|
if (Array.isArray(value)) {
|
|
735
1007
|
for (const child of value) {
|
|
736
1008
|
if (isNode(child)) {
|
|
737
|
-
|
|
1009
|
+
recurse(child);
|
|
738
1010
|
}
|
|
739
1011
|
}
|
|
740
1012
|
} else if (isNode(value)) {
|
|
741
|
-
|
|
1013
|
+
recurse(value);
|
|
742
1014
|
}
|
|
743
1015
|
}
|
|
744
1016
|
};
|
|
745
|
-
|
|
1017
|
+
recurse(node);
|
|
746
1018
|
return found;
|
|
747
1019
|
}
|
|
748
|
-
function
|
|
749
|
-
return
|
|
1020
|
+
function containsThrow(node) {
|
|
1021
|
+
return walkWithinScope(
|
|
1022
|
+
node,
|
|
1023
|
+
(current) => current.type === AST_NODE_TYPES3.ThrowStatement
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
function argsIncludeBinding(args, caughtName) {
|
|
1027
|
+
if (caughtName === null) {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
return args.some(
|
|
1031
|
+
(arg) => arg.type === AST_NODE_TYPES3.Identifier && arg.name === caughtName
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
function logsOrReportsError(catchBody, caughtName) {
|
|
1035
|
+
return walkWithinScope(catchBody, (current) => {
|
|
1036
|
+
if (current.type !== AST_NODE_TYPES3.CallExpression) {
|
|
1037
|
+
return false;
|
|
1038
|
+
}
|
|
1039
|
+
if (isLoggingCall(current)) {
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
const name = calleeName(current.callee);
|
|
1043
|
+
return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
function tryBlockOf(catchNode) {
|
|
1047
|
+
return catchNode.parent.block;
|
|
1048
|
+
}
|
|
1049
|
+
function isSafeParseExpression(arg) {
|
|
1050
|
+
if (arg === null) {
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
if (arg.type === AST_NODE_TYPES3.CallExpression && arg.callee.type === AST_NODE_TYPES3.MemberExpression && !arg.callee.computed && arg.callee.property.type === AST_NODE_TYPES3.Identifier && arg.callee.property.name === "parse") {
|
|
1054
|
+
return true;
|
|
1055
|
+
}
|
|
1056
|
+
if (arg.type === AST_NODE_TYPES3.NewExpression && arg.callee.type === AST_NODE_TYPES3.Identifier) {
|
|
1057
|
+
return arg.callee.name === "RegExp" || arg.callee.name === "URL";
|
|
1058
|
+
}
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
function tryReturnsSafeParse(catchNode) {
|
|
1062
|
+
return walkWithinScope(
|
|
1063
|
+
tryBlockOf(catchNode),
|
|
1064
|
+
(current) => current.type === AST_NODE_TYPES3.ReturnStatement && isSafeParseExpression(current.argument)
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
function enclosingFunctionBody(node) {
|
|
1068
|
+
let current = node.parent;
|
|
1069
|
+
while (current !== void 0 && current !== null) {
|
|
1070
|
+
if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === AST_NODE_TYPES3.BlockStatement) {
|
|
1071
|
+
return current.body;
|
|
1072
|
+
}
|
|
1073
|
+
current = current.parent;
|
|
1074
|
+
}
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
1078
|
+
const functionBody = enclosingFunctionBody(catchNode);
|
|
1079
|
+
if (functionBody === null) {
|
|
1080
|
+
return false;
|
|
1081
|
+
}
|
|
1082
|
+
return walkWithinScope(functionBody, (current) => {
|
|
1083
|
+
if (current.type !== AST_NODE_TYPES3.ReturnStatement) {
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
if (isWithin(current, catchNode.body)) {
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
return sentinelKind(current.argument) === kind;
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
function isWithin(node, ancestor) {
|
|
1093
|
+
let current = node;
|
|
1094
|
+
while (current !== void 0 && current !== null) {
|
|
1095
|
+
if (current === ancestor) {
|
|
1096
|
+
return true;
|
|
1097
|
+
}
|
|
1098
|
+
current = current.parent;
|
|
1099
|
+
}
|
|
1100
|
+
return false;
|
|
750
1101
|
}
|
|
751
1102
|
var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
752
1103
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -755,11 +1106,11 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
755
1106
|
meta: {
|
|
756
1107
|
type: "problem",
|
|
757
1108
|
docs: {
|
|
758
|
-
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
|
|
1109
|
+
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."
|
|
759
1110
|
},
|
|
760
1111
|
schema: [],
|
|
761
1112
|
messages: {
|
|
762
|
-
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it,
|
|
1113
|
+
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."
|
|
763
1114
|
}
|
|
764
1115
|
},
|
|
765
1116
|
defaultOptions: [],
|
|
@@ -780,6 +1131,17 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
780
1131
|
if (containsThrow(node.body)) {
|
|
781
1132
|
return;
|
|
782
1133
|
}
|
|
1134
|
+
const caughtName = node.param?.type === AST_NODE_TYPES3.Identifier ? node.param.name : null;
|
|
1135
|
+
if (logsOrReportsError(node.body, caughtName)) {
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
if (tryReturnsSafeParse(node)) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const kind = sentinelKind(last.argument);
|
|
1142
|
+
if (kind !== null && functionReturnsSameSentinelKindElsewhere(node, kind)) {
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
783
1145
|
context.report({
|
|
784
1146
|
node: last,
|
|
785
1147
|
messageId: "noSentinelReturn"
|
|
@@ -791,12 +1153,110 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
791
1153
|
|
|
792
1154
|
// src/rules/no-sequential-await.ts
|
|
793
1155
|
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
1156
|
+
var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
|
|
1157
|
+
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain|buffer|stream|teleport|chunk|\bqueue|drain/i;
|
|
794
1158
|
function isFunctionLike(node) {
|
|
795
1159
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
796
1160
|
}
|
|
797
1161
|
function isLoop(node) {
|
|
798
1162
|
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
799
1163
|
}
|
|
1164
|
+
function isNode2(value) {
|
|
1165
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1166
|
+
}
|
|
1167
|
+
function visitScope(root, visit) {
|
|
1168
|
+
visit(root);
|
|
1169
|
+
for (const key of Object.keys(root)) {
|
|
1170
|
+
if (key === "parent") {
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
const value = root[key];
|
|
1174
|
+
const children = Array.isArray(value) ? value : [value];
|
|
1175
|
+
for (const child of children) {
|
|
1176
|
+
if (isNode2(child) && !isFunctionLike(child) && !isLoop(child)) {
|
|
1177
|
+
visitScope(child, visit);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
function collectAwaits(root) {
|
|
1183
|
+
const awaits = [];
|
|
1184
|
+
visitScope(root, (node) => {
|
|
1185
|
+
if (node.type === "AwaitExpression") {
|
|
1186
|
+
awaits.push(node);
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
return awaits;
|
|
1190
|
+
}
|
|
1191
|
+
function hasEarlyExit(root) {
|
|
1192
|
+
let found = false;
|
|
1193
|
+
visitScope(root, (node) => {
|
|
1194
|
+
if (node.type === "ReturnStatement" || node.type === "BreakStatement" || node.type === "ContinueStatement") {
|
|
1195
|
+
found = true;
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
return found;
|
|
1199
|
+
}
|
|
1200
|
+
var TIMER_HELPER_RE = /^(sleep|timeout|delay|wait|pause|tick)$/i;
|
|
1201
|
+
function calleeName2(callee) {
|
|
1202
|
+
if (callee.type === "Identifier") return callee.name;
|
|
1203
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
1204
|
+
return callee.property.name;
|
|
1205
|
+
}
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
function isTimerYield(node) {
|
|
1209
|
+
const arg = node.argument;
|
|
1210
|
+
if (arg.type === "NewExpression" && arg.callee.type === "Identifier" && arg.callee.name === "Promise") {
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1213
|
+
if (arg.type === "CallExpression") {
|
|
1214
|
+
const name = calleeName2(arg.callee);
|
|
1215
|
+
return name !== null && TIMER_HELPER_RE.test(name);
|
|
1216
|
+
}
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
var QUEUE_DRAIN_METHODS = /^(shift|pop|dequeue|next|poll)$/;
|
|
1220
|
+
function isQueueDrain(node) {
|
|
1221
|
+
const arg = node.argument;
|
|
1222
|
+
return arg.type === "CallExpression" && arg.callee.type === "MemberExpression" && !arg.callee.computed && arg.callee.property.type === "Identifier" && QUEUE_DRAIN_METHODS.test(arg.callee.property.name);
|
|
1223
|
+
}
|
|
1224
|
+
function referencesName(root, name) {
|
|
1225
|
+
let found = false;
|
|
1226
|
+
visitScope(root, (node) => {
|
|
1227
|
+
if (node.type === "Identifier" && node.name === name) {
|
|
1228
|
+
found = true;
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
return found;
|
|
1232
|
+
}
|
|
1233
|
+
function isThreadedAccumulator(node) {
|
|
1234
|
+
const parent = node.parent;
|
|
1235
|
+
let target = null;
|
|
1236
|
+
if (parent.type === "AssignmentExpression" && parent.operator === "=" && parent.right === node && parent.left.type === "Identifier") {
|
|
1237
|
+
target = parent.left.name;
|
|
1238
|
+
} else if (parent.type === "VariableDeclarator" && parent.init === node && parent.id.type === "Identifier") {
|
|
1239
|
+
target = parent.id.name;
|
|
1240
|
+
}
|
|
1241
|
+
if (target === null) {
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
return referencesName(node.argument, target);
|
|
1245
|
+
}
|
|
1246
|
+
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1247
|
+
if (awaits.length === 0) {
|
|
1248
|
+
return false;
|
|
1249
|
+
}
|
|
1250
|
+
if (earlyExit) {
|
|
1251
|
+
return false;
|
|
1252
|
+
}
|
|
1253
|
+
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1256
|
+
return awaits.some(
|
|
1257
|
+
(node) => !isTimerYield(node) && !isThreadedAccumulator(node) && !isQueueDrain(node)
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
800
1260
|
var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
801
1261
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
802
1262
|
)({
|
|
@@ -813,54 +1273,36 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
813
1273
|
},
|
|
814
1274
|
defaultOptions: [],
|
|
815
1275
|
create(context) {
|
|
816
|
-
function
|
|
817
|
-
if (node.type === "
|
|
818
|
-
return node;
|
|
1276
|
+
function loopParts(node) {
|
|
1277
|
+
if (node.type === "ForStatement") {
|
|
1278
|
+
return [node.body, node.test, node.update];
|
|
819
1279
|
}
|
|
820
|
-
if (
|
|
821
|
-
return
|
|
1280
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1281
|
+
return [node.body];
|
|
822
1282
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (Array.isArray(value)) {
|
|
829
|
-
for (const child of value) {
|
|
830
|
-
if (isNode4(child) && !isLoop(child)) {
|
|
831
|
-
const found = findAwaitInScope(child);
|
|
832
|
-
if (found) {
|
|
833
|
-
return found;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
} else if (isNode4(value) && !isLoop(value)) {
|
|
838
|
-
const found = findAwaitInScope(value);
|
|
839
|
-
if (found) {
|
|
840
|
-
return found;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
1283
|
+
return [node.body, node.test];
|
|
1284
|
+
}
|
|
1285
|
+
function iterableTextOf(node) {
|
|
1286
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1287
|
+
return context.sourceCode.getText(node.right);
|
|
843
1288
|
}
|
|
844
1289
|
return null;
|
|
845
1290
|
}
|
|
846
|
-
function isNode4(value) {
|
|
847
|
-
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
848
|
-
}
|
|
849
1291
|
function checkLoop(node) {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if (part && !isLoop(part) && findAwaitInScope(part)) {
|
|
860
|
-
context.report({ node, messageId: "noSequentialAwait" });
|
|
861
|
-
return;
|
|
1292
|
+
const awaits = [];
|
|
1293
|
+
let earlyExit = false;
|
|
1294
|
+
for (const part of loopParts(node)) {
|
|
1295
|
+
if (part === null || isLoop(part)) {
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
awaits.push(...collectAwaits(part));
|
|
1299
|
+
if (!earlyExit && hasEarlyExit(part)) {
|
|
1300
|
+
earlyExit = true;
|
|
862
1301
|
}
|
|
863
1302
|
}
|
|
1303
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1304
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
1305
|
+
}
|
|
864
1306
|
}
|
|
865
1307
|
return {
|
|
866
1308
|
ForStatement: checkLoop,
|
|
@@ -872,6 +1314,28 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
872
1314
|
return;
|
|
873
1315
|
}
|
|
874
1316
|
checkLoop(node);
|
|
1317
|
+
},
|
|
1318
|
+
CallExpression(node) {
|
|
1319
|
+
const callee = node.callee;
|
|
1320
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
if (callee.property.type !== "Identifier" || !ARRAY_ITERATION_METHODS.has(callee.property.name)) {
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
const callback = node.arguments[0];
|
|
1327
|
+
if (callback === void 0 || !isFunctionLike(callback) || !("async" in callback && callback.async)) {
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
if (callee.property.name !== "forEach" && node.parent.type !== "ExpressionStatement") {
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
const awaits = collectAwaits(callback.body);
|
|
1334
|
+
const earlyExit = hasEarlyExit(callback.body);
|
|
1335
|
+
const iterableText = context.sourceCode.getText(callee.object);
|
|
1336
|
+
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1337
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
1338
|
+
}
|
|
875
1339
|
}
|
|
876
1340
|
};
|
|
877
1341
|
}
|
|
@@ -923,6 +1387,21 @@ function isStringInitializedVariable(variable) {
|
|
|
923
1387
|
}
|
|
924
1388
|
return isStringLiteralInit(declarator.init);
|
|
925
1389
|
}
|
|
1390
|
+
function isConcatOperand(node, target) {
|
|
1391
|
+
if (node.type === "Identifier") {
|
|
1392
|
+
return node.name === target;
|
|
1393
|
+
}
|
|
1394
|
+
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
1395
|
+
return isConcatOperand(node.left, target) || isConcatOperand(node.right, target);
|
|
1396
|
+
}
|
|
1397
|
+
return false;
|
|
1398
|
+
}
|
|
1399
|
+
function isConcatOntoTarget(rhs, target) {
|
|
1400
|
+
if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1404
|
+
}
|
|
926
1405
|
function isInsideLoopBody(node) {
|
|
927
1406
|
let child = node;
|
|
928
1407
|
let parent = node.parent;
|
|
@@ -956,10 +1435,11 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
956
1435
|
create(context) {
|
|
957
1436
|
return {
|
|
958
1437
|
AssignmentExpression(node) {
|
|
959
|
-
if (node.
|
|
1438
|
+
if (node.left.type !== "Identifier") {
|
|
960
1439
|
return;
|
|
961
1440
|
}
|
|
962
|
-
|
|
1441
|
+
const isAccumulation = node.operator === "+=" || node.operator === "=" && isConcatOntoTarget(node.right, node.left.name);
|
|
1442
|
+
if (!isAccumulation) {
|
|
963
1443
|
return;
|
|
964
1444
|
}
|
|
965
1445
|
if (!isInsideLoopBody(node)) {
|
|
@@ -1262,6 +1742,30 @@ var findVariable2 = (scope, name) => {
|
|
|
1262
1742
|
}
|
|
1263
1743
|
return null;
|
|
1264
1744
|
};
|
|
1745
|
+
var GUARD_NAME_RE = /^is[A-Z]/;
|
|
1746
|
+
var isGuardTestPosition = (node) => {
|
|
1747
|
+
let current = node;
|
|
1748
|
+
let parent = current.parent;
|
|
1749
|
+
while (parent !== void 0 && parent !== null) {
|
|
1750
|
+
switch (parent.type) {
|
|
1751
|
+
case AST_NODE_TYPES6.UnaryExpression:
|
|
1752
|
+
case AST_NODE_TYPES6.LogicalExpression:
|
|
1753
|
+
case AST_NODE_TYPES6.ChainExpression:
|
|
1754
|
+
current = parent;
|
|
1755
|
+
parent = parent.parent;
|
|
1756
|
+
continue;
|
|
1757
|
+
case AST_NODE_TYPES6.IfStatement:
|
|
1758
|
+
case AST_NODE_TYPES6.ConditionalExpression:
|
|
1759
|
+
case AST_NODE_TYPES6.WhileStatement:
|
|
1760
|
+
case AST_NODE_TYPES6.DoWhileStatement:
|
|
1761
|
+
case AST_NODE_TYPES6.ForStatement:
|
|
1762
|
+
return parent.test === current;
|
|
1763
|
+
default:
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return false;
|
|
1768
|
+
};
|
|
1265
1769
|
var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
1266
1770
|
const unwrapped = unwrap(node);
|
|
1267
1771
|
if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES6.Identifier) {
|
|
@@ -1340,6 +1844,22 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
1340
1844
|
}
|
|
1341
1845
|
}
|
|
1342
1846
|
},
|
|
1847
|
+
CallExpression(node) {
|
|
1848
|
+
if (node.callee.type !== AST_NODE_TYPES6.Identifier) return;
|
|
1849
|
+
if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
const scope = context.sourceCode.getScope(node);
|
|
1853
|
+
for (const arg of node.arguments) {
|
|
1854
|
+
if (arg.type === AST_NODE_TYPES6.SpreadElement) continue;
|
|
1855
|
+
const unwrapped = unwrap(arg);
|
|
1856
|
+
if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES6.Identifier) {
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
const variable = findVariable2(scope, unwrapped.name);
|
|
1860
|
+
if (variable !== null) unvalidatedVariables.delete(variable);
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1343
1863
|
MemberExpression(node) {
|
|
1344
1864
|
const scope = context.sourceCode.getScope(node);
|
|
1345
1865
|
const obj = unwrap(node.object);
|
|
@@ -1401,6 +1921,35 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
|
1401
1921
|
"lightingColor"
|
|
1402
1922
|
]);
|
|
1403
1923
|
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
1924
|
+
var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
1925
|
+
var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
|
|
1926
|
+
"mask",
|
|
1927
|
+
"clipPath",
|
|
1928
|
+
"defs",
|
|
1929
|
+
"pattern",
|
|
1930
|
+
"linearGradient",
|
|
1931
|
+
"radialGradient"
|
|
1932
|
+
]);
|
|
1933
|
+
var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
|
|
1934
|
+
"#fff",
|
|
1935
|
+
"#ffffff",
|
|
1936
|
+
"#000",
|
|
1937
|
+
"#000000",
|
|
1938
|
+
"transparent",
|
|
1939
|
+
"none",
|
|
1940
|
+
"currentcolor",
|
|
1941
|
+
"inherit"
|
|
1942
|
+
]);
|
|
1943
|
+
var isInsideSvg = (node) => {
|
|
1944
|
+
let current = node.parent;
|
|
1945
|
+
while (current !== void 0 && current !== null) {
|
|
1946
|
+
if (current.type === AST_NODE_TYPES7.JSXElement && current.openingElement.name.type === AST_NODE_TYPES7.JSXIdentifier && (current.openingElement.name.name === "svg" || SVG_DEFS_CONTAINERS.has(current.openingElement.name.name))) {
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
current = current.parent;
|
|
1950
|
+
}
|
|
1951
|
+
return false;
|
|
1952
|
+
};
|
|
1404
1953
|
var propName = (key) => {
|
|
1405
1954
|
if (key.type === AST_NODE_TYPES7.Identifier) return key.name;
|
|
1406
1955
|
if (key.type === AST_NODE_TYPES7.Literal && typeof key.value === "string") return key.value;
|
|
@@ -1424,6 +1973,7 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
1424
1973
|
},
|
|
1425
1974
|
defaultOptions: [],
|
|
1426
1975
|
create(context) {
|
|
1976
|
+
if (STORIES_FILE_RE.test(context.filename)) return {};
|
|
1427
1977
|
const reportClasses = (value, node) => {
|
|
1428
1978
|
for (const token of classTokens(value)) {
|
|
1429
1979
|
const base = tailwindBase(token);
|
|
@@ -1495,9 +2045,16 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
1495
2045
|
const name = propName(node.key);
|
|
1496
2046
|
if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
|
|
1497
2047
|
},
|
|
1498
|
-
// SVG presentation attributes: <path fill="#
|
|
2048
|
+
// SVG presentation attributes: <path fill="#7c3aed" stroke="#7c3aed" />.
|
|
2049
|
+
// Neutral drawing literals and anything inside an SVG defs container are
|
|
2050
|
+
// structural, not UI tokens, so they never fire.
|
|
1499
2051
|
"JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
|
|
1500
|
-
if (node.value?.type
|
|
2052
|
+
if (node.value?.type !== AST_NODE_TYPES7.Literal) return;
|
|
2053
|
+
if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
if (isInsideSvg(node)) return;
|
|
2057
|
+
checkColorValueNode(node.value);
|
|
1501
2058
|
},
|
|
1502
2059
|
// Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
|
|
1503
2060
|
"JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
|
|
@@ -1656,13 +2213,40 @@ var prefer_server_actions_default = ESLintUtils16.RuleCreator(
|
|
|
1656
2213
|
});
|
|
1657
2214
|
|
|
1658
2215
|
// src/rules/prefer-shadcn.ts
|
|
1659
|
-
import {
|
|
2216
|
+
import {
|
|
2217
|
+
AST_NODE_TYPES as AST_NODE_TYPES8,
|
|
2218
|
+
ESLintUtils as ESLintUtils17
|
|
2219
|
+
} from "@typescript-eslint/utils";
|
|
1660
2220
|
var REPLACEMENTS = {
|
|
1661
|
-
input: "Input",
|
|
1662
2221
|
select: "Select",
|
|
1663
2222
|
textarea: "Textarea",
|
|
1664
2223
|
dialog: "Dialog"
|
|
1665
2224
|
};
|
|
2225
|
+
var INPUT_TYPE_REPLACEMENTS = {
|
|
2226
|
+
checkbox: "Checkbox",
|
|
2227
|
+
radio: "RadioGroup",
|
|
2228
|
+
range: "Slider"
|
|
2229
|
+
};
|
|
2230
|
+
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
|
|
2231
|
+
var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2232
|
+
var literalTypeAttr = (node) => {
|
|
2233
|
+
for (const attribute of node.attributes) {
|
|
2234
|
+
if (attribute.type !== AST_NODE_TYPES8.JSXAttribute || attribute.name.type !== AST_NODE_TYPES8.JSXIdentifier || attribute.name.name !== "type") {
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
if (attribute.value?.type === AST_NODE_TYPES8.Literal && typeof attribute.value.value === "string") {
|
|
2238
|
+
return { kind: "literal", value: attribute.value.value.toLowerCase() };
|
|
2239
|
+
}
|
|
2240
|
+
return { kind: "dynamic" };
|
|
2241
|
+
}
|
|
2242
|
+
return null;
|
|
2243
|
+
};
|
|
2244
|
+
var resolveInputReplacement = (node) => {
|
|
2245
|
+
const typeAttr = literalTypeAttr(node);
|
|
2246
|
+
if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
|
|
2247
|
+
if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
|
|
2248
|
+
return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
|
|
2249
|
+
};
|
|
1666
2250
|
var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
1667
2251
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1668
2252
|
)({
|
|
@@ -1685,8 +2269,8 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1685
2269
|
return;
|
|
1686
2270
|
}
|
|
1687
2271
|
const elementName = node.name.name;
|
|
1688
|
-
const replacement = REPLACEMENTS[elementName];
|
|
1689
|
-
if (replacement === void 0) {
|
|
2272
|
+
const replacement = elementName === "input" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];
|
|
2273
|
+
if (replacement === void 0 || replacement === null) {
|
|
1690
2274
|
return;
|
|
1691
2275
|
}
|
|
1692
2276
|
context.report({
|
|
@@ -1695,7 +2279,7 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1695
2279
|
data: {
|
|
1696
2280
|
element: elementName,
|
|
1697
2281
|
replacement,
|
|
1698
|
-
lowercase:
|
|
2282
|
+
lowercase: kebabCase(replacement)
|
|
1699
2283
|
}
|
|
1700
2284
|
});
|
|
1701
2285
|
}
|
|
@@ -1704,40 +2288,59 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1704
2288
|
});
|
|
1705
2289
|
|
|
1706
2290
|
// src/rules/require-assert-never.ts
|
|
1707
|
-
import {
|
|
2291
|
+
import {
|
|
2292
|
+
ESLintUtils as ESLintUtils18,
|
|
2293
|
+
AST_NODE_TYPES as AST_NODE_TYPES9
|
|
2294
|
+
} from "@typescript-eslint/utils";
|
|
1708
2295
|
var isAssertNeverCall = (expression) => {
|
|
1709
|
-
if (expression.type !==
|
|
2296
|
+
if (expression.type !== AST_NODE_TYPES9.CallExpression) return false;
|
|
1710
2297
|
const callee = expression.callee;
|
|
1711
|
-
if (callee.type ===
|
|
2298
|
+
if (callee.type === AST_NODE_TYPES9.Identifier) {
|
|
1712
2299
|
return callee.name === "assertNever";
|
|
1713
2300
|
}
|
|
1714
|
-
if (callee.type ===
|
|
2301
|
+
if (callee.type === AST_NODE_TYPES9.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES9.Identifier) {
|
|
1715
2302
|
return callee.property.name === "assertNever";
|
|
1716
2303
|
}
|
|
1717
2304
|
return false;
|
|
1718
2305
|
};
|
|
1719
2306
|
var statementContainsAssertNever = (statement) => {
|
|
1720
|
-
if (statement.type ===
|
|
2307
|
+
if (statement.type === AST_NODE_TYPES9.ExpressionStatement) {
|
|
1721
2308
|
return isAssertNeverCall(statement.expression);
|
|
1722
2309
|
}
|
|
1723
|
-
if (statement.type ===
|
|
2310
|
+
if (statement.type === AST_NODE_TYPES9.ThrowStatement) {
|
|
1724
2311
|
return isAssertNeverCall(statement.argument);
|
|
1725
2312
|
}
|
|
1726
|
-
if (statement.type ===
|
|
2313
|
+
if (statement.type === AST_NODE_TYPES9.ReturnStatement) {
|
|
1727
2314
|
return statement.argument !== null && isAssertNeverCall(statement.argument);
|
|
1728
2315
|
}
|
|
1729
|
-
if (statement.type ===
|
|
2316
|
+
if (statement.type === AST_NODE_TYPES9.BlockStatement) {
|
|
1730
2317
|
return statement.body.some(statementContainsAssertNever);
|
|
1731
2318
|
}
|
|
1732
2319
|
return false;
|
|
1733
2320
|
};
|
|
1734
2321
|
var isRuntimeHandlingStatement = (statement) => {
|
|
1735
|
-
if (statement.type ===
|
|
1736
|
-
if (statement.type ===
|
|
2322
|
+
if (statement.type === AST_NODE_TYPES9.EmptyStatement) return false;
|
|
2323
|
+
if (statement.type === AST_NODE_TYPES9.BlockStatement) {
|
|
1737
2324
|
return statement.body.some(isRuntimeHandlingStatement);
|
|
1738
2325
|
}
|
|
1739
2326
|
return true;
|
|
1740
2327
|
};
|
|
2328
|
+
var isFallthroughDefault = (node, defaultIndex) => {
|
|
2329
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2330
|
+
return defaultCase !== void 0 && defaultCase.consequent.length === 0 && defaultIndex < node.cases.length - 1;
|
|
2331
|
+
};
|
|
2332
|
+
var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
|
|
2333
|
+
if (defaultCase.consequent.length === 0) {
|
|
2334
|
+
const defaultToken = sourceCode.getFirstToken(defaultCase);
|
|
2335
|
+
const colonToken = defaultToken ? sourceCode.getTokenAfter(defaultToken) : null;
|
|
2336
|
+
return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
|
|
2337
|
+
}
|
|
2338
|
+
const only = defaultCase.consequent[0];
|
|
2339
|
+
if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === AST_NODE_TYPES9.BlockStatement && only.body.length === 0) {
|
|
2340
|
+
return sourceCode.getCommentsInside(only).length > 0;
|
|
2341
|
+
}
|
|
2342
|
+
return false;
|
|
2343
|
+
};
|
|
1741
2344
|
var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
1742
2345
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1743
2346
|
)({
|
|
@@ -1756,12 +2359,16 @@ var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
|
1756
2359
|
create(context) {
|
|
1757
2360
|
return {
|
|
1758
2361
|
SwitchStatement(node) {
|
|
1759
|
-
const
|
|
2362
|
+
const defaultIndex = node.cases.findIndex(
|
|
1760
2363
|
(caseNode) => caseNode.test === null
|
|
1761
2364
|
);
|
|
1762
|
-
if (
|
|
2365
|
+
if (defaultIndex === -1) return;
|
|
2366
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2367
|
+
if (defaultCase === void 0) return;
|
|
1763
2368
|
if (defaultCase.consequent.some(statementContainsAssertNever)) return;
|
|
1764
2369
|
if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
|
|
2370
|
+
if (isFallthroughDefault(node, defaultIndex)) return;
|
|
2371
|
+
if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;
|
|
1765
2372
|
context.report({
|
|
1766
2373
|
node: defaultCase,
|
|
1767
2374
|
messageId: "missingAssertNever"
|
|
@@ -1772,19 +2379,19 @@ var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
|
1772
2379
|
});
|
|
1773
2380
|
|
|
1774
2381
|
// src/rules/require-zod-form-validation.ts
|
|
1775
|
-
import { ESLintUtils as ESLintUtils19, AST_NODE_TYPES as
|
|
2382
|
+
import { ESLintUtils as ESLintUtils19, AST_NODE_TYPES as AST_NODE_TYPES10 } from "@typescript-eslint/utils";
|
|
1776
2383
|
var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
|
|
1777
2384
|
var looksLikeZodSchema = (node) => {
|
|
1778
2385
|
let current = node;
|
|
1779
2386
|
while (true) {
|
|
1780
|
-
if (current.type ===
|
|
2387
|
+
if (current.type === AST_NODE_TYPES10.Identifier) {
|
|
1781
2388
|
return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
|
|
1782
2389
|
}
|
|
1783
|
-
if (current.type ===
|
|
2390
|
+
if (current.type === AST_NODE_TYPES10.CallExpression) {
|
|
1784
2391
|
current = current.callee;
|
|
1785
2392
|
continue;
|
|
1786
2393
|
}
|
|
1787
|
-
if (current.type ===
|
|
2394
|
+
if (current.type === AST_NODE_TYPES10.MemberExpression) {
|
|
1788
2395
|
current = current.object;
|
|
1789
2396
|
continue;
|
|
1790
2397
|
}
|
|
@@ -1792,23 +2399,23 @@ var looksLikeZodSchema = (node) => {
|
|
|
1792
2399
|
}
|
|
1793
2400
|
};
|
|
1794
2401
|
var isZodParseCall = (node) => {
|
|
1795
|
-
if (node.type !==
|
|
2402
|
+
if (node.type !== AST_NODE_TYPES10.CallExpression) return false;
|
|
1796
2403
|
const callee = node.callee;
|
|
1797
|
-
if (callee.type !==
|
|
2404
|
+
if (callee.type !== AST_NODE_TYPES10.MemberExpression) return false;
|
|
1798
2405
|
if (callee.computed) return false;
|
|
1799
|
-
if (callee.property.type !==
|
|
2406
|
+
if (callee.property.type !== AST_NODE_TYPES10.Identifier) return false;
|
|
1800
2407
|
const method = callee.property.name;
|
|
1801
2408
|
if (method !== "parse" && method !== "safeParse") return false;
|
|
1802
2409
|
return looksLikeZodSchema(callee.object);
|
|
1803
2410
|
};
|
|
1804
2411
|
var isFormDataMethodCall = (node) => {
|
|
1805
2412
|
let current = node;
|
|
1806
|
-
if (current.type ===
|
|
2413
|
+
if (current.type === AST_NODE_TYPES10.AwaitExpression) {
|
|
1807
2414
|
current = current.argument;
|
|
1808
2415
|
}
|
|
1809
|
-
if (current.type !==
|
|
2416
|
+
if (current.type !== AST_NODE_TYPES10.CallExpression) return false;
|
|
1810
2417
|
const callee = current.callee;
|
|
1811
|
-
return callee.type ===
|
|
2418
|
+
return callee.type === AST_NODE_TYPES10.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES10.Identifier && callee.property.name === "formData";
|
|
1812
2419
|
};
|
|
1813
2420
|
var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
1814
2421
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -1827,14 +2434,14 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1827
2434
|
defaultOptions: [],
|
|
1828
2435
|
create(context) {
|
|
1829
2436
|
const isFormSourceIdentifier = (node) => {
|
|
1830
|
-
if (node.type !==
|
|
2437
|
+
if (node.type !== AST_NODE_TYPES10.Identifier) return false;
|
|
1831
2438
|
if (/formdata/i.test(node.name)) return true;
|
|
1832
2439
|
let scope = context.sourceCode.getScope(node);
|
|
1833
2440
|
while (scope !== null) {
|
|
1834
2441
|
const variable = scope.set.get(node.name);
|
|
1835
2442
|
if (variable !== void 0 && variable.defs.length === 1) {
|
|
1836
2443
|
const def = variable.defs[0];
|
|
1837
|
-
if (def !== void 0 && def.type === "Variable" && def.node.type ===
|
|
2444
|
+
if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES10.VariableDeclarator && def.node.init !== null) {
|
|
1838
2445
|
return isFormDataMethodCall(def.node.init);
|
|
1839
2446
|
}
|
|
1840
2447
|
return false;
|
|
@@ -1845,8 +2452,8 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1845
2452
|
};
|
|
1846
2453
|
const isFormDataGetCall = (node) => {
|
|
1847
2454
|
const callee = node.callee;
|
|
1848
|
-
if (callee.type !==
|
|
1849
|
-
if (callee.property.type !==
|
|
2455
|
+
if (callee.type !== AST_NODE_TYPES10.MemberExpression) return false;
|
|
2456
|
+
if (callee.property.type !== AST_NODE_TYPES10.Identifier || callee.property.name !== "get") {
|
|
1850
2457
|
return false;
|
|
1851
2458
|
}
|
|
1852
2459
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -1869,15 +2476,15 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1869
2476
|
});
|
|
1870
2477
|
|
|
1871
2478
|
// src/rules/zod-naming-convention.ts
|
|
1872
|
-
import { ESLintUtils as ESLintUtils20, AST_NODE_TYPES as
|
|
2479
|
+
import { ESLintUtils as ESLintUtils20, AST_NODE_TYPES as AST_NODE_TYPES11 } from "@typescript-eslint/utils";
|
|
1873
2480
|
var calleeChainStartsWithZ = (node) => {
|
|
1874
2481
|
let current = node;
|
|
1875
|
-
while (current.type ===
|
|
2482
|
+
while (current.type === AST_NODE_TYPES11.MemberExpression) {
|
|
1876
2483
|
const receiver = current.object;
|
|
1877
|
-
if (receiver.type ===
|
|
2484
|
+
if (receiver.type === AST_NODE_TYPES11.Identifier && receiver.name === "z") {
|
|
1878
2485
|
return true;
|
|
1879
2486
|
}
|
|
1880
|
-
if (receiver.type ===
|
|
2487
|
+
if (receiver.type === AST_NODE_TYPES11.CallExpression) {
|
|
1881
2488
|
current = receiver.callee;
|
|
1882
2489
|
continue;
|
|
1883
2490
|
}
|
|
@@ -1905,11 +2512,11 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
1905
2512
|
VariableDeclarator(node) {
|
|
1906
2513
|
const init = node.init;
|
|
1907
2514
|
if (init === null || init === void 0) return;
|
|
1908
|
-
if (init.type !==
|
|
2515
|
+
if (init.type !== AST_NODE_TYPES11.CallExpression) return;
|
|
1909
2516
|
const callee = init.callee;
|
|
1910
|
-
if (callee.type !==
|
|
2517
|
+
if (callee.type !== AST_NODE_TYPES11.MemberExpression) return;
|
|
1911
2518
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
1912
|
-
if (node.id.type !==
|
|
2519
|
+
if (node.id.type !== AST_NODE_TYPES11.Identifier) return;
|
|
1913
2520
|
const variableName = node.id.name;
|
|
1914
2521
|
if (variableName.startsWith("Z")) return;
|
|
1915
2522
|
context.report({
|
|
@@ -1954,17 +2561,17 @@ function subtreeContainsStarLiteral(node) {
|
|
|
1954
2561
|
const value = node[key];
|
|
1955
2562
|
if (Array.isArray(value)) {
|
|
1956
2563
|
for (const child of value) {
|
|
1957
|
-
if (
|
|
2564
|
+
if (isNode3(child) && subtreeContainsStarLiteral(child)) {
|
|
1958
2565
|
return true;
|
|
1959
2566
|
}
|
|
1960
2567
|
}
|
|
1961
|
-
} else if (
|
|
2568
|
+
} else if (isNode3(value) && subtreeContainsStarLiteral(value)) {
|
|
1962
2569
|
return true;
|
|
1963
2570
|
}
|
|
1964
2571
|
}
|
|
1965
2572
|
return false;
|
|
1966
2573
|
}
|
|
1967
|
-
function
|
|
2574
|
+
function isNode3(value) {
|
|
1968
2575
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1969
2576
|
}
|
|
1970
2577
|
function propertyKeyName(prop) {
|
|
@@ -1980,7 +2587,7 @@ function propertyKeyName(prop) {
|
|
|
1980
2587
|
}
|
|
1981
2588
|
return void 0;
|
|
1982
2589
|
}
|
|
1983
|
-
function
|
|
2590
|
+
function calleeName3(node) {
|
|
1984
2591
|
const callee = node.callee;
|
|
1985
2592
|
if (callee.type === "Identifier") {
|
|
1986
2593
|
return callee.name;
|
|
@@ -1991,7 +2598,7 @@ function calleeName(node) {
|
|
|
1991
2598
|
return void 0;
|
|
1992
2599
|
}
|
|
1993
2600
|
function isCorsWildcardCredentialsCall(node) {
|
|
1994
|
-
const name =
|
|
2601
|
+
const name = calleeName3(node);
|
|
1995
2602
|
if (name === void 0 || name.toLowerCase() !== "cors") {
|
|
1996
2603
|
return false;
|
|
1997
2604
|
}
|
|
@@ -2134,13 +2741,13 @@ var no_cors_wildcard_with_credentials_default = ESLintUtils21.RuleCreator(
|
|
|
2134
2741
|
// src/rules/no-fat-try-blocks.ts
|
|
2135
2742
|
import {
|
|
2136
2743
|
ESLintUtils as ESLintUtils22,
|
|
2137
|
-
AST_NODE_TYPES as
|
|
2744
|
+
AST_NODE_TYPES as AST_NODE_TYPES12
|
|
2138
2745
|
} from "@typescript-eslint/utils";
|
|
2139
2746
|
var MAX_TRY_BODY_STATEMENTS = 3;
|
|
2140
2747
|
var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2748
|
+
AST_NODE_TYPES12.FunctionDeclaration,
|
|
2749
|
+
AST_NODE_TYPES12.FunctionExpression,
|
|
2750
|
+
AST_NODE_TYPES12.ArrowFunctionExpression
|
|
2144
2751
|
]);
|
|
2145
2752
|
var PURE_METHODS = /* @__PURE__ */ new Set([
|
|
2146
2753
|
"map",
|
|
@@ -2233,25 +2840,25 @@ var PURE_CONSTRUCTORS = /* @__PURE__ */ new Set([
|
|
|
2233
2840
|
"Response",
|
|
2234
2841
|
"AbortController"
|
|
2235
2842
|
]);
|
|
2236
|
-
function
|
|
2843
|
+
function isNode4(value) {
|
|
2237
2844
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
2238
2845
|
}
|
|
2239
2846
|
function isPureCall(node) {
|
|
2240
2847
|
const callee = node.callee;
|
|
2241
|
-
if (callee.type !==
|
|
2848
|
+
if (callee.type !== AST_NODE_TYPES12.MemberExpression) {
|
|
2242
2849
|
return false;
|
|
2243
2850
|
}
|
|
2244
2851
|
const property = callee.property;
|
|
2245
|
-
if (property.type !==
|
|
2852
|
+
if (property.type !== AST_NODE_TYPES12.Identifier) {
|
|
2246
2853
|
return false;
|
|
2247
2854
|
}
|
|
2248
|
-
if (callee.object.type ===
|
|
2855
|
+
if (callee.object.type === AST_NODE_TYPES12.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
|
|
2249
2856
|
return true;
|
|
2250
2857
|
}
|
|
2251
2858
|
return PURE_METHODS.has(property.name);
|
|
2252
2859
|
}
|
|
2253
2860
|
function isPureNew(node) {
|
|
2254
|
-
return node.callee.type ===
|
|
2861
|
+
return node.callee.type === AST_NODE_TYPES12.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
|
|
2255
2862
|
}
|
|
2256
2863
|
function subtreeMatches(stmt, predicate) {
|
|
2257
2864
|
let found = false;
|
|
@@ -2273,11 +2880,11 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2273
2880
|
const value = current[key];
|
|
2274
2881
|
if (Array.isArray(value)) {
|
|
2275
2882
|
for (const child of value) {
|
|
2276
|
-
if (
|
|
2883
|
+
if (isNode4(child)) {
|
|
2277
2884
|
visit(child);
|
|
2278
2885
|
}
|
|
2279
2886
|
}
|
|
2280
|
-
} else if (
|
|
2887
|
+
} else if (isNode4(value)) {
|
|
2281
2888
|
visit(value);
|
|
2282
2889
|
}
|
|
2283
2890
|
if (found) {
|
|
@@ -2288,14 +2895,14 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2288
2895
|
visit(stmt);
|
|
2289
2896
|
return found;
|
|
2290
2897
|
}
|
|
2291
|
-
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type ===
|
|
2898
|
+
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === AST_NODE_TYPES12.AwaitExpression);
|
|
2292
2899
|
var hasThrowingCallOrNew = (stmt) => subtreeMatches(
|
|
2293
2900
|
stmt,
|
|
2294
|
-
(n) => n.type ===
|
|
2901
|
+
(n) => n.type === AST_NODE_TYPES12.CallExpression && !isPureCall(n) || n.type === AST_NODE_TYPES12.NewExpression && !isPureNew(n)
|
|
2295
2902
|
);
|
|
2296
2903
|
function unwrap2(expr) {
|
|
2297
2904
|
let current = expr;
|
|
2298
|
-
while (current.type ===
|
|
2905
|
+
while (current.type === AST_NODE_TYPES12.ChainExpression || current.type === AST_NODE_TYPES12.TSNonNullExpression) {
|
|
2299
2906
|
current = current.expression;
|
|
2300
2907
|
}
|
|
2301
2908
|
return current;
|
|
@@ -2304,7 +2911,7 @@ function canThrow(stmt) {
|
|
|
2304
2911
|
if (hasAwait(stmt)) {
|
|
2305
2912
|
return true;
|
|
2306
2913
|
}
|
|
2307
|
-
if (stmt.type ===
|
|
2914
|
+
if (stmt.type === AST_NODE_TYPES12.ExpressionStatement && unwrap2(stmt.expression).type === AST_NODE_TYPES12.CallExpression) {
|
|
2308
2915
|
return false;
|
|
2309
2916
|
}
|
|
2310
2917
|
return hasThrowingCallOrNew(stmt);
|
|
@@ -2315,7 +2922,7 @@ function handlerRethrows(handler) {
|
|
|
2315
2922
|
}
|
|
2316
2923
|
const body = handler.body.body;
|
|
2317
2924
|
const last = body[body.length - 1];
|
|
2318
|
-
return last !== void 0 && last.type ===
|
|
2925
|
+
return last !== void 0 && last.type === AST_NODE_TYPES12.ThrowStatement;
|
|
2319
2926
|
}
|
|
2320
2927
|
var no_fat_try_blocks_default = ESLintUtils22.RuleCreator(
|
|
2321
2928
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -2359,7 +2966,7 @@ var no_fat_try_blocks_default = ESLintUtils22.RuleCreator(
|
|
|
2359
2966
|
|
|
2360
2967
|
// src/rules/no-secret-in-log.ts
|
|
2361
2968
|
import { ESLintUtils as ESLintUtils23 } from "@typescript-eslint/utils";
|
|
2362
|
-
var
|
|
2969
|
+
var LOG_METHODS2 = /* @__PURE__ */ new Set([
|
|
2363
2970
|
"debug",
|
|
2364
2971
|
"info",
|
|
2365
2972
|
"warn",
|
|
@@ -2372,7 +2979,7 @@ var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
|
2372
2979
|
"fatal",
|
|
2373
2980
|
"success"
|
|
2374
2981
|
]);
|
|
2375
|
-
var
|
|
2982
|
+
var LOGGER_NAMES2 = /* @__PURE__ */ new Set([
|
|
2376
2983
|
"logger",
|
|
2377
2984
|
"log",
|
|
2378
2985
|
"logging",
|
|
@@ -2513,12 +3120,12 @@ function isSecretKeyword(name) {
|
|
|
2513
3120
|
function isLoggerExpr(expr) {
|
|
2514
3121
|
switch (expr.type) {
|
|
2515
3122
|
case "Identifier":
|
|
2516
|
-
return
|
|
3123
|
+
return LOGGER_NAMES2.has(expr.name.toLowerCase());
|
|
2517
3124
|
case "MemberExpression": {
|
|
2518
3125
|
const { property, object } = expr;
|
|
2519
3126
|
if (!expr.computed && property.type === "Identifier") {
|
|
2520
3127
|
const lowered = property.name.toLowerCase();
|
|
2521
|
-
if (
|
|
3128
|
+
if (LOGGER_NAMES2.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
|
|
2522
3129
|
return true;
|
|
2523
3130
|
}
|
|
2524
3131
|
}
|
|
@@ -2575,7 +3182,7 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2575
3182
|
return {
|
|
2576
3183
|
CallExpression(node) {
|
|
2577
3184
|
const callee = node.callee;
|
|
2578
|
-
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !
|
|
3185
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS2.has(callee.property.name)) {
|
|
2579
3186
|
return;
|
|
2580
3187
|
}
|
|
2581
3188
|
if (!isLoggerExpr(callee.object)) {
|
|
@@ -2592,6 +3199,16 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2592
3199
|
}
|
|
2593
3200
|
continue;
|
|
2594
3201
|
}
|
|
3202
|
+
if (arg.type === "MemberExpression") {
|
|
3203
|
+
if (!arg.computed && arg.property.type === "Identifier" && isSecretKeyword(arg.property.name)) {
|
|
3204
|
+
context.report({
|
|
3205
|
+
node: arg,
|
|
3206
|
+
messageId: "noSecretInLog",
|
|
3207
|
+
data: { name: arg.property.name }
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
continue;
|
|
3211
|
+
}
|
|
2595
3212
|
if (arg.type === "ObjectExpression") {
|
|
2596
3213
|
for (const prop of arg.properties) {
|
|
2597
3214
|
if (prop.type !== "Property") {
|
|
@@ -2616,7 +3233,7 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2616
3233
|
// src/rules/prefer-string-literal-union.ts
|
|
2617
3234
|
import {
|
|
2618
3235
|
ESLintUtils as ESLintUtils24,
|
|
2619
|
-
AST_NODE_TYPES as
|
|
3236
|
+
AST_NODE_TYPES as AST_NODE_TYPES13
|
|
2620
3237
|
} from "@typescript-eslint/utils";
|
|
2621
3238
|
import * as ts from "typescript";
|
|
2622
3239
|
var CHOICE_TOKENS = /* @__PURE__ */ new Set([
|
|
@@ -2660,19 +3277,19 @@ function isChoiceLikeName(name) {
|
|
|
2660
3277
|
return CHOICE_TOKENS.has(lastWord(name));
|
|
2661
3278
|
}
|
|
2662
3279
|
function keyName(key) {
|
|
2663
|
-
if (key.type ===
|
|
3280
|
+
if (key.type === AST_NODE_TYPES13.Identifier) {
|
|
2664
3281
|
return key.name;
|
|
2665
3282
|
}
|
|
2666
|
-
if (key.type ===
|
|
3283
|
+
if (key.type === AST_NODE_TYPES13.Literal && typeof key.value === "string") {
|
|
2667
3284
|
return key.value;
|
|
2668
3285
|
}
|
|
2669
3286
|
return null;
|
|
2670
3287
|
}
|
|
2671
3288
|
function isStringLiteralMember(t) {
|
|
2672
|
-
return t.type ===
|
|
3289
|
+
return t.type === AST_NODE_TYPES13.TSLiteralType && t.literal.type === AST_NODE_TYPES13.Literal && typeof t.literal.value === "string";
|
|
2673
3290
|
}
|
|
2674
3291
|
function isStringLiteralUnion(node) {
|
|
2675
|
-
if (node?.type !==
|
|
3292
|
+
if (node?.type !== AST_NODE_TYPES13.TSUnionType) {
|
|
2676
3293
|
return false;
|
|
2677
3294
|
}
|
|
2678
3295
|
return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
|
|
@@ -2701,12 +3318,12 @@ function bindingSourceExpression(decl) {
|
|
|
2701
3318
|
return ts.isForOfStatement(node) ? node.expression : node.initializer;
|
|
2702
3319
|
}
|
|
2703
3320
|
function refKey(node) {
|
|
2704
|
-
if (node.type ===
|
|
3321
|
+
if (node.type === AST_NODE_TYPES13.Identifier) {
|
|
2705
3322
|
return node.name;
|
|
2706
3323
|
}
|
|
2707
|
-
if (node.type ===
|
|
3324
|
+
if (node.type === AST_NODE_TYPES13.MemberExpression && !node.computed) {
|
|
2708
3325
|
const inner = refKey(node.object);
|
|
2709
|
-
if (inner === null || node.property.type !==
|
|
3326
|
+
if (inner === null || node.property.type !== AST_NODE_TYPES13.Identifier) {
|
|
2710
3327
|
return null;
|
|
2711
3328
|
}
|
|
2712
3329
|
return `${inner}.${node.property.name}`;
|
|
@@ -2714,7 +3331,7 @@ function refKey(node) {
|
|
|
2714
3331
|
return null;
|
|
2715
3332
|
}
|
|
2716
3333
|
function strLiteral(node) {
|
|
2717
|
-
if (node.type ===
|
|
3334
|
+
if (node.type === AST_NODE_TYPES13.Literal && typeof node.value === "string") {
|
|
2718
3335
|
return node.value;
|
|
2719
3336
|
}
|
|
2720
3337
|
return null;
|
|
@@ -2827,7 +3444,7 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2827
3444
|
containersWithUnion.add(container);
|
|
2828
3445
|
return;
|
|
2829
3446
|
}
|
|
2830
|
-
if (typeNode?.type !==
|
|
3447
|
+
if (typeNode?.type !== AST_NODE_TYPES13.TSStringKeyword) {
|
|
2831
3448
|
return;
|
|
2832
3449
|
}
|
|
2833
3450
|
const name = keyName(key);
|
|
@@ -2915,10 +3532,10 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2915
3532
|
}
|
|
2916
3533
|
};
|
|
2917
3534
|
function refKeyText(node) {
|
|
2918
|
-
if (node.type ===
|
|
3535
|
+
if (node.type === AST_NODE_TYPES13.BinaryExpression) {
|
|
2919
3536
|
return refKey(node.left) ?? refKey(node.right) ?? "value";
|
|
2920
3537
|
}
|
|
2921
|
-
if (node.type ===
|
|
3538
|
+
if (node.type === AST_NODE_TYPES13.SwitchStatement) {
|
|
2922
3539
|
return refKey(node.discriminant) ?? "value";
|
|
2923
3540
|
}
|
|
2924
3541
|
return "value";
|
|
@@ -2927,7 +3544,7 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2927
3544
|
});
|
|
2928
3545
|
|
|
2929
3546
|
// src/rules/single-public-export.ts
|
|
2930
|
-
import { ESLintUtils as ESLintUtils25, AST_NODE_TYPES as
|
|
3547
|
+
import { ESLintUtils as ESLintUtils25, AST_NODE_TYPES as AST_NODE_TYPES14 } from "@typescript-eslint/utils";
|
|
2931
3548
|
var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
|
|
2932
3549
|
"util",
|
|
2933
3550
|
"utils",
|
|
@@ -2954,20 +3571,20 @@ var TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
|
|
|
2954
3571
|
var SCRIPT_EXT_RE = /\.[cm]?[jt]sx?$/i;
|
|
2955
3572
|
var basename = (filename) => filename.split(/[/\\]/).pop() ?? filename;
|
|
2956
3573
|
var stemOf = (base) => base.replace(SCRIPT_EXT_RE, "");
|
|
2957
|
-
var
|
|
3574
|
+
var kebabCase2 = (name) => {
|
|
2958
3575
|
let normalized = name;
|
|
2959
3576
|
for (const [pattern, replacement] of ACRONYM_OVERRIDES) {
|
|
2960
3577
|
normalized = normalized.replace(pattern, replacement);
|
|
2961
3578
|
}
|
|
2962
3579
|
return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
|
|
2963
3580
|
};
|
|
2964
|
-
var
|
|
3581
|
+
var isFunctionExpression = (node) => node !== null && (node.type === AST_NODE_TYPES14.ArrowFunctionExpression || node.type === AST_NODE_TYPES14.FunctionExpression);
|
|
2965
3582
|
var functionConstName = (decl) => {
|
|
2966
3583
|
if (decl.declarations.length !== 1) return null;
|
|
2967
3584
|
const [declarator] = decl.declarations;
|
|
2968
3585
|
if (declarator === void 0) return null;
|
|
2969
|
-
if (declarator.id.type !==
|
|
2970
|
-
if (!
|
|
3586
|
+
if (declarator.id.type !== AST_NODE_TYPES14.Identifier) return null;
|
|
3587
|
+
if (!isFunctionExpression(declarator.init)) return null;
|
|
2971
3588
|
return declarator.id.name;
|
|
2972
3589
|
};
|
|
2973
3590
|
var summarizeExports = (body) => {
|
|
@@ -2980,20 +3597,20 @@ var summarizeExports = (body) => {
|
|
|
2980
3597
|
};
|
|
2981
3598
|
for (const statement of body) {
|
|
2982
3599
|
switch (statement.type) {
|
|
2983
|
-
case
|
|
3600
|
+
case AST_NODE_TYPES14.ExportAllDeclaration:
|
|
2984
3601
|
hasReExport = true;
|
|
2985
3602
|
break;
|
|
2986
|
-
case
|
|
3603
|
+
case AST_NODE_TYPES14.ExportDefaultDeclaration: {
|
|
2987
3604
|
names += 1;
|
|
2988
3605
|
const decl = statement.declaration;
|
|
2989
|
-
if (decl.type ===
|
|
3606
|
+
if (decl.type === AST_NODE_TYPES14.FunctionDeclaration && decl.id !== null) {
|
|
2990
3607
|
candidate = { name: decl.id.name, node: statement };
|
|
2991
|
-
} else if (decl.type ===
|
|
3608
|
+
} else if (decl.type === AST_NODE_TYPES14.ClassDeclaration && decl.id !== null) {
|
|
2992
3609
|
candidate = { name: decl.id.name, node: statement };
|
|
2993
3610
|
}
|
|
2994
3611
|
break;
|
|
2995
3612
|
}
|
|
2996
|
-
case
|
|
3613
|
+
case AST_NODE_TYPES14.ExportNamedDeclaration: {
|
|
2997
3614
|
if (statement.source !== null) {
|
|
2998
3615
|
hasReExport = true;
|
|
2999
3616
|
break;
|
|
@@ -3004,15 +3621,15 @@ var summarizeExports = (body) => {
|
|
|
3004
3621
|
break;
|
|
3005
3622
|
}
|
|
3006
3623
|
switch (decl.type) {
|
|
3007
|
-
case
|
|
3624
|
+
case AST_NODE_TYPES14.FunctionDeclaration:
|
|
3008
3625
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3009
3626
|
else names += 1;
|
|
3010
3627
|
break;
|
|
3011
|
-
case
|
|
3628
|
+
case AST_NODE_TYPES14.ClassDeclaration:
|
|
3012
3629
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3013
3630
|
else names += 1;
|
|
3014
3631
|
break;
|
|
3015
|
-
case
|
|
3632
|
+
case AST_NODE_TYPES14.VariableDeclaration: {
|
|
3016
3633
|
const fnName = functionConstName(decl);
|
|
3017
3634
|
if (fnName !== null && decl.declarations.length === 1) {
|
|
3018
3635
|
addCandidate(fnName, statement);
|
|
@@ -3059,7 +3676,7 @@ var single_public_export_default = ESLintUtils25.RuleCreator(
|
|
|
3059
3676
|
if (hasReExport) return;
|
|
3060
3677
|
if (names !== 1 || candidate === null) return;
|
|
3061
3678
|
if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;
|
|
3062
|
-
const expected =
|
|
3679
|
+
const expected = kebabCase2(candidate.name);
|
|
3063
3680
|
if (stem === expected) return;
|
|
3064
3681
|
context.report({
|
|
3065
3682
|
node: candidate.node,
|
|
@@ -3102,7 +3719,7 @@ var rules = {
|
|
|
3102
3719
|
var plugin = {
|
|
3103
3720
|
meta: {
|
|
3104
3721
|
name: "@sarj/eslint-plugin",
|
|
3105
|
-
version: "2.
|
|
3722
|
+
version: "2.4.1"
|
|
3106
3723
|
},
|
|
3107
3724
|
rules,
|
|
3108
3725
|
configs: {
|