@openrewrite/rewrite 8.62.0 → 8.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) 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 +506 -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/queue.d.ts +0 -3
  47. package/dist/rpc/queue.d.ts.map +1 -1
  48. package/dist/rpc/queue.js +6 -6
  49. package/dist/rpc/queue.js.map +1 -1
  50. package/dist/rpc/request/parse.d.ts.map +1 -1
  51. package/dist/rpc/request/parse.js.map +1 -1
  52. package/dist/rpc/rewrite-rpc.d.ts +1 -0
  53. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  54. package/dist/rpc/rewrite-rpc.js +1 -1
  55. package/dist/rpc/rewrite-rpc.js.map +1 -1
  56. package/dist/rpc/server.js +1 -1
  57. package/dist/test/rewrite-test.d.ts +1 -1
  58. package/dist/test/rewrite-test.d.ts.map +1 -1
  59. package/dist/test/rewrite-test.js +18 -2
  60. package/dist/test/rewrite-test.js.map +1 -1
  61. package/dist/text/tree.d.ts +0 -2
  62. package/dist/text/tree.d.ts.map +1 -1
  63. package/dist/text/tree.js +4 -17
  64. package/dist/text/tree.js.map +1 -1
  65. package/dist/tree.d.ts +1 -0
  66. package/dist/tree.d.ts.map +1 -1
  67. package/dist/tree.js +14 -0
  68. package/dist/tree.js.map +1 -1
  69. package/dist/version.txt +1 -1
  70. package/package.json +3 -3
  71. package/src/java/rpc.ts +18 -15
  72. package/src/java/tree.ts +68 -38
  73. package/src/java/type.ts +475 -74
  74. package/src/java/visitor.ts +45 -39
  75. package/src/javascript/assertions.ts +52 -1
  76. package/src/javascript/format.ts +4 -4
  77. package/src/javascript/parser.ts +51 -18
  78. package/src/javascript/rpc.ts +8 -10
  79. package/src/javascript/tree.ts +34 -35
  80. package/src/javascript/type-mapping.ts +519 -47
  81. package/src/javascript/visitor.ts +5 -5
  82. package/src/json/tree.ts +1 -2
  83. package/src/reference.ts +1 -1
  84. package/src/rpc/queue.ts +8 -12
  85. package/src/rpc/request/parse.ts +1 -1
  86. package/src/rpc/rewrite-rpc.ts +3 -2
  87. package/src/rpc/server.ts +1 -1
  88. package/src/test/rewrite-test.ts +19 -3
  89. package/src/text/tree.ts +0 -1
  90. 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,547 @@ 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, use the symbol's parent or module
177
+ const parent = (symbol as any).parent;
178
+ if (parent) {
179
+ const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
180
+ declaringType = this.getType(parentType) as Type.FullyQualified;
181
+ } else {
182
+ declaringType = Type.unknownType as Type.FullyQualified;
183
+ }
184
+ } else {
185
+ methodName = symbol.getName();
186
+ declaringType = Type.unknownType as Type.FullyQualified;
187
+ }
188
+
189
+ // Get type parameters from signature
190
+ const typeParameters = signature.getTypeParameters();
191
+ if (typeParameters) {
192
+ for (const tp of typeParameters) {
193
+ declaredFormalTypeNames.push(tp.symbol.getName());
194
+ }
195
+ }
196
+ } else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
197
+ // For method declarations
198
+ const symbol = this.checker.getSymbolAtLocation(node.name!);
199
+ if (!symbol) {
200
+ return undefined;
201
+ }
202
+
203
+ signature = this.checker.getSignatureFromDeclaration(node);
204
+ if (!signature) {
205
+ return undefined;
206
+ }
207
+
208
+ methodName = symbol.getName();
209
+
210
+ // Get the declaring type (the class/interface that contains this method)
211
+ const parent = node.parent;
212
+ if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isObjectLiteralExpression(parent)) {
213
+ const parentType = this.checker.getTypeAtLocation(parent);
214
+ declaringType = this.getType(parentType) as Type.FullyQualified;
215
+ } else {
216
+ declaringType = Type.unknownType as Type.FullyQualified;
217
+ }
218
+
219
+ // Get type parameters from node
220
+ if (node.typeParameters) {
221
+ for (const tp of node.typeParameters) {
222
+ declaredFormalTypeNames.push(tp.name.getText());
223
+ }
224
+ }
225
+ } else if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
226
+ // For function declarations/expressions
227
+ signature = this.checker.getSignatureFromDeclaration(node);
228
+ if (!signature) {
229
+ return undefined;
230
+ }
231
+
232
+ methodName = node.name ? node.name.getText() : "<anonymous>";
233
+ declaringType = Type.unknownType as Type.FullyQualified;
234
+
235
+ // Get type parameters from node
236
+ if (node.typeParameters) {
237
+ for (const tp of node.typeParameters) {
238
+ declaredFormalTypeNames.push(tp.name.getText());
239
+ }
240
+ }
241
+ } else {
242
+ // For other node types, return undefined
243
+ return undefined;
244
+ }
245
+
246
+ // Common logic for all method types
247
+ const returnType = signature.getReturnType();
248
+ const parameters = signature.getParameters();
249
+ const parameterTypes: Type[] = [];
250
+ const parameterNames: string[] = [];
251
+
252
+ for (const param of parameters) {
253
+ parameterNames.push(param.getName());
254
+ const paramType = this.checker.getTypeOfSymbolAtLocation(param, node);
255
+ parameterTypes.push(this.getType(paramType));
256
+ }
257
+
258
+ // Create the Type.Method object
259
+ return Object.assign(new NonDraftableType(), {
260
+ kind: Type.Kind.Method,
261
+ declaringType: declaringType,
262
+ name: methodName,
263
+ returnType: this.getType(returnType),
264
+ parameterNames: parameterNames,
265
+ parameterTypes: parameterTypes,
266
+ thrownExceptions: [], // JavaScript doesn't have checked exceptions
267
+ annotations: [],
268
+ defaultValue: undefined,
269
+ declaredFormalTypeNames: declaredFormalTypeNames,
270
+ toJSON: function () {
271
+ return Type.signature(this);
272
+ }
273
+ }) as Type.Method;
274
+ }
275
+
276
+ /**
277
+ * Create a JavaType.Array from a TypeScript array type
278
+ */
279
+ private createArrayType(type: ts.TypeReference): Type.Array {
280
+ // Get the element type (type argument of Array<T>)
281
+ const typeArgs = this.checker.getTypeArguments(type);
282
+ const elemType = typeArgs.length > 0 ? this.getType(typeArgs[0]) : Type.unknownType;
283
+
284
+ return Object.assign(new NonDraftableType(), {
285
+ kind: Type.Kind.Array,
286
+ elemType: elemType,
287
+ annotations: [],
288
+ toJSON: function () {
289
+ return Type.signature(this);
290
+ }
291
+ }) as Type.Array;
292
+ }
293
+
294
+ /**
295
+ * Get the fully qualified name for a TypeScript type.
296
+ * Format: "module-specifier.TypeName" (e.g., "@mui/material.Button", "src/components/Button.Button")
297
+ */
298
+ private getFullyQualifiedName(type: ts.Type): string {
299
+ const symbol = type.getSymbol?.();
300
+ if (!symbol) {
301
+ return "unknown";
302
+ }
303
+
304
+ const typeName = symbol.getName();
305
+ const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
306
+ if (!declaration) {
307
+ // No declaration - might be a built-in or synthetic type
308
+ if (builtInTypes.has(typeName)) {
309
+ return `lib.${typeName}`;
310
+ }
311
+ return typeName;
312
+ }
313
+
314
+ const sourceFile = declaration.getSourceFile();
315
+ const fileName = sourceFile.fileName;
316
+
317
+ // Check if this is a test file (snowflake ID as filename)
318
+ // Test files are generated with numeric IDs like "672087069480189952.ts"
319
+ if (/^\d+\.(ts|tsx|js|jsx)$/.test(fileName)) {
320
+ // For test files, just return the type name without module prefix
321
+ return typeName;
322
+ }
323
+
324
+ // Check if this is from TypeScript's lib files (lib.d.ts, lib.dom.d.ts, etc.)
325
+ if (fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.")) {
326
+ return `lib.${typeName}`;
327
+ }
328
+
329
+ // Check if this is from an external module (node_modules or .d.ts)
330
+ if (sourceFile.isDeclarationFile || fileName.includes("node_modules")) {
331
+ const packageName = this.extractPackageName(fileName);
332
+ if (packageName) {
333
+ return `${packageName}.${typeName}`;
334
+ }
335
+ }
336
+
337
+ // For local files, use relative path from project root
338
+ const relativePath = this.getRelativeModulePath(fileName);
339
+ return `${relativePath}.${typeName}`;
340
+ }
341
+
342
+ /**
343
+ * Extract package name from a node_modules path.
344
+ * Examples:
345
+ * - /path/to/project/node_modules/react/index.d.ts -> "react"
346
+ * - /path/to/project/node_modules/@mui/material/Button/index.d.ts -> "@mui/material"
347
+ */
348
+ private extractPackageName(fileName: string): string | null {
349
+ const match = fileName.match(/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/);
350
+ return match ? match[1] : null;
82
351
  }
83
352
 
84
- private createType(type: ts.Type, cacheKey: number): JavaType {
85
- const signature = this.checker.typeToString(type);
353
+ /**
354
+ * Get relative module path from project root.
355
+ * Removes file extension and uses forward slashes.
356
+ */
357
+ private getRelativeModulePath(fileName: string): string {
358
+ // Remove project root and normalize path
359
+ let relativePath = fileName;
360
+ if (fileName.startsWith(this.projectRoot)) {
361
+ relativePath = fileName.slice(this.projectRoot.length);
362
+ }
363
+
364
+ // Remove leading slash and file extension
365
+ relativePath = relativePath.replace(/^\//, '').replace(/\.[^/.]+$/, '');
366
+
367
+ // Convert backslashes to forward slashes (for Windows)
368
+ relativePath = relativePath.replace(/\\/g, '/');
369
+
370
+ return relativePath;
371
+ }
372
+
373
+ /**
374
+ * Create an empty JavaType.Class shell from a TypeScript type.
375
+ * The shell will be populated later to handle circular references.
376
+ */
377
+ private createEmptyClassType(type: ts.Type): Type.Class {
378
+ // Use our custom getFullyQualifiedName method for consistent naming
379
+ let fullyQualifiedName = this.getFullyQualifiedName(type);
380
+
381
+ // If getFullyQualifiedName returned unknown, fall back to TypeScript's method
382
+ if (fullyQualifiedName === "unknown") {
383
+ const symbol = type.symbol;
384
+ fullyQualifiedName = symbol ? this.checker.getFullyQualifiedName(symbol) : `<anonymous>${this.checker.typeToString(type)}`;
385
+
386
+ // Fix FQN for types from @types packages
387
+ // TypeScript returns "_.LoDashStatic" but we want "@types/lodash.LoDashStatic"
388
+ if (symbol && symbol.declarations && symbol.declarations.length > 0) {
389
+ const sourceFile = symbol.declarations[0].getSourceFile();
390
+ const fileName = sourceFile.fileName;
391
+ // Check if this is from @types package
392
+ const typesMatch = fileName.match(/node_modules\/@types\/([^/]+)/);
393
+ if (typesMatch) {
394
+ const packageName = typesMatch[1];
395
+ // Replace the module specifier part with @types/package
396
+ fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `@types/${packageName}.`);
397
+ }
398
+ }
399
+ }
400
+
401
+ // Determine the class kind based on symbol flags
402
+ let classKind = Type.Class.Kind.Interface; // Default to interface
403
+ const symbol = type.getSymbol?.();
404
+ if (symbol) {
405
+ if (symbol.flags & ts.SymbolFlags.Class) {
406
+ classKind = Type.Class.Kind.Class;
407
+ } else if (symbol.flags & ts.SymbolFlags.Enum) {
408
+ classKind = Type.Class.Kind.Enum;
409
+ } else if (symbol.flags & ts.SymbolFlags.Interface) {
410
+ classKind = Type.Class.Kind.Interface;
411
+ }
412
+ }
413
+
414
+ // Create empty class type shell (no members yet to avoid recursion)
415
+ return Object.assign(new NonDraftableType(), {
416
+ kind: Type.Kind.Class,
417
+ classKind: classKind,
418
+ fullyQualifiedName: fullyQualifiedName,
419
+ typeParameters: [],
420
+ annotations: [],
421
+ interfaces: [],
422
+ members: [],
423
+ methods: [],
424
+ toJSON: function () {
425
+ return Type.signature(this);
426
+ }
427
+ }) as Type.Class;
428
+ }
429
+
430
+ /**
431
+ * Populates the class type with members, methods, heritage, and type parameters
432
+ * Since the shell is already in the cache, any recursive references will find it
433
+ */
434
+ private populateClassType(classType: Type.Class, type: ts.Type): void {
435
+ const symbol = type.getSymbol?.();
436
+
437
+ // Try to get base types using TypeScript's getBaseTypes API
438
+ // This works for both local and external types (from node_modules)
439
+ if (type.flags & ts.TypeFlags.Object) {
440
+ let baseTypes: ts.Type[] | undefined;
441
+
442
+ // Check if this is a class or interface type that supports getBaseTypes
443
+ const objectType = type as ts.ObjectType;
444
+ if (objectType.objectFlags & (ts.ObjectFlags.Class | ts.ObjectFlags.Interface)) {
445
+ try {
446
+ baseTypes = (this.checker as any).getBaseTypes?.(type as ts.InterfaceType);
447
+ } catch (e) {
448
+ // getBaseTypes might fail for some types, fall back to declaration-based extraction
449
+ }
450
+ } else if (symbol) {
451
+ // For constructor functions or type references, we need to get the actual class type
452
+ // Try to get the type of the class itself (not the constructor or instance)
453
+ const classSymbol = symbol.flags & ts.SymbolFlags.Alias ?
454
+ this.checker.getAliasedSymbol(symbol) : symbol;
455
+
456
+ if (classSymbol && classSymbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) {
457
+ // Get the type of the class declaration itself
458
+ const declaredType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
459
+ if (declaredType && declaredType !== type) {
460
+ try {
461
+ baseTypes = (this.checker as any).getBaseTypes?.(declaredType as ts.InterfaceType);
462
+ } catch (e) {
463
+ // getBaseTypes might fail, fall back to declaration-based extraction
464
+ }
465
+ }
466
+ } else if (classSymbol && classSymbol.valueDeclaration && ts.isClassDeclaration(classSymbol.valueDeclaration)) {
467
+ // Handle the case where the symbol is for a class value (constructor function)
468
+ // Get the instance type of the class
469
+ const instanceType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
470
+ if (instanceType && instanceType !== type) {
471
+ try {
472
+ baseTypes = (this.checker as any).getBaseTypes?.(instanceType as ts.InterfaceType);
473
+ } catch (e) {
474
+ // getBaseTypes might fail, fall back to declaration-based extraction
475
+ }
476
+ }
477
+ }
478
+ }
479
+
480
+ if (baseTypes && baseTypes.length > 0) {
481
+ // For classes, the first base type is usually the superclass
482
+ // Additional base types are interfaces
483
+ if (classType.classKind === Type.Class.Kind.Class) {
484
+ const firstBase = this.getType(baseTypes[0]);
485
+ if (Type.isClass(firstBase)) {
486
+ (classType as any).supertype = firstBase;
487
+ }
488
+ // Rest are interfaces
489
+ for (let i = 1; i < baseTypes.length; i++) {
490
+ const interfaceType = this.getType(baseTypes[i]);
491
+ if (Type.isClass(interfaceType)) {
492
+ classType.interfaces.push(interfaceType);
493
+ }
494
+ }
495
+ } else {
496
+ // For interfaces, all base types are extended interfaces
497
+ for (const baseType of baseTypes) {
498
+ const interfaceType = this.getType(baseType);
499
+ if (Type.isClass(interfaceType)) {
500
+ classType.interfaces.push(interfaceType);
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+
507
+ // Extract type parameters from declarations (not provided by getBaseTypes)
508
+ if (symbol?.declarations) {
509
+ for (const declaration of symbol.declarations) {
510
+ if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) {
511
+ // Extract type parameters
512
+ if (declaration.typeParameters) {
513
+ for (const tp of declaration.typeParameters) {
514
+ const tpType = this.checker.getTypeAtLocation(tp);
515
+ classType.typeParameters.push(this.getType(tpType));
516
+ }
517
+ }
518
+ break; // Only process the first declaration
519
+ }
520
+ }
521
+ }
522
+
523
+ // Get properties and methods
524
+ const properties = this.checker.getPropertiesOfType(type);
525
+ for (const prop of properties) {
526
+ const declaration = prop.valueDeclaration || prop.declarations?.[0];
527
+ if (!declaration) {
528
+ // Skip properties without declarations (synthetic/built-in properties)
529
+ continue;
530
+ }
531
+
532
+ if (prop.flags & ts.SymbolFlags.Method) {
533
+ // TODO: Create Type.Method when method support is added
534
+ // For now, skip methods
535
+ continue;
536
+ } else {
537
+ // Create Type.Variable for fields/properties
538
+ const propType = this.checker.getTypeOfSymbolAtLocation(prop, declaration);
539
+ const variable: Type.Variable = Object.assign(new NonDraftableType(), {
540
+ kind: Type.Kind.Variable,
541
+ name: prop.getName(),
542
+ owner: classType, // Cyclic reference to the containing class (already in cache)
543
+ type: this.getType(propType), // This will find classType in cache if it's recursive
544
+ annotations: [],
545
+ toJSON: function () {
546
+ return Type.signature(this);
547
+ }
548
+ }) as Type.Variable;
549
+ classType.members.push(variable);
550
+ }
551
+ }
552
+ }
553
+
554
+
555
+ /**
556
+ * Note: Object/Class/Interface types are handled in getType() to properly manage circular references
557
+ * This method should only be called for primitive and unknown types
558
+ */
559
+ private createPrimitiveOrUnknownType(type: ts.Type): Type {
560
+ // Check for literals first
86
561
  if (type.isLiteral()) {
87
562
  if (type.isNumberLiteral()) {
88
- return JavaType.Primitive.Double;
563
+ return Type.Primitive.Double;
89
564
  } else if (type.isStringLiteral()) {
90
- return JavaType.Primitive.String;
565
+ return Type.Primitive.String;
91
566
  }
92
567
  }
93
568
 
569
+ // Check for primitive types
94
570
  if (type.flags === ts.TypeFlags.Null) {
95
- return JavaType.Primitive.Null;
571
+ return Type.Primitive.Null;
96
572
  } else if (type.flags === ts.TypeFlags.Undefined) {
97
- return JavaType.Primitive.None;
573
+ return Type.Primitive.None;
98
574
  } else if (
99
575
  type.flags === ts.TypeFlags.Number ||
100
576
  type.flags === ts.TypeFlags.NumberLiteral ||
101
577
  type.flags === ts.TypeFlags.NumberLike
102
578
  ) {
103
- return JavaType.Primitive.Double;
579
+ return Type.Primitive.Double;
104
580
  } else if (
105
581
  type.flags === ts.TypeFlags.String ||
106
582
  type.flags === ts.TypeFlags.StringLiteral ||
107
583
  type.flags === ts.TypeFlags.StringLike
108
584
  ) {
109
- return JavaType.Primitive.String;
585
+ return Type.Primitive.String;
110
586
  } else if (type.flags === ts.TypeFlags.Void) {
111
- return JavaType.Primitive.Void;
587
+ return Type.Primitive.Void;
112
588
  } else if (
113
589
  type.flags === ts.TypeFlags.BigInt ||
114
590
  type.flags === ts.TypeFlags.BigIntLiteral ||
115
591
  type.flags === ts.TypeFlags.BigIntLike
116
592
  ) {
117
- return JavaType.Primitive.Long;
593
+ return Type.Primitive.Long;
118
594
  } else if (
119
595
  (type.symbol !== undefined && type.symbol === this.regExpSymbol) ||
120
596
  this.checker.typeToString(type) === "RegExp"
121
597
  ) {
122
- return JavaType.Primitive.String;
598
+ return Type.Primitive.String;
123
599
  }
124
600
 
125
601
  /**
@@ -131,23 +607,19 @@ export class JavaScriptTypeMapping {
131
607
  type.flags & ts.TypeFlags.BooleanLiteral ||
132
608
  type.flags & ts.TypeFlags.BooleanLike
133
609
  ) {
134
- return JavaType.Primitive.Boolean;
610
+ return Type.Primitive.Boolean;
135
611
  }
136
612
 
137
- // if (ts.isRegularExpressionLiteral(node)) {
138
- // return JavaType.Primitive.String;
139
- // }
613
+ // Check for type aliases that may resolve to primitives
614
+ const symbol = type.getSymbol?.();
615
+ if (symbol && symbol.flags & ts.SymbolFlags.TypeAlias) {
616
+ // Type aliases may resolve to primitive types
617
+ const aliasedType = this.checker.getDeclaredTypeOfSymbol(symbol);
618
+ if (aliasedType !== type) {
619
+ return this.getType(aliasedType);
620
+ }
621
+ }
140
622
 
141
- return JavaType.unknownType;
623
+ return Type.unknownType;
142
624
  }
143
625
  }
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
- });