diff --git a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.ts b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.ts index 1a1bc2c58f34..afd6316da83d 100644 --- a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.ts +++ b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.ts @@ -16,6 +16,8 @@ import { sendDebugMessage } from './send-debug-message'; import { getImportSpecifier, loadTypescript } from './ts-utils'; import type { MigrationResponse } from './types'; +const supportedStrategies: ReadonlySet = new Set(['OnPush', 'Default', 'Eager']); + export async function migrateSingleFile( sourceFile: SourceFile, extras: RequestHandlerExtra, @@ -31,7 +33,7 @@ export async function migrateSingleFile( return unsupportedZoneUseResponse; } - let detectedStrategy: 'OnPush' | 'Default' | undefined; + let detectedStrategy: string | undefined; let hasComponentDecorator = false; const componentSpecifier = await getImportSpecifier(sourceFile, '@angular/core', 'Component'); @@ -63,7 +65,7 @@ export async function migrateSingleFile( prop.initializer.expression.getText(sourceFile) === 'ChangeDetectionStrategy' ) { const strategy = prop.initializer.name.text; - if (strategy === 'OnPush' || strategy === 'Default') { + if (supportedStrategies.has(strategy)) { detectedStrategy = strategy; return; @@ -77,13 +79,7 @@ export async function migrateSingleFile( ts.forEachChild(node, visit); }); - if ( - !hasComponentDecorator || - // component uses OnPush. We don't have anything more to do here. - detectedStrategy === 'OnPush' || - // Explicit default strategy, assume there's a reason for it (already migrated, or is a library that hosts Default components) and skip. - detectedStrategy === 'Default' - ) { + if (!hasComponentDecorator || (detectedStrategy && supportedStrategies.has(detectedStrategy))) { sendDebugMessage( `Component decorator found with strategy: ${detectedStrategy} in file: ${sourceFile.fileName}. Skipping migration for file.`, extras, diff --git a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file_spec.ts b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file_spec.ts index 20ed43626639..442dc2c68378 100644 --- a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file_spec.ts +++ b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file_spec.ts @@ -85,7 +85,7 @@ describe('migrateSingleFile', () => { expect(result).toBeNull(); }); - it('should return null if component has ChangeDetectionStrategy.Default', async () => { + it('should return null if component has ChangeDetectionStrategy.Eager', async () => { const fileName = 'app.component.ts'; const content = ` import { Component, ChangeDetectionStrategy } from '@angular/core'; @@ -93,7 +93,7 @@ describe('migrateSingleFile', () => { @Component({ selector: 'app-root', template: 'Hello', - changeDetection: ChangeDetectionStrategy.Default, + changeDetection: ChangeDetectionStrategy.Eager, }) export class AppComponent {} `; diff --git a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/prompts.ts b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/prompts.ts index f4eba63ceb4c..af925bd4f70a 100644 --- a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/prompts.ts +++ b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/prompts.ts @@ -130,11 +130,11 @@ export function generateZonelessMigrationInstructionsForComponent( * **(Preferred) Convert to Signal**: The best approach is to convert the property to an Angular Signal. This is the most idiomatic and future-proof way to handle state in zoneless applications. * **(Alternative) Use \`markForCheck()\`**: If converting to a signal is too complex or would require extensive refactoring, you can instead inject \`ChangeDetectorRef\` and call \`this.cdr.markForCheck()\` immediately after the property is updated. - #### Step 2: Add \`ChangeDetectionStrategy.Default\` + #### Step 2: Add \`ChangeDetectionStrategy.Eager\` After you have refactored all necessary properties, you must update the component's decorator to explicitly set the change detection strategy. 1. Add \`ChangeDetectionStrategy\` to the import from \`@angular/core\`. - 2. In the \`@Component\` decorator, add the property \`changeDetection: ChangeDetectionStrategy.Default\`. + 2. In the \`@Component\` decorator, add the property \`changeDetection: ChangeDetectionStrategy.Eager\`. 3. Add a \`// TODO\` comment above this line explaining that the component should be fully migrated to \`OnPush\` after the application has been tested with these changes. Example: @@ -143,14 +143,14 @@ export function generateZonelessMigrationInstructionsForComponent( ... // TODO: This component has been partially migrated to be zoneless-compatible. // After testing, this should be updated to ChangeDetectionStrategy.OnPush. - changeDetection: ChangeDetectionStrategy.Default, + changeDetection: ChangeDetectionStrategy.Eager, }) \`\`\` ### IMPORTANT: Rules and Constraints You must follow these rules without exception: 1. **DO** apply one of the two refactoring strategies (signals or \`markForCheck()\`) for all relevant component properties. - 2. **DO** add \`changeDetection: ChangeDetectionStrategy.Default\` with the specified TODO comment as the final code change. + 2. **DO** add \`changeDetection: ChangeDetectionStrategy.Eager\` with the specified TODO comment as the final code change. 3. **DO NOT** use \`ChangeDetectionStrategy.OnPush\`. This will be the next step in the migration, but it is not part of this task. 4. **DO NOT** modify properties that are already signals or are used with the \`async\` pipe in the template, as they are already zoneless-compatible. 5. **DO NOT** make any changes to files other than the component file at \`${filePath}\` and its direct template/style files if necessary. diff --git a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts index 065d4e28669e..28941e47b355 100644 --- a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts +++ b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts @@ -204,8 +204,7 @@ async function categorizeFile( componentTestFiles.add(sourceFile); } else if (componentSpecifier) { if ( - !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.OnPush') && - !content.includes('changeDetectionStrategy: ChangeDetectionStrategy.Default') + !/changeDetectionStrategy:\s*ChangeDetectionStrategy\.(?:OnPush|Default|Eager)/.test(content) ) { filesWithComponents.add(sourceFile); } else { diff --git a/packages/schematics/angular/component/index_spec.ts b/packages/schematics/angular/component/index_spec.ts index 3aea03309e12..5bff59152715 100644 --- a/packages/schematics/angular/component/index_spec.ts +++ b/packages/schematics/angular/component/index_spec.ts @@ -23,7 +23,7 @@ describe('Component Schematic', () => { inlineStyle: false, inlineTemplate: false, displayBlock: false, - changeDetection: ChangeDetection.Default, + changeDetection: ChangeDetection.Eager, style: Style.Css, type: 'Component', skipTests: false, diff --git a/packages/schematics/angular/component/schema.json b/packages/schematics/angular/component/schema.json index eaa2c95f197b..a2c04647abf5 100644 --- a/packages/schematics/angular/component/schema.json +++ b/packages/schematics/angular/component/schema.json @@ -65,9 +65,9 @@ }, "changeDetection": { "description": "Configures the change detection strategy for the component.", - "enum": ["Default", "OnPush"], + "enum": ["Eager", "OnPush"], "type": "string", - "default": "Default", + "default": "Eager", "alias": "c" }, "prefix": {