diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index f986e71771..85f5e6742e 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -2117,7 +2117,7 @@ describe('compiler compliance', () => { selectors: [["lifecycle-comp"]], factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); }, inputs: {nameMin: ["name", "nameMin"]}, - features: [$r3$.ɵNgOnChangesFeature], + features: [$r3$.ɵNgOnChangesFeature()], consts: 0, vars: 0, template: function LifecycleComp_Template(rf, ctx) {}, @@ -2239,7 +2239,7 @@ describe('compiler compliance', () => { factory: function ForOfDirective_Factory(t) { return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef)); }, - features: [$r3$.ɵNgOnChangesFeature], + features: [$r3$.ɵNgOnChangesFeature()], inputs: {forOf: "forOf"} }); `; @@ -2315,7 +2315,7 @@ describe('compiler compliance', () => { factory: function ForOfDirective_Factory(t) { return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef)); }, - features: [$r3$.ɵNgOnChangesFeature], + features: [$r3$.ɵNgOnChangesFeature()], inputs: {forOf: "forOf"} }); `; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 4adc4cbd39..2f1ac60ea4 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -125,7 +125,7 @@ function baseDirectiveFields( */ function addFeatures( definitionMap: DefinitionMap, meta: R3DirectiveMetadata | R3ComponentMetadata) { - // e.g. `features: [NgOnChangesFeature]` + // e.g. `features: [NgOnChangesFeature()]` const features: o.Expression[] = []; const providers = meta.providers; @@ -142,7 +142,7 @@ function addFeatures( features.push(o.importExpr(R3.InheritDefinitionFeature)); } if (meta.lifecycle.usesOnChanges) { - features.push(o.importExpr(R3.NgOnChangesFeature)); + features.push(o.importExpr(R3.NgOnChangesFeature).callFn(EMPTY_ARRAY)); } if (features.length) { definitionMap.set('features', o.literalArr(features)); diff --git a/packages/core/src/render3/features/ng_onchanges_feature.ts b/packages/core/src/render3/features/ng_onchanges_feature.ts index 9ebb00bc6f..66a5619330 100644 --- a/packages/core/src/render3/features/ng_onchanges_feature.ts +++ b/packages/core/src/render3/features/ng_onchanges_feature.ts @@ -35,11 +35,18 @@ type OnChangesExpando = OnChanges & { * static ngComponentDef = defineComponent({ * ... * inputs: {name: 'publicName'}, - * features: [NgOnChangesFeature] + * features: [NgOnChangesFeature()] * }); * ``` */ -export function NgOnChangesFeature(definition: DirectiveDef): void { +export function NgOnChangesFeature(): DirectiveDefFeature { + // This option ensures that the ngOnChanges lifecycle hook will be inherited + // from superclasses (in InheritDefinitionFeature). + (NgOnChangesFeatureImpl as DirectiveDefFeature).ngInherit = true; + return NgOnChangesFeatureImpl; +} + +function NgOnChangesFeatureImpl(definition: DirectiveDef): void { if (definition.type.prototype.ngOnChanges) { definition.setInput = ngOnChangesSetInput; @@ -91,11 +98,6 @@ function setSimpleChangesStore(instance: any, store: NgSimpleChangesStore): NgSi return instance[SIMPLE_CHANGES_STORE] = store; } -// This option ensures that the ngOnChanges lifecycle hook will be inherited -// from superclasses (in InheritDefinitionFeature). -(NgOnChangesFeature as DirectiveDefFeature).ngInherit = true; - - interface NgSimpleChangesStore { previous: SimpleChanges; current: SimpleChanges|null; diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 78be2de004..2924344433 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -302,11 +302,27 @@ export type PipeDefWithMeta = PipeDef; export interface DirectiveDefFeature { (directiveDef: DirectiveDef): void; + /** + * Marks a feature as something that {@link InheritDefinitionFeature} will execute + * during inheritance. + * + * NOTE: DO NOT SET IN ROOT OF MODULE! Doing so will result in tree-shakers/bundlers + * identifying the change as a side effect, and the feature will be included in + * every bundle. + */ ngInherit?: true; } export interface ComponentDefFeature { (componentDef: ComponentDef): void; + /** + * Marks a feature as something that {@link InheritDefinitionFeature} will execute + * during inheritance. + * + * NOTE: DO NOT SET IN ROOT OF MODULE! Doing so will result in tree-shakers/bundlers + * identifying the change as a side effect, and the feature will be included in + * every bundle. + */ ngInherit?: true; } diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 84854e952d..075c989328 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -80,9 +80,6 @@ { "name": "NO_PARENT_INJECTOR" }, - { - "name": "NgOnChangesFeature" - }, { "name": "NodeInjectorFactory" }, @@ -104,12 +101,6 @@ { "name": "SANITIZER" }, - { - "name": "SIMPLE_CHANGES_STORE" - }, - { - "name": "SimpleChange" - }, { "name": "TVIEW" }, @@ -308,9 +299,6 @@ { "name": "getRootView" }, - { - "name": "getSimpleChangesStore" - }, { "name": "hasParentInjector" }, @@ -368,9 +356,6 @@ { "name": "nextNgElementId" }, - { - "name": "ngOnChangesSetInput" - }, { "name": "noSideEffects" }, @@ -440,9 +425,6 @@ { "name": "setPreviousOrParentTNode" }, - { - "name": "setSimpleChangesStore" - }, { "name": "setTNodeAndViewData" }, @@ -460,8 +442,5 @@ }, { "name": "viewAttached" - }, - { - "name": "wrapOnChanges" } ] \ No newline at end of file diff --git a/packages/core/test/bundling/injection/bundle.golden_symbols.json b/packages/core/test/bundling/injection/bundle.golden_symbols.json index 2304efae4d..33e1162f8f 100644 --- a/packages/core/test/bundling/injection/bundle.golden_symbols.json +++ b/packages/core/test/bundling/injection/bundle.golden_symbols.json @@ -8,9 +8,6 @@ { "name": "EMPTY_ARRAY" }, - { - "name": "EMPTY_OBJ" - }, { "name": "EmptyErrorImpl" }, @@ -38,9 +35,6 @@ { "name": "NULL_INJECTOR" }, - { - "name": "NgOnChangesFeature" - }, { "name": "NullInjector" }, @@ -56,18 +50,12 @@ { "name": "R3Injector" }, - { - "name": "SIMPLE_CHANGES_STORE" - }, { "name": "ScopedService" }, { "name": "Self" }, - { - "name": "SimpleChange" - }, { "name": "SkipSelf" }, @@ -125,9 +113,6 @@ { "name": "getNullInjector" }, - { - "name": "getSimpleChangesStore" - }, { "name": "hasDeps" }, @@ -170,9 +155,6 @@ { "name": "makeRecord" }, - { - "name": "ngOnChangesSetInput" - }, { "name": "providerToFactory" }, @@ -185,13 +167,7 @@ { "name": "setCurrentInjector" }, - { - "name": "setSimpleChangesStore" - }, { "name": "stringify" - }, - { - "name": "wrapOnChanges" } ] \ No newline at end of file diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 0ddada3067..f93c9288a8 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -143,9 +143,6 @@ { "name": "NgModuleRef" }, - { - "name": "NgOnChangesFeature" - }, { "name": "NodeInjector" }, @@ -185,9 +182,6 @@ { "name": "SANITIZER" }, - { - "name": "SIMPLE_CHANGES_STORE" - }, { "name": "SWITCH_ELEMENT_REF_FACTORY" }, @@ -197,9 +191,6 @@ { "name": "SWITCH_VIEW_CONTAINER_REF_FACTORY" }, - { - "name": "SimpleChange" - }, { "name": "SkipSelf" }, @@ -785,9 +776,6 @@ { "name": "getRootView" }, - { - "name": "getSimpleChangesStore" - }, { "name": "getSinglePropIndexValue" }, @@ -1016,9 +1004,6 @@ { "name": "nextNgElementId" }, - { - "name": "ngOnChangesSetInput" - }, { "name": "noSideEffects" }, @@ -1187,9 +1172,6 @@ { "name": "setSanitizeFlag" }, - { - "name": "setSimpleChangesStore" - }, { "name": "setStyle" }, @@ -1255,8 +1237,5 @@ }, { "name": "wrapListenerWithPreventDefault" - }, - { - "name": "wrapOnChanges" } ] \ No newline at end of file diff --git a/packages/core/test/render3/common_with_def.ts b/packages/core/test/render3/common_with_def.ts index 73cbda6bf8..c51ed2b167 100644 --- a/packages/core/test/render3/common_with_def.ts +++ b/packages/core/test/render3/common_with_def.ts @@ -40,7 +40,7 @@ NgForOf.ngDirectiveDef = defineDirective({ type: NgTemplateOutletDef, selectors: [['', 'ngTemplateOutlet', '']], factory: () => new NgTemplateOutletDef(directiveInject(ViewContainerRef as any)), - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], inputs: {ngTemplateOutlet: 'ngTemplateOutlet', ngTemplateOutletContext: 'ngTemplateOutletContext'} }); diff --git a/packages/core/test/render3/host_binding_spec.ts b/packages/core/test/render3/host_binding_spec.ts index 35ab940486..c9108ef69f 100644 --- a/packages/core/test/render3/host_binding_spec.ts +++ b/packages/core/test/render3/host_binding_spec.ts @@ -357,7 +357,7 @@ describe('host bindings', () => { template: (rf: RenderFlags, ctx: InitHookComp) => {}, consts: 0, vars: 0, - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], hostBindings: (rf: RenderFlags, ctx: InitHookComp, elIndex: number) => { if (rf & RenderFlags.Create) { allocHostVars(1); diff --git a/packages/core/test/render3/inherit_definition_feature_spec.ts b/packages/core/test/render3/inherit_definition_feature_spec.ts index cd04aa05c2..d51f096aa1 100644 --- a/packages/core/test/render3/inherit_definition_feature_spec.ts +++ b/packages/core/test/render3/inherit_definition_feature_spec.ts @@ -501,7 +501,7 @@ describe('InheritDefinitionFeature', () => { type: SuperDirective, selectors: [['', 'superDir', '']], factory: () => new SuperDirective(), - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], inputs: {someInput: 'someInput'} }); } diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index b612c4b19d..dc2c9ac96c 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -2008,7 +2008,7 @@ describe('lifecycles', () => { vars: vars, inputs: {a: 'val1', b: ['publicVal2', 'val2']}, template, directives: directives, - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], }); }; } @@ -2027,7 +2027,7 @@ describe('lifecycles', () => { selectors: [['', 'dir', '']], factory: () => new Directive(), inputs: {a: 'val1', b: ['publicVal2', 'val2']}, - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], }); } @@ -2732,7 +2732,7 @@ describe('lifecycles', () => { vars: vars, inputs: {val: 'val'}, template, directives: directives, - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], }); }; } diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 1283409be3..14c1dc4772 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -1631,7 +1631,7 @@ describe('ViewContainerRef', () => { textBinding(0, interpolation1('', cmp.name, '')); } }, - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], inputs: {name: 'name'} }); } @@ -1678,7 +1678,7 @@ describe('ViewContainerRef', () => { } }, directives: [ComponentWithHooks, DirectiveWithVCRef], - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], }); } @@ -1771,7 +1771,7 @@ describe('ViewContainerRef', () => { } }, directives: [ComponentWithHooks, DirectiveWithVCRef], - features: [NgOnChangesFeature], + features: [NgOnChangesFeature()], }); } diff --git a/tools/public_api_guard/common/common.d.ts b/tools/public_api_guard/common/common.d.ts index 58fbcfd538..c77d0dac54 100644 --- a/tools/public_api_guard/common/common.d.ts +++ b/tools/public_api_guard/common/common.d.ts @@ -306,8 +306,8 @@ export declare class NgSwitchDefault { } export declare class NgTemplateOutlet implements OnChanges { - ngTemplateOutlet: TemplateRef; - ngTemplateOutletContext: Object; + ngTemplateOutlet: TemplateRef | null; + ngTemplateOutletContext: Object | null; constructor(_viewContainerRef: ViewContainerRef); ngOnChanges(changes: SimpleChanges): void; }