fix(compiler): throw descriptive error meesage for invalid NgModule providers (#10947)
Fixes #10714
This commit is contained in:
parent
5c93a8800a
commit
aa5c8ca61f
|
@ -146,8 +146,8 @@ export class CompileMetadataResolver {
|
|||
changeDetectionStrategy = cmpMeta.changeDetection;
|
||||
if (isPresent(dirMeta.viewProviders)) {
|
||||
viewProviders = this.getProvidersMetadata(
|
||||
verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders'),
|
||||
entryComponentMetadata);
|
||||
dirMeta.viewProviders, entryComponentMetadata,
|
||||
`viewProviders for "${stringify(directiveType)}"`);
|
||||
}
|
||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
|
||||
if (cmpMeta.entryComponents) {
|
||||
|
@ -169,8 +169,8 @@ export class CompileMetadataResolver {
|
|||
var providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||
if (isPresent(dirMeta.providers)) {
|
||||
providers = this.getProvidersMetadata(
|
||||
verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers'),
|
||||
entryComponentMetadata);
|
||||
dirMeta.providers, entryComponentMetadata,
|
||||
`providers for "${stringify(directiveType)}"`);
|
||||
}
|
||||
var queries: cpl.CompileQueryMetadata[] = [];
|
||||
var viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||
|
@ -227,8 +227,9 @@ export class CompileMetadataResolver {
|
|||
const moduleWithProviders: ModuleWithProviders = importedType;
|
||||
importedModuleType = moduleWithProviders.ngModule;
|
||||
if (moduleWithProviders.providers) {
|
||||
providers.push(
|
||||
...this.getProvidersMetadata(moduleWithProviders.providers, entryComponents));
|
||||
providers.push(...this.getProvidersMetadata(
|
||||
moduleWithProviders.providers, entryComponents,
|
||||
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
||||
}
|
||||
}
|
||||
if (importedModuleType) {
|
||||
|
@ -295,7 +296,9 @@ export class CompileMetadataResolver {
|
|||
// The providers of the module have to go last
|
||||
// so that they overwrite any other provider we already added.
|
||||
if (meta.providers) {
|
||||
providers.push(...this.getProvidersMetadata(meta.providers, entryComponents));
|
||||
providers.push(...this.getProvidersMetadata(
|
||||
meta.providers, entryComponents,
|
||||
`provider for the NgModule '${stringify(moduleType)}'`));
|
||||
}
|
||||
if (meta.entryComponents) {
|
||||
entryComponents.push(
|
||||
|
@ -550,17 +553,18 @@ export class CompileMetadataResolver {
|
|||
return compileToken;
|
||||
}
|
||||
|
||||
getProvidersMetadata(providers: Provider[], targetEntryComponents: cpl.CompileTypeMetadata[]):
|
||||
Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
||||
getProvidersMetadata(
|
||||
providers: Provider[], targetEntryComponents: cpl.CompileTypeMetadata[],
|
||||
debugInfo?: string): Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> {
|
||||
const compileProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||
providers.forEach((provider: any) => {
|
||||
providers.forEach((provider: any, providerIdx: number) => {
|
||||
provider = resolveForwardRef(provider);
|
||||
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
|
||||
provider = new cpl.ProviderMeta(provider.provide, provider);
|
||||
}
|
||||
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
||||
if (isArray(provider)) {
|
||||
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents);
|
||||
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
||||
} else if (provider instanceof cpl.ProviderMeta) {
|
||||
let tokenMeta = this.getTokenMetadata(provider.token);
|
||||
if (tokenMeta.equalsTo(identifierToken(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS))) {
|
||||
|
@ -571,8 +575,22 @@ export class CompileMetadataResolver {
|
|||
} else if (isValidType(provider)) {
|
||||
compileProvider = this.getTypeMetadata(provider, staticTypeModuleUrl(provider));
|
||||
} else {
|
||||
let providersInfo = (<string[]>providers.reduce(
|
||||
(soFar: string[], seenProvider: any, seenProviderIdx: number) => {
|
||||
if (seenProviderIdx < providerIdx) {
|
||||
soFar.push(`${stringify(seenProvider)}`);
|
||||
} else if (seenProviderIdx == providerIdx) {
|
||||
soFar.push(`?${stringify(seenProvider)}?`);
|
||||
} else if (seenProviderIdx == providerIdx + 1) {
|
||||
soFar.push('...');
|
||||
}
|
||||
return soFar;
|
||||
},
|
||||
[]))
|
||||
.join(', ');
|
||||
|
||||
throw new BaseException(
|
||||
`Invalid provider - only instances of Provider and Type are allowed, got: ${stringify(provider)}`);
|
||||
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`);
|
||||
}
|
||||
if (compileProvider) {
|
||||
compileProviders.push(compileProvider);
|
||||
|
@ -696,23 +714,6 @@ function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
|||
return out;
|
||||
}
|
||||
|
||||
function verifyNonBlankProviders(
|
||||
directiveType: Type<any>, providersTree: any[], providersType: string): any[] {
|
||||
var flat: any[] = [];
|
||||
var errMsg: string;
|
||||
|
||||
flattenArray(providersTree, flat);
|
||||
for (var i = 0; i < flat.length; i++) {
|
||||
if (isBlank(flat[i])) {
|
||||
errMsg = flat.map(provider => isBlank(provider) ? '?' : stringify(provider)).join(', ');
|
||||
throw new BaseException(
|
||||
`One or more of ${providersType} for "${stringify(directiveType)}" were not defined: [${errMsg}].`);
|
||||
}
|
||||
}
|
||||
|
||||
return providersTree;
|
||||
}
|
||||
|
||||
function isValidType(value: any): boolean {
|
||||
return cpl.isStaticSymbol(value) || (value instanceof Type);
|
||||
}
|
||||
|
|
|
@ -130,14 +130,14 @@ export function main() {
|
|||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp3))
|
||||
.toThrowError(
|
||||
`One or more of providers for "MyBrokenComp3" were not defined: [?, SimpleService, ?].`);
|
||||
`Invalid providers for "MyBrokenComp3" - only instances of Provider and Type are allowed, got: [SimpleService, ?null?, ...]`);
|
||||
}));
|
||||
|
||||
it('should throw with descriptive error message when one of viewProviders is not present',
|
||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
expect(() => resolver.getDirectiveMetadata(MyBrokenComp4))
|
||||
.toThrowError(
|
||||
`One or more of viewProviders for "MyBrokenComp4" were not defined: [?, SimpleService, ?].`);
|
||||
`Invalid viewProviders for "MyBrokenComp4" - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
||||
}));
|
||||
|
||||
it('should throw an error when the interpolation config has invalid symbols',
|
||||
|
@ -220,7 +220,7 @@ class MyBrokenComp2 {
|
|||
class SimpleService {
|
||||
}
|
||||
|
||||
@Component({selector: 'my-broken-comp', template: '', providers: [null, SimpleService, [null]]})
|
||||
@Component({selector: 'my-broken-comp', template: '', providers: [SimpleService, null, [null]]})
|
||||
class MyBrokenComp3 {
|
||||
}
|
||||
|
||||
|
|
|
@ -732,7 +732,13 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||
it('should throw when given invalid providers', () => {
|
||||
expect(() => createInjector(<any>['blah']))
|
||||
.toThrowError(
|
||||
'Invalid provider - only instances of Provider and Type are allowed, got: blah');
|
||||
`Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?blah?]`);
|
||||
});
|
||||
|
||||
it('should throw when given blank providers', () => {
|
||||
expect(() => createInjector(<any>[null, {provide: 'token', useValue: 'value'}]))
|
||||
.toThrowError(
|
||||
`Invalid provider for the NgModule 'SomeModule' - only instances of Provider and Type are allowed, got: [?null?, ...]`);
|
||||
});
|
||||
|
||||
it('should provide itself', () => {
|
||||
|
@ -1104,6 +1110,20 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||
const injector = createModule(SomeModule).injector;
|
||||
expect(injector.get('token1')).toBe('imported2');
|
||||
});
|
||||
|
||||
it('should throw when given invalid providers in an imported ModuleWithProviders', () => {
|
||||
@NgModule()
|
||||
class ImportedModule1 {
|
||||
}
|
||||
|
||||
@NgModule({imports: [{ngModule: ImportedModule1, providers: [<any>'broken']}]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule).injector)
|
||||
.toThrowError(
|
||||
`Invalid provider for the NgModule 'ImportedModule1' - only instances of Provider and Type are allowed, got: [?broken?]`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue