fix(core): only warn and auto declare undeclared `entryComponents`.

This is needed to support existing applications.
After final these warnings will become errors.

Closes #10316
This commit is contained in:
Tobias Bosch 2016-07-27 01:45:20 -07:00
parent 69e72c0786
commit e44e8668ea
3 changed files with 51 additions and 22 deletions

View File

@ -351,17 +351,23 @@ export class CompileMetadataResolver {
} }
}); });
moduleMeta.declaredDirectives.forEach((dirMeta) => { moduleMeta.declaredDirectives.forEach((dirMeta) => {
dirMeta.entryComponents.forEach((entryComponent) => { dirMeta.entryComponents.forEach((entryComponentType) => {
if (!moduleMeta.transitiveModule.directivesSet.has(entryComponent.runtime)) { if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) {
throw new BaseException( this._addDirectiveToModule(
`Component ${stringify(dirMeta.type.runtime)} in NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponent.runtime)} via "entryComponents" but it was neither declared nor imported into the module!`); 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)) {
throw new BaseException( this._addDirectiveToModule(
`NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported!`); this.getDirectiveMetadata(entryComponentType.runtime), moduleMeta.type.runtime,
moduleMeta.transitiveModule, moduleMeta.declaredDirectives);
this._console.warn(
`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.`);
} }
}); });
} }

View File

@ -7,20 +7,33 @@
*/ */
import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter, beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
import {TestComponentBuilder, configureModule} from '@angular/core/testing'; import {TestComponentBuilder, configureModule, configureCompiler} from '@angular/core/testing';
import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_ENTRY_COMPONENTS, ViewMetadata} from '@angular/core'; import {Component, ComponentFactoryResolver, NoComponentFactoryError, forwardRef, ANALYZE_FOR_ENTRY_COMPONENTS, ViewMetadata} from '@angular/core';
import {stringify} from '../../src/facade/lang'; import {stringify} from '../../src/facade/lang';
import {Console} from '../../src/console';
export function main() { export function main() {
describe('jit', () => { declareTests({useJit: true}); }); describe('jit', () => { declareTests({useJit: true}); });
describe('no jit', () => { declareTests({useJit: false}); }); describe('no jit', () => { declareTests({useJit: false}); });
} }
class DummyConsole implements Console {
public warnings: string[] = [];
log(message: string) {}
warn(message: string) { this.warnings.push(message); }
}
function declareTests({useJit}: {useJit: boolean}) { function declareTests({useJit}: {useJit: boolean}) {
describe('@Component.entryComponents', function() { describe('@Component.entryComponents', function() {
beforeEach(() => { configureModule({declarations: [MainComp, ChildComp, NestedChildComp]}); }); var console: DummyConsole;
beforeEach(() => {
console = new DummyConsole();
configureCompiler({useJit: useJit, providers: [{provide: Console, useValue: console}]});
configureModule({declarations: [MainComp, ChildComp, NestedChildComp]});
});
it('should error 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: 'child', template: ''})
@ -31,9 +44,14 @@ function declareTests({useJit}: {useJit: boolean}) {
class SomeComp { class SomeComp {
} }
expect(() => tcb.createSync(SomeComp)) const compFixture = tcb.createSync(SomeComp);
.toThrowError( const cf = compFixture.componentRef.injector.get(ComponentFactoryResolver)
`Component ${stringify(SomeComp)} in NgModule DynamicTestModule uses ${stringify(ChildComp)} via "entryComponents" but it was neither declared nor imported into the module!`); .resolveComponentFactory(ChildComp);
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.`
]);
})); }));

View File

@ -164,16 +164,6 @@ function declareTests({useJit}: {useJit: boolean}) {
`Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`); `Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`);
}); });
it('should error when using an entryComponent that was neither declared nor imported', () => {
@NgModule({entryComponents: [SomeComp]})
class SomeModule {
}
expect(() => createModule(SomeModule))
.toThrowError(
`NgModule ${stringify(SomeModule)} uses ${stringify(SomeComp)} via "entryComponents" but it was neither declared nor imported!`);
});
it('should error if a directive is declared in more than 1 module', () => { it('should error if a directive is declared in more than 1 module', () => {
@NgModule({declarations: [SomeDirective]}) @NgModule({declarations: [SomeDirective]})
class Module1 { class Module1 {
@ -281,6 +271,21 @@ function declareTests({useJit}: {useJit: boolean}) {
.toBe(SomeComp); .toBe(SomeComp);
}); });
it('should warn and auto declare when using an entryComponent that was neither declared nor imported',
() => {
@NgModule({entryComponents: [SomeComp]})
class SomeModule {
}
const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType)
.toBe(SomeComp);
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.`
]);
});
it('should entryComponents ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => { it('should entryComponents ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => {
@NgModule({ @NgModule({
declarations: [SomeComp], declarations: [SomeComp],