refactor(core): group provider and circular errors (#39251)
group together similar error messages as part of error code efforts ProviderNotFound & NodeInjector grouped into throwProviderNotFoundError Cyclic dependency errors grouped into throwCyclicDependencyError PR Close #39251
This commit is contained in:
parent
3f00c6150b
commit
dcafac1b8f
|
@ -196,7 +196,7 @@ which *is* what parent means.
|
|||
|
||||
2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator.
|
||||
|
||||
`Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)`
|
||||
`Circular dependency in DI detected for BethComponent. Dependency path: BethComponent -> Parent -> BethComponent`
|
||||
|
||||
Here's *Alice*, *Barry*, and family in action.
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('Ivy NgModule', () => {
|
|||
|
||||
expect(() => createInjector(AModule))
|
||||
.toThrowError(
|
||||
'Circular dependency in DI detected for type AModule. Dependency path: AModule > BModule > AModule.');
|
||||
'Circular dependency in DI detected for AModule. Dependency path: AModule > BModule > AModule');
|
||||
});
|
||||
|
||||
it('merges imports and exports', () => {
|
||||
|
|
|
@ -291,8 +291,8 @@ export class R3Injector {
|
|||
// Check for circular dependencies.
|
||||
if (ngDevMode && parents.indexOf(defType) !== -1) {
|
||||
const defName = stringify(defType);
|
||||
throw new Error(`Circular dependency in DI detected for type ${defName}. Dependency path: ${
|
||||
parents.map(defType => stringify(defType)).join(' > ')} > ${defName}.`);
|
||||
const path = parents.map(stringify);
|
||||
throwCyclicDependencyError(defName, path);
|
||||
}
|
||||
|
||||
// Check for multiple imports of the same module
|
||||
|
|
|
@ -15,6 +15,7 @@ import {assertDefined, assertIndexInRange} from '../util/assert';
|
|||
import {assertComponentType} from './assert';
|
||||
import {getComponentDef} from './definition';
|
||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {throwProviderNotFoundError} from './errors';
|
||||
import {registerPostOrderHooks} from './hooks';
|
||||
import {addToViewTree, CLEAN_PROMISE, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, initTNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, registerHostBindingOpCodes, renderView} from './instructions/shared';
|
||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||
|
@ -88,7 +89,7 @@ type HostFeature = (<T>(component: T, componentDef: ComponentDef<T>) => void);
|
|||
// TODO: A hack to not pull in the NullInjector from @angular/core.
|
||||
export const NULL_INJECTOR: Injector = {
|
||||
get: (token: any, notFoundValue?: any) => {
|
||||
throw new Error('NullInjector: Not found: ' + stringifyForError(token));
|
||||
throwProviderNotFoundError(token, 'NullInjector');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import {noSideEffects} from '../util/closure';
|
|||
|
||||
import {assertDirectiveDef, assertNodeInjector, assertTNodeForLView} from './assert';
|
||||
import {getFactoryDef} from './definition';
|
||||
import {throwCyclicDependencyError, throwProviderNotFoundError} from './errors';
|
||||
import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields';
|
||||
import {registerPreOrderHooks} from './hooks';
|
||||
import {DirectiveDef, FactoryFn} from './interfaces/definition';
|
||||
|
@ -373,7 +374,7 @@ export function getOrCreateInjectable<T>(
|
|||
try {
|
||||
const value = bloomHash();
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ${stringifyForError(token)}!`);
|
||||
throwProviderNotFoundError(token);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
@ -476,7 +477,7 @@ export function getOrCreateInjectable<T>(
|
|||
if (flags & InjectFlags.Optional) {
|
||||
return notFoundValue;
|
||||
} else {
|
||||
throw new Error(`NodeInjector: NOT_FOUND [${stringifyForError(token)}]`);
|
||||
throwProviderNotFoundError(token, 'NodeInjector');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,7 +576,7 @@ export function getNodeInjectable(
|
|||
if (isFactory(value)) {
|
||||
const factory: NodeInjectorFactory = value;
|
||||
if (factory.resolving) {
|
||||
throw new Error(`Circular dep for ${stringifyForError(tData[index])}`);
|
||||
throwCyclicDependencyError(stringifyForError(tData[index]));
|
||||
}
|
||||
const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
|
||||
factory.resolving = true;
|
||||
|
|
|
@ -11,13 +11,14 @@ import {stringify} from '../util/stringify';
|
|||
|
||||
import {TNode} from './interfaces/node';
|
||||
import {LView, TVIEW} from './interfaces/view';
|
||||
import {INTERPOLATION_DELIMITER} from './util/misc_utils';
|
||||
import {INTERPOLATION_DELIMITER, stringifyForError} from './util/misc_utils';
|
||||
|
||||
|
||||
|
||||
/** Called when directives inject each other (creating a circular dependency) */
|
||||
export function throwCyclicDependencyError(token: any): never {
|
||||
throw new Error(`Cannot instantiate cyclic dependency! ${token}`);
|
||||
export function throwCyclicDependencyError(token: string, path?: string[]): never {
|
||||
const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
|
||||
throw new Error(`Circular dependency in DI detected for ${token}${depPath}`);
|
||||
}
|
||||
|
||||
/** Called when there are multiple component selectors that match a given node */
|
||||
|
@ -116,3 +117,9 @@ export function getExpressionChangedErrorDetails(
|
|||
}
|
||||
return {propName: undefined, oldValue, newValue};
|
||||
}
|
||||
|
||||
/** Throws an error when a token is not found in DI. */
|
||||
export function throwProviderNotFoundError(token: any, injectorName?: string): never {
|
||||
const injectorDetails = injectorName ? ` in ${injectorName}` : '';
|
||||
throw new Error(`No provider for ${stringifyForError(token)} found${injectorDetails}`);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
|||
import {InjectFlags} from '../di/interface/injector';
|
||||
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
|
||||
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
|
||||
import {throwProviderNotFoundError} from './errors';
|
||||
|
||||
import {TNode} from './interfaces/node';
|
||||
import {LView} from './interfaces/view';
|
||||
|
@ -37,7 +38,7 @@ export function ɵɵtemplateRefExtractor(tNode: TNode, currentView: LView) {
|
|||
export function ɵɵinjectPipeChangeDetectorRef(flags = InjectFlags.Default): ChangeDetectorRef|null {
|
||||
const value = injectChangeDetectorRef(true);
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ChangeDetectorRef!`);
|
||||
throwProviderNotFoundError('ChangeDetectorRef');
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -615,7 +615,8 @@ describe('di', () => {
|
|||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
||||
expect(() => TestBed.createComponent(MyComp))
|
||||
.toThrowError('Circular dependency in DI detected for DirectiveA');
|
||||
});
|
||||
|
||||
onlyInIvy('Ivy has different error message for circular dependency')
|
||||
|
@ -630,7 +631,8 @@ describe('di', () => {
|
|||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||
expect(() => TestBed.createComponent(MyComp)).toThrowError(/Circular dep for/);
|
||||
expect(() => TestBed.createComponent(MyComp))
|
||||
.toThrowError('Circular dependency in DI detected for DirectiveA');
|
||||
});
|
||||
|
||||
describe('flags', () => {
|
||||
|
@ -734,7 +736,7 @@ describe('di', () => {
|
|||
}
|
||||
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
|
||||
expect(() => TestBed.createComponent(MyComp))
|
||||
.toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||
.toThrowError(/No provider for DirectiveB found in NodeInjector/);
|
||||
});
|
||||
|
||||
describe('@Host', () => {
|
||||
|
@ -812,7 +814,7 @@ describe('di', () => {
|
|||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]});
|
||||
expect(() => TestBed.createComponent(MyApp))
|
||||
.toThrowError(/NodeInjector: NOT_FOUND \[String]/);
|
||||
.toThrowError('No provider for String found in NodeInjector');
|
||||
});
|
||||
|
||||
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||
|
@ -828,7 +830,7 @@ describe('di', () => {
|
|||
TestBed.configureTestingModule(
|
||||
{declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
|
||||
expect(() => TestBed.createComponent(MyApp))
|
||||
.toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||
.toThrowError(/No provider for DirectiveB found in NodeInjector/);
|
||||
});
|
||||
|
||||
onlyInIvy('Ivy has different error message when dependency is not found')
|
||||
|
@ -853,7 +855,7 @@ describe('di', () => {
|
|||
expect(() => {
|
||||
fixture.componentInstance.myComp.showing = true;
|
||||
fixture.detectChanges();
|
||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirectiveB]/);
|
||||
}).toThrowError(/No provider for DirectiveB found in NodeInjector/);
|
||||
});
|
||||
|
||||
it('should find providers across embedded views if not passing component boundary', () => {
|
||||
|
@ -892,7 +894,7 @@ describe('di', () => {
|
|||
|
||||
TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
|
||||
expect(() => TestBed.createComponent(MyApp))
|
||||
.toThrowError(/NodeInjector: NOT_FOUND \[MyApp]/);
|
||||
.toThrowError('No provider for MyApp found in NodeInjector');
|
||||
});
|
||||
|
||||
describe('regression', () => {
|
||||
|
|
|
@ -1568,6 +1568,9 @@
|
|||
{
|
||||
"name": "syncPendingControls"
|
||||
},
|
||||
{
|
||||
"name": "throwProviderNotFoundError"
|
||||
},
|
||||
{
|
||||
"name": "toObservable"
|
||||
},
|
||||
|
|
|
@ -1931,6 +1931,9 @@
|
|||
{
|
||||
"name": "throwIfEmpty"
|
||||
},
|
||||
{
|
||||
"name": "throwProviderNotFoundError"
|
||||
},
|
||||
{
|
||||
"name": "toRefArray"
|
||||
},
|
||||
|
|
|
@ -698,6 +698,9 @@
|
|||
{
|
||||
"name": "stringifyForError"
|
||||
},
|
||||
{
|
||||
"name": "throwProviderNotFoundError"
|
||||
},
|
||||
{
|
||||
"name": "toTStylingRange"
|
||||
},
|
||||
|
|
|
@ -1024,8 +1024,10 @@ function declareTests(config?: {useJit: boolean}) {
|
|||
});
|
||||
|
||||
it('should throw when trying to instantiate a cyclic dependency', () => {
|
||||
let errorMessage = ivyEnabled ? /Circular dependency in DI detected for Car/g :
|
||||
/Cannot instantiate cyclic dependency! Car/g;
|
||||
expect(() => createInjector([Car, {provide: Engine, useClass: CyclicEngine}]).get(Car))
|
||||
.toThrowError(/Cannot instantiate cyclic dependency! Car/g);
|
||||
.toThrowError(errorMessage);
|
||||
});
|
||||
|
||||
it('should support null values', () => {
|
||||
|
|
|
@ -628,7 +628,7 @@ describe('View injector', () => {
|
|||
.it('should not instantiate a directive with cyclic dependencies', () => {
|
||||
TestBed.configureTestingModule({declarations: [CycleDirective]});
|
||||
expect(() => createComponent('<div cycleDirective></div>'))
|
||||
.toThrowError('Circular dep for CycleDirective');
|
||||
.toThrowError('Circular dependency in DI detected for CycleDirective');
|
||||
});
|
||||
|
||||
obsoleteInIvy('This error is no longer generated by the compiler')
|
||||
|
@ -661,7 +661,7 @@ describe('View injector', () => {
|
|||
SimpleComponent, {set: {template: '<div needsServiceFromHost><div>'}});
|
||||
|
||||
expect(() => createComponent('<div simpleComponent></div>'))
|
||||
.toThrowError('NodeInjector: NOT_FOUND [service]');
|
||||
.toThrowError('No provider for service found in NodeInjector');
|
||||
});
|
||||
|
||||
obsoleteInIvy('This error is no longer generated by the compiler')
|
||||
|
@ -694,7 +694,7 @@ describe('View injector', () => {
|
|||
SimpleComponent, {set: {template: '<div needsServiceFromHost><div>'}});
|
||||
|
||||
expect(() => createComponent('<div simpleComponent someOtherDirective></div>'))
|
||||
.toThrowError('NodeInjector: NOT_FOUND [service]');
|
||||
.toThrowError('No provider for service found in NodeInjector');
|
||||
});
|
||||
|
||||
obsoleteInIvy('This error is no longer generated by the compiler')
|
||||
|
@ -717,7 +717,7 @@ describe('View injector', () => {
|
|||
expect(
|
||||
() => createComponent(
|
||||
'<div simpleDirective><div needsDirectiveFromSelf></div></div>'))
|
||||
.toThrowError('NodeInjector: NOT_FOUND [SimpleDirective]');
|
||||
.toThrowError('No provider for SimpleDirective found in NodeInjector');
|
||||
});
|
||||
|
||||
it('should instantiate directives that depend on other directives', fakeAsync(() => {
|
||||
|
@ -779,7 +779,7 @@ describe('View injector', () => {
|
|||
TestBed.overrideComponent(
|
||||
SimpleComponent, {set: {template: '<div needsDirectiveFromHost></div>'}});
|
||||
expect(() => createComponent('<div simpleComponent simpleDirective></div>'))
|
||||
.toThrowError('NodeInjector: NOT_FOUND [SimpleDirective]');
|
||||
.toThrowError('No provider for SimpleDirective found in NodeInjector');
|
||||
});
|
||||
|
||||
it('should allow to use the NgModule injector from a root ViewContainerRef.parentInjector',
|
||||
|
@ -970,7 +970,7 @@ describe('View injector', () => {
|
|||
it('should throw if there is no TemplateRef', () => {
|
||||
TestBed.configureTestingModule({declarations: [NeedsTemplateRef]});
|
||||
expect(() => createComponent('<div needsTemplateRef></div>'))
|
||||
.toThrowError(/No provider for TemplateRef!/);
|
||||
.toThrowError(/No provider for TemplateRef/);
|
||||
});
|
||||
|
||||
it('should inject null if there is no TemplateRef when the dependency is optional', () => {
|
||||
|
|
|
@ -107,7 +107,7 @@ describe('di', () => {
|
|||
(DirA as any)['__NG_ELEMENT_ID__'] = 1;
|
||||
(DirC as any)['__NG_ELEMENT_ID__'] = 257;
|
||||
new ComponentFixture(App);
|
||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB]/);
|
||||
}).toThrowError('No provider for DirB found in NodeInjector');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue