@fairfox/polly 0.2.1 → 0.3.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.
|
@@ -727,10 +727,12 @@ import { Project, SyntaxKind, Node } from "ts-morph";
|
|
|
727
727
|
|
|
728
728
|
class HandlerExtractor {
|
|
729
729
|
project;
|
|
730
|
+
typeGuardCache;
|
|
730
731
|
constructor(tsConfigPath) {
|
|
731
732
|
this.project = new Project({
|
|
732
733
|
tsConfigFilePath: tsConfigPath
|
|
733
734
|
});
|
|
735
|
+
this.typeGuardCache = new WeakMap;
|
|
734
736
|
}
|
|
735
737
|
extractHandlers() {
|
|
736
738
|
const handlers = [];
|
|
@@ -757,7 +759,7 @@ class HandlerExtractor {
|
|
|
757
759
|
const expression = node.getExpression();
|
|
758
760
|
if (Node.isPropertyAccessExpression(expression)) {
|
|
759
761
|
const methodName = expression.getName();
|
|
760
|
-
if (methodName === "on") {
|
|
762
|
+
if (methodName === "on" || methodName === "addEventListener") {
|
|
761
763
|
const handler = this.extractHandler(node, context, filePath);
|
|
762
764
|
if (handler) {
|
|
763
765
|
handlers.push(handler);
|
|
@@ -765,6 +767,18 @@ class HandlerExtractor {
|
|
|
765
767
|
}
|
|
766
768
|
}
|
|
767
769
|
}
|
|
770
|
+
if (Node.isSwitchStatement(node)) {
|
|
771
|
+
const switchHandlers = this.extractSwitchCaseHandlers(node, context, filePath);
|
|
772
|
+
handlers.push(...switchHandlers);
|
|
773
|
+
}
|
|
774
|
+
if (Node.isVariableDeclaration(node)) {
|
|
775
|
+
const mapHandlers = this.extractHandlerMapPattern(node, context, filePath);
|
|
776
|
+
handlers.push(...mapHandlers);
|
|
777
|
+
}
|
|
778
|
+
if (Node.isIfStatement(node)) {
|
|
779
|
+
const typeGuardHandlers = this.extractTypeGuardHandlers(node, context, filePath);
|
|
780
|
+
handlers.push(...typeGuardHandlers);
|
|
781
|
+
}
|
|
768
782
|
});
|
|
769
783
|
return handlers;
|
|
770
784
|
}
|
|
@@ -906,6 +920,182 @@ class HandlerExtractor {
|
|
|
906
920
|
}
|
|
907
921
|
return;
|
|
908
922
|
}
|
|
923
|
+
extractSwitchCaseHandlers(switchNode, context, filePath) {
|
|
924
|
+
const handlers = [];
|
|
925
|
+
try {
|
|
926
|
+
const expression = switchNode.getExpression();
|
|
927
|
+
const expressionText = expression.getText();
|
|
928
|
+
if (!/\.(type|kind|event|action)/.test(expressionText)) {
|
|
929
|
+
return handlers;
|
|
930
|
+
}
|
|
931
|
+
const caseClauses = switchNode.getClauses();
|
|
932
|
+
for (const clause of caseClauses) {
|
|
933
|
+
if (Node.isCaseClause(clause)) {
|
|
934
|
+
const caseExpr = clause.getExpression();
|
|
935
|
+
let messageType = null;
|
|
936
|
+
if (Node.isStringLiteral(caseExpr)) {
|
|
937
|
+
messageType = caseExpr.getLiteralValue();
|
|
938
|
+
}
|
|
939
|
+
if (messageType) {
|
|
940
|
+
const line = clause.getStartLineNumber();
|
|
941
|
+
handlers.push({
|
|
942
|
+
messageType,
|
|
943
|
+
node: context,
|
|
944
|
+
assignments: [],
|
|
945
|
+
preconditions: [],
|
|
946
|
+
postconditions: [],
|
|
947
|
+
location: { file: filePath, line }
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
} catch (error) {}
|
|
953
|
+
return handlers;
|
|
954
|
+
}
|
|
955
|
+
extractHandlerMapPattern(varDecl, context, filePath) {
|
|
956
|
+
const handlers = [];
|
|
957
|
+
try {
|
|
958
|
+
const initializer = varDecl.getInitializer();
|
|
959
|
+
if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
|
|
960
|
+
return handlers;
|
|
961
|
+
}
|
|
962
|
+
const varName = varDecl.getName().toLowerCase();
|
|
963
|
+
if (!/(handler|listener|callback|event)s?/.test(varName)) {
|
|
964
|
+
return handlers;
|
|
965
|
+
}
|
|
966
|
+
const properties = initializer.getProperties();
|
|
967
|
+
for (const prop of properties) {
|
|
968
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
969
|
+
const nameNode = prop.getNameNode();
|
|
970
|
+
let messageType = null;
|
|
971
|
+
if (Node.isStringLiteral(nameNode)) {
|
|
972
|
+
messageType = nameNode.getLiteralValue();
|
|
973
|
+
} else if (Node.isIdentifier(nameNode)) {
|
|
974
|
+
messageType = nameNode.getText();
|
|
975
|
+
}
|
|
976
|
+
if (messageType) {
|
|
977
|
+
const line = prop.getStartLineNumber();
|
|
978
|
+
handlers.push({
|
|
979
|
+
messageType,
|
|
980
|
+
node: context,
|
|
981
|
+
assignments: [],
|
|
982
|
+
preconditions: [],
|
|
983
|
+
postconditions: [],
|
|
984
|
+
location: { file: filePath, line }
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
} catch (error) {}
|
|
990
|
+
return handlers;
|
|
991
|
+
}
|
|
992
|
+
extractTypeGuardHandlers(ifNode, context, filePath) {
|
|
993
|
+
const handlers = [];
|
|
994
|
+
try {
|
|
995
|
+
const sourceFile = ifNode.getSourceFile();
|
|
996
|
+
let typeGuards = this.typeGuardCache.get(sourceFile);
|
|
997
|
+
if (!typeGuards) {
|
|
998
|
+
typeGuards = this.findTypePredicateFunctions(sourceFile);
|
|
999
|
+
this.typeGuardCache.set(sourceFile, typeGuards);
|
|
1000
|
+
}
|
|
1001
|
+
if (typeGuards.size === 0) {
|
|
1002
|
+
return handlers;
|
|
1003
|
+
}
|
|
1004
|
+
let currentIf = ifNode;
|
|
1005
|
+
while (currentIf) {
|
|
1006
|
+
const handler = this.extractHandlerFromIfClause(currentIf, typeGuards, context, filePath);
|
|
1007
|
+
if (handler) {
|
|
1008
|
+
handlers.push(handler);
|
|
1009
|
+
}
|
|
1010
|
+
const elseStatement = currentIf.getElseStatement();
|
|
1011
|
+
if (elseStatement && Node.isIfStatement(elseStatement)) {
|
|
1012
|
+
currentIf = elseStatement;
|
|
1013
|
+
} else {
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
} catch (error) {}
|
|
1018
|
+
return handlers;
|
|
1019
|
+
}
|
|
1020
|
+
extractHandlerFromIfClause(ifNode, typeGuards, context, filePath) {
|
|
1021
|
+
try {
|
|
1022
|
+
const condition = ifNode.getExpression();
|
|
1023
|
+
if (!Node.isCallExpression(condition)) {
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
const funcExpr = condition.getExpression();
|
|
1027
|
+
let funcName;
|
|
1028
|
+
if (Node.isIdentifier(funcExpr)) {
|
|
1029
|
+
funcName = funcExpr.getText();
|
|
1030
|
+
}
|
|
1031
|
+
if (!funcName || !typeGuards.has(funcName)) {
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
const messageType = typeGuards.get(funcName);
|
|
1035
|
+
const line = ifNode.getStartLineNumber();
|
|
1036
|
+
return {
|
|
1037
|
+
messageType,
|
|
1038
|
+
node: context,
|
|
1039
|
+
assignments: [],
|
|
1040
|
+
preconditions: [],
|
|
1041
|
+
postconditions: [],
|
|
1042
|
+
location: { file: filePath, line }
|
|
1043
|
+
};
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
findTypePredicateFunctions(sourceFile) {
|
|
1049
|
+
const typeGuards = new Map;
|
|
1050
|
+
sourceFile.forEachDescendant((node) => {
|
|
1051
|
+
if (Node.isFunctionDeclaration(node) || Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {
|
|
1052
|
+
const returnType = node.getReturnType();
|
|
1053
|
+
const returnTypeText = returnType.getText();
|
|
1054
|
+
if (/is\s+\w+/.test(returnTypeText)) {
|
|
1055
|
+
let functionName;
|
|
1056
|
+
if (Node.isFunctionDeclaration(node)) {
|
|
1057
|
+
functionName = node.getName();
|
|
1058
|
+
} else if (Node.isFunctionExpression(node)) {
|
|
1059
|
+
const parent = node.getParent();
|
|
1060
|
+
if (Node.isVariableDeclaration(parent)) {
|
|
1061
|
+
functionName = parent.getName();
|
|
1062
|
+
}
|
|
1063
|
+
} else if (Node.isArrowFunction(node)) {
|
|
1064
|
+
const parent = node.getParent();
|
|
1065
|
+
if (Node.isVariableDeclaration(parent)) {
|
|
1066
|
+
functionName = parent.getName();
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (functionName) {
|
|
1070
|
+
let messageType = null;
|
|
1071
|
+
const typeMatch = returnTypeText.match(/is\s+(\w+)/);
|
|
1072
|
+
if (typeMatch) {
|
|
1073
|
+
const typeName = typeMatch[1];
|
|
1074
|
+
messageType = this.extractMessageTypeFromTypeName(typeName);
|
|
1075
|
+
}
|
|
1076
|
+
if (!messageType) {
|
|
1077
|
+
const body = node.getBody();
|
|
1078
|
+
if (body) {
|
|
1079
|
+
const bodyText = body.getText();
|
|
1080
|
+
const typeValueMatch = bodyText.match(/\.type\s*===?\s*['"](\w+)['"]/);
|
|
1081
|
+
if (typeValueMatch) {
|
|
1082
|
+
messageType = typeValueMatch[1];
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (messageType) {
|
|
1087
|
+
typeGuards.set(functionName, messageType);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
return typeGuards;
|
|
1094
|
+
}
|
|
1095
|
+
extractMessageTypeFromTypeName(typeName) {
|
|
1096
|
+
const messageType = typeName.replace(/Message$/, "").replace(/Event$/, "").replace(/Request$/, "").replace(/Command$/, "").replace(/Query$/, "").toLowerCase();
|
|
1097
|
+
return messageType;
|
|
1098
|
+
}
|
|
909
1099
|
inferContext(filePath) {
|
|
910
1100
|
const path = filePath.toLowerCase();
|
|
911
1101
|
if (path.includes("/background/") || path.includes("\\background\\")) {
|
|
@@ -926,6 +1116,15 @@ class HandlerExtractor {
|
|
|
926
1116
|
if (path.includes("/offscreen/") || path.includes("\\offscreen\\")) {
|
|
927
1117
|
return "offscreen";
|
|
928
1118
|
}
|
|
1119
|
+
if (path.includes("/server/") || path.includes("\\server\\") || path.includes("/server.")) {
|
|
1120
|
+
return "server";
|
|
1121
|
+
}
|
|
1122
|
+
if (path.includes("/client/") || path.includes("\\client\\") || path.includes("/client.")) {
|
|
1123
|
+
return "client";
|
|
1124
|
+
}
|
|
1125
|
+
if (path.includes("/worker/") || path.includes("\\worker\\") || path.includes("service-worker")) {
|
|
1126
|
+
return "worker";
|
|
1127
|
+
}
|
|
929
1128
|
return "unknown";
|
|
930
1129
|
}
|
|
931
1130
|
}
|
|
@@ -2089,4 +2288,4 @@ Stack trace:`, COLORS.gray));
|
|
|
2089
2288
|
process.exit(1);
|
|
2090
2289
|
});
|
|
2091
2290
|
|
|
2092
|
-
//# debugId=
|
|
2291
|
+
//# debugId=5E943FE5FB56B58764756E2164756E21
|