Skip to content

Commit

Permalink
fix(compiler): disable non-components as an entry component (angular#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bowenni authored and juleskremer committed Aug 24, 2017
1 parent a67fd99 commit f337716
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
15 changes: 10 additions & 5 deletions modules/@angular/compiler/src/metadata_resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,25 +930,30 @@ 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);
}
});
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 =
<cpl.CompileDirectiveSummary>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 {
Expand Down Expand Up @@ -1109,4 +1114,4 @@ function componentStillLoadingError(compType: Type<any>) {
Error(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
(error as any)[ERROR_COMPONENT_TYPE] = compType;
return error;
}
}
57 changes: 56 additions & 1 deletion modules/@angular/compiler/test/metadata_resolver_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -312,6 +312,61 @@ export function main() {
expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(Module5, true))
.toThrowError(`['&lbrace;', '}}'] 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',
Expand Down
12 changes: 5 additions & 7 deletions modules/@angular/platform-browser/test/browser/bootstrap_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit f337716

Please sign in to comment.