@openrewrite/rewrite 8.62.4 → 8.62.6
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/java/tree.d.ts +12 -6
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +12 -5
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type.d.ts +2 -0
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +12 -0
- package/dist/java/type.js.map +1 -1
- package/dist/java/visitor.d.ts +8 -0
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +23 -1
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/index.d.ts +3 -0
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +3 -0
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/method-matcher.d.ts +16 -0
- package/dist/javascript/method-matcher.d.ts.map +1 -0
- package/dist/javascript/method-matcher.js +222 -0
- package/dist/javascript/method-matcher.js.map +1 -0
- package/dist/javascript/parser.js +1 -1
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/preconditions.d.ts +6 -0
- package/dist/javascript/preconditions.d.ts.map +1 -0
- package/dist/javascript/preconditions.js +58 -0
- package/dist/javascript/preconditions.js.map +1 -0
- package/dist/javascript/remove-import.d.ts +56 -0
- package/dist/javascript/remove-import.d.ts.map +1 -0
- package/dist/javascript/remove-import.js +715 -0
- package/dist/javascript/remove-import.js.map +1 -0
- package/dist/javascript/rpc.js +2 -2
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/search/index.d.ts +3 -0
- package/dist/javascript/search/index.d.ts.map +1 -0
- package/dist/javascript/search/index.js +19 -0
- package/dist/javascript/search/index.js.map +1 -0
- package/dist/javascript/search/uses-method.d.ts +8 -0
- package/dist/javascript/search/uses-method.d.ts.map +1 -0
- package/dist/javascript/search/uses-method.js +35 -0
- package/dist/javascript/search/uses-method.js.map +1 -0
- package/dist/javascript/search/uses-type.d.ts +8 -0
- package/dist/javascript/search/uses-type.d.ts.map +1 -0
- package/dist/javascript/search/uses-type.js +71 -0
- package/dist/javascript/search/uses-type.js.map +1 -0
- package/dist/javascript/templating.d.ts +1 -1
- package/dist/javascript/templating.d.ts.map +1 -1
- package/dist/javascript/templating.js +1 -1
- package/dist/javascript/templating.js.map +1 -1
- package/dist/javascript/tree.d.ts +3 -3
- package/dist/javascript/tree.d.ts.map +1 -1
- package/dist/javascript/tree.js +28 -0
- package/dist/javascript/tree.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +4 -0
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +92 -46
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/visitor.js +1 -1
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/print.d.ts +1 -0
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +6 -0
- package/dist/print.js.map +1 -1
- package/dist/rpc/rewrite-rpc.d.ts +1 -1
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +0 -3
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/search/index.d.ts +2 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +18 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/is-source-file.d.ts +8 -0
- package/dist/search/is-source-file.d.ts.map +1 -0
- package/dist/search/is-source-file.js +70 -0
- package/dist/search/is-source-file.js.map +1 -0
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +3 -0
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +13 -0
- package/dist/util.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/visitor.d.ts +1 -1
- package/dist/visitor.d.ts.map +1 -1
- package/dist/visitor.js +3 -2
- package/dist/visitor.js.map +1 -1
- package/package.json +3 -1
- package/src/java/tree.ts +19 -11
- package/src/java/type.ts +14 -0
- package/src/java/visitor.ts +28 -8
- package/src/javascript/index.ts +4 -0
- package/src/javascript/method-matcher.ts +250 -0
- package/src/javascript/parser.ts +1 -1
- package/src/javascript/preconditions.ts +40 -0
- package/src/javascript/remove-import.ts +780 -0
- package/src/javascript/rpc.ts +2 -2
- package/src/javascript/search/index.ts +2 -0
- package/src/javascript/search/uses-method.ts +21 -0
- package/src/javascript/search/uses-type.ts +27 -0
- package/src/javascript/templating.ts +4 -3
- package/src/javascript/tree.ts +47 -3
- package/src/javascript/type-mapping.ts +113 -50
- package/src/javascript/visitor.ts +125 -125
- package/src/print.ts +9 -3
- package/src/rpc/rewrite-rpc.ts +1 -4
- package/src/search/index.ts +1 -0
- package/src/search/is-source-file.ts +26 -0
- package/src/test/rewrite-test.ts +4 -1
- package/src/util.ts +19 -4
- package/src/visitor.ts +3 -3
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import {JavaScriptVisitor} from "./visitor";
|
|
2
|
+
import {J} from "../java";
|
|
3
|
+
import {JS} from "./tree";
|
|
4
|
+
import {mapAsync} from "../util";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param visitor The visitor to add the import removal to
|
|
8
|
+
* @param target Either the module name (e.g., 'fs') to remove specific members from,
|
|
9
|
+
* or the name of the import to remove entirely
|
|
10
|
+
* @param member Optionally, the specific member to remove from the import.
|
|
11
|
+
* If not specified, removes the import matching `target`
|
|
12
|
+
*/
|
|
13
|
+
export function maybeRemoveImport(visitor: JavaScriptVisitor<any>, target: string, member?: string) {
|
|
14
|
+
for (const v of visitor.afterVisit || []) {
|
|
15
|
+
if (v instanceof RemoveImport && v.target === target && v.member === member) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
visitor.afterVisit.push(new RemoveImport(target, member));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Type alias for RightPadded elements to simplify type signatures
|
|
23
|
+
type RightPaddedElement<T extends J> = {
|
|
24
|
+
element?: T;
|
|
25
|
+
after?: J.Space;
|
|
26
|
+
markers?: any;
|
|
27
|
+
kind?: any; // Add kind to match the RightPadded type structure
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class RemoveImport<P> extends JavaScriptVisitor<P> {
|
|
31
|
+
/**
|
|
32
|
+
* @param target Either the module name (e.g., 'fs') to remove specific members from,
|
|
33
|
+
* or the name of the import to remove entirely
|
|
34
|
+
* @param member Optionally, the specific member to remove from the import.
|
|
35
|
+
* If not specified, removes the import matching `target`
|
|
36
|
+
*/
|
|
37
|
+
constructor(readonly target: string,
|
|
38
|
+
readonly member?: string) {
|
|
39
|
+
super();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generic helper to filter elements from a RightPadded array while preserving formatting.
|
|
44
|
+
* When removing elements, the prefix from the first removed element is applied to the
|
|
45
|
+
* first remaining element to maintain proper spacing.
|
|
46
|
+
*/
|
|
47
|
+
private async filterElementsWithPrefixPreservation<T extends J>(
|
|
48
|
+
elements: RightPaddedElement<T>[],
|
|
49
|
+
shouldKeep: (elem: T) => boolean,
|
|
50
|
+
updatePrefix: (elem: T, prefix: J.Space) => Promise<T>,
|
|
51
|
+
_p: P
|
|
52
|
+
): Promise<{ filtered: RightPaddedElement<T>[], allRemoved: boolean }> {
|
|
53
|
+
const filtered: RightPaddedElement<T>[] = [];
|
|
54
|
+
let removedPrefix: J.Space | undefined;
|
|
55
|
+
|
|
56
|
+
for (const elem of elements) {
|
|
57
|
+
if (elem.element && shouldKeep(elem.element)) {
|
|
58
|
+
// If we removed the previous element and this is the first kept element,
|
|
59
|
+
// apply the removed element's prefix to maintain formatting
|
|
60
|
+
if (removedPrefix && filtered.length === 0) {
|
|
61
|
+
const updatedElement = await updatePrefix(elem.element, removedPrefix);
|
|
62
|
+
filtered.push({...elem, element: updatedElement});
|
|
63
|
+
removedPrefix = undefined;
|
|
64
|
+
} else {
|
|
65
|
+
filtered.push(elem);
|
|
66
|
+
}
|
|
67
|
+
} else if (elem.element) {
|
|
68
|
+
// Store the prefix of the first removed element
|
|
69
|
+
if (filtered.length === 0 && !removedPrefix) {
|
|
70
|
+
removedPrefix = elem.element.prefix;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// Keep non-element entries (shouldn't happen but be safe)
|
|
74
|
+
filtered.push(elem);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
filtered,
|
|
80
|
+
allRemoved: filtered.length === 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Helper to update an import clause by removing specific bindings
|
|
86
|
+
*/
|
|
87
|
+
private async updateImportClause(
|
|
88
|
+
jsImport: JS.Import,
|
|
89
|
+
importClause: JS.ImportClause,
|
|
90
|
+
updateFn: (draft: any) => void,
|
|
91
|
+
p: P
|
|
92
|
+
): Promise<JS.Import> {
|
|
93
|
+
return this.produceJavaScript<JS.Import>(jsImport, p, async draft => {
|
|
94
|
+
if (draft.importClause) {
|
|
95
|
+
draft.importClause = await this.produceJavaScript<JS.ImportClause>(
|
|
96
|
+
importClause, p, async (clauseDraft: any) => updateFn(clauseDraft)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
override async visitJsCompilationUnit(compilationUnit: JS.CompilationUnit, p: P): Promise<J | undefined> {
|
|
103
|
+
// First, collect all used identifiers in the file
|
|
104
|
+
const usedIdentifiers = new Set<string>();
|
|
105
|
+
const usedTypes = new Set<string>();
|
|
106
|
+
|
|
107
|
+
// Traverse the AST to collect used identifiers
|
|
108
|
+
await this.collectUsedIdentifiers(compilationUnit, usedIdentifiers, usedTypes);
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
// Now process imports with knowledge of what's used
|
|
112
|
+
return this.produceJavaScript<JS.CompilationUnit>(compilationUnit, p, async draft => {
|
|
113
|
+
let nextStatementPrefix: J.Space | undefined;
|
|
114
|
+
|
|
115
|
+
draft.statements = await mapAsync(compilationUnit.statements, async (stmt, index) => {
|
|
116
|
+
const statement = stmt.element;
|
|
117
|
+
|
|
118
|
+
// Handle ES6 imports
|
|
119
|
+
if (statement?.kind === JS.Kind.Import) {
|
|
120
|
+
const jsImport = statement as JS.Import;
|
|
121
|
+
const result = await this.processImport(jsImport, usedIdentifiers, usedTypes, p);
|
|
122
|
+
if (result === undefined) {
|
|
123
|
+
// Store the prefix for the next statement to avoid leaving blank lines
|
|
124
|
+
if (index < compilationUnit.statements.length - 1) {
|
|
125
|
+
const nextStmt = compilationUnit.statements[index + 1];
|
|
126
|
+
if (nextStmt?.element) {
|
|
127
|
+
nextStatementPrefix = jsImport.prefix;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Remove the entire import
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return {...stmt, element: result};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Handle CommonJS require statements
|
|
137
|
+
// Note: const fs = require() comes as J.VariableDeclarations, not ScopedVariableDeclarations
|
|
138
|
+
if (statement?.kind === J.Kind.VariableDeclarations) {
|
|
139
|
+
const varDecl = statement as J.VariableDeclarations;
|
|
140
|
+
const result = await this.processRequireFromVarDecls(varDecl, usedIdentifiers, p);
|
|
141
|
+
if (result === undefined) {
|
|
142
|
+
// Store the prefix for the next statement to avoid leaving blank lines
|
|
143
|
+
if (index < compilationUnit.statements.length - 1) {
|
|
144
|
+
const nextStmt = compilationUnit.statements[index + 1];
|
|
145
|
+
if (nextStmt?.element) {
|
|
146
|
+
nextStatementPrefix = varDecl.prefix;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Remove the entire require statement
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
return {...stmt, element: result};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Apply stored prefix if this statement follows a removed import
|
|
156
|
+
if (nextStatementPrefix && statement) {
|
|
157
|
+
const updatedStatement = await this.visit(statement, p) as J;
|
|
158
|
+
if (updatedStatement) {
|
|
159
|
+
(updatedStatement as any).prefix = nextStatementPrefix;
|
|
160
|
+
nextStatementPrefix = undefined;
|
|
161
|
+
return {...stmt, element: updatedStatement};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return stmt;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Filter out undefined (removed) statements
|
|
169
|
+
draft.statements = draft.statements.filter(s => s !== undefined);
|
|
170
|
+
draft.eof = await this.visitSpace(compilationUnit.eof, p);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private async processImport(
|
|
175
|
+
jsImport: JS.Import,
|
|
176
|
+
usedIdentifiers: Set<string>,
|
|
177
|
+
usedTypes: Set<string>,
|
|
178
|
+
p: P
|
|
179
|
+
): Promise<JS.Import | undefined> {
|
|
180
|
+
// Check if this import is from the target module
|
|
181
|
+
if (!this.isTargetModule(jsImport)) {
|
|
182
|
+
return jsImport;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const importClause = jsImport.importClause;
|
|
186
|
+
if (!importClause) {
|
|
187
|
+
// Side-effect import like: import 'module'
|
|
188
|
+
if (this.member === '*') {
|
|
189
|
+
return undefined; // Remove the entire import
|
|
190
|
+
}
|
|
191
|
+
return jsImport;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Process default import
|
|
195
|
+
if (importClause.name) {
|
|
196
|
+
const defaultName = importClause.name.element;
|
|
197
|
+
if (defaultName && defaultName.kind === J.Kind.Identifier) {
|
|
198
|
+
const identifier = defaultName as J.Identifier;
|
|
199
|
+
const name = identifier.simpleName;
|
|
200
|
+
|
|
201
|
+
if (this.shouldRemoveImport(name, usedIdentifiers, usedTypes)) {
|
|
202
|
+
// If there are no named imports, remove the entire import
|
|
203
|
+
if (!importClause.namedBindings) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
// Otherwise, just remove the default import
|
|
207
|
+
return this.updateImportClause(jsImport, importClause, draft => {
|
|
208
|
+
draft.name = undefined;
|
|
209
|
+
}, p);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Process named imports
|
|
215
|
+
if (importClause.namedBindings) {
|
|
216
|
+
const namedBindings = importClause.namedBindings;
|
|
217
|
+
|
|
218
|
+
// Handle namespace import: import * as X from 'module'
|
|
219
|
+
if (namedBindings.kind === J.Kind.Identifier) {
|
|
220
|
+
const identifier = namedBindings as J.Identifier;
|
|
221
|
+
const name = identifier.simpleName;
|
|
222
|
+
|
|
223
|
+
if (this.shouldRemoveImport(name, usedIdentifiers, usedTypes)) {
|
|
224
|
+
// If there's no default import, remove the entire import
|
|
225
|
+
if (!importClause.name) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
// Otherwise, just remove the namespace import
|
|
229
|
+
return this.updateImportClause(jsImport, importClause, draft => {
|
|
230
|
+
draft.namedBindings = undefined;
|
|
231
|
+
}, p);
|
|
232
|
+
}
|
|
233
|
+
} else if (namedBindings.kind === JS.Kind.Alias) {
|
|
234
|
+
// Handle import * as X from 'module' - represented as Alias with propertyName = "*"
|
|
235
|
+
const alias = namedBindings as JS.Alias;
|
|
236
|
+
const aliasName = (alias.alias as J.Identifier).simpleName;
|
|
237
|
+
|
|
238
|
+
if (this.shouldRemoveImport(aliasName, usedIdentifiers, usedTypes)) {
|
|
239
|
+
// If there's no default import, remove the entire import
|
|
240
|
+
if (!importClause.name) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
// Otherwise, just remove the namespace import
|
|
244
|
+
return this.updateImportClause(jsImport, importClause, draft => {
|
|
245
|
+
draft.namedBindings = undefined;
|
|
246
|
+
}, p);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Handle named imports: import { a, b } from 'module'
|
|
251
|
+
if (namedBindings.kind === JS.Kind.NamedImports) {
|
|
252
|
+
const namedImports = namedBindings as JS.NamedImports;
|
|
253
|
+
const updatedImports = await this.processNamedImports(namedImports, usedIdentifiers, usedTypes, p);
|
|
254
|
+
|
|
255
|
+
if (updatedImports === undefined) {
|
|
256
|
+
// All named imports were removed
|
|
257
|
+
if (!importClause.name) {
|
|
258
|
+
// No default import either, remove the entire import
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
// Keep the import with just the default import
|
|
262
|
+
return this.updateImportClause(jsImport, importClause, draft => {
|
|
263
|
+
draft.namedBindings = undefined;
|
|
264
|
+
}, p);
|
|
265
|
+
} else if (updatedImports !== namedImports) {
|
|
266
|
+
// Some named imports were removed
|
|
267
|
+
return this.updateImportClause(jsImport, importClause, draft => {
|
|
268
|
+
draft.namedBindings = updatedImports;
|
|
269
|
+
}, p);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return jsImport;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private async processNamedImports(
|
|
278
|
+
namedImports: JS.NamedImports,
|
|
279
|
+
usedIdentifiers: Set<string>,
|
|
280
|
+
usedTypes: Set<string>,
|
|
281
|
+
p: P
|
|
282
|
+
): Promise<JS.NamedImports | undefined> {
|
|
283
|
+
const {filtered, allRemoved} = await this.filterElementsWithPrefixPreservation(
|
|
284
|
+
namedImports.elements.elements,
|
|
285
|
+
(elem: J) => {
|
|
286
|
+
if (elem.kind === JS.Kind.ImportSpecifier) {
|
|
287
|
+
const importName = this.getImportName(elem as JS.ImportSpecifier);
|
|
288
|
+
return !this.shouldRemoveImport(importName, usedIdentifiers, usedTypes);
|
|
289
|
+
}
|
|
290
|
+
return true; // Keep non-ImportSpecifier elements
|
|
291
|
+
},
|
|
292
|
+
async (elem: J, prefix: J.Space) => {
|
|
293
|
+
if (elem.kind === JS.Kind.ImportSpecifier) {
|
|
294
|
+
return this.produceJavaScript<JS.ImportSpecifier>(
|
|
295
|
+
elem as JS.ImportSpecifier, p, async draft => {
|
|
296
|
+
draft.prefix = prefix;
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return elem;
|
|
301
|
+
},
|
|
302
|
+
p
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
if (allRemoved) {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (filtered.length === namedImports.elements.elements.length) {
|
|
310
|
+
return namedImports; // No changes
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Create updated named imports with filtered elements
|
|
314
|
+
return this.produceJavaScript<JS.NamedImports>(namedImports, p, async draft => {
|
|
315
|
+
draft.elements = {
|
|
316
|
+
...namedImports.elements,
|
|
317
|
+
elements: filtered as any
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async processRequireFromVarDecls(
|
|
323
|
+
varDecls: J.VariableDeclarations,
|
|
324
|
+
usedIdentifiers: Set<string>,
|
|
325
|
+
p: P
|
|
326
|
+
): Promise<J.VariableDeclarations | undefined> {
|
|
327
|
+
// Check if this is a require() call
|
|
328
|
+
if (varDecls.variables.length !== 1) {
|
|
329
|
+
return varDecls;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const namedVar = varDecls.variables[0].element;
|
|
333
|
+
if (!namedVar) {
|
|
334
|
+
return varDecls;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const initializer = namedVar.initializer?.element;
|
|
338
|
+
if (!initializer || initializer.kind !== J.Kind.MethodInvocation) {
|
|
339
|
+
return varDecls;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const methodInv = initializer as J.MethodInvocation;
|
|
343
|
+
if (methodInv.name?.kind !== J.Kind.Identifier || (methodInv.name as J.Identifier).simpleName !== 'require') {
|
|
344
|
+
return varDecls;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// This is a require() statement
|
|
348
|
+
const pattern = namedVar.name;
|
|
349
|
+
if (!pattern) {
|
|
350
|
+
return varDecls;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Handle: const fs = require('fs')
|
|
354
|
+
if (pattern.kind === J.Kind.Identifier) {
|
|
355
|
+
const varName = (pattern as J.Identifier).simpleName;
|
|
356
|
+
if (this.shouldRemoveImport(varName, usedIdentifiers, new Set())) {
|
|
357
|
+
return undefined; // Remove the entire require statement
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Handle: const { readFile } = require('fs')
|
|
362
|
+
if (pattern.kind === JS.Kind.ObjectBindingPattern && this.member !== undefined) {
|
|
363
|
+
const objectPattern = pattern as JS.ObjectBindingPattern;
|
|
364
|
+
const updatedPattern = await this.processObjectBindingPattern(objectPattern, usedIdentifiers, p);
|
|
365
|
+
|
|
366
|
+
if (updatedPattern === undefined) {
|
|
367
|
+
return undefined; // Remove entire require
|
|
368
|
+
} else if (updatedPattern !== objectPattern) {
|
|
369
|
+
// Update with filtered bindings
|
|
370
|
+
return this.produceJava<J.VariableDeclarations>(varDecls, p, async draft => {
|
|
371
|
+
const updatedNamedVar = await this.produceJava<J.VariableDeclarations.NamedVariable>(
|
|
372
|
+
namedVar, p, async namedDraft => {
|
|
373
|
+
namedDraft.name = updatedPattern;
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
draft.variables = [{...varDecls.variables[0], element: updatedNamedVar}];
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return varDecls;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private async processObjectBindingPattern(
|
|
385
|
+
pattern: JS.ObjectBindingPattern,
|
|
386
|
+
usedIdentifiers: Set<string>,
|
|
387
|
+
p: P
|
|
388
|
+
): Promise<JS.ObjectBindingPattern | undefined> {
|
|
389
|
+
const {filtered, allRemoved} = await this.filterElementsWithPrefixPreservation(
|
|
390
|
+
pattern.bindings.elements,
|
|
391
|
+
(elem: J) => {
|
|
392
|
+
if (elem.kind === JS.Kind.BindingElement) {
|
|
393
|
+
const name = this.getBindingElementName(elem as JS.BindingElement);
|
|
394
|
+
return !this.shouldRemoveImport(name, usedIdentifiers, new Set());
|
|
395
|
+
} else if (elem.kind === J.Kind.Identifier) {
|
|
396
|
+
const name = (elem as J.Identifier).simpleName;
|
|
397
|
+
return !this.shouldRemoveImport(name, usedIdentifiers, new Set());
|
|
398
|
+
}
|
|
399
|
+
return true; // Keep other element types
|
|
400
|
+
},
|
|
401
|
+
async (elem: J, prefix: J.Space) => {
|
|
402
|
+
if (elem.kind === J.Kind.Identifier) {
|
|
403
|
+
return this.produceJava<J.Identifier>(
|
|
404
|
+
elem as J.Identifier, p, async draft => {
|
|
405
|
+
draft.prefix = prefix;
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
} else if (elem.kind === JS.Kind.BindingElement) {
|
|
409
|
+
return this.produceJavaScript<JS.BindingElement>(
|
|
410
|
+
elem as JS.BindingElement, p, async draft => {
|
|
411
|
+
draft.prefix = prefix;
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
return elem;
|
|
416
|
+
},
|
|
417
|
+
p
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
if (allRemoved) {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (filtered.length === pattern.bindings.elements.length) {
|
|
425
|
+
return pattern;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return this.produceJavaScript<JS.ObjectBindingPattern>(pattern, p, async draft => {
|
|
429
|
+
draft.bindings = {
|
|
430
|
+
...pattern.bindings,
|
|
431
|
+
elements: filtered as any
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private getImportName(specifier: JS.ImportSpecifier): string {
|
|
437
|
+
const spec = specifier.specifier;
|
|
438
|
+
if (spec?.kind === JS.Kind.Alias) {
|
|
439
|
+
// Handle aliased import: import { foo as bar }
|
|
440
|
+
const alias = spec as JS.Alias;
|
|
441
|
+
const propertyName = alias.propertyName.element;
|
|
442
|
+
if (propertyName?.kind === J.Kind.Identifier) {
|
|
443
|
+
return (propertyName as J.Identifier).simpleName;
|
|
444
|
+
}
|
|
445
|
+
} else if (spec?.kind === J.Kind.Identifier) {
|
|
446
|
+
// Handle regular import: import { foo }
|
|
447
|
+
return (spec as J.Identifier).simpleName;
|
|
448
|
+
}
|
|
449
|
+
return '';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private getBindingElementName(bindingElement: JS.BindingElement): string {
|
|
453
|
+
const name = bindingElement.name;
|
|
454
|
+
if (name?.kind === J.Kind.Identifier) {
|
|
455
|
+
return (name as J.Identifier).simpleName;
|
|
456
|
+
}
|
|
457
|
+
return '';
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private shouldRemoveImport(
|
|
461
|
+
name: string,
|
|
462
|
+
usedIdentifiers: Set<string>,
|
|
463
|
+
usedTypes: Set<string>
|
|
464
|
+
): boolean {
|
|
465
|
+
// If member is specified, we're removing a specific member from a module
|
|
466
|
+
if (this.member !== undefined) {
|
|
467
|
+
// Only remove if this is the specific member we're looking for
|
|
468
|
+
if (this.member !== name) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
// If no member specified, we're removing based on the import name itself
|
|
473
|
+
if (this.target !== name) {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Check if it's used
|
|
479
|
+
return !(usedIdentifiers.has(name) || usedTypes.has(name));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private isTargetModule(jsImport: JS.Import): boolean {
|
|
483
|
+
// If member is specified, we're looking for imports from a specific module
|
|
484
|
+
if (this.member !== undefined) {
|
|
485
|
+
const moduleSpecifier = jsImport.moduleSpecifier?.element;
|
|
486
|
+
if (!moduleSpecifier || moduleSpecifier.kind !== J.Kind.Literal) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const literal = moduleSpecifier as J.Literal;
|
|
491
|
+
const moduleName = literal.value?.toString().replace(/['"`]/g, '');
|
|
492
|
+
|
|
493
|
+
// Match the module name
|
|
494
|
+
return moduleName === this.target;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// If no member specified, we process all imports to check their names
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Helper to traverse parameters from various node types
|
|
503
|
+
*/
|
|
504
|
+
private async traverseParameters(
|
|
505
|
+
params: any,
|
|
506
|
+
usedIdentifiers: Set<string>,
|
|
507
|
+
usedTypes: Set<string>
|
|
508
|
+
): Promise<void> {
|
|
509
|
+
if (!params || typeof params !== 'object') return;
|
|
510
|
+
|
|
511
|
+
if (Array.isArray(params)) {
|
|
512
|
+
for (const p of params) {
|
|
513
|
+
await this.collectUsedIdentifiers(p, usedIdentifiers, usedTypes);
|
|
514
|
+
}
|
|
515
|
+
} else if (params.elements) {
|
|
516
|
+
for (const p of params.elements) {
|
|
517
|
+
if (p.element) {
|
|
518
|
+
await this.collectUsedIdentifiers(p.element, usedIdentifiers, usedTypes);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} else if (params.parameters) {
|
|
522
|
+
for (const p of params.parameters) {
|
|
523
|
+
const elem = p.element || p;
|
|
524
|
+
await this.collectUsedIdentifiers(elem, usedIdentifiers, usedTypes);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Helper to traverse statements from various node types
|
|
531
|
+
*/
|
|
532
|
+
private async traverseStatements(
|
|
533
|
+
statements: any,
|
|
534
|
+
usedIdentifiers: Set<string>,
|
|
535
|
+
usedTypes: Set<string>
|
|
536
|
+
): Promise<void> {
|
|
537
|
+
if (!statements) return;
|
|
538
|
+
|
|
539
|
+
if (Array.isArray(statements)) {
|
|
540
|
+
for (const stmt of statements) {
|
|
541
|
+
const element = stmt.element || stmt;
|
|
542
|
+
if (element) {
|
|
543
|
+
await this.collectUsedIdentifiers(element, usedIdentifiers, usedTypes);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Helper to check for type expressions and collect type usage
|
|
551
|
+
*/
|
|
552
|
+
private async checkTypeExpression(
|
|
553
|
+
node: any,
|
|
554
|
+
usedTypes: Set<string>
|
|
555
|
+
): Promise<void> {
|
|
556
|
+
if (node.typeExpression) {
|
|
557
|
+
await this.collectTypeUsage(node.typeExpression, usedTypes);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private async collectUsedIdentifiers(
|
|
562
|
+
node: J,
|
|
563
|
+
usedIdentifiers: Set<string>,
|
|
564
|
+
usedTypes: Set<string>
|
|
565
|
+
): Promise<void> {
|
|
566
|
+
// This is a simplified version - in a real implementation,
|
|
567
|
+
// we'd need to traverse the entire AST and collect all identifier usages
|
|
568
|
+
// For now, we'll implement a basic traversal
|
|
569
|
+
|
|
570
|
+
if (node.kind === J.Kind.Identifier) {
|
|
571
|
+
const identifier = node as J.Identifier;
|
|
572
|
+
usedIdentifiers.add(identifier.simpleName);
|
|
573
|
+
} else if (node.kind === J.Kind.MethodInvocation) {
|
|
574
|
+
const methodInv = node as J.MethodInvocation;
|
|
575
|
+
|
|
576
|
+
// Check if this is a member access pattern like fs.readFileSync
|
|
577
|
+
if (methodInv.select?.element?.kind === J.Kind.FieldAccess) {
|
|
578
|
+
const fieldAccess = methodInv.select.element as J.FieldAccess;
|
|
579
|
+
if (fieldAccess.target?.kind === J.Kind.Identifier) {
|
|
580
|
+
usedIdentifiers.add((fieldAccess.target as J.Identifier).simpleName);
|
|
581
|
+
}
|
|
582
|
+
} else if (methodInv.select?.element?.kind === J.Kind.Identifier) {
|
|
583
|
+
// Direct identifier like fs in fs.method() - though this is rare
|
|
584
|
+
usedIdentifiers.add((methodInv.select.element as J.Identifier).simpleName);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Collect method name
|
|
588
|
+
if (methodInv.name && methodInv.name.kind === J.Kind.Identifier) {
|
|
589
|
+
usedIdentifiers.add((methodInv.name as J.Identifier).simpleName);
|
|
590
|
+
}
|
|
591
|
+
// Recursively check arguments
|
|
592
|
+
if (methodInv.arguments) {
|
|
593
|
+
for (const arg of methodInv.arguments.elements) {
|
|
594
|
+
if (arg.element) {
|
|
595
|
+
await this.collectUsedIdentifiers(arg.element, usedIdentifiers, usedTypes);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// Check select (object being called on)
|
|
600
|
+
if (methodInv.select?.element) {
|
|
601
|
+
await this.collectUsedIdentifiers(methodInv.select.element, usedIdentifiers, usedTypes);
|
|
602
|
+
}
|
|
603
|
+
} else if (node.kind === J.Kind.MemberReference) {
|
|
604
|
+
const memberRef = node as J.MemberReference;
|
|
605
|
+
if (memberRef.containing && memberRef.containing.element?.kind === J.Kind.Identifier) {
|
|
606
|
+
usedIdentifiers.add((memberRef.containing.element as J.Identifier).simpleName);
|
|
607
|
+
}
|
|
608
|
+
} else if (node.kind === J.Kind.FieldAccess) {
|
|
609
|
+
// Handle field access like fs.readFileSync
|
|
610
|
+
const fieldAccess = node as J.FieldAccess;
|
|
611
|
+
if (fieldAccess.target?.kind === J.Kind.Identifier) {
|
|
612
|
+
usedIdentifiers.add((fieldAccess.target as J.Identifier).simpleName);
|
|
613
|
+
}
|
|
614
|
+
// Recursively check the target
|
|
615
|
+
if (fieldAccess.target) {
|
|
616
|
+
await this.collectUsedIdentifiers(fieldAccess.target, usedIdentifiers, usedTypes);
|
|
617
|
+
}
|
|
618
|
+
} else if (node.kind === JS.Kind.CompilationUnit) {
|
|
619
|
+
const cu = node as JS.CompilationUnit;
|
|
620
|
+
for (const stmt of cu.statements) {
|
|
621
|
+
if (stmt.element && stmt.element.kind !== JS.Kind.Import) {
|
|
622
|
+
// Skip require() statements at the top level
|
|
623
|
+
if (stmt.element.kind === J.Kind.VariableDeclarations) {
|
|
624
|
+
const varDecls = stmt.element as J.VariableDeclarations;
|
|
625
|
+
// Check if this is a require() statement
|
|
626
|
+
let isRequire = false;
|
|
627
|
+
for (const v of varDecls.variables) {
|
|
628
|
+
const namedVar = v.element;
|
|
629
|
+
if (namedVar?.initializer?.element?.kind === J.Kind.MethodInvocation) {
|
|
630
|
+
const methodInv = namedVar.initializer.element as J.MethodInvocation;
|
|
631
|
+
if (methodInv.name?.kind === J.Kind.Identifier &&
|
|
632
|
+
(methodInv.name as J.Identifier).simpleName === 'require') {
|
|
633
|
+
isRequire = true;
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (!isRequire) {
|
|
639
|
+
// Not a require statement, process normally
|
|
640
|
+
await this.collectUsedIdentifiers(stmt.element, usedIdentifiers, usedTypes);
|
|
641
|
+
}
|
|
642
|
+
} else if (stmt.element.kind !== JS.Kind.ScopedVariableDeclarations) {
|
|
643
|
+
// Process other non-import, non-require statements normally
|
|
644
|
+
await this.collectUsedIdentifiers(stmt.element, usedIdentifiers, usedTypes);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
} else if (node.kind === J.Kind.Return) {
|
|
649
|
+
// Handle return statements
|
|
650
|
+
const returnStmt = node as J.Return;
|
|
651
|
+
if (returnStmt.expression) {
|
|
652
|
+
await this.collectUsedIdentifiers(returnStmt.expression, usedIdentifiers, usedTypes);
|
|
653
|
+
}
|
|
654
|
+
} else if (node.kind === J.Kind.Block) {
|
|
655
|
+
const block = node as J.Block;
|
|
656
|
+
for (const stmt of block.statements) {
|
|
657
|
+
if (stmt.element) {
|
|
658
|
+
await this.collectUsedIdentifiers(stmt.element, usedIdentifiers, usedTypes);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
} else if (node.kind === J.Kind.MethodDeclaration) {
|
|
662
|
+
const method = node as J.MethodDeclaration;
|
|
663
|
+
// Check parameters for type usage
|
|
664
|
+
if (method.parameters) {
|
|
665
|
+
for (const param of method.parameters.elements) {
|
|
666
|
+
// Parameters can be various types, handle them recursively
|
|
667
|
+
if (param.element) {
|
|
668
|
+
await this.collectUsedIdentifiers(param.element, usedIdentifiers, usedTypes);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// Check body
|
|
673
|
+
if (method.body) {
|
|
674
|
+
await this.collectUsedIdentifiers(method.body, usedIdentifiers, usedTypes);
|
|
675
|
+
}
|
|
676
|
+
} else if ((node as any).typeExpression) {
|
|
677
|
+
// Handle nodes with type expressions (parameters, variables, etc.)
|
|
678
|
+
await this.checkTypeExpression(node, usedTypes);
|
|
679
|
+
// Continue traversing other parts
|
|
680
|
+
if ((node as any).name) {
|
|
681
|
+
await this.collectUsedIdentifiers((node as any).name, usedIdentifiers, usedTypes);
|
|
682
|
+
}
|
|
683
|
+
if ((node as any).initializer) {
|
|
684
|
+
await this.collectUsedIdentifiers((node as any).initializer, usedIdentifiers, usedTypes);
|
|
685
|
+
}
|
|
686
|
+
} else if (node.kind === JS.Kind.ArrowFunction || node.kind === J.Kind.Lambda) {
|
|
687
|
+
// Handle arrow functions and lambdas
|
|
688
|
+
const func = node as any;
|
|
689
|
+
if (func.parameters) {
|
|
690
|
+
await this.collectUsedIdentifiers(func.parameters, usedIdentifiers, usedTypes);
|
|
691
|
+
}
|
|
692
|
+
if (func.lambda) {
|
|
693
|
+
await this.collectUsedIdentifiers(func.lambda, usedIdentifiers, usedTypes);
|
|
694
|
+
}
|
|
695
|
+
if (func.body) {
|
|
696
|
+
await this.collectUsedIdentifiers(func.body, usedIdentifiers, usedTypes);
|
|
697
|
+
}
|
|
698
|
+
} else if (node.kind === J.Kind.Lambda) {
|
|
699
|
+
const lambda = node as J.Lambda;
|
|
700
|
+
if (lambda.parameters?.parameters) {
|
|
701
|
+
for (const param of lambda.parameters.parameters) {
|
|
702
|
+
if (param.element) {
|
|
703
|
+
await this.collectUsedIdentifiers(param.element, usedIdentifiers, usedTypes);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (lambda.body) {
|
|
708
|
+
await this.collectUsedIdentifiers(lambda.body, usedIdentifiers, usedTypes);
|
|
709
|
+
}
|
|
710
|
+
} else if (node.kind === J.Kind.VariableDeclarations) {
|
|
711
|
+
const varDecls = node as J.VariableDeclarations;
|
|
712
|
+
// Check the type expression on the VariableDeclarations itself
|
|
713
|
+
await this.checkTypeExpression(varDecls, usedTypes);
|
|
714
|
+
for (const v of varDecls.variables) {
|
|
715
|
+
const namedVar = v.element;
|
|
716
|
+
if (namedVar) {
|
|
717
|
+
// Check type annotation on the variable
|
|
718
|
+
await this.checkTypeExpression(namedVar, usedTypes);
|
|
719
|
+
// Check the variable name
|
|
720
|
+
if (namedVar.name) {
|
|
721
|
+
await this.collectUsedIdentifiers(namedVar.name, usedIdentifiers, usedTypes);
|
|
722
|
+
}
|
|
723
|
+
// Check the initializer
|
|
724
|
+
if (namedVar.initializer && namedVar.initializer.element) {
|
|
725
|
+
await this.collectUsedIdentifiers(namedVar.initializer.element, usedIdentifiers, usedTypes);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} else if ((node as any).statements) {
|
|
730
|
+
// Generic handler for nodes with statements
|
|
731
|
+
await this.traverseStatements((node as any).statements, usedIdentifiers, usedTypes);
|
|
732
|
+
} else if ((node as any).body) {
|
|
733
|
+
// Generic handler for nodes with body
|
|
734
|
+
await this.collectUsedIdentifiers((node as any).body, usedIdentifiers, usedTypes);
|
|
735
|
+
} else if ((node as any).parameters) {
|
|
736
|
+
// Handle anything with parameters (functions, methods, etc.)
|
|
737
|
+
await this.traverseParameters((node as any).parameters, usedIdentifiers, usedTypes);
|
|
738
|
+
// Continue with the body if it exists
|
|
739
|
+
if ((node as any).body) {
|
|
740
|
+
await this.collectUsedIdentifiers((node as any).body, usedIdentifiers, usedTypes);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
private async collectTypeUsage(typeExpr: J, usedTypes: Set<string>): Promise<void> {
|
|
746
|
+
if (typeExpr.kind === J.Kind.Identifier) {
|
|
747
|
+
usedTypes.add((typeExpr as J.Identifier).simpleName);
|
|
748
|
+
} else if (typeExpr.kind === J.Kind.ParameterizedType) {
|
|
749
|
+
const paramType = typeExpr as J.ParameterizedType;
|
|
750
|
+
// In TypeScript AST, ParameterizedType might have a different structure
|
|
751
|
+
// We'll need to handle the type parameters appropriately
|
|
752
|
+
if (paramType.typeParameters) {
|
|
753
|
+
for (const typeParam of paramType.typeParameters.elements) {
|
|
754
|
+
if (typeParam.element) {
|
|
755
|
+
await this.collectTypeUsage(typeParam.element, usedTypes);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
} else if (typeExpr.kind === JS.Kind.TypeTreeExpression) {
|
|
760
|
+
// Handle TypeTreeExpression which wraps type identifiers
|
|
761
|
+
const typeTree = typeExpr as JS.TypeTreeExpression;
|
|
762
|
+
if (typeTree.expression) {
|
|
763
|
+
await this.collectTypeUsage(typeTree.expression, usedTypes);
|
|
764
|
+
}
|
|
765
|
+
} else if (typeExpr.kind === JS.Kind.TypeInfo) {
|
|
766
|
+
// Handle TypeInfo which contains type identifiers
|
|
767
|
+
const typeInfo = typeExpr as JS.TypeInfo;
|
|
768
|
+
if (typeInfo.typeIdentifier) {
|
|
769
|
+
await this.collectTypeUsage(typeInfo.typeIdentifier, usedTypes);
|
|
770
|
+
}
|
|
771
|
+
} else if ((typeExpr as any).expression) {
|
|
772
|
+
// Generic handler for nodes with expression property
|
|
773
|
+
await this.collectTypeUsage((typeExpr as any).expression, usedTypes);
|
|
774
|
+
} else if ((typeExpr as any).typeIdentifier) {
|
|
775
|
+
// Generic handler for nodes with typeIdentifier property
|
|
776
|
+
await this.collectTypeUsage((typeExpr as any).typeIdentifier, usedTypes);
|
|
777
|
+
}
|
|
778
|
+
// Add more type expression handlers as needed
|
|
779
|
+
}
|
|
780
|
+
}
|