feat(core): Set default Component changeDetection strategy to OnPush

The default change detection strategy is now OnPush.

BREAKING CHANGE: Component with undefined `changeDetection` property are now `OnPush` by default. Specify `changeDetection: ChangeDetectionStrategy.Eager` to keep the previous behavior.
This commit is contained in:
Matthieu Riegler
2026-03-16 09:27:03 +01:00
committed by Leon Senft
parent 050b14bd7f
commit eae8f7e30b
121 changed files with 5310 additions and 272 deletions
@@ -36,10 +36,10 @@ import {AstObject, AstValue} from '../../ast/ast_value';
import {FatalLinkerError} from '../../fatal_linker_error';
import {GetSourceFileFn} from '../get_source_file';
import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system/src/types';
import {toR3DirectiveMeta} from './partial_directive_linker_1';
import {LinkedDefinition, PartialLinker} from './partial_linker';
import {extractForwardRef, PLACEHOLDER_VERSION} from './util';
import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system/src/types';
function makeDirectiveMetadata<TExpression>(
directiveExpr: AstObject<R3DeclareDirectiveDependencyMetadata, TExpression>,
@@ -68,9 +68,10 @@ function makeDirectiveMetadata<TExpression>(
/**
* A `PartialLinker` that is designed to process `ɵɵngDeclareComponent()` call expressions.
*/
export class PartialComponentLinkerVersion1<TStatement, TExpression>
implements PartialLinker<TExpression>
{
export class PartialComponentLinkerVersion1<
TStatement,
TExpression,
> implements PartialLinker<TExpression> {
constructor(
private readonly getSourceFile: GetSourceFileFn,
private sourceUrl: AbsoluteFsPath,
@@ -103,6 +104,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression>
const enableBlockSyntax = major >= 17 || version === PLACEHOLDER_VERSION;
const enableLetSyntax =
major > 18 || (major === 18 && minor >= 1) || version === PLACEHOLDER_VERSION;
const hasOnPushByDefault = major >= 22 || version === PLACEHOLDER_VERSION;
const template = parseTemplate(templateInfo.code, templateInfo.sourceUrl, {
escapedString: templateInfo.isEscaped,
@@ -242,7 +244,9 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression>
: ViewEncapsulation.Emulated,
changeDetection: metaObj.has('changeDetection')
? parseChangeDetectionStrategy(metaObj.getValue('changeDetection'))
: ChangeDetectionStrategy.Default,
: hasOnPushByDefault
? ChangeDetectionStrategy.OnPush
: ChangeDetectionStrategy.Eager,
animations: metaObj.has('animations') ? metaObj.getOpaque('animations') : null,
relativeContextFilePath: this.sourceUrl,
relativeTemplatePath: null,
+22 -2
View File
@@ -5987,7 +5987,7 @@ runInEachFileSystem((os: string) => {
expect(messageText).toContain("Value is of type 'string'.");
});
it('should handle `changeDetection` field', () => {
it('should handle `changeDetection` field with the "default" value: OnPush', () => {
env.write(
`test.ts`,
`
@@ -6003,7 +6003,27 @@ runInEachFileSystem((os: string) => {
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('changeDetection: 0');
// because this default value is implicit
expect(jsContents).not.toContain('changeDetection: 0');
});
it('should handle `changeDetection` field', () => {
env.write(
`test.ts`,
`
import {Component, ChangeDetectionStrategy} from '@angular/core';
@Component({
selector: 'comp-a',
template: '...',
changeDetection: ChangeDetectionStrategy.Eager
})
class CompA {}
`,
);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('changeDetection: 1');
});
it('should throw if `changeDetection` contains invalid value', () => {