@openrewrite/rewrite 8.62.2 → 8.62.4

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 (95) 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.map +1 -1
  8. package/dist/java/tree.js +0 -87
  9. package/dist/java/tree.js.map +1 -1
  10. package/dist/java/visitor.d.ts +4 -4
  11. package/dist/java/visitor.d.ts.map +1 -1
  12. package/dist/java/visitor.js.map +1 -1
  13. package/dist/javascript/format.d.ts +2 -2
  14. package/dist/javascript/format.d.ts.map +1 -1
  15. package/dist/javascript/format.js.map +1 -1
  16. package/dist/javascript/parser.d.ts +1 -1
  17. package/dist/javascript/parser.d.ts.map +1 -1
  18. package/dist/javascript/parser.js +26 -4
  19. package/dist/javascript/parser.js.map +1 -1
  20. package/dist/javascript/print.d.ts +2 -2
  21. package/dist/javascript/print.d.ts.map +1 -1
  22. package/dist/javascript/print.js.map +1 -1
  23. package/dist/javascript/rpc.js +1 -16
  24. package/dist/javascript/rpc.js.map +1 -1
  25. package/dist/javascript/type-mapping.d.ts +4 -19
  26. package/dist/javascript/type-mapping.d.ts.map +1 -1
  27. package/dist/javascript/type-mapping.js +228 -163
  28. package/dist/javascript/type-mapping.js.map +1 -1
  29. package/dist/javascript/visitor.d.ts +1 -1
  30. package/dist/javascript/visitor.d.ts.map +1 -1
  31. package/dist/javascript/visitor.js.map +1 -1
  32. package/dist/json/print.js.map +1 -1
  33. package/dist/json/rpc.js +46 -17
  34. package/dist/json/rpc.js.map +1 -1
  35. package/dist/json/visitor.d.ts +2 -2
  36. package/dist/json/visitor.d.ts.map +1 -1
  37. package/dist/json/visitor.js.map +1 -1
  38. package/dist/print.d.ts +2 -2
  39. package/dist/print.d.ts.map +1 -1
  40. package/dist/print.js +4 -2
  41. package/dist/print.js.map +1 -1
  42. package/dist/rpc/queue.d.ts +15 -6
  43. package/dist/rpc/queue.d.ts.map +1 -1
  44. package/dist/rpc/queue.js +37 -13
  45. package/dist/rpc/queue.js.map +1 -1
  46. package/dist/rpc/request/generate.d.ts +4 -0
  47. package/dist/rpc/request/generate.d.ts.map +1 -1
  48. package/dist/rpc/request/generate.js +9 -4
  49. package/dist/rpc/request/generate.js.map +1 -1
  50. package/dist/rpc/request/get-object.d.ts +2 -2
  51. package/dist/rpc/request/get-object.d.ts.map +1 -1
  52. package/dist/rpc/request/get-object.js +4 -12
  53. package/dist/rpc/request/get-object.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/request/print.d.ts +3 -4
  57. package/dist/rpc/request/print.d.ts.map +1 -1
  58. package/dist/rpc/request/print.js +5 -6
  59. package/dist/rpc/request/print.js.map +1 -1
  60. package/dist/rpc/request/visit.d.ts +3 -2
  61. package/dist/rpc/request/visit.d.ts.map +1 -1
  62. package/dist/rpc/request/visit.js +5 -4
  63. package/dist/rpc/request/visit.js.map +1 -1
  64. package/dist/rpc/rewrite-rpc.d.ts +3 -3
  65. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  66. package/dist/rpc/rewrite-rpc.js +19 -16
  67. package/dist/rpc/rewrite-rpc.js.map +1 -1
  68. package/dist/test/rewrite-test.js.map +1 -1
  69. package/dist/text/rpc.js +37 -40
  70. package/dist/text/rpc.js.map +1 -1
  71. package/dist/version.txt +1 -1
  72. package/package.json +1 -1
  73. package/src/execution.ts +0 -2
  74. package/src/java/rpc.ts +68 -0
  75. package/src/java/tree.ts +1 -65
  76. package/src/java/visitor.ts +4 -4
  77. package/src/javascript/format.ts +2 -2
  78. package/src/javascript/parser.ts +19 -5
  79. package/src/javascript/print.ts +3 -3
  80. package/src/javascript/rpc.ts +4 -17
  81. package/src/javascript/type-mapping.ts +235 -170
  82. package/src/javascript/visitor.ts +1 -1
  83. package/src/json/print.ts +1 -1
  84. package/src/json/rpc.ts +40 -19
  85. package/src/json/visitor.ts +2 -2
  86. package/src/print.ts +6 -4
  87. package/src/rpc/queue.ts +36 -12
  88. package/src/rpc/request/generate.ts +18 -6
  89. package/src/rpc/request/get-object.ts +6 -13
  90. package/src/rpc/request/parse.ts +1 -1
  91. package/src/rpc/request/print.ts +5 -7
  92. package/src/rpc/request/visit.ts +6 -5
  93. package/src/rpc/rewrite-rpc.ts +24 -19
  94. package/src/test/rewrite-test.ts +1 -1
  95. package/src/text/rpc.ts +33 -37
@@ -34,8 +34,7 @@ export class JavaScriptTypeMapping {
34
34
  private readonly regExpSymbol: ts.Symbol | undefined;
35
35
 
36
36
  constructor(
37
- private readonly checker: ts.TypeChecker,
38
- private readonly projectRoot: string = process.cwd()
37
+ private readonly checker: ts.TypeChecker
39
38
  ) {
40
39
  this.regExpSymbol = checker.resolveName(
41
40
  "RegExp",
@@ -62,15 +61,8 @@ export class JavaScriptTypeMapping {
62
61
  return existing;
63
62
  }
64
63
 
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)
64
+ // Check for class/interface/enum types (including arrays)
65
+ // Arrays in JavaScript are objects with methods, so we treat them as class types
74
66
  const symbol = type.getSymbol?.();
75
67
  if (symbol) {
76
68
  if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface | ts.SymbolFlags.Enum | ts.SymbolFlags.TypeAlias)) {
@@ -169,41 +161,164 @@ export class JavaScriptTypeMapping {
169
161
  // Get the method name
170
162
  if (ts.isPropertyAccessExpression(node.expression)) {
171
163
  methodName = node.expression.name.getText();
164
+
165
+ // Check if the object is an imported symbol
166
+ const objSymbol = this.checker.getSymbolAtLocation(node.expression.expression);
167
+ let isImport = false;
168
+ if (objSymbol) {
169
+ // Only call getAliasedSymbol if the symbol is actually an alias
170
+ if (objSymbol.flags & ts.SymbolFlags.Alias) {
171
+ const aliasedSymbol = this.checker.getAliasedSymbol(objSymbol);
172
+ isImport = aliasedSymbol && aliasedSymbol !== objSymbol;
173
+ }
174
+ }
175
+
172
176
  const exprType = this.checker.getTypeAtLocation(node.expression.expression);
173
- declaringType = this.getType(exprType) as Type.FullyQualified;
177
+ const mappedType = this.getType(exprType);
178
+
179
+ // Handle different types
180
+ if (mappedType && mappedType.kind === Type.Kind.Class) {
181
+ // Update the declaring type with the corrected FQN
182
+ if (isImport && objSymbol) {
183
+ const importName = objSymbol.getName();
184
+ const origFqn = (mappedType as Type.Class).fullyQualifiedName;
185
+ const lastDot = origFqn.lastIndexOf('.');
186
+ if (lastDot > 0) {
187
+ const typeName = origFqn.substring(lastDot + 1);
188
+ declaringType = {
189
+ kind: Type.Kind.Class,
190
+ fullyQualifiedName: `${importName}.${typeName}`
191
+ } as Type.FullyQualified;
192
+ } else {
193
+ declaringType = mappedType as Type.FullyQualified;
194
+ }
195
+ } else {
196
+ declaringType = mappedType as Type.FullyQualified;
197
+ }
198
+ } else {
199
+ // Handle primitive types and other non-class types
200
+ if (mappedType) {
201
+ if ((mappedType as any).keyword === 'String') {
202
+ declaringType = {
203
+ kind: Type.Kind.Class,
204
+ fullyQualifiedName: 'lib.String'
205
+ } as Type.FullyQualified;
206
+ } else if ((mappedType as any).keyword === 'Number') {
207
+ declaringType = {
208
+ kind: Type.Kind.Class,
209
+ fullyQualifiedName: 'lib.Number'
210
+ } as Type.FullyQualified;
211
+ } else {
212
+ // Fallback for other types
213
+ declaringType = mappedType as Type.FullyQualified;
214
+ }
215
+ } else {
216
+ // Default to unknown if we can't determine the type
217
+ declaringType = Type.unknownType as Type.FullyQualified;
218
+ }
219
+ }
220
+
221
+ // For string methods like 'hello'.split(), ensure we have a proper declaring type for primitives
222
+ if (!isImport && declaringType === Type.unknownType) {
223
+ // If the expression type is a primitive string, use lib.String as declaring type
224
+ const typeString = this.checker.typeToString(exprType);
225
+ if (typeString === 'string' || exprType.flags & ts.TypeFlags.String || exprType.flags & ts.TypeFlags.StringLiteral) {
226
+ declaringType = {
227
+ kind: Type.Kind.Class,
228
+ fullyQualifiedName: 'lib.String'
229
+ } as Type.FullyQualified;
230
+ } else if (typeString === 'number' || exprType.flags & ts.TypeFlags.Number || exprType.flags & ts.TypeFlags.NumberLiteral) {
231
+ declaringType = {
232
+ kind: Type.Kind.Class,
233
+ fullyQualifiedName: 'lib.Number'
234
+ } as Type.FullyQualified;
235
+ } else {
236
+ // Fallback for other primitive types or unknown
237
+ declaringType = Type.unknownType as Type.FullyQualified;
238
+ }
239
+ }
240
+
174
241
  } else if (ts.isIdentifier(node.expression)) {
175
242
  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"
243
+
244
+ // Check if this is an import first
245
+ const symbol = this.checker.getSymbolAtLocation(node.expression);
246
+ let moduleSpecifier: string | undefined;
247
+
248
+ if (symbol) {
249
+ // Check if this is an aliased symbol (i.e., an import)
250
+ let aliasedSymbol: ts.Symbol | undefined;
251
+ if (symbol.flags & ts.SymbolFlags.Alias) {
252
+ aliasedSymbol = this.checker.getAliasedSymbol(symbol);
253
+ }
254
+
255
+ // If getAliasedSymbol returns something different, it's an import
256
+ if (aliasedSymbol && aliasedSymbol !== symbol) {
257
+ // This is definitely an imported symbol
258
+ // Now find the import declaration to get the module specifier
259
+ if (symbol.declarations && symbol.declarations.length > 0) {
260
+ let importNode: ts.Node = symbol.declarations[0];
261
+
262
+ // Traverse up to find the ImportDeclaration
263
+ while (importNode && !ts.isImportDeclaration(importNode)) {
264
+ importNode = importNode.parent;
265
+ }
266
+
267
+ if (importNode && ts.isImportDeclaration(importNode)) {
268
+ const importDeclNode = importNode as ts.ImportDeclaration;
269
+ if (ts.isStringLiteral(importDeclNode.moduleSpecifier)) {
270
+ moduleSpecifier = importDeclNode.moduleSpecifier.text;
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ if (moduleSpecifier) {
278
+ // This is an imported function - use the module specifier as declaring type
279
+ if (moduleSpecifier.startsWith('node:')) {
280
+ // Node.js built-in module
190
281
  declaringType = {
191
282
  kind: Type.Kind.Class,
192
- fullyQualifiedName: fqn.substring(0, lastDot)
283
+ fullyQualifiedName: 'node'
193
284
  } as Type.FullyQualified;
285
+ methodName = moduleSpecifier.substring(5); // Remove 'node:' prefix
194
286
  } 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;
287
+ // Regular module import
288
+ declaringType = {
289
+ kind: Type.Kind.Class,
290
+ fullyQualifiedName: moduleSpecifier
291
+ } as Type.FullyQualified;
292
+ methodName = '<default>';
198
293
  }
199
294
  } 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;
295
+ // Fall back to the original logic for non-imported functions
296
+ const exprType = this.checker.getTypeAtLocation(node.expression);
297
+ const funcType = this.getType(exprType);
298
+
299
+ if (funcType && funcType.kind === Type.Kind.Class) {
300
+ const fqn = (funcType as Type.Class).fullyQualifiedName;
301
+ const lastDot = fqn.lastIndexOf('.');
302
+
303
+ if (lastDot > 0) {
304
+ // For functions from modules, use the module part as declaring type
305
+ declaringType = {
306
+ kind: Type.Kind.Class,
307
+ fullyQualifiedName: fqn.substring(0, lastDot)
308
+ } as Type.FullyQualified;
309
+ } else {
310
+ // No dots in the name - the type IS the module itself
311
+ declaringType = funcType as Type.FullyQualified;
312
+ }
205
313
  } else {
206
- declaringType = Type.unknownType as Type.FullyQualified;
314
+ // Try to use the symbol's parent or module
315
+ const parent = (symbol as any).parent;
316
+ if (parent) {
317
+ const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
318
+ declaringType = this.getType(parentType) as Type.FullyQualified;
319
+ } else {
320
+ declaringType = Type.unknownType as Type.FullyQualified;
321
+ }
207
322
  }
208
323
  }
209
324
  } else {
@@ -298,27 +413,12 @@ export class JavaScriptTypeMapping {
298
413
  }) as Type.Method;
299
414
  }
300
415
 
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
416
 
319
417
  /**
320
418
  * Get the fully qualified name for a TypeScript type.
321
- * Format: "module-specifier.TypeName" (e.g., "@mui/material.Button", "src/components/Button.Button")
419
+ * Uses TypeScript's built-in resolution which properly handles things like:
420
+ * - React.Component (not @types/react.Component)
421
+ * - _.LoDashStatic (not @types/lodash.LoDashStatic)
322
422
  */
323
423
  private getFullyQualifiedName(type: ts.Type): string {
324
424
  const symbol = type.getSymbol?.();
@@ -326,140 +426,105 @@ export class JavaScriptTypeMapping {
326
426
  return "unknown";
327
427
  }
328
428
 
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
- }
429
+ // First, check if this symbol is an import/alias
430
+ // For imported types, we want to use the module specifier instead of the file path
431
+ if (symbol.flags & ts.SymbolFlags.Alias) {
432
+ const aliasedSymbol = this.checker.getAliasedSymbol(symbol);
433
+ if (aliasedSymbol && aliasedSymbol !== symbol && symbol.declarations && symbol.declarations.length > 0) {
434
+ // Try to find the import declaration to get the module specifier
435
+ let importNode: ts.Node | undefined = symbol.declarations[0];
436
+
437
+ // Traverse up to find the ImportDeclaration or ImportSpecifier
438
+ while (importNode && importNode.parent && !ts.isImportDeclaration(importNode) && !ts.isImportSpecifier(importNode)) {
439
+ importNode = importNode.parent;
440
+ }
353
441
 
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}`;
442
+ let moduleSpecifier: string | undefined;
443
+
444
+ if (importNode && ts.isImportSpecifier(importNode)) {
445
+ // Named import like: import { ClipLoader } from 'react-spinners'
446
+ // ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
447
+ const namedImports = importNode.parent; // NamedImports
448
+ if (namedImports && ts.isNamedImports(namedImports)) {
449
+ const importClause = namedImports.parent; // ImportClause
450
+ if (importClause && ts.isImportClause(importClause)) {
451
+ const importDecl = importClause.parent; // ImportDeclaration
452
+ if (importDecl && ts.isImportDeclaration(importDecl) && ts.isStringLiteral(importDecl.moduleSpecifier)) {
453
+ moduleSpecifier = importDecl.moduleSpecifier.text;
454
+ }
372
455
  }
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
456
  }
379
- // Fallback for @types/node types that don't match the pattern
380
- return `node.${typeName}`;
457
+ } else if (importNode && ts.isImportDeclaration(importNode)) {
458
+ // Default or namespace import
459
+ if (ts.isStringLiteral(importNode.moduleSpecifier)) {
460
+ moduleSpecifier = importNode.moduleSpecifier.text;
461
+ }
462
+ }
463
+
464
+ if (moduleSpecifier) {
465
+ // Build the fully qualified name from module specifier + symbol name
466
+ const symbolName = symbol.getName();
467
+ return `${moduleSpecifier}.${symbolName}`;
381
468
  }
382
- return `${packageName}.${typeName}`;
383
469
  }
384
470
  }
385
471
 
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
- }
472
+ // Fall back to TypeScript's built-in getFullyQualifiedName
473
+ // This returns names with quotes that we need to clean up
474
+ // e.g., '"React"."Component"' -> 'React.Component'
475
+ const tsQualifiedName = this.checker.getFullyQualifiedName(symbol);
476
+ let cleanedName = tsQualifiedName.replace(/"/g, '');
477
+
478
+ // Check if this is a file path from node_modules (happens with some packages)
479
+ // TypeScript sometimes returns full paths instead of module names
480
+ if (cleanedName.includes('node_modules/')) {
481
+ // Extract the module name from the path
482
+ // Example: /private/var/.../node_modules/react-spinners/src/index.ClipLoader
483
+ // Should become: react-spinners.ClipLoader
484
+ const nodeModulesIndex = cleanedName.indexOf('node_modules/');
485
+ const afterNodeModules = cleanedName.substring(nodeModulesIndex + 'node_modules/'.length);
486
+
487
+ // Split by '/' to get parts of the path
488
+ const pathParts = afterNodeModules.split('/');
489
+
490
+ if (pathParts.length > 0) {
491
+ // First part is the package name (might be scoped like @types)
492
+ let packageName = pathParts[0];
493
+
494
+ // Handle scoped packages
495
+ if (packageName.startsWith('@') && pathParts.length > 1) {
496
+ packageName = `${packageName}/${pathParts[1]}`;
497
+ }
401
498
 
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);
499
+ // Find the symbol name (everything after the last dot in the original cleaned name)
500
+ const lastDotIndex = cleanedName.lastIndexOf('.');
501
+ if (lastDotIndex > 0) {
502
+ const symbolName = cleanedName.substring(lastDotIndex + 1);
503
+ cleanedName = `${packageName}.${symbolName}`;
504
+ } else {
505
+ cleanedName = packageName;
506
+ }
507
+ }
411
508
  }
412
509
 
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, '/');
510
+ // If it's just a simple name without dots, check if it's a built-in type
511
+ if (!cleanedName.includes('.')) {
512
+ if (builtInTypes.has(cleanedName)) {
513
+ return `lib.${cleanedName}`;
514
+ }
515
+ }
418
516
 
419
- return relativePath;
517
+ return cleanedName;
420
518
  }
421
519
 
520
+
422
521
  /**
423
522
  * Create an empty JavaType.Class shell from a TypeScript type.
424
523
  * The shell will be populated later to handle circular references.
425
524
  */
426
525
  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
- }
526
+ // Use our getFullyQualifiedName method which uses TypeScript's built-in resolution
527
+ const fullyQualifiedName = this.getFullyQualifiedName(type);
463
528
 
464
529
  // Determine the class kind based on symbol flags
465
530
  let classKind = Type.Class.Kind.Interface; // Default to interface
@@ -40,7 +40,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
40
40
  }
41
41
 
42
42
  // noinspection JSUnusedLocalSymbols
43
- protected override async visitSpace(space: J.Space, p: P): Promise<J.Space> {
43
+ override async visitSpace(space: J.Space, p: P): Promise<J.Space> {
44
44
  return space;
45
45
  }
46
46
 
package/src/json/print.ts CHANGED
@@ -75,7 +75,7 @@ class JsonPrinter extends JsonVisitor<PrintOutputCapture> {
75
75
  return jsonObject;
76
76
  }
77
77
 
78
- protected async visitSpace(space: Json.Space, p: PrintOutputCapture): Promise<Json.Space> {
78
+ public async visitSpace(space: Json.Space, p: PrintOutputCapture): Promise<Json.Space> {
79
79
  p.append(space.whitespace);
80
80
  for (const comment of space.comments) {
81
81
  await this.visitMarkers(comment.markers, p);
package/src/json/rpc.ts CHANGED
@@ -14,11 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import {JsonVisitor} from "./visitor";
17
- import {asRef, RpcCodec, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
17
+ import {asRef, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
18
18
  import {Json} from "./tree";
19
19
  import {produceAsync} from "../visitor";
20
20
  import {createDraft, Draft, finishDraft} from "immer";
21
- import {TreeKind} from "../tree";
22
21
 
23
22
  class JsonSender extends JsonVisitor<RpcSendQueue> {
24
23
 
@@ -76,7 +75,7 @@ class JsonSender extends JsonVisitor<RpcSendQueue> {
76
75
  return obj;
77
76
  }
78
77
 
79
- protected async visitSpace(space: Json.Space, q: RpcSendQueue): Promise<Json.Space> {
78
+ public async visitSpace(space: Json.Space, q: RpcSendQueue): Promise<Json.Space> {
80
79
  await q.getAndSendList(space, s => s.comments, c => c.text + c.suffix, async c => {
81
80
  await q.getAndSend(c, c2 => c2.multiline);
82
81
  await q.getAndSend(c, c2 => c2.text);
@@ -87,7 +86,7 @@ class JsonSender extends JsonVisitor<RpcSendQueue> {
87
86
  return space;
88
87
  }
89
88
 
90
- protected async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, q: RpcSendQueue): Promise<Json.RightPadded<T> | undefined> {
89
+ public async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, q: RpcSendQueue): Promise<Json.RightPadded<T> | undefined> {
91
90
  await q.getAndSend(right, r => r.element, j => this.visit(j, q));
92
91
  await q.getAndSend(right, r => asRef(r.after), async space => await this.visitSpace(space, q));
93
92
  await q.getAndSend(right, r => r.markers);
@@ -158,7 +157,7 @@ class JsonReceiver extends JsonVisitor<RpcReceiveQueue> {
158
157
  return finishDraft(draft);
159
158
  }
160
159
 
161
- protected async visitSpace(space: Json.Space, q: RpcReceiveQueue): Promise<Json.Space> {
160
+ public async visitSpace(space: Json.Space, q: RpcReceiveQueue): Promise<Json.Space> {
162
161
  return produceAsync<Json.Space>(space, async draft => {
163
162
  draft.comments = await q.receiveListDefined(space.comments, async c => {
164
163
  return await produceAsync(c, async draft => {
@@ -172,7 +171,7 @@ class JsonReceiver extends JsonVisitor<RpcReceiveQueue> {
172
171
  });
173
172
  }
174
173
 
175
- protected async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, p: RpcReceiveQueue): Promise<Json.RightPadded<T> | undefined> {
174
+ public async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, p: RpcReceiveQueue): Promise<Json.RightPadded<T> | undefined> {
176
175
  if (!right) {
177
176
  throw new Error("TreeDataReceiveQueue should have instantiated an empty padding")
178
177
  }
@@ -184,18 +183,40 @@ class JsonReceiver extends JsonVisitor<RpcReceiveQueue> {
184
183
  }
185
184
  }
186
185
 
187
- const jsonCodec: RpcCodec<Json> = {
188
- async rpcReceive(before: Json, q: RpcReceiveQueue): Promise<Json> {
189
- return (await new JsonReceiver().visit(before, q))!;
190
- },
191
-
192
- async rpcSend(after: Json, q: RpcSendQueue): Promise<void> {
193
- await new JsonSender().visit(after, q);
186
+ const receiver = new JsonReceiver();
187
+ const sender = new JsonSender();
188
+
189
+ // Register codec for all Java AST node types
190
+ for (const kind of Object.values(Json.Kind)) {
191
+ if (kind === Json.Kind.Space) {
192
+ RpcCodecs.registerCodec(kind, {
193
+ async rpcReceive(before: Json.Space, q: RpcReceiveQueue): Promise<Json.Space> {
194
+ return (await receiver.visitSpace(before, q))!;
195
+ },
196
+
197
+ async rpcSend(after: Json.Space, q: RpcSendQueue): Promise<void> {
198
+ await sender.visitSpace(after, q);
199
+ }
200
+ }, Json.Kind.Document);
201
+ } else if (kind === Json.Kind.RightPadded) {
202
+ RpcCodecs.registerCodec(kind, {
203
+ async rpcReceive<T extends Json>(before: Json.RightPadded<T>, q: RpcReceiveQueue): Promise<Json.RightPadded<T>> {
204
+ return (await receiver.visitRightPadded(before, q))!;
205
+ },
206
+
207
+ async rpcSend<T extends Json>(after: Json.RightPadded<T>, q: RpcSendQueue): Promise<void> {
208
+ await sender.visitRightPadded(after, q);
209
+ }
210
+ }, Json.Kind.Document);
211
+ } else {
212
+ RpcCodecs.registerCodec(kind as string, {
213
+ async rpcReceive(before: Json, q: RpcReceiveQueue): Promise<Json> {
214
+ return (await receiver.visit(before, q))!;
215
+ },
216
+
217
+ async rpcSend(after: Json, q: RpcSendQueue): Promise<void> {
218
+ await sender.visit(after, q);
219
+ }
220
+ }, Json.Kind.Document);
194
221
  }
195
222
  }
196
-
197
- Object.values(Json.Kind).forEach(kind => {
198
- if (!Object.values(TreeKind).includes(kind as any)) {
199
- RpcCodecs.registerCodec(kind, jsonCodec);
200
- }
201
- });
@@ -62,7 +62,7 @@ export class JsonVisitor<P> extends TreeVisitor<Json, P> {
62
62
  });
63
63
  }
64
64
 
65
- protected async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, p: P):
65
+ public async visitRightPadded<T extends Json>(right: Json.RightPadded<T>, p: P):
66
66
  Promise<Json.RightPadded<T> | undefined> {
67
67
  return produceAsync<Json.RightPadded<T>>(right, async draft => {
68
68
  draft.element = await this.visitDefined(right.element, p);
@@ -70,7 +70,7 @@ export class JsonVisitor<P> extends TreeVisitor<Json, P> {
70
70
  });
71
71
  }
72
72
 
73
- protected async visitSpace(space: Json.Space, p: P): Promise<Json.Space> {
73
+ public async visitSpace(space: Json.Space, p: P): Promise<Json.Space> {
74
74
  return space;
75
75
  }
76
76
 
package/src/print.ts CHANGED
@@ -143,11 +143,13 @@ export class TreePrinters {
143
143
  * @param target Helps to determine what kind of language we are dealing with when
144
144
  * printing a subtree whose LST type is shared between multiple languages in a language family.
145
145
  */
146
- static printer(target: Cursor | SourceFile): TreePrinter {
147
- const sourceFileKind = (isSourceFile(target) ?
146
+ static printer(target: Cursor | SourceFile | string): TreePrinter {
147
+ const sourceFileKind = typeof target === 'string' ?
148
+ target :
149
+ (isSourceFile(target) ?
148
150
  target as SourceFile :
149
151
  target.firstEnclosing(isSourceFile)
150
- )!.kind
152
+ )!.kind
151
153
 
152
154
  if (!this._registry.has(sourceFileKind)) {
153
155
  throw new Error(`No printer registered for ${sourceFileKind}`)
@@ -160,6 +162,6 @@ export class TreePrinters {
160
162
  }
161
163
  }
162
164
 
163
- export function printer(target: Cursor | SourceFile): TreePrinter {
165
+ export function printer(target: Cursor | SourceFile | string): TreePrinter {
164
166
  return TreePrinters.printer(target);
165
167
  }