feat(core): add source to StaticInjectorError message (#20817)

Closes #19302
PR Close #20817
This commit is contained in:
Olivier Combe
2017-12-06 10:13:50 +01:00
committed by Jason Aden
parent 634d33f5dd
commit b7738e1fe5
11 changed files with 106 additions and 34 deletions

View File

@ -102,7 +102,8 @@ export function createPlatformFactory(
parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef) | null,
name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) =>
PlatformRef {
const marker = new InjectionToken(`Platform: ${name}`);
const desc = `Platform: ${name}`;
const marker = new InjectionToken(desc);
return (extraProviders: StaticProvider[] = []) => {
let platform = getPlatform();
if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
@ -110,8 +111,9 @@ export function createPlatformFactory(
parentPlatformFactory(
providers.concat(extraProviders).concat({provide: marker, useValue: true}));
} else {
createPlatform(Injector.create(
providers.concat(extraProviders).concat({provide: marker, useValue: true})));
const injectedProviders: StaticProvider[] =
providers.concat(extraProviders).concat({provide: marker, useValue: true});
createPlatform(Injector.create({providers: injectedProviders, name: desc}));
}
}
return assertPlatform(marker);
@ -224,10 +226,12 @@ export class PlatformRef {
// pass that as parent to the NgModuleFactory.
const ngZoneOption = options ? options.ngZone : undefined;
const ngZone = getNgZone(ngZoneOption);
const providers: StaticProvider[] = [{provide: NgZone, useValue: ngZone}];
// Attention: Don't use ApplicationRef.run here,
// as we want to be sure that all possible constructor calls are inside `ngZone.run`!
return ngZone.run(() => {
const ngZoneInjector = Injector.create([{provide: NgZone, useValue: ngZone}], this.injector);
const ngZoneInjector = Injector.create(
{providers: providers, parent: this.injector, name: moduleFactory.moduleType.name});
const moduleRef = <InternalNgModuleRef<M>>moduleFactory.create(ngZoneInjector);
const exceptionHandler: ErrorHandler = moduleRef.injector.get(ErrorHandler, null);
if (!exceptionHandler) {

View File

@ -8,12 +8,12 @@
import {Type} from '../type';
import {stringify} from '../util';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
export const SOURCE = '__source';
const _THROW_IF_NOT_FOUND = new Object();
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
@ -64,6 +64,13 @@ export abstract class Injector {
*/
abstract get(token: any, notFoundValue?: any): any;
/**
* @deprecated from v5 use the new signature Injector.create(options)
*/
static create(providers: StaticProvider[], parent?: Injector): Injector;
static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;
/**
* Create a new Injector which is configure using `StaticProvider`s.
*
@ -71,8 +78,14 @@ export abstract class Injector {
*
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
*/
static create(providers: StaticProvider[], parent?: Injector): Injector {
return new StaticInjector(providers, parent);
static create(
options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},
parent?: Injector): Injector {
if (Array.isArray(options)) {
return new StaticInjector(options, parent);
} else {
return new StaticInjector(options.providers, options.parent, options.name || null);
}
}
}
@ -103,11 +116,14 @@ const NO_NEW_LINE = 'ɵ';
export class StaticInjector implements Injector {
readonly parent: Injector;
readonly source: string|null;
private _records: Map<any, Record>;
constructor(providers: StaticProvider[], parent: Injector = NULL_INJECTOR) {
constructor(
providers: StaticProvider[], parent: Injector = NULL_INJECTOR, source: string|null = null) {
this.parent = parent;
this.source = source;
const records = this._records = new Map<any, Record>();
records.set(
Injector, <Record>{token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false});
@ -122,7 +138,10 @@ export class StaticInjector implements Injector {
return tryResolveToken(token, record, this._records, this.parent, notFoundValue);
} catch (e) {
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
e.message = formatError('\n' + e.message, tokenPath);
if (token[SOURCE]) {
tokenPath.unshift(token[SOURCE]);
}
e.message = formatError('\n' + e.message, tokenPath, this.source);
e[NG_TOKEN_PATH] = tokenPath;
e[NG_TEMP_TOKEN_PATH] = null;
throw e;
@ -336,7 +355,7 @@ function computeDeps(provider: StaticProvider): DependencyRecord[] {
return deps;
}
function formatError(text: string, obj: any): string {
function formatError(text: string, obj: any, source: string | null = null): string {
text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.substr(2) : text;
let context = stringify(obj);
if (obj instanceof Array) {
@ -352,7 +371,7 @@ function formatError(text: string, obj: any): string {
}
context = `{${parts.join(', ')}}`;
}
return `StaticInjectorError[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
return `StaticInjectorError${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
}
function staticError(text: string, obj: any): Error {

View File

@ -9,6 +9,7 @@
import {resolveForwardRef} from '../di/forward_ref';
import {Injector} from '../di/injector';
import {NgModuleRef} from '../linker/ng_module_factory';
import {stringify} from '../util';
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleProviderDef, NodeFlags} from './types';
import {splitDepsDsl, tokenKey} from './util';
@ -25,7 +26,7 @@ export function moduleProvideDef(
// lowered the expression and then stopped evaluating it,
// i.e. also didn't unwrap it.
value = resolveForwardRef(value);
const depDefs = splitDepsDsl(deps);
const depDefs = splitDepsDsl(deps, stringify(token));
return {
// will bet set by the module definition
index: -1,

View File

@ -12,7 +12,7 @@ import {ElementRef} from '../linker/element_ref';
import {TemplateRef} from '../linker/template_ref';
import {ViewContainerRef} from '../linker/view_container_ref';
import {Renderer as RendererV1, Renderer2} from '../render/api';
import {stringify} from '../util';
import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData, shouldCallLifecycleInitHook} from './types';
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
@ -83,7 +83,7 @@ export function _def(
// i.e. also didn't unwrap it.
value = resolveForwardRef(value);
const depDefs = splitDepsDsl(deps);
const depDefs = splitDepsDsl(deps, stringify(token));
return {
// will bet set by the view definition

View File

@ -7,10 +7,10 @@
*/
import {WrappedValue, devModeEqual} from '../change_detection/change_detection';
import {SOURCE} from '../di/injector';
import {ViewEncapsulation} from '../metadata/view';
import {RendererType2} from '../render/api';
import {looseIdentical, stringify} from '../util';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {BindingDef, BindingFlags, Definition, DefinitionFactory, DepDef, DepFlags, ElementData, NodeDef, NodeFlags, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewState, asElementData, asTextData} from './types';
@ -209,7 +209,7 @@ export function splitMatchedQueriesDsl(
return {matchedQueries, references, matchedQueryIds};
}
export function splitDepsDsl(deps: ([DepFlags, any] | any)[]): DepDef[] {
export function splitDepsDsl(deps: ([DepFlags, any] | any)[], sourceName?: string): DepDef[] {
return deps.map(value => {
let token: any;
let flags: DepFlags;
@ -219,6 +219,9 @@ export function splitDepsDsl(deps: ([DepFlags, any] | any)[]): DepDef[] {
flags = DepFlags.None;
token = value;
}
if (token && (typeof token === 'function' || typeof token === 'object') && sourceName) {
Object.defineProperty(token, SOURCE, {value: sourceName, configurable: true});
}
return {flags, token, tokenKey: tokenKey(token)};
});
}