unguard 0.10.4 → 0.12.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/README.md +2 -0
- package/dist/{chunk-7UXUNJSF.js → chunk-HE647T6E.js} +381 -176
- package/dist/chunk-HE647T6E.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-7UXUNJSF.js.map +0 -1
|
@@ -314,9 +314,17 @@ function isTupleSlotDefinitelyPresent(type, index, checker) {
|
|
|
314
314
|
return type.types.every((member) => isTupleSlotDefinitelyPresent(member, index, checker));
|
|
315
315
|
}
|
|
316
316
|
const apparent = checker.getApparentType(type);
|
|
317
|
-
if (!
|
|
317
|
+
if (!isTupleTypeReference(apparent, checker)) return false;
|
|
318
318
|
return index < apparent.target.minLength;
|
|
319
319
|
}
|
|
320
|
+
function isTupleTypeReference(type, checker) {
|
|
321
|
+
if (!checker.isTupleType(type)) return false;
|
|
322
|
+
if (!("target" in type)) return false;
|
|
323
|
+
const target = type.target;
|
|
324
|
+
if (typeof target !== "object" || target === null) return false;
|
|
325
|
+
if (!("minLength" in target)) return false;
|
|
326
|
+
return typeof target.minLength === "number";
|
|
327
|
+
}
|
|
320
328
|
|
|
321
329
|
// src/rules/ts/no-optional-call.ts
|
|
322
330
|
import * as ts7 from "typescript";
|
|
@@ -535,43 +543,61 @@ var duplicateInlineTypeInParams = {
|
|
|
535
543
|
}
|
|
536
544
|
};
|
|
537
545
|
|
|
538
|
-
// src/rules/ts/no-type-assertion.ts
|
|
546
|
+
// src/rules/ts/no-inline-type-assertion.ts
|
|
539
547
|
import * as ts14 from "typescript";
|
|
548
|
+
var noInlineTypeAssertion = {
|
|
549
|
+
kind: "ts",
|
|
550
|
+
id: "no-inline-type-assertion",
|
|
551
|
+
severity: "error",
|
|
552
|
+
message: "Inline object type assertions (`x as { ... }`) hide missing named types; extract a named type or fix the upstream type",
|
|
553
|
+
visit(node, ctx) {
|
|
554
|
+
if (ts14.isAsExpression(node) && ts14.isTypeLiteralNode(node.type)) {
|
|
555
|
+
ctx.report(node);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (ts14.isTypeAssertionExpression(node) && ts14.isTypeLiteralNode(node.type)) {
|
|
559
|
+
ctx.report(node);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/rules/ts/no-type-assertion.ts
|
|
565
|
+
import * as ts15 from "typescript";
|
|
540
566
|
var noTypeAssertion = {
|
|
541
567
|
kind: "ts",
|
|
542
568
|
id: "no-type-assertion",
|
|
543
569
|
severity: "error",
|
|
544
570
|
message: "Double type assertion (`as unknown as T`) circumvents the type system; fix the upstream type or use a type guard",
|
|
545
571
|
visit(node, ctx) {
|
|
546
|
-
if (!
|
|
547
|
-
if (!
|
|
548
|
-
if (node.expression.type.kind !==
|
|
572
|
+
if (!ts15.isAsExpression(node)) return;
|
|
573
|
+
if (!ts15.isAsExpression(node.expression)) return;
|
|
574
|
+
if (node.expression.type.kind !== ts15.SyntaxKind.UnknownKeyword) return;
|
|
549
575
|
ctx.report(node);
|
|
550
576
|
}
|
|
551
577
|
};
|
|
552
578
|
|
|
553
579
|
// src/rules/ts/no-redundant-existence-guard.ts
|
|
554
|
-
import * as
|
|
580
|
+
import * as ts16 from "typescript";
|
|
555
581
|
var noRedundantExistenceGuard = {
|
|
556
582
|
kind: "ts",
|
|
557
583
|
id: "no-redundant-existence-guard",
|
|
558
584
|
severity: "warning",
|
|
559
585
|
message: "Redundant existence guard (obj && obj.prop) on a non-nullable type; remove the guard or fix the type upstream",
|
|
560
586
|
visit(node, ctx) {
|
|
561
|
-
if (!
|
|
562
|
-
if (node.operatorToken.kind !==
|
|
587
|
+
if (!ts16.isBinaryExpression(node)) return;
|
|
588
|
+
if (node.operatorToken.kind !== ts16.SyntaxKind.AmpersandAmpersandToken) return;
|
|
563
589
|
const left = node.left;
|
|
564
590
|
const right = node.right;
|
|
565
|
-
if (
|
|
591
|
+
if (ts16.isIdentifier(left) && accessesIdentifier(right, left.text)) {
|
|
566
592
|
if (ctx.isNullable(left)) return;
|
|
567
593
|
ctx.report(node);
|
|
568
594
|
return;
|
|
569
595
|
}
|
|
570
|
-
if (
|
|
596
|
+
if (ts16.isBinaryExpression(left) && isNullCheck(left)) {
|
|
571
597
|
const checked = getNullCheckedIdentifier(left);
|
|
572
598
|
if (checked && accessesIdentifier(right, checked)) {
|
|
573
|
-
const identNode =
|
|
574
|
-
if (
|
|
599
|
+
const identNode = ts16.isIdentifier(left.left) ? left.left : left.right;
|
|
600
|
+
if (ts16.isIdentifier(identNode) && identNode.text === checked && !ctx.isNullable(identNode)) {
|
|
575
601
|
ctx.report(node);
|
|
576
602
|
}
|
|
577
603
|
}
|
|
@@ -580,27 +606,27 @@ var noRedundantExistenceGuard = {
|
|
|
580
606
|
};
|
|
581
607
|
function accessesIdentifier(expr, name) {
|
|
582
608
|
const root = getExpressionRoot(expr);
|
|
583
|
-
return
|
|
609
|
+
return ts16.isIdentifier(root) && root.text === name;
|
|
584
610
|
}
|
|
585
611
|
function getExpressionRoot(node) {
|
|
586
|
-
if (
|
|
587
|
-
if (
|
|
588
|
-
if (
|
|
612
|
+
if (ts16.isPropertyAccessExpression(node)) return getExpressionRoot(node.expression);
|
|
613
|
+
if (ts16.isElementAccessExpression(node)) return getExpressionRoot(node.expression);
|
|
614
|
+
if (ts16.isCallExpression(node)) return getExpressionRoot(node.expression);
|
|
589
615
|
return node;
|
|
590
616
|
}
|
|
591
617
|
function isNullCheck(expr) {
|
|
592
618
|
const op = expr.operatorToken.kind;
|
|
593
|
-
if (op !==
|
|
619
|
+
if (op !== ts16.SyntaxKind.ExclamationEqualsToken && op !== ts16.SyntaxKind.ExclamationEqualsEqualsToken) return false;
|
|
594
620
|
return isNullishLiteral(expr.right) || isNullishLiteral(expr.left);
|
|
595
621
|
}
|
|
596
622
|
function getNullCheckedIdentifier(expr) {
|
|
597
|
-
if (
|
|
598
|
-
if (
|
|
623
|
+
if (ts16.isIdentifier(expr.left) && isNullishLiteral(expr.right)) return expr.left.text;
|
|
624
|
+
if (ts16.isIdentifier(expr.right) && isNullishLiteral(expr.left)) return expr.right.text;
|
|
599
625
|
return null;
|
|
600
626
|
}
|
|
601
627
|
|
|
602
628
|
// src/rules/ts/prefer-default-param-value.ts
|
|
603
|
-
import * as
|
|
629
|
+
import * as ts17 from "typescript";
|
|
604
630
|
var preferDefaultParamValue = {
|
|
605
631
|
kind: "ts",
|
|
606
632
|
id: "prefer-default-param-value",
|
|
@@ -611,21 +637,21 @@ var preferDefaultParamValue = {
|
|
|
611
637
|
if (result === null) return;
|
|
612
638
|
const { statements: stmts, fn } = result;
|
|
613
639
|
const firstStmt = stmts[0];
|
|
614
|
-
if (firstStmt === void 0 || !
|
|
640
|
+
if (firstStmt === void 0 || !ts17.isExpressionStatement(firstStmt)) return;
|
|
615
641
|
const expr = firstStmt.expression;
|
|
616
|
-
if (!
|
|
642
|
+
if (!ts17.isBinaryExpression(expr) || expr.operatorToken.kind !== ts17.SyntaxKind.EqualsToken) return;
|
|
617
643
|
const right = expr.right;
|
|
618
|
-
if (!
|
|
619
|
-
if (!
|
|
644
|
+
if (!ts17.isBinaryExpression(right) || right.operatorToken.kind !== ts17.SyntaxKind.QuestionQuestionToken) return;
|
|
645
|
+
if (!ts17.isIdentifier(expr.left) || !ts17.isIdentifier(right.left)) return;
|
|
620
646
|
if (expr.left.text !== right.left.text) return;
|
|
621
647
|
const paramName = expr.left.text;
|
|
622
|
-
const isParam = fn.parameters.some((p) =>
|
|
648
|
+
const isParam = fn.parameters.some((p) => ts17.isIdentifier(p.name) && p.name.text === paramName);
|
|
623
649
|
if (isParam) ctx.report(firstStmt);
|
|
624
650
|
}
|
|
625
651
|
};
|
|
626
652
|
|
|
627
653
|
// src/rules/ts/prefer-required-param-with-guard.ts
|
|
628
|
-
import * as
|
|
654
|
+
import * as ts18 from "typescript";
|
|
629
655
|
var preferRequiredParamWithGuard = {
|
|
630
656
|
kind: "ts",
|
|
631
657
|
id: "prefer-required-param-with-guard",
|
|
@@ -636,33 +662,33 @@ var preferRequiredParamWithGuard = {
|
|
|
636
662
|
if (result === null) return;
|
|
637
663
|
const { statements: stmts, fn } = result;
|
|
638
664
|
const firstStmt = stmts[0];
|
|
639
|
-
if (firstStmt === void 0 || !
|
|
665
|
+
if (firstStmt === void 0 || !ts18.isIfStatement(firstStmt)) return;
|
|
640
666
|
const test = firstStmt.expression;
|
|
641
667
|
let guardedName = null;
|
|
642
|
-
if (
|
|
643
|
-
if (
|
|
668
|
+
if (ts18.isPrefixUnaryExpression(test) && test.operator === ts18.SyntaxKind.ExclamationToken) {
|
|
669
|
+
if (ts18.isIdentifier(test.operand)) guardedName = test.operand.text;
|
|
644
670
|
}
|
|
645
|
-
if (
|
|
671
|
+
if (ts18.isBinaryExpression(test)) {
|
|
646
672
|
const op = test.operatorToken.kind;
|
|
647
|
-
if (op ===
|
|
648
|
-
if (
|
|
673
|
+
if (op === ts18.SyntaxKind.EqualsEqualsEqualsToken || op === ts18.SyntaxKind.EqualsEqualsToken) {
|
|
674
|
+
if (ts18.isIdentifier(test.left) && ts18.isIdentifier(test.right) && test.right.text === "undefined") {
|
|
649
675
|
guardedName = test.left.text;
|
|
650
676
|
}
|
|
651
677
|
}
|
|
652
678
|
}
|
|
653
679
|
if (!guardedName) return;
|
|
654
680
|
const consequent = firstStmt.thenStatement;
|
|
655
|
-
const isGuard =
|
|
681
|
+
const isGuard = ts18.isReturnStatement(consequent) || ts18.isThrowStatement(consequent) || ts18.isBlock(consequent) && consequent.statements.length === 1 && consequent.statements[0] !== void 0 && (ts18.isReturnStatement(consequent.statements[0]) || ts18.isThrowStatement(consequent.statements[0]));
|
|
656
682
|
if (!isGuard) return;
|
|
657
683
|
const isOptional = fn.parameters.some(
|
|
658
|
-
(p) =>
|
|
684
|
+
(p) => ts18.isIdentifier(p.name) && p.name.text === guardedName && p.questionToken !== void 0
|
|
659
685
|
);
|
|
660
686
|
if (isOptional) ctx.report(firstStmt);
|
|
661
687
|
}
|
|
662
688
|
};
|
|
663
689
|
|
|
664
690
|
// src/rules/cross-file/duplicate-type-declaration.ts
|
|
665
|
-
import * as
|
|
691
|
+
import * as ts19 from "typescript";
|
|
666
692
|
var duplicateTypeDeclaration = {
|
|
667
693
|
id: "duplicate-type-declaration",
|
|
668
694
|
severity: "warning",
|
|
@@ -686,13 +712,13 @@ var duplicateTypeDeclaration = {
|
|
|
686
712
|
}
|
|
687
713
|
};
|
|
688
714
|
function isTrivialObjectShape(node) {
|
|
689
|
-
if (
|
|
690
|
-
if (
|
|
715
|
+
if (ts19.isTypeLiteralNode(node)) return node.members.length <= 1;
|
|
716
|
+
if (ts19.isInterfaceDeclaration(node)) return node.members.length <= 1;
|
|
691
717
|
return false;
|
|
692
718
|
}
|
|
693
719
|
|
|
694
720
|
// src/rules/cross-file/duplicate-function-declaration.ts
|
|
695
|
-
import * as
|
|
721
|
+
import * as ts20 from "typescript";
|
|
696
722
|
var duplicateFunctionDeclaration = {
|
|
697
723
|
id: "duplicate-function-declaration",
|
|
698
724
|
severity: "warning",
|
|
@@ -717,13 +743,13 @@ var duplicateFunctionDeclaration = {
|
|
|
717
743
|
}
|
|
718
744
|
};
|
|
719
745
|
function getBodyBlock(node) {
|
|
720
|
-
if (
|
|
721
|
-
if (
|
|
722
|
-
if (
|
|
746
|
+
if (ts20.isFunctionDeclaration(node) || ts20.isFunctionExpression(node)) return node.body;
|
|
747
|
+
if (ts20.isArrowFunction(node)) return ts20.isBlock(node.body) ? node.body : void 0;
|
|
748
|
+
if (ts20.isMethodDeclaration(node)) return node.body;
|
|
723
749
|
return void 0;
|
|
724
750
|
}
|
|
725
751
|
function isSingleStatement(node) {
|
|
726
|
-
if (
|
|
752
|
+
if (ts20.isArrowFunction(node) && !ts20.isBlock(node.body)) return true;
|
|
727
753
|
const body = getBodyBlock(node);
|
|
728
754
|
return body !== void 0 && body.statements.length <= 1;
|
|
729
755
|
}
|
|
@@ -731,8 +757,8 @@ function isSetter(node) {
|
|
|
731
757
|
const body = getBodyBlock(node);
|
|
732
758
|
if (!body || body.statements.length !== 1) return false;
|
|
733
759
|
const stmt = body.statements[0];
|
|
734
|
-
if (stmt === void 0 || !
|
|
735
|
-
return
|
|
760
|
+
if (stmt === void 0 || !ts20.isExpressionStatement(stmt)) return false;
|
|
761
|
+
return ts20.isBinaryExpression(stmt.expression) && stmt.expression.operatorToken.kind === ts20.SyntaxKind.EqualsToken;
|
|
736
762
|
}
|
|
737
763
|
|
|
738
764
|
// src/rules/cross-file/optional-arg-always-used.ts
|
|
@@ -767,14 +793,14 @@ var optionalArgAlwaysUsed = {
|
|
|
767
793
|
};
|
|
768
794
|
|
|
769
795
|
// src/rules/ts/no-catch-return.ts
|
|
770
|
-
import * as
|
|
796
|
+
import * as ts21 from "typescript";
|
|
771
797
|
var noCatchReturn = {
|
|
772
798
|
kind: "ts",
|
|
773
799
|
id: "no-catch-return",
|
|
774
800
|
severity: "warning",
|
|
775
801
|
message: "Catch block silently returns a fallback value with no logging; rethrow, log the error, or let it propagate",
|
|
776
802
|
visit(node, ctx) {
|
|
777
|
-
if (!
|
|
803
|
+
if (!ts21.isCatchClause(node)) return;
|
|
778
804
|
const block = node.block;
|
|
779
805
|
if (hasReturn(block) && !hasThrow(block) && !hasLogging(block)) {
|
|
780
806
|
ctx.report(node);
|
|
@@ -784,57 +810,57 @@ var noCatchReturn = {
|
|
|
784
810
|
function walkBlock(block, predicate) {
|
|
785
811
|
function visit(node) {
|
|
786
812
|
if (predicate(node)) return true;
|
|
787
|
-
if (
|
|
788
|
-
return
|
|
813
|
+
if (ts21.isFunctionDeclaration(node) || ts21.isFunctionExpression(node) || ts21.isArrowFunction(node)) return false;
|
|
814
|
+
return ts21.forEachChild(node, visit) ?? false;
|
|
789
815
|
}
|
|
790
|
-
return
|
|
816
|
+
return ts21.forEachChild(block, visit) ?? false;
|
|
791
817
|
}
|
|
792
818
|
function hasReturn(block) {
|
|
793
|
-
return walkBlock(block, (n) =>
|
|
819
|
+
return walkBlock(block, (n) => ts21.isReturnStatement(n));
|
|
794
820
|
}
|
|
795
821
|
function hasThrow(block) {
|
|
796
|
-
return walkBlock(block, (n) =>
|
|
822
|
+
return walkBlock(block, (n) => ts21.isThrowStatement(n));
|
|
797
823
|
}
|
|
798
824
|
var LOG_OBJECTS = /* @__PURE__ */ new Set(["console", "logger", "log"]);
|
|
799
825
|
function hasLogging(block) {
|
|
800
826
|
return walkBlock(block, (n) => {
|
|
801
|
-
if (!
|
|
827
|
+
if (!ts21.isCallExpression(n)) return false;
|
|
802
828
|
const callee = n.expression;
|
|
803
|
-
if (!
|
|
804
|
-
if (!
|
|
829
|
+
if (!ts21.isPropertyAccessExpression(callee)) return false;
|
|
830
|
+
if (!ts21.isIdentifier(callee.expression)) return false;
|
|
805
831
|
return LOG_OBJECTS.has(callee.expression.text);
|
|
806
832
|
});
|
|
807
833
|
}
|
|
808
834
|
|
|
809
835
|
// src/rules/ts/no-error-rewrap.ts
|
|
810
|
-
import * as
|
|
836
|
+
import * as ts22 from "typescript";
|
|
811
837
|
var noErrorRewrap = {
|
|
812
838
|
kind: "ts",
|
|
813
839
|
id: "no-error-rewrap",
|
|
814
840
|
severity: "error",
|
|
815
841
|
message: "Re-wrapped error loses the original stack trace and type; use { cause: originalError } to preserve the error chain",
|
|
816
842
|
visit(node, ctx) {
|
|
817
|
-
if (!
|
|
843
|
+
if (!ts22.isCatchClause(node)) return;
|
|
818
844
|
if (!node.variableDeclaration) return;
|
|
819
845
|
const param = node.variableDeclaration.name;
|
|
820
|
-
if (!
|
|
846
|
+
if (!ts22.isIdentifier(param)) return;
|
|
821
847
|
const catchName = param.text;
|
|
822
848
|
findRewraps(node.block, catchName, ctx);
|
|
823
849
|
}
|
|
824
850
|
};
|
|
825
851
|
function findRewraps(block, catchName, ctx) {
|
|
826
852
|
function visit(node) {
|
|
827
|
-
if (
|
|
828
|
-
if (
|
|
853
|
+
if (ts22.isFunctionDeclaration(node) || ts22.isFunctionExpression(node) || ts22.isArrowFunction(node)) return;
|
|
854
|
+
if (ts22.isThrowStatement(node) && node.expression && ts22.isNewExpression(node.expression)) {
|
|
829
855
|
const args = node.expression.arguments;
|
|
830
856
|
if (args && args.length > 0 && referencesName(args, catchName) && !hasCauseArg(args)) {
|
|
831
857
|
ctx.report(node);
|
|
832
858
|
}
|
|
833
859
|
return;
|
|
834
860
|
}
|
|
835
|
-
|
|
861
|
+
ts22.forEachChild(node, visit);
|
|
836
862
|
}
|
|
837
|
-
|
|
863
|
+
ts22.forEachChild(block, visit);
|
|
838
864
|
}
|
|
839
865
|
function referencesName(args, name) {
|
|
840
866
|
for (const arg of args) {
|
|
@@ -843,15 +869,15 @@ function referencesName(args, name) {
|
|
|
843
869
|
return false;
|
|
844
870
|
}
|
|
845
871
|
function containsIdentifier(node, name) {
|
|
846
|
-
if (
|
|
847
|
-
return
|
|
872
|
+
if (ts22.isIdentifier(node) && node.text === name) return true;
|
|
873
|
+
return ts22.forEachChild(node, (child) => containsIdentifier(child, name) || void 0) ?? false;
|
|
848
874
|
}
|
|
849
875
|
function hasCauseArg(args) {
|
|
850
876
|
for (const arg of args) {
|
|
851
|
-
if (
|
|
877
|
+
if (ts22.isObjectLiteralExpression(arg)) {
|
|
852
878
|
for (const prop of arg.properties) {
|
|
853
|
-
if (
|
|
854
|
-
if (
|
|
879
|
+
if (ts22.isPropertyAssignment(prop) && ts22.isIdentifier(prop.name) && prop.name.text === "cause") return true;
|
|
880
|
+
if (ts22.isShorthandPropertyAssignment(prop) && prop.name.text === "cause") return true;
|
|
855
881
|
}
|
|
856
882
|
}
|
|
857
883
|
}
|
|
@@ -859,7 +885,7 @@ function hasCauseArg(args) {
|
|
|
859
885
|
}
|
|
860
886
|
|
|
861
887
|
// src/rules/cross-file/explicit-null-arg.ts
|
|
862
|
-
import * as
|
|
888
|
+
import * as ts23 from "typescript";
|
|
863
889
|
var explicitNullArg = {
|
|
864
890
|
id: "explicit-null-arg",
|
|
865
891
|
severity: "warning",
|
|
@@ -879,7 +905,7 @@ var explicitNullArg = {
|
|
|
879
905
|
const arg = site.node.arguments[i];
|
|
880
906
|
if (arg === void 0) continue;
|
|
881
907
|
if (isNullishLiteral(arg)) {
|
|
882
|
-
const val = arg.kind ===
|
|
908
|
+
const val = arg.kind === ts23.SyntaxKind.NullKeyword ? "null" : "undefined";
|
|
883
909
|
diagnostics.push({
|
|
884
910
|
ruleId: this.id,
|
|
885
911
|
severity: this.severity,
|
|
@@ -952,7 +978,7 @@ function resolveCandidates(fromFile, specifier) {
|
|
|
952
978
|
}
|
|
953
979
|
|
|
954
980
|
// src/rules/cross-file/duplicate-type-name.ts
|
|
955
|
-
import * as
|
|
981
|
+
import * as ts24 from "typescript";
|
|
956
982
|
var duplicateTypeName = {
|
|
957
983
|
id: "duplicate-type-name",
|
|
958
984
|
severity: "warning",
|
|
@@ -963,7 +989,7 @@ var duplicateTypeName = {
|
|
|
963
989
|
const hashes = new Set(group.map((e) => e.hash));
|
|
964
990
|
if (hashes.size === 1) continue;
|
|
965
991
|
const hasInferredType = group.some(
|
|
966
|
-
(e) => !
|
|
992
|
+
(e) => !ts24.isTypeLiteralNode(e.node) && !ts24.isInterfaceDeclaration(e.node)
|
|
967
993
|
);
|
|
968
994
|
if (hasInferredType) continue;
|
|
969
995
|
reportDuplicateGroup(
|
|
@@ -1003,21 +1029,21 @@ var duplicateConstantDeclaration = {
|
|
|
1003
1029
|
};
|
|
1004
1030
|
|
|
1005
1031
|
// src/rules/ts/no-dynamic-import.ts
|
|
1006
|
-
import * as
|
|
1032
|
+
import * as ts25 from "typescript";
|
|
1007
1033
|
var noDynamicImport = {
|
|
1008
1034
|
kind: "ts",
|
|
1009
1035
|
id: "no-dynamic-import",
|
|
1010
1036
|
severity: "error",
|
|
1011
1037
|
message: "Dynamic import() breaks static analysis and hides dependencies; use a static import instead",
|
|
1012
1038
|
visit(node, ctx) {
|
|
1013
|
-
if (!
|
|
1014
|
-
if (node.expression.kind !==
|
|
1039
|
+
if (!ts25.isCallExpression(node)) return;
|
|
1040
|
+
if (node.expression.kind !== ts25.SyntaxKind.ImportKeyword) return;
|
|
1015
1041
|
ctx.report(node);
|
|
1016
1042
|
}
|
|
1017
1043
|
};
|
|
1018
1044
|
|
|
1019
1045
|
// src/rules/cross-file/near-duplicate-function.ts
|
|
1020
|
-
import * as
|
|
1046
|
+
import * as ts26 from "typescript";
|
|
1021
1047
|
var nearDuplicateFunction = {
|
|
1022
1048
|
id: "near-duplicate-function",
|
|
1023
1049
|
severity: "warning",
|
|
@@ -1041,24 +1067,24 @@ var nearDuplicateFunction = {
|
|
|
1041
1067
|
}
|
|
1042
1068
|
};
|
|
1043
1069
|
function getFunctionBody(node) {
|
|
1044
|
-
if (
|
|
1045
|
-
if (
|
|
1046
|
-
if (
|
|
1070
|
+
if (ts26.isFunctionDeclaration(node) || ts26.isFunctionExpression(node)) return node.body;
|
|
1071
|
+
if (ts26.isArrowFunction(node)) return node.body;
|
|
1072
|
+
if (ts26.isMethodDeclaration(node)) return node.body;
|
|
1047
1073
|
return void 0;
|
|
1048
1074
|
}
|
|
1049
1075
|
function getTopLevelStatementCount(body) {
|
|
1050
|
-
if (
|
|
1076
|
+
if (ts26.isBlock(body)) return body.statements.length;
|
|
1051
1077
|
return 1;
|
|
1052
1078
|
}
|
|
1053
1079
|
function hasControlFlow(node) {
|
|
1054
1080
|
let found = false;
|
|
1055
1081
|
function visit(current) {
|
|
1056
1082
|
if (found) return;
|
|
1057
|
-
if (
|
|
1083
|
+
if (ts26.isIfStatement(current) || ts26.isSwitchStatement(current) || ts26.isTryStatement(current) || ts26.isForStatement(current) || ts26.isForInStatement(current) || ts26.isForOfStatement(current) || ts26.isWhileStatement(current) || ts26.isDoStatement(current)) {
|
|
1058
1084
|
found = true;
|
|
1059
1085
|
return;
|
|
1060
1086
|
}
|
|
1061
|
-
|
|
1087
|
+
ts26.forEachChild(current, visit);
|
|
1062
1088
|
}
|
|
1063
1089
|
visit(node);
|
|
1064
1090
|
return found;
|
|
@@ -1071,36 +1097,36 @@ function isSimpleStructure(entry) {
|
|
|
1071
1097
|
}
|
|
1072
1098
|
|
|
1073
1099
|
// src/rules/cross-file/trivial-wrapper.ts
|
|
1074
|
-
import * as
|
|
1100
|
+
import * as ts27 from "typescript";
|
|
1075
1101
|
function getFunctionBody2(node) {
|
|
1076
|
-
if (
|
|
1102
|
+
if (ts27.isFunctionDeclaration(node) || ts27.isFunctionExpression(node)) {
|
|
1077
1103
|
return node.body;
|
|
1078
1104
|
}
|
|
1079
|
-
if (
|
|
1105
|
+
if (ts27.isArrowFunction(node)) {
|
|
1080
1106
|
return node.body;
|
|
1081
1107
|
}
|
|
1082
|
-
if (
|
|
1108
|
+
if (ts27.isMethodDeclaration(node)) {
|
|
1083
1109
|
return node.body;
|
|
1084
1110
|
}
|
|
1085
1111
|
return void 0;
|
|
1086
1112
|
}
|
|
1087
1113
|
function getCalleeName(expr) {
|
|
1088
|
-
if (
|
|
1089
|
-
if (
|
|
1114
|
+
if (ts27.isIdentifier(expr)) return expr.text;
|
|
1115
|
+
if (ts27.isPropertyAccessExpression(expr)) return expr.name.text;
|
|
1090
1116
|
return void 0;
|
|
1091
1117
|
}
|
|
1092
1118
|
function getTrivialCallTarget(fn) {
|
|
1093
1119
|
const body = getFunctionBody2(fn.node);
|
|
1094
1120
|
if (!body) return null;
|
|
1095
1121
|
let callExpr;
|
|
1096
|
-
if (
|
|
1122
|
+
if (ts27.isBlock(body)) {
|
|
1097
1123
|
if (body.statements.length !== 1) return null;
|
|
1098
1124
|
const stmt = body.statements[0];
|
|
1099
|
-
if (!stmt || !
|
|
1100
|
-
if (!
|
|
1125
|
+
if (!stmt || !ts27.isReturnStatement(stmt) || !stmt.expression) return null;
|
|
1126
|
+
if (!ts27.isCallExpression(stmt.expression)) return null;
|
|
1101
1127
|
callExpr = stmt.expression;
|
|
1102
1128
|
} else {
|
|
1103
|
-
if (!
|
|
1129
|
+
if (!ts27.isCallExpression(body)) return null;
|
|
1104
1130
|
callExpr = body;
|
|
1105
1131
|
}
|
|
1106
1132
|
const calleeName = getCalleeName(callExpr.expression);
|
|
@@ -1108,7 +1134,7 @@ function getTrivialCallTarget(fn) {
|
|
|
1108
1134
|
const paramNames = fn.params.map((p) => p.name);
|
|
1109
1135
|
const args = callExpr.arguments;
|
|
1110
1136
|
for (const arg of args) {
|
|
1111
|
-
if (!
|
|
1137
|
+
if (!ts27.isIdentifier(arg)) return null;
|
|
1112
1138
|
if (!paramNames.includes(arg.text)) return null;
|
|
1113
1139
|
}
|
|
1114
1140
|
return { calleeName };
|
|
@@ -1278,6 +1304,177 @@ var duplicateStatementSequence = {
|
|
|
1278
1304
|
}
|
|
1279
1305
|
};
|
|
1280
1306
|
|
|
1307
|
+
// src/rules/cross-file/dead-overload.ts
|
|
1308
|
+
import * as ts28 from "typescript";
|
|
1309
|
+
var deadOverload = {
|
|
1310
|
+
id: "dead-overload",
|
|
1311
|
+
severity: "warning",
|
|
1312
|
+
message: "Overload signature has no matching call sites in the project",
|
|
1313
|
+
analyze(project) {
|
|
1314
|
+
const diagnostics = [];
|
|
1315
|
+
for (const [file, { sourceFile }] of project.files) {
|
|
1316
|
+
for (const family of collectOverloadFamilies(sourceFile, file)) {
|
|
1317
|
+
const matchedOverloads = /* @__PURE__ */ new Set();
|
|
1318
|
+
for (const site of project.callSites) {
|
|
1319
|
+
const declaration = site.resolvedDeclaration;
|
|
1320
|
+
if (declaration === void 0) continue;
|
|
1321
|
+
if (family.overloads.includes(declaration)) {
|
|
1322
|
+
matchedOverloads.add(declaration);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
if (matchedOverloads.size === 0) continue;
|
|
1326
|
+
const deadOverloads = family.overloads.filter((overload) => !matchedOverloads.has(overload));
|
|
1327
|
+
const liveOverloads = family.overloads.filter((overload) => matchedOverloads.has(overload));
|
|
1328
|
+
for (const overload of deadOverloads) {
|
|
1329
|
+
diagnostics.push({
|
|
1330
|
+
ruleId: this.id,
|
|
1331
|
+
severity: this.severity,
|
|
1332
|
+
message: buildMessage(family, overload, deadOverloads, liveOverloads),
|
|
1333
|
+
file,
|
|
1334
|
+
line: lineOf(sourceFile, overload),
|
|
1335
|
+
column: 1
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return diagnostics;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
function buildMessage(family, overload, deadOverloads, liveOverloads) {
|
|
1344
|
+
const base = `Overload signature for "${family.name}" has no matching call sites in the project`;
|
|
1345
|
+
if (!shouldMentionCascade(family, overload, deadOverloads, liveOverloads)) {
|
|
1346
|
+
return base;
|
|
1347
|
+
}
|
|
1348
|
+
return `${base}; removing it may let you collapse to the remaining constrained signature and drop implementation casts`;
|
|
1349
|
+
}
|
|
1350
|
+
function shouldMentionCascade(family, overload, deadOverloads, liveOverloads) {
|
|
1351
|
+
if (deadOverloads.length !== 1 || liveOverloads.length !== 1) return false;
|
|
1352
|
+
if (deadOverloads[0] !== overload) return false;
|
|
1353
|
+
const liveOverload = liveOverloads[0];
|
|
1354
|
+
if (liveOverload === void 0) return false;
|
|
1355
|
+
if (family.implementation.typeParameters === void 0 || liveOverload.typeParameters === void 0) return false;
|
|
1356
|
+
const body = family.implementation.body;
|
|
1357
|
+
if (body === void 0) return false;
|
|
1358
|
+
const implementationTypeParams = family.implementation.typeParameters;
|
|
1359
|
+
const liveTypeParams = liveOverload.typeParameters;
|
|
1360
|
+
if (implementationTypeParams.length === 0 || implementationTypeParams.length !== liveTypeParams.length) return false;
|
|
1361
|
+
const constrainedParams = implementationTypeParams.flatMap((typeParam, index) => {
|
|
1362
|
+
const liveTypeParam = liveTypeParams[index];
|
|
1363
|
+
if (liveTypeParam === void 0) return [];
|
|
1364
|
+
if (typeParam.constraint !== void 0) return [];
|
|
1365
|
+
if (liveTypeParam.constraint === void 0) return [];
|
|
1366
|
+
return [{
|
|
1367
|
+
paramName: typeParam.name.text,
|
|
1368
|
+
constraintText: normalizeText(liveTypeParam.constraint.getText(family.sourceFile))
|
|
1369
|
+
}];
|
|
1370
|
+
});
|
|
1371
|
+
if (constrainedParams.length === 0) return false;
|
|
1372
|
+
return constrainedParams.some(
|
|
1373
|
+
({ paramName, constraintText }) => hasIntersectionCast(body, family.sourceFile, paramName, constraintText)
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
function hasIntersectionCast(body, sourceFile, paramName, constraintText) {
|
|
1377
|
+
let found = false;
|
|
1378
|
+
function visit(node) {
|
|
1379
|
+
if (found) return;
|
|
1380
|
+
if (ts28.isAsExpression(node) || ts28.isTypeAssertionExpression(node)) {
|
|
1381
|
+
if (isConstraintIntersection(node.type, sourceFile, paramName, constraintText)) {
|
|
1382
|
+
found = true;
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
ts28.forEachChild(node, visit);
|
|
1387
|
+
}
|
|
1388
|
+
visit(body);
|
|
1389
|
+
return found;
|
|
1390
|
+
}
|
|
1391
|
+
function isConstraintIntersection(typeNode, sourceFile, paramName, constraintText) {
|
|
1392
|
+
const target = unwrapParenthesizedType(typeNode);
|
|
1393
|
+
if (!ts28.isIntersectionTypeNode(target)) return false;
|
|
1394
|
+
let hasParam = false;
|
|
1395
|
+
let hasConstraint = false;
|
|
1396
|
+
for (const part of target.types) {
|
|
1397
|
+
const text = normalizeText(unwrapParenthesizedType(part).getText(sourceFile));
|
|
1398
|
+
if (text === paramName) hasParam = true;
|
|
1399
|
+
if (text === constraintText) hasConstraint = true;
|
|
1400
|
+
}
|
|
1401
|
+
return hasParam && hasConstraint;
|
|
1402
|
+
}
|
|
1403
|
+
function unwrapParenthesizedType(typeNode) {
|
|
1404
|
+
let current = typeNode;
|
|
1405
|
+
while (ts28.isParenthesizedTypeNode(current)) {
|
|
1406
|
+
current = current.type;
|
|
1407
|
+
}
|
|
1408
|
+
return current;
|
|
1409
|
+
}
|
|
1410
|
+
function collectOverloadFamilies(sourceFile, file) {
|
|
1411
|
+
const families = [];
|
|
1412
|
+
collectFromList(sourceFile.statements, sourceFile, file, families);
|
|
1413
|
+
function visit(node) {
|
|
1414
|
+
if (ts28.isClassDeclaration(node) || ts28.isClassExpression(node)) {
|
|
1415
|
+
collectFromList(node.members, sourceFile, file, families);
|
|
1416
|
+
}
|
|
1417
|
+
ts28.forEachChild(node, visit);
|
|
1418
|
+
}
|
|
1419
|
+
ts28.forEachChild(sourceFile, visit);
|
|
1420
|
+
return families;
|
|
1421
|
+
}
|
|
1422
|
+
function collectFromList(nodes, sourceFile, file, families) {
|
|
1423
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
1424
|
+
const current = asOverloadDeclaration(nodes[index]);
|
|
1425
|
+
const name = current ? getDeclarationName(current) : null;
|
|
1426
|
+
if (current === null || name === null) continue;
|
|
1427
|
+
const run = [current];
|
|
1428
|
+
let nextIndex = index + 1;
|
|
1429
|
+
while (nextIndex < nodes.length) {
|
|
1430
|
+
const next = asOverloadDeclaration(nodes[nextIndex]);
|
|
1431
|
+
if (next === null) break;
|
|
1432
|
+
if (getDeclarationName(next) !== name) break;
|
|
1433
|
+
run.push(next);
|
|
1434
|
+
nextIndex++;
|
|
1435
|
+
}
|
|
1436
|
+
const family = toOverloadFamily(run, name, file, sourceFile);
|
|
1437
|
+
if (family !== null) {
|
|
1438
|
+
families.push(family);
|
|
1439
|
+
}
|
|
1440
|
+
index = nextIndex - 1;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function toOverloadFamily(run, name, file, sourceFile) {
|
|
1444
|
+
if (run.length < 2) return null;
|
|
1445
|
+
const implementation = run.at(-1);
|
|
1446
|
+
if (implementation === void 0 || implementation.body === void 0) return null;
|
|
1447
|
+
if (run.slice(0, -1).some((declaration) => declaration.body !== void 0)) return null;
|
|
1448
|
+
return {
|
|
1449
|
+
name,
|
|
1450
|
+
file,
|
|
1451
|
+
sourceFile,
|
|
1452
|
+
overloads: run.slice(0, -1),
|
|
1453
|
+
implementation
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
function asOverloadDeclaration(node) {
|
|
1457
|
+
if (node === void 0) return null;
|
|
1458
|
+
if (ts28.isFunctionDeclaration(node)) return node;
|
|
1459
|
+
if (ts28.isMethodDeclaration(node)) return node;
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
function getDeclarationName(node) {
|
|
1463
|
+
if (ts28.isFunctionDeclaration(node)) {
|
|
1464
|
+
return node.name?.text ?? null;
|
|
1465
|
+
}
|
|
1466
|
+
if (ts28.isIdentifier(node.name) || ts28.isStringLiteral(node.name) || ts28.isNumericLiteral(node.name)) {
|
|
1467
|
+
return node.name.text;
|
|
1468
|
+
}
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
function lineOf(sourceFile, node) {
|
|
1472
|
+
return ts28.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1473
|
+
}
|
|
1474
|
+
function normalizeText(text) {
|
|
1475
|
+
return text.replace(/\s+/g, "");
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1281
1478
|
// src/rules/index.ts
|
|
1282
1479
|
var allRules = [
|
|
1283
1480
|
noEmptyCatch,
|
|
@@ -1293,6 +1490,7 @@ var allRules = [
|
|
|
1293
1490
|
noAnyCast,
|
|
1294
1491
|
noExplicitAnyAnnotation,
|
|
1295
1492
|
duplicateInlineTypeInParams,
|
|
1493
|
+
noInlineTypeAssertion,
|
|
1296
1494
|
noTypeAssertion,
|
|
1297
1495
|
noRedundantExistenceGuard,
|
|
1298
1496
|
preferDefaultParamValue,
|
|
@@ -1311,11 +1509,13 @@ var allRules = [
|
|
|
1311
1509
|
trivialWrapper,
|
|
1312
1510
|
unusedExport,
|
|
1313
1511
|
duplicateFile,
|
|
1314
|
-
duplicateStatementSequence
|
|
1512
|
+
duplicateStatementSequence,
|
|
1513
|
+
deadOverload
|
|
1315
1514
|
];
|
|
1316
1515
|
var ruleMetadata = {
|
|
1317
1516
|
"no-any-cast": { category: "type-evasion", tags: ["safety"] },
|
|
1318
1517
|
"no-explicit-any-annotation": { category: "type-evasion", tags: ["safety"] },
|
|
1518
|
+
"no-inline-type-assertion": { category: "type-evasion", tags: ["safety"] },
|
|
1319
1519
|
"no-type-assertion": { category: "type-evasion", tags: ["safety"] },
|
|
1320
1520
|
"no-ts-ignore": { category: "type-evasion", tags: ["safety"] },
|
|
1321
1521
|
"no-optional-property-access": { category: "defensive-code", tags: ["type-aware"] },
|
|
@@ -1345,7 +1545,8 @@ var ruleMetadata = {
|
|
|
1345
1545
|
"trivial-wrapper": { category: "cross-file", tags: ["duplicate"] },
|
|
1346
1546
|
"unused-export": { category: "cross-file", tags: ["api"] },
|
|
1347
1547
|
"duplicate-file": { category: "cross-file", tags: ["duplicate"] },
|
|
1348
|
-
"duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] }
|
|
1548
|
+
"duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] },
|
|
1549
|
+
"dead-overload": { category: "cross-file", tags: ["api", "type-evasion"] }
|
|
1349
1550
|
};
|
|
1350
1551
|
function getRuleMetadata(ruleId) {
|
|
1351
1552
|
const metadata = ruleMetadata[ruleId];
|
|
@@ -1405,32 +1606,32 @@ function isFailOn(value) {
|
|
|
1405
1606
|
}
|
|
1406
1607
|
|
|
1407
1608
|
// src/collect/index.ts
|
|
1408
|
-
import * as
|
|
1609
|
+
import * as ts31 from "typescript";
|
|
1409
1610
|
import { createHash as createHash2 } from "crypto";
|
|
1410
1611
|
|
|
1411
1612
|
// src/utils/hash.ts
|
|
1412
1613
|
import { createHash } from "crypto";
|
|
1413
|
-
import * as
|
|
1614
|
+
import * as ts29 from "typescript";
|
|
1414
1615
|
function hashTypeShape(node, sourceFile) {
|
|
1415
1616
|
const normalized = normalizeTypeNode(node, sourceFile);
|
|
1416
1617
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
1417
1618
|
}
|
|
1418
1619
|
function normalizeTypeNode(node, sourceFile) {
|
|
1419
|
-
if (
|
|
1620
|
+
if (ts29.isTypeLiteralNode(node)) {
|
|
1420
1621
|
const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
|
|
1421
1622
|
return `{${normalized}}`;
|
|
1422
1623
|
}
|
|
1423
|
-
if (
|
|
1624
|
+
if (ts29.isInterfaceDeclaration(node)) {
|
|
1424
1625
|
const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
|
|
1425
1626
|
return `{${normalized}}`;
|
|
1426
1627
|
}
|
|
1427
|
-
if (
|
|
1628
|
+
if (ts29.isPropertySignature(node)) {
|
|
1428
1629
|
const keyName = node.name.getText(sourceFile);
|
|
1429
1630
|
const optional = node.questionToken ? "?" : "";
|
|
1430
1631
|
const type = node.type ? normalizeTypeNode(node.type, sourceFile) : "any";
|
|
1431
1632
|
return `${keyName}${optional}:${type}`;
|
|
1432
1633
|
}
|
|
1433
|
-
if (
|
|
1634
|
+
if (ts29.isTypeAliasDeclaration(node)) {
|
|
1434
1635
|
return normalizeTypeNode(node.type, sourceFile);
|
|
1435
1636
|
}
|
|
1436
1637
|
return node.getText(sourceFile).replace(/\s+/g, " ").trim();
|
|
@@ -1463,7 +1664,7 @@ function normalizeBody(node, sourceFile, paramNames) {
|
|
|
1463
1664
|
text = text.replace(/\s+/g, " ").trim();
|
|
1464
1665
|
return text;
|
|
1465
1666
|
}
|
|
1466
|
-
function
|
|
1667
|
+
function normalizeText2(text) {
|
|
1467
1668
|
let t = text.replace(/\/\/[^\n]*/g, "");
|
|
1468
1669
|
t = t.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1469
1670
|
t = t.replace(/"(?:[^"\\]|\\.)*"/g, '"__STR__"');
|
|
@@ -1596,7 +1797,7 @@ var InlineParamTypeRegistry = class extends BaseRegistry {
|
|
|
1596
1797
|
};
|
|
1597
1798
|
|
|
1598
1799
|
// src/typecheck/walk.ts
|
|
1599
|
-
import * as
|
|
1800
|
+
import * as ts30 from "typescript";
|
|
1600
1801
|
function buildContext(rule, sourceFile, checker, source, filename, diagnostics) {
|
|
1601
1802
|
return {
|
|
1602
1803
|
filename,
|
|
@@ -1604,7 +1805,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
|
|
|
1604
1805
|
sourceFile,
|
|
1605
1806
|
checker,
|
|
1606
1807
|
report(node, message) {
|
|
1607
|
-
const { line, character } =
|
|
1808
|
+
const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
|
|
1608
1809
|
diagnostics.push({
|
|
1609
1810
|
ruleId: rule.id,
|
|
1610
1811
|
severity: rule.severity,
|
|
@@ -1615,7 +1816,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
|
|
|
1615
1816
|
});
|
|
1616
1817
|
},
|
|
1617
1818
|
reportAtOffset(offset, message) {
|
|
1618
|
-
const { line, character } =
|
|
1819
|
+
const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, offset);
|
|
1619
1820
|
diagnostics.push({
|
|
1620
1821
|
ruleId: rule.id,
|
|
1621
1822
|
severity: rule.severity,
|
|
@@ -1666,7 +1867,7 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1666
1867
|
rule.visit(node, ctx);
|
|
1667
1868
|
}
|
|
1668
1869
|
}
|
|
1669
|
-
|
|
1870
|
+
ts31.forEachChild(node, visit2);
|
|
1670
1871
|
};
|
|
1671
1872
|
var visit = visit2;
|
|
1672
1873
|
const file = sourceFile.fileName;
|
|
@@ -1680,7 +1881,7 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1680
1881
|
rule,
|
|
1681
1882
|
ctx: buildContext(rule, sourceFile, checker, source, file, diagnostics)
|
|
1682
1883
|
}));
|
|
1683
|
-
|
|
1884
|
+
ts31.forEachChild(sourceFile, visit2);
|
|
1684
1885
|
}
|
|
1685
1886
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
1686
1887
|
for (const [file, { source }] of fileMap) {
|
|
@@ -1696,52 +1897,52 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1696
1897
|
return { index: { types, functions, constants, callSites, imports, files: fileMap, fileHashes, statementSequences, inlineParamTypes }, diagnostics };
|
|
1697
1898
|
}
|
|
1698
1899
|
function hasNonPublicModifier(node) {
|
|
1699
|
-
if (!
|
|
1700
|
-
const mods =
|
|
1900
|
+
if (!ts31.canHaveModifiers(node)) return false;
|
|
1901
|
+
const mods = ts31.getModifiers(node);
|
|
1701
1902
|
if (mods === void 0) return false;
|
|
1702
1903
|
return mods.some(
|
|
1703
|
-
(m) => m.kind ===
|
|
1904
|
+
(m) => m.kind === ts31.SyntaxKind.PrivateKeyword || m.kind === ts31.SyntaxKind.ProtectedKeyword
|
|
1704
1905
|
);
|
|
1705
1906
|
}
|
|
1706
1907
|
function isExported(node) {
|
|
1707
|
-
if (!
|
|
1708
|
-
const mods =
|
|
1908
|
+
if (!ts31.canHaveModifiers(node)) return false;
|
|
1909
|
+
const mods = ts31.getModifiers(node);
|
|
1709
1910
|
if (mods === void 0) return false;
|
|
1710
|
-
return mods.some((m) => m.kind ===
|
|
1911
|
+
return mods.some((m) => m.kind === ts31.SyntaxKind.ExportKeyword);
|
|
1711
1912
|
}
|
|
1712
1913
|
function collectTypes(node, file, sourceFile, registry) {
|
|
1713
|
-
if (
|
|
1714
|
-
const line =
|
|
1914
|
+
if (ts31.isTypeAliasDeclaration(node)) {
|
|
1915
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1715
1916
|
registry.add(node.name.text, file, line, node.type, sourceFile, isExported(node));
|
|
1716
1917
|
}
|
|
1717
|
-
if (
|
|
1718
|
-
const line =
|
|
1918
|
+
if (ts31.isInterfaceDeclaration(node)) {
|
|
1919
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1719
1920
|
registry.add(node.name.text, file, line, node, sourceFile, isExported(node));
|
|
1720
1921
|
}
|
|
1721
1922
|
}
|
|
1722
1923
|
function isConstantValue(node) {
|
|
1723
|
-
if (
|
|
1724
|
-
if (
|
|
1725
|
-
if (
|
|
1726
|
-
if (
|
|
1727
|
-
if (
|
|
1924
|
+
if (ts31.isStringLiteral(node)) return true;
|
|
1925
|
+
if (ts31.isNumericLiteral(node)) return true;
|
|
1926
|
+
if (ts31.isNoSubstitutionTemplateLiteral(node)) return true;
|
|
1927
|
+
if (ts31.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
|
|
1928
|
+
if (ts31.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
|
|
1728
1929
|
return false;
|
|
1729
1930
|
}
|
|
1730
1931
|
function collectConstants(node, file, sourceFile, registry) {
|
|
1731
|
-
if (!
|
|
1732
|
-
if (!(node.declarationList.flags &
|
|
1932
|
+
if (!ts31.isVariableStatement(node)) return;
|
|
1933
|
+
if (!(node.declarationList.flags & ts31.NodeFlags.Const)) return;
|
|
1733
1934
|
const exported = isExported(node);
|
|
1734
1935
|
for (const decl of node.declarationList.declarations) {
|
|
1735
|
-
if (!decl.initializer || !
|
|
1936
|
+
if (!decl.initializer || !ts31.isIdentifier(decl.name)) continue;
|
|
1736
1937
|
if (!isConstantValue(decl.initializer)) continue;
|
|
1737
1938
|
const valueText = decl.initializer.getText(sourceFile).replace(/\s+/g, " ").trim();
|
|
1738
1939
|
const valueHash = createHash2("sha256").update(valueText).digest("hex").slice(0, 16);
|
|
1739
|
-
const line =
|
|
1940
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
|
|
1740
1941
|
registry.add({ name: decl.name.text, file, line, valueHash, valueText, exported });
|
|
1741
1942
|
}
|
|
1742
1943
|
}
|
|
1743
1944
|
function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name, extra) {
|
|
1744
|
-
const line =
|
|
1945
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
|
|
1745
1946
|
const params = extractParams(parameters, sourceFile);
|
|
1746
1947
|
const paramNames = params.map((p) => p.name);
|
|
1747
1948
|
const hash = hashFunctionBody(body, sourceFile);
|
|
@@ -1763,17 +1964,17 @@ function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name,
|
|
|
1763
1964
|
};
|
|
1764
1965
|
}
|
|
1765
1966
|
function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
1766
|
-
if (
|
|
1967
|
+
if (ts31.isFunctionDeclaration(node) && node.name && node.body) {
|
|
1767
1968
|
registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, node.name.text, {
|
|
1768
1969
|
exported: isExported(node),
|
|
1769
1970
|
symbol: checker.getSymbolAtLocation(node.name),
|
|
1770
1971
|
node
|
|
1771
1972
|
}));
|
|
1772
1973
|
}
|
|
1773
|
-
if (
|
|
1974
|
+
if (ts31.isVariableStatement(node)) {
|
|
1774
1975
|
const exported = isExported(node);
|
|
1775
1976
|
for (const decl of node.declarationList.declarations) {
|
|
1776
|
-
if (decl.initializer &&
|
|
1977
|
+
if (decl.initializer && ts31.isArrowFunction(decl.initializer) && ts31.isIdentifier(decl.name)) {
|
|
1777
1978
|
const arrow = decl.initializer;
|
|
1778
1979
|
registry.add(buildFunctionEntry(arrow.body, arrow.parameters, sourceFile, file, decl, decl.name.text, {
|
|
1779
1980
|
exported,
|
|
@@ -1781,7 +1982,7 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1781
1982
|
node: arrow
|
|
1782
1983
|
}));
|
|
1783
1984
|
}
|
|
1784
|
-
if (decl.initializer &&
|
|
1985
|
+
if (decl.initializer && ts31.isFunctionExpression(decl.initializer) && ts31.isIdentifier(decl.name)) {
|
|
1785
1986
|
const fn = decl.initializer;
|
|
1786
1987
|
if (fn.body) {
|
|
1787
1988
|
registry.add(buildFunctionEntry(fn.body, fn.parameters, sourceFile, file, decl, decl.name.text, {
|
|
@@ -1793,22 +1994,22 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1793
1994
|
}
|
|
1794
1995
|
}
|
|
1795
1996
|
}
|
|
1796
|
-
if (
|
|
1997
|
+
if (ts31.isPropertyAssignment(node) && (ts31.isIdentifier(node.name) || ts31.isStringLiteral(node.name))) {
|
|
1797
1998
|
const init = node.initializer;
|
|
1798
1999
|
const propName = node.name.text;
|
|
1799
|
-
if (
|
|
2000
|
+
if (ts31.isArrowFunction(init)) {
|
|
1800
2001
|
registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
|
|
1801
2002
|
}
|
|
1802
|
-
if (
|
|
2003
|
+
if (ts31.isFunctionExpression(init) && init.body) {
|
|
1803
2004
|
registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
|
|
1804
2005
|
}
|
|
1805
2006
|
}
|
|
1806
|
-
if (
|
|
2007
|
+
if (ts31.isArrowFunction(node) || ts31.isFunctionExpression(node)) {
|
|
1807
2008
|
const parent = node.parent;
|
|
1808
|
-
if (
|
|
1809
|
-
} else if (
|
|
2009
|
+
if (ts31.isVariableDeclaration(parent) && ts31.isIdentifier(parent.name)) {
|
|
2010
|
+
} else if (ts31.isPropertyAssignment(parent)) {
|
|
1810
2011
|
} else {
|
|
1811
|
-
const body =
|
|
2012
|
+
const body = ts31.isArrowFunction(node) ? node.body : node.body;
|
|
1812
2013
|
if (body) {
|
|
1813
2014
|
const MIN_ANON_BODY = 64;
|
|
1814
2015
|
if (bodyTextLength(body, sourceFile) >= MIN_ANON_BODY) {
|
|
@@ -1818,14 +2019,14 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1818
2019
|
}
|
|
1819
2020
|
}
|
|
1820
2021
|
}
|
|
1821
|
-
if (
|
|
2022
|
+
if (ts31.isMethodDeclaration(node) && node.body && ts31.isIdentifier(node.name)) {
|
|
1822
2023
|
const parent = node.parent;
|
|
1823
|
-
if (
|
|
2024
|
+
if (ts31.isClassDeclaration(parent)) {
|
|
1824
2025
|
if (hasNonPublicModifier(node)) return;
|
|
1825
2026
|
const className = parent.name ? parent.name.text : "<anonymous>";
|
|
1826
2027
|
const name = `${className}.${node.name.text}`;
|
|
1827
2028
|
const implementsInterface = parent.heritageClauses?.some(
|
|
1828
|
-
(c) => c.token ===
|
|
2029
|
+
(c) => c.token === ts31.SyntaxKind.ImplementsKeyword
|
|
1829
2030
|
) ?? false;
|
|
1830
2031
|
registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, name, {
|
|
1831
2032
|
exported: isExported(parent),
|
|
@@ -1848,16 +2049,16 @@ function extractParams(parameters, sourceFile) {
|
|
|
1848
2049
|
}
|
|
1849
2050
|
function deriveAnonymousName(node, sourceFile) {
|
|
1850
2051
|
const parent = node.parent;
|
|
1851
|
-
const line =
|
|
1852
|
-
if (
|
|
2052
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
2053
|
+
if (ts31.isCallExpression(parent)) {
|
|
1853
2054
|
const grandparent = parent.parent;
|
|
1854
|
-
if (
|
|
2055
|
+
if (ts31.isPropertyAssignment(grandparent) && ts31.isIdentifier(grandparent.name)) {
|
|
1855
2056
|
return grandparent.name.text;
|
|
1856
2057
|
}
|
|
1857
2058
|
let calleeName = null;
|
|
1858
|
-
if (
|
|
2059
|
+
if (ts31.isIdentifier(parent.expression)) {
|
|
1859
2060
|
calleeName = parent.expression.text;
|
|
1860
|
-
} else if (
|
|
2061
|
+
} else if (ts31.isPropertyAccessExpression(parent.expression)) {
|
|
1861
2062
|
calleeName = parent.expression.name.text;
|
|
1862
2063
|
}
|
|
1863
2064
|
if (calleeName) {
|
|
@@ -1868,7 +2069,7 @@ function deriveAnonymousName(node, sourceFile) {
|
|
|
1868
2069
|
return `<anonymous>:${line}`;
|
|
1869
2070
|
}
|
|
1870
2071
|
function collectImports(node, file, imports) {
|
|
1871
|
-
if (
|
|
2072
|
+
if (ts31.isImportDeclaration(node) && ts31.isStringLiteral(node.moduleSpecifier)) {
|
|
1872
2073
|
const moduleSource = node.moduleSpecifier.text;
|
|
1873
2074
|
const clause = node.importClause;
|
|
1874
2075
|
if (!clause) return;
|
|
@@ -1880,7 +2081,7 @@ function collectImports(node, file, imports) {
|
|
|
1880
2081
|
source: moduleSource
|
|
1881
2082
|
});
|
|
1882
2083
|
}
|
|
1883
|
-
if (clause.namedBindings &&
|
|
2084
|
+
if (clause.namedBindings && ts31.isNamedImports(clause.namedBindings)) {
|
|
1884
2085
|
for (const el of clause.namedBindings.elements) {
|
|
1885
2086
|
imports.push({
|
|
1886
2087
|
file,
|
|
@@ -1891,9 +2092,9 @@ function collectImports(node, file, imports) {
|
|
|
1891
2092
|
}
|
|
1892
2093
|
}
|
|
1893
2094
|
}
|
|
1894
|
-
if (
|
|
2095
|
+
if (ts31.isExportDeclaration(node) && node.moduleSpecifier && ts31.isStringLiteral(node.moduleSpecifier)) {
|
|
1895
2096
|
const moduleSource = node.moduleSpecifier.text;
|
|
1896
|
-
if (node.exportClause &&
|
|
2097
|
+
if (node.exportClause && ts31.isNamedExports(node.exportClause)) {
|
|
1897
2098
|
for (const el of node.exportClause.elements) {
|
|
1898
2099
|
imports.push({
|
|
1899
2100
|
file,
|
|
@@ -1906,7 +2107,7 @@ function collectImports(node, file, imports) {
|
|
|
1906
2107
|
}
|
|
1907
2108
|
}
|
|
1908
2109
|
function collectStatementSequences(node, file, sourceFile, registry) {
|
|
1909
|
-
if (!
|
|
2110
|
+
if (!ts31.isBlock(node)) return;
|
|
1910
2111
|
const stmts = node.statements;
|
|
1911
2112
|
const n = stmts.length;
|
|
1912
2113
|
if (n < 3) return;
|
|
@@ -1916,43 +2117,46 @@ function collectStatementSequences(node, file, sourceFile, registry) {
|
|
|
1916
2117
|
const window = stmts.slice(start, start + size);
|
|
1917
2118
|
const texts = window.map((s) => s.getText(sourceFile));
|
|
1918
2119
|
const joined = texts.join("\n");
|
|
1919
|
-
const normalized =
|
|
2120
|
+
const normalized = normalizeText2(joined);
|
|
1920
2121
|
if (normalized.length < 128) continue;
|
|
1921
2122
|
const hash = hashText(joined.replace(/\s+/g, " ").trim());
|
|
1922
2123
|
const normalizedHash = hashText(normalized);
|
|
1923
2124
|
const firstStmt = window[0];
|
|
1924
2125
|
const lastStmt = window[window.length - 1];
|
|
1925
2126
|
if (firstStmt === void 0 || lastStmt === void 0) continue;
|
|
1926
|
-
const line =
|
|
1927
|
-
const endLine =
|
|
2127
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
|
|
2128
|
+
const endLine = ts31.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
|
|
1928
2129
|
registry.add({ file, line, endLine, hash, normalizedHash, statementCount: size, normalizedBodyLength: normalized.length });
|
|
1929
2130
|
}
|
|
1930
2131
|
}
|
|
1931
2132
|
}
|
|
1932
2133
|
function collectInlineParamTypes(node, file, sourceFile, registry) {
|
|
1933
|
-
if (!
|
|
2134
|
+
if (!ts31.isTypeLiteralNode(node)) return;
|
|
1934
2135
|
const parent = node.parent;
|
|
1935
|
-
if (!parent || !
|
|
2136
|
+
if (!parent || !ts31.isParameter(parent)) return;
|
|
1936
2137
|
if (parent.type !== node) return;
|
|
1937
|
-
const line =
|
|
2138
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1938
2139
|
registry.add(file, line, node, sourceFile);
|
|
1939
2140
|
}
|
|
1940
2141
|
function collectCallSites(node, file, sourceFile, checker, sites) {
|
|
1941
|
-
if (!
|
|
2142
|
+
if (!ts31.isCallExpression(node)) return;
|
|
1942
2143
|
let calleeName = null;
|
|
1943
|
-
if (
|
|
2144
|
+
if (ts31.isIdentifier(node.expression)) {
|
|
1944
2145
|
calleeName = node.expression.text;
|
|
1945
|
-
} else if (
|
|
2146
|
+
} else if (ts31.isPropertyAccessExpression(node.expression)) {
|
|
1946
2147
|
calleeName = node.expression.name.text;
|
|
1947
2148
|
}
|
|
1948
2149
|
if (calleeName) {
|
|
1949
|
-
const line =
|
|
2150
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1950
2151
|
let symbol;
|
|
2152
|
+
let resolvedDeclaration;
|
|
1951
2153
|
try {
|
|
1952
2154
|
symbol = checker.getSymbolAtLocation(node.expression);
|
|
1953
|
-
if (symbol && symbol.flags &
|
|
2155
|
+
if (symbol && symbol.flags & ts31.SymbolFlags.Alias) {
|
|
1954
2156
|
symbol = checker.getAliasedSymbol(symbol);
|
|
1955
2157
|
}
|
|
2158
|
+
const signature = checker.getResolvedSignature(node);
|
|
2159
|
+
resolvedDeclaration = signature?.declaration;
|
|
1956
2160
|
} catch {
|
|
1957
2161
|
}
|
|
1958
2162
|
sites.push({
|
|
@@ -1961,7 +2165,8 @@ function collectCallSites(node, file, sourceFile, checker, sites) {
|
|
|
1961
2165
|
line,
|
|
1962
2166
|
argCount: node.arguments.length,
|
|
1963
2167
|
node,
|
|
1964
|
-
symbol
|
|
2168
|
+
symbol,
|
|
2169
|
+
resolvedDeclaration
|
|
1965
2170
|
});
|
|
1966
2171
|
}
|
|
1967
2172
|
}
|
|
@@ -1970,12 +2175,12 @@ function collectAllComments(sourceFile) {
|
|
|
1970
2175
|
const source = sourceFile.getFullText();
|
|
1971
2176
|
const seen = /* @__PURE__ */ new Set();
|
|
1972
2177
|
function visit(node) {
|
|
1973
|
-
const leading =
|
|
2178
|
+
const leading = ts31.getLeadingCommentRanges(source, node.getFullStart());
|
|
1974
2179
|
if (leading) {
|
|
1975
2180
|
for (const r of leading) {
|
|
1976
2181
|
if (seen.has(r.pos)) continue;
|
|
1977
2182
|
seen.add(r.pos);
|
|
1978
|
-
const isLine = r.kind ===
|
|
2183
|
+
const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
|
|
1979
2184
|
comments.push({
|
|
1980
2185
|
type: isLine ? "Line" : "Block",
|
|
1981
2186
|
value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
|
|
@@ -1984,12 +2189,12 @@ function collectAllComments(sourceFile) {
|
|
|
1984
2189
|
});
|
|
1985
2190
|
}
|
|
1986
2191
|
}
|
|
1987
|
-
const trailing =
|
|
2192
|
+
const trailing = ts31.getTrailingCommentRanges(source, node.getEnd());
|
|
1988
2193
|
if (trailing) {
|
|
1989
2194
|
for (const r of trailing) {
|
|
1990
2195
|
if (seen.has(r.pos)) continue;
|
|
1991
2196
|
seen.add(r.pos);
|
|
1992
|
-
const isLine = r.kind ===
|
|
2197
|
+
const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
|
|
1993
2198
|
comments.push({
|
|
1994
2199
|
type: isLine ? "Line" : "Block",
|
|
1995
2200
|
value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
|
|
@@ -1998,34 +2203,34 @@ function collectAllComments(sourceFile) {
|
|
|
1998
2203
|
});
|
|
1999
2204
|
}
|
|
2000
2205
|
}
|
|
2001
|
-
|
|
2206
|
+
ts31.forEachChild(node, visit);
|
|
2002
2207
|
}
|
|
2003
2208
|
visit(sourceFile);
|
|
2004
2209
|
return comments;
|
|
2005
2210
|
}
|
|
2006
2211
|
|
|
2007
2212
|
// src/typecheck/program.ts
|
|
2008
|
-
import * as
|
|
2213
|
+
import * as ts32 from "typescript";
|
|
2009
2214
|
import { dirname as dirname2 } from "path";
|
|
2010
2215
|
function createProgramFromFiles(files) {
|
|
2011
2216
|
let configPath;
|
|
2012
2217
|
if (files.length > 0) {
|
|
2013
|
-
configPath =
|
|
2218
|
+
configPath = ts32.findConfigFile(dirname2(files[0]), ts32.sys.fileExists, "tsconfig.json");
|
|
2014
2219
|
}
|
|
2015
2220
|
if (configPath) {
|
|
2016
|
-
const configFile =
|
|
2017
|
-
const parsed =
|
|
2018
|
-
return
|
|
2221
|
+
const configFile = ts32.readConfigFile(configPath, ts32.sys.readFile);
|
|
2222
|
+
const parsed = ts32.parseJsonConfigFileContent(configFile.config, ts32.sys, dirname2(configPath));
|
|
2223
|
+
return ts32.createProgram({
|
|
2019
2224
|
rootNames: files,
|
|
2020
2225
|
options: { ...parsed.options, skipLibCheck: true }
|
|
2021
2226
|
});
|
|
2022
2227
|
}
|
|
2023
|
-
return
|
|
2228
|
+
return ts32.createProgram({
|
|
2024
2229
|
rootNames: files,
|
|
2025
2230
|
options: {
|
|
2026
|
-
target:
|
|
2027
|
-
module:
|
|
2028
|
-
moduleResolution:
|
|
2231
|
+
target: ts32.ScriptTarget.ESNext,
|
|
2232
|
+
module: ts32.ModuleKind.ESNext,
|
|
2233
|
+
moduleResolution: ts32.ModuleResolutionKind.Bundler,
|
|
2029
2234
|
strict: true,
|
|
2030
2235
|
skipLibCheck: true,
|
|
2031
2236
|
noEmit: true
|
|
@@ -2265,4 +2470,4 @@ export {
|
|
|
2265
2470
|
executeScan,
|
|
2266
2471
|
scan
|
|
2267
2472
|
};
|
|
2268
|
-
//# sourceMappingURL=chunk-
|
|
2473
|
+
//# sourceMappingURL=chunk-HE647T6E.js.map
|