@openrewrite/rewrite 8.62.3 → 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 (90) 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 +208 -167
  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/rpc/queue.d.ts +15 -6
  39. package/dist/rpc/queue.d.ts.map +1 -1
  40. package/dist/rpc/queue.js +37 -13
  41. package/dist/rpc/queue.js.map +1 -1
  42. package/dist/rpc/request/generate.d.ts +4 -0
  43. package/dist/rpc/request/generate.d.ts.map +1 -1
  44. package/dist/rpc/request/generate.js +9 -4
  45. package/dist/rpc/request/generate.js.map +1 -1
  46. package/dist/rpc/request/get-object.d.ts +2 -2
  47. package/dist/rpc/request/get-object.d.ts.map +1 -1
  48. package/dist/rpc/request/get-object.js +4 -12
  49. package/dist/rpc/request/get-object.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/request/print.d.ts +1 -1
  53. package/dist/rpc/request/print.d.ts.map +1 -1
  54. package/dist/rpc/request/print.js +1 -1
  55. package/dist/rpc/request/print.js.map +1 -1
  56. package/dist/rpc/request/visit.d.ts +3 -2
  57. package/dist/rpc/request/visit.d.ts.map +1 -1
  58. package/dist/rpc/request/visit.js +5 -4
  59. package/dist/rpc/request/visit.js.map +1 -1
  60. package/dist/rpc/rewrite-rpc.d.ts +3 -3
  61. package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
  62. package/dist/rpc/rewrite-rpc.js +16 -14
  63. package/dist/rpc/rewrite-rpc.js.map +1 -1
  64. package/dist/test/rewrite-test.js.map +1 -1
  65. package/dist/text/rpc.js +37 -40
  66. package/dist/text/rpc.js.map +1 -1
  67. package/dist/version.txt +1 -1
  68. package/package.json +1 -1
  69. package/src/execution.ts +0 -2
  70. package/src/java/rpc.ts +68 -0
  71. package/src/java/tree.ts +1 -65
  72. package/src/java/visitor.ts +4 -4
  73. package/src/javascript/format.ts +2 -2
  74. package/src/javascript/parser.ts +19 -5
  75. package/src/javascript/print.ts +3 -3
  76. package/src/javascript/rpc.ts +4 -17
  77. package/src/javascript/type-mapping.ts +216 -173
  78. package/src/javascript/visitor.ts +1 -1
  79. package/src/json/print.ts +1 -1
  80. package/src/json/rpc.ts +40 -19
  81. package/src/json/visitor.ts +2 -2
  82. package/src/rpc/queue.ts +36 -12
  83. package/src/rpc/request/generate.ts +18 -6
  84. package/src/rpc/request/get-object.ts +6 -13
  85. package/src/rpc/request/parse.ts +1 -1
  86. package/src/rpc/request/print.ts +2 -2
  87. package/src/rpc/request/visit.ts +6 -5
  88. package/src/rpc/rewrite-rpc.ts +21 -17
  89. package/src/test/rewrite-test.ts +1 -1
  90. 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,11 +161,65 @@ 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
177
  const mappedType = this.getType(exprType);
174
178
 
175
- // For string methods like 'hello'.split(), ensure we have a proper declaring type
176
- if (!mappedType || mappedType.kind !== Type.Kind.Class) {
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) {
177
223
  // If the expression type is a primitive string, use lib.String as declaring type
178
224
  const typeString = this.checker.typeToString(exprType);
179
225
  if (typeString === 'string' || exprType.flags & ts.TypeFlags.String || exprType.flags & ts.TypeFlags.StringLiteral) {
@@ -190,42 +236,89 @@ export class JavaScriptTypeMapping {
190
236
  // Fallback for other primitive types or unknown
191
237
  declaringType = Type.unknownType as Type.FullyQualified;
192
238
  }
193
- } else {
194
- declaringType = mappedType as Type.FullyQualified;
195
239
  }
240
+
196
241
  } else if (ts.isIdentifier(node.expression)) {
197
242
  methodName = node.expression.getText();
198
- // For standalone functions, we need to determine the appropriate declaring type
199
- const exprType = this.checker.getTypeAtLocation(node.expression);
200
- const funcType = this.getType(exprType);
201
-
202
- if (funcType && funcType.kind === Type.Kind.Class) {
203
- const fqn = (funcType as Type.Class).fullyQualifiedName;
204
- const lastDot = fqn.lastIndexOf('.');
205
-
206
- if (lastDot > 0) {
207
- // For functions from modules, use the module part as declaring type
208
- // Examples:
209
- // - "node.assert" -> declaring type: "node"
210
- // - "@types/lodash.map" -> declaring type: "@types/lodash"
211
- // - "@types/express.express" -> declaring type: "@types/express"
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
212
281
  declaringType = {
213
282
  kind: Type.Kind.Class,
214
- fullyQualifiedName: fqn.substring(0, lastDot)
283
+ fullyQualifiedName: 'node'
215
284
  } as Type.FullyQualified;
285
+ methodName = moduleSpecifier.substring(5); // Remove 'node:' prefix
216
286
  } else {
217
- // No dots in the name - the type IS the module itself
218
- // This handles single-name modules like "axios", "lodash" etc.
219
- declaringType = funcType as Type.FullyQualified;
287
+ // Regular module import
288
+ declaringType = {
289
+ kind: Type.Kind.Class,
290
+ fullyQualifiedName: moduleSpecifier
291
+ } as Type.FullyQualified;
292
+ methodName = '<default>';
220
293
  }
221
294
  } else {
222
- // Try to use the symbol's parent or module
223
- const parent = (symbol as any).parent;
224
- if (parent) {
225
- const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
226
- declaringType = this.getType(parentType) as Type.FullyQualified;
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
+ }
227
313
  } else {
228
- 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
+ }
229
322
  }
230
323
  }
231
324
  } else {
@@ -320,27 +413,12 @@ export class JavaScriptTypeMapping {
320
413
  }) as Type.Method;
321
414
  }
322
415
 
323
- /**
324
- * Create a JavaType.Array from a TypeScript array type
325
- */
326
- private createArrayType(type: ts.TypeReference): Type.Array {
327
- // Get the element type (type argument of Array<T>)
328
- const typeArgs = this.checker.getTypeArguments(type);
329
- const elemType = typeArgs.length > 0 ? this.getType(typeArgs[0]) : Type.unknownType;
330
-
331
- return Object.assign(new NonDraftableType(), {
332
- kind: Type.Kind.Array,
333
- elemType: elemType,
334
- annotations: [],
335
- toJSON: function () {
336
- return Type.signature(this);
337
- }
338
- }) as Type.Array;
339
- }
340
416
 
341
417
  /**
342
418
  * Get the fully qualified name for a TypeScript type.
343
- * 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)
344
422
  */
345
423
  private getFullyQualifiedName(type: ts.Type): string {
346
424
  const symbol = type.getSymbol?.();
@@ -348,140 +426,105 @@ export class JavaScriptTypeMapping {
348
426
  return "unknown";
349
427
  }
350
428
 
351
- const typeName = symbol.getName();
352
- const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
353
- if (!declaration) {
354
- // No declaration - might be a built-in or synthetic type
355
- if (builtInTypes.has(typeName)) {
356
- return `lib.${typeName}`;
357
- }
358
- return typeName;
359
- }
360
-
361
- const sourceFile = declaration.getSourceFile();
362
- const fileName = sourceFile.fileName;
363
-
364
- // Check if this is a test file (snowflake ID as filename)
365
- // Test files are generated with numeric IDs like "672087069480189952.ts"
366
- if (/^\d+\.(ts|tsx|js|jsx)$/.test(fileName)) {
367
- // For test files, just return the type name without module prefix
368
- return typeName;
369
- }
370
-
371
- // Check if this is from TypeScript's lib files (lib.d.ts, lib.dom.d.ts, etc.)
372
- if (fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.")) {
373
- return `lib.${typeName}`;
374
- }
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
+ }
375
441
 
376
- // Check if this is from an external module (node_modules or .d.ts)
377
- if (sourceFile.isDeclarationFile || fileName.includes("node_modules")) {
378
- const packageName = this.extractPackageName(fileName);
379
- if (packageName) {
380
- // Special handling for @types/node - these are Node.js built-in modules
381
- // and should be mapped to "node.*" instead of "@types/node.*"
382
- if (packageName === "@types/node") {
383
- // Extract the module name from the file path
384
- // e.g., /node_modules/@types/node/assert.d.ts -> assert
385
- // e.g., /node_modules/@types/node/fs/promises.d.ts -> fs/promises
386
- const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
387
- if (nodeMatch) {
388
- const modulePath = nodeMatch[1];
389
- // For default exports from Node modules, we want the module to be the "class"
390
- // But we still need to include the type name for proper identification
391
- if (typeName === "default" || typeName === modulePath) {
392
- // This is likely the default export, just use the module name
393
- return `node.${modulePath}`;
394
- }
395
- // For named exports, include both module and type name
396
- if (modulePath.includes('/')) {
397
- return `node.${modulePath.replace(/\//g, '.')}.${typeName}`;
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
+ }
398
455
  }
399
- return `node.${modulePath}.${typeName}`;
400
456
  }
401
- // Fallback for @types/node types that don't match the pattern
402
- 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}`;
403
468
  }
404
- return `${packageName}.${typeName}`;
405
469
  }
406
470
  }
407
471
 
408
- // For local files, use relative path from project root
409
- const relativePath = this.getRelativeModulePath(fileName);
410
- return `${relativePath}.${typeName}`;
411
- }
412
-
413
- /**
414
- * Extract package name from a node_modules path.
415
- * Examples:
416
- * - /path/to/project/node_modules/react/index.d.ts -> "react"
417
- * - /path/to/project/node_modules/@mui/material/Button/index.d.ts -> "@mui/material"
418
- */
419
- private extractPackageName(fileName: string): string | null {
420
- const match = fileName.match(/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/);
421
- return match ? match[1] : null;
422
- }
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
+ }
423
498
 
424
- /**
425
- * Get relative module path from project root.
426
- * Removes file extension and uses forward slashes.
427
- */
428
- private getRelativeModulePath(fileName: string): string {
429
- // Remove project root and normalize path
430
- let relativePath = fileName;
431
- if (fileName.startsWith(this.projectRoot)) {
432
- relativePath = fileName.slice(this.projectRoot.length);
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
+ }
433
508
  }
434
509
 
435
- // Remove leading slash and file extension
436
- relativePath = relativePath.replace(/^\//, '').replace(/\.[^/.]+$/, '');
437
-
438
- // Convert backslashes to forward slashes (for Windows)
439
- relativePath = relativePath.replace(/\\/g, '/');
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
+ }
440
516
 
441
- return relativePath;
517
+ return cleanedName;
442
518
  }
443
519
 
520
+
444
521
  /**
445
522
  * Create an empty JavaType.Class shell from a TypeScript type.
446
523
  * The shell will be populated later to handle circular references.
447
524
  */
448
525
  private createEmptyClassType(type: ts.Type): Type.Class {
449
- // Use our custom getFullyQualifiedName method for consistent naming
450
- let fullyQualifiedName = this.getFullyQualifiedName(type);
451
-
452
- // If getFullyQualifiedName returned unknown, fall back to TypeScript's method
453
- if (fullyQualifiedName === "unknown") {
454
- const symbol = type.symbol;
455
- fullyQualifiedName = symbol ? this.checker.getFullyQualifiedName(symbol) : `<anonymous>${this.checker.typeToString(type)}`;
456
-
457
- // Fix FQN for types from @types packages
458
- // TypeScript returns "_.LoDashStatic" but we want "@types/lodash.LoDashStatic"
459
- if (symbol && symbol.declarations && symbol.declarations.length > 0) {
460
- const sourceFile = symbol.declarations[0].getSourceFile();
461
- const fileName = sourceFile.fileName;
462
- // Check if this is from @types package
463
- const typesMatch = fileName.match(/node_modules\/@types\/([^/]+)/);
464
- if (typesMatch) {
465
- const packageName = typesMatch[1];
466
- // Special handling for @types/node - use "node" prefix instead
467
- if (packageName === "node") {
468
- // Extract the module name from the file path if possible
469
- const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
470
- if (nodeMatch) {
471
- const modulePath = nodeMatch[1];
472
- // Replace the module specifier with node.module
473
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.${modulePath}.`);
474
- } else {
475
- // Fallback: just use "node" prefix
476
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.`);
477
- }
478
- } else {
479
- // Replace the module specifier part with @types/package
480
- fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `@types/${packageName}.`);
481
- }
482
- }
483
- }
484
- }
526
+ // Use our getFullyQualifiedName method which uses TypeScript's built-in resolution
527
+ const fullyQualifiedName = this.getFullyQualifiedName(type);
485
528
 
486
529
  // Determine the class kind based on symbol flags
487
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