import { type MethodDeclaration } from 'ts-morph'; import { type CrossServiceCall } from './cross-service-call-detector.js'; /** * Rewrites method bodies to use injected services for cross-service calls. * * Transforms: * - `this.sortUsers(users)` → `this.transformationService.sortUsers(users)` */ export class MethodCallRewriter { /** * Rewrites cross-service calls in a method body. * * @param method - Method to rewrite * @param crossServiceCalls - Cross-service calls detected in this method * @param servicePropertyMap - Map of service name to property name (e.g., 'TransformationService' → 'transformationService') * @returns Rewritten method body text */ rewriteMethodBody( method: MethodDeclaration, crossServiceCalls: CrossServiceCall[], servicePropertyMap: Map, ): string { let methodBody = method.getBodyText() ?? ''; // Group calls by target service for efficient rewriting const callsByService = new Map(); for (const call of crossServiceCalls) { if (!callsByService.has(call.targetService)) { callsByService.set(call.targetService, []); } callsByService.get(call.targetService)!.push(call); } // Rewrite calls for each service for (const [targetService, calls] of callsByService.entries()) { const servicePropertyName = servicePropertyMap.get(targetService); if (!servicePropertyName) { continue; } for (const call of calls) { // Replace: this.methodName( → this.serviceProperty.methodName( const pattern = new RegExp( String.raw`\bthis\.${call.methodName}\s*\(`, 'g', ); const replacement = `this.${servicePropertyName}.${call.methodName}(`; methodBody = methodBody.replace(pattern, replacement); } } return methodBody; } /** * Converts service name to property name. * * @param serviceName - Service name (e.g., 'TransformationService') * @returns Property name (e.g., 'transformationService') */ toPropertyName(serviceName: string): string { // Convert to camelCase (keep Service suffix for clarity) return serviceName.charAt(0).toLowerCase() + serviceName.slice(1); } }