@openrewrite/rewrite 8.62.0 → 8.62.2

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 (97) hide show
  1. package/dist/java/rpc.d.ts +3 -3
  2. package/dist/java/rpc.d.ts.map +1 -1
  3. package/dist/java/rpc.js +6 -6
  4. package/dist/java/rpc.js.map +1 -1
  5. package/dist/java/tree.d.ts +31 -33
  6. package/dist/java/tree.d.ts.map +1 -1
  7. package/dist/java/tree.js +125 -7
  8. package/dist/java/tree.js.map +1 -1
  9. package/dist/java/type.d.ts +70 -57
  10. package/dist/java/type.d.ts.map +1 -1
  11. package/dist/java/type.js +454 -25
  12. package/dist/java/type.js.map +1 -1
  13. package/dist/java/visitor.d.ts +4 -4
  14. package/dist/java/visitor.d.ts.map +1 -1
  15. package/dist/java/visitor.js +34 -28
  16. package/dist/java/visitor.js.map +1 -1
  17. package/dist/javascript/assertions.d.ts +3 -0
  18. package/dist/javascript/assertions.d.ts.map +1 -1
  19. package/dist/javascript/assertions.js +78 -1
  20. package/dist/javascript/assertions.js.map +1 -1
  21. package/dist/javascript/format.js +4 -4
  22. package/dist/javascript/format.js.map +1 -1
  23. package/dist/javascript/parser.d.ts +1 -1
  24. package/dist/javascript/parser.d.ts.map +1 -1
  25. package/dist/javascript/parser.js +38 -11
  26. package/dist/javascript/parser.js.map +1 -1
  27. package/dist/javascript/rpc.js +1 -4
  28. package/dist/javascript/rpc.js.map +1 -1
  29. package/dist/javascript/tree.d.ts +33 -35
  30. package/dist/javascript/tree.d.ts.map +1 -1
  31. package/dist/javascript/tree.js +70 -17
  32. package/dist/javascript/tree.js.map +1 -1
  33. package/dist/javascript/type-mapping.d.ts +44 -8
  34. package/dist/javascript/type-mapping.d.ts.map +1 -1
  35. package/dist/javascript/type-mapping.js +571 -101
  36. package/dist/javascript/type-mapping.js.map +1 -1
  37. package/dist/javascript/visitor.d.ts +2 -2
  38. package/dist/javascript/visitor.d.ts.map +1 -1
  39. package/dist/javascript/visitor.js.map +1 -1
  40. package/dist/json/tree.d.ts +0 -2
  41. package/dist/json/tree.d.ts.map +1 -1
  42. package/dist/json/tree.js +12 -2
  43. package/dist/json/tree.js.map +1 -1
  44. package/dist/reference.js +1 -1
  45. package/dist/reference.js.map +1 -1
  46. package/dist/rpc/chrome-profiler.d.ts +25 -0
  47. package/dist/rpc/chrome-profiler.d.ts.map +1 -0
  48. package/dist/rpc/chrome-profiler.js +405 -0
  49. package/dist/rpc/chrome-profiler.js.map +1 -0
  50. package/dist/rpc/queue.d.ts +0 -3
  51. package/dist/rpc/queue.d.ts.map +1 -1
  52. package/dist/rpc/queue.js +6 -6
  53. package/dist/rpc/queue.js.map +1 -1
  54. package/dist/rpc/request/parse.d.ts.map +1 -1
  55. package/dist/rpc/request/parse.js.map +1 -1
  56. package/dist/rpc/rewrite-rpc.d.ts +1 -0
  57. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  58. package/dist/rpc/rewrite-rpc.js +1 -1
  59. package/dist/rpc/rewrite-rpc.js.map +1 -1
  60. package/dist/rpc/server.d.ts.map +1 -1
  61. package/dist/rpc/server.js +26 -1
  62. package/dist/rpc/server.js.map +1 -1
  63. package/dist/test/rewrite-test.d.ts +1 -1
  64. package/dist/test/rewrite-test.d.ts.map +1 -1
  65. package/dist/test/rewrite-test.js +18 -2
  66. package/dist/test/rewrite-test.js.map +1 -1
  67. package/dist/text/tree.d.ts +0 -2
  68. package/dist/text/tree.d.ts.map +1 -1
  69. package/dist/text/tree.js +4 -17
  70. package/dist/text/tree.js.map +1 -1
  71. package/dist/tree.d.ts +1 -0
  72. package/dist/tree.d.ts.map +1 -1
  73. package/dist/tree.js +14 -0
  74. package/dist/tree.js.map +1 -1
  75. package/dist/version.txt +1 -1
  76. package/package.json +3 -3
  77. package/src/java/rpc.ts +18 -15
  78. package/src/java/tree.ts +68 -38
  79. package/src/java/type.ts +475 -74
  80. package/src/java/visitor.ts +45 -39
  81. package/src/javascript/assertions.ts +52 -1
  82. package/src/javascript/format.ts +4 -4
  83. package/src/javascript/parser.ts +51 -18
  84. package/src/javascript/rpc.ts +8 -10
  85. package/src/javascript/tree.ts +34 -35
  86. package/src/javascript/type-mapping.ts +582 -47
  87. package/src/javascript/visitor.ts +5 -5
  88. package/src/json/tree.ts +1 -2
  89. package/src/reference.ts +1 -1
  90. package/src/rpc/chrome-profiler.ts +373 -0
  91. package/src/rpc/queue.ts +8 -12
  92. package/src/rpc/request/parse.ts +1 -1
  93. package/src/rpc/rewrite-rpc.ts +3 -2
  94. package/src/rpc/server.ts +30 -1
  95. package/src/test/rewrite-test.ts +19 -3
  96. package/src/text/tree.ts +0 -1
  97. package/src/tree.ts +15 -0
@@ -13,17 +13,30 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import * as ts from "typescript";
17
- import {JavaType} from "../java";
18
- import {createDraft, Draft, finishDraft} from "immer";
19
- import {asRef, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
20
- import {MarkersKind, ParseExceptionResult} from "../markers";
16
+ import ts from "typescript";
17
+ import {Type} from "../java";
18
+ import {immerable} from "immer";
19
+
20
+ // Helper class to create Type objects that immer won't traverse
21
+ class NonDraftableType {
22
+ [immerable] = false;
23
+ }
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
+ ]);
21
31
 
22
32
  export class JavaScriptTypeMapping {
23
- private readonly typeCache: Map<number, JavaType> = new Map();
33
+ private readonly typeCache: Map<string | number, Type> = new Map();
24
34
  private readonly regExpSymbol: ts.Symbol | undefined;
25
35
 
26
- constructor(private readonly checker: ts.TypeChecker) {
36
+ constructor(
37
+ private readonly checker: ts.TypeChecker,
38
+ private readonly projectRoot: string = process.cwd()
39
+ ) {
27
40
  this.regExpSymbol = checker.resolveName(
28
41
  "RegExp",
29
42
  undefined,
@@ -32,7 +45,7 @@ export class JavaScriptTypeMapping {
32
45
  );
33
46
  }
34
47
 
35
- type(node: ts.Node): JavaType | undefined {
48
+ type(node: ts.Node): Type | undefined {
36
49
  let type: ts.Type | undefined;
37
50
  if (ts.isExpression(node)) {
38
51
  type = this.checker.getTypeAtLocation(node);
@@ -42,84 +55,610 @@ export class JavaScriptTypeMapping {
42
55
  return type && this.getType(type);
43
56
  }
44
57
 
45
- private getType(type: ts.Type) {
58
+ private getType(type: ts.Type): Type {
46
59
  const signature = this.getSignature(type);
47
60
  const existing = this.typeCache.get(signature);
48
61
  if (existing) {
49
62
  return existing;
50
63
  }
51
- const result = this.createType(type, signature);
64
+
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)
74
+ const symbol = type.getSymbol?.();
75
+ if (symbol) {
76
+ if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface | ts.SymbolFlags.Enum | ts.SymbolFlags.TypeAlias)) {
77
+ // Create and cache shell first to handle circular references
78
+ const classType = this.createEmptyClassType(type);
79
+ this.typeCache.set(signature, classType);
80
+ this.populateClassType(classType, type);
81
+ return classType;
82
+ }
83
+ }
84
+
85
+ // For anonymous object types that could have circular references
86
+ if (type.flags & ts.TypeFlags.Object) {
87
+ const objectFlags = (type as ts.ObjectType).objectFlags;
88
+ if (objectFlags & ts.ObjectFlags.Anonymous) {
89
+ // Anonymous object type - create and cache shell, then populate
90
+ const classType = this.createEmptyClassType(type);
91
+ this.typeCache.set(signature, classType);
92
+ this.populateClassType(classType, type);
93
+ return classType;
94
+ }
95
+ }
96
+
97
+ // For non-object types, we can create them directly without recursion concerns
98
+ const result = this.createPrimitiveOrUnknownType(type);
52
99
  this.typeCache.set(signature, result);
53
100
  return result;
54
101
  }
55
102
 
56
- private getSignature(type: ts.Type): number {
57
- // FIXME for classes we need to include the containing module / package in the signature and probably include in the qualified name
58
- if ("id" in type) { // a private field returned by the type checker
103
+ private getSignature(type: ts.Type): string | number {
104
+ // Try to use TypeScript's internal id if available
105
+ if ("id" in type && type.id !== undefined) {
59
106
  return type.id as number;
60
- } else {
61
- throw new Error("no id property in type: " + JSON.stringify(type));
62
107
  }
108
+
109
+ // Fallback: Generate a string signature based on type characteristics
110
+ const typeString = this.checker.typeToString(type);
111
+ const symbol = type.getSymbol?.();
112
+
113
+ if (symbol) {
114
+ const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
115
+ if (declaration) {
116
+ const sourceFile = declaration.getSourceFile();
117
+ const fileName = sourceFile.fileName;
118
+ const pos = declaration.pos;
119
+ // Create unique signature from file + position + type string
120
+ // This ensures types from different modules are distinguished
121
+ return `${fileName}:${pos}:${typeString}`;
122
+ }
123
+ }
124
+
125
+ // Last resort: use type string with a prefix to distinguish from numeric IDs
126
+ // This might happen for synthetic types or types without declarations
127
+ return `synthetic:${typeString}`;
63
128
  }
64
129
 
65
- primitiveType(node: ts.Node): JavaType.Primitive {
130
+ primitiveType(node: ts.Node): Type.Primitive {
66
131
  const type = this.type(node);
67
- return JavaType.isPrimitive(type) ? type : JavaType.Primitive.None;
132
+ if (Type.isClass(type) && type.fullyQualifiedName === 'lib.RegExp') {
133
+ return Type.Primitive.String;
134
+ }
135
+ return Type.isPrimitive(type) ? type : Type.Primitive.None;
68
136
  }
69
137
 
70
- variableType(node: ts.NamedDeclaration): JavaType.Variable | undefined {
138
+ variableType(node: ts.NamedDeclaration): Type.Variable | undefined {
71
139
  if (ts.isVariableDeclaration(node)) {
72
140
  const symbol = this.checker.getSymbolAtLocation(node.name);
73
141
  if (symbol) {
74
- const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
142
+ // TODO: Implement in Phase 6
143
+ // const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
144
+ // return JavaType.Variable with proper mapping
75
145
  }
76
146
  }
77
147
  return undefined;
78
148
  }
79
149
 
80
- methodType(node: ts.Node): JavaType.Method | undefined {
81
- return undefined;
150
+ methodType(node: ts.Node): Type.Method | undefined {
151
+ let signature: ts.Signature | undefined;
152
+ let methodName: string;
153
+ let declaringType: Type.FullyQualified;
154
+ let declaredFormalTypeNames: string[] = [];
155
+
156
+ // Handle different kinds of nodes that represent methods or method invocations
157
+ if (ts.isCallExpression(node)) {
158
+ // For method invocations (e.g., _.map(...))
159
+ signature = this.checker.getResolvedSignature(node);
160
+ if (!signature) {
161
+ return undefined;
162
+ }
163
+
164
+ const symbol = this.checker.getSymbolAtLocation(node.expression);
165
+ if (!symbol) {
166
+ return undefined;
167
+ }
168
+
169
+ // Get the method name
170
+ if (ts.isPropertyAccessExpression(node.expression)) {
171
+ methodName = node.expression.name.getText();
172
+ const exprType = this.checker.getTypeAtLocation(node.expression.expression);
173
+ declaringType = this.getType(exprType) as Type.FullyQualified;
174
+ } else if (ts.isIdentifier(node.expression)) {
175
+ methodName = node.expression.getText();
176
+ // For standalone functions, we need to determine the appropriate declaring type
177
+ const exprType = this.checker.getTypeAtLocation(node.expression);
178
+ const funcType = this.getType(exprType);
179
+
180
+ if (funcType && funcType.kind === Type.Kind.Class) {
181
+ const fqn = (funcType as Type.Class).fullyQualifiedName;
182
+ const lastDot = fqn.lastIndexOf('.');
183
+
184
+ if (lastDot > 0) {
185
+ // For functions from modules, use the module part as declaring type
186
+ // Examples:
187
+ // - "node.assert" -> declaring type: "node"
188
+ // - "@types/lodash.map" -> declaring type: "@types/lodash"
189
+ // - "@types/express.express" -> declaring type: "@types/express"
190
+ declaringType = {
191
+ kind: Type.Kind.Class,
192
+ fullyQualifiedName: fqn.substring(0, lastDot)
193
+ } as Type.FullyQualified;
194
+ } else {
195
+ // No dots in the name - the type IS the module itself
196
+ // This handles single-name modules like "axios", "lodash" etc.
197
+ declaringType = funcType as Type.FullyQualified;
198
+ }
199
+ } else {
200
+ // Try to use the symbol's parent or module
201
+ const parent = (symbol as any).parent;
202
+ if (parent) {
203
+ const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
204
+ declaringType = this.getType(parentType) as Type.FullyQualified;
205
+ } else {
206
+ declaringType = Type.unknownType as Type.FullyQualified;
207
+ }
208
+ }
209
+ } else {
210
+ methodName = symbol.getName();
211
+ declaringType = Type.unknownType as Type.FullyQualified;
212
+ }
213
+
214
+ // Get type parameters from signature
215
+ const typeParameters = signature.getTypeParameters();
216
+ if (typeParameters) {
217
+ for (const tp of typeParameters) {
218
+ declaredFormalTypeNames.push(tp.symbol.getName());
219
+ }
220
+ }
221
+ } else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
222
+ // For method declarations
223
+ const symbol = this.checker.getSymbolAtLocation(node.name!);
224
+ if (!symbol) {
225
+ return undefined;
226
+ }
227
+
228
+ signature = this.checker.getSignatureFromDeclaration(node);
229
+ if (!signature) {
230
+ return undefined;
231
+ }
232
+
233
+ methodName = symbol.getName();
234
+
235
+ // Get the declaring type (the class/interface that contains this method)
236
+ const parent = node.parent;
237
+ if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isObjectLiteralExpression(parent)) {
238
+ const parentType = this.checker.getTypeAtLocation(parent);
239
+ declaringType = this.getType(parentType) as Type.FullyQualified;
240
+ } else {
241
+ declaringType = Type.unknownType as Type.FullyQualified;
242
+ }
243
+
244
+ // Get type parameters from node
245
+ if (node.typeParameters) {
246
+ for (const tp of node.typeParameters) {
247
+ declaredFormalTypeNames.push(tp.name.getText());
248
+ }
249
+ }
250
+ } else if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
251
+ // For function declarations/expressions
252
+ signature = this.checker.getSignatureFromDeclaration(node);
253
+ if (!signature) {
254
+ return undefined;
255
+ }
256
+
257
+ methodName = node.name ? node.name.getText() : "<anonymous>";
258
+ declaringType = Type.unknownType as Type.FullyQualified;
259
+
260
+ // Get type parameters from node
261
+ if (node.typeParameters) {
262
+ for (const tp of node.typeParameters) {
263
+ declaredFormalTypeNames.push(tp.name.getText());
264
+ }
265
+ }
266
+ } else {
267
+ // For other node types, return undefined
268
+ return undefined;
269
+ }
270
+
271
+ // Common logic for all method types
272
+ const returnType = signature.getReturnType();
273
+ const parameters = signature.getParameters();
274
+ const parameterTypes: Type[] = [];
275
+ const parameterNames: string[] = [];
276
+
277
+ for (const param of parameters) {
278
+ parameterNames.push(param.getName());
279
+ const paramType = this.checker.getTypeOfSymbolAtLocation(param, node);
280
+ parameterTypes.push(this.getType(paramType));
281
+ }
282
+
283
+ // Create the Type.Method object
284
+ return Object.assign(new NonDraftableType(), {
285
+ kind: Type.Kind.Method,
286
+ declaringType: declaringType,
287
+ name: methodName,
288
+ returnType: this.getType(returnType),
289
+ parameterNames: parameterNames,
290
+ parameterTypes: parameterTypes,
291
+ thrownExceptions: [], // JavaScript doesn't have checked exceptions
292
+ annotations: [],
293
+ defaultValue: undefined,
294
+ declaredFormalTypeNames: declaredFormalTypeNames,
295
+ toJSON: function () {
296
+ return Type.signature(this);
297
+ }
298
+ }) as Type.Method;
299
+ }
300
+
301
+ /**
302
+ * Create a JavaType.Array from a TypeScript array type
303
+ */
304
+ private createArrayType(type: ts.TypeReference): Type.Array {
305
+ // Get the element type (type argument of Array<T>)
306
+ const typeArgs = this.checker.getTypeArguments(type);
307
+ const elemType = typeArgs.length > 0 ? this.getType(typeArgs[0]) : Type.unknownType;
308
+
309
+ return Object.assign(new NonDraftableType(), {
310
+ kind: Type.Kind.Array,
311
+ elemType: elemType,
312
+ annotations: [],
313
+ toJSON: function () {
314
+ return Type.signature(this);
315
+ }
316
+ }) as Type.Array;
317
+ }
318
+
319
+ /**
320
+ * Get the fully qualified name for a TypeScript type.
321
+ * Format: "module-specifier.TypeName" (e.g., "@mui/material.Button", "src/components/Button.Button")
322
+ */
323
+ private getFullyQualifiedName(type: ts.Type): string {
324
+ const symbol = type.getSymbol?.();
325
+ if (!symbol) {
326
+ return "unknown";
327
+ }
328
+
329
+ const typeName = symbol.getName();
330
+ const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
331
+ if (!declaration) {
332
+ // No declaration - might be a built-in or synthetic type
333
+ if (builtInTypes.has(typeName)) {
334
+ return `lib.${typeName}`;
335
+ }
336
+ return typeName;
337
+ }
338
+
339
+ const sourceFile = declaration.getSourceFile();
340
+ const fileName = sourceFile.fileName;
341
+
342
+ // Check if this is a test file (snowflake ID as filename)
343
+ // Test files are generated with numeric IDs like "672087069480189952.ts"
344
+ if (/^\d+\.(ts|tsx|js|jsx)$/.test(fileName)) {
345
+ // For test files, just return the type name without module prefix
346
+ return typeName;
347
+ }
348
+
349
+ // Check if this is from TypeScript's lib files (lib.d.ts, lib.dom.d.ts, etc.)
350
+ if (fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.")) {
351
+ return `lib.${typeName}`;
352
+ }
353
+
354
+ // Check if this is from an external module (node_modules or .d.ts)
355
+ if (sourceFile.isDeclarationFile || fileName.includes("node_modules")) {
356
+ const packageName = this.extractPackageName(fileName);
357
+ if (packageName) {
358
+ // Special handling for @types/node - these are Node.js built-in modules
359
+ // and should be mapped to "node.*" instead of "@types/node.*"
360
+ if (packageName === "@types/node") {
361
+ // Extract the module name from the file path
362
+ // e.g., /node_modules/@types/node/assert.d.ts -> assert
363
+ // e.g., /node_modules/@types/node/fs/promises.d.ts -> fs/promises
364
+ const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
365
+ if (nodeMatch) {
366
+ const modulePath = nodeMatch[1];
367
+ // For default exports from Node modules, we want the module to be the "class"
368
+ // But we still need to include the type name for proper identification
369
+ if (typeName === "default" || typeName === modulePath) {
370
+ // This is likely the default export, just use the module name
371
+ return `node.${modulePath}`;
372
+ }
373
+ // For named exports, include both module and type name
374
+ if (modulePath.includes('/')) {
375
+ return `node.${modulePath.replace(/\//g, '.')}.${typeName}`;
376
+ }
377
+ return `node.${modulePath}.${typeName}`;
378
+ }
379
+ // Fallback for @types/node types that don't match the pattern
380
+ return `node.${typeName}`;
381
+ }
382
+ return `${packageName}.${typeName}`;
383
+ }
384
+ }
385
+
386
+ // For local files, use relative path from project root
387
+ const relativePath = this.getRelativeModulePath(fileName);
388
+ return `${relativePath}.${typeName}`;
389
+ }
390
+
391
+ /**
392
+ * Extract package name from a node_modules path.
393
+ * Examples:
394
+ * - /path/to/project/node_modules/react/index.d.ts -> "react"
395
+ * - /path/to/project/node_modules/@mui/material/Button/index.d.ts -> "@mui/material"
396
+ */
397
+ private extractPackageName(fileName: string): string | null {
398
+ const match = fileName.match(/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/);
399
+ return match ? match[1] : null;
400
+ }
401
+
402
+ /**
403
+ * Get relative module path from project root.
404
+ * Removes file extension and uses forward slashes.
405
+ */
406
+ private getRelativeModulePath(fileName: string): string {
407
+ // Remove project root and normalize path
408
+ let relativePath = fileName;
409
+ if (fileName.startsWith(this.projectRoot)) {
410
+ relativePath = fileName.slice(this.projectRoot.length);
411
+ }
412
+
413
+ // Remove leading slash and file extension
414
+ relativePath = relativePath.replace(/^\//, '').replace(/\.[^/.]+$/, '');
415
+
416
+ // Convert backslashes to forward slashes (for Windows)
417
+ relativePath = relativePath.replace(/\\/g, '/');
418
+
419
+ return relativePath;
420
+ }
421
+
422
+ /**
423
+ * Create an empty JavaType.Class shell from a TypeScript type.
424
+ * The shell will be populated later to handle circular references.
425
+ */
426
+ private createEmptyClassType(type: ts.Type): Type.Class {
427
+ // Use our custom getFullyQualifiedName method for consistent naming
428
+ let fullyQualifiedName = this.getFullyQualifiedName(type);
429
+
430
+ // If getFullyQualifiedName returned unknown, fall back to TypeScript's method
431
+ if (fullyQualifiedName === "unknown") {
432
+ const symbol = type.symbol;
433
+ fullyQualifiedName = symbol ? this.checker.getFullyQualifiedName(symbol) : `<anonymous>${this.checker.typeToString(type)}`;
434
+
435
+ // Fix FQN for types from @types packages
436
+ // TypeScript returns "_.LoDashStatic" but we want "@types/lodash.LoDashStatic"
437
+ if (symbol && symbol.declarations && symbol.declarations.length > 0) {
438
+ const sourceFile = symbol.declarations[0].getSourceFile();
439
+ const fileName = sourceFile.fileName;
440
+ // Check if this is from @types package
441
+ const typesMatch = fileName.match(/node_modules\/@types\/([^/]+)/);
442
+ if (typesMatch) {
443
+ const packageName = typesMatch[1];
444
+ // Special handling for @types/node - use "node" prefix instead
445
+ if (packageName === "node") {
446
+ // Extract the module name from the file path if possible
447
+ const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
448
+ if (nodeMatch) {
449
+ const modulePath = nodeMatch[1];
450
+ // Replace the module specifier with node.module
451
+ fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.${modulePath}.`);
452
+ } else {
453
+ // Fallback: just use "node" prefix
454
+ fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.`);
455
+ }
456
+ } else {
457
+ // Replace the module specifier part with @types/package
458
+ fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `@types/${packageName}.`);
459
+ }
460
+ }
461
+ }
462
+ }
463
+
464
+ // Determine the class kind based on symbol flags
465
+ let classKind = Type.Class.Kind.Interface; // Default to interface
466
+ const symbol = type.getSymbol?.();
467
+ if (symbol) {
468
+ if (symbol.flags & ts.SymbolFlags.Class) {
469
+ classKind = Type.Class.Kind.Class;
470
+ } else if (symbol.flags & ts.SymbolFlags.Enum) {
471
+ classKind = Type.Class.Kind.Enum;
472
+ } else if (symbol.flags & ts.SymbolFlags.Interface) {
473
+ classKind = Type.Class.Kind.Interface;
474
+ }
475
+ }
476
+
477
+ // Create empty class type shell (no members yet to avoid recursion)
478
+ return Object.assign(new NonDraftableType(), {
479
+ kind: Type.Kind.Class,
480
+ classKind: classKind,
481
+ fullyQualifiedName: fullyQualifiedName,
482
+ typeParameters: [],
483
+ annotations: [],
484
+ interfaces: [],
485
+ members: [],
486
+ methods: [],
487
+ toJSON: function () {
488
+ return Type.signature(this);
489
+ }
490
+ }) as Type.Class;
82
491
  }
83
492
 
84
- private createType(type: ts.Type, cacheKey: number): JavaType {
85
- const signature = this.checker.typeToString(type);
493
+ /**
494
+ * Populates the class type with members, methods, heritage, and type parameters
495
+ * Since the shell is already in the cache, any recursive references will find it
496
+ */
497
+ private populateClassType(classType: Type.Class, type: ts.Type): void {
498
+ const symbol = type.getSymbol?.();
499
+
500
+ // Try to get base types using TypeScript's getBaseTypes API
501
+ // This works for both local and external types (from node_modules)
502
+ if (type.flags & ts.TypeFlags.Object) {
503
+ let baseTypes: ts.Type[] | undefined;
504
+
505
+ // Check if this is a class or interface type that supports getBaseTypes
506
+ const objectType = type as ts.ObjectType;
507
+ if (objectType.objectFlags & (ts.ObjectFlags.Class | ts.ObjectFlags.Interface)) {
508
+ try {
509
+ baseTypes = (this.checker as any).getBaseTypes?.(type as ts.InterfaceType);
510
+ } catch (e) {
511
+ // getBaseTypes might fail for some types, fall back to declaration-based extraction
512
+ }
513
+ } else if (symbol) {
514
+ // For constructor functions or type references, we need to get the actual class type
515
+ // Try to get the type of the class itself (not the constructor or instance)
516
+ const classSymbol = symbol.flags & ts.SymbolFlags.Alias ?
517
+ this.checker.getAliasedSymbol(symbol) : symbol;
518
+
519
+ if (classSymbol && classSymbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) {
520
+ // Get the type of the class declaration itself
521
+ const declaredType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
522
+ if (declaredType && declaredType !== type) {
523
+ try {
524
+ baseTypes = (this.checker as any).getBaseTypes?.(declaredType as ts.InterfaceType);
525
+ } catch (e) {
526
+ // getBaseTypes might fail, fall back to declaration-based extraction
527
+ }
528
+ }
529
+ } else if (classSymbol && classSymbol.valueDeclaration && ts.isClassDeclaration(classSymbol.valueDeclaration)) {
530
+ // Handle the case where the symbol is for a class value (constructor function)
531
+ // Get the instance type of the class
532
+ const instanceType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
533
+ if (instanceType && instanceType !== type) {
534
+ try {
535
+ baseTypes = (this.checker as any).getBaseTypes?.(instanceType as ts.InterfaceType);
536
+ } catch (e) {
537
+ // getBaseTypes might fail, fall back to declaration-based extraction
538
+ }
539
+ }
540
+ }
541
+ }
542
+
543
+ if (baseTypes && baseTypes.length > 0) {
544
+ // For classes, the first base type is usually the superclass
545
+ // Additional base types are interfaces
546
+ if (classType.classKind === Type.Class.Kind.Class) {
547
+ const firstBase = this.getType(baseTypes[0]);
548
+ if (Type.isClass(firstBase)) {
549
+ (classType as any).supertype = firstBase;
550
+ }
551
+ // Rest are interfaces
552
+ for (let i = 1; i < baseTypes.length; i++) {
553
+ const interfaceType = this.getType(baseTypes[i]);
554
+ if (Type.isClass(interfaceType)) {
555
+ classType.interfaces.push(interfaceType);
556
+ }
557
+ }
558
+ } else {
559
+ // For interfaces, all base types are extended interfaces
560
+ for (const baseType of baseTypes) {
561
+ const interfaceType = this.getType(baseType);
562
+ if (Type.isClass(interfaceType)) {
563
+ classType.interfaces.push(interfaceType);
564
+ }
565
+ }
566
+ }
567
+ }
568
+ }
569
+
570
+ // Extract type parameters from declarations (not provided by getBaseTypes)
571
+ if (symbol?.declarations) {
572
+ for (const declaration of symbol.declarations) {
573
+ if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) {
574
+ // Extract type parameters
575
+ if (declaration.typeParameters) {
576
+ for (const tp of declaration.typeParameters) {
577
+ const tpType = this.checker.getTypeAtLocation(tp);
578
+ classType.typeParameters.push(this.getType(tpType));
579
+ }
580
+ }
581
+ break; // Only process the first declaration
582
+ }
583
+ }
584
+ }
585
+
586
+ // Get properties and methods
587
+ const properties = this.checker.getPropertiesOfType(type);
588
+ for (const prop of properties) {
589
+ const declaration = prop.valueDeclaration || prop.declarations?.[0];
590
+ if (!declaration) {
591
+ // Skip properties without declarations (synthetic/built-in properties)
592
+ continue;
593
+ }
594
+
595
+ if (prop.flags & ts.SymbolFlags.Method) {
596
+ // TODO: Create Type.Method when method support is added
597
+ // For now, skip methods
598
+ continue;
599
+ } else {
600
+ // Create Type.Variable for fields/properties
601
+ const propType = this.checker.getTypeOfSymbolAtLocation(prop, declaration);
602
+ const variable: Type.Variable = Object.assign(new NonDraftableType(), {
603
+ kind: Type.Kind.Variable,
604
+ name: prop.getName(),
605
+ owner: classType, // Cyclic reference to the containing class (already in cache)
606
+ type: this.getType(propType), // This will find classType in cache if it's recursive
607
+ annotations: [],
608
+ toJSON: function () {
609
+ return Type.signature(this);
610
+ }
611
+ }) as Type.Variable;
612
+ classType.members.push(variable);
613
+ }
614
+ }
615
+ }
616
+
617
+
618
+ /**
619
+ * Note: Object/Class/Interface types are handled in getType() to properly manage circular references
620
+ * This method should only be called for primitive and unknown types
621
+ */
622
+ private createPrimitiveOrUnknownType(type: ts.Type): Type {
623
+ // Check for literals first
86
624
  if (type.isLiteral()) {
87
625
  if (type.isNumberLiteral()) {
88
- return JavaType.Primitive.Double;
626
+ return Type.Primitive.Double;
89
627
  } else if (type.isStringLiteral()) {
90
- return JavaType.Primitive.String;
628
+ return Type.Primitive.String;
91
629
  }
92
630
  }
93
631
 
632
+ // Check for primitive types
94
633
  if (type.flags === ts.TypeFlags.Null) {
95
- return JavaType.Primitive.Null;
634
+ return Type.Primitive.Null;
96
635
  } else if (type.flags === ts.TypeFlags.Undefined) {
97
- return JavaType.Primitive.None;
636
+ return Type.Primitive.None;
98
637
  } else if (
99
638
  type.flags === ts.TypeFlags.Number ||
100
639
  type.flags === ts.TypeFlags.NumberLiteral ||
101
640
  type.flags === ts.TypeFlags.NumberLike
102
641
  ) {
103
- return JavaType.Primitive.Double;
642
+ return Type.Primitive.Double;
104
643
  } else if (
105
644
  type.flags === ts.TypeFlags.String ||
106
645
  type.flags === ts.TypeFlags.StringLiteral ||
107
646
  type.flags === ts.TypeFlags.StringLike
108
647
  ) {
109
- return JavaType.Primitive.String;
648
+ return Type.Primitive.String;
110
649
  } else if (type.flags === ts.TypeFlags.Void) {
111
- return JavaType.Primitive.Void;
650
+ return Type.Primitive.Void;
112
651
  } else if (
113
652
  type.flags === ts.TypeFlags.BigInt ||
114
653
  type.flags === ts.TypeFlags.BigIntLiteral ||
115
654
  type.flags === ts.TypeFlags.BigIntLike
116
655
  ) {
117
- return JavaType.Primitive.Long;
656
+ return Type.Primitive.Long;
118
657
  } else if (
119
658
  (type.symbol !== undefined && type.symbol === this.regExpSymbol) ||
120
659
  this.checker.typeToString(type) === "RegExp"
121
660
  ) {
122
- return JavaType.Primitive.String;
661
+ return Type.Primitive.String;
123
662
  }
124
663
 
125
664
  /**
@@ -131,23 +670,19 @@ export class JavaScriptTypeMapping {
131
670
  type.flags & ts.TypeFlags.BooleanLiteral ||
132
671
  type.flags & ts.TypeFlags.BooleanLike
133
672
  ) {
134
- return JavaType.Primitive.Boolean;
673
+ return Type.Primitive.Boolean;
135
674
  }
136
675
 
137
- // if (ts.isRegularExpressionLiteral(node)) {
138
- // return JavaType.Primitive.String;
139
- // }
676
+ // Check for type aliases that may resolve to primitives
677
+ const symbol = type.getSymbol?.();
678
+ if (symbol && symbol.flags & ts.SymbolFlags.TypeAlias) {
679
+ // Type aliases may resolve to primitive types
680
+ const aliasedType = this.checker.getDeclaredTypeOfSymbol(symbol);
681
+ if (aliasedType !== type) {
682
+ return this.getType(aliasedType);
683
+ }
684
+ }
140
685
 
141
- return JavaType.unknownType;
686
+ return Type.unknownType;
142
687
  }
143
688
  }
144
-
145
- RpcCodecs.registerCodec(JavaType.Kind.Primitive, {
146
- async rpcSend(after: JavaType.Primitive, q: RpcSendQueue): Promise<void> {
147
- await q.getAndSend(after, p => p.keyword);
148
- },
149
- async rpcReceive(before: JavaType.Primitive, q: RpcReceiveQueue): Promise<JavaType.Primitive> {
150
- const keyword: string = await q.receive(before.keyword);
151
- return JavaType.Primitive.fromKeyword(keyword)!;
152
- }
153
- });