diff --git a/modules/@angular/compiler/src/metadata_resolver.ts b/modules/@angular/compiler/src/metadata_resolver.ts index fc26623085..7b6244ee43 100644 --- a/modules/@angular/compiler/src/metadata_resolver.ts +++ b/modules/@angular/compiler/src/metadata_resolver.ts @@ -14,9 +14,8 @@ import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import * as cpl from './compile_metadata'; import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveResolver} from './directive_resolver'; -import {ListWrapper, StringMapWrapper} from './facade/collection'; -import {isBlank, isPresent, stringify} from './facade/lang'; -import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers'; +import {stringify} from './facade/lang'; +import {Identifiers, resolveIdentifier} from './identifiers'; import {CompilerInjectable} from './injectable'; import {hasLifecycleHook} from './lifecycle_reflector'; import {NgModuleResolver} from './ng_module_resolver'; @@ -25,7 +24,7 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {SummaryResolver} from './summary_resolver'; import {getUrlScheme} from './url_resolver'; -import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util'; +import {MODULE_SUFFIX, SyntaxError, ValueTransformer, visitValue} from './util'; export type ErrorCollector = (error: any, type?: any) => void; export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector'); @@ -70,7 +69,7 @@ export class CompileMetadataResolver { } } - clearCache() { + clearCache(): void { this._directiveCache.clear(); this._nonNormalizedDirectiveCache.clear(); this._summaryCache.clear(); @@ -337,14 +336,14 @@ export class CompileMetadataResolver { } let providers: cpl.CompileProviderMetadata[] = []; - if (isPresent(dirMeta.providers)) { + if (dirMeta.providers != null) { providers = this._getProvidersMetadata( dirMeta.providers, entryComponentMetadata, `providers for "${stringifyType(directiveType)}"`, [], directiveType); } let queries: cpl.CompileQueryMetadata[] = []; let viewQueries: cpl.CompileQueryMetadata[] = []; - if (isPresent(dirMeta.queries)) { + if (dirMeta.queries != null) { queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType); viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType); } @@ -807,14 +806,14 @@ export class CompileMetadataResolver { token = paramEntry.attributeName; } else if (paramEntry instanceof Inject) { token = paramEntry.token; - } else if (isValidType(paramEntry) && isBlank(token)) { + } else if (isValidType(paramEntry) && token == null) { token = paramEntry; } }); } else { token = param; } - if (isBlank(token)) { + if (token == null) { hasUnknownDeps = true; return null; } @@ -864,6 +863,7 @@ export class CompileMetadataResolver { provider = resolveForwardRef(provider); let providerMeta: cpl.ProviderMeta; if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) { + this._validateProvider(provider); providerMeta = new cpl.ProviderMeta(provider.provide, provider); } else if (isValidType(provider)) { providerMeta = new cpl.ProviderMeta(provider, {useClass: provider}); @@ -897,6 +897,16 @@ export class CompileMetadataResolver { return compileProviders; } + private _validateProvider(provider: any): void { + if (provider.hasOwnProperty('useClass') && provider.useClass == null) { + this._reportError(new SyntaxError( + `Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}. + Usually it happens when: + 1. There's a circular dependency (might be caused by using index.ts (barrel) files). + 2. Class was used before it was declared. Use forwardRef in this case.`)); + } + } + private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any): cpl.CompileEntryComponentMetadata[] { const components: cpl.CompileEntryComponentMetadata[] = []; diff --git a/modules/@angular/compiler/test/metadata_resolver_spec.ts b/modules/@angular/compiler/test/metadata_resolver_spec.ts index 0bcec64de6..e7255eaad1 100644 --- a/modules/@angular/compiler/test/metadata_resolver_spec.ts +++ b/modules/@angular/compiler/test/metadata_resolver_spec.ts @@ -222,6 +222,17 @@ export function main() { SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`); })); + it('should throw with descriptive error message when encounter invalid provider', + inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { + @NgModule({providers: [{provide: SimpleService, useClass: undefined}]}) + class SomeModule { + } + + expect(() => resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, true)) + .toThrowError( + SyntaxError, /Invalid provider for SimpleService. useClass cannot be undefined./); + })); + it('should throw with descriptive error message when one of providers is not present', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { @NgModule({declarations: [MyBrokenComp3]})