@sarj/eslint-plugin 2.0.2 → 2.1.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 +710 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +715 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.cjs
CHANGED
|
@@ -345,9 +345,260 @@ var no_enum_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
345
345
|
}
|
|
346
346
|
});
|
|
347
347
|
|
|
348
|
-
// src/rules/no-
|
|
348
|
+
// src/rules/no-insecure-random-id.ts
|
|
349
349
|
var import_utils4 = require("@typescript-eslint/utils");
|
|
350
|
-
var
|
|
350
|
+
var NAME_PATTERN = /id|token|key|secret|uuid|nonce|session|password|salt/i;
|
|
351
|
+
function isMathRandomCall(node) {
|
|
352
|
+
if (node.type !== "CallExpression") {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const callee = node.callee;
|
|
356
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
const { object, property } = callee;
|
|
360
|
+
return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
|
|
361
|
+
}
|
|
362
|
+
function isPartOfToString36Chain(node) {
|
|
363
|
+
let current = node;
|
|
364
|
+
let parent = current.parent;
|
|
365
|
+
while (parent) {
|
|
366
|
+
if (parent.type === "MemberExpression" && parent.object === current && !parent.computed && parent.property.type === "Identifier" && parent.property.name === "toString") {
|
|
367
|
+
const grandparent = parent.parent;
|
|
368
|
+
if (grandparent && grandparent.type === "CallExpression" && grandparent.callee === parent) {
|
|
369
|
+
const firstArg = grandparent.arguments[0];
|
|
370
|
+
if (firstArg && firstArg.type === "Literal" && firstArg.value === 36) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (parent.type === "MemberExpression" && parent.object === current) {
|
|
376
|
+
current = parent;
|
|
377
|
+
parent = current.parent;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
381
|
+
current = parent;
|
|
382
|
+
parent = current.parent;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
function findEnclosingName(node) {
|
|
390
|
+
let current = node;
|
|
391
|
+
let parent = current.parent;
|
|
392
|
+
while (parent) {
|
|
393
|
+
if (parent.type === "VariableDeclarator" && parent.init === current) {
|
|
394
|
+
if (parent.id.type === "Identifier") {
|
|
395
|
+
return parent.id.name;
|
|
396
|
+
}
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
if (parent.type === "Property" && parent.value === current) {
|
|
400
|
+
const key = parent.key;
|
|
401
|
+
if (!parent.computed && key.type === "Identifier") {
|
|
402
|
+
return key.name;
|
|
403
|
+
}
|
|
404
|
+
if (key.type === "Literal" && typeof key.value === "string") {
|
|
405
|
+
return key.value;
|
|
406
|
+
}
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
if (parent.type === "PropertyDefinition" && parent.value === current) {
|
|
410
|
+
const key = parent.key;
|
|
411
|
+
if (!parent.computed && key.type === "Identifier") {
|
|
412
|
+
return key.name;
|
|
413
|
+
}
|
|
414
|
+
if (key.type === "Literal" && typeof key.value === "string") {
|
|
415
|
+
return key.value;
|
|
416
|
+
}
|
|
417
|
+
return void 0;
|
|
418
|
+
}
|
|
419
|
+
if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression" || parent.type === "BlockStatement" || parent.type === "ReturnStatement" || parent.type === "ExpressionStatement") {
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
current = parent;
|
|
423
|
+
parent = current.parent;
|
|
424
|
+
}
|
|
425
|
+
return void 0;
|
|
426
|
+
}
|
|
427
|
+
var no_insecure_random_id_default = import_utils4.ESLintUtils.RuleCreator(
|
|
428
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
429
|
+
)({
|
|
430
|
+
name: "no-insecure-random-id",
|
|
431
|
+
meta: {
|
|
432
|
+
type: "problem",
|
|
433
|
+
docs: {
|
|
434
|
+
description: "Disallow using `Math.random()` to generate identifiers, tokens, or secrets; use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead."
|
|
435
|
+
},
|
|
436
|
+
schema: [],
|
|
437
|
+
messages: {
|
|
438
|
+
insecureRandomId: "`Math.random()` is not cryptographically secure and is predictable; do not use it to generate IDs, tokens, or secrets. Use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead."
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
defaultOptions: [],
|
|
442
|
+
create(context) {
|
|
443
|
+
return {
|
|
444
|
+
CallExpression(node) {
|
|
445
|
+
if (!isMathRandomCall(node)) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (isPartOfToString36Chain(node)) {
|
|
449
|
+
context.report({ node, messageId: "insecureRandomId" });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const name = findEnclosingName(node);
|
|
453
|
+
if (name !== void 0 && NAME_PATTERN.test(name)) {
|
|
454
|
+
context.report({ node, messageId: "insecureRandomId" });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// src/rules/no-json-stringify-error.ts
|
|
462
|
+
var import_utils5 = require("@typescript-eslint/utils");
|
|
463
|
+
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
464
|
+
function isCatchBinding(scope, name) {
|
|
465
|
+
let current = scope;
|
|
466
|
+
while (current) {
|
|
467
|
+
const variable = current.set.get(name);
|
|
468
|
+
if (variable) {
|
|
469
|
+
for (const def of variable.defs) {
|
|
470
|
+
if (def.type === "CatchClause") {
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
current = current.upper;
|
|
476
|
+
}
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
function isJsonStringify(callee) {
|
|
480
|
+
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
481
|
+
}
|
|
482
|
+
var no_json_stringify_error_default = import_utils5.ESLintUtils.RuleCreator(
|
|
483
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
484
|
+
)({
|
|
485
|
+
name: "no-json-stringify-error",
|
|
486
|
+
meta: {
|
|
487
|
+
type: "problem",
|
|
488
|
+
docs: {
|
|
489
|
+
description: "Disallow `JSON.stringify` on an Error value; it yields `{}` because `message`/`stack` are non-enumerable."
|
|
490
|
+
},
|
|
491
|
+
schema: [],
|
|
492
|
+
messages: {
|
|
493
|
+
noJsonStringifyError: "`JSON.stringify` on an Error yields `{}` because `message`/`stack` are non-enumerable. Log `err.message` / `err.stack`, or use a proper error serializer."
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
defaultOptions: [],
|
|
497
|
+
create(context) {
|
|
498
|
+
return {
|
|
499
|
+
CallExpression(node) {
|
|
500
|
+
if (!isJsonStringify(node.callee)) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const firstArg = node.arguments[0];
|
|
504
|
+
if (!firstArg || firstArg.type !== "Identifier") {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const name = firstArg.name;
|
|
508
|
+
const scope = context.sourceCode.getScope(firstArg);
|
|
509
|
+
if (ERROR_NAME_PATTERN.test(name) || isCatchBinding(scope, name)) {
|
|
510
|
+
context.report({
|
|
511
|
+
node,
|
|
512
|
+
messageId: "noJsonStringifyError"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// src/rules/no-log-only-catch.ts
|
|
521
|
+
var import_utils6 = require("@typescript-eslint/utils");
|
|
522
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
523
|
+
/\.test\./,
|
|
524
|
+
/\.spec\./,
|
|
525
|
+
/[\\/]__tests__[\\/]/
|
|
526
|
+
];
|
|
527
|
+
var CONSOLE_METHODS = /* @__PURE__ */ new Set([
|
|
528
|
+
"log",
|
|
529
|
+
"error",
|
|
530
|
+
"warn",
|
|
531
|
+
"info",
|
|
532
|
+
"debug"
|
|
533
|
+
]);
|
|
534
|
+
function isConsoleCallStatement(statement) {
|
|
535
|
+
if (statement.type !== "ExpressionStatement") {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
538
|
+
const expr = statement.expression;
|
|
539
|
+
if (expr.type !== "CallExpression") {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
const callee = expr.callee;
|
|
543
|
+
if (callee.type !== "MemberExpression") {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
const { object, property } = callee;
|
|
547
|
+
if (object.type !== "Identifier" || object.name !== "console") {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
if (callee.computed) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
if (property.type !== "Identifier") {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
return CONSOLE_METHODS.has(property.name);
|
|
557
|
+
}
|
|
558
|
+
var no_log_only_catch_default = import_utils6.ESLintUtils.RuleCreator(
|
|
559
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
560
|
+
)({
|
|
561
|
+
name: "no-log-only-catch",
|
|
562
|
+
meta: {
|
|
563
|
+
type: "problem",
|
|
564
|
+
docs: {
|
|
565
|
+
description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
|
|
566
|
+
},
|
|
567
|
+
schema: [],
|
|
568
|
+
messages: {
|
|
569
|
+
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
defaultOptions: [],
|
|
573
|
+
create(context) {
|
|
574
|
+
const filename = context.filename;
|
|
575
|
+
const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS2.some(
|
|
576
|
+
(re) => re.test(filename)
|
|
577
|
+
);
|
|
578
|
+
if (isIgnoredByDefault) {
|
|
579
|
+
return {};
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
CatchClause(node) {
|
|
583
|
+
const statements = node.body.body;
|
|
584
|
+
if (statements.length === 0) {
|
|
585
|
+
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const everyStatementIsConsoleLog = statements.every(
|
|
589
|
+
(statement) => isConsoleCallStatement(statement)
|
|
590
|
+
);
|
|
591
|
+
if (everyStatementIsConsoleLog) {
|
|
592
|
+
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
// src/rules/no-raw-env.ts
|
|
600
|
+
var import_utils7 = require("@typescript-eslint/utils");
|
|
601
|
+
var no_raw_env_default = import_utils7.ESLintUtils.RuleCreator(
|
|
351
602
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
352
603
|
)({
|
|
353
604
|
name: "no-raw-env",
|
|
@@ -379,8 +630,300 @@ var no_raw_env_default = import_utils4.ESLintUtils.RuleCreator(
|
|
|
379
630
|
}
|
|
380
631
|
});
|
|
381
632
|
|
|
633
|
+
// src/rules/no-sentinel-return-on-catch.ts
|
|
634
|
+
var import_utils8 = require("@typescript-eslint/utils");
|
|
635
|
+
function isSentinelArgument(arg) {
|
|
636
|
+
if (arg === null) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
639
|
+
if (arg.type === import_utils8.AST_NODE_TYPES.Literal && arg.value === null) {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
if (arg.type === import_utils8.AST_NODE_TYPES.Literal && arg.value === false) {
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
if (arg.type === import_utils8.AST_NODE_TYPES.Identifier && arg.name === "undefined") {
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
if (arg.type === import_utils8.AST_NODE_TYPES.ArrayExpression && arg.elements.length === 0) {
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
if (arg.type === import_utils8.AST_NODE_TYPES.ObjectExpression && arg.properties.length === 0) {
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
function containsThrow(node) {
|
|
657
|
+
let found = false;
|
|
658
|
+
const visit = (current) => {
|
|
659
|
+
if (found) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (current.type === import_utils8.AST_NODE_TYPES.ThrowStatement) {
|
|
663
|
+
found = true;
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (current.type === import_utils8.AST_NODE_TYPES.FunctionDeclaration || current.type === import_utils8.AST_NODE_TYPES.FunctionExpression || current.type === import_utils8.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
for (const key of Object.keys(current)) {
|
|
670
|
+
if (key === "parent") {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const value = current[key];
|
|
674
|
+
if (Array.isArray(value)) {
|
|
675
|
+
for (const child of value) {
|
|
676
|
+
if (isNode(child)) {
|
|
677
|
+
visit(child);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} else if (isNode(value)) {
|
|
681
|
+
visit(value);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
visit(node);
|
|
686
|
+
return found;
|
|
687
|
+
}
|
|
688
|
+
function isNode(value) {
|
|
689
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
690
|
+
}
|
|
691
|
+
var no_sentinel_return_on_catch_default = import_utils8.ESLintUtils.RuleCreator(
|
|
692
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
693
|
+
)({
|
|
694
|
+
name: "no-sentinel-return-on-catch",
|
|
695
|
+
meta: {
|
|
696
|
+
type: "problem",
|
|
697
|
+
docs: {
|
|
698
|
+
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
|
|
699
|
+
},
|
|
700
|
+
schema: [],
|
|
701
|
+
messages: {
|
|
702
|
+
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it, return a typed Result, or handle the error explicitly."
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
defaultOptions: [],
|
|
706
|
+
create(context) {
|
|
707
|
+
return {
|
|
708
|
+
CatchClause(node) {
|
|
709
|
+
const body = node.body.body;
|
|
710
|
+
if (body.length === 0) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const last = body[body.length - 1];
|
|
714
|
+
if (last === void 0 || last.type !== import_utils8.AST_NODE_TYPES.ReturnStatement) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (!isSentinelArgument(last.argument)) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (containsThrow(node.body)) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
context.report({
|
|
724
|
+
node: last,
|
|
725
|
+
messageId: "noSentinelReturn"
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// src/rules/no-sequential-await.ts
|
|
733
|
+
var import_utils9 = require("@typescript-eslint/utils");
|
|
734
|
+
function isFunctionLike(node) {
|
|
735
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
736
|
+
}
|
|
737
|
+
function isLoop(node) {
|
|
738
|
+
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
739
|
+
}
|
|
740
|
+
var no_sequential_await_default = import_utils9.ESLintUtils.RuleCreator(
|
|
741
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
742
|
+
)({
|
|
743
|
+
name: "no-sequential-await",
|
|
744
|
+
meta: {
|
|
745
|
+
type: "problem",
|
|
746
|
+
docs: {
|
|
747
|
+
description: "Disallow serial `await` inside a loop; use `await Promise.all(...)` to run the operations concurrently."
|
|
748
|
+
},
|
|
749
|
+
schema: [],
|
|
750
|
+
messages: {
|
|
751
|
+
noSequentialAwait: "Avoid `await` inside a loop \u2014 it serializes I/O. Collect the promises and `await Promise.all(xs.map(async (x) => ...))` instead."
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
defaultOptions: [],
|
|
755
|
+
create(context) {
|
|
756
|
+
function findAwaitInScope(node) {
|
|
757
|
+
if (node.type === "AwaitExpression") {
|
|
758
|
+
return node;
|
|
759
|
+
}
|
|
760
|
+
if (isFunctionLike(node)) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
for (const key of Object.keys(node)) {
|
|
764
|
+
if (key === "parent") {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const value = node[key];
|
|
768
|
+
if (Array.isArray(value)) {
|
|
769
|
+
for (const child of value) {
|
|
770
|
+
if (isNode2(child) && !isLoop(child)) {
|
|
771
|
+
const found = findAwaitInScope(child);
|
|
772
|
+
if (found) {
|
|
773
|
+
return found;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
} else if (isNode2(value) && !isLoop(value)) {
|
|
778
|
+
const found = findAwaitInScope(value);
|
|
779
|
+
if (found) {
|
|
780
|
+
return found;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
function isNode2(value) {
|
|
787
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
788
|
+
}
|
|
789
|
+
function checkLoop(node) {
|
|
790
|
+
const parts = [node.body];
|
|
791
|
+
if (node.type === "ForStatement") {
|
|
792
|
+
parts.push(node.init, node.test, node.update);
|
|
793
|
+
} else if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
794
|
+
parts.push(node.right);
|
|
795
|
+
} else {
|
|
796
|
+
parts.push(node.test);
|
|
797
|
+
}
|
|
798
|
+
for (const part of parts) {
|
|
799
|
+
if (part && !isLoop(part) && findAwaitInScope(part)) {
|
|
800
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return {
|
|
806
|
+
ForStatement: checkLoop,
|
|
807
|
+
ForInStatement: checkLoop,
|
|
808
|
+
WhileStatement: checkLoop,
|
|
809
|
+
DoWhileStatement: checkLoop,
|
|
810
|
+
ForOfStatement(node) {
|
|
811
|
+
if (node.await) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
checkLoop(node);
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// src/rules/no-string-concat-in-loop.ts
|
|
821
|
+
var import_utils10 = require("@typescript-eslint/utils");
|
|
822
|
+
var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
823
|
+
"ForStatement",
|
|
824
|
+
"ForOfStatement",
|
|
825
|
+
"ForInStatement",
|
|
826
|
+
"WhileStatement",
|
|
827
|
+
"DoWhileStatement"
|
|
828
|
+
]);
|
|
829
|
+
function isStringLiteralInit(node) {
|
|
830
|
+
if (node === null) {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
if (node.type === "TemplateLiteral") {
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
if (node.type === "Literal") {
|
|
837
|
+
return typeof node.value === "string";
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
function findVariable(scope, name) {
|
|
842
|
+
let current = scope;
|
|
843
|
+
while (current !== null) {
|
|
844
|
+
const variable = current.variables.find((v) => v.name === name);
|
|
845
|
+
if (variable !== void 0) {
|
|
846
|
+
return variable;
|
|
847
|
+
}
|
|
848
|
+
current = current.upper;
|
|
849
|
+
}
|
|
850
|
+
return void 0;
|
|
851
|
+
}
|
|
852
|
+
function isStringInitializedVariable(variable) {
|
|
853
|
+
if (variable.defs.length !== 1) {
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
const def = variable.defs[0];
|
|
857
|
+
if (def === void 0 || def.type !== "Variable") {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
const declarator = def.node;
|
|
861
|
+
if (declarator.type !== "VariableDeclarator") {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
return isStringLiteralInit(declarator.init);
|
|
865
|
+
}
|
|
866
|
+
function isInsideLoopBody(node) {
|
|
867
|
+
let child = node;
|
|
868
|
+
let parent = node.parent;
|
|
869
|
+
while (parent !== void 0 && parent !== null) {
|
|
870
|
+
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
871
|
+
const loop = parent;
|
|
872
|
+
if (loop.body === child) {
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
child = parent;
|
|
877
|
+
parent = parent.parent;
|
|
878
|
+
}
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
var no_string_concat_in_loop_default = import_utils10.ESLintUtils.RuleCreator(
|
|
882
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
883
|
+
)({
|
|
884
|
+
name: "no-string-concat-in-loop",
|
|
885
|
+
meta: {
|
|
886
|
+
type: "suggestion",
|
|
887
|
+
docs: {
|
|
888
|
+
description: "Disallow O(n^2) string building via `+=` on a string variable inside a loop; push parts to an array and `join` instead."
|
|
889
|
+
},
|
|
890
|
+
schema: [],
|
|
891
|
+
messages: {
|
|
892
|
+
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.'
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
defaultOptions: [],
|
|
896
|
+
create(context) {
|
|
897
|
+
return {
|
|
898
|
+
AssignmentExpression(node) {
|
|
899
|
+
if (node.operator !== "+=") {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
if (node.left.type !== "Identifier") {
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (!isInsideLoopBody(node)) {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const scope = context.sourceCode.getScope(node);
|
|
909
|
+
const variable = findVariable(scope, node.left.name);
|
|
910
|
+
if (variable === void 0) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
if (!isStringInitializedVariable(variable)) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
context.report({
|
|
917
|
+
node,
|
|
918
|
+
messageId: "noStringConcatInLoop"
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
|
|
382
925
|
// src/rules/no-unnecessary-use-client.ts
|
|
383
|
-
var
|
|
926
|
+
var import_utils11 = require("@typescript-eslint/utils");
|
|
384
927
|
var HOOK_REGEX = /^use([A-Z]|$)/;
|
|
385
928
|
var EVENT_PROP_REGEX = /^on[A-Z]/;
|
|
386
929
|
var ERROR_FILE_REGEX = /\b(?:global-)?error\.[jt]sx?$/;
|
|
@@ -403,16 +946,16 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
403
946
|
]);
|
|
404
947
|
var CLIENT_ONLY_PACKAGES_REGEX = /^(?:@radix-ui\/|framer-motion|react-dom|react-day-picker|@floating-ui\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\/dnd|react-virtualized|react-window|@tanstack\/react-table|@tanstack\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\/)/;
|
|
405
948
|
var isUseClientDirective = (node) => {
|
|
406
|
-
return node.type ===
|
|
949
|
+
return node.type === import_utils11.AST_NODE_TYPES.ExpressionStatement && node.expression.type === import_utils11.AST_NODE_TYPES.Literal && node.expression.value === "use client";
|
|
407
950
|
};
|
|
408
951
|
var isGlobalReference = (node, context) => {
|
|
409
952
|
if (!BROWSER_GLOBALS.has(node.name)) return false;
|
|
410
953
|
const parent = node.parent;
|
|
411
954
|
if (parent !== void 0) {
|
|
412
|
-
if (parent.type ===
|
|
955
|
+
if (parent.type === import_utils11.AST_NODE_TYPES.MemberExpression && parent.property === node && !parent.computed) {
|
|
413
956
|
return false;
|
|
414
957
|
}
|
|
415
|
-
if (parent.type ===
|
|
958
|
+
if (parent.type === import_utils11.AST_NODE_TYPES.Property && parent.key === node && !parent.computed) {
|
|
416
959
|
return false;
|
|
417
960
|
}
|
|
418
961
|
if (parent.type.startsWith("TS")) {
|
|
@@ -429,7 +972,7 @@ var isGlobalReference = (node, context) => {
|
|
|
429
972
|
}
|
|
430
973
|
return true;
|
|
431
974
|
};
|
|
432
|
-
var no_unnecessary_use_client_default =
|
|
975
|
+
var no_unnecessary_use_client_default = import_utils11.ESLintUtils.RuleCreator(
|
|
433
976
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
434
977
|
)({
|
|
435
978
|
name: "no-unnecessary-use-client",
|
|
@@ -452,13 +995,13 @@ var no_unnecessary_use_client_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
452
995
|
let directiveNode = null;
|
|
453
996
|
let hasClientIndicator = false;
|
|
454
997
|
const markIfHookOrContext = (callee) => {
|
|
455
|
-
if (callee.type ===
|
|
998
|
+
if (callee.type === import_utils11.AST_NODE_TYPES.Identifier) {
|
|
456
999
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
457
1000
|
hasClientIndicator = true;
|
|
458
1001
|
}
|
|
459
1002
|
return;
|
|
460
1003
|
}
|
|
461
|
-
if (callee.type ===
|
|
1004
|
+
if (callee.type === import_utils11.AST_NODE_TYPES.MemberExpression && callee.property.type === import_utils11.AST_NODE_TYPES.Identifier) {
|
|
462
1005
|
const name = callee.property.name;
|
|
463
1006
|
if (HOOK_REGEX.test(name) || name === "createContext") {
|
|
464
1007
|
hasClientIndicator = true;
|
|
@@ -468,7 +1011,7 @@ var no_unnecessary_use_client_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
468
1011
|
return {
|
|
469
1012
|
Program(node) {
|
|
470
1013
|
for (const stmt of node.body) {
|
|
471
|
-
if (stmt.type !==
|
|
1014
|
+
if (stmt.type !== import_utils11.AST_NODE_TYPES.ExpressionStatement) break;
|
|
472
1015
|
if (isUseClientDirective(stmt)) {
|
|
473
1016
|
directiveNode = stmt;
|
|
474
1017
|
break;
|
|
@@ -479,7 +1022,7 @@ var no_unnecessary_use_client_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
479
1022
|
markIfHookOrContext(node.callee);
|
|
480
1023
|
},
|
|
481
1024
|
JSXAttribute(node) {
|
|
482
|
-
if (node.name.type ===
|
|
1025
|
+
if (node.name.type === import_utils11.AST_NODE_TYPES.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
|
|
483
1026
|
hasClientIndicator = true;
|
|
484
1027
|
}
|
|
485
1028
|
},
|
|
@@ -521,14 +1064,98 @@ var no_unnecessary_use_client_default = import_utils5.ESLintUtils.RuleCreator(
|
|
|
521
1064
|
}
|
|
522
1065
|
});
|
|
523
1066
|
|
|
1067
|
+
// src/rules/prefer-discriminated-union.ts
|
|
1068
|
+
var import_utils12 = require("@typescript-eslint/utils");
|
|
1069
|
+
var import_utils13 = require("@typescript-eslint/utils");
|
|
1070
|
+
var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
1071
|
+
"success",
|
|
1072
|
+
"ok",
|
|
1073
|
+
"error",
|
|
1074
|
+
"failed",
|
|
1075
|
+
"isError"
|
|
1076
|
+
]);
|
|
1077
|
+
var MIN_OPTIONAL_MEMBERS = 2;
|
|
1078
|
+
function getMemberName(member) {
|
|
1079
|
+
if (member.type !== import_utils13.AST_NODE_TYPES.TSPropertySignature) {
|
|
1080
|
+
return null;
|
|
1081
|
+
}
|
|
1082
|
+
const { key } = member;
|
|
1083
|
+
if (key.type === import_utils13.AST_NODE_TYPES.Identifier) {
|
|
1084
|
+
return key.name;
|
|
1085
|
+
}
|
|
1086
|
+
if (key.type === import_utils13.AST_NODE_TYPES.Literal && typeof key.value === "string") {
|
|
1087
|
+
return key.value;
|
|
1088
|
+
}
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
function isBooleanTyped(member) {
|
|
1092
|
+
return member.typeAnnotation?.typeAnnotation.type === import_utils13.AST_NODE_TYPES.TSBooleanKeyword;
|
|
1093
|
+
}
|
|
1094
|
+
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1095
|
+
let hasStatusBoolean = false;
|
|
1096
|
+
let optionalCount = 0;
|
|
1097
|
+
for (const member of typeLiteral.members) {
|
|
1098
|
+
if (member.type !== import_utils13.AST_NODE_TYPES.TSPropertySignature) {
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (member.optional) {
|
|
1102
|
+
optionalCount += 1;
|
|
1103
|
+
}
|
|
1104
|
+
const name = getMemberName(member);
|
|
1105
|
+
if (name !== null && STATUS_MEMBER_NAMES.has(name) && isBooleanTyped(member)) {
|
|
1106
|
+
hasStatusBoolean = true;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
1110
|
+
}
|
|
1111
|
+
var prefer_discriminated_union_default = import_utils12.ESLintUtils.RuleCreator(
|
|
1112
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1113
|
+
)({
|
|
1114
|
+
name: "prefer-discriminated-union",
|
|
1115
|
+
meta: {
|
|
1116
|
+
type: "suggestion",
|
|
1117
|
+
docs: {
|
|
1118
|
+
description: "Flag object types with a boolean status flag and many optionals; model them as a discriminated union instead."
|
|
1119
|
+
},
|
|
1120
|
+
schema: [],
|
|
1121
|
+
messages: {
|
|
1122
|
+
preferDiscriminatedUnion: "This object type uses a boolean status flag alongside several optional fields, which lets illegal states be representable. Model it as a `z.discriminatedUnion` / discriminated union (e.g. `{ ok: true; data: T } | { ok: false; error: E }`) to make illegal states unrepresentable."
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
defaultOptions: [],
|
|
1126
|
+
create(context) {
|
|
1127
|
+
function checkTypeLiteral(typeLiteral, reportNode) {
|
|
1128
|
+
if (looksLikeMutuallyExclusiveState(typeLiteral)) {
|
|
1129
|
+
context.report({
|
|
1130
|
+
node: reportNode,
|
|
1131
|
+
messageId: "preferDiscriminatedUnion"
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
return {
|
|
1136
|
+
TSInterfaceDeclaration(node) {
|
|
1137
|
+
const synthetic = {
|
|
1138
|
+
...node.body,
|
|
1139
|
+
type: import_utils13.AST_NODE_TYPES.TSTypeLiteral,
|
|
1140
|
+
members: node.body.body
|
|
1141
|
+
};
|
|
1142
|
+
checkTypeLiteral(synthetic, node);
|
|
1143
|
+
},
|
|
1144
|
+
"TSTypeAliasDeclaration > TSTypeLiteral"(node) {
|
|
1145
|
+
checkTypeLiteral(node, node.parent);
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
|
|
524
1151
|
// src/rules/prefer-schema-for-api-payload.ts
|
|
525
|
-
var
|
|
1152
|
+
var import_utils14 = require("@typescript-eslint/utils");
|
|
526
1153
|
var unwrap = (node) => {
|
|
527
1154
|
let current = node;
|
|
528
1155
|
while (current !== null && current !== void 0) {
|
|
529
|
-
if (current.type ===
|
|
1156
|
+
if (current.type === import_utils14.AST_NODE_TYPES.TSAsExpression || current.type === import_utils14.AST_NODE_TYPES.TSTypeAssertion || current.type === import_utils14.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils14.AST_NODE_TYPES.TSSatisfiesExpression) {
|
|
530
1157
|
current = current.expression;
|
|
531
|
-
} else if (current.type ===
|
|
1158
|
+
} else if (current.type === import_utils14.AST_NODE_TYPES.ChainExpression) {
|
|
532
1159
|
current = current.expression;
|
|
533
1160
|
} else {
|
|
534
1161
|
break;
|
|
@@ -539,20 +1166,20 @@ var unwrap = (node) => {
|
|
|
539
1166
|
var isJsonCall = (node) => {
|
|
540
1167
|
let current = unwrap(node);
|
|
541
1168
|
if (current === null) return false;
|
|
542
|
-
if (current.type ===
|
|
1169
|
+
if (current.type === import_utils14.AST_NODE_TYPES.AwaitExpression) {
|
|
543
1170
|
current = unwrap(current.argument);
|
|
544
1171
|
}
|
|
545
|
-
if (current === null || current.type !==
|
|
1172
|
+
if (current === null || current.type !== import_utils14.AST_NODE_TYPES.CallExpression) {
|
|
546
1173
|
return false;
|
|
547
1174
|
}
|
|
548
1175
|
const callee = unwrap(current.callee);
|
|
549
|
-
if (callee === null || callee.type !==
|
|
1176
|
+
if (callee === null || callee.type !== import_utils14.AST_NODE_TYPES.MemberExpression) {
|
|
550
1177
|
return false;
|
|
551
1178
|
}
|
|
552
1179
|
const property = unwrap(callee.property);
|
|
553
|
-
return property !== null && property.type ===
|
|
1180
|
+
return property !== null && property.type === import_utils14.AST_NODE_TYPES.Identifier && property.name === "json";
|
|
554
1181
|
};
|
|
555
|
-
var
|
|
1182
|
+
var findVariable2 = (scope, name) => {
|
|
556
1183
|
let current = scope;
|
|
557
1184
|
while (current !== null) {
|
|
558
1185
|
const variable = current.set.get(name);
|
|
@@ -563,13 +1190,13 @@ var findVariable = (scope, name) => {
|
|
|
563
1190
|
};
|
|
564
1191
|
var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
565
1192
|
const unwrapped = unwrap(node);
|
|
566
|
-
if (unwrapped === null || unwrapped.type !==
|
|
1193
|
+
if (unwrapped === null || unwrapped.type !== import_utils14.AST_NODE_TYPES.Identifier) {
|
|
567
1194
|
return false;
|
|
568
1195
|
}
|
|
569
|
-
const variable =
|
|
1196
|
+
const variable = findVariable2(scope, unwrapped.name);
|
|
570
1197
|
return variable !== null && tracked.has(variable);
|
|
571
1198
|
};
|
|
572
|
-
var prefer_schema_for_api_payload_default =
|
|
1199
|
+
var prefer_schema_for_api_payload_default = import_utils14.ESLintUtils.RuleCreator(
|
|
573
1200
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
574
1201
|
)({
|
|
575
1202
|
name: "prefer-schema-for-api-payload",
|
|
@@ -597,11 +1224,11 @@ var prefer_schema_for_api_payload_default = import_utils6.ESLintUtils.RuleCreato
|
|
|
597
1224
|
return {
|
|
598
1225
|
VariableDeclarator(node) {
|
|
599
1226
|
const scope = context.sourceCode.getScope(node);
|
|
600
|
-
if (node.id.type ===
|
|
1227
|
+
if (node.id.type === import_utils14.AST_NODE_TYPES.Identifier) {
|
|
601
1228
|
trackInitializer(node);
|
|
602
1229
|
return;
|
|
603
1230
|
}
|
|
604
|
-
if (node.id.type ===
|
|
1231
|
+
if (node.id.type === import_utils14.AST_NODE_TYPES.ObjectPattern || node.id.type === import_utils14.AST_NODE_TYPES.ArrayPattern) {
|
|
605
1232
|
if (isJsonCall(node.init)) {
|
|
606
1233
|
context.report({ node: node.id, messageId: "unparsedJsonAccess" });
|
|
607
1234
|
return;
|
|
@@ -613,8 +1240,8 @@ var prefer_schema_for_api_payload_default = import_utils6.ESLintUtils.RuleCreato
|
|
|
613
1240
|
},
|
|
614
1241
|
AssignmentExpression(node) {
|
|
615
1242
|
const scope = context.sourceCode.getScope(node);
|
|
616
|
-
if (node.left.type ===
|
|
617
|
-
const variable =
|
|
1243
|
+
if (node.left.type === import_utils14.AST_NODE_TYPES.Identifier) {
|
|
1244
|
+
const variable = findVariable2(scope, node.left.name);
|
|
618
1245
|
if (variable === null) return;
|
|
619
1246
|
if (isJsonCall(node.right)) {
|
|
620
1247
|
unvalidatedVariables.add(variable);
|
|
@@ -623,7 +1250,7 @@ var prefer_schema_for_api_payload_default = import_utils6.ESLintUtils.RuleCreato
|
|
|
623
1250
|
}
|
|
624
1251
|
return;
|
|
625
1252
|
}
|
|
626
|
-
if (node.left.type ===
|
|
1253
|
+
if (node.left.type === import_utils14.AST_NODE_TYPES.ObjectPattern || node.left.type === import_utils14.AST_NODE_TYPES.ArrayPattern) {
|
|
627
1254
|
if (isJsonCall(node.right)) {
|
|
628
1255
|
context.report({
|
|
629
1256
|
node: node.left,
|
|
@@ -644,15 +1271,15 @@ var prefer_schema_for_api_payload_default = import_utils6.ESLintUtils.RuleCreato
|
|
|
644
1271
|
const obj = unwrap(node.object);
|
|
645
1272
|
if (isJsonCall(obj)) {
|
|
646
1273
|
const parent = node.parent;
|
|
647
|
-
if (parent.type ===
|
|
1274
|
+
if (parent.type === import_utils14.AST_NODE_TYPES.CallExpression && parent.callee === node && node.property.type === import_utils14.AST_NODE_TYPES.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
|
|
648
1275
|
return;
|
|
649
1276
|
}
|
|
650
1277
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
651
1278
|
return;
|
|
652
1279
|
}
|
|
653
|
-
if (obj !== null && obj.type ===
|
|
1280
|
+
if (obj !== null && obj.type === import_utils14.AST_NODE_TYPES.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
|
|
654
1281
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
655
|
-
const variable =
|
|
1282
|
+
const variable = findVariable2(scope, obj.name);
|
|
656
1283
|
if (variable !== null) {
|
|
657
1284
|
unvalidatedVariables.delete(variable);
|
|
658
1285
|
}
|
|
@@ -663,7 +1290,7 @@ var prefer_schema_for_api_payload_default = import_utils6.ESLintUtils.RuleCreato
|
|
|
663
1290
|
});
|
|
664
1291
|
|
|
665
1292
|
// src/rules/prefer-server-actions.ts
|
|
666
|
-
var
|
|
1293
|
+
var import_utils15 = require("@typescript-eslint/utils");
|
|
667
1294
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
668
1295
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
669
1296
|
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
@@ -742,7 +1369,7 @@ function getPropertyNode(objNode, propName) {
|
|
|
742
1369
|
}
|
|
743
1370
|
return null;
|
|
744
1371
|
}
|
|
745
|
-
var prefer_server_actions_default =
|
|
1372
|
+
var prefer_server_actions_default = import_utils15.ESLintUtils.RuleCreator(
|
|
746
1373
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
747
1374
|
)({
|
|
748
1375
|
name: "prefer-server-actions",
|
|
@@ -807,14 +1434,14 @@ var prefer_server_actions_default = import_utils7.ESLintUtils.RuleCreator(
|
|
|
807
1434
|
});
|
|
808
1435
|
|
|
809
1436
|
// src/rules/prefer-shadcn.ts
|
|
810
|
-
var
|
|
1437
|
+
var import_utils16 = require("@typescript-eslint/utils");
|
|
811
1438
|
var REPLACEMENTS = {
|
|
812
1439
|
input: "Input",
|
|
813
1440
|
select: "Select",
|
|
814
1441
|
textarea: "Textarea",
|
|
815
1442
|
dialog: "Dialog"
|
|
816
1443
|
};
|
|
817
|
-
var prefer_shadcn_default =
|
|
1444
|
+
var prefer_shadcn_default = import_utils16.ESLintUtils.RuleCreator(
|
|
818
1445
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
819
1446
|
)({
|
|
820
1447
|
name: "prefer-shadcn",
|
|
@@ -855,25 +1482,25 @@ var prefer_shadcn_default = import_utils8.ESLintUtils.RuleCreator(
|
|
|
855
1482
|
});
|
|
856
1483
|
|
|
857
1484
|
// src/rules/require-assert-never.ts
|
|
858
|
-
var
|
|
1485
|
+
var import_utils17 = require("@typescript-eslint/utils");
|
|
859
1486
|
var isAssertNeverCall = (expression) => {
|
|
860
|
-
if (expression.type !==
|
|
1487
|
+
if (expression.type !== import_utils17.AST_NODE_TYPES.CallExpression) return false;
|
|
861
1488
|
const callee = expression.callee;
|
|
862
|
-
return callee.type ===
|
|
1489
|
+
return callee.type === import_utils17.AST_NODE_TYPES.Identifier && callee.name === "assertNever";
|
|
863
1490
|
};
|
|
864
1491
|
var statementContainsAssertNever = (statement) => {
|
|
865
|
-
if (statement.type ===
|
|
1492
|
+
if (statement.type === import_utils17.AST_NODE_TYPES.ExpressionStatement) {
|
|
866
1493
|
return isAssertNeverCall(statement.expression);
|
|
867
1494
|
}
|
|
868
|
-
if (statement.type ===
|
|
1495
|
+
if (statement.type === import_utils17.AST_NODE_TYPES.ThrowStatement) {
|
|
869
1496
|
return isAssertNeverCall(statement.argument);
|
|
870
1497
|
}
|
|
871
|
-
if (statement.type ===
|
|
1498
|
+
if (statement.type === import_utils17.AST_NODE_TYPES.BlockStatement) {
|
|
872
1499
|
return statement.body.some(statementContainsAssertNever);
|
|
873
1500
|
}
|
|
874
1501
|
return false;
|
|
875
1502
|
};
|
|
876
|
-
var require_assert_never_default =
|
|
1503
|
+
var require_assert_never_default = import_utils17.ESLintUtils.RuleCreator(
|
|
877
1504
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
878
1505
|
)({
|
|
879
1506
|
name: "require-assert-never",
|
|
@@ -909,22 +1536,22 @@ var require_assert_never_default = import_utils9.ESLintUtils.RuleCreator(
|
|
|
909
1536
|
});
|
|
910
1537
|
|
|
911
1538
|
// src/rules/require-zod-form-validation.ts
|
|
912
|
-
var
|
|
1539
|
+
var import_utils18 = require("@typescript-eslint/utils");
|
|
913
1540
|
var isFormDataGetCall = (node) => {
|
|
914
1541
|
const callee = node.callee;
|
|
915
|
-
if (callee.type !==
|
|
916
|
-
if (callee.property.type !==
|
|
1542
|
+
if (callee.type !== import_utils18.AST_NODE_TYPES.MemberExpression) return false;
|
|
1543
|
+
if (callee.property.type !== import_utils18.AST_NODE_TYPES.Identifier || callee.property.name !== "get") {
|
|
917
1544
|
return false;
|
|
918
1545
|
}
|
|
919
|
-
return callee.object.type ===
|
|
1546
|
+
return callee.object.type === import_utils18.AST_NODE_TYPES.Identifier && callee.object.name === "formData";
|
|
920
1547
|
};
|
|
921
1548
|
var isParseCallExpression = (node) => {
|
|
922
|
-
if (node.type !==
|
|
1549
|
+
if (node.type !== import_utils18.AST_NODE_TYPES.CallExpression) return false;
|
|
923
1550
|
const callee = node.callee;
|
|
924
|
-
if (callee.type !==
|
|
925
|
-
return callee.property.type ===
|
|
1551
|
+
if (callee.type !== import_utils18.AST_NODE_TYPES.MemberExpression) return false;
|
|
1552
|
+
return callee.property.type === import_utils18.AST_NODE_TYPES.Identifier && callee.property.name === "parse";
|
|
926
1553
|
};
|
|
927
|
-
var require_zod_form_validation_default =
|
|
1554
|
+
var require_zod_form_validation_default = import_utils18.ESLintUtils.RuleCreator(
|
|
928
1555
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
929
1556
|
)({
|
|
930
1557
|
name: "require-zod-form-validation",
|
|
@@ -958,15 +1585,15 @@ var require_zod_form_validation_default = import_utils10.ESLintUtils.RuleCreator
|
|
|
958
1585
|
});
|
|
959
1586
|
|
|
960
1587
|
// src/rules/zod-naming-convention.ts
|
|
961
|
-
var
|
|
1588
|
+
var import_utils19 = require("@typescript-eslint/utils");
|
|
962
1589
|
var calleeChainStartsWithZ = (node) => {
|
|
963
1590
|
let current = node;
|
|
964
|
-
while (current.type ===
|
|
1591
|
+
while (current.type === import_utils19.AST_NODE_TYPES.MemberExpression) {
|
|
965
1592
|
const receiver = current.object;
|
|
966
|
-
if (receiver.type ===
|
|
1593
|
+
if (receiver.type === import_utils19.AST_NODE_TYPES.Identifier && receiver.name === "z") {
|
|
967
1594
|
return true;
|
|
968
1595
|
}
|
|
969
|
-
if (receiver.type ===
|
|
1596
|
+
if (receiver.type === import_utils19.AST_NODE_TYPES.CallExpression) {
|
|
970
1597
|
current = receiver.callee;
|
|
971
1598
|
continue;
|
|
972
1599
|
}
|
|
@@ -974,7 +1601,7 @@ var calleeChainStartsWithZ = (node) => {
|
|
|
974
1601
|
}
|
|
975
1602
|
return false;
|
|
976
1603
|
};
|
|
977
|
-
var zod_naming_convention_default =
|
|
1604
|
+
var zod_naming_convention_default = import_utils19.ESLintUtils.RuleCreator(
|
|
978
1605
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
979
1606
|
)({
|
|
980
1607
|
name: "zod-naming-convention",
|
|
@@ -994,11 +1621,11 @@ var zod_naming_convention_default = import_utils11.ESLintUtils.RuleCreator(
|
|
|
994
1621
|
VariableDeclarator(node) {
|
|
995
1622
|
const init = node.init;
|
|
996
1623
|
if (init === null || init === void 0) return;
|
|
997
|
-
if (init.type !==
|
|
1624
|
+
if (init.type !== import_utils19.AST_NODE_TYPES.CallExpression) return;
|
|
998
1625
|
const callee = init.callee;
|
|
999
|
-
if (callee.type !==
|
|
1626
|
+
if (callee.type !== import_utils19.AST_NODE_TYPES.MemberExpression) return;
|
|
1000
1627
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
1001
|
-
if (node.id.type !==
|
|
1628
|
+
if (node.id.type !== import_utils19.AST_NODE_TYPES.Identifier) return;
|
|
1002
1629
|
const variableName = node.id.name;
|
|
1003
1630
|
if (variableName.startsWith("Z")) return;
|
|
1004
1631
|
context.report({
|
|
@@ -1015,8 +1642,15 @@ var rules = {
|
|
|
1015
1642
|
"enforce-file-structure": enforce_file_structure_default,
|
|
1016
1643
|
"no-client-side-data-fetching": no_client_side_data_fetching_default,
|
|
1017
1644
|
"no-enum": no_enum_default,
|
|
1645
|
+
"no-insecure-random-id": no_insecure_random_id_default,
|
|
1646
|
+
"no-json-stringify-error": no_json_stringify_error_default,
|
|
1647
|
+
"no-log-only-catch": no_log_only_catch_default,
|
|
1018
1648
|
"no-raw-env": no_raw_env_default,
|
|
1649
|
+
"no-sentinel-return-on-catch": no_sentinel_return_on_catch_default,
|
|
1650
|
+
"no-sequential-await": no_sequential_await_default,
|
|
1651
|
+
"no-string-concat-in-loop": no_string_concat_in_loop_default,
|
|
1019
1652
|
"no-unnecessary-use-client": no_unnecessary_use_client_default,
|
|
1653
|
+
"prefer-discriminated-union": prefer_discriminated_union_default,
|
|
1020
1654
|
"prefer-schema-for-api-payload": prefer_schema_for_api_payload_default,
|
|
1021
1655
|
"prefer-server-actions": prefer_server_actions_default,
|
|
1022
1656
|
"prefer-shadcn": prefer_shadcn_default,
|
|
@@ -1027,7 +1661,7 @@ var rules = {
|
|
|
1027
1661
|
var plugin = {
|
|
1028
1662
|
meta: {
|
|
1029
1663
|
name: "@sarj/eslint-plugin",
|
|
1030
|
-
version: "2.
|
|
1664
|
+
version: "2.1.1"
|
|
1031
1665
|
},
|
|
1032
1666
|
rules,
|
|
1033
1667
|
configs: {
|
|
@@ -1041,7 +1675,15 @@ var plugin = {
|
|
|
1041
1675
|
"@sarj/no-client-side-data-fetching": "warn",
|
|
1042
1676
|
"@sarj/prefer-server-actions": "warn",
|
|
1043
1677
|
"@sarj/no-unnecessary-use-client": "warn",
|
|
1044
|
-
"@sarj/prefer-schema-for-api-payload": "warn"
|
|
1678
|
+
"@sarj/prefer-schema-for-api-payload": "warn",
|
|
1679
|
+
// Distilled from sarj-audit skills — warn in recommended, error in strict.
|
|
1680
|
+
"@sarj/no-sequential-await": "warn",
|
|
1681
|
+
"@sarj/no-sentinel-return-on-catch": "warn",
|
|
1682
|
+
"@sarj/no-log-only-catch": "warn",
|
|
1683
|
+
"@sarj/no-insecure-random-id": "warn",
|
|
1684
|
+
"@sarj/no-json-stringify-error": "warn",
|
|
1685
|
+
"@sarj/no-string-concat-in-loop": "warn",
|
|
1686
|
+
"@sarj/prefer-discriminated-union": "warn"
|
|
1045
1687
|
}
|
|
1046
1688
|
},
|
|
1047
1689
|
strict: {
|
|
@@ -1057,7 +1699,15 @@ var plugin = {
|
|
|
1057
1699
|
"@sarj/no-client-side-data-fetching": "error",
|
|
1058
1700
|
"@sarj/prefer-server-actions": "error",
|
|
1059
1701
|
"@sarj/no-unnecessary-use-client": "error",
|
|
1060
|
-
"@sarj/prefer-schema-for-api-payload": "error"
|
|
1702
|
+
"@sarj/prefer-schema-for-api-payload": "error",
|
|
1703
|
+
// Distilled from sarj-audit skills.
|
|
1704
|
+
"@sarj/no-sequential-await": "error",
|
|
1705
|
+
"@sarj/no-sentinel-return-on-catch": "error",
|
|
1706
|
+
"@sarj/no-log-only-catch": "error",
|
|
1707
|
+
"@sarj/no-insecure-random-id": "error",
|
|
1708
|
+
"@sarj/no-json-stringify-error": "error",
|
|
1709
|
+
"@sarj/no-string-concat-in-loop": "error",
|
|
1710
|
+
"@sarj/prefer-discriminated-union": "error"
|
|
1061
1711
|
}
|
|
1062
1712
|
}
|
|
1063
1713
|
}
|