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)) {
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(

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',
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.`
]);
}));

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',
() => {
@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.`
]);
});