fix(compiler): throw an error for invalid provider (#13544)
Closes #8870
This commit is contained in:
parent
174334dec3
commit
445ed43b9a
|
@ -14,9 +14,8 @@ import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
import * as cpl from './compile_metadata';
|
import * as cpl from './compile_metadata';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {ListWrapper, StringMapWrapper} from './facade/collection';
|
import {stringify} from './facade/lang';
|
||||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
import {Identifiers, resolveIdentifier} from './identifiers';
|
||||||
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
|
|
||||||
import {CompilerInjectable} from './injectable';
|
import {CompilerInjectable} from './injectable';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
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 {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||||
import {SummaryResolver} from './summary_resolver';
|
import {SummaryResolver} from './summary_resolver';
|
||||||
import {getUrlScheme} from './url_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 type ErrorCollector = (error: any, type?: any) => void;
|
||||||
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
|
||||||
|
@ -70,7 +69,7 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache(): void {
|
||||||
this._directiveCache.clear();
|
this._directiveCache.clear();
|
||||||
this._nonNormalizedDirectiveCache.clear();
|
this._nonNormalizedDirectiveCache.clear();
|
||||||
this._summaryCache.clear();
|
this._summaryCache.clear();
|
||||||
|
@ -337,14 +336,14 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
let providers: cpl.CompileProviderMetadata[] = [];
|
let providers: cpl.CompileProviderMetadata[] = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (dirMeta.providers != null) {
|
||||||
providers = this._getProvidersMetadata(
|
providers = this._getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata,
|
dirMeta.providers, entryComponentMetadata,
|
||||||
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
||||||
}
|
}
|
||||||
let queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
if (isPresent(dirMeta.queries)) {
|
if (dirMeta.queries != null) {
|
||||||
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
||||||
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||||
}
|
}
|
||||||
|
@ -807,14 +806,14 @@ export class CompileMetadataResolver {
|
||||||
token = paramEntry.attributeName;
|
token = paramEntry.attributeName;
|
||||||
} else if (paramEntry instanceof Inject) {
|
} else if (paramEntry instanceof Inject) {
|
||||||
token = paramEntry.token;
|
token = paramEntry.token;
|
||||||
} else if (isValidType(paramEntry) && isBlank(token)) {
|
} else if (isValidType(paramEntry) && token == null) {
|
||||||
token = paramEntry;
|
token = paramEntry;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
token = param;
|
token = param;
|
||||||
}
|
}
|
||||||
if (isBlank(token)) {
|
if (token == null) {
|
||||||
hasUnknownDeps = true;
|
hasUnknownDeps = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -864,6 +863,7 @@ export class CompileMetadataResolver {
|
||||||
provider = resolveForwardRef(provider);
|
provider = resolveForwardRef(provider);
|
||||||
let providerMeta: cpl.ProviderMeta;
|
let providerMeta: cpl.ProviderMeta;
|
||||||
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
||||||
|
this._validateProvider(provider);
|
||||||
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
|
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
|
||||||
} else if (isValidType(provider)) {
|
} else if (isValidType(provider)) {
|
||||||
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
|
||||||
|
@ -897,6 +897,16 @@ export class CompileMetadataResolver {
|
||||||
return compileProviders;
|
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):
|
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
|
||||||
cpl.CompileEntryComponentMetadata[] {
|
cpl.CompileEntryComponentMetadata[] {
|
||||||
const components: cpl.CompileEntryComponentMetadata[] = [];
|
const components: cpl.CompileEntryComponentMetadata[] = [];
|
||||||
|
|
|
@ -222,6 +222,17 @@ export function main() {
|
||||||
SyntaxError, `Can't resolve all parameters for NonAnnotatedService: (?).`);
|
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',
|
it('should throw with descriptive error message when one of providers is not present',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
@NgModule({declarations: [MyBrokenComp3]})
|
@NgModule({declarations: [MyBrokenComp3]})
|
||||||
|
|
Loading…
Reference in New Issue