/** * @angular-modernizer/plugin-angular - Service Bag Transform Orchestrator * * The central "brain" of the Service Bag transformation. * Orchestrates the splitting of Service Bags into focused services following SRP. * * Philosophy: "API-Driven Orchestration" * - Receives all capabilities via TransformContext * - Uses PublicApi for all AST operations * - Stateless - no constructor dependencies * - Pure business logic, no low-level manipulation * * Transformation Strategy: * 1. Detect Service Bag (>10 methods, >3 responsibilities) * 2. Group methods by responsibility patterns (8 domains) * 3. Analyze properties for RxJS patterns (Subjects, Observables) * 4. Analyze method bodies for implementation patterns (.next(), JSON.parse(), etc.) * 5. Merge method + property responsibilities * 6. Create focused service files (one per responsibility) * 7. Update imports in original service * 8. Inject focused services via constructor DI * 9. Delegate method calls to focused services * * Enhanced Pattern Detection: * - Three-tier analysis: Method names → Method bodies → Properties * - Extended patterns reduce 43% miss rate to <10% target * - Real-world validation on config.service.ts (25+ methods, 7 responsibilities) * * Responsibility Domains (kebab-case internally, converted to PascalCase for service names): * - data-retrieval: get, fetch, load, read, query → DataRetrievalService * - data-mutation: save, update, delete, write, create, remove, set, modify → DataMutationService * - cache-management: cache, clear, reset, invalidate, cleanup → CacheManagementService * - validation: validate, check, verify, ensure → ValidationService * - transformation: transform, convert, map, parse, decode, AsBoolean, AsNumber → TransformationService * - event-handling: on, handle, emit, trigger, notify, clicked, Saved, Changed, toggle, RxJS Subjects → EventHandlingService * - authentication: auth, login, logout, token → AuthenticationService * - authorization: can, has, is, permission, role → AuthorizationService * * Idempotency: * - Checks if focused service files already exist * - Checks if constructor parameters already exist * - Safe to run multiple times */ import type { TransformContext } from '@angular-modernizer/plugin-system'; import type { PublicApi } from '@angular-modernizer/api'; /** * Service Bag Transform Orchestrator - contains all transformation logic. * * This orchestrator handles the complete transformation of Service Bags into * focused services following the Single Responsibility Principle. * * @example * ```typescript * const orchestrator = new ServiceBagTransformOrchestrator(); * const context = ContextFactory.createTransformContext({ sourceFile, project, api }); * await orchestrator.run(context); * * // Results in: * // - data-retrieval-service.ts (with getUser, fetchOrders, etc.) * // - data-mutation-service.ts (with saveUser, updateProduct, etc.) * // - Original service updated to delegate to focused services * ``` */ export declare class ServiceBagTransformOrchestrator { private readonly methodBodyAnalyzer; private readonly crossServiceCallDetector; private readonly methodCallRewriter; private readonly privateHelperDetector; /** * Main orchestration method - executes the complete transformation. * * @param context - Transform context with source file, project, and PublicApi * @returns Promise that resolves when transformation is complete */ run(context: TransformContext): Promise; /** * Groups methods by responsibility domain based on naming patterns and body analysis. * * Analyzes method names and bodies against 8 responsibility patterns to identify * distinct domains. Also performs AST-based method body analysis to catch * patterns missed by name-only detection. * * Enhanced Detection: * - Method name patterns (primary) * - Method body analysis (secondary) - detects .next(), .emit(), JSON.parse(), etc. * - Extended patterns for event-handling (notify, clicked, Saved, Changed, toggle, etc.) * - Extended patterns for transformation (decode, AsBoolean, AsNumber, AsString, etc.) * * Refactoring (2026-02-10): * - Uses shared analyzeMethodResponsibility utility from plugin-architecture * - Eliminates pattern duplication * * Private Helper Detection (2026-02-12): * - Second pass detects private helper methods called by public methods * - Transitive helpers are included (helper calling another helper) * - Prevents TypeScript compilation errors (TS2339) after extraction * * Shared Helper Deduplication (2026-02-16): * - Detects helpers used by multiple responsibility groups * - Extracts shared helpers to utility service instead of duplicating * - Updates call sites to use utility service * * @param methods - Array of public method declarations to group * @param classDecl - The class containing the methods * @returns Array of responsibility groups (including private helpers) * @private */ private groupMethodsByResponsibility; /** * Analyzes class properties to detect RxJS-based event handling. * * Detects properties using reactive patterns that indicate event handling * responsibility: * - Subject - RxJS event emitters * - BehaviorSubject - Stateful event streams * - Observable - Event streams (less common as properties) * - ReplaySubject, AsyncSubject - Other RxJS subjects * * This catches properties like: * - private onSettingsChanged: BehaviorSubject * - private onConfigSaved: Subject * - private onToggle: Subject * * Real-world impact: config.service.ts has 8 RxJS Subjects that were * previously missed, representing a significant event-handling responsibility. * * @param classDecl - Class declaration to analyze properties from * @returns Map of responsibility domains to property counts * @private */ private analyzeProperties; /** * Merges property-based responsibilities with method-based responsibility groups. * * Combines two sources of responsibility detection: * 1. Method-based (from groupMethodsByResponsibility) * 2. Property-based (from analyzeProperties) * * If properties indicate a responsibility domain that wasn't detected from methods, * we add it to the groups to ensure accurate Service Bag detection. * * Example: 8 RxJS Subject properties indicate event-handling responsibility * even if no event-handling methods were detected by name patterns. * * @param methodGroups - Responsibility groups from method analysis * @param propertyResponsibilities - Responsibility counts from property analysis * @returns Merged responsibility groups * @private */ private mergeResponsibilities; /** * Creates a focused service file for a responsibility group. * * Generates a new service file with: * - Injectable decorator with providedIn: 'root' * - Constructor with dependencies used by methods (bugfix) * - All required imports (HttpClient, Observable, types) (bugfix) * - Methods from the responsibility group * * Enhancements: * - Uses ConstructorDependencyHelper to extract dependencies * - Uses ImportCleanupHelper to analyze and add imports * * Idempotent: Skips if file already exists. * * @param group - Responsibility group to create service for * @param originalFilePath - Path of the original service file * @param originalClass - Original service class declaration (for dependency extraction) * @param project - ts-morph Project instance * @param api - PublicApi for ImportManager and SymbolLocator * @returns Promise that resolves with the generated SourceFile, or null if skipped * @private */ private createFocusedService; /** * Creates a state service for shared properties. * * Generates a service with: * - Injectable decorator with providedIn: 'root' * - Private properties for state * - Getter/setter methods for each property * * @param spec - State service specification * @param originalFilePath - Path to the original service file * @param project - ts-morph Project * @param api - Public API for transformation utilities * @returns Promise resolving to the created SourceFile or null if file exists * @private */ private createStateService; /** * Updates the original service to delegate to focused services. * * Performs the following updates: * 1. Adds imports for focused services * 2. Injects focused services via constructor DI * 3. Updates method bodies to delegate to focused services * 4. Cleans up unused imports and constructor parameters * * Idempotent: Checks for existing parameters before adding. * * @param classDecl - Original service class declaration * @param groups - Responsibility groups with focused services * @param sourceFile - Original source file * @param api - PublicApi for AST operations * @returns Promise that resolves when updates are complete * @private */ private updateOriginalService; /** * Analyzes initialization code to determine required constructor parameters. * * Scans initialization expressions to find: * 1. Constructor parameters (non-DI) - identifiers without 'this.' prefix * 2. Dependencies - already handled as injected properties * * For example: * - `new BehaviorSubject(initialData)` → needs `initialData: Data` parameter * - `this.config.get('apiUrl')` → `config` already injected, no param needed * - `this.processData(initialData)` → needs `initialData: Data` parameter * * @param initializations - Property initialization metadata * @param originalClass - Original service class to extract parameter types * @param existingDependencies - Dependencies already injected as properties * @returns Array of required constructor parameters * @private */ private analyzeInitializationDependencies; /** * Extracts dependencies referenced in initialization code. * * Scans initialization expressions for `this.` patterns and * looks up their types from the original constructor parameters. * * For example: * - `this.config.get('apiUrl')` → extracts `config: ConfigService` * - `this.http.get(url)` → extracts `http: HttpClient` * * @param initializations - Property initialization metadata * @param originalClass - Original service class to extract dependency types * @returns Array of dependency metadata * @private */ private extractDependenciesFromInitializations; /** * Merges two dependency arrays, avoiding duplicates. * * @param deps1 - First dependency array * @param deps2 - Second dependency array * @returns Merged dependency array without duplicates * @private */ private mergeDependencies; /** * Find the project root by walking up the directory tree from the given file path. * Looks for tsconfig.json or package.json to identify the project root. * * @param filePath - Path of a file in the project * @returns Project root directory * @private */ private findProjectRoot; /** * Post-processes a generated file to add missing imports for locally-declared types. * * Detects TS2304 "Cannot find name" errors and resolves types (interfaces, type aliases, * enums) declared locally in the original source file. If found, exports them and adds * the relative import to the generated file. * * @param newFile - Generated service file to fix * @param newFilePath - Absolute path of the generated file * @param originalSourceFile - Original source file containing local declarations * @private */ private fixLocalTypeImports; /** * Builds a map from import alias names to their original export names and module specifiers. * * For example, `import { UserModel as DomainUser } from './user.model'` produces: * `'DomainUser' → { originalName: 'UserModel', specifier: './user.model' }` * * @param sourceFile - Source file to scan for aliased imports * @returns Map from alias name to original name + specifier * @private */ private buildAliasMap; /** * Resolves the module specifier for a type name. * * Determines where a type should be imported from by: * 1. Checking existing imports in the original service file * 2. Looking up the type in the project symbol map * 3. Falling back to common Angular imports * * @param typeName - Type name to resolve (e.g., 'HttpClient', 'User') * @param originalServiceFile - Original service source file * @param symbolMap - Project-wide symbol map from SymbolLocator * @returns Module specifier or undefined if not found * @private */ private resolveModuleSpecifier; /** * Calculates a relative import path between two file paths. * * @param fromPath - The file path that will contain the import * @param toPath - The file path to import from * @returns Relative import path without extension * @private */ private calculateRelativeImportPath; /** * Extracts type references from a method signature (return type or parameter type). * * Handles: * - Simple types: `User` → ['User'] * - Generic types: `Observable` → ['Observable', 'User'] * - Union types: `User | Admin` → ['User', 'Admin'] * - Array types: `User[]` → ['User'] * * @param typeText - Type text from method signature * @param originalServiceFile - Original service source file * @param symbolMap - Project-wide symbol map * @param trackImport - Callback to track an import * @private */ private extractTypeReferencesFromSignature; /** * Extracts identifiers used in method bodies and tracks their imports. * * Scans all method bodies for identifiers (function calls, property accesses) and * maps them back to their import declarations in the original service file. * * Handles: * - Named imports: `import { map, filter } from 'rxjs/operators'` * - RxJS operators: `.pipe(map(...))` → track 'map' import * - Utility functions: `parseData(...)` → track 'parseData' import * * @param classDecl - Class declaration to analyze * @param originalServiceFile - Original service source file to resolve imports from * @param trackImport - Callback to track an import * @private */ private extractMethodBodyIdentifiers; /** * Checks if an import is still used in a class. * * Verifies whether an import name (or its alias) is referenced in: * - Constructor parameters * - Method signatures (return types and parameters) * - Properties * - Method bodies * * @param classDecl - Class declaration to check * @param importName - Import name to check * @param alias - Optional alias for the import * @returns True if the import is still used, false otherwise * @private */ private isImportUsedInClass; /** * Converts kebab-case to PascalCase for service naming. * * @param str - String in kebab-case format * @returns String in PascalCase format * @private * @example * toPascalCase('data-retrieval') // Returns 'DataRetrieval' */ private toPascalCase; /** * Handles cross-service dependencies between focused services. * * For each focused service: * 1. Detects cross-service method calls * 2. Injects required services using inject() * 3. Rewrites method bodies to use injected services * 4. Adds imports for injected services * 5. Builds dependency graph * * @param groups - Responsibility groups * @param project - ts-morph Project * @param api - PublicApi for ImportManager * @param originalFilePath - Original service file path * @param dependencyGraphBuilder - Dependency graph builder instance * @private */ private handleCrossServiceDependencies; /** * Converts PascalCase to kebab-case for file naming. * * @param str - String in PascalCase format * @returns String in kebab-case format * @private * @example * toKebabCase('DataRetrievalService') // Returns 'data-retrieval-service' */ private toKebabCase; /** * Converts PascalCase to camelCase for variable naming. * * @param str - String in PascalCase format * @returns String in camelCase format * @private * @example * toCamelCase('DataRetrievalService') // Returns 'dataRetrievalService' */ private toCamelCase; /** * Extracts shared private helpers to a utility service. * * Detects helpers used by multiple responsibility groups and: * 1. Creates a utility service with static methods * 2. Updates call sites in all focused services to use the utility * 3. Prevents duplication of helper logic across services * * Example: formatDate() used by both DataRetrievalService and CacheManagementService * → Extracted to ConfigServiceUtils.formatDate() * * @param classDecl - Original service class declaration * @param groups - Responsibility groups * @param originalFilePath - Path to original service file * @param project - ts-morph Project * @param api - PublicApi for transformation utilities * @returns Promise resolving to utility service file or null if no shared helpers * @private */ private extractSharedHelpers; /** * Updates method bodies in focused services to use utility service for shared helpers. * * Replaces `this.helperMethod(args)` with `UtilityService.helperMethod(args)` * for all shared helpers. * * @param classDecl - Original service class declaration * @param groups - Responsibility groups * @param utilityServiceName - Name of the utility service * @param originalFilePath - Path to original service file * @param project - ts-morph Project * @param api - PublicApi for transformation utilities * @returns Promise resolving when all call sites are updated * @private */ private updateSharedHelperCallSites; } //# sourceMappingURL=service-bag-transform.orchestrator.d.ts.map