fix(compiler): auto declare `entryComponents` recursively

Closes #10348
This commit is contained in:
Tobias Bosch 2016-07-28 02:50:50 -07:00
parent fb3608aa5d
commit a32c4ad2f0
3 changed files with 37 additions and 32 deletions

View File

@ -280,10 +280,6 @@ export class CompileMetadataResolver {
if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) { if (declaredDirMeta = this.getDirectiveMetadata(declaredType, false)) {
this._addDirectiveToModule( this._addDirectiveToModule(
declaredDirMeta, moduleType, transitiveModule, declaredDirectives, true); 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)) { } else if (declaredPipeMeta = this.getPipeMetadata(declaredType, false)) {
this._addPipeToModule( this._addPipeToModule(
declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true); declaredPipeMeta, moduleType, transitiveModule, declaredPipes, true);
@ -338,9 +334,6 @@ export class CompileMetadataResolver {
this._addDirectiveToModule( this._addDirectiveToModule(
compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
moduleMeta.declaredDirectives); moduleMeta.declaredDirectives);
this._getTransitiveViewDirectivesAndPipes(
compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
moduleMeta.declaredDirectives, moduleMeta.declaredPipes);
moduleMeta.transitiveModule.entryComponents.push(compMeta.type); moduleMeta.transitiveModule.entryComponents.push(compMeta.type);
moduleMeta.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!`); `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) => { moduleMeta.entryComponents.forEach((entryComponentType) => {
if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) { if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) {
this._addDirectiveToModule( 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.`); `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) { private _addTypeToModule(type: Type, moduleType: Type) {
@ -394,10 +381,7 @@ export class CompileMetadataResolver {
private _getTransitiveViewDirectivesAndPipes( private _getTransitiveViewDirectivesAndPipes(
compMeta: cpl.CompileDirectiveMetadata, moduleType: any, compMeta: cpl.CompileDirectiveMetadata, moduleMeta: cpl.CompileNgModuleMetadata) {
transitiveModule: cpl.TransitiveCompileNgModuleMetadata,
declaredDirectives: cpl.CompileDirectiveMetadata[],
declaredPipes: cpl.CompilePipeMetadata[]) {
if (!compMeta.isComponent) { if (!compMeta.isComponent) {
return; return;
} }
@ -407,7 +391,8 @@ export class CompileMetadataResolver {
`Unexpected pipe value '${pipeType}' on the View of component '${stringify(compMeta.type.runtime)}'`); `Unexpected pipe value '${pipeType}' on the View of component '${stringify(compMeta.type.runtime)}'`);
} }
const pipeMeta = this.getPipeMetadata(pipeType); 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) => { const addDirective = (dirType: Type) => {
@ -416,9 +401,10 @@ export class CompileMetadataResolver {
`Unexpected directive value '${dirType}' on the View of component '${stringify(compMeta.type.runtime)}'`); `Unexpected directive value '${dirType}' on the View of component '${stringify(compMeta.type.runtime)}'`);
} }
const dirMeta = this.getDirectiveMetadata(dirType); const dirMeta = this.getDirectiveMetadata(dirType);
if (this._addDirectiveToModule(dirMeta, moduleType, transitiveModule, declaredDirectives)) { if (this._addDirectiveToModule(
this._getTransitiveViewDirectivesAndPipes( dirMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
dirMeta, moduleType, transitiveModule, declaredDirectives, declaredPipes); moduleMeta.declaredDirectives)) {
this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta);
} }
}; };
const view = this._viewResolver.resolve(compMeta.type.runtime); const view = this._viewResolver.resolve(compMeta.type.runtime);
@ -428,6 +414,13 @@ export class CompileMetadataResolver {
if (view.directives) { if (view.directives) {
flattenArray(view.directives).forEach(addDirective); 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( private _getTransitiveNgModuleMetadata(

View File

@ -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', it('should warn and auto declare if the component was not declared nor imported by the module',
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
@Component({selector: 'child', template: ''}) @Component({selector: 'nestedchild', template: ''})
class NestedChildComp {
}
@Component({selector: 'child', template: '', entryComponents: [NestedChildComp]})
class ChildComp { class ChildComp {
} }
@ -50,7 +54,8 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(cf.componentType).toBe(ChildComp); expect(cf.componentType).toBe(ChildComp);
expect(console.warnings).toEqual([ 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.`
]); ]);
})); }));

View File

@ -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', 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 { class SomeModule {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver
.toBe(SomeComp); .resolveComponentFactory(SomeCompWithEntryComponents)
.componentType)
.toBe(SomeCompWithEntryComponents);
expect(console.warnings).toEqual([ 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.`
]); ]);
}); });