/** * @angular-modernizer/plugin-angular - Subscription Transform Rule * * Thin rule wrapper: delegates all logic to SubscriptionTransformOrchestrator. * Activated via transformationType: 'subscription-transform' in plugin config. */ import type { TransformRule, TransformContext, TransformResult, } from '@angular-modernizer/plugin-system'; import type { PublicApi } from '@angular-modernizer/api'; import { SubscriptionTransformOrchestrator } from '../orchestrators/subscription-transform.orchestrator.js'; /** * Transform Rule: Subscription in Lifecycle Hook * * Converts raw `.subscribe()` calls in lifecycle hooks to properly managed * subscriptions using `takeUntilDestroyed()` operator from * `@angular/core/rxjs-interop`. * * This fixes the `subscription-in-lifecycle` violation type detected by * `PresentationalComponentViolationRule`. * * @example * ```typescript * // BEFORE: raw subscription in lifecycle hook * ngOnInit() { * this.dataService.getData().subscribe(data => this.data = data); * } * * // AFTER: takeUntilDestroyed ensures cleanup * private readonly destroyRef = inject(DestroyRef); * * ngOnInit() { * this.dataService.getData() * .pipe(takeUntilDestroyed(this.destroyRef)) * .subscribe(data => this.data = data); * } * ``` * * @see SubscriptionTransformOrchestrator * @see PresentationalComponentViolationRule */ export class SubscriptionTransformRule implements TransformRule { public readonly id = 'angular:subscription-transform'; public readonly name = 'Subscription Transform'; public readonly category = 'angular-architecture'; public readonly description = 'Add takeUntilDestroyed() to raw .subscribe() calls in lifecycle hooks'; private readonly orchestrator = new SubscriptionTransformOrchestrator(); async transform( context: TransformContext, ): Promise { const pluginConfig = context.config[ '@angular-modernizer/plugin-angular' ] as Record | undefined; if (pluginConfig?.['transformationType'] !== 'subscription-transform') { return { ruleId: this.id, modified: false, message: 'Transformation type not applicable', filePath: context.filePath, }; } return this.orchestrator.run(context); } }