From 44bb337accba4d575bd8cdf948850beee5990eae Mon Sep 17 00:00:00 2001 From: Bowen Ni Date: Mon, 6 Feb 2017 17:55:58 -0800 Subject: [PATCH] fix(compiler): disable non-components as an entry component (#14335) PR Close #14335 --- .../compiler/src/metadata_resolver.ts | 15 +++-- .../compiler/test/metadata_resolver_spec.ts | 57 ++++++++++++++++++- .../test/browser/bootstrap_spec.ts | 12 ++-- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index 647a5d11ec..afb5a2fd4d 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -930,7 +930,7 @@ export class CompileMetadataResolver { extractIdentifiers(provider.useValue, collectedIdentifiers); collectedIdentifiers.forEach((identifier) => { - const entry = this._getEntryComponentMetadata(identifier.reference); + const entry = this._getEntryComponentMetadata(identifier.reference, false); if (entry) { components.push(entry); } @@ -938,17 +938,22 @@ export class CompileMetadataResolver { return components; } - private _getEntryComponentMetadata(dirType: any): cpl.CompileEntryComponentMetadata { + private _getEntryComponentMetadata(dirType: any, throwIfNotFound = true): + cpl.CompileEntryComponentMetadata { const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType); - if (dirMeta) { + if (dirMeta && dirMeta.metadata.isComponent) { return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory}; } else { const dirSummary = this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); - if (dirSummary) { + if (dirSummary && dirSummary.isComponent) { return {componentType: dirType, componentFactory: dirSummary.componentFactory}; } } + + if (throwIfNotFound) { + throw syntaxError(`${dirType.name} cannot be used as an entry component.`); + } } getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata { @@ -1109,4 +1114,4 @@ function componentStillLoadingError(compType: Type) { Error(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`); (error as any)[ERROR_COMPONENT_TYPE] = compType; return error; -} \ No newline at end of file +} diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index 9f6c50a5ba..70328dc020 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -7,7 +7,7 @@ */ import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings'; -import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core'; +import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core'; import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks'; import {TestBed, async, inject} from '@angular/core/testing'; @@ -312,6 +312,61 @@ export function main() { expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module5, true)) .toThrowError(`['{', '}}'] contains unusable interpolation symbol.`); })); + + it(`should throw an error when a Pipe is added to module's bootstrap list`, + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + + @Pipe({name: 'pipe'}) + class MyPipe { + } + + @NgModule({declarations: [MyPipe], bootstrap: [MyPipe]}) + class ModuleWithPipeInBootstrap { + } + + expect(() => resolver.getNgModuleMetadata(ModuleWithPipeInBootstrap)) + .toThrowError(`MyPipe cannot be used as an entry component.`); + })); + + it(`should throw an error when a Service is added to module's bootstrap list`, + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + + @NgModule({declarations: [], bootstrap: [SimpleService]}) + class ModuleWithServiceInBootstrap { + } + + expect(() => resolver.getNgModuleMetadata(ModuleWithServiceInBootstrap)) + .toThrowError(`SimpleService cannot be used as an entry component.`); + })); + + it(`should throw an error when a Directive is added to module's bootstrap list`, + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + + @Directive({selector: 'directive'}) + class MyDirective { + } + + @NgModule({declarations: [], bootstrap: [MyDirective]}) + class ModuleWithDirectiveInBootstrap { + } + + expect(() => resolver.getNgModuleMetadata(ModuleWithDirectiveInBootstrap)) + .toThrowError(`MyDirective cannot be used as an entry component.`); + })); + + it(`should not throw an error when a Component is added to module's bootstrap list`, + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + + @Component({template: ''}) + class MyComp { + } + + @NgModule({declarations: [MyComp], bootstrap: [MyComp]}) + class ModuleWithComponentInBootstrap { + } + + expect(() => resolver.getNgModuleMetadata(ModuleWithComponentInBootstrap)).not.toThrow(); + })); }); it('should dedupe declarations in NgModule', diff --git a/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts b/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts index e0e883da7b..d27cd97480 100644 --- a/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts +++ b/modules/@angular/platform-browser/test/browser/bootstrap_spec.ts @@ -155,13 +155,11 @@ export function main() { const logger = new MockConsole(); const errorHandler = new ErrorHandler(false); errorHandler._console = logger as any; - bootstrap(HelloRootDirectiveIsNotCmp, [ - {provide: ErrorHandler, useValue: errorHandler} - ]).catch((e) => { - expect(e.message).toBe( - `Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`); - done.done(); - }); + expect( + () => bootstrap( + HelloRootDirectiveIsNotCmp, [{provide: ErrorHandler, useValue: errorHandler}])) + .toThrowError(`HelloRootDirectiveIsNotCmp cannot be used as an entry component.`); + done.done(); })); it('should throw if no element is found',