When we log DI errors we get the name of the provider via `SomeClass.name`. In IE functions that inherit from other functions don't have their own `name`, but they take the `name` from the lowest parent in the chain, before `Function`. I've added some changes to fall back to parsing out the function name from the function's string form. PR Close #34305
		
			
				
	
	
		
			279 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google Inc. All Rights Reserved.
 | |
|  *
 | |
|  * 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
 | |
|  */
 | |
| 
 | |
| import {Type} from '../../interface/type';
 | |
| import {getClosureSafeProperty} from '../../util/property';
 | |
| import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, ValueProvider} from './provider';
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Information about how a type or `InjectionToken` interfaces with the DI system.
 | |
|  *
 | |
|  * At a minimum, this includes a `factory` which defines how to create the given type `T`, possibly
 | |
|  * requesting injection of other types if necessary.
 | |
|  *
 | |
|  * Optionally, a `providedIn` parameter specifies that the given type belongs to a particular
 | |
|  * `InjectorDef`, `NgModule`, or a special scope (e.g. `'root'`). A value of `null` indicates
 | |
|  * that the injectable does not belong to any scope.
 | |
|  *
 | |
|  * NOTE: This is a private type and should not be exported
 | |
|  *
 | |
|  * @publicApi
 | |
|  */
 | |
| export interface ɵɵInjectableDef<T> {
 | |
|   /**
 | |
|    * Specifies that the given type belongs to a particular injector:
 | |
|    * - `InjectorType` such as `NgModule`,
 | |
|    * - `'root'` the root injector
 | |
|    * - `'any'` all injectors.
 | |
|    * - `null`, does not belong to any injector. Must be explicitly listed in the injector
 | |
|    *   `providers`.
 | |
|    */
 | |
|   providedIn: InjectorType<any>|'root'|'platform'|'any'|null;
 | |
| 
 | |
|   /**
 | |
|    * The token to which this definition belongs.
 | |
|    *
 | |
|    * Note that this may not be the same as the type that the `factory` will create.
 | |
|    */
 | |
|   token: unknown;
 | |
| 
 | |
|   /**
 | |
|    * Factory method to execute to create an instance of the injectable.
 | |
|    */
 | |
|   factory: (t?: Type<any>) => T;
 | |
| 
 | |
|   /**
 | |
|    * In a case of no explicit injector, a location where the instance of the injectable is stored.
 | |
|    */
 | |
|   value: T|undefined;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Information about the providers to be included in an `Injector` as well as how the given type
 | |
|  * which carries the information should be created by the DI system.
 | |
|  *
 | |
|  * An `InjectorDef` can import other types which have `InjectorDefs`, forming a deep nested
 | |
|  * structure of providers with a defined priority (identically to how `NgModule`s also have
 | |
|  * an import/dependency structure).
 | |
|  *
 | |
|  * NOTE: This is a private type and should not be exported
 | |
|  *
 | |
|  * @publicApi
 | |
|  */
 | |
| export interface ɵɵInjectorDef<T> {
 | |
|   factory: () => T;
 | |
| 
 | |
|   // TODO(alxhub): Narrow down the type here once decorators properly change the return type of the
 | |
|   // class they are decorating (to add the ɵprov property for example).
 | |
|   providers: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider|
 | |
|               StaticClassProvider|ClassProvider|any[])[];
 | |
| 
 | |
|   imports: (InjectorType<any>|InjectorTypeWithProviders<any>)[];
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A `Type` which has an `InjectableDef` static field.
 | |
|  *
 | |
|  * `InjectableDefType`s contain their own Dependency Injection metadata and are usable in an
 | |
|  * `InjectorDef`-based `StaticInjector.
 | |
|  *
 | |
|  * @publicApi
 | |
|  */
 | |
| export interface InjectableType<T> extends Type<T> {
 | |
|   /**
 | |
|    * Opaque type whose structure is highly version dependent. Do not rely on any properties.
 | |
|    */
 | |
|   ɵprov: never;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A type which has an `InjectorDef` static field.
 | |
|  *
 | |
|  * `InjectorDefTypes` can be used to configure a `StaticInjector`.
 | |
|  *
 | |
|  * @publicApi
 | |
|  */
 | |
| export interface InjectorType<T> extends Type<T> {
 | |
|   /**
 | |
|    * Opaque type whose structure is highly version dependent. Do not rely on any properties.
 | |
|    */
 | |
|   ɵinj: never;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Describes the `InjectorDef` equivalent of a `ModuleWithProviders`, an `InjectorDefType` with an
 | |
|  * associated array of providers.
 | |
|  *
 | |
|  * Objects of this type can be listed in the imports section of an `InjectorDef`.
 | |
|  *
 | |
|  * NOTE: This is a private type and should not be exported
 | |
|  */
 | |
| export interface InjectorTypeWithProviders<T> {
 | |
|   ngModule: InjectorType<T>;
 | |
|   providers?: (Type<any>|ValueProvider|ExistingProvider|FactoryProvider|ConstructorProvider|
 | |
|                StaticClassProvider|ClassProvider|any[])[];
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
 | |
|  * in which injectors (if any) it will be available.
 | |
|  *
 | |
|  * This should be assigned to a static `ɵprov` field on a type, which will then be an
 | |
|  * `InjectableType`.
 | |
|  *
 | |
|  * Options:
 | |
|  * * `providedIn` determines which injectors will include the injectable, by either associating it
 | |
|  *   with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
 | |
|  *   provided in the `'root'` injector, which will be the application-level injector in most apps.
 | |
|  * * `factory` gives the zero argument function which will create an instance of the injectable.
 | |
|  *   The factory can call `inject` to access the `Injector` and request injection of dependencies.
 | |
|  *
 | |
|  * @codeGenApi
 | |
|  */
 | |
| export function ɵɵdefineInjectable<T>(opts: {
 | |
|   token: unknown,
 | |
|   providedIn?: Type<any>| 'root' | 'platform' | 'any' | null,
 | |
|   factory: () => T,
 | |
| }): never {
 | |
|   return ({
 | |
|     token: opts.token, providedIn: opts.providedIn as any || null, factory: opts.factory,
 | |
|         value: undefined,
 | |
|   } as ɵɵInjectableDef<T>) as never;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @deprecated in v8, delete after v10. This API should be used only be generated code, and that
 | |
|  * code should now use ɵɵdefineInjectable instead.
 | |
|  * @publicApi
 | |
|  */
 | |
| export const defineInjectable = ɵɵdefineInjectable;
 | |
| 
 | |
| /**
 | |
|  * Construct an `InjectorDef` which configures an injector.
 | |
|  *
 | |
|  * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
 | |
|  * `InjectorType`.
 | |
|  *
 | |
|  * Options:
 | |
|  *
 | |
|  * * `factory`: an `InjectorType` is an instantiable type, so a zero argument `factory` function to
 | |
|  *   create the type must be provided. If that factory function needs to inject arguments, it can
 | |
|  *   use the `inject` function.
 | |
|  * * `providers`: an optional array of providers to add to the injector. Each provider must
 | |
|  *   either have a factory or point to a type which has a `ɵprov` static property (the
 | |
|  *   type must be an `InjectableType`).
 | |
|  * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
 | |
|  *   whose providers will also be added to the injector. Locally provided types will override
 | |
|  *   providers from imports.
 | |
|  *
 | |
|  * @publicApi
 | |
|  */
 | |
| export function ɵɵdefineInjector(options: {factory: () => any, providers?: any[], imports?: any[]}):
 | |
|     never {
 | |
|   return ({
 | |
|     factory: options.factory, providers: options.providers || [], imports: options.imports || [],
 | |
|   } as ɵɵInjectorDef<any>) as never;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
 | |
|  * inherited value.
 | |
|  *
 | |
|  * @param type A type which may have its own (non-inherited) `ɵprov`.
 | |
|  */
 | |
| export function getInjectableDef<T>(type: any): ɵɵInjectableDef<T>|null {
 | |
|   return getOwnDefinition(type, type[NG_PROV_DEF]) ||
 | |
|       getOwnDefinition(type, type[NG_INJECTABLE_DEF]);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Return `def` only if it is defined directly on `type` and is not inherited from a base
 | |
|  * class of `type`.
 | |
|  *
 | |
|  * The function `Object.hasOwnProperty` is not sufficient to distinguish this case because in older
 | |
|  * browsers (e.g. IE10) static property inheritance is implemented by copying the properties.
 | |
|  *
 | |
|  * Instead, the definition's `token` is compared to the `type`, and if they don't match then the
 | |
|  * property was not defined directly on the type itself, and was likely inherited. The definition
 | |
|  * is only returned if the `type` matches the `def.token`.
 | |
|  */
 | |
| function getOwnDefinition<T>(type: any, def: ɵɵInjectableDef<T>): ɵɵInjectableDef<T>|null {
 | |
|   return def && def.token === type ? def : null;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
 | |
|  *
 | |
|  * @param type A type which may have `ɵprov`, via inheritance.
 | |
|  *
 | |
|  * @deprecated Will be removed in v10, where an error will occur in the scenario if we find the
 | |
|  * `ɵprov` on an ancestor only.
 | |
|  */
 | |
| export function getInheritedInjectableDef<T>(type: any): ɵɵInjectableDef<T>|null {
 | |
|   // See `jit/injectable.ts#compileInjectable` for context on NG_PROV_DEF_FALLBACK.
 | |
|   const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF] ||
 | |
|                        (type[NG_PROV_DEF_FALLBACK] && type[NG_PROV_DEF_FALLBACK]()));
 | |
| 
 | |
|   if (def) {
 | |
|     const typeName = getTypeName(type);
 | |
|     // TODO(FW-1307): Re-add ngDevMode when closure can handle it
 | |
|     // ngDevMode &&
 | |
|     console.warn(
 | |
|         `DEPRECATED: DI is instantiating a token "${typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` +
 | |
|         `This will become an error in v10. Please add @Injectable() to the "${typeName}" class.`);
 | |
|     return def;
 | |
|   } else {
 | |
|     return null;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** Gets the name of a type, accounting for some cross-browser differences. */
 | |
| function getTypeName(type: any): string {
 | |
|   // `Function.prototype.name` behaves differently between IE and other browsers. In most browsers
 | |
|   // it'll always return the name of the function itself, no matter how many other functions it
 | |
|   // inherits from. On IE the function doesn't have its own `name` property, but it takes it from
 | |
|   // the lowest level in the prototype chain. E.g. if we have `class Foo extends Parent` most
 | |
|   // browsers will evaluate `Foo.name` to `Foo` while IE will return `Parent`. We work around
 | |
|   // the issue by converting the function to a string and parsing its name out that way via a regex.
 | |
|   if (type.hasOwnProperty('name')) {
 | |
|     return type.name;
 | |
|   }
 | |
| 
 | |
|   const match = ('' + type).match(/^function\s*([^\s(]+)/);
 | |
|   return match === null ? '' : match[1];
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read the injector def type in a way which is immune to accidentally reading inherited value.
 | |
|  *
 | |
|  * @param type type which may have an injector def (`ɵinj`)
 | |
|  */
 | |
| export function getInjectorDef<T>(type: any): ɵɵInjectorDef<T>|null {
 | |
|   return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ?
 | |
|       (type as any)[NG_INJ_DEF] :
 | |
|       null;
 | |
| }
 | |
| 
 | |
| export const NG_PROV_DEF = getClosureSafeProperty({ɵprov: getClosureSafeProperty});
 | |
| export const NG_INJ_DEF = getClosureSafeProperty({ɵinj: getClosureSafeProperty});
 | |
| 
 | |
| // On IE10 properties defined via `defineProperty` won't be inherited by child classes,
 | |
| // which will break inheriting the injectable definition from a grandparent through an
 | |
| // undecorated parent class. We work around it by defining a fallback method which will be
 | |
| // used to retrieve the definition. This should only be a problem in JIT mode, because in
 | |
| // AOT TypeScript seems to have a workaround for static properties. When inheriting from an
 | |
| // undecorated parent is no longer supported in v10, this can safely be removed.
 | |
| export const NG_PROV_DEF_FALLBACK = getClosureSafeProperty({ɵprovFallback: getClosureSafeProperty});
 | |
| 
 | |
| // We need to keep these around so we can read off old defs if new defs are unavailable
 | |
| export const NG_INJECTABLE_DEF = getClosureSafeProperty({ngInjectableDef: getClosureSafeProperty});
 | |
| export const NG_INJECTOR_DEF = getClosureSafeProperty({ngInjectorDef: getClosureSafeProperty});
 |