fix(ivy): properly tree-shake away StaticInjector (#30219)
Ivy uses R3Injector, but we are currently pulling in both the StaticInjector (View Engine injector) and the R3Injector when running with Ivy. This commit adds an ivy switch so calling Injector.create() pulls in the correct implementation of the injector depending on whether you are using VE or Ivy. This saves us about 3KB in the bundle. PR Close #30219
This commit is contained in:
parent
b1506a3271
commit
7d6f4885b2
|
@ -21,7 +21,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime": 1440,
|
"runtime": 1440,
|
||||||
"main": 149205,
|
"main": 146225,
|
||||||
"polyfills": 43567
|
"polyfills": 43567
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,4 +291,6 @@ export {
|
||||||
|
|
||||||
export {createInjector as ɵcreateInjector} from './di/r3_injector';
|
export {createInjector as ɵcreateInjector} from './di/r3_injector';
|
||||||
|
|
||||||
|
export {INJECTOR_IMPL__POST_R3__ as ɵINJECTOR_IMPL__POST_R3__} from './di/injector';
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
@ -17,8 +17,8 @@ export {InjectFlags} from './interface/injector';
|
||||||
export {ɵɵdefineInjectable, defineInjectable, ɵɵdefineInjector, InjectableType, InjectorType} from './interface/defs';
|
export {ɵɵdefineInjectable, defineInjectable, ɵɵdefineInjector, InjectableType, InjectorType} from './interface/defs';
|
||||||
export {forwardRef, resolveForwardRef, ForwardRefFn} from './forward_ref';
|
export {forwardRef, resolveForwardRef, ForwardRefFn} from './forward_ref';
|
||||||
export {Injectable, InjectableDecorator, InjectableProvider} from './injectable';
|
export {Injectable, InjectableDecorator, InjectableProvider} from './injectable';
|
||||||
export {INJECTOR, Injector} from './injector';
|
export {Injector} from './injector';
|
||||||
export {ɵɵinject, inject} from './injector_compatibility';
|
export {ɵɵinject, inject, INJECTOR} from './injector_compatibility';
|
||||||
export {ReflectiveInjector} from './reflective_injector';
|
export {ReflectiveInjector} from './reflective_injector';
|
||||||
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './interface/provider';
|
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './interface/provider';
|
||||||
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './reflective_provider';
|
export {ResolvedReflectiveFactory, ResolvedReflectiveProvider} from './reflective_provider';
|
||||||
|
|
|
@ -7,48 +7,29 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type} from '../interface/type';
|
import {Type} from '../interface/type';
|
||||||
import {getClosureSafeProperty} from '../util/property';
|
|
||||||
import {stringify} from '../util/stringify';
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {resolveForwardRef} from './forward_ref';
|
import {resolveForwardRef} from './forward_ref';
|
||||||
import {InjectionToken} from './injection_token';
|
import {InjectionToken} from './injection_token';
|
||||||
import {ɵɵinject} from './injector_compatibility';
|
import {INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, catchInjectorError, formatError, ɵɵinject} from './injector_compatibility';
|
||||||
import {ɵɵdefineInjectable} from './interface/defs';
|
import {ɵɵdefineInjectable} from './interface/defs';
|
||||||
import {InjectFlags} from './interface/injector';
|
import {InjectFlags} from './interface/injector';
|
||||||
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './interface/provider';
|
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './interface/provider';
|
||||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||||
|
import {createInjector} from './r3_injector';
|
||||||
|
|
||||||
export const SOURCE = '__source';
|
export function INJECTOR_IMPL__PRE_R3__(
|
||||||
const _THROW_IF_NOT_FOUND = new Object();
|
providers: StaticProvider[], parent: Injector | undefined, name: string) {
|
||||||
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
return new StaticInjector(providers, parent, name);
|
||||||
|
|
||||||
/**
|
|
||||||
* An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
|
|
||||||
*
|
|
||||||
* Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
|
|
||||||
* project.
|
|
||||||
*
|
|
||||||
* @publicApi
|
|
||||||
*/
|
|
||||||
export const INJECTOR = new InjectionToken<Injector>(
|
|
||||||
'INJECTOR',
|
|
||||||
-1 as any // `-1` is used by Ivy DI system as special value to recognize it as `Injector`.
|
|
||||||
);
|
|
||||||
|
|
||||||
export class NullInjector implements Injector {
|
|
||||||
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
|
|
||||||
if (notFoundValue === _THROW_IF_NOT_FOUND) {
|
|
||||||
// Intentionally left behind: With dev tools open the debugger will stop here. There is no
|
|
||||||
// reason why correctly written application should cause this exception.
|
|
||||||
// TODO(misko): uncomment the next line once `ngDevMode` works with closure.
|
|
||||||
// if(ngDevMode) debugger;
|
|
||||||
const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
|
|
||||||
error.name = 'NullInjectorError';
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return notFoundValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function INJECTOR_IMPL__POST_R3__(
|
||||||
|
providers: StaticProvider[], parent: Injector | undefined, name: string) {
|
||||||
|
return createInjector({name: name}, parent, providers, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const INJECTOR_IMPL = INJECTOR_IMPL__PRE_R3__;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete injectors implement this interface.
|
* Concrete injectors implement this interface.
|
||||||
*
|
*
|
||||||
|
@ -66,7 +47,7 @@ export class NullInjector implements Injector {
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export abstract class Injector {
|
export abstract class Injector {
|
||||||
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
|
||||||
static NULL: Injector = new NullInjector();
|
static NULL: Injector = new NullInjector();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,9 +81,9 @@ export abstract class Injector {
|
||||||
options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},
|
options: StaticProvider[]|{providers: StaticProvider[], parent?: Injector, name?: string},
|
||||||
parent?: Injector): Injector {
|
parent?: Injector): Injector {
|
||||||
if (Array.isArray(options)) {
|
if (Array.isArray(options)) {
|
||||||
return new StaticInjector(options, parent);
|
return INJECTOR_IMPL(options, parent, '');
|
||||||
} else {
|
} else {
|
||||||
return new StaticInjector(options.providers, options.parent, options.name || null);
|
return INJECTOR_IMPL(options.providers, options.parent, options.name || '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,10 +110,7 @@ const CIRCULAR = IDENT;
|
||||||
const MULTI_PROVIDER_FN = function(): any[] {
|
const MULTI_PROVIDER_FN = function(): any[] {
|
||||||
return Array.prototype.slice.call(arguments);
|
return Array.prototype.slice.call(arguments);
|
||||||
};
|
};
|
||||||
export const USE_VALUE =
|
|
||||||
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});
|
|
||||||
const NG_TOKEN_PATH = 'ngTokenPath';
|
|
||||||
export const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
|
|
||||||
const enum OptionFlags {
|
const enum OptionFlags {
|
||||||
Optional = 1 << 0,
|
Optional = 1 << 0,
|
||||||
CheckSelf = 1 << 1,
|
CheckSelf = 1 << 1,
|
||||||
|
@ -140,7 +118,6 @@ const enum OptionFlags {
|
||||||
Default = CheckSelf | CheckParent
|
Default = CheckSelf | CheckParent
|
||||||
}
|
}
|
||||||
const NULL_INJECTOR = Injector.NULL;
|
const NULL_INJECTOR = Injector.NULL;
|
||||||
const NEW_LINE = /\n/gm;
|
|
||||||
const NO_NEW_LINE = 'ɵ';
|
const NO_NEW_LINE = 'ɵ';
|
||||||
|
|
||||||
export class StaticInjector implements Injector {
|
export class StaticInjector implements Injector {
|
||||||
|
@ -377,38 +354,6 @@ function computeDeps(provider: StaticProvider): DependencyRecord[] {
|
||||||
return deps;
|
return deps;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function catchInjectorError(
|
|
||||||
e: any, token: any, injectorErrorName: string, source: string | null): never {
|
|
||||||
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
|
|
||||||
if (token[SOURCE]) {
|
|
||||||
tokenPath.unshift(token[SOURCE]);
|
|
||||||
}
|
|
||||||
e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
|
|
||||||
e[NG_TOKEN_PATH] = tokenPath;
|
|
||||||
e[NG_TEMP_TOKEN_PATH] = null;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatError(
|
|
||||||
text: string, obj: any, injectorErrorName: string, 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) {
|
|
||||||
context = obj.map(stringify).join(' -> ');
|
|
||||||
} else if (typeof obj === 'object') {
|
|
||||||
let parts = <string[]>[];
|
|
||||||
for (let key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key)) {
|
|
||||||
let value = obj[key];
|
|
||||||
parts.push(
|
|
||||||
key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context = `{${parts.join(', ')}}`;
|
|
||||||
}
|
|
||||||
return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function staticError(text: string, obj: any): Error {
|
function staticError(text: string, obj: any): Error {
|
||||||
return new Error(formatError(text, obj, 'StaticInjectorError'));
|
return new Error(formatError(text, obj, 'StaticInjectorError'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type} from '../interface/type';
|
import {Type} from '../interface/type';
|
||||||
|
import {getClosureSafeProperty} from '../util/property';
|
||||||
import {stringify} from '../util/stringify';
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {resolveForwardRef} from './forward_ref';
|
import {resolveForwardRef} from './forward_ref';
|
||||||
|
@ -14,10 +15,36 @@ import {InjectionToken} from './injection_token';
|
||||||
import {Injector} from './injector';
|
import {Injector} from './injector';
|
||||||
import {getInjectableDef, ɵɵInjectableDef} from './interface/defs';
|
import {getInjectableDef, ɵɵInjectableDef} from './interface/defs';
|
||||||
import {InjectFlags} from './interface/injector';
|
import {InjectFlags} from './interface/injector';
|
||||||
|
import {ValueProvider} from './interface/provider';
|
||||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
|
||||||
|
*
|
||||||
|
* Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
|
||||||
|
* project.
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
|
*/
|
||||||
|
export const INJECTOR = new InjectionToken<Injector>(
|
||||||
|
'INJECTOR',
|
||||||
|
-1 as any // `-1` is used by Ivy DI system as special value to recognize it as `Injector`.
|
||||||
|
);
|
||||||
|
|
||||||
|
const _THROW_IF_NOT_FOUND = new Object();
|
||||||
|
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||||
|
|
||||||
|
export const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
|
||||||
|
const NG_TOKEN_PATH = 'ngTokenPath';
|
||||||
|
const NEW_LINE = /\n/gm;
|
||||||
|
const NO_NEW_LINE = 'ɵ';
|
||||||
|
export const SOURCE = '__source';
|
||||||
|
|
||||||
|
export const USE_VALUE =
|
||||||
|
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current injector value used by `inject`.
|
* Current injector value used by `inject`.
|
||||||
* - `undefined`: it is an error to call `inject`
|
* - `undefined`: it is an error to call `inject`
|
||||||
|
@ -166,3 +193,52 @@ export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): an
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class NullInjector implements Injector {
|
||||||
|
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
|
||||||
|
if (notFoundValue === THROW_IF_NOT_FOUND) {
|
||||||
|
// Intentionally left behind: With dev tools open the debugger will stop here. There is no
|
||||||
|
// reason why correctly written application should cause this exception.
|
||||||
|
// TODO(misko): uncomment the next line once `ngDevMode` works with closure.
|
||||||
|
// if(ngDevMode) debugger;
|
||||||
|
const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
|
||||||
|
error.name = 'NullInjectorError';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return notFoundValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function catchInjectorError(
|
||||||
|
e: any, token: any, injectorErrorName: string, source: string | null): never {
|
||||||
|
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
|
||||||
|
if (token[SOURCE]) {
|
||||||
|
tokenPath.unshift(token[SOURCE]);
|
||||||
|
}
|
||||||
|
e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
|
||||||
|
e[NG_TOKEN_PATH] = tokenPath;
|
||||||
|
e[NG_TEMP_TOKEN_PATH] = null;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatError(
|
||||||
|
text: string, obj: any, injectorErrorName: string, 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) {
|
||||||
|
context = obj.map(stringify).join(' -> ');
|
||||||
|
} else if (typeof obj === 'object') {
|
||||||
|
let parts = <string[]>[];
|
||||||
|
for (let key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
let value = obj[key];
|
||||||
|
parts.push(
|
||||||
|
key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context = `{${parts.join(', ')}}`;
|
||||||
|
}
|
||||||
|
return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
|
||||||
|
}
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
|
|
||||||
import {OnDestroy} from '../interface/lifecycle_hooks';
|
import {OnDestroy} from '../interface/lifecycle_hooks';
|
||||||
import {Type} from '../interface/type';
|
import {Type} from '../interface/type';
|
||||||
|
import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiProviderError} from '../render3/errors';
|
||||||
import {stringify} from '../util/stringify';
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {resolveForwardRef} from './forward_ref';
|
import {resolveForwardRef} from './forward_ref';
|
||||||
import {InjectionToken} from './injection_token';
|
import {InjectionToken} from './injection_token';
|
||||||
import {INJECTOR, Injector, NG_TEMP_TOKEN_PATH, NullInjector, USE_VALUE, catchInjectorError} from './injector';
|
import {Injector} from './injector';
|
||||||
import {injectArgs, setCurrentInjector, ɵɵinject} from './injector_compatibility';
|
import {INJECTOR, NG_TEMP_TOKEN_PATH, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, catchInjectorError, injectArgs, setCurrentInjector, ɵɵinject} from './injector_compatibility';
|
||||||
import {InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef, ɵɵInjectableDef} from './interface/defs';
|
import {InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef, ɵɵInjectableDef} from './interface/defs';
|
||||||
import {InjectFlags} from './interface/injector';
|
import {InjectFlags} from './interface/injector';
|
||||||
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './interface/provider';
|
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './interface/provider';
|
||||||
|
@ -131,7 +132,7 @@ export class R3Injector {
|
||||||
this.injectorDefTypes.forEach(defType => this.get(defType));
|
this.injectorDefTypes.forEach(defType => this.get(defType));
|
||||||
|
|
||||||
// Source name, used for debugging
|
// Source name, used for debugging
|
||||||
this.source = source || (def instanceof Array ? null : stringify(def));
|
this.source = source || (typeof def === 'object' ? null : stringify(def));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +158,7 @@ export class R3Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
get<T>(
|
get<T>(
|
||||||
token: Type<T>|InjectionToken<T>, notFoundValue: any = Injector.THROW_IF_NOT_FOUND,
|
token: Type<T>|InjectionToken<T>, notFoundValue: any = THROW_IF_NOT_FOUND,
|
||||||
flags = InjectFlags.Default): T {
|
flags = InjectFlags.Default): T {
|
||||||
this.assertNotDestroyed();
|
this.assertNotDestroyed();
|
||||||
// Set the injection context.
|
// Set the injection context.
|
||||||
|
@ -208,6 +209,12 @@ export class R3Injector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
const tokens = <string[]>[], records = this.records;
|
||||||
|
records.forEach((v, token) => tokens.push(stringify(token)));
|
||||||
|
return `R3Injector[${tokens.join(', ')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
private assertNotDestroyed(): void {
|
private assertNotDestroyed(): void {
|
||||||
if (this._destroyed) {
|
if (this._destroyed) {
|
||||||
throw new Error('Injector has already been destroyed.');
|
throw new Error('Injector has already been destroyed.');
|
||||||
|
@ -242,7 +249,8 @@ export class R3Injector {
|
||||||
(ngModule === undefined) ? (defOrWrappedDef as InjectorType<any>) : ngModule;
|
(ngModule === undefined) ? (defOrWrappedDef as InjectorType<any>) : ngModule;
|
||||||
|
|
||||||
// Check for circular dependencies.
|
// Check for circular dependencies.
|
||||||
if (ngDevMode && parents.indexOf(defType) !== -1) {
|
// TODO(FW-1307): Re-add ngDevMode when closure can handle it
|
||||||
|
if (parents.indexOf(defType) !== -1) {
|
||||||
const defName = stringify(defType);
|
const defName = stringify(defType);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Circular dependency in DI detected for type ${defName}. Dependency path: ${parents.map(defType => stringify(defType)).join(' > ')} > ${defName}.`);
|
`Circular dependency in DI detected for type ${defName}. Dependency path: ${parents.map(defType => stringify(defType)).join(' > ')} > ${defName}.`);
|
||||||
|
@ -278,7 +286,8 @@ export class R3Injector {
|
||||||
if (def.imports != null && !isDuplicate) {
|
if (def.imports != null && !isDuplicate) {
|
||||||
// Before processing defType's imports, add it to the set of parents. This way, if it ends
|
// Before processing defType's imports, add it to the set of parents. This way, if it ends
|
||||||
// up deeply importing itself, this can be detected.
|
// up deeply importing itself, this can be detected.
|
||||||
ngDevMode && parents.push(defType);
|
// TODO(FW-1307): Re-add ngDevMode when closure can handle it
|
||||||
|
parents.push(defType);
|
||||||
// Add it to the set of dedups. This way we can detect multiple imports of the same module
|
// Add it to the set of dedups. This way we can detect multiple imports of the same module
|
||||||
dedupStack.push(defType);
|
dedupStack.push(defType);
|
||||||
|
|
||||||
|
@ -287,7 +296,8 @@ export class R3Injector {
|
||||||
def.imports, imported => this.processInjectorType(imported, parents, dedupStack));
|
def.imports, imported => this.processInjectorType(imported, parents, dedupStack));
|
||||||
} finally {
|
} finally {
|
||||||
// Remove it from the parents set when finished.
|
// Remove it from the parents set when finished.
|
||||||
ngDevMode && parents.pop();
|
// TODO(FW-1307): Re-add ngDevMode when closure can handle it
|
||||||
|
parents.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +335,7 @@ export class R3Injector {
|
||||||
if (multiRecord) {
|
if (multiRecord) {
|
||||||
// It has. Throw a nice error if
|
// It has. Throw a nice error if
|
||||||
if (multiRecord.multi === undefined) {
|
if (multiRecord.multi === undefined) {
|
||||||
throw new Error(`Mixed multi-provider for ${token}.`);
|
throwMixedMultiProviderError();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
multiRecord = makeRecord(undefined, NOT_YET, true);
|
multiRecord = makeRecord(undefined, NOT_YET, true);
|
||||||
|
@ -337,7 +347,7 @@ export class R3Injector {
|
||||||
} else {
|
} else {
|
||||||
const existing = this.records.get(token);
|
const existing = this.records.get(token);
|
||||||
if (existing && existing.multi !== undefined) {
|
if (existing && existing.multi !== undefined) {
|
||||||
throw new Error(`Mixed multi-provider for ${stringify(token)}`);
|
throwMixedMultiProviderError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.records.set(token, record);
|
this.records.set(token, record);
|
||||||
|
@ -345,7 +355,7 @@ export class R3Injector {
|
||||||
|
|
||||||
private hydrate<T>(token: Type<T>|InjectionToken<T>, record: Record<T>): T {
|
private hydrate<T>(token: Type<T>|InjectionToken<T>, record: Record<T>): T {
|
||||||
if (record.value === CIRCULAR) {
|
if (record.value === CIRCULAR) {
|
||||||
throw new Error(`Cannot instantiate cyclic dependency! ${stringify(token)}`);
|
throwCyclicDependencyError(stringify(token));
|
||||||
} else if (record.value === NOT_YET) {
|
} else if (record.value === NOT_YET) {
|
||||||
record.value = CIRCULAR;
|
record.value = CIRCULAR;
|
||||||
record.value = record.factory !();
|
record.value = record.factory !();
|
||||||
|
@ -421,14 +431,7 @@ export function providerToFactory(
|
||||||
provider &&
|
provider &&
|
||||||
((provider as StaticClassProvider | ClassProvider).useClass || provider.provide));
|
((provider as StaticClassProvider | ClassProvider).useClass || provider.provide));
|
||||||
if (!classRef) {
|
if (!classRef) {
|
||||||
let ngModuleDetail = '';
|
throwInvalidProviderError(ngModuleType, providers, provider);
|
||||||
if (ngModuleType && providers) {
|
|
||||||
const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
|
|
||||||
ngModuleDetail =
|
|
||||||
` - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`;
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Invalid provider for the NgModule '${stringify(ngModuleType)}'` + ngModuleDetail);
|
|
||||||
}
|
}
|
||||||
if (hasDeps(provider)) {
|
if (hasDeps(provider)) {
|
||||||
factory = () => new (classRef)(...injectArgs(provider.deps));
|
factory = () => new (classRef)(...injectArgs(provider.deps));
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, THROW_IF_NOT_FOUND} from './injector';
|
import {Injector} from './injector';
|
||||||
|
import {THROW_IF_NOT_FOUND} from './injector_compatibility';
|
||||||
import {Provider} from './interface/provider';
|
import {Provider} from './interface/provider';
|
||||||
import {Self, SkipSelf} from './metadata';
|
import {Self, SkipSelf} from './metadata';
|
||||||
import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
|
import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {InjectorType} from '../di/interface/defs';
|
||||||
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {TNode} from './interfaces/node';
|
import {TNode} from './interfaces/node';
|
||||||
|
|
||||||
|
|
||||||
/** Called when directives inject each other (creating a circular dependency) */
|
/** Called when directives inject each other (creating a circular dependency) */
|
||||||
export function throwCyclicDependencyError(token: any): never {
|
export function throwCyclicDependencyError(token: any): never {
|
||||||
throw new Error(`Cannot instantiate cyclic dependency! ${token}`);
|
throw new Error(`Cannot instantiate cyclic dependency! ${token}`);
|
||||||
|
@ -31,3 +35,20 @@ export function throwErrorIfNoChangesMode(
|
||||||
// TODO: include debug context
|
// TODO: include debug context
|
||||||
throw new Error(msg);
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function throwMixedMultiProviderError() {
|
||||||
|
throw new Error(`Cannot mix multi providers and regular providers`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function throwInvalidProviderError(
|
||||||
|
ngModuleType?: InjectorType<any>, providers?: any[], provider?: any) {
|
||||||
|
let ngModuleDetail = '';
|
||||||
|
if (ngModuleType && providers) {
|
||||||
|
const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
|
||||||
|
ngModuleDetail =
|
||||||
|
` - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Invalid provider for the NgModule '${stringify(ngModuleType)}'` + ngModuleDetail);
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {INJECTOR, Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
|
import {INJECTOR} from '../di/injector_compatibility';
|
||||||
import {InjectFlags} from '../di/interface/injector';
|
import {InjectFlags} from '../di/interface/injector';
|
||||||
import {StaticProvider} from '../di/interface/provider';
|
import {StaticProvider} from '../di/interface/provider';
|
||||||
import {R3Injector, createInjector} from '../di/r3_injector';
|
import {R3Injector, createInjector} from '../di/r3_injector';
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {resolveForwardRef} from '../di/forward_ref';
|
import {resolveForwardRef} from '../di/forward_ref';
|
||||||
import {INJECTOR, Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
import {setCurrentInjector} from '../di/injector_compatibility';
|
import {INJECTOR, setCurrentInjector} from '../di/injector_compatibility';
|
||||||
import {getInjectableDef, ɵɵInjectableDef} from '../di/interface/defs';
|
import {getInjectableDef, ɵɵInjectableDef} from '../di/interface/defs';
|
||||||
import {APP_ROOT} from '../di/scope';
|
import {APP_ROOT} from '../di/scope';
|
||||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {WrappedValue, devModeEqual} from '../change_detection/change_detection';
|
import {WrappedValue, devModeEqual} from '../change_detection/change_detection';
|
||||||
import {SOURCE} from '../di/injector';
|
import {SOURCE} from '../di/injector_compatibility';
|
||||||
import {ViewEncapsulation} from '../metadata/view';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {RendererType2} from '../render/api';
|
import {RendererType2} from '../render/api';
|
||||||
import {looseIdentical} from '../util/comparison';
|
import {looseIdentical} from '../util/comparison';
|
||||||
|
|
|
@ -5,21 +5,12 @@
|
||||||
{
|
{
|
||||||
"name": "CIRCULAR"
|
"name": "CIRCULAR"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "CIRCULAR"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "EMPTY"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "EMPTY_ARRAY"
|
"name": "EMPTY_ARRAY"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "EmptyErrorImpl"
|
"name": "EmptyErrorImpl"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "IDENT"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "INJECTOR"
|
"name": "INJECTOR"
|
||||||
},
|
},
|
||||||
|
@ -32,12 +23,6 @@
|
||||||
{
|
{
|
||||||
"name": "InjectionToken"
|
"name": "InjectionToken"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Injector"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MULTI_PROVIDER_FN"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "NEW_LINE"
|
"name": "NEW_LINE"
|
||||||
},
|
},
|
||||||
|
@ -62,9 +47,6 @@
|
||||||
{
|
{
|
||||||
"name": "NULL_INJECTOR"
|
"name": "NULL_INJECTOR"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "NULL_INJECTOR"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "NullInjector"
|
"name": "NullInjector"
|
||||||
},
|
},
|
||||||
|
@ -93,7 +75,7 @@
|
||||||
"name": "SkipSelf"
|
"name": "SkipSelf"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "StaticInjector"
|
"name": "THROW_IF_NOT_FOUND"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "USE_VALUE"
|
"name": "USE_VALUE"
|
||||||
|
@ -101,9 +83,6 @@
|
||||||
{
|
{
|
||||||
"name": "UnsubscriptionErrorImpl"
|
"name": "UnsubscriptionErrorImpl"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_THROW_IF_NOT_FOUND"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "__forward_ref__"
|
"name": "__forward_ref__"
|
||||||
},
|
},
|
||||||
|
@ -122,9 +101,6 @@
|
||||||
{
|
{
|
||||||
"name": "catchInjectorError"
|
"name": "catchInjectorError"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "computeDeps"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "couldBeInjectableType"
|
"name": "couldBeInjectableType"
|
||||||
},
|
},
|
||||||
|
@ -194,38 +170,29 @@
|
||||||
{
|
{
|
||||||
"name": "makeRecord"
|
"name": "makeRecord"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "multiProviderMixError"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "providerToFactory"
|
"name": "providerToFactory"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "providerToRecord"
|
"name": "providerToRecord"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "recursivelyProcessProviders"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "resolveForwardRef"
|
"name": "resolveForwardRef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resolveProvider"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "resolveToken"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "setCurrentInjector"
|
"name": "setCurrentInjector"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "staticError"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "stringify"
|
"name": "stringify"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "tryResolveToken"
|
"name": "throwCyclicDependencyError"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "throwInvalidProviderError"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "throwMixedMultiProviderError"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ɵɵdefineInjectable"
|
"name": "ɵɵdefineInjectable"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import {Inject, InjectionToken, Injector, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
|
import {Inject, InjectionToken, Injector, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
import {ivyEnabled, modifiedInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
import {stringify} from '../../src/util/stringify';
|
import {stringify} from '../../src/util/stringify';
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ function factoryFn(a: any){}
|
||||||
{provide: 'provider10', useValue: 1}
|
{provide: 'provider10', useValue: 1}
|
||||||
];
|
];
|
||||||
|
|
||||||
describe(`StaticInjector`, () => {
|
modifiedInIvy('Ivy uses R3Injector').describe(`StaticInjector`, () => {
|
||||||
|
|
||||||
it('should instantiate a class without dependencies', () => {
|
it('should instantiate a class without dependencies', () => {
|
||||||
const injector = Injector.create([Engine.PROVIDER]);
|
const injector = Injector.create([Engine.PROVIDER]);
|
||||||
|
@ -413,9 +414,11 @@ function factoryFn(a: any){}
|
||||||
[{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}],
|
[{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}],
|
||||||
parent);
|
parent);
|
||||||
|
|
||||||
|
const injectorName = ivyEnabled ? `R3Injector` : `StaticInjector`;
|
||||||
|
|
||||||
expect(() => child.get(Car))
|
expect(() => child.get(Car))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`StaticInjectorError[${stringify(Car)} -> ${stringify(Engine)}]: \n` +
|
`${injectorName}Error[${stringify(Car)} -> ${stringify(Engine)}]: \n` +
|
||||||
' NullInjectorError: No provider for Engine!');
|
' NullInjectorError: No provider for Engine!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -472,8 +475,11 @@ function factoryFn(a: any){}
|
||||||
|
|
||||||
describe('displayName', () => {
|
describe('displayName', () => {
|
||||||
it('should work', () => {
|
it('should work', () => {
|
||||||
|
const ivyError = `R3Injector[Engine, BrokenEngine, InjectionToken INJECTOR]`;
|
||||||
|
const viewEngineError =
|
||||||
|
`StaticInjector[Injector, InjectionToken INJECTOR, Engine, BrokenEngine]`;
|
||||||
expect(Injector.create([Engine.PROVIDER, {provide: BrokenEngine, useValue: null}]).toString())
|
expect(Injector.create([Engine.PROVIDER, {provide: BrokenEngine, useValue: null}]).toString())
|
||||||
.toEqual('StaticInjector[Injector, InjectionToken INJECTOR, Engine, BrokenEngine]');
|
.toEqual(ivyEnabled ? ivyError : viewEngineError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
import {NgModuleRef} from '@angular/core';
|
import {NgModuleRef} from '@angular/core';
|
||||||
import {InjectFlags, inject} from '@angular/core/src/di';
|
import {InjectFlags, inject} from '@angular/core/src/di';
|
||||||
import {INJECTOR, Injector} from '@angular/core/src/di/injector';
|
import {Injector} from '@angular/core/src/di/injector';
|
||||||
|
import {INJECTOR} from '@angular/core/src/di/injector_compatibility';
|
||||||
import {ɵɵInjectableDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs';
|
import {ɵɵInjectableDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs';
|
||||||
import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
|
import {NgModuleDefinition, NgModuleProviderDef, NodeFlags} from '@angular/core/src/view';
|
||||||
import {moduleDef} from '@angular/core/src/view/ng_module';
|
import {moduleDef} from '@angular/core/src/view/ng_module';
|
||||||
|
|
|
@ -232,10 +232,7 @@ function bootstrap(
|
||||||
]).then(null, (e: Error) => {
|
]).then(null, (e: Error) => {
|
||||||
let errorMsg: string;
|
let errorMsg: string;
|
||||||
if (ivyEnabled) {
|
if (ivyEnabled) {
|
||||||
errorMsg = `R3InjectorError(TestModule)[IDontExist]: \n` +
|
errorMsg = `R3InjectorError(TestModule)[IDontExist -> IDontExist -> IDontExist]: \n`;
|
||||||
' StaticInjectorError(TestModule)[IDontExist]: \n' +
|
|
||||||
' StaticInjectorError(Platform: core)[IDontExist]: \n' +
|
|
||||||
' NullInjectorError: No provider for IDontExist!';
|
|
||||||
} else {
|
} else {
|
||||||
errorMsg = `StaticInjectorError(TestModule)[CustomCmp -> IDontExist]: \n` +
|
errorMsg = `StaticInjectorError(TestModule)[CustomCmp -> IDontExist]: \n` +
|
||||||
' StaticInjectorError(Platform: core)[CustomCmp -> IDontExist]: \n' +
|
' StaticInjectorError(Platform: core)[CustomCmp -> IDontExist]: \n' +
|
||||||
|
|
Loading…
Reference in New Issue