1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.TypesUsageEvaluator = void 0;
|
4 | const ts = require("typescript");
|
5 | const typescript_1 = require("./helpers/typescript");
|
6 | class TypesUsageEvaluator {
|
7 | constructor(files, typeChecker) {
|
8 | this.nodesParentsMap = new Map();
|
9 | this.usageResultCache = new Map();
|
10 | this.typeChecker = typeChecker;
|
11 | this.computeUsages(files);
|
12 | }
|
13 | isSymbolUsedBySymbol(symbol, by) {
|
14 | return this.isSymbolUsedBySymbolImpl(this.getActualSymbol(symbol), this.getActualSymbol(by), new Set());
|
15 | }
|
16 | getSymbolsUsingSymbol(symbol) {
|
17 | return this.nodesParentsMap.get(this.getActualSymbol(symbol)) || null;
|
18 | }
|
19 | isSymbolUsedBySymbolImpl(fromSymbol, toSymbol, visitedSymbols) {
|
20 | if (fromSymbol === toSymbol) {
|
21 | return this.setUsageCacheValue(fromSymbol, toSymbol, true);
|
22 | }
|
23 | const cacheResult = this.usageResultCache.get(fromSymbol)?.get(toSymbol);
|
24 | if (cacheResult !== undefined) {
|
25 | return cacheResult;
|
26 | }
|
27 | const reachableNodes = this.nodesParentsMap.get(fromSymbol);
|
28 | if (reachableNodes !== undefined) {
|
29 | for (const symbol of reachableNodes) {
|
30 | if (visitedSymbols.has(symbol)) {
|
31 | continue;
|
32 | }
|
33 | visitedSymbols.add(symbol);
|
34 | if (this.isSymbolUsedBySymbolImpl(symbol, toSymbol, visitedSymbols)) {
|
35 | return this.setUsageCacheValue(fromSymbol, toSymbol, true);
|
36 | }
|
37 | }
|
38 | }
|
39 | visitedSymbols.add(fromSymbol);
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | return false;
|
45 | }
|
46 | setUsageCacheValue(fromSymbol, toSymbol, value) {
|
47 | let fromSymbolCacheMap = this.usageResultCache.get(fromSymbol);
|
48 | if (fromSymbolCacheMap === undefined) {
|
49 | fromSymbolCacheMap = new Map();
|
50 | this.usageResultCache.set(fromSymbol, fromSymbolCacheMap);
|
51 | }
|
52 | fromSymbolCacheMap.set(toSymbol, value);
|
53 | return value;
|
54 | }
|
55 | computeUsages(files) {
|
56 | this.nodesParentsMap.clear();
|
57 | for (const file of files) {
|
58 | ts.forEachChild(file, this.computeUsageForNode.bind(this));
|
59 | }
|
60 | }
|
61 |
|
62 | computeUsageForNode(node) {
|
63 | if ((0, typescript_1.isDeclareModule)(node) && node.body !== undefined && ts.isModuleBlock(node.body)) {
|
64 | const moduleSymbol = this.getSymbol(node.name);
|
65 | for (const statement of node.body.statements) {
|
66 | this.computeUsageForNode(statement);
|
67 | if ((0, typescript_1.isNodeNamedDeclaration)(statement)) {
|
68 | const nodeName = (0, typescript_1.getNodeName)(statement);
|
69 | if (nodeName !== undefined) {
|
70 |
|
71 |
|
72 | const statementSymbol = this.getSymbol(nodeName);
|
73 | this.addUsages(statementSymbol, moduleSymbol);
|
74 | }
|
75 | }
|
76 | }
|
77 | }
|
78 | if ((0, typescript_1.isNodeNamedDeclaration)(node)) {
|
79 | const nodeName = (0, typescript_1.getNodeName)(node);
|
80 | if (nodeName !== undefined) {
|
81 | if (ts.isObjectBindingPattern(nodeName) || ts.isArrayBindingPattern(nodeName)) {
|
82 | for (const element of nodeName.elements) {
|
83 | this.computeUsageForNode(element);
|
84 | }
|
85 | }
|
86 | else {
|
87 | const childSymbol = this.getSymbol(nodeName);
|
88 | if (childSymbol !== null) {
|
89 | this.computeUsagesRecursively(node, childSymbol);
|
90 | }
|
91 | }
|
92 | }
|
93 | }
|
94 | if (ts.isVariableStatement(node)) {
|
95 | for (const varDeclaration of node.declarationList.declarations) {
|
96 | this.computeUsageForNode(varDeclaration);
|
97 | }
|
98 | }
|
99 |
|
100 | if (ts.isExportDeclaration(node) && node.moduleSpecifier !== undefined && node.exportClause !== undefined && ts.isNamespaceExport(node.exportClause)) {
|
101 | this.addUsagesForNamespacedModule(node.exportClause, node.moduleSpecifier);
|
102 | }
|
103 |
|
104 | if (ts.isImportDeclaration(node) && node.moduleSpecifier !== undefined && node.importClause?.namedBindings !== undefined && ts.isNamespaceImport(node.importClause.namedBindings)) {
|
105 |
|
106 |
|
107 |
|
108 | this.addUsagesForNamespacedModule(node.importClause.namedBindings, node.moduleSpecifier, false);
|
109 | }
|
110 |
|
111 | if (ts.isExportDeclaration(node) && node.exportClause !== undefined && ts.isNamedExports(node.exportClause)) {
|
112 | for (const exportElement of node.exportClause.elements) {
|
113 | const exportElementSymbol = (0, typescript_1.getImportExportReferencedSymbol)(exportElement, this.typeChecker);
|
114 |
|
115 | const namespaceImportForElement = (0, typescript_1.getDeclarationsForSymbol)(exportElementSymbol).find(ts.isNamespaceImport);
|
116 | if (namespaceImportForElement !== undefined) {
|
117 |
|
118 |
|
119 | this.addUsagesForNamespacedModule(namespaceImportForElement, namespaceImportForElement.parent.parent.moduleSpecifier);
|
120 | }
|
121 |
|
122 | const exportElementOwnSymbol = this.getNodeOwnSymbol(exportElement.name);
|
123 | this.addUsages(exportElementSymbol, exportElementOwnSymbol);
|
124 | this.addUsages(this.getActualSymbol(exportElementSymbol), exportElementOwnSymbol);
|
125 | }
|
126 | }
|
127 |
|
128 | if (ts.isExportAssignment(node) && node.isExportEquals) {
|
129 | this.addUsagesForExportAssignment(node);
|
130 | }
|
131 | }
|
132 | addUsagesForExportAssignment(exportAssignment) {
|
133 | for (const declaration of (0, typescript_1.getDeclarationsForExportedValues)(exportAssignment, this.typeChecker)) {
|
134 |
|
135 | if (ts.isModuleDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.body !== undefined && ts.isModuleBlock(declaration.body)) {
|
136 | const moduleSymbol = this.getSymbol(declaration.name);
|
137 | for (const statement of declaration.body.statements) {
|
138 | if ((0, typescript_1.isNodeNamedDeclaration)(statement) && statement.name !== undefined) {
|
139 | const statementSymbol = this.getSymbol(statement.name);
|
140 | if (statementSymbol !== null) {
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | this.addUsages(moduleSymbol, statementSymbol);
|
151 | }
|
152 | }
|
153 | }
|
154 | }
|
155 | }
|
156 | }
|
157 | addUsagesForNamespacedModule(namespaceNode, moduleSpecifier, includeExports = true) {
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | const namespaceSymbol = this.getNodeOwnSymbol(namespaceNode.name);
|
163 | const referencedSourceFileSymbol = this.getSymbol(moduleSpecifier);
|
164 | this.addUsages(referencedSourceFileSymbol, namespaceSymbol);
|
165 |
|
166 | const resolvedNamespaceSymbol = this.getSymbol(namespaceNode.name);
|
167 | this.addUsages(resolvedNamespaceSymbol, namespaceSymbol);
|
168 | if (includeExports) {
|
169 |
|
170 | this.addExportsToSymbol(referencedSourceFileSymbol.exports, referencedSourceFileSymbol);
|
171 | }
|
172 | }
|
173 | addExportsToSymbol(exports, parentSymbol, visitedSymbols = new Set()) {
|
174 | exports?.forEach((moduleExportedSymbol, name) => {
|
175 | if (name === ts.InternalSymbolName.ExportStar) {
|
176 |
|
177 | for (const exportStarDeclaration of (0, typescript_1.getSymbolExportStarDeclarations)(moduleExportedSymbol)) {
|
178 | if (exportStarDeclaration.moduleSpecifier === undefined) {
|
179 | throw new Error(`Export star declaration does not have a module specifier '${exportStarDeclaration.getText()}'`);
|
180 | }
|
181 | const referencedSourceFileSymbol = this.getSymbol(exportStarDeclaration.moduleSpecifier);
|
182 | if (visitedSymbols.has(referencedSourceFileSymbol)) {
|
183 | continue;
|
184 | }
|
185 | visitedSymbols.add(referencedSourceFileSymbol);
|
186 | this.addExportsToSymbol(referencedSourceFileSymbol.exports, parentSymbol, visitedSymbols);
|
187 | }
|
188 | return;
|
189 | }
|
190 | this.addUsages(moduleExportedSymbol, parentSymbol);
|
191 | });
|
192 | }
|
193 | computeUsagesRecursively(parent, parentSymbol) {
|
194 | ts.forEachChild(parent, (child) => {
|
195 | if (child.kind === ts.SyntaxKind.JSDoc) {
|
196 | return;
|
197 | }
|
198 | this.computeUsagesRecursively(child, parentSymbol);
|
199 | if (ts.isIdentifier(child) || child.kind === ts.SyntaxKind.DefaultKeyword) {
|
200 |
|
201 |
|
202 | if (ts.isNamedTupleMember(child.parent) && child.parent.name === child) {
|
203 | return;
|
204 | }
|
205 |
|
206 | if (ts.isBindingElement(child.parent) && child.parent.propertyName === child) {
|
207 | return;
|
208 | }
|
209 | this.addUsages(this.getSymbol(child), parentSymbol);
|
210 | if (!ts.isQualifiedName(child.parent)) {
|
211 | const childOwnSymbol = this.getNodeOwnSymbol(child);
|
212 |
|
213 | const namespaceImport = (0, typescript_1.getDeclarationsForSymbol)(childOwnSymbol).find(ts.isNamespaceImport);
|
214 | if (namespaceImport !== undefined) {
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | this.addUsagesForNamespacedModule(namespaceImport, namespaceImport.parent.parent.moduleSpecifier, true);
|
220 | }
|
221 | }
|
222 | }
|
223 | });
|
224 | }
|
225 | addUsages(childSymbol, parentSymbol) {
|
226 | const childSymbols = (0, typescript_1.splitTransientSymbol)(childSymbol, this.typeChecker);
|
227 | for (const childSplitSymbol of childSymbols) {
|
228 | let symbols = this.nodesParentsMap.get(childSplitSymbol);
|
229 | if (symbols === undefined) {
|
230 | symbols = new Set();
|
231 | this.nodesParentsMap.set(childSplitSymbol, symbols);
|
232 | }
|
233 |
|
234 | if (childSplitSymbol !== parentSymbol) {
|
235 | symbols.add(parentSymbol);
|
236 | }
|
237 | }
|
238 | }
|
239 | getSymbol(node) {
|
240 | return this.getActualSymbol(this.getNodeOwnSymbol(node));
|
241 | }
|
242 | getNodeOwnSymbol(node) {
|
243 | return (0, typescript_1.getNodeOwnSymbol)(node, this.typeChecker);
|
244 | }
|
245 | getActualSymbol(symbol) {
|
246 | return (0, typescript_1.getActualSymbol)(symbol, this.typeChecker);
|
247 | }
|
248 | }
|
249 | exports.TypesUsageEvaluator = TypesUsageEvaluator;
|