From a32c4ad2f053aef6a45001aa23e1cfb0aa4d76d1 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 28 Jul 2016 02:50:50 -0700 Subject: [PATCH] fix(compiler): auto declare `entryComponents` recursively Closes #10348 --- .../compiler/src/metadata_resolver.ts | 45 ++++++++----------- .../entry_components_integration_spec.ts | 9 +++- .../test/linker/ng_module_integration_spec.ts | 15 +++++-- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 7b00c65403..ba36b7bcf1 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -280,10 +280,6 @@ export class CompileMetadataResolver { if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) { this._addDirectiveToModule( declaredDirMeta, moduleType, transitiveModule, declaredDirectives, true); - // Collect @Component.directives/pipes/entryComponents into our declared - // directives/pipes. - this._getTransitiveViewDirectivesAndPipes( - declaredDirMeta, moduleType, transitiveModule, declaredDirectives, declaredPipes); } else if (declaredPipeMeta = this.getPipeMetadata(declaredType, false)) { this._addPipeToModule( declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true); @@ -338,9 +334,6 @@ export class CompileMetadataResolver { this._addDirectiveToModule( compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, moduleMeta.declaredDirectives); - this._getTransitiveViewDirectivesAndPipes( - compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, - moduleMeta.declaredDirectives, moduleMeta.declaredPipes); moduleMeta.transitiveModule.entryComponents.push(compMeta.type); moduleMeta.entryComponents.push(compMeta.type); @@ -361,17 +354,6 @@ export class CompileMetadataResolver { `Can't export pipe ${stringify(pipeMeta.type.runtime)} from ${stringify(moduleMeta.type.runtime)} as it was neither declared nor imported!`); } }); - moduleMeta.declaredDirectives.forEach((dirMeta) => { - dirMeta.entryComponents.forEach((entryComponentType) => { - if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) { - this._addDirectiveToModule( - this.getDirectiveMetadata(entryComponentType.runtime), moduleMeta.type.runtime, - moduleMeta.transitiveModule, moduleMeta.declaredDirectives); - this._console.warn( - `Component ${stringify(dirMeta.type.runtime)} in NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.`); - } - }); - }); moduleMeta.entryComponents.forEach((entryComponentType) => { if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) { this._addDirectiveToModule( @@ -381,6 +363,11 @@ export class CompileMetadataResolver { `NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported! This warning will become an error after final.`); } }); + // Collect @Component.directives/pipes/entryComponents into our declared + // directives/pipes. Do this last so that directives added by previous steps + // are considered as well! + moduleMeta.declaredDirectives.forEach( + (dirMeta) => { this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta); }); } private _addTypeToModule(type: Type, moduleType: Type) { @@ -394,10 +381,7 @@ export class CompileMetadataResolver { private _getTransitiveViewDirectivesAndPipes( - compMeta: cpl.CompileDirectiveMetadata, moduleType: any, - transitiveModule: cpl.TransitiveCompileNgModuleMetadata, - declaredDirectives: cpl.CompileDirectiveMetadata[], - declaredPipes: cpl.CompilePipeMetadata[]) { + compMeta: cpl.CompileDirectiveMetadata, moduleMeta: cpl.CompileNgModuleMetadata) { if (!compMeta.isComponent) { return; } @@ -407,7 +391,8 @@ export class CompileMetadataResolver { `Unexpected pipe value '${pipeType}' on the View of component '${stringify(compMeta.type.runtime)}'`); } const pipeMeta = this.getPipeMetadata(pipeType); - this._addPipeToModule(pipeMeta, moduleType, transitiveModule, declaredPipes); + this._addPipeToModule( + pipeMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, moduleMeta.declaredPipes); }; const addDirective = (dirType: Type) => { @@ -416,9 +401,10 @@ export class CompileMetadataResolver { `Unexpected directive value '${dirType}' on the View of component '${stringify(compMeta.type.runtime)}'`); } const dirMeta = this.getDirectiveMetadata(dirType); - if (this._addDirectiveToModule(dirMeta, moduleType, transitiveModule, declaredDirectives)) { - this._getTransitiveViewDirectivesAndPipes( - dirMeta, moduleType, transitiveModule, declaredDirectives, declaredPipes); + if (this._addDirectiveToModule( + dirMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, + moduleMeta.declaredDirectives)) { + this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta); } }; const view = this._viewResolver.resolve(compMeta.type.runtime); @@ -428,6 +414,13 @@ export class CompileMetadataResolver { if (view.directives) { flattenArray(view.directives).forEach(addDirective); } + compMeta.entryComponents.forEach((entryComponentType) => { + if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) { + this._console.warn( + `Component ${stringify(compMeta.type.runtime)} in NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.`); + addDirective(entryComponentType.runtime); + } + }); } private _getTransitiveNgModuleMetadata( diff --git a/modules/@angular/core/test/linker/entry_components_integration_spec.ts b/modules/@angular/core/test/linker/entry_components_integration_spec.ts index 2766d25951..50b24d1e04 100644 --- a/modules/@angular/core/test/linker/entry_components_integration_spec.ts +++ b/modules/@angular/core/test/linker/entry_components_integration_spec.ts @@ -36,7 +36,11 @@ function declareTests({useJit}: {useJit: boolean}) { it('should warn and auto declare if the component was not declared nor imported by the module', inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - @Component({selector: 'child', template: ''}) + @Component({selector: 'nestedchild', template: ''}) + class NestedChildComp { + } + + @Component({selector: 'child', template: '', entryComponents: [NestedChildComp]}) class ChildComp { } @@ -50,7 +54,8 @@ function declareTests({useJit}: {useJit: boolean}) { expect(cf.componentType).toBe(ChildComp); expect(console.warnings).toEqual([ - `Component ${stringify(SomeComp)} in NgModule DynamicTestModule uses ${stringify(ChildComp)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.` + `Component ${stringify(SomeComp)} in NgModule DynamicTestModule uses ${stringify(ChildComp)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.`, + `Component ${stringify(ChildComp)} in NgModule DynamicTestModule uses ${stringify(NestedChildComp)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.` ]); })); diff --git a/modules/@angular/core/test/linker/ng_module_integration_spec.ts b/modules/@angular/core/test/linker/ng_module_integration_spec.ts index 24740b3d17..3b08cf1e6f 100644 --- a/modules/@angular/core/test/linker/ng_module_integration_spec.ts +++ b/modules/@angular/core/test/linker/ng_module_integration_spec.ts @@ -273,16 +273,23 @@ function declareTests({useJit}: {useJit: boolean}) { it('should warn and auto declare when using an entryComponent that was neither declared nor imported', () => { - @NgModule({entryComponents: [SomeComp]}) + @Component({template: '', entryComponents: [SomeComp]}) + class SomeCompWithEntryComponents { + } + + @NgModule({entryComponents: [SomeCompWithEntryComponents]}) class SomeModule { } const ngModule = createModule(SomeModule); - expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) - .toBe(SomeComp); + expect(ngModule.componentFactoryResolver + .resolveComponentFactory(SomeCompWithEntryComponents) + .componentType) + .toBe(SomeCompWithEntryComponents); expect(console.warnings).toEqual([ - `NgModule ${stringify(SomeModule)} uses ${stringify(SomeComp)} via "entryComponents" but it was neither declared nor imported! This warning will become an error after final.` + `NgModule ${stringify(SomeModule)} uses ${stringify(SomeCompWithEntryComponents)} via "entryComponents" but it was neither declared nor imported! This warning will become an error after final.`, + `Component ${stringify(SomeCompWithEntryComponents)} in NgModule ${stringify(SomeModule)} uses ${stringify(SomeComp)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.` ]); });