@sarj/eslint-plugin 2.3.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +923 -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 +849 -250
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,40 +1,19 @@
|
|
|
1
1
|
// src/rules/enforce-file-structure.ts
|
|
2
2
|
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
|
-
var SECTION = {
|
|
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
3
|
var SERVER_ACTION_FILE_RE = /(?:^|\/)actions\/|\.action\.[jt]sx?$|(?:^|\/)actions\.[jt]sx?$/;
|
|
14
|
-
var
|
|
15
|
-
var isFunctionLikeVariable = (statement) => statement.declarations.length > 0 && statement.declarations.every(
|
|
16
|
-
(decl) => decl.init !== null && isFunctionExpression(decl.init)
|
|
17
|
-
);
|
|
18
|
-
var getStatementSection = (statement) => {
|
|
4
|
+
var classifyStatement = (statement) => {
|
|
19
5
|
switch (statement.type) {
|
|
20
6
|
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:
|
|
7
|
+
return "import";
|
|
32
8
|
case AST_NODE_TYPES.ExportAllDeclaration:
|
|
33
|
-
return
|
|
9
|
+
return "reexport";
|
|
10
|
+
case AST_NODE_TYPES.ExportNamedDeclaration:
|
|
11
|
+
return statement.declaration === null ? "reexport" : "body";
|
|
34
12
|
default:
|
|
35
|
-
return
|
|
13
|
+
return "body";
|
|
36
14
|
}
|
|
37
15
|
};
|
|
16
|
+
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
17
|
var isUseServerDirective = (statement) => {
|
|
39
18
|
if (statement === void 0) return false;
|
|
40
19
|
if (statement.type !== AST_NODE_TYPES.ExpressionStatement) return false;
|
|
@@ -49,47 +28,43 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
49
28
|
meta: {
|
|
50
29
|
type: "suggestion",
|
|
51
30
|
docs: {
|
|
52
|
-
description: "
|
|
31
|
+
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. Server-action files (under `/actions/`, named `*.action.ts`, or `actions.ts`) must also begin with a `use server` directive."
|
|
53
32
|
},
|
|
54
33
|
schema: [],
|
|
55
34
|
messages: {
|
|
56
|
-
|
|
35
|
+
importsFirst: "File structure violation: import statements must come before other declarations",
|
|
57
36
|
useServerDirective: "Server action files must start with 'use server' directive"
|
|
58
37
|
}
|
|
59
38
|
},
|
|
60
39
|
defaultOptions: [],
|
|
61
40
|
create(context) {
|
|
62
|
-
const
|
|
63
|
-
const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
|
|
41
|
+
const isServerAction = SERVER_ACTION_FILE_RE.test(context.filename);
|
|
64
42
|
return {
|
|
65
43
|
Program(node) {
|
|
66
44
|
const body = node.body;
|
|
67
|
-
if (isServerAction) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
messageId: "useServerDirective"
|
|
73
|
-
});
|
|
74
|
-
}
|
|
45
|
+
if (isServerAction && !isUseServerDirective(body[0])) {
|
|
46
|
+
context.report({
|
|
47
|
+
node,
|
|
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,61 @@ 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
|
+
function negatedInstanceofErrorSubject(test) {
|
|
633
|
+
if (test.type === "UnaryExpression" && test.operator === "!") {
|
|
634
|
+
return instanceofErrorSubject(test.argument);
|
|
635
|
+
}
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
function nodeWithin(node, container) {
|
|
639
|
+
return container !== null && node.range[0] >= container.range[0] && node.range[1] <= container.range[1];
|
|
640
|
+
}
|
|
641
|
+
function isGuardedByInstanceofError(node, argExpr, sourceCode) {
|
|
642
|
+
const argText = sourceCode.getText(argExpr);
|
|
643
|
+
const sameSubject = (subject) => sourceCode.getText(subject) === argText;
|
|
644
|
+
let current = node.parent;
|
|
645
|
+
while (current) {
|
|
646
|
+
if (current.type === "ConditionalExpression") {
|
|
647
|
+
const subject = instanceofErrorSubject(current.test);
|
|
648
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
652
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
653
|
+
return true;
|
|
654
|
+
}
|
|
655
|
+
} else if (current.type === "IfStatement") {
|
|
656
|
+
const subject = instanceofErrorSubject(current.test);
|
|
657
|
+
if (subject && sameSubject(subject) && nodeWithin(node, current.alternate)) {
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
const negated = negatedInstanceofErrorSubject(current.test);
|
|
661
|
+
if (negated && sameSubject(negated) && nodeWithin(node, current.consequent)) {
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
current = current.parent;
|
|
666
|
+
}
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
536
669
|
function isJsonStringify(callee) {
|
|
537
670
|
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
538
671
|
}
|
|
@@ -558,17 +691,28 @@ var no_json_stringify_error_default = ESLintUtils6.RuleCreator(
|
|
|
558
691
|
return;
|
|
559
692
|
}
|
|
560
693
|
const firstArg = node.arguments[0];
|
|
561
|
-
if (!firstArg
|
|
694
|
+
if (!firstArg) {
|
|
562
695
|
return;
|
|
563
696
|
}
|
|
564
|
-
const name = firstArg.name;
|
|
565
697
|
const scope = context.sourceCode.getScope(firstArg);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
698
|
+
let suggestsError;
|
|
699
|
+
if (firstArg.type === "Identifier") {
|
|
700
|
+
suggestsError = ERROR_NAME_PATTERN.test(firstArg.name) || isCatchBinding(scope, firstArg.name);
|
|
701
|
+
} else if (firstArg.type === "MemberExpression") {
|
|
702
|
+
suggestsError = memberSuggestsError(firstArg, scope);
|
|
703
|
+
} else {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (!suggestsError) {
|
|
707
|
+
return;
|
|
571
708
|
}
|
|
709
|
+
if (isGuardedByInstanceofError(node, firstArg, context.sourceCode)) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
context.report({
|
|
713
|
+
node,
|
|
714
|
+
messageId: "noJsonStringifyError"
|
|
715
|
+
});
|
|
572
716
|
}
|
|
573
717
|
};
|
|
574
718
|
}
|
|
@@ -576,41 +720,81 @@ var no_json_stringify_error_default = ESLintUtils6.RuleCreator(
|
|
|
576
720
|
|
|
577
721
|
// src/rules/no-log-only-catch.ts
|
|
578
722
|
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",
|
|
723
|
+
|
|
724
|
+
// src/rules/_logging.ts
|
|
725
|
+
import "@typescript-eslint/utils";
|
|
726
|
+
var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
727
|
+
"debug",
|
|
588
728
|
"info",
|
|
589
|
-
"
|
|
729
|
+
"warn",
|
|
730
|
+
"warning",
|
|
731
|
+
"error",
|
|
732
|
+
"exception",
|
|
733
|
+
"critical",
|
|
734
|
+
"trace",
|
|
735
|
+
"log",
|
|
736
|
+
"fatal",
|
|
737
|
+
"success"
|
|
590
738
|
]);
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
739
|
+
var LOGGER_NAMES = /* @__PURE__ */ new Set([
|
|
740
|
+
"logger",
|
|
741
|
+
"log",
|
|
742
|
+
"logging",
|
|
743
|
+
"loguru",
|
|
744
|
+
"console",
|
|
745
|
+
"_logger",
|
|
746
|
+
"_log"
|
|
747
|
+
]);
|
|
748
|
+
var REPORT_NAME_RE = /error|report|capture|log|trace|warn/i;
|
|
749
|
+
function isLoggerReceiver(expr) {
|
|
750
|
+
switch (expr.type) {
|
|
751
|
+
case "Identifier":
|
|
752
|
+
return LOGGER_NAMES.has(expr.name.toLowerCase());
|
|
753
|
+
case "MemberExpression": {
|
|
754
|
+
const { property, object } = expr;
|
|
755
|
+
if (!expr.computed && property.type === "Identifier" && LOGGER_NAMES.has(property.name.toLowerCase())) {
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
return isLoggerReceiver(object);
|
|
759
|
+
}
|
|
760
|
+
default:
|
|
761
|
+
return false;
|
|
594
762
|
}
|
|
595
|
-
|
|
763
|
+
}
|
|
764
|
+
function isLoggingCall(expr) {
|
|
596
765
|
if (expr.type !== "CallExpression") {
|
|
597
766
|
return false;
|
|
598
767
|
}
|
|
599
768
|
const callee = expr.callee;
|
|
600
|
-
if (callee.type !== "MemberExpression") {
|
|
769
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier") {
|
|
601
770
|
return false;
|
|
602
771
|
}
|
|
603
|
-
|
|
604
|
-
if (object.type !== "Identifier" || object.name !== "console") {
|
|
772
|
+
if (!LOG_METHODS.has(callee.property.name.toLowerCase())) {
|
|
605
773
|
return false;
|
|
606
774
|
}
|
|
607
|
-
|
|
608
|
-
|
|
775
|
+
return isLoggerReceiver(callee.object);
|
|
776
|
+
}
|
|
777
|
+
function calleeName(callee) {
|
|
778
|
+
if (callee.type === "Identifier") {
|
|
779
|
+
return callee.name;
|
|
780
|
+
}
|
|
781
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
782
|
+
return callee.property.name;
|
|
609
783
|
}
|
|
610
|
-
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/rules/no-log-only-catch.ts
|
|
788
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
789
|
+
/\.test\./,
|
|
790
|
+
/\.spec\./,
|
|
791
|
+
/[\\/]__tests__[\\/]/
|
|
792
|
+
];
|
|
793
|
+
function isLoggingCallStatement(statement) {
|
|
794
|
+
if (statement.type !== "ExpressionStatement") {
|
|
611
795
|
return false;
|
|
612
796
|
}
|
|
613
|
-
return
|
|
797
|
+
return isLoggingCall(statement.expression);
|
|
614
798
|
}
|
|
615
799
|
var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
616
800
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -619,11 +803,12 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
619
803
|
meta: {
|
|
620
804
|
type: "problem",
|
|
621
805
|
docs: {
|
|
622
|
-
description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
|
|
806
|
+
description: "Disallow `catch` clauses that only log (or silently do nothing) and then swallow the error; rethrow or handle it instead."
|
|
623
807
|
},
|
|
624
808
|
schema: [],
|
|
625
809
|
messages: {
|
|
626
|
-
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
|
|
810
|
+
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real.",
|
|
811
|
+
emptyCatch: "Empty catch silently swallows the error. Rethrow it, handle it, or add a comment explaining why it is safe to ignore."
|
|
627
812
|
}
|
|
628
813
|
},
|
|
629
814
|
defaultOptions: [],
|
|
@@ -639,13 +824,16 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
639
824
|
CatchClause(node) {
|
|
640
825
|
const statements = node.body.body;
|
|
641
826
|
if (statements.length === 0) {
|
|
642
|
-
context.
|
|
827
|
+
if (context.sourceCode.getCommentsInside(node.body).length > 0) {
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
context.report({ node, messageId: "emptyCatch" });
|
|
643
831
|
return;
|
|
644
832
|
}
|
|
645
|
-
const
|
|
646
|
-
(statement) =>
|
|
833
|
+
const everyStatementIsLogging = statements.every(
|
|
834
|
+
(statement) => isLoggingCallStatement(statement)
|
|
647
835
|
);
|
|
648
|
-
if (
|
|
836
|
+
if (everyStatementIsLogging) {
|
|
649
837
|
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
650
838
|
}
|
|
651
839
|
}
|
|
@@ -655,6 +843,12 @@ var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
|
655
843
|
|
|
656
844
|
// src/rules/no-raw-env.ts
|
|
657
845
|
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
846
|
+
function isProcessEnv(node) {
|
|
847
|
+
return !node.computed && node.object.type === "Identifier" && node.object.name === "process" && node.property.type === "Identifier" && node.property.name === "env";
|
|
848
|
+
}
|
|
849
|
+
function isImportMetaEnv(node) {
|
|
850
|
+
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";
|
|
851
|
+
}
|
|
658
852
|
var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
659
853
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
660
854
|
)({
|
|
@@ -673,10 +867,7 @@ var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
|
673
867
|
create(context) {
|
|
674
868
|
return {
|
|
675
869
|
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") {
|
|
870
|
+
if (isProcessEnv(node) || isImportMetaEnv(node)) {
|
|
680
871
|
context.report({
|
|
681
872
|
node,
|
|
682
873
|
messageId: "noRawEnv"
|
|
@@ -692,6 +883,33 @@ import {
|
|
|
692
883
|
ESLintUtils as ESLintUtils9,
|
|
693
884
|
AST_NODE_TYPES as AST_NODE_TYPES3
|
|
694
885
|
} from "@typescript-eslint/utils";
|
|
886
|
+
function sentinelKind(arg) {
|
|
887
|
+
if (arg === null) {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
if (arg.type === AST_NODE_TYPES3.Literal) {
|
|
891
|
+
if (arg.value === null) {
|
|
892
|
+
return "nullish";
|
|
893
|
+
}
|
|
894
|
+
if (typeof arg.value === "boolean") {
|
|
895
|
+
return "boolean";
|
|
896
|
+
}
|
|
897
|
+
if (typeof arg.value === "string") {
|
|
898
|
+
return "string";
|
|
899
|
+
}
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
if (arg.type === AST_NODE_TYPES3.Identifier && arg.name === "undefined") {
|
|
903
|
+
return "nullish";
|
|
904
|
+
}
|
|
905
|
+
if (arg.type === AST_NODE_TYPES3.ArrayExpression) {
|
|
906
|
+
return "array";
|
|
907
|
+
}
|
|
908
|
+
if (arg.type === AST_NODE_TYPES3.ObjectExpression) {
|
|
909
|
+
return "object";
|
|
910
|
+
}
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
695
913
|
function isSentinelArgument(arg) {
|
|
696
914
|
if (arg === null) {
|
|
697
915
|
return false;
|
|
@@ -713,17 +931,23 @@ function isSentinelArgument(arg) {
|
|
|
713
931
|
}
|
|
714
932
|
return false;
|
|
715
933
|
}
|
|
716
|
-
function
|
|
934
|
+
function isFunctionNode(node) {
|
|
935
|
+
return node.type === AST_NODE_TYPES3.FunctionDeclaration || node.type === AST_NODE_TYPES3.FunctionExpression || node.type === AST_NODE_TYPES3.ArrowFunctionExpression;
|
|
936
|
+
}
|
|
937
|
+
function isNode(value) {
|
|
938
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
939
|
+
}
|
|
940
|
+
function walkWithinScope(node, visit) {
|
|
717
941
|
let found = false;
|
|
718
|
-
const
|
|
942
|
+
const recurse = (current) => {
|
|
719
943
|
if (found) {
|
|
720
944
|
return;
|
|
721
945
|
}
|
|
722
|
-
if (current
|
|
946
|
+
if (visit(current)) {
|
|
723
947
|
found = true;
|
|
724
948
|
return;
|
|
725
949
|
}
|
|
726
|
-
if (current
|
|
950
|
+
if (isFunctionNode(current)) {
|
|
727
951
|
return;
|
|
728
952
|
}
|
|
729
953
|
for (const key of Object.keys(current)) {
|
|
@@ -734,19 +958,98 @@ function containsThrow(node) {
|
|
|
734
958
|
if (Array.isArray(value)) {
|
|
735
959
|
for (const child of value) {
|
|
736
960
|
if (isNode(child)) {
|
|
737
|
-
|
|
961
|
+
recurse(child);
|
|
738
962
|
}
|
|
739
963
|
}
|
|
740
964
|
} else if (isNode(value)) {
|
|
741
|
-
|
|
965
|
+
recurse(value);
|
|
742
966
|
}
|
|
743
967
|
}
|
|
744
968
|
};
|
|
745
|
-
|
|
969
|
+
recurse(node);
|
|
746
970
|
return found;
|
|
747
971
|
}
|
|
748
|
-
function
|
|
749
|
-
return
|
|
972
|
+
function containsThrow(node) {
|
|
973
|
+
return walkWithinScope(
|
|
974
|
+
node,
|
|
975
|
+
(current) => current.type === AST_NODE_TYPES3.ThrowStatement
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
function argsIncludeBinding(args, caughtName) {
|
|
979
|
+
if (caughtName === null) {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
return args.some(
|
|
983
|
+
(arg) => arg.type === AST_NODE_TYPES3.Identifier && arg.name === caughtName
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
function logsOrReportsError(catchBody, caughtName) {
|
|
987
|
+
return walkWithinScope(catchBody, (current) => {
|
|
988
|
+
if (current.type !== AST_NODE_TYPES3.CallExpression) {
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
if (isLoggingCall(current)) {
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
994
|
+
const name = calleeName(current.callee);
|
|
995
|
+
return name !== null && REPORT_NAME_RE.test(name) && argsIncludeBinding(current.arguments, caughtName);
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
function tryBlockOf(catchNode) {
|
|
999
|
+
return catchNode.parent.block;
|
|
1000
|
+
}
|
|
1001
|
+
function isSafeParseExpression(arg) {
|
|
1002
|
+
if (arg === null) {
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
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") {
|
|
1006
|
+
return true;
|
|
1007
|
+
}
|
|
1008
|
+
if (arg.type === AST_NODE_TYPES3.NewExpression && arg.callee.type === AST_NODE_TYPES3.Identifier) {
|
|
1009
|
+
return arg.callee.name === "RegExp" || arg.callee.name === "URL";
|
|
1010
|
+
}
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
function tryReturnsSafeParse(catchNode) {
|
|
1014
|
+
return walkWithinScope(
|
|
1015
|
+
tryBlockOf(catchNode),
|
|
1016
|
+
(current) => current.type === AST_NODE_TYPES3.ReturnStatement && isSafeParseExpression(current.argument)
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
function enclosingFunctionBody(node) {
|
|
1020
|
+
let current = node.parent;
|
|
1021
|
+
while (current !== void 0 && current !== null) {
|
|
1022
|
+
if (isFunctionNode(current) && "body" in current && isNode(current.body) && current.body.type === AST_NODE_TYPES3.BlockStatement) {
|
|
1023
|
+
return current.body;
|
|
1024
|
+
}
|
|
1025
|
+
current = current.parent;
|
|
1026
|
+
}
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
function functionReturnsSameSentinelKindElsewhere(catchNode, kind) {
|
|
1030
|
+
const functionBody = enclosingFunctionBody(catchNode);
|
|
1031
|
+
if (functionBody === null) {
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
return walkWithinScope(functionBody, (current) => {
|
|
1035
|
+
if (current.type !== AST_NODE_TYPES3.ReturnStatement) {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
if (isWithin(current, catchNode.body)) {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
return sentinelKind(current.argument) === kind;
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
function isWithin(node, ancestor) {
|
|
1045
|
+
let current = node;
|
|
1046
|
+
while (current !== void 0 && current !== null) {
|
|
1047
|
+
if (current === ancestor) {
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
current = current.parent;
|
|
1051
|
+
}
|
|
1052
|
+
return false;
|
|
750
1053
|
}
|
|
751
1054
|
var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
752
1055
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -755,11 +1058,11 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
755
1058
|
meta: {
|
|
756
1059
|
type: "problem",
|
|
757
1060
|
docs: {
|
|
758
|
-
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
|
|
1061
|
+
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
1062
|
},
|
|
760
1063
|
schema: [],
|
|
761
1064
|
messages: {
|
|
762
|
-
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it,
|
|
1065
|
+
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
1066
|
}
|
|
764
1067
|
},
|
|
765
1068
|
defaultOptions: [],
|
|
@@ -780,6 +1083,17 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
780
1083
|
if (containsThrow(node.body)) {
|
|
781
1084
|
return;
|
|
782
1085
|
}
|
|
1086
|
+
const caughtName = node.param?.type === AST_NODE_TYPES3.Identifier ? node.param.name : null;
|
|
1087
|
+
if (logsOrReportsError(node.body, caughtName)) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (tryReturnsSafeParse(node)) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const kind = sentinelKind(last.argument);
|
|
1094
|
+
if (kind !== null && functionReturnsSameSentinelKindElsewhere(node, kind)) {
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
783
1097
|
context.report({
|
|
784
1098
|
node: last,
|
|
785
1099
|
messageId: "noSentinelReturn"
|
|
@@ -791,12 +1105,90 @@ var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
|
791
1105
|
|
|
792
1106
|
// src/rules/no-sequential-await.ts
|
|
793
1107
|
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
1108
|
+
var ARRAY_ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map", "filter"]);
|
|
1109
|
+
var SEQUENTIAL_ITERABLE_HINT = /sort|reverse|ordered|sequence|hook|middleware|pipeline|\bstage|\bstep|\bphase|migration|chain/i;
|
|
794
1110
|
function isFunctionLike(node) {
|
|
795
1111
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
796
1112
|
}
|
|
797
1113
|
function isLoop(node) {
|
|
798
1114
|
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
799
1115
|
}
|
|
1116
|
+
function isNode2(value) {
|
|
1117
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1118
|
+
}
|
|
1119
|
+
function visitScope(root, visit) {
|
|
1120
|
+
visit(root);
|
|
1121
|
+
for (const key of Object.keys(root)) {
|
|
1122
|
+
if (key === "parent") {
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
const value = root[key];
|
|
1126
|
+
const children = Array.isArray(value) ? value : [value];
|
|
1127
|
+
for (const child of children) {
|
|
1128
|
+
if (isNode2(child) && !isFunctionLike(child) && !isLoop(child)) {
|
|
1129
|
+
visitScope(child, visit);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
function collectAwaits(root) {
|
|
1135
|
+
const awaits = [];
|
|
1136
|
+
visitScope(root, (node) => {
|
|
1137
|
+
if (node.type === "AwaitExpression") {
|
|
1138
|
+
awaits.push(node);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
return awaits;
|
|
1142
|
+
}
|
|
1143
|
+
function hasEarlyExit(root) {
|
|
1144
|
+
let found = false;
|
|
1145
|
+
visitScope(root, (node) => {
|
|
1146
|
+
if (node.type === "ReturnStatement" || node.type === "BreakStatement" || node.type === "ContinueStatement") {
|
|
1147
|
+
found = true;
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
return found;
|
|
1151
|
+
}
|
|
1152
|
+
function isTimerYield(node) {
|
|
1153
|
+
const arg = node.argument;
|
|
1154
|
+
return arg.type === "NewExpression" && arg.callee.type === "Identifier" && arg.callee.name === "Promise";
|
|
1155
|
+
}
|
|
1156
|
+
function referencesName(root, name) {
|
|
1157
|
+
let found = false;
|
|
1158
|
+
visitScope(root, (node) => {
|
|
1159
|
+
if (node.type === "Identifier" && node.name === name) {
|
|
1160
|
+
found = true;
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
return found;
|
|
1164
|
+
}
|
|
1165
|
+
function isThreadedAccumulator(node) {
|
|
1166
|
+
const parent = node.parent;
|
|
1167
|
+
let target = null;
|
|
1168
|
+
if (parent.type === "AssignmentExpression" && parent.operator === "=" && parent.right === node && parent.left.type === "Identifier") {
|
|
1169
|
+
target = parent.left.name;
|
|
1170
|
+
} else if (parent.type === "VariableDeclarator" && parent.init === node && parent.id.type === "Identifier") {
|
|
1171
|
+
target = parent.id.name;
|
|
1172
|
+
}
|
|
1173
|
+
if (target === null) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
return referencesName(node.argument, target);
|
|
1177
|
+
}
|
|
1178
|
+
function shouldReport(awaits, earlyExit, iterableText) {
|
|
1179
|
+
if (awaits.length === 0) {
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1182
|
+
if (earlyExit) {
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
if (iterableText !== null && SEQUENTIAL_ITERABLE_HINT.test(iterableText)) {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
return awaits.some(
|
|
1189
|
+
(node) => !isTimerYield(node) && !isThreadedAccumulator(node)
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
800
1192
|
var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
801
1193
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
802
1194
|
)({
|
|
@@ -813,54 +1205,36 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
813
1205
|
},
|
|
814
1206
|
defaultOptions: [],
|
|
815
1207
|
create(context) {
|
|
816
|
-
function
|
|
817
|
-
if (node.type === "
|
|
818
|
-
return node;
|
|
1208
|
+
function loopParts(node) {
|
|
1209
|
+
if (node.type === "ForStatement") {
|
|
1210
|
+
return [node.body, node.init, node.test, node.update];
|
|
819
1211
|
}
|
|
820
|
-
if (
|
|
821
|
-
return
|
|
1212
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1213
|
+
return [node.body, node.right];
|
|
822
1214
|
}
|
|
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
|
-
}
|
|
1215
|
+
return [node.body, node.test];
|
|
1216
|
+
}
|
|
1217
|
+
function iterableTextOf(node) {
|
|
1218
|
+
if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
1219
|
+
return context.sourceCode.getText(node.right);
|
|
843
1220
|
}
|
|
844
1221
|
return null;
|
|
845
1222
|
}
|
|
846
|
-
function isNode4(value) {
|
|
847
|
-
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
848
|
-
}
|
|
849
1223
|
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;
|
|
1224
|
+
const awaits = [];
|
|
1225
|
+
let earlyExit = false;
|
|
1226
|
+
for (const part of loopParts(node)) {
|
|
1227
|
+
if (part === null || isLoop(part)) {
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
awaits.push(...collectAwaits(part));
|
|
1231
|
+
if (!earlyExit && hasEarlyExit(part)) {
|
|
1232
|
+
earlyExit = true;
|
|
862
1233
|
}
|
|
863
1234
|
}
|
|
1235
|
+
if (shouldReport(awaits, earlyExit, iterableTextOf(node))) {
|
|
1236
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
1237
|
+
}
|
|
864
1238
|
}
|
|
865
1239
|
return {
|
|
866
1240
|
ForStatement: checkLoop,
|
|
@@ -872,6 +1246,28 @@ var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
|
872
1246
|
return;
|
|
873
1247
|
}
|
|
874
1248
|
checkLoop(node);
|
|
1249
|
+
},
|
|
1250
|
+
CallExpression(node) {
|
|
1251
|
+
const callee = node.callee;
|
|
1252
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
if (callee.property.type !== "Identifier" || !ARRAY_ITERATION_METHODS.has(callee.property.name)) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
const callback = node.arguments[0];
|
|
1259
|
+
if (callback === void 0 || !isFunctionLike(callback) || !("async" in callback && callback.async)) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (callee.property.name !== "forEach" && node.parent.type !== "ExpressionStatement") {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
const awaits = collectAwaits(callback.body);
|
|
1266
|
+
const earlyExit = hasEarlyExit(callback.body);
|
|
1267
|
+
const iterableText = context.sourceCode.getText(callee.object);
|
|
1268
|
+
if (shouldReport(awaits, earlyExit, iterableText)) {
|
|
1269
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
1270
|
+
}
|
|
875
1271
|
}
|
|
876
1272
|
};
|
|
877
1273
|
}
|
|
@@ -923,6 +1319,21 @@ function isStringInitializedVariable(variable) {
|
|
|
923
1319
|
}
|
|
924
1320
|
return isStringLiteralInit(declarator.init);
|
|
925
1321
|
}
|
|
1322
|
+
function isConcatOperand(node, target) {
|
|
1323
|
+
if (node.type === "Identifier") {
|
|
1324
|
+
return node.name === target;
|
|
1325
|
+
}
|
|
1326
|
+
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
1327
|
+
return isConcatOperand(node.left, target) || isConcatOperand(node.right, target);
|
|
1328
|
+
}
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
function isConcatOntoTarget(rhs, target) {
|
|
1332
|
+
if (rhs.type !== "BinaryExpression" || rhs.operator !== "+") {
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
return isConcatOperand(rhs.left, target) || isConcatOperand(rhs.right, target);
|
|
1336
|
+
}
|
|
926
1337
|
function isInsideLoopBody(node) {
|
|
927
1338
|
let child = node;
|
|
928
1339
|
let parent = node.parent;
|
|
@@ -956,10 +1367,11 @@ var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
|
956
1367
|
create(context) {
|
|
957
1368
|
return {
|
|
958
1369
|
AssignmentExpression(node) {
|
|
959
|
-
if (node.
|
|
1370
|
+
if (node.left.type !== "Identifier") {
|
|
960
1371
|
return;
|
|
961
1372
|
}
|
|
962
|
-
|
|
1373
|
+
const isAccumulation = node.operator === "+=" || node.operator === "=" && isConcatOntoTarget(node.right, node.left.name);
|
|
1374
|
+
if (!isAccumulation) {
|
|
963
1375
|
return;
|
|
964
1376
|
}
|
|
965
1377
|
if (!isInsideLoopBody(node)) {
|
|
@@ -1262,6 +1674,30 @@ var findVariable2 = (scope, name) => {
|
|
|
1262
1674
|
}
|
|
1263
1675
|
return null;
|
|
1264
1676
|
};
|
|
1677
|
+
var GUARD_NAME_RE = /^is[A-Z]/;
|
|
1678
|
+
var isGuardTestPosition = (node) => {
|
|
1679
|
+
let current = node;
|
|
1680
|
+
let parent = current.parent;
|
|
1681
|
+
while (parent !== void 0 && parent !== null) {
|
|
1682
|
+
switch (parent.type) {
|
|
1683
|
+
case AST_NODE_TYPES6.UnaryExpression:
|
|
1684
|
+
case AST_NODE_TYPES6.LogicalExpression:
|
|
1685
|
+
case AST_NODE_TYPES6.ChainExpression:
|
|
1686
|
+
current = parent;
|
|
1687
|
+
parent = parent.parent;
|
|
1688
|
+
continue;
|
|
1689
|
+
case AST_NODE_TYPES6.IfStatement:
|
|
1690
|
+
case AST_NODE_TYPES6.ConditionalExpression:
|
|
1691
|
+
case AST_NODE_TYPES6.WhileStatement:
|
|
1692
|
+
case AST_NODE_TYPES6.DoWhileStatement:
|
|
1693
|
+
case AST_NODE_TYPES6.ForStatement:
|
|
1694
|
+
return parent.test === current;
|
|
1695
|
+
default:
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return false;
|
|
1700
|
+
};
|
|
1265
1701
|
var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
1266
1702
|
const unwrapped = unwrap(node);
|
|
1267
1703
|
if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES6.Identifier) {
|
|
@@ -1340,6 +1776,22 @@ var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
|
1340
1776
|
}
|
|
1341
1777
|
}
|
|
1342
1778
|
},
|
|
1779
|
+
CallExpression(node) {
|
|
1780
|
+
if (node.callee.type !== AST_NODE_TYPES6.Identifier) return;
|
|
1781
|
+
if (!GUARD_NAME_RE.test(node.callee.name) && !isGuardTestPosition(node)) {
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
const scope = context.sourceCode.getScope(node);
|
|
1785
|
+
for (const arg of node.arguments) {
|
|
1786
|
+
if (arg.type === AST_NODE_TYPES6.SpreadElement) continue;
|
|
1787
|
+
const unwrapped = unwrap(arg);
|
|
1788
|
+
if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES6.Identifier) {
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1791
|
+
const variable = findVariable2(scope, unwrapped.name);
|
|
1792
|
+
if (variable !== null) unvalidatedVariables.delete(variable);
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1343
1795
|
MemberExpression(node) {
|
|
1344
1796
|
const scope = context.sourceCode.getScope(node);
|
|
1345
1797
|
const obj = unwrap(node.object);
|
|
@@ -1401,6 +1853,35 @@ var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
|
1401
1853
|
"lightingColor"
|
|
1402
1854
|
]);
|
|
1403
1855
|
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
1856
|
+
var STORIES_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
1857
|
+
var SVG_DEFS_CONTAINERS = /* @__PURE__ */ new Set([
|
|
1858
|
+
"mask",
|
|
1859
|
+
"clipPath",
|
|
1860
|
+
"defs",
|
|
1861
|
+
"pattern",
|
|
1862
|
+
"linearGradient",
|
|
1863
|
+
"radialGradient"
|
|
1864
|
+
]);
|
|
1865
|
+
var SVG_EXEMPT_COLOR_VALUES = /* @__PURE__ */ new Set([
|
|
1866
|
+
"#fff",
|
|
1867
|
+
"#ffffff",
|
|
1868
|
+
"#000",
|
|
1869
|
+
"#000000",
|
|
1870
|
+
"transparent",
|
|
1871
|
+
"none",
|
|
1872
|
+
"currentcolor",
|
|
1873
|
+
"inherit"
|
|
1874
|
+
]);
|
|
1875
|
+
var isInsideSvgDefsContainer = (node) => {
|
|
1876
|
+
let current = node.parent;
|
|
1877
|
+
while (current !== void 0 && current !== null) {
|
|
1878
|
+
if (current.type === AST_NODE_TYPES7.JSXElement && current.openingElement.name.type === AST_NODE_TYPES7.JSXIdentifier && SVG_DEFS_CONTAINERS.has(current.openingElement.name.name)) {
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
current = current.parent;
|
|
1882
|
+
}
|
|
1883
|
+
return false;
|
|
1884
|
+
};
|
|
1404
1885
|
var propName = (key) => {
|
|
1405
1886
|
if (key.type === AST_NODE_TYPES7.Identifier) return key.name;
|
|
1406
1887
|
if (key.type === AST_NODE_TYPES7.Literal && typeof key.value === "string") return key.value;
|
|
@@ -1424,6 +1905,7 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
1424
1905
|
},
|
|
1425
1906
|
defaultOptions: [],
|
|
1426
1907
|
create(context) {
|
|
1908
|
+
if (STORIES_FILE_RE.test(context.filename)) return {};
|
|
1427
1909
|
const reportClasses = (value, node) => {
|
|
1428
1910
|
for (const token of classTokens(value)) {
|
|
1429
1911
|
const base = tailwindBase(token);
|
|
@@ -1495,9 +1977,16 @@ var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
|
1495
1977
|
const name = propName(node.key);
|
|
1496
1978
|
if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
|
|
1497
1979
|
},
|
|
1498
|
-
// SVG presentation attributes: <path fill="#
|
|
1980
|
+
// SVG presentation attributes: <path fill="#7c3aed" stroke="#7c3aed" />.
|
|
1981
|
+
// Neutral drawing literals and anything inside an SVG defs container are
|
|
1982
|
+
// structural, not UI tokens, so they never fire.
|
|
1499
1983
|
"JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
|
|
1500
|
-
if (node.value?.type
|
|
1984
|
+
if (node.value?.type !== AST_NODE_TYPES7.Literal) return;
|
|
1985
|
+
if (typeof node.value.value === "string" && SVG_EXEMPT_COLOR_VALUES.has(node.value.value.toLowerCase())) {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
if (isInsideSvgDefsContainer(node)) return;
|
|
1989
|
+
checkColorValueNode(node.value);
|
|
1501
1990
|
},
|
|
1502
1991
|
// Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
|
|
1503
1992
|
"JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
|
|
@@ -1656,13 +2145,40 @@ var prefer_server_actions_default = ESLintUtils16.RuleCreator(
|
|
|
1656
2145
|
});
|
|
1657
2146
|
|
|
1658
2147
|
// src/rules/prefer-shadcn.ts
|
|
1659
|
-
import {
|
|
2148
|
+
import {
|
|
2149
|
+
AST_NODE_TYPES as AST_NODE_TYPES8,
|
|
2150
|
+
ESLintUtils as ESLintUtils17
|
|
2151
|
+
} from "@typescript-eslint/utils";
|
|
1660
2152
|
var REPLACEMENTS = {
|
|
1661
|
-
input: "Input",
|
|
1662
2153
|
select: "Select",
|
|
1663
2154
|
textarea: "Textarea",
|
|
1664
2155
|
dialog: "Dialog"
|
|
1665
2156
|
};
|
|
2157
|
+
var INPUT_TYPE_REPLACEMENTS = {
|
|
2158
|
+
checkbox: "Checkbox",
|
|
2159
|
+
radio: "RadioGroup",
|
|
2160
|
+
range: "Slider"
|
|
2161
|
+
};
|
|
2162
|
+
var SKIPPED_INPUT_TYPES = /* @__PURE__ */ new Set(["hidden"]);
|
|
2163
|
+
var kebabCase = (component) => component.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2164
|
+
var literalTypeAttr = (node) => {
|
|
2165
|
+
for (const attribute of node.attributes) {
|
|
2166
|
+
if (attribute.type !== AST_NODE_TYPES8.JSXAttribute || attribute.name.type !== AST_NODE_TYPES8.JSXIdentifier || attribute.name.name !== "type") {
|
|
2167
|
+
continue;
|
|
2168
|
+
}
|
|
2169
|
+
if (attribute.value?.type === AST_NODE_TYPES8.Literal && typeof attribute.value.value === "string") {
|
|
2170
|
+
return { kind: "literal", value: attribute.value.value.toLowerCase() };
|
|
2171
|
+
}
|
|
2172
|
+
return { kind: "dynamic" };
|
|
2173
|
+
}
|
|
2174
|
+
return null;
|
|
2175
|
+
};
|
|
2176
|
+
var resolveInputReplacement = (node) => {
|
|
2177
|
+
const typeAttr = literalTypeAttr(node);
|
|
2178
|
+
if (typeAttr === null || typeAttr.kind === "dynamic") return "Input";
|
|
2179
|
+
if (SKIPPED_INPUT_TYPES.has(typeAttr.value)) return null;
|
|
2180
|
+
return INPUT_TYPE_REPLACEMENTS[typeAttr.value] ?? "Input";
|
|
2181
|
+
};
|
|
1666
2182
|
var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
1667
2183
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1668
2184
|
)({
|
|
@@ -1685,8 +2201,8 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1685
2201
|
return;
|
|
1686
2202
|
}
|
|
1687
2203
|
const elementName = node.name.name;
|
|
1688
|
-
const replacement = REPLACEMENTS[elementName];
|
|
1689
|
-
if (replacement === void 0) {
|
|
2204
|
+
const replacement = elementName === "input" ? resolveInputReplacement(node) : REPLACEMENTS[elementName];
|
|
2205
|
+
if (replacement === void 0 || replacement === null) {
|
|
1690
2206
|
return;
|
|
1691
2207
|
}
|
|
1692
2208
|
context.report({
|
|
@@ -1695,7 +2211,7 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1695
2211
|
data: {
|
|
1696
2212
|
element: elementName,
|
|
1697
2213
|
replacement,
|
|
1698
|
-
lowercase:
|
|
2214
|
+
lowercase: kebabCase(replacement)
|
|
1699
2215
|
}
|
|
1700
2216
|
});
|
|
1701
2217
|
}
|
|
@@ -1704,40 +2220,59 @@ var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
|
1704
2220
|
});
|
|
1705
2221
|
|
|
1706
2222
|
// src/rules/require-assert-never.ts
|
|
1707
|
-
import {
|
|
2223
|
+
import {
|
|
2224
|
+
ESLintUtils as ESLintUtils18,
|
|
2225
|
+
AST_NODE_TYPES as AST_NODE_TYPES9
|
|
2226
|
+
} from "@typescript-eslint/utils";
|
|
1708
2227
|
var isAssertNeverCall = (expression) => {
|
|
1709
|
-
if (expression.type !==
|
|
2228
|
+
if (expression.type !== AST_NODE_TYPES9.CallExpression) return false;
|
|
1710
2229
|
const callee = expression.callee;
|
|
1711
|
-
if (callee.type ===
|
|
2230
|
+
if (callee.type === AST_NODE_TYPES9.Identifier) {
|
|
1712
2231
|
return callee.name === "assertNever";
|
|
1713
2232
|
}
|
|
1714
|
-
if (callee.type ===
|
|
2233
|
+
if (callee.type === AST_NODE_TYPES9.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES9.Identifier) {
|
|
1715
2234
|
return callee.property.name === "assertNever";
|
|
1716
2235
|
}
|
|
1717
2236
|
return false;
|
|
1718
2237
|
};
|
|
1719
2238
|
var statementContainsAssertNever = (statement) => {
|
|
1720
|
-
if (statement.type ===
|
|
2239
|
+
if (statement.type === AST_NODE_TYPES9.ExpressionStatement) {
|
|
1721
2240
|
return isAssertNeverCall(statement.expression);
|
|
1722
2241
|
}
|
|
1723
|
-
if (statement.type ===
|
|
2242
|
+
if (statement.type === AST_NODE_TYPES9.ThrowStatement) {
|
|
1724
2243
|
return isAssertNeverCall(statement.argument);
|
|
1725
2244
|
}
|
|
1726
|
-
if (statement.type ===
|
|
2245
|
+
if (statement.type === AST_NODE_TYPES9.ReturnStatement) {
|
|
1727
2246
|
return statement.argument !== null && isAssertNeverCall(statement.argument);
|
|
1728
2247
|
}
|
|
1729
|
-
if (statement.type ===
|
|
2248
|
+
if (statement.type === AST_NODE_TYPES9.BlockStatement) {
|
|
1730
2249
|
return statement.body.some(statementContainsAssertNever);
|
|
1731
2250
|
}
|
|
1732
2251
|
return false;
|
|
1733
2252
|
};
|
|
1734
2253
|
var isRuntimeHandlingStatement = (statement) => {
|
|
1735
|
-
if (statement.type ===
|
|
1736
|
-
if (statement.type ===
|
|
2254
|
+
if (statement.type === AST_NODE_TYPES9.EmptyStatement) return false;
|
|
2255
|
+
if (statement.type === AST_NODE_TYPES9.BlockStatement) {
|
|
1737
2256
|
return statement.body.some(isRuntimeHandlingStatement);
|
|
1738
2257
|
}
|
|
1739
2258
|
return true;
|
|
1740
2259
|
};
|
|
2260
|
+
var isFallthroughDefault = (node, defaultIndex) => {
|
|
2261
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2262
|
+
return defaultCase !== void 0 && defaultCase.consequent.length === 0 && defaultIndex < node.cases.length - 1;
|
|
2263
|
+
};
|
|
2264
|
+
var isCommentOnlyNoopDefault = (defaultCase, sourceCode) => {
|
|
2265
|
+
if (defaultCase.consequent.length === 0) {
|
|
2266
|
+
const defaultToken = sourceCode.getFirstToken(defaultCase);
|
|
2267
|
+
const colonToken = defaultToken ? sourceCode.getTokenAfter(defaultToken) : null;
|
|
2268
|
+
return colonToken !== null && sourceCode.getCommentsAfter(colonToken).length > 0;
|
|
2269
|
+
}
|
|
2270
|
+
const only = defaultCase.consequent[0];
|
|
2271
|
+
if (only !== void 0 && defaultCase.consequent.length === 1 && only.type === AST_NODE_TYPES9.BlockStatement && only.body.length === 0) {
|
|
2272
|
+
return sourceCode.getCommentsInside(only).length > 0;
|
|
2273
|
+
}
|
|
2274
|
+
return false;
|
|
2275
|
+
};
|
|
1741
2276
|
var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
1742
2277
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1743
2278
|
)({
|
|
@@ -1756,12 +2291,16 @@ var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
|
1756
2291
|
create(context) {
|
|
1757
2292
|
return {
|
|
1758
2293
|
SwitchStatement(node) {
|
|
1759
|
-
const
|
|
2294
|
+
const defaultIndex = node.cases.findIndex(
|
|
1760
2295
|
(caseNode) => caseNode.test === null
|
|
1761
2296
|
);
|
|
1762
|
-
if (
|
|
2297
|
+
if (defaultIndex === -1) return;
|
|
2298
|
+
const defaultCase = node.cases[defaultIndex];
|
|
2299
|
+
if (defaultCase === void 0) return;
|
|
1763
2300
|
if (defaultCase.consequent.some(statementContainsAssertNever)) return;
|
|
1764
2301
|
if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
|
|
2302
|
+
if (isFallthroughDefault(node, defaultIndex)) return;
|
|
2303
|
+
if (isCommentOnlyNoopDefault(defaultCase, context.sourceCode)) return;
|
|
1765
2304
|
context.report({
|
|
1766
2305
|
node: defaultCase,
|
|
1767
2306
|
messageId: "missingAssertNever"
|
|
@@ -1772,19 +2311,19 @@ var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
|
1772
2311
|
});
|
|
1773
2312
|
|
|
1774
2313
|
// src/rules/require-zod-form-validation.ts
|
|
1775
|
-
import { ESLintUtils as ESLintUtils19, AST_NODE_TYPES as
|
|
2314
|
+
import { ESLintUtils as ESLintUtils19, AST_NODE_TYPES as AST_NODE_TYPES10 } from "@typescript-eslint/utils";
|
|
1776
2315
|
var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
|
|
1777
2316
|
var looksLikeZodSchema = (node) => {
|
|
1778
2317
|
let current = node;
|
|
1779
2318
|
while (true) {
|
|
1780
|
-
if (current.type ===
|
|
2319
|
+
if (current.type === AST_NODE_TYPES10.Identifier) {
|
|
1781
2320
|
return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
|
|
1782
2321
|
}
|
|
1783
|
-
if (current.type ===
|
|
2322
|
+
if (current.type === AST_NODE_TYPES10.CallExpression) {
|
|
1784
2323
|
current = current.callee;
|
|
1785
2324
|
continue;
|
|
1786
2325
|
}
|
|
1787
|
-
if (current.type ===
|
|
2326
|
+
if (current.type === AST_NODE_TYPES10.MemberExpression) {
|
|
1788
2327
|
current = current.object;
|
|
1789
2328
|
continue;
|
|
1790
2329
|
}
|
|
@@ -1792,23 +2331,23 @@ var looksLikeZodSchema = (node) => {
|
|
|
1792
2331
|
}
|
|
1793
2332
|
};
|
|
1794
2333
|
var isZodParseCall = (node) => {
|
|
1795
|
-
if (node.type !==
|
|
2334
|
+
if (node.type !== AST_NODE_TYPES10.CallExpression) return false;
|
|
1796
2335
|
const callee = node.callee;
|
|
1797
|
-
if (callee.type !==
|
|
2336
|
+
if (callee.type !== AST_NODE_TYPES10.MemberExpression) return false;
|
|
1798
2337
|
if (callee.computed) return false;
|
|
1799
|
-
if (callee.property.type !==
|
|
2338
|
+
if (callee.property.type !== AST_NODE_TYPES10.Identifier) return false;
|
|
1800
2339
|
const method = callee.property.name;
|
|
1801
2340
|
if (method !== "parse" && method !== "safeParse") return false;
|
|
1802
2341
|
return looksLikeZodSchema(callee.object);
|
|
1803
2342
|
};
|
|
1804
2343
|
var isFormDataMethodCall = (node) => {
|
|
1805
2344
|
let current = node;
|
|
1806
|
-
if (current.type ===
|
|
2345
|
+
if (current.type === AST_NODE_TYPES10.AwaitExpression) {
|
|
1807
2346
|
current = current.argument;
|
|
1808
2347
|
}
|
|
1809
|
-
if (current.type !==
|
|
2348
|
+
if (current.type !== AST_NODE_TYPES10.CallExpression) return false;
|
|
1810
2349
|
const callee = current.callee;
|
|
1811
|
-
return callee.type ===
|
|
2350
|
+
return callee.type === AST_NODE_TYPES10.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES10.Identifier && callee.property.name === "formData";
|
|
1812
2351
|
};
|
|
1813
2352
|
var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
1814
2353
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -1827,14 +2366,14 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1827
2366
|
defaultOptions: [],
|
|
1828
2367
|
create(context) {
|
|
1829
2368
|
const isFormSourceIdentifier = (node) => {
|
|
1830
|
-
if (node.type !==
|
|
2369
|
+
if (node.type !== AST_NODE_TYPES10.Identifier) return false;
|
|
1831
2370
|
if (/formdata/i.test(node.name)) return true;
|
|
1832
2371
|
let scope = context.sourceCode.getScope(node);
|
|
1833
2372
|
while (scope !== null) {
|
|
1834
2373
|
const variable = scope.set.get(node.name);
|
|
1835
2374
|
if (variable !== void 0 && variable.defs.length === 1) {
|
|
1836
2375
|
const def = variable.defs[0];
|
|
1837
|
-
if (def !== void 0 && def.type === "Variable" && def.node.type ===
|
|
2376
|
+
if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES10.VariableDeclarator && def.node.init !== null) {
|
|
1838
2377
|
return isFormDataMethodCall(def.node.init);
|
|
1839
2378
|
}
|
|
1840
2379
|
return false;
|
|
@@ -1845,8 +2384,8 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1845
2384
|
};
|
|
1846
2385
|
const isFormDataGetCall = (node) => {
|
|
1847
2386
|
const callee = node.callee;
|
|
1848
|
-
if (callee.type !==
|
|
1849
|
-
if (callee.property.type !==
|
|
2387
|
+
if (callee.type !== AST_NODE_TYPES10.MemberExpression) return false;
|
|
2388
|
+
if (callee.property.type !== AST_NODE_TYPES10.Identifier || callee.property.name !== "get") {
|
|
1850
2389
|
return false;
|
|
1851
2390
|
}
|
|
1852
2391
|
return isFormSourceIdentifier(callee.object);
|
|
@@ -1869,15 +2408,15 @@ var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
|
1869
2408
|
});
|
|
1870
2409
|
|
|
1871
2410
|
// src/rules/zod-naming-convention.ts
|
|
1872
|
-
import { ESLintUtils as ESLintUtils20, AST_NODE_TYPES as
|
|
2411
|
+
import { ESLintUtils as ESLintUtils20, AST_NODE_TYPES as AST_NODE_TYPES11 } from "@typescript-eslint/utils";
|
|
1873
2412
|
var calleeChainStartsWithZ = (node) => {
|
|
1874
2413
|
let current = node;
|
|
1875
|
-
while (current.type ===
|
|
2414
|
+
while (current.type === AST_NODE_TYPES11.MemberExpression) {
|
|
1876
2415
|
const receiver = current.object;
|
|
1877
|
-
if (receiver.type ===
|
|
2416
|
+
if (receiver.type === AST_NODE_TYPES11.Identifier && receiver.name === "z") {
|
|
1878
2417
|
return true;
|
|
1879
2418
|
}
|
|
1880
|
-
if (receiver.type ===
|
|
2419
|
+
if (receiver.type === AST_NODE_TYPES11.CallExpression) {
|
|
1881
2420
|
current = receiver.callee;
|
|
1882
2421
|
continue;
|
|
1883
2422
|
}
|
|
@@ -1905,11 +2444,11 @@ var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
|
1905
2444
|
VariableDeclarator(node) {
|
|
1906
2445
|
const init = node.init;
|
|
1907
2446
|
if (init === null || init === void 0) return;
|
|
1908
|
-
if (init.type !==
|
|
2447
|
+
if (init.type !== AST_NODE_TYPES11.CallExpression) return;
|
|
1909
2448
|
const callee = init.callee;
|
|
1910
|
-
if (callee.type !==
|
|
2449
|
+
if (callee.type !== AST_NODE_TYPES11.MemberExpression) return;
|
|
1911
2450
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
1912
|
-
if (node.id.type !==
|
|
2451
|
+
if (node.id.type !== AST_NODE_TYPES11.Identifier) return;
|
|
1913
2452
|
const variableName = node.id.name;
|
|
1914
2453
|
if (variableName.startsWith("Z")) return;
|
|
1915
2454
|
context.report({
|
|
@@ -1954,17 +2493,17 @@ function subtreeContainsStarLiteral(node) {
|
|
|
1954
2493
|
const value = node[key];
|
|
1955
2494
|
if (Array.isArray(value)) {
|
|
1956
2495
|
for (const child of value) {
|
|
1957
|
-
if (
|
|
2496
|
+
if (isNode3(child) && subtreeContainsStarLiteral(child)) {
|
|
1958
2497
|
return true;
|
|
1959
2498
|
}
|
|
1960
2499
|
}
|
|
1961
|
-
} else if (
|
|
2500
|
+
} else if (isNode3(value) && subtreeContainsStarLiteral(value)) {
|
|
1962
2501
|
return true;
|
|
1963
2502
|
}
|
|
1964
2503
|
}
|
|
1965
2504
|
return false;
|
|
1966
2505
|
}
|
|
1967
|
-
function
|
|
2506
|
+
function isNode3(value) {
|
|
1968
2507
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
1969
2508
|
}
|
|
1970
2509
|
function propertyKeyName(prop) {
|
|
@@ -1980,7 +2519,7 @@ function propertyKeyName(prop) {
|
|
|
1980
2519
|
}
|
|
1981
2520
|
return void 0;
|
|
1982
2521
|
}
|
|
1983
|
-
function
|
|
2522
|
+
function calleeName2(node) {
|
|
1984
2523
|
const callee = node.callee;
|
|
1985
2524
|
if (callee.type === "Identifier") {
|
|
1986
2525
|
return callee.name;
|
|
@@ -1991,7 +2530,7 @@ function calleeName(node) {
|
|
|
1991
2530
|
return void 0;
|
|
1992
2531
|
}
|
|
1993
2532
|
function isCorsWildcardCredentialsCall(node) {
|
|
1994
|
-
const name =
|
|
2533
|
+
const name = calleeName2(node);
|
|
1995
2534
|
if (name === void 0 || name.toLowerCase() !== "cors") {
|
|
1996
2535
|
return false;
|
|
1997
2536
|
}
|
|
@@ -2134,13 +2673,13 @@ var no_cors_wildcard_with_credentials_default = ESLintUtils21.RuleCreator(
|
|
|
2134
2673
|
// src/rules/no-fat-try-blocks.ts
|
|
2135
2674
|
import {
|
|
2136
2675
|
ESLintUtils as ESLintUtils22,
|
|
2137
|
-
AST_NODE_TYPES as
|
|
2676
|
+
AST_NODE_TYPES as AST_NODE_TYPES12
|
|
2138
2677
|
} from "@typescript-eslint/utils";
|
|
2139
2678
|
var MAX_TRY_BODY_STATEMENTS = 3;
|
|
2140
2679
|
var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2680
|
+
AST_NODE_TYPES12.FunctionDeclaration,
|
|
2681
|
+
AST_NODE_TYPES12.FunctionExpression,
|
|
2682
|
+
AST_NODE_TYPES12.ArrowFunctionExpression
|
|
2144
2683
|
]);
|
|
2145
2684
|
var PURE_METHODS = /* @__PURE__ */ new Set([
|
|
2146
2685
|
"map",
|
|
@@ -2233,25 +2772,25 @@ var PURE_CONSTRUCTORS = /* @__PURE__ */ new Set([
|
|
|
2233
2772
|
"Response",
|
|
2234
2773
|
"AbortController"
|
|
2235
2774
|
]);
|
|
2236
|
-
function
|
|
2775
|
+
function isNode4(value) {
|
|
2237
2776
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
2238
2777
|
}
|
|
2239
2778
|
function isPureCall(node) {
|
|
2240
2779
|
const callee = node.callee;
|
|
2241
|
-
if (callee.type !==
|
|
2780
|
+
if (callee.type !== AST_NODE_TYPES12.MemberExpression) {
|
|
2242
2781
|
return false;
|
|
2243
2782
|
}
|
|
2244
2783
|
const property = callee.property;
|
|
2245
|
-
if (property.type !==
|
|
2784
|
+
if (property.type !== AST_NODE_TYPES12.Identifier) {
|
|
2246
2785
|
return false;
|
|
2247
2786
|
}
|
|
2248
|
-
if (callee.object.type ===
|
|
2787
|
+
if (callee.object.type === AST_NODE_TYPES12.Identifier && PURE_NAMESPACES.has(callee.object.name)) {
|
|
2249
2788
|
return true;
|
|
2250
2789
|
}
|
|
2251
2790
|
return PURE_METHODS.has(property.name);
|
|
2252
2791
|
}
|
|
2253
2792
|
function isPureNew(node) {
|
|
2254
|
-
return node.callee.type ===
|
|
2793
|
+
return node.callee.type === AST_NODE_TYPES12.Identifier && PURE_CONSTRUCTORS.has(node.callee.name);
|
|
2255
2794
|
}
|
|
2256
2795
|
function subtreeMatches(stmt, predicate) {
|
|
2257
2796
|
let found = false;
|
|
@@ -2273,11 +2812,11 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2273
2812
|
const value = current[key];
|
|
2274
2813
|
if (Array.isArray(value)) {
|
|
2275
2814
|
for (const child of value) {
|
|
2276
|
-
if (
|
|
2815
|
+
if (isNode4(child)) {
|
|
2277
2816
|
visit(child);
|
|
2278
2817
|
}
|
|
2279
2818
|
}
|
|
2280
|
-
} else if (
|
|
2819
|
+
} else if (isNode4(value)) {
|
|
2281
2820
|
visit(value);
|
|
2282
2821
|
}
|
|
2283
2822
|
if (found) {
|
|
@@ -2288,14 +2827,14 @@ function subtreeMatches(stmt, predicate) {
|
|
|
2288
2827
|
visit(stmt);
|
|
2289
2828
|
return found;
|
|
2290
2829
|
}
|
|
2291
|
-
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type ===
|
|
2830
|
+
var hasAwait = (stmt) => subtreeMatches(stmt, (n) => n.type === AST_NODE_TYPES12.AwaitExpression);
|
|
2292
2831
|
var hasThrowingCallOrNew = (stmt) => subtreeMatches(
|
|
2293
2832
|
stmt,
|
|
2294
|
-
(n) => n.type ===
|
|
2833
|
+
(n) => n.type === AST_NODE_TYPES12.CallExpression && !isPureCall(n) || n.type === AST_NODE_TYPES12.NewExpression && !isPureNew(n)
|
|
2295
2834
|
);
|
|
2296
2835
|
function unwrap2(expr) {
|
|
2297
2836
|
let current = expr;
|
|
2298
|
-
while (current.type ===
|
|
2837
|
+
while (current.type === AST_NODE_TYPES12.ChainExpression || current.type === AST_NODE_TYPES12.TSNonNullExpression) {
|
|
2299
2838
|
current = current.expression;
|
|
2300
2839
|
}
|
|
2301
2840
|
return current;
|
|
@@ -2304,7 +2843,7 @@ function canThrow(stmt) {
|
|
|
2304
2843
|
if (hasAwait(stmt)) {
|
|
2305
2844
|
return true;
|
|
2306
2845
|
}
|
|
2307
|
-
if (stmt.type ===
|
|
2846
|
+
if (stmt.type === AST_NODE_TYPES12.ExpressionStatement && unwrap2(stmt.expression).type === AST_NODE_TYPES12.CallExpression) {
|
|
2308
2847
|
return false;
|
|
2309
2848
|
}
|
|
2310
2849
|
return hasThrowingCallOrNew(stmt);
|
|
@@ -2315,7 +2854,7 @@ function handlerRethrows(handler) {
|
|
|
2315
2854
|
}
|
|
2316
2855
|
const body = handler.body.body;
|
|
2317
2856
|
const last = body[body.length - 1];
|
|
2318
|
-
return last !== void 0 && last.type ===
|
|
2857
|
+
return last !== void 0 && last.type === AST_NODE_TYPES12.ThrowStatement;
|
|
2319
2858
|
}
|
|
2320
2859
|
var no_fat_try_blocks_default = ESLintUtils22.RuleCreator(
|
|
2321
2860
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -2359,7 +2898,7 @@ var no_fat_try_blocks_default = ESLintUtils22.RuleCreator(
|
|
|
2359
2898
|
|
|
2360
2899
|
// src/rules/no-secret-in-log.ts
|
|
2361
2900
|
import { ESLintUtils as ESLintUtils23 } from "@typescript-eslint/utils";
|
|
2362
|
-
var
|
|
2901
|
+
var LOG_METHODS2 = /* @__PURE__ */ new Set([
|
|
2363
2902
|
"debug",
|
|
2364
2903
|
"info",
|
|
2365
2904
|
"warn",
|
|
@@ -2372,7 +2911,7 @@ var LOG_METHODS = /* @__PURE__ */ new Set([
|
|
|
2372
2911
|
"fatal",
|
|
2373
2912
|
"success"
|
|
2374
2913
|
]);
|
|
2375
|
-
var
|
|
2914
|
+
var LOGGER_NAMES2 = /* @__PURE__ */ new Set([
|
|
2376
2915
|
"logger",
|
|
2377
2916
|
"log",
|
|
2378
2917
|
"logging",
|
|
@@ -2513,12 +3052,12 @@ function isSecretKeyword(name) {
|
|
|
2513
3052
|
function isLoggerExpr(expr) {
|
|
2514
3053
|
switch (expr.type) {
|
|
2515
3054
|
case "Identifier":
|
|
2516
|
-
return
|
|
3055
|
+
return LOGGER_NAMES2.has(expr.name.toLowerCase());
|
|
2517
3056
|
case "MemberExpression": {
|
|
2518
3057
|
const { property, object } = expr;
|
|
2519
3058
|
if (!expr.computed && property.type === "Identifier") {
|
|
2520
3059
|
const lowered = property.name.toLowerCase();
|
|
2521
|
-
if (
|
|
3060
|
+
if (LOGGER_NAMES2.has(lowered) || LOGGER_FACTORIES.has(lowered)) {
|
|
2522
3061
|
return true;
|
|
2523
3062
|
}
|
|
2524
3063
|
}
|
|
@@ -2575,7 +3114,7 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2575
3114
|
return {
|
|
2576
3115
|
CallExpression(node) {
|
|
2577
3116
|
const callee = node.callee;
|
|
2578
|
-
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !
|
|
3117
|
+
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !LOG_METHODS2.has(callee.property.name)) {
|
|
2579
3118
|
return;
|
|
2580
3119
|
}
|
|
2581
3120
|
if (!isLoggerExpr(callee.object)) {
|
|
@@ -2592,6 +3131,16 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2592
3131
|
}
|
|
2593
3132
|
continue;
|
|
2594
3133
|
}
|
|
3134
|
+
if (arg.type === "MemberExpression") {
|
|
3135
|
+
if (!arg.computed && arg.property.type === "Identifier" && isSecretKeyword(arg.property.name)) {
|
|
3136
|
+
context.report({
|
|
3137
|
+
node: arg,
|
|
3138
|
+
messageId: "noSecretInLog",
|
|
3139
|
+
data: { name: arg.property.name }
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
continue;
|
|
3143
|
+
}
|
|
2595
3144
|
if (arg.type === "ObjectExpression") {
|
|
2596
3145
|
for (const prop of arg.properties) {
|
|
2597
3146
|
if (prop.type !== "Property") {
|
|
@@ -2616,7 +3165,7 @@ var no_secret_in_log_default = ESLintUtils23.RuleCreator(
|
|
|
2616
3165
|
// src/rules/prefer-string-literal-union.ts
|
|
2617
3166
|
import {
|
|
2618
3167
|
ESLintUtils as ESLintUtils24,
|
|
2619
|
-
AST_NODE_TYPES as
|
|
3168
|
+
AST_NODE_TYPES as AST_NODE_TYPES13
|
|
2620
3169
|
} from "@typescript-eslint/utils";
|
|
2621
3170
|
import * as ts from "typescript";
|
|
2622
3171
|
var CHOICE_TOKENS = /* @__PURE__ */ new Set([
|
|
@@ -2660,19 +3209,19 @@ function isChoiceLikeName(name) {
|
|
|
2660
3209
|
return CHOICE_TOKENS.has(lastWord(name));
|
|
2661
3210
|
}
|
|
2662
3211
|
function keyName(key) {
|
|
2663
|
-
if (key.type ===
|
|
3212
|
+
if (key.type === AST_NODE_TYPES13.Identifier) {
|
|
2664
3213
|
return key.name;
|
|
2665
3214
|
}
|
|
2666
|
-
if (key.type ===
|
|
3215
|
+
if (key.type === AST_NODE_TYPES13.Literal && typeof key.value === "string") {
|
|
2667
3216
|
return key.value;
|
|
2668
3217
|
}
|
|
2669
3218
|
return null;
|
|
2670
3219
|
}
|
|
2671
3220
|
function isStringLiteralMember(t) {
|
|
2672
|
-
return t.type ===
|
|
3221
|
+
return t.type === AST_NODE_TYPES13.TSLiteralType && t.literal.type === AST_NODE_TYPES13.Literal && typeof t.literal.value === "string";
|
|
2673
3222
|
}
|
|
2674
3223
|
function isStringLiteralUnion(node) {
|
|
2675
|
-
if (node?.type !==
|
|
3224
|
+
if (node?.type !== AST_NODE_TYPES13.TSUnionType) {
|
|
2676
3225
|
return false;
|
|
2677
3226
|
}
|
|
2678
3227
|
return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
|
|
@@ -2681,13 +3230,32 @@ function typeHasRawString(type) {
|
|
|
2681
3230
|
const parts = type.isUnion() ? type.types : [type];
|
|
2682
3231
|
return parts.some((t) => (t.flags & ts.TypeFlags.String) !== 0);
|
|
2683
3232
|
}
|
|
3233
|
+
function isExternalSourceFile(sf) {
|
|
3234
|
+
if (sf === void 0) {
|
|
3235
|
+
return false;
|
|
3236
|
+
}
|
|
3237
|
+
return sf.isDeclarationFile || sf.fileName.includes("/node_modules/");
|
|
3238
|
+
}
|
|
3239
|
+
function symbolIsExternallyDeclared(sym) {
|
|
3240
|
+
return sym?.declarations?.some((d) => isExternalSourceFile(d.getSourceFile())) ?? false;
|
|
3241
|
+
}
|
|
3242
|
+
function bindingSourceExpression(decl) {
|
|
3243
|
+
let node = decl.parent;
|
|
3244
|
+
while (!ts.isForOfStatement(node) && !(ts.isVariableDeclaration(node) && node.initializer !== void 0)) {
|
|
3245
|
+
if (node.parent === void 0) {
|
|
3246
|
+
return void 0;
|
|
3247
|
+
}
|
|
3248
|
+
node = node.parent;
|
|
3249
|
+
}
|
|
3250
|
+
return ts.isForOfStatement(node) ? node.expression : node.initializer;
|
|
3251
|
+
}
|
|
2684
3252
|
function refKey(node) {
|
|
2685
|
-
if (node.type ===
|
|
3253
|
+
if (node.type === AST_NODE_TYPES13.Identifier) {
|
|
2686
3254
|
return node.name;
|
|
2687
3255
|
}
|
|
2688
|
-
if (node.type ===
|
|
3256
|
+
if (node.type === AST_NODE_TYPES13.MemberExpression && !node.computed) {
|
|
2689
3257
|
const inner = refKey(node.object);
|
|
2690
|
-
if (inner === null || node.property.type !==
|
|
3258
|
+
if (inner === null || node.property.type !== AST_NODE_TYPES13.Identifier) {
|
|
2691
3259
|
return null;
|
|
2692
3260
|
}
|
|
2693
3261
|
return `${inner}.${node.property.name}`;
|
|
@@ -2695,7 +3263,7 @@ function refKey(node) {
|
|
|
2695
3263
|
return null;
|
|
2696
3264
|
}
|
|
2697
3265
|
function strLiteral(node) {
|
|
2698
|
-
if (node.type ===
|
|
3266
|
+
if (node.type === AST_NODE_TYPES13.Literal && typeof node.value === "string") {
|
|
2699
3267
|
return node.value;
|
|
2700
3268
|
}
|
|
2701
3269
|
return null;
|
|
@@ -2738,6 +3306,37 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2738
3306
|
}
|
|
2739
3307
|
return typeHasRawString(services.getTypeAtLocation(node));
|
|
2740
3308
|
}
|
|
3309
|
+
function originIsExternal(node, depth) {
|
|
3310
|
+
if (node === void 0 || services === null || depth > 6) {
|
|
3311
|
+
return false;
|
|
3312
|
+
}
|
|
3313
|
+
const checker = services.program.getTypeChecker();
|
|
3314
|
+
if (ts.isParenthesizedExpression(node) || ts.isNonNullExpression(node) || ts.isAsExpression(node)) {
|
|
3315
|
+
return originIsExternal(node.expression, depth + 1);
|
|
3316
|
+
}
|
|
3317
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
3318
|
+
return symbolIsExternallyDeclared(checker.getSymbolAtLocation(node.name));
|
|
3319
|
+
}
|
|
3320
|
+
if (ts.isCallExpression(node)) {
|
|
3321
|
+
return originIsExternal(node.expression, depth + 1);
|
|
3322
|
+
}
|
|
3323
|
+
if (ts.isIdentifier(node)) {
|
|
3324
|
+
const decl = checker.getSymbolAtLocation(node)?.valueDeclaration;
|
|
3325
|
+
if (decl === void 0) {
|
|
3326
|
+
return false;
|
|
3327
|
+
}
|
|
3328
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer !== void 0) {
|
|
3329
|
+
return originIsExternal(decl.initializer, depth + 1);
|
|
3330
|
+
}
|
|
3331
|
+
if (ts.isBindingElement(decl)) {
|
|
3332
|
+
return originIsExternal(bindingSourceExpression(decl), depth + 1);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
return false;
|
|
3336
|
+
}
|
|
3337
|
+
function operandIsFlaggable(node) {
|
|
3338
|
+
return operandIsRawString(node) && !originIsExternal(services?.esTreeNodeToTSNodeMap.get(node), 0);
|
|
3339
|
+
}
|
|
2741
3340
|
function pushScope() {
|
|
2742
3341
|
scopeStack.push({ clusters: /* @__PURE__ */ new Map() });
|
|
2743
3342
|
}
|
|
@@ -2777,7 +3376,7 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2777
3376
|
containersWithUnion.add(container);
|
|
2778
3377
|
return;
|
|
2779
3378
|
}
|
|
2780
|
-
if (typeNode?.type !==
|
|
3379
|
+
if (typeNode?.type !== AST_NODE_TYPES13.TSStringKeyword) {
|
|
2781
3380
|
return;
|
|
2782
3381
|
}
|
|
2783
3382
|
const name = keyName(key);
|
|
@@ -2802,18 +3401,18 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2802
3401
|
const rightKey = refKey(node.right);
|
|
2803
3402
|
const leftLit = strLiteral(node.left);
|
|
2804
3403
|
if (leftKey !== null && rightLit !== null) {
|
|
2805
|
-
if (
|
|
3404
|
+
if (operandIsFlaggable(node.left)) {
|
|
2806
3405
|
accumulate(leftKey, [rightLit], node);
|
|
2807
3406
|
}
|
|
2808
3407
|
} else if (rightKey !== null && leftLit !== null) {
|
|
2809
|
-
if (
|
|
3408
|
+
if (operandIsFlaggable(node.right)) {
|
|
2810
3409
|
accumulate(rightKey, [leftLit], node);
|
|
2811
3410
|
}
|
|
2812
3411
|
}
|
|
2813
3412
|
},
|
|
2814
3413
|
SwitchStatement(node) {
|
|
2815
3414
|
const key = refKey(node.discriminant);
|
|
2816
|
-
if (key === null || !
|
|
3415
|
+
if (key === null || !operandIsFlaggable(node.discriminant)) {
|
|
2817
3416
|
return;
|
|
2818
3417
|
}
|
|
2819
3418
|
const literals = [];
|
|
@@ -2865,10 +3464,10 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2865
3464
|
}
|
|
2866
3465
|
};
|
|
2867
3466
|
function refKeyText(node) {
|
|
2868
|
-
if (node.type ===
|
|
3467
|
+
if (node.type === AST_NODE_TYPES13.BinaryExpression) {
|
|
2869
3468
|
return refKey(node.left) ?? refKey(node.right) ?? "value";
|
|
2870
3469
|
}
|
|
2871
|
-
if (node.type ===
|
|
3470
|
+
if (node.type === AST_NODE_TYPES13.SwitchStatement) {
|
|
2872
3471
|
return refKey(node.discriminant) ?? "value";
|
|
2873
3472
|
}
|
|
2874
3473
|
return "value";
|
|
@@ -2877,7 +3476,7 @@ var prefer_string_literal_union_default = ESLintUtils24.RuleCreator(
|
|
|
2877
3476
|
});
|
|
2878
3477
|
|
|
2879
3478
|
// src/rules/single-public-export.ts
|
|
2880
|
-
import { ESLintUtils as ESLintUtils25, AST_NODE_TYPES as
|
|
3479
|
+
import { ESLintUtils as ESLintUtils25, AST_NODE_TYPES as AST_NODE_TYPES14 } from "@typescript-eslint/utils";
|
|
2881
3480
|
var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
|
|
2882
3481
|
"util",
|
|
2883
3482
|
"utils",
|
|
@@ -2904,20 +3503,20 @@ var TEST_FILE_RE = /\.(test|spec)\.[cm]?[jt]sx?$/i;
|
|
|
2904
3503
|
var SCRIPT_EXT_RE = /\.[cm]?[jt]sx?$/i;
|
|
2905
3504
|
var basename = (filename) => filename.split(/[/\\]/).pop() ?? filename;
|
|
2906
3505
|
var stemOf = (base) => base.replace(SCRIPT_EXT_RE, "");
|
|
2907
|
-
var
|
|
3506
|
+
var kebabCase2 = (name) => {
|
|
2908
3507
|
let normalized = name;
|
|
2909
3508
|
for (const [pattern, replacement] of ACRONYM_OVERRIDES) {
|
|
2910
3509
|
normalized = normalized.replace(pattern, replacement);
|
|
2911
3510
|
}
|
|
2912
3511
|
return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
|
|
2913
3512
|
};
|
|
2914
|
-
var
|
|
3513
|
+
var isFunctionExpression = (node) => node !== null && (node.type === AST_NODE_TYPES14.ArrowFunctionExpression || node.type === AST_NODE_TYPES14.FunctionExpression);
|
|
2915
3514
|
var functionConstName = (decl) => {
|
|
2916
3515
|
if (decl.declarations.length !== 1) return null;
|
|
2917
3516
|
const [declarator] = decl.declarations;
|
|
2918
3517
|
if (declarator === void 0) return null;
|
|
2919
|
-
if (declarator.id.type !==
|
|
2920
|
-
if (!
|
|
3518
|
+
if (declarator.id.type !== AST_NODE_TYPES14.Identifier) return null;
|
|
3519
|
+
if (!isFunctionExpression(declarator.init)) return null;
|
|
2921
3520
|
return declarator.id.name;
|
|
2922
3521
|
};
|
|
2923
3522
|
var summarizeExports = (body) => {
|
|
@@ -2930,20 +3529,20 @@ var summarizeExports = (body) => {
|
|
|
2930
3529
|
};
|
|
2931
3530
|
for (const statement of body) {
|
|
2932
3531
|
switch (statement.type) {
|
|
2933
|
-
case
|
|
3532
|
+
case AST_NODE_TYPES14.ExportAllDeclaration:
|
|
2934
3533
|
hasReExport = true;
|
|
2935
3534
|
break;
|
|
2936
|
-
case
|
|
3535
|
+
case AST_NODE_TYPES14.ExportDefaultDeclaration: {
|
|
2937
3536
|
names += 1;
|
|
2938
3537
|
const decl = statement.declaration;
|
|
2939
|
-
if (decl.type ===
|
|
3538
|
+
if (decl.type === AST_NODE_TYPES14.FunctionDeclaration && decl.id !== null) {
|
|
2940
3539
|
candidate = { name: decl.id.name, node: statement };
|
|
2941
|
-
} else if (decl.type ===
|
|
3540
|
+
} else if (decl.type === AST_NODE_TYPES14.ClassDeclaration && decl.id !== null) {
|
|
2942
3541
|
candidate = { name: decl.id.name, node: statement };
|
|
2943
3542
|
}
|
|
2944
3543
|
break;
|
|
2945
3544
|
}
|
|
2946
|
-
case
|
|
3545
|
+
case AST_NODE_TYPES14.ExportNamedDeclaration: {
|
|
2947
3546
|
if (statement.source !== null) {
|
|
2948
3547
|
hasReExport = true;
|
|
2949
3548
|
break;
|
|
@@ -2954,15 +3553,15 @@ var summarizeExports = (body) => {
|
|
|
2954
3553
|
break;
|
|
2955
3554
|
}
|
|
2956
3555
|
switch (decl.type) {
|
|
2957
|
-
case
|
|
3556
|
+
case AST_NODE_TYPES14.FunctionDeclaration:
|
|
2958
3557
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
2959
3558
|
else names += 1;
|
|
2960
3559
|
break;
|
|
2961
|
-
case
|
|
3560
|
+
case AST_NODE_TYPES14.ClassDeclaration:
|
|
2962
3561
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
2963
3562
|
else names += 1;
|
|
2964
3563
|
break;
|
|
2965
|
-
case
|
|
3564
|
+
case AST_NODE_TYPES14.VariableDeclaration: {
|
|
2966
3565
|
const fnName = functionConstName(decl);
|
|
2967
3566
|
if (fnName !== null && decl.declarations.length === 1) {
|
|
2968
3567
|
addCandidate(fnName, statement);
|
|
@@ -3009,7 +3608,7 @@ var single_public_export_default = ESLintUtils25.RuleCreator(
|
|
|
3009
3608
|
if (hasReExport) return;
|
|
3010
3609
|
if (names !== 1 || candidate === null) return;
|
|
3011
3610
|
if (CONVENTIONAL_BUCKET_EXPORTS.has(candidate.name)) return;
|
|
3012
|
-
const expected =
|
|
3611
|
+
const expected = kebabCase2(candidate.name);
|
|
3013
3612
|
if (stem === expected) return;
|
|
3014
3613
|
context.report({
|
|
3015
3614
|
node: candidate.node,
|
|
@@ -3052,7 +3651,7 @@ var rules = {
|
|
|
3052
3651
|
var plugin = {
|
|
3053
3652
|
meta: {
|
|
3054
3653
|
name: "@sarj/eslint-plugin",
|
|
3055
|
-
version: "2.
|
|
3654
|
+
version: "2.4.0"
|
|
3056
3655
|
},
|
|
3057
3656
|
rules,
|
|
3058
3657
|
configs: {
|