/** * @angular-modernizer/plugin-angular - Constructor to Inject Orchestrator * * The central "brain" of the constructor to inject transformation plugin. * Orchestrates the conversion of Angular constructor injection to inject() function calls. * * 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 */ import type { TransformContext } from '@angular-modernizer/plugin-system'; import type { PublicApi } from '@angular-modernizer/api'; /** * Constructor to Inject Orchestrator * * @remarks * The central orchestrator for converting Angular constructor injection to the * inject() function pattern introduced in Angular 14+. This class demonstrates * the "API-Driven Plugin" pattern where all capabilities are received via context * objects rather than constructor injection. * * ## Architecture Pattern * * This orchestrator follows three core principles: * 1. **Stateless Design** - No constructor dependencies * 2. **Context-Driven** - All capabilities injected via {@link TransformContext} * 3. **API-First** - Uses {@link PublicApi} for all AST operations * * ## Transformation Workflow * * The orchestrator performs the following steps: * 1. Detect classes with constructor injection * 2. Extract constructor parameters and their types * 3. Convert parameters to property declarations with inject() calls * 4. Add inject import from @angular/core if not present * 5. Remove or simplify the constructor * * ## Example Usage * * ```typescript * const orchestrator = new ConstructorToInjectOrchestrator(); * const context = ContextFactory.createTransformContext({ * sourceFile, * project, * api, * config: {} * }); * orchestrator.run(context); * ``` * * @see {@link TransformContext} for context structure * @see {@link PublicApi} for available API tools * * @public */ export declare class ConstructorToInjectOrchestrator { /** * Creates a new ConstructorToInjectOrchestrator instance. * * @remarks * The constructor is intentionally stateless. All dependencies are provided * through the {@link TransformContext} passed to the {@link run} method. * This enables testing, composition, and prevents tight coupling. * * @public */ constructor(); /** * Executes the constructor to inject transformation on a single source file. * * @remarks * This method is the main entry point for the orchestrator. It receives all * necessary dependencies (project, API, config) through the context parameter, * scans the file for classes with constructor injection, and converts them * to use inject() function calls. * * The method operates on one source file at a time and is safe to call * multiple times. It will: * - Find all classes with constructors that have parameters * - Convert constructor parameters to property declarations with inject() * - Add the inject import if not already present * - Remove or simplify constructors that become empty * * ## Transformation Behavior * * **Constructor Parameters**: * - `constructor(private http: HttpClient)` → `private http = inject(HttpClient)` * - `constructor(private router: Router, private service: MyService)` → * `private router = inject(Router); private service = inject(MyService)` * - Preserves access modifiers (private, protected, public) * - Preserves parameter names and types * * **Constructor Cleanup**: * - Removes constructors that become empty after parameter conversion * - Keeps constructors with additional logic (super() calls, initialization) * * @param context - The transformation context containing: * - `sourceFile`: The TypeScript source file to transform * - `project`: The ts-morph Project for cross-file analysis * - `api`: The PublicApi with analysis and transformation tools * - `config`: User-provided configuration options * * @example * ```typescript * // Basic usage * orchestrator.run(context); * * // With custom configuration * const context = ContextFactory.createTransformContext({ * sourceFile, * project, * api, * config: {} * }); * orchestrator.run(context); * ``` * * @see {@link TransformContext} for context structure * * @public */ run(context: TransformContext): void; /** * Transform a single class by converting its constructor injection to inject() calls. * * This method now includes enhanced error handling to prevent file corruption when * invalid types are encountered. It validates all parameters and provides clear * warnings for any that cannot be converted. * * @param classDecl - The class declaration to transform * @param sourceFile - The source file containing the class * @param api - The PublicApi with transformation tools * * @remarks * **Enhanced Error Handling:** * - If ALL parameters are invalid → File unchanged, warning logged * - If SOME parameters are invalid → Valid ones converted, invalid ones skipped with warning * - Clear console warnings indicate which parameters need manual review * * This prevents corruption from invalid types like `HttpClient | undefined` * being used directly in inject() calls. * * @private */ private transformClass; /** * Convert constructor parameters to inject() property declarations. * * This method validates each parameter type to ensure it can be used as an Angular * dependency injection token. Invalid types (primitives, arrays, etc.) are skipped * with a warning logged to the console. * * @param parameters - The constructor parameters to convert * @returns Array of property declaration info (only valid parameters) * * @remarks * Parameters are validated using {@link normalizeInjectionType}. Invalid parameters * are skipped rather than corrupting the file. This ensures: * - Primitives (string, number) are not used as injection tokens * - Array types are not used without proper InjectionToken wrapper * - Union types with undefined/null are normalized to base type * - Warnings are logged for manual review * * @private */ private convertParametersToInjectProperties; /** * Add inject() property declarations to the class. * * @param classDecl - The class to add properties to * @param injectProperties - Array of property declaration info * * @remarks * Logs warnings to console if any properties were normalized (e.g., extracted from * union types or had optional modifiers removed). * * @private */ private addInjectPropertiesToClass; /** * Ensure the inject function is imported from @angular/core. * * **CRITICAL FIX:** This method now ALWAYS uses ImportManager API for consistency * and safety. The previous implementation used direct ts-morph API (addNamedImport) * which bypassed validation and could create malformed imports. * * @param sourceFile - The source file to add the import to * @param api - The PublicApi with import management tools * * @remarks * **Bug Fix (Line 270):** * Previous code used `importDecl.addNamedImport('inject')` which directly called * ts-morph's API, bypassing ImportManager's: * - Deduplication logic * - Validation * - Proper formatting * - Edge case handling (comments, whitespace, aliases) * * This caused file corruption in production use. * * **New behavior:** * - Always uses ImportManager.addNamedImport() for ALL import operations * - ImportManager handles merging with existing @angular/core imports * - Automatically deduplicates if inject already exists * - Properly formats complex import statements * * @private */ private ensureInjectImport; /** * Normalize a TypeScript type string to a valid Angular injection token. * * This method validates and normalizes parameter types to ensure they can be used * as Angular dependency injection tokens. It handles common TypeScript patterns * like union types, optional types, and generics. * * @param typeText - Raw type string from param.getType().getText() * @returns Normalized type with optional warning, or null if type is invalid * * @remarks * **Valid injection tokens:** * - Class types: `HttpClient`, `Router`, `MyService` * - InjectionToken: `InjectionToken` * - Abstract classes: `AbstractControl` * * **Invalid injection tokens (returned as null):** * - Primitives: `string`, `number`, `boolean` (need InjectionToken wrapper) * - Arrays: `HttpClient[]`, `Array` (need InjectionToken pattern) * - Observable/Promise generics: `Observable` (not injection tokens) * - Multiple union types: `ServiceA | ServiceB` (ambiguous, needs manual review) * * **Normalized patterns:** * - Union with undefined/null: `HttpClient | undefined` → `HttpClient` * - Optional types: `Router?` → `Router` * * @example * ```typescript * // Valid types * normalizeInjectionType("HttpClient") → { type: "HttpClient" } * normalizeInjectionType("HttpClient | undefined") → { type: "HttpClient", warning: "..." } * normalizeInjectionType("Router?") → { type: "Router", warning: "..." } * * // Invalid types (return null) * normalizeInjectionType("string") → null * normalizeInjectionType("HttpClient[]") → null * normalizeInjectionType("Observable") → null * ``` * * @private */ private normalizeInjectionType; } //# sourceMappingURL=constructor-to-inject-orchestrator.d.ts.map