@openrewrite/rewrite 8.62.3 → 8.62.5

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.
Files changed (170) hide show
  1. package/dist/execution.d.ts.map +1 -1
  2. package/dist/execution.js.map +1 -1
  3. package/dist/java/rpc.d.ts +1 -0
  4. package/dist/java/rpc.d.ts.map +1 -1
  5. package/dist/java/rpc.js +81 -0
  6. package/dist/java/rpc.js.map +1 -1
  7. package/dist/java/tree.d.ts +12 -6
  8. package/dist/java/tree.d.ts.map +1 -1
  9. package/dist/java/tree.js +12 -92
  10. package/dist/java/tree.js.map +1 -1
  11. package/dist/java/type.d.ts +2 -0
  12. package/dist/java/type.d.ts.map +1 -1
  13. package/dist/java/type.js +12 -0
  14. package/dist/java/type.js.map +1 -1
  15. package/dist/java/visitor.d.ts +12 -4
  16. package/dist/java/visitor.d.ts.map +1 -1
  17. package/dist/java/visitor.js +23 -1
  18. package/dist/java/visitor.js.map +1 -1
  19. package/dist/javascript/format.d.ts +2 -2
  20. package/dist/javascript/format.d.ts.map +1 -1
  21. package/dist/javascript/format.js.map +1 -1
  22. package/dist/javascript/index.d.ts +3 -0
  23. package/dist/javascript/index.d.ts.map +1 -1
  24. package/dist/javascript/index.js +3 -0
  25. package/dist/javascript/index.js.map +1 -1
  26. package/dist/javascript/method-matcher.d.ts +16 -0
  27. package/dist/javascript/method-matcher.d.ts.map +1 -0
  28. package/dist/javascript/method-matcher.js +222 -0
  29. package/dist/javascript/method-matcher.js.map +1 -0
  30. package/dist/javascript/parser.d.ts +1 -1
  31. package/dist/javascript/parser.d.ts.map +1 -1
  32. package/dist/javascript/parser.js +27 -5
  33. package/dist/javascript/parser.js.map +1 -1
  34. package/dist/javascript/preconditions.d.ts +6 -0
  35. package/dist/javascript/preconditions.d.ts.map +1 -0
  36. package/dist/javascript/preconditions.js +58 -0
  37. package/dist/javascript/preconditions.js.map +1 -0
  38. package/dist/javascript/print.d.ts +2 -2
  39. package/dist/javascript/print.d.ts.map +1 -1
  40. package/dist/javascript/print.js.map +1 -1
  41. package/dist/javascript/remove-import.d.ts +56 -0
  42. package/dist/javascript/remove-import.d.ts.map +1 -0
  43. package/dist/javascript/remove-import.js +715 -0
  44. package/dist/javascript/remove-import.js.map +1 -0
  45. package/dist/javascript/rpc.js +3 -18
  46. package/dist/javascript/rpc.js.map +1 -1
  47. package/dist/javascript/search/index.d.ts +3 -0
  48. package/dist/javascript/search/index.d.ts.map +1 -0
  49. package/dist/javascript/search/index.js +19 -0
  50. package/dist/javascript/search/index.js.map +1 -0
  51. package/dist/javascript/search/uses-method.d.ts +8 -0
  52. package/dist/javascript/search/uses-method.d.ts.map +1 -0
  53. package/dist/javascript/search/uses-method.js +35 -0
  54. package/dist/javascript/search/uses-method.js.map +1 -0
  55. package/dist/javascript/search/uses-type.d.ts +8 -0
  56. package/dist/javascript/search/uses-type.d.ts.map +1 -0
  57. package/dist/javascript/search/uses-type.js +71 -0
  58. package/dist/javascript/search/uses-type.js.map +1 -0
  59. package/dist/javascript/templating.d.ts +1 -1
  60. package/dist/javascript/templating.d.ts.map +1 -1
  61. package/dist/javascript/templating.js +1 -1
  62. package/dist/javascript/templating.js.map +1 -1
  63. package/dist/javascript/tree.d.ts +3 -3
  64. package/dist/javascript/tree.d.ts.map +1 -1
  65. package/dist/javascript/tree.js +28 -0
  66. package/dist/javascript/tree.js.map +1 -1
  67. package/dist/javascript/type-mapping.d.ts +7 -18
  68. package/dist/javascript/type-mapping.d.ts.map +1 -1
  69. package/dist/javascript/type-mapping.js +290 -203
  70. package/dist/javascript/type-mapping.js.map +1 -1
  71. package/dist/javascript/visitor.d.ts +1 -1
  72. package/dist/javascript/visitor.d.ts.map +1 -1
  73. package/dist/javascript/visitor.js +1 -1
  74. package/dist/javascript/visitor.js.map +1 -1
  75. package/dist/json/print.js.map +1 -1
  76. package/dist/json/rpc.js +46 -17
  77. package/dist/json/rpc.js.map +1 -1
  78. package/dist/json/visitor.d.ts +2 -2
  79. package/dist/json/visitor.d.ts.map +1 -1
  80. package/dist/json/visitor.js.map +1 -1
  81. package/dist/print.d.ts +1 -0
  82. package/dist/print.d.ts.map +1 -1
  83. package/dist/print.js +6 -0
  84. package/dist/print.js.map +1 -1
  85. package/dist/rpc/queue.d.ts +15 -6
  86. package/dist/rpc/queue.d.ts.map +1 -1
  87. package/dist/rpc/queue.js +37 -13
  88. package/dist/rpc/queue.js.map +1 -1
  89. package/dist/rpc/request/generate.d.ts +4 -0
  90. package/dist/rpc/request/generate.d.ts.map +1 -1
  91. package/dist/rpc/request/generate.js +9 -4
  92. package/dist/rpc/request/generate.js.map +1 -1
  93. package/dist/rpc/request/get-object.d.ts +2 -2
  94. package/dist/rpc/request/get-object.d.ts.map +1 -1
  95. package/dist/rpc/request/get-object.js +4 -12
  96. package/dist/rpc/request/get-object.js.map +1 -1
  97. package/dist/rpc/request/parse.d.ts.map +1 -1
  98. package/dist/rpc/request/parse.js.map +1 -1
  99. package/dist/rpc/request/print.d.ts +1 -1
  100. package/dist/rpc/request/print.d.ts.map +1 -1
  101. package/dist/rpc/request/print.js +1 -1
  102. package/dist/rpc/request/print.js.map +1 -1
  103. package/dist/rpc/request/visit.d.ts +3 -2
  104. package/dist/rpc/request/visit.d.ts.map +1 -1
  105. package/dist/rpc/request/visit.js +5 -4
  106. package/dist/rpc/request/visit.js.map +1 -1
  107. package/dist/rpc/rewrite-rpc.d.ts +4 -4
  108. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  109. package/dist/rpc/rewrite-rpc.js +16 -17
  110. package/dist/rpc/rewrite-rpc.js.map +1 -1
  111. package/dist/search/index.d.ts +2 -0
  112. package/dist/search/index.d.ts.map +1 -0
  113. package/dist/search/index.js +18 -0
  114. package/dist/search/index.js.map +1 -0
  115. package/dist/search/is-source-file.d.ts +8 -0
  116. package/dist/search/is-source-file.d.ts.map +1 -0
  117. package/dist/search/is-source-file.js +70 -0
  118. package/dist/search/is-source-file.js.map +1 -0
  119. package/dist/test/rewrite-test.d.ts.map +1 -1
  120. package/dist/test/rewrite-test.js +3 -0
  121. package/dist/test/rewrite-test.js.map +1 -1
  122. package/dist/text/rpc.js +37 -40
  123. package/dist/text/rpc.js.map +1 -1
  124. package/dist/util.d.ts +1 -0
  125. package/dist/util.d.ts.map +1 -1
  126. package/dist/util.js +13 -0
  127. package/dist/util.js.map +1 -1
  128. package/dist/version.txt +1 -1
  129. package/dist/visitor.d.ts +1 -1
  130. package/dist/visitor.d.ts.map +1 -1
  131. package/dist/visitor.js +3 -2
  132. package/dist/visitor.js.map +1 -1
  133. package/package.json +3 -1
  134. package/src/execution.ts +0 -2
  135. package/src/java/rpc.ts +68 -0
  136. package/src/java/tree.ts +20 -76
  137. package/src/java/type.ts +14 -0
  138. package/src/java/visitor.ts +32 -12
  139. package/src/javascript/format.ts +2 -2
  140. package/src/javascript/index.ts +4 -0
  141. package/src/javascript/method-matcher.ts +250 -0
  142. package/src/javascript/parser.ts +20 -6
  143. package/src/javascript/preconditions.ts +40 -0
  144. package/src/javascript/print.ts +3 -3
  145. package/src/javascript/remove-import.ts +780 -0
  146. package/src/javascript/rpc.ts +6 -19
  147. package/src/javascript/search/index.ts +2 -0
  148. package/src/javascript/search/uses-method.ts +21 -0
  149. package/src/javascript/search/uses-type.ts +27 -0
  150. package/src/javascript/templating.ts +4 -3
  151. package/src/javascript/tree.ts +47 -3
  152. package/src/javascript/type-mapping.ts +320 -214
  153. package/src/javascript/visitor.ts +126 -126
  154. package/src/json/print.ts +1 -1
  155. package/src/json/rpc.ts +40 -19
  156. package/src/json/visitor.ts +2 -2
  157. package/src/print.ts +9 -3
  158. package/src/rpc/queue.ts +36 -12
  159. package/src/rpc/request/generate.ts +18 -6
  160. package/src/rpc/request/get-object.ts +6 -13
  161. package/src/rpc/request/parse.ts +1 -1
  162. package/src/rpc/request/print.ts +2 -2
  163. package/src/rpc/request/visit.ts +6 -5
  164. package/src/rpc/rewrite-rpc.ts +22 -21
  165. package/src/search/index.ts +1 -0
  166. package/src/search/is-source-file.ts +26 -0
  167. package/src/test/rewrite-test.ts +5 -2
  168. package/src/text/rpc.ts +33 -37
  169. package/src/util.ts +19 -4
  170. package/src/visitor.ts +3 -3
@@ -22,20 +22,12 @@ class NonDraftableType {
22
22
  [immerable] = false;
23
23
  }
24
24
 
25
- const builtInTypes = new Set([
26
- 'Array', 'Object', 'Function', 'String', 'Number', 'Boolean',
27
- 'Date', 'RegExp', 'Error', 'Promise', 'Map', 'Set', 'WeakMap',
28
- 'WeakSet', 'Symbol', 'BigInt', 'HTMLElement', 'Document',
29
- 'Window', 'Console', 'JSON', 'Math', 'Reflect', 'Proxy'
30
- ]);
31
-
32
25
  export class JavaScriptTypeMapping {
33
26
  private readonly typeCache: Map<string | number, Type> = new Map();
34
27
  private readonly regExpSymbol: ts.Symbol | undefined;
35
28
 
36
29
  constructor(
37
- private readonly checker: ts.TypeChecker,
38
- private readonly projectRoot: string = process.cwd()
30
+ private readonly checker: ts.TypeChecker
39
31
  ) {
40
32
  this.regExpSymbol = checker.resolveName(
41
33
  "RegExp",
@@ -62,15 +54,8 @@ export class JavaScriptTypeMapping {
62
54
  return existing;
63
55
  }
64
56
 
65
- // Check if this is an array type BEFORE checking for classes
66
- // TypeScript represents Array<T> as a reference to the Array interface
67
- if (this.checker.isArrayType(type)) {
68
- const arrayType = this.createArrayType(type as ts.TypeReference);
69
- this.typeCache.set(signature, arrayType);
70
- return arrayType;
71
- }
72
-
73
- // Check for class/interface/enum types (but not arrays)
57
+ // Check for class/interface/enum types (including arrays)
58
+ // Arrays in JavaScript are objects with methods, so we treat them as class types
74
59
  const symbol = type.getSymbol?.();
75
60
  if (symbol) {
76
61
  if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface | ts.SymbolFlags.Enum | ts.SymbolFlags.TypeAlias)) {
@@ -129,7 +114,7 @@ export class JavaScriptTypeMapping {
129
114
 
130
115
  primitiveType(node: ts.Node): Type.Primitive {
131
116
  const type = this.type(node);
132
- if (Type.isClass(type) && type.fullyQualifiedName === 'lib.RegExp') {
117
+ if (Type.isClass(type) && type.fullyQualifiedName === 'RegExp') {
133
118
  return Type.Primitive.String;
134
119
  }
135
120
  return Type.isPrimitive(type) ? type : Type.Primitive.None;
@@ -147,7 +132,47 @@ export class JavaScriptTypeMapping {
147
132
  return undefined;
148
133
  }
149
134
 
135
+ /**
136
+ * Helper to create a Type.Method object from common parameters
137
+ */
138
+ private createMethodType(
139
+ signature: ts.Signature,
140
+ node: ts.Node,
141
+ declaringType: Type.FullyQualified,
142
+ name: string,
143
+ declaredFormalTypeNames: string[] = []
144
+ ): Type.Method {
145
+ const returnType = signature.getReturnType();
146
+ const parameters = signature.getParameters();
147
+ const parameterTypes: Type[] = [];
148
+ const parameterNames: string[] = [];
149
+
150
+ for (const param of parameters) {
151
+ parameterNames.push(param.getName());
152
+ const paramType = this.checker.getTypeOfSymbolAtLocation(param, node);
153
+ parameterTypes.push(this.getType(paramType));
154
+ }
155
+
156
+ // Create the Type.Method object
157
+ return Object.assign(new NonDraftableType(), {
158
+ kind: Type.Kind.Method,
159
+ declaringType: declaringType,
160
+ name: name,
161
+ returnType: this.getType(returnType),
162
+ parameterNames: parameterNames,
163
+ parameterTypes: parameterTypes,
164
+ thrownExceptions: [], // JavaScript doesn't have checked exceptions
165
+ annotations: [],
166
+ defaultValue: undefined,
167
+ declaredFormalTypeNames: declaredFormalTypeNames,
168
+ toJSON: function () {
169
+ return Type.signature(this);
170
+ }
171
+ }) as Type.Method;
172
+ }
173
+
150
174
  methodType(node: ts.Node): Type.Method | undefined {
175
+
151
176
  let signature: ts.Signature | undefined;
152
177
  let methodName: string;
153
178
  let declaringType: Type.FullyQualified;
@@ -161,7 +186,68 @@ export class JavaScriptTypeMapping {
161
186
  return undefined;
162
187
  }
163
188
 
164
- const symbol = this.checker.getSymbolAtLocation(node.expression);
189
+ let symbol = this.checker.getSymbolAtLocation(node.expression);
190
+
191
+
192
+ if (!symbol && ts.isPropertyAccessExpression(node.expression)) {
193
+ // For property access expressions where we couldn't get a symbol,
194
+ // try to get the symbol from the signature's declaration
195
+ const declaration = signature?.getDeclaration();
196
+ if (declaration) {
197
+ symbol = this.checker.getSymbolAtLocation(declaration);
198
+ }
199
+
200
+ // If still no symbol but we have a signature, we can proceed with limited info
201
+ if (!symbol && signature) {
202
+ // For cases like util.isArray where the module is 'any' type
203
+ // We'll construct a basic method type from the signature
204
+ methodName = node.expression.name.getText();
205
+
206
+ // When there's no symbol but we have a signature, we need to work harder
207
+ // to find the declaring type. This happens with CommonJS require() calls
208
+ // where the module is typed as 'any' but methods still have signatures
209
+
210
+ // Try to trace back through the AST to find the require() call
211
+ let inferredDeclaringType: Type.FullyQualified | undefined;
212
+ const objExpr = node.expression.expression;
213
+
214
+ if (ts.isIdentifier(objExpr)) {
215
+ // Look for the variable declaration that assigns the require() result
216
+ const objSymbol = this.checker.getSymbolAtLocation(objExpr);
217
+
218
+ if (objSymbol && objSymbol.valueDeclaration) {
219
+ const valueDecl = objSymbol.valueDeclaration;
220
+ if (ts.isVariableDeclaration(valueDecl) && valueDecl.initializer) {
221
+ // Check if it's a require() call
222
+ if (ts.isCallExpression(valueDecl.initializer)) {
223
+ const callExpr = valueDecl.initializer;
224
+ if (ts.isIdentifier(callExpr.expression) &&
225
+ callExpr.expression.getText() === 'require' &&
226
+ callExpr.arguments.length > 0) {
227
+ // Extract the module name from require('module-name')
228
+ const moduleArg = callExpr.arguments[0];
229
+ if (ts.isStringLiteral(moduleArg)) {
230
+ const moduleName = moduleArg.text;
231
+
232
+ inferredDeclaringType = {
233
+ kind: Type.Kind.Class,
234
+ fullyQualifiedName: moduleName
235
+ } as Type.FullyQualified;
236
+ }
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ // Use the inferred type or fall back to unknown
244
+ declaringType = inferredDeclaringType || Type.unknownType as Type.FullyQualified;
245
+
246
+ // Create the method type using the helper
247
+ return this.createMethodType(signature, node, declaringType, methodName);
248
+ }
249
+ }
250
+
165
251
  if (!symbol) {
166
252
  return undefined;
167
253
  }
@@ -169,63 +255,164 @@ export class JavaScriptTypeMapping {
169
255
  // Get the method name
170
256
  if (ts.isPropertyAccessExpression(node.expression)) {
171
257
  methodName = node.expression.name.getText();
258
+
259
+ // Check if the object is an imported symbol
260
+ const objSymbol = this.checker.getSymbolAtLocation(node.expression.expression);
261
+ let isImport = false;
262
+ if (objSymbol) {
263
+ // Only call getAliasedSymbol if the symbol is actually an alias
264
+ if (objSymbol.flags & ts.SymbolFlags.Alias) {
265
+ const aliasedSymbol = this.checker.getAliasedSymbol(objSymbol);
266
+ isImport = aliasedSymbol && aliasedSymbol !== objSymbol;
267
+ }
268
+ }
269
+
172
270
  const exprType = this.checker.getTypeAtLocation(node.expression.expression);
173
271
  const mappedType = this.getType(exprType);
174
272
 
175
- // For string methods like 'hello'.split(), ensure we have a proper declaring type
176
- if (!mappedType || mappedType.kind !== Type.Kind.Class) {
177
- // If the expression type is a primitive string, use lib.String as declaring type
273
+ // Handle different types
274
+ if (mappedType && mappedType.kind === Type.Kind.Class) {
275
+ // Update the declaring type with the corrected FQN
276
+ if (isImport && objSymbol) {
277
+ const importName = objSymbol.getName();
278
+ const origFqn = (mappedType as Type.Class).fullyQualifiedName;
279
+ const lastDot = origFqn.lastIndexOf('.');
280
+ if (lastDot > 0) {
281
+ const typeName = origFqn.substring(lastDot + 1);
282
+ declaringType = {
283
+ kind: Type.Kind.Class,
284
+ fullyQualifiedName: `${importName}.${typeName}`
285
+ } as Type.FullyQualified;
286
+ } else {
287
+ declaringType = mappedType as Type.FullyQualified;
288
+ }
289
+ } else {
290
+ declaringType = mappedType as Type.FullyQualified;
291
+ }
292
+ } else {
293
+ // Handle primitive types and other non-class types
294
+ if (mappedType) {
295
+ if ((mappedType as any).keyword === 'String') {
296
+ declaringType = {
297
+ kind: Type.Kind.Class,
298
+ fullyQualifiedName: 'String'
299
+ } as Type.FullyQualified;
300
+ } else if ((mappedType as any).keyword === 'Number') {
301
+ declaringType = {
302
+ kind: Type.Kind.Class,
303
+ fullyQualifiedName: 'Number'
304
+ } as Type.FullyQualified;
305
+ } else {
306
+ // Fallback for other types
307
+ declaringType = mappedType as Type.FullyQualified;
308
+ }
309
+ } else {
310
+ // Default to unknown if we can't determine the type
311
+ declaringType = Type.unknownType as Type.FullyQualified;
312
+ }
313
+ }
314
+
315
+ // For string methods like 'hello'.split(), ensure we have a proper declaring type for primitives
316
+ if (!isImport && declaringType === Type.unknownType) {
317
+ // If the expression type is a primitive string, use String as declaring type
178
318
  const typeString = this.checker.typeToString(exprType);
179
319
  if (typeString === 'string' || exprType.flags & ts.TypeFlags.String || exprType.flags & ts.TypeFlags.StringLiteral) {
180
320
  declaringType = {
181
321
  kind: Type.Kind.Class,
182
- fullyQualifiedName: 'lib.String'
322
+ fullyQualifiedName: 'string'
183
323
  } as Type.FullyQualified;
184
324
  } else if (typeString === 'number' || exprType.flags & ts.TypeFlags.Number || exprType.flags & ts.TypeFlags.NumberLiteral) {
185
325
  declaringType = {
186
326
  kind: Type.Kind.Class,
187
- fullyQualifiedName: 'lib.Number'
327
+ fullyQualifiedName: 'number'
188
328
  } as Type.FullyQualified;
189
329
  } else {
190
330
  // Fallback for other primitive types or unknown
191
331
  declaringType = Type.unknownType as Type.FullyQualified;
192
332
  }
193
- } else {
194
- declaringType = mappedType as Type.FullyQualified;
195
333
  }
334
+
196
335
  } else if (ts.isIdentifier(node.expression)) {
197
336
  methodName = node.expression.getText();
198
- // For standalone functions, we need to determine the appropriate declaring type
199
- const exprType = this.checker.getTypeAtLocation(node.expression);
200
- const funcType = this.getType(exprType);
201
-
202
- if (funcType && funcType.kind === Type.Kind.Class) {
203
- const fqn = (funcType as Type.Class).fullyQualifiedName;
204
- const lastDot = fqn.lastIndexOf('.');
205
-
206
- if (lastDot > 0) {
207
- // For functions from modules, use the module part as declaring type
208
- // Examples:
209
- // - "node.assert" -> declaring type: "node"
210
- // - "@types/lodash.map" -> declaring type: "@types/lodash"
211
- // - "@types/express.express" -> declaring type: "@types/express"
337
+
338
+ // Check if this is an import first
339
+ const symbol = this.checker.getSymbolAtLocation(node.expression);
340
+ let moduleSpecifier: string | undefined;
341
+
342
+ if (symbol) {
343
+ // Check if this is an aliased symbol (i.e., an import)
344
+ let aliasedSymbol: ts.Symbol | undefined;
345
+ if (symbol.flags & ts.SymbolFlags.Alias) {
346
+ aliasedSymbol = this.checker.getAliasedSymbol(symbol);
347
+ }
348
+
349
+ // If getAliasedSymbol returns something different, it's an import
350
+ if (aliasedSymbol && aliasedSymbol !== symbol) {
351
+ // This is definitely an imported symbol
352
+ // Now find the import declaration to get the module specifier
353
+ if (symbol.declarations && symbol.declarations.length > 0) {
354
+ let importNode: ts.Node = symbol.declarations[0];
355
+
356
+ // Traverse up to find the ImportDeclaration
357
+ while (importNode && !ts.isImportDeclaration(importNode)) {
358
+ importNode = importNode.parent;
359
+ }
360
+
361
+ if (importNode && ts.isImportDeclaration(importNode)) {
362
+ const importDeclNode = importNode as ts.ImportDeclaration;
363
+ if (ts.isStringLiteral(importDeclNode.moduleSpecifier)) {
364
+ moduleSpecifier = importDeclNode.moduleSpecifier.text;
365
+ }
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ if (moduleSpecifier) {
372
+ // This is an imported function - use the module specifier as declaring type
373
+ if (moduleSpecifier.startsWith('node:')) {
374
+ // Node.js built-in module
212
375
  declaringType = {
213
376
  kind: Type.Kind.Class,
214
- fullyQualifiedName: fqn.substring(0, lastDot)
377
+ fullyQualifiedName: 'node'
215
378
  } as Type.FullyQualified;
379
+ methodName = moduleSpecifier.substring(5); // Remove 'node:' prefix
216
380
  } else {
217
- // No dots in the name - the type IS the module itself
218
- // This handles single-name modules like "axios", "lodash" etc.
219
- declaringType = funcType as Type.FullyQualified;
381
+ // Regular module import
382
+ declaringType = {
383
+ kind: Type.Kind.Class,
384
+ fullyQualifiedName: moduleSpecifier
385
+ } as Type.FullyQualified;
386
+ methodName = '<default>';
220
387
  }
221
388
  } else {
222
- // Try to use the symbol's parent or module
223
- const parent = (symbol as any).parent;
224
- if (parent) {
225
- const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
226
- declaringType = this.getType(parentType) as Type.FullyQualified;
389
+ // Fall back to the original logic for non-imported functions
390
+ const exprType = this.checker.getTypeAtLocation(node.expression);
391
+ const funcType = this.getType(exprType);
392
+
393
+ if (funcType && funcType.kind === Type.Kind.Class) {
394
+ const fqn = (funcType as Type.Class).fullyQualifiedName;
395
+ const lastDot = fqn.lastIndexOf('.');
396
+
397
+ if (lastDot > 0) {
398
+ // For functions from modules, use the module part as declaring type
399
+ declaringType = {
400
+ kind: Type.Kind.Class,
401
+ fullyQualifiedName: fqn.substring(0, lastDot)
402
+ } as Type.FullyQualified;
403
+ } else {
404
+ // No dots in the name - the type IS the module itself
405
+ declaringType = funcType as Type.FullyQualified;
406
+ }
227
407
  } else {
228
- declaringType = Type.unknownType as Type.FullyQualified;
408
+ // Try to use the symbol's parent or module
409
+ const parent = (symbol as any).parent;
410
+ if (parent) {
411
+ const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
412
+ declaringType = this.getType(parentType) as Type.FullyQualified;
413
+ } else {
414
+ declaringType = Type.unknownType as Type.FullyQualified;
415
+ }
229
416
  }
230
417
  }
231
418
  } else {
@@ -290,57 +477,16 @@ export class JavaScriptTypeMapping {
290
477
  return undefined;
291
478
  }
292
479
 
293
- // Common logic for all method types
294
- const returnType = signature.getReturnType();
295
- const parameters = signature.getParameters();
296
- const parameterTypes: Type[] = [];
297
- const parameterNames: string[] = [];
298
-
299
- for (const param of parameters) {
300
- parameterNames.push(param.getName());
301
- const paramType = this.checker.getTypeOfSymbolAtLocation(param, node);
302
- parameterTypes.push(this.getType(paramType));
303
- }
304
-
305
- // Create the Type.Method object
306
- return Object.assign(new NonDraftableType(), {
307
- kind: Type.Kind.Method,
308
- declaringType: declaringType,
309
- name: methodName,
310
- returnType: this.getType(returnType),
311
- parameterNames: parameterNames,
312
- parameterTypes: parameterTypes,
313
- thrownExceptions: [], // JavaScript doesn't have checked exceptions
314
- annotations: [],
315
- defaultValue: undefined,
316
- declaredFormalTypeNames: declaredFormalTypeNames,
317
- toJSON: function () {
318
- return Type.signature(this);
319
- }
320
- }) as Type.Method;
480
+ // Create the method type using the helper
481
+ return this.createMethodType(signature, node, declaringType, methodName, declaredFormalTypeNames);
321
482
  }
322
483
 
323
- /**
324
- * Create a JavaType.Array from a TypeScript array type
325
- */
326
- private createArrayType(type: ts.TypeReference): Type.Array {
327
- // Get the element type (type argument of Array<T>)
328
- const typeArgs = this.checker.getTypeArguments(type);
329
- const elemType = typeArgs.length > 0 ? this.getType(typeArgs[0]) : Type.unknownType;
330
-
331
- return Object.assign(new NonDraftableType(), {
332
- kind: Type.Kind.Array,
333
- elemType: elemType,
334
- annotations: [],
335
- toJSON: function () {
336
- return Type.signature(this);
337
- }
338
- }) as Type.Array;
339
- }
340
484
 
341
485
  /**
342
486
  * Get the fully qualified name for a TypeScript type.
343
- * Format: "module-specifier.TypeName" (e.g., "@mui/material.Button", "src/components/Button.Button")
487
+ * Uses TypeScript's built-in resolution which properly handles things like:
488
+ * - React.Component (not @types/react.Component)
489
+ * - _.LoDashStatic (not @types/lodash.LoDashStatic)
344
490
  */
345
491
  private getFullyQualifiedName(type: ts.Type): string {
346
492
  const symbol = type.getSymbol?.();
@@ -348,140 +494,100 @@ export class JavaScriptTypeMapping {
348
494
  return "unknown";
349
495
  }
350
496
 
351
- const typeName = symbol.getName();
352
- const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
353
- if (!declaration) {
354
- // No declaration - might be a built-in or synthetic type
355
- if (builtInTypes.has(typeName)) {
356
- return `lib.${typeName}`;
357
- }
358
- return typeName;
359
- }
360
-
361
- const sourceFile = declaration.getSourceFile();
362
- const fileName = sourceFile.fileName;
363
-
364
- // Check if this is a test file (snowflake ID as filename)
365
- // Test files are generated with numeric IDs like "672087069480189952.ts"
366
- if (/^\d+\.(ts|tsx|js|jsx)$/.test(fileName)) {
367
- // For test files, just return the type name without module prefix
368
- return typeName;
369
- }
370
-
371
- // Check if this is from TypeScript's lib files (lib.d.ts, lib.dom.d.ts, etc.)
372
- if (fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.")) {
373
- return `lib.${typeName}`;
374
- }
497
+ // First, check if this symbol is an import/alias
498
+ // For imported types, we want to use the module specifier instead of the file path
499
+ if (symbol.flags & ts.SymbolFlags.Alias) {
500
+ const aliasedSymbol = this.checker.getAliasedSymbol(symbol);
501
+ if (aliasedSymbol && aliasedSymbol !== symbol && symbol.declarations && symbol.declarations.length > 0) {
502
+ // Try to find the import declaration to get the module specifier
503
+ let importNode: ts.Node | undefined = symbol.declarations[0];
504
+
505
+ // Traverse up to find the ImportDeclaration or ImportSpecifier
506
+ while (importNode && importNode.parent && !ts.isImportDeclaration(importNode) && !ts.isImportSpecifier(importNode)) {
507
+ importNode = importNode.parent;
508
+ }
375
509
 
376
- // Check if this is from an external module (node_modules or .d.ts)
377
- if (sourceFile.isDeclarationFile || fileName.includes("node_modules")) {
378
- const packageName = this.extractPackageName(fileName);
379
- if (packageName) {
380
- // Special handling for @types/node - these are Node.js built-in modules
381
- // and should be mapped to "node.*" instead of "@types/node.*"
382
- if (packageName === "@types/node") {
383
- // Extract the module name from the file path
384
- // e.g., /node_modules/@types/node/assert.d.ts -> assert
385
- // e.g., /node_modules/@types/node/fs/promises.d.ts -> fs/promises
386
- const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
387
- if (nodeMatch) {
388
- const modulePath = nodeMatch[1];
389
- // For default exports from Node modules, we want the module to be the "class"
390
- // But we still need to include the type name for proper identification
391
- if (typeName === "default" || typeName === modulePath) {
392
- // This is likely the default export, just use the module name
393
- return `node.${modulePath}`;
394
- }
395
- // For named exports, include both module and type name
396
- if (modulePath.includes('/')) {
397
- return `node.${modulePath.replace(/\//g, '.')}.${typeName}`;
510
+ let moduleSpecifier: string | undefined;
511
+
512
+ if (importNode && ts.isImportSpecifier(importNode)) {
513
+ // Named import like: import { ClipLoader } from 'react-spinners'
514
+ // ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
515
+ const namedImports = importNode.parent; // NamedImports
516
+ if (namedImports && ts.isNamedImports(namedImports)) {
517
+ const importClause = namedImports.parent; // ImportClause
518
+ if (importClause && ts.isImportClause(importClause)) {
519
+ const importDecl = importClause.parent; // ImportDeclaration
520
+ if (importDecl && ts.isImportDeclaration(importDecl) && ts.isStringLiteral(importDecl.moduleSpecifier)) {
521
+ moduleSpecifier = importDecl.moduleSpecifier.text;
522
+ }
398
523
  }
399
- return `node.${modulePath}.${typeName}`;
400
524
  }
401
- // Fallback for @types/node types that don't match the pattern
402
- return `node.${typeName}`;
525
+ } else if (importNode && ts.isImportDeclaration(importNode)) {
526
+ // Default or namespace import
527
+ if (ts.isStringLiteral(importNode.moduleSpecifier)) {
528
+ moduleSpecifier = importNode.moduleSpecifier.text;
529
+ }
530
+ }
531
+
532
+ if (moduleSpecifier) {
533
+ // Build the fully qualified name from module specifier + symbol name
534
+ const symbolName = symbol.getName();
535
+ return `${moduleSpecifier}.${symbolName}`;
403
536
  }
404
- return `${packageName}.${typeName}`;
405
537
  }
406
538
  }
407
539
 
408
- // For local files, use relative path from project root
409
- const relativePath = this.getRelativeModulePath(fileName);
410
- return `${relativePath}.${typeName}`;
411
- }
412
-
413
- /**
414
- * Extract package name from a node_modules path.
415
- * Examples:
416
- * - /path/to/project/node_modules/react/index.d.ts -> "react"
417
- * - /path/to/project/node_modules/@mui/material/Button/index.d.ts -> "@mui/material"
418
- */
419
- private extractPackageName(fileName: string): string | null {
420
- const match = fileName.match(/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/);
421
- return match ? match[1] : null;
422
- }
540
+ // Fall back to TypeScript's built-in getFullyQualifiedName
541
+ // This returns names with quotes that we need to clean up
542
+ // e.g., '"React"."Component"' -> 'React.Component'
543
+ const tsQualifiedName = this.checker.getFullyQualifiedName(symbol);
544
+ let cleanedName = tsQualifiedName.replace(/"/g, '');
545
+
546
+ // Check if this is a file path from node_modules (happens with some packages)
547
+ // TypeScript sometimes returns full paths instead of module names
548
+ if (cleanedName.includes('node_modules/')) {
549
+ // Extract the module name from the path
550
+ // Example: /private/var/.../node_modules/react-spinners/src/index.ClipLoader
551
+ // Should become: react-spinners.ClipLoader
552
+ const nodeModulesIndex = cleanedName.indexOf('node_modules/');
553
+ const afterNodeModules = cleanedName.substring(nodeModulesIndex + 'node_modules/'.length);
554
+
555
+ // Split by '/' to get parts of the path
556
+ const pathParts = afterNodeModules.split('/');
557
+
558
+ if (pathParts.length > 0) {
559
+ // First part is the package name (might be scoped like @types)
560
+ let packageName = pathParts[0];
561
+
562
+ // Handle scoped packages
563
+ if (packageName.startsWith('@') && pathParts.length > 1) {
564
+ packageName = `${packageName}/${pathParts[1]}`;
565
+ }
423
566
 
424
- /**
425
- * Get relative module path from project root.
426
- * Removes file extension and uses forward slashes.
427
- */
428
- private getRelativeModulePath(fileName: string): string {
429
- // Remove project root and normalize path
430
- let relativePath = fileName;
431
- if (fileName.startsWith(this.projectRoot)) {
432
- relativePath = fileName.slice(this.projectRoot.length);
567
+ // Find the symbol name (everything after the last dot in the original cleaned name)
568
+ const lastDotIndex = cleanedName.lastIndexOf('.');
569
+ if (lastDotIndex > 0) {
570
+ const symbolName = cleanedName.substring(lastDotIndex + 1);
571
+ cleanedName = `${packageName}.${symbolName}`;
572
+ } else {
573
+ cleanedName = packageName;
574
+ }
575
+ }
433
576
  }
434
577
 
435
- // Remove leading slash and file extension
436
- relativePath = relativePath.replace(/^\//, '').replace(/\.[^/.]+$/, '');
437
-
438
- // Convert backslashes to forward slashes (for Windows)
439
- relativePath = relativePath.replace(/\\/g, '/');
440
-
441
- return relativePath;
578
+ return cleanedName.endsWith('Constructor') ?
579
+ cleanedName.substring(0, cleanedName.length - 'Constructor'.length) :
580
+ cleanedName;
442
581
  }
443
582
 
583
+
444
584
  /**
445
585
  * Create an empty JavaType.Class shell from a TypeScript type.
446
586
  * The shell will be populated later to handle circular references.
447
587
  */
448
588
  private createEmptyClassType(type: ts.Type): Type.Class {
449
- // Use our custom getFullyQualifiedName method for consistent naming
450
- let fullyQualifiedName = this.getFullyQualifiedName(type);
451
-
452
- // If getFullyQualifiedName returned unknown, fall back to TypeScript's method
453
- if (fullyQualifiedName === "unknown") {
454
- const symbol = type.symbol;
455
- fullyQualifiedName = symbol ? this.checker.getFullyQualifiedName(symbol) : `<anonymous>${this.checker.typeToString(type)}`;
456
-
457
- // Fix FQN for types from @types packages
458
- // TypeScript returns "_.LoDashStatic" but we want "@types/lodash.LoDashStatic"
459
- if (symbol && symbol.declarations && symbol.declarations.length > 0) {
460
- const sourceFile = symbol.declarations[0].getSourceFile();
461
- const fileName = sourceFile.fileName;
462
- // Check if this is from @types package
463
- const typesMatch = fileName.match(/node_modules\/@types\/([^/]+)/);
464
- if (typesMatch) {
465
- const packageName = typesMatch[1];
466
- // Special handling for @types/node - use "node" prefix instead
467
- if (packageName === "node") {
468
- // Extract the module name from the file path if possible
469
- const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
470
- if (nodeMatch) {
471
- const modulePath = nodeMatch[1];
472
- // Replace the module specifier with node.module
473
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.${modulePath}.`);
474
- } else {
475
- // Fallback: just use "node" prefix
476
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.`);
477
- }
478
- } else {
479
- // Replace the module specifier part with @types/package
480
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `@types/${packageName}.`);
481
- }
482
- }
483
- }
484
- }
589
+ // Use our getFullyQualifiedName method which uses TypeScript's built-in resolution
590
+ const fullyQualifiedName = this.getFullyQualifiedName(type);
485
591
 
486
592
  // Determine the class kind based on symbol flags
487
593
  let classKind = Type.Class.Kind.Interface; // Default to interface