refactor: move angular source to /packages rather than modules/@angular
This commit is contained in:
61
packages/core/src/di/forward_ref.ts
Normal file
61
packages/core/src/di/forward_ref.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @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 '../type';
|
||||
import {stringify} from '../util';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface that a function passed into {@link forwardRef} has to implement.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref_fn'}
|
||||
* @experimental
|
||||
*/
|
||||
export interface ForwardRefFn { (): any; }
|
||||
|
||||
/**
|
||||
* Allows to refer to references which are not yet defined.
|
||||
*
|
||||
* For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
|
||||
* DI is declared,
|
||||
* but not yet defined. It is also used when the `token` which we use when creating a query is not
|
||||
* yet defined.
|
||||
*
|
||||
* ### Example
|
||||
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
|
||||
* @experimental
|
||||
*/
|
||||
export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
|
||||
(<any>forwardRefFn).__forward_ref__ = forwardRef;
|
||||
(<any>forwardRefFn).toString = function() { return stringify(this()); };
|
||||
return (<Type<any>><any>forwardRefFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily retrieves the reference value from a forwardRef.
|
||||
*
|
||||
* Acts as the identity function when given a non-forward-ref value.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/GU72mJrk1fiodChcmiDR?p=preview))
|
||||
*
|
||||
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
|
||||
*
|
||||
* See: {@link forwardRef}
|
||||
* @experimental
|
||||
*/
|
||||
export function resolveForwardRef(type: any): any {
|
||||
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
|
||||
type.__forward_ref__ === forwardRef) {
|
||||
return (<ForwardRefFn>type)();
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
67
packages/core/src/di/injection_token.ts
Normal file
67
packages/core/src/di/injection_token.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a token that can be used in a DI Provider.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/Ys9ezXpj2Mnoy3Uc8KBp?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var t = new OpaqueToken("value");
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* {provide: t, useValue: "bindingValue"}
|
||||
* ]);
|
||||
*
|
||||
* expect(injector.get(t)).toEqual("bindingValue");
|
||||
* ```
|
||||
*
|
||||
* Using an `OpaqueToken` is preferable to using strings as tokens because of possible collisions
|
||||
* caused by multiple providers using the same string as two different tokens.
|
||||
*
|
||||
* Using an `OpaqueToken` is preferable to using an `Object` as tokens because it provides better
|
||||
* error messages.
|
||||
* @deprecated since v4.0.0 because it does not support type information, use `InjectionToken<?>`
|
||||
* instead.
|
||||
*/
|
||||
export class OpaqueToken {
|
||||
constructor(protected _desc: string) {}
|
||||
|
||||
toString(): string { return `Token ${this._desc}`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a token that can be used in a DI Provider.
|
||||
*
|
||||
* Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
|
||||
* runtime representation) such as when injecting an interface, callable type, array or
|
||||
* parametrized type.
|
||||
*
|
||||
* `InjectionToken` is parametrize on `T` which is the type of object which will be returned by the
|
||||
* `Injector`. This provides additional level of type safety.
|
||||
*
|
||||
* ```
|
||||
* interface MyInterface {...}
|
||||
* var myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken'));
|
||||
* // myInterface is inferred to be MyInterface.
|
||||
* ```
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export class InjectionToken<T> extends OpaqueToken {
|
||||
// This unused property is needed here so that TS can differentiate InjectionToken from
|
||||
// OpaqueToken since otherwise they would have the same shape and be treated as equivalent.
|
||||
private _differentiate_from_OpaqueToken_structurally: any;
|
||||
constructor(desc: string) { super(desc); }
|
||||
|
||||
toString(): string { return `InjectionToken ${this._desc}`; }
|
||||
}
|
63
packages/core/src/di/injector.ts
Normal file
63
packages/core/src/di/injector.ts
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @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 '../type';
|
||||
import {stringify} from '../util';
|
||||
|
||||
import {InjectionToken} from './injection_token';
|
||||
|
||||
const _THROW_IF_NOT_FOUND = new Object();
|
||||
export const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
|
||||
class _NullInjector implements Injector {
|
||||
get(token: any, notFoundValue: any = _THROW_IF_NOT_FOUND): any {
|
||||
if (notFoundValue === _THROW_IF_NOT_FOUND) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
}
|
||||
return notFoundValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Injector interface
|
||||
* @howToUse
|
||||
* ```
|
||||
* const injector: Injector = ...;
|
||||
* injector.get(...);
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
||||
*
|
||||
* `Injector` returns itself when given `Injector` as a token:
|
||||
* {@example core/di/ts/injector_spec.ts region='injectInjector'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export abstract class Injector {
|
||||
static THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
||||
static NULL: Injector = new _NullInjector();
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector based on the provided token.
|
||||
* If not found:
|
||||
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
|
||||
* Injector.THROW_IF_NOT_FOUND is given
|
||||
* - Returns the `notFoundValue` otherwise
|
||||
*/
|
||||
abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
|
||||
/**
|
||||
* @deprecated from v4.0.0 use Type<T> or InjectToken<T>
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
abstract get(token: any, notFoundValue?: any): any;
|
||||
}
|
288
packages/core/src/di/metadata.ts
Normal file
288
packages/core/src/di/metadata.ts
Normal file
@ -0,0 +1,288 @@
|
||||
/**
|
||||
* @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 {makeDecorator, makeParamDecorator} from '../util/decorators';
|
||||
|
||||
|
||||
/**
|
||||
* Type of the Inject decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface InjectDecorator {
|
||||
/**
|
||||
* @whatItDoes A parameter decorator that specifies a dependency.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(@Inject("MyEngine") public engine:Engine) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Inject'}
|
||||
*
|
||||
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the
|
||||
* parameter.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='InjectWithoutDecorator'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(token: any): any;
|
||||
new (token: any): Inject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the Inject metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface Inject { token: any; }
|
||||
|
||||
/**
|
||||
* Inject decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Inject: InjectDecorator = makeParamDecorator('Inject', [['token', undefined]]);
|
||||
|
||||
|
||||
/**
|
||||
* Type of the Optional decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface OptionalDecorator {
|
||||
/**
|
||||
* @whatItDoes A parameter metadata that marks a dependency as optional.
|
||||
* {@link Injector} provides `null` if the dependency is not found.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(@Optional() public engine:Engine) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Optional'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(): any;
|
||||
new (): Optional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the Optional metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface Optional {}
|
||||
|
||||
/**
|
||||
* Optional decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Optional: OptionalDecorator = makeParamDecorator('Optional', []);
|
||||
|
||||
/**
|
||||
* Type of the Injectable decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface InjectableDecorator {
|
||||
/**
|
||||
* @whatItDoes A marker metadata that marks a class as available to {@link Injector} for creation.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {}
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Injectable'}
|
||||
*
|
||||
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
|
||||
* does not have `@Injectable` marker, as shown in the example below.
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='InjectableThrows'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(): any;
|
||||
new (): Injectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the Injectable metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface Injectable {}
|
||||
|
||||
/**
|
||||
* Injectable decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Injectable: InjectableDecorator = <InjectableDecorator>makeDecorator('Injectable', []);
|
||||
|
||||
/**
|
||||
* Type of the Self decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface SelfDecorator {
|
||||
/**
|
||||
* @whatItDoes Specifies that an {@link Injector} should retrieve a dependency only from itself.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(@Self() public engine:Engine) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Self'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(): any;
|
||||
new (): Self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the Self metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface Self {}
|
||||
|
||||
/**
|
||||
* Self decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Self: SelfDecorator = makeParamDecorator('Self', []);
|
||||
|
||||
|
||||
/**
|
||||
* Type of the SkipSelf decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface SkipSelfDecorator {
|
||||
/**
|
||||
* @whatItDoes Specifies that the dependency resolution should start from the parent injector.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(@SkipSelf() public engine:Engine) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='SkipSelf'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(): any;
|
||||
new (): SkipSelf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the SkipSelf metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface SkipSelf {}
|
||||
|
||||
/**
|
||||
* SkipSelf decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf', []);
|
||||
|
||||
/**
|
||||
* Type of the Host decorator / constructor function.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface HostDecorator {
|
||||
/**
|
||||
* @whatItDoes Specifies that an injector should retrieve a dependency from any injector until
|
||||
* reaching the host element of the current component.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(@Host() public engine:Engine) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/metadata_spec.ts region='Host'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
(): any;
|
||||
new (): Host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of the Host metadata.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface Host {}
|
||||
|
||||
/**
|
||||
* Host decorator and metadata.
|
||||
*
|
||||
* @stable
|
||||
* @Annotation
|
||||
*/
|
||||
export const Host: HostDecorator = makeParamDecorator('Host', []);
|
220
packages/core/src/di/provider.ts
Normal file
220
packages/core/src/di/provider.ts
Normal file
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @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 '../type';
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of `Type` when `Type' is used
|
||||
* as token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: TypeProvider = MyService;
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Create an instance by invoking the `new` operator and supplying additional arguments.
|
||||
* This form is a short form of `TypeProvider`;
|
||||
*
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface TypeProvider extends Type<any> {}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return a value for a token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* const provider: ValueProvider = {provide: 'someToken', useValue: 'someValue'};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ValueProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ValueProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* The value to inject.
|
||||
*/
|
||||
useValue: any;
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return an instance of `useClass` for a token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* @Injectable()
|
||||
* class MyService {}
|
||||
*
|
||||
* const provider: ClassProvider = {provide: 'someToken', useClass: MyService};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProvider'}
|
||||
*
|
||||
* Note that following two providers are not equal:
|
||||
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ClassProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* Class to instantiate for the `token`.
|
||||
*/
|
||||
useClass: Type<any>;
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return a value of another `useExisting` token.
|
||||
* @howToUse
|
||||
* ```
|
||||
* const provider: ExistingProvider = {provide: 'someToken', useExisting: 'someOtherToken'};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='ExistingProvider'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface ExistingProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* Existing `token` to return. (equivalent to `injector.get(useExisting)`)
|
||||
*/
|
||||
useExisting: any;
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Configures the {@link Injector} to return a value by invoking a `useFactory`
|
||||
* function.
|
||||
* @howToUse
|
||||
* ```
|
||||
* function serviceFactory() { ... }
|
||||
*
|
||||
* const provider: FactoryProvider = {provide: 'someToken', useFactory: serviceFactory, deps: []};
|
||||
* ```
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='FactoryProvider'}
|
||||
*
|
||||
* Dependencies can also be marked as optional:
|
||||
* {@example core/di/ts/provider_spec.ts region='FactoryProviderOptionalDeps'}
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface FactoryProvider {
|
||||
/**
|
||||
* An injection token. (Typically an instance of `Type` or `InjectionToken`, but can be `any`).
|
||||
*/
|
||||
provide: any;
|
||||
|
||||
/**
|
||||
* A function to invoke to create a value for this `token`. The function is invoked with
|
||||
* resolved values of `token`s in the `deps` field.
|
||||
*/
|
||||
useFactory: Function;
|
||||
|
||||
/**
|
||||
* A list of `token`s which need to be resolved by the injector. The list of values is then
|
||||
* used as arguments to the `useFactory` function.
|
||||
*/
|
||||
deps?: any[];
|
||||
|
||||
/**
|
||||
* If true, then injector returns an array of instances. This is useful to allow multiple
|
||||
* providers spread across many files to provide configuration information to a common token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||
*/
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @whatItDoes Describes how the {@link Injector} should be configured.
|
||||
* @howToUse
|
||||
* See {@link TypeProvider}, {@link ValueProvider}, {@link ClassProvider}, {@link ExistingProvider},
|
||||
* {@link FactoryProvider}.
|
||||
*
|
||||
* @description
|
||||
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export type Provider =
|
||||
TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];
|
240
packages/core/src/di/reflective_errors.ts
Normal file
240
packages/core/src/di/reflective_errors.ts
Normal file
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* @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 {wrappedError} from '../error_handler';
|
||||
import {ERROR_ORIGINAL_ERROR, getOriginalError} from '../errors';
|
||||
import {Type} from '../type';
|
||||
import {stringify} from '../util';
|
||||
|
||||
import {ReflectiveInjector} from './reflective_injector';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
|
||||
function findFirstClosedCycle(keys: any[]): any[] {
|
||||
const res: any[] = [];
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
if (res.indexOf(keys[i]) > -1) {
|
||||
res.push(keys[i]);
|
||||
return res;
|
||||
}
|
||||
res.push(keys[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function constructResolvingPath(keys: any[]): string {
|
||||
if (keys.length > 1) {
|
||||
const reversed = findFirstClosedCycle(keys.slice().reverse());
|
||||
const tokenStrs = reversed.map(k => stringify(k.token));
|
||||
return ' (' + tokenStrs.join(' -> ') + ')';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export interface InjectionError extends Error {
|
||||
keys: ReflectiveKey[];
|
||||
injectors: ReflectiveInjector[];
|
||||
constructResolvingMessage: (this: InjectionError) => string;
|
||||
addKey(injector: ReflectiveInjector, key: ReflectiveKey): void;
|
||||
}
|
||||
|
||||
function injectionError(
|
||||
injector: ReflectiveInjector, key: ReflectiveKey,
|
||||
constructResolvingMessage: (this: InjectionError) => string,
|
||||
originalError?: Error): InjectionError {
|
||||
const error = (originalError ? wrappedError('', originalError) : Error()) as InjectionError;
|
||||
error.addKey = addKey;
|
||||
error.keys = [key];
|
||||
error.injectors = [injector];
|
||||
error.constructResolvingMessage = constructResolvingMessage;
|
||||
error.message = error.constructResolvingMessage();
|
||||
(error as any)[ERROR_ORIGINAL_ERROR] = originalError;
|
||||
return error;
|
||||
}
|
||||
|
||||
function addKey(this: InjectionError, injector: ReflectiveInjector, key: ReflectiveKey): void {
|
||||
this.injectors.push(injector);
|
||||
this.keys.push(key);
|
||||
this.message = this.constructResolvingMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when trying to retrieve a dependency by key from {@link Injector}, but the
|
||||
* {@link Injector} does not have a {@link Provider} for the given key.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/vq8D3FRB9aGbnWJqtEPE?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor(b:B) {}
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export function noProviderError(injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
||||
return injectionError(injector, key, function(this: InjectionError) {
|
||||
const first = stringify(this.keys[0].token);
|
||||
return `No provider for ${first}!${constructResolvingPath(this.keys)}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when dependencies form a cycle.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/wYQdNos0Tzql3ei1EV9j?p=info))
|
||||
*
|
||||
* ```typescript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* {provide: "one", useFactory: (two) => "two", deps: [[new Inject("two")]]},
|
||||
* {provide: "two", useFactory: (one) => "one", deps: [[new Inject("one")]]}
|
||||
* ]);
|
||||
*
|
||||
* expect(() => injector.get("one")).toThrowError();
|
||||
* ```
|
||||
*
|
||||
* Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
|
||||
*/
|
||||
export function cyclicDependencyError(
|
||||
injector: ReflectiveInjector, key: ReflectiveKey): InjectionError {
|
||||
return injectionError(injector, key, function(this: InjectionError) {
|
||||
return `Cannot instantiate cyclic dependency!${constructResolvingPath(this.keys)}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a constructing type returns with an Error.
|
||||
*
|
||||
* The `InstantiationError` class contains the original error plus the dependency graph which caused
|
||||
* this object to be instantiated.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/7aWYdcqTQsP0eNqEdUAf?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor() {
|
||||
* throw new Error('message');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([A]);
|
||||
|
||||
* try {
|
||||
* injector.get(A);
|
||||
* } catch (e) {
|
||||
* expect(e instanceof InstantiationError).toBe(true);
|
||||
* expect(e.originalException.message).toEqual("message");
|
||||
* expect(e.originalStack).toBeDefined();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function instantiationError(
|
||||
injector: ReflectiveInjector, originalException: any, originalStack: any,
|
||||
key: ReflectiveKey): InjectionError {
|
||||
return injectionError(injector, key, function(this: InjectionError) {
|
||||
const first = stringify(this.keys[0].token);
|
||||
return `${getOriginalError(this).message}: Error during instantiation of ${first}!${constructResolvingPath(this.keys)}.`;
|
||||
}, originalException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector}
|
||||
* creation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YatCFbPAMCL0JSSQ4mvH?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export function invalidProviderError(provider: any) {
|
||||
return Error(
|
||||
`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when the class has no annotation information.
|
||||
*
|
||||
* Lack of annotation information prevents the {@link Injector} from determining which dependencies
|
||||
* need to be injected into the constructor.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/rHnZtlNS7vJOPQ6pcVkm?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {
|
||||
* constructor(b) {}
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
||||
* ```
|
||||
*
|
||||
* This error is also thrown when the class not marked with {@link Injectable} has parameter types.
|
||||
*
|
||||
* ```typescript
|
||||
* class B {}
|
||||
*
|
||||
* class A {
|
||||
* constructor(b:B) {} // no information about the parameter types of A is available at runtime.
|
||||
* }
|
||||
*
|
||||
* expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
|
||||
* ```
|
||||
* @stable
|
||||
*/
|
||||
export function noAnnotationError(typeOrFunc: Type<any>| Function, params: any[][]): Error {
|
||||
const signature: string[] = [];
|
||||
for (let i = 0, ii = params.length; i < ii; i++) {
|
||||
const parameter = params[i];
|
||||
if (!parameter || parameter.length == 0) {
|
||||
signature.push('?');
|
||||
} else {
|
||||
signature.push(parameter.map(stringify).join(' '));
|
||||
}
|
||||
}
|
||||
return Error(
|
||||
'Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' +
|
||||
signature.join(', ') + '). ' +
|
||||
'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
|
||||
stringify(typeOrFunc) + '\' is decorated with Injectable.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when getting an object by index.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/bRs0SX2OTQiJzqvjgl8P?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class A {}
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([A]);
|
||||
*
|
||||
* expect(() => injector.getAt(100)).toThrowError();
|
||||
* ```
|
||||
* @stable
|
||||
*/
|
||||
export function outOfBoundsError(index: number) {
|
||||
return Error(`Index ${index} is out-of-bounds.`);
|
||||
}
|
||||
|
||||
// TODO: add a working example after alpha38 is released
|
||||
/**
|
||||
* Thrown when a multi provider and a regular provider are bound to the same token.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* expect(() => Injector.resolveAndCreate([
|
||||
* { provide: "Strings", useValue: "string1", multi: true},
|
||||
* { provide: "Strings", useValue: "string2", multi: false}
|
||||
* ])).toThrowError();
|
||||
* ```
|
||||
*/
|
||||
export function mixingMultiProvidersWithRegularProvidersError(
|
||||
provider1: any, provider2: any): Error {
|
||||
return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
|
||||
}
|
474
packages/core/src/di/reflective_injector.ts
Normal file
474
packages/core/src/di/reflective_injector.ts
Normal file
@ -0,0 +1,474 @@
|
||||
/**
|
||||
* @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 {Injector, THROW_IF_NOT_FOUND} from './injector';
|
||||
import {Self, SkipSelf} from './metadata';
|
||||
import {Provider} from './provider';
|
||||
import {cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError} from './reflective_errors';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
|
||||
|
||||
// Threshold for the dynamic version
|
||||
const UNDEFINED = new Object();
|
||||
|
||||
/**
|
||||
* A ReflectiveDependency injection container used for instantiating objects and resolving
|
||||
* dependencies.
|
||||
*
|
||||
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
||||
* constructor dependencies.
|
||||
*
|
||||
* In typical use, application code asks for the dependencies in the constructor and they are
|
||||
* resolved by the `Injector`.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
|
||||
*
|
||||
* The following example creates an `Injector` configured to create `Engine` and `Car`.
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
||||
* var car = injector.get(Car);
|
||||
* expect(car instanceof Car).toBe(true);
|
||||
* expect(car.engine instanceof Engine).toBe(true);
|
||||
* ```
|
||||
*
|
||||
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
||||
* resolve all of the object's dependencies automatically.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export abstract class ReflectiveInjector implements Injector {
|
||||
/**
|
||||
* Turns an array of provider definitions into an array of resolved providers.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested arrays and converting individual
|
||||
* providers into an array of {@link ResolvedReflectiveProvider}s.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/AiXTHi?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
|
||||
*
|
||||
* expect(providers.length).toEqual(2);
|
||||
*
|
||||
* expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
|
||||
* expect(providers[0].key.displayName).toBe("Car");
|
||||
* expect(providers[0].dependencies.length).toEqual(1);
|
||||
* expect(providers[0].factory).toBeDefined();
|
||||
*
|
||||
* expect(providers[1].key.displayName).toBe("Engine");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* See {@link ReflectiveInjector#fromResolvedProviders} for more info.
|
||||
*/
|
||||
static resolve(providers: Provider[]): ResolvedReflectiveProvider[] {
|
||||
return resolveReflectiveProviders(providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an array of providers and creates an injector from those providers.
|
||||
*
|
||||
* The passed-in providers can be an array of `Type`, {@link Provider},
|
||||
* or a recursive array of more providers.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ePOccA?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
||||
* expect(injector.get(Car) instanceof Car).toBe(true);
|
||||
* ```
|
||||
*
|
||||
* This function is slower than the corresponding `fromResolvedProviders`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
|
||||
*/
|
||||
static resolveAndCreate(providers: Provider[], parent: Injector = null): ReflectiveInjector {
|
||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an injector from previously resolved providers.
|
||||
*
|
||||
* This API is the recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/KrSMci?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var providers = ReflectiveInjector.resolve([Car, Engine]);
|
||||
* var injector = ReflectiveInjector.fromResolvedProviders(providers);
|
||||
* expect(injector.get(Car) instanceof Car).toBe(true);
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
|
||||
ReflectiveInjector {
|
||||
return new ReflectiveInjector_(providers, parent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parent of this injector.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/eosMGo?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var parent = ReflectiveInjector.resolveAndCreate([]);
|
||||
* var child = parent.resolveAndCreateChild([]);
|
||||
* expect(child.parent).toBe(parent);
|
||||
* ```
|
||||
*/
|
||||
abstract get parent(): Injector;
|
||||
|
||||
/**
|
||||
* Resolves an array of providers and creates a child injector from those providers.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* The passed-in providers can be an array of `Type`, {@link Provider},
|
||||
* or a recursive array of more providers.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/opB3T4?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class ParentProvider {}
|
||||
* class ChildProvider {}
|
||||
*
|
||||
* var parent = ReflectiveInjector.resolveAndCreate([ParentProvider]);
|
||||
* var child = parent.resolveAndCreateChild([ChildProvider]);
|
||||
*
|
||||
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
|
||||
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
|
||||
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
||||
* ```
|
||||
*
|
||||
* This function is slower than the corresponding `createChildFromResolved`
|
||||
* because it needs to resolve the passed-in providers first.
|
||||
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
|
||||
*/
|
||||
abstract resolveAndCreateChild(providers: Provider[]): ReflectiveInjector;
|
||||
|
||||
/**
|
||||
* Creates a child injector from previously resolved providers.
|
||||
*
|
||||
* <!-- TODO: Add a link to the section of the user guide talking about hierarchical injection.
|
||||
* -->
|
||||
*
|
||||
* This API is the recommended way to construct injectors in performance-sensitive parts.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/VhyfjN?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* class ParentProvider {}
|
||||
* class ChildProvider {}
|
||||
*
|
||||
* var parentProviders = ReflectiveInjector.resolve([ParentProvider]);
|
||||
* var childProviders = ReflectiveInjector.resolve([ChildProvider]);
|
||||
*
|
||||
* var parent = ReflectiveInjector.fromResolvedProviders(parentProviders);
|
||||
* var child = parent.createChildFromResolved(childProviders);
|
||||
*
|
||||
* expect(child.get(ParentProvider) instanceof ParentProvider).toBe(true);
|
||||
* expect(child.get(ChildProvider) instanceof ChildProvider).toBe(true);
|
||||
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
||||
* ```
|
||||
*/
|
||||
abstract createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector;
|
||||
|
||||
/**
|
||||
* Resolves a provider and instantiates an object in the context of the injector.
|
||||
*
|
||||
* The created object does not get cached by the injector.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yvVXoB?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
|
||||
*
|
||||
* var car = injector.resolveAndInstantiate(Car);
|
||||
* expect(car.engine).toBe(injector.get(Engine));
|
||||
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
|
||||
* ```
|
||||
*/
|
||||
abstract resolveAndInstantiate(provider: Provider): any;
|
||||
|
||||
/**
|
||||
* Instantiates an object using a resolved provider in the context of the injector.
|
||||
*
|
||||
* The created object does not get cached by the injector.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ptCImQ?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Injectable()
|
||||
* class Engine {
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class Car {
|
||||
* constructor(public engine:Engine) {}
|
||||
* }
|
||||
*
|
||||
* var injector = ReflectiveInjector.resolveAndCreate([Engine]);
|
||||
* var carProvider = ReflectiveInjector.resolve([Car])[0];
|
||||
* var car = injector.instantiateResolved(carProvider);
|
||||
* expect(car.engine).toBe(injector.get(Engine));
|
||||
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
|
||||
* ```
|
||||
*/
|
||||
abstract instantiateResolved(provider: ResolvedReflectiveProvider): any;
|
||||
|
||||
abstract get(token: any, notFoundValue?: any): any;
|
||||
}
|
||||
|
||||
export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
/** @internal */
|
||||
_constructionCounter: number = 0;
|
||||
/** @internal */
|
||||
public _providers: ResolvedReflectiveProvider[];
|
||||
/** @internal */
|
||||
public _parent: Injector;
|
||||
|
||||
keyIds: number[];
|
||||
objs: any[];
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) {
|
||||
this._providers = _providers;
|
||||
this._parent = _parent;
|
||||
|
||||
const len = _providers.length;
|
||||
|
||||
this.keyIds = new Array(len);
|
||||
this.objs = new Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
this.keyIds[i] = _providers[i].key.id;
|
||||
this.objs[i] = UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
|
||||
return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
|
||||
}
|
||||
|
||||
get parent(): Injector { return this._parent; }
|
||||
|
||||
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
|
||||
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
||||
return this.createChildFromResolved(ResolvedReflectiveProviders);
|
||||
}
|
||||
|
||||
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
|
||||
const inj = new ReflectiveInjector_(providers);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
|
||||
resolveAndInstantiate(provider: Provider): any {
|
||||
return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
|
||||
}
|
||||
|
||||
instantiateResolved(provider: ResolvedReflectiveProvider): any {
|
||||
return this._instantiateProvider(provider);
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
|
||||
if (index < 0 || index >= this._providers.length) {
|
||||
throw outOfBoundsError(index);
|
||||
}
|
||||
return this._providers[index];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_new(provider: ResolvedReflectiveProvider): any {
|
||||
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
|
||||
throw cyclicDependencyError(this, provider.key);
|
||||
}
|
||||
return this._instantiateProvider(provider);
|
||||
}
|
||||
|
||||
private _getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||
|
||||
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||
if (provider.multiProvider) {
|
||||
const res = new Array(provider.resolvedFactories.length);
|
||||
for (let i = 0; i < provider.resolvedFactories.length; ++i) {
|
||||
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return this._instantiate(provider, provider.resolvedFactories[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private _instantiate(
|
||||
provider: ResolvedReflectiveProvider,
|
||||
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
|
||||
const factory = ResolvedReflectiveFactory.factory;
|
||||
|
||||
let deps: any[];
|
||||
try {
|
||||
deps =
|
||||
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
|
||||
} catch (e) {
|
||||
if (e.addKey) {
|
||||
e.addKey(this, provider.key);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
let obj: any;
|
||||
try {
|
||||
obj = factory(...deps);
|
||||
} catch (e) {
|
||||
throw instantiationError(this, e, e.stack, provider.key);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private _getByReflectiveDependency(dep: ReflectiveDependency): any {
|
||||
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
|
||||
}
|
||||
|
||||
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any {
|
||||
if (key === INJECTOR_KEY) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (visibility instanceof Self) {
|
||||
return this._getByKeySelf(key, notFoundValue);
|
||||
|
||||
} else {
|
||||
return this._getByKeyDefault(key, notFoundValue, visibility);
|
||||
}
|
||||
}
|
||||
|
||||
private _getObjByKeyId(keyId: number): any {
|
||||
for (let i = 0; i < this.keyIds.length; i++) {
|
||||
if (this.keyIds[i] === keyId) {
|
||||
if (this.objs[i] === UNDEFINED) {
|
||||
this.objs[i] = this._new(this._providers[i]);
|
||||
}
|
||||
|
||||
return this.objs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
|
||||
if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
||||
return notFoundValue;
|
||||
} else {
|
||||
throw noProviderError(this, key);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
|
||||
const obj = this._getObjByKeyId(key.id);
|
||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any {
|
||||
let inj: Injector;
|
||||
|
||||
if (visibility instanceof SkipSelf) {
|
||||
inj = this._parent;
|
||||
} else {
|
||||
inj = this;
|
||||
}
|
||||
|
||||
while (inj instanceof ReflectiveInjector_) {
|
||||
const inj_ = <ReflectiveInjector_>inj;
|
||||
const obj = inj_._getObjByKeyId(key.id);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
inj = inj_._parent;
|
||||
}
|
||||
if (inj !== null) {
|
||||
return inj.get(key.token, notFoundValue);
|
||||
} else {
|
||||
return this._throwOrNull(key, notFoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
const providers =
|
||||
_mapProviders(this, (b: ResolvedReflectiveProvider) => ' "' + b.key.displayName + '" ')
|
||||
.join(', ');
|
||||
return `ReflectiveInjector(providers: [${providers}])`;
|
||||
}
|
||||
|
||||
toString(): string { return this.displayName; }
|
||||
}
|
||||
|
||||
const INJECTOR_KEY = ReflectiveKey.get(Injector);
|
||||
|
||||
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
|
||||
const res: any[] = new Array(injector._providers.length);
|
||||
for (let i = 0; i < injector._providers.length; ++i) {
|
||||
res[i] = fn(injector.getProviderAtIndex(i));
|
||||
}
|
||||
return res;
|
||||
}
|
78
packages/core/src/di/reflective_key.ts
Normal file
78
packages/core/src/di/reflective_key.ts
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @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 {stringify} from '../util';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
|
||||
|
||||
/**
|
||||
* A unique object used for retrieving items from the {@link ReflectiveInjector}.
|
||||
*
|
||||
* Keys have:
|
||||
* - a system-wide unique `id`.
|
||||
* - a `token`.
|
||||
*
|
||||
* `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
|
||||
* the
|
||||
* injector to store created objects in a more efficient way.
|
||||
*
|
||||
* `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
|
||||
* resolving
|
||||
* providers.
|
||||
* @experimental
|
||||
*/
|
||||
export class ReflectiveKey {
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
constructor(public token: Object, public id: number) {
|
||||
if (!token) {
|
||||
throw new Error('Token must be defined!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stringified token.
|
||||
*/
|
||||
get displayName(): string { return stringify(this.token); }
|
||||
|
||||
/**
|
||||
* Retrieves a `Key` for a token.
|
||||
*/
|
||||
static get(token: Object): ReflectiveKey {
|
||||
return _globalKeyRegistry.get(resolveForwardRef(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the number of keys registered in the system.
|
||||
*/
|
||||
static get numberOfKeys(): number { return _globalKeyRegistry.numberOfKeys; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class KeyRegistry {
|
||||
private _allKeys = new Map<Object, ReflectiveKey>();
|
||||
|
||||
get(token: Object): ReflectiveKey {
|
||||
if (token instanceof ReflectiveKey) return token;
|
||||
|
||||
if (this._allKeys.has(token)) {
|
||||
return this._allKeys.get(token);
|
||||
}
|
||||
|
||||
const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
|
||||
this._allKeys.set(token, newKey);
|
||||
return newKey;
|
||||
}
|
||||
|
||||
get numberOfKeys(): number { return this._allKeys.size; }
|
||||
}
|
||||
|
||||
const _globalKeyRegistry = new KeyRegistry();
|
266
packages/core/src/di/reflective_provider.ts
Normal file
266
packages/core/src/di/reflective_provider.ts
Normal file
@ -0,0 +1,266 @@
|
||||
/**
|
||||
* @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 {reflector} from '../reflection/reflection';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {Inject, Optional, Self, SkipSelf} from './metadata';
|
||||
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './provider';
|
||||
import {invalidProviderError, mixingMultiProvidersWithRegularProvidersError, noAnnotationError} from './reflective_errors';
|
||||
import {ReflectiveKey} from './reflective_key';
|
||||
|
||||
|
||||
interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider, ExistingProvider,
|
||||
FactoryProvider {}
|
||||
|
||||
/**
|
||||
* `Dependency` is used by the framework to extend DI.
|
||||
* This is internal to Angular and should not be used directly.
|
||||
*/
|
||||
export class ReflectiveDependency {
|
||||
constructor(
|
||||
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {}
|
||||
|
||||
static fromKey(key: ReflectiveKey): ReflectiveDependency {
|
||||
return new ReflectiveDependency(key, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
const _EMPTY_LIST: any[] = [];
|
||||
|
||||
/**
|
||||
* An internal resolved representation of a {@link Provider} used by the {@link Injector}.
|
||||
*
|
||||
* It is usually created automatically by `Injector.resolveAndCreate`.
|
||||
*
|
||||
* It can be created manually, as follows:
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/RfEnhh8kUEI0G3qsnIeT?p%3Dpreview&p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* var resolvedProviders = Injector.resolve([{ provide: 'message', useValue: 'Hello' }]);
|
||||
* var injector = Injector.fromResolvedProviders(resolvedProviders);
|
||||
*
|
||||
* expect(injector.get('message')).toEqual('Hello');
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface ResolvedReflectiveProvider {
|
||||
/**
|
||||
* A key, usually a `Type<any>`.
|
||||
*/
|
||||
key: ReflectiveKey;
|
||||
|
||||
/**
|
||||
* Factory function which can return an instance of an object represented by a key.
|
||||
*/
|
||||
resolvedFactories: ResolvedReflectiveFactory[];
|
||||
|
||||
/**
|
||||
* Indicates if the provider is a multi-provider or a regular provider.
|
||||
*/
|
||||
multiProvider: boolean;
|
||||
}
|
||||
|
||||
export class ResolvedReflectiveProvider_ implements ResolvedReflectiveProvider {
|
||||
constructor(
|
||||
public key: ReflectiveKey, public resolvedFactories: ResolvedReflectiveFactory[],
|
||||
public multiProvider: boolean) {}
|
||||
|
||||
get resolvedFactory(): ResolvedReflectiveFactory { return this.resolvedFactories[0]; }
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal resolved representation of a factory function created by resolving {@link
|
||||
* Provider}.
|
||||
* @experimental
|
||||
*/
|
||||
export class ResolvedReflectiveFactory {
|
||||
constructor(
|
||||
/**
|
||||
* Factory function which can return an instance of an object represented by a key.
|
||||
*/
|
||||
public factory: Function,
|
||||
|
||||
/**
|
||||
* Arguments (dependencies) to the `factory` function.
|
||||
*/
|
||||
public dependencies: ReflectiveDependency[]) {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve a single provider.
|
||||
*/
|
||||
function resolveReflectiveFactory(provider: NormalizedProvider): ResolvedReflectiveFactory {
|
||||
let factoryFn: Function;
|
||||
let resolvedDeps: ReflectiveDependency[];
|
||||
if (provider.useClass) {
|
||||
const useClass = resolveForwardRef(provider.useClass);
|
||||
factoryFn = reflector.factory(useClass);
|
||||
resolvedDeps = _dependenciesFor(useClass);
|
||||
} else if (provider.useExisting) {
|
||||
factoryFn = (aliasInstance: any) => aliasInstance;
|
||||
resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
|
||||
} else if (provider.useFactory) {
|
||||
factoryFn = provider.useFactory;
|
||||
resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
|
||||
} else {
|
||||
factoryFn = () => provider.useValue;
|
||||
resolvedDeps = _EMPTY_LIST;
|
||||
}
|
||||
return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the {@link Provider} into {@link ResolvedProvider}.
|
||||
*
|
||||
* {@link Injector} internally only uses {@link ResolvedProvider}, {@link Provider} contains
|
||||
* convenience provider syntax.
|
||||
*/
|
||||
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
|
||||
return new ResolvedReflectiveProvider_(
|
||||
ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of Providers.
|
||||
*/
|
||||
export function resolveReflectiveProviders(providers: Provider[]): ResolvedReflectiveProvider[] {
|
||||
const normalized = _normalizeProviders(providers, []);
|
||||
const resolved = normalized.map(resolveReflectiveProvider);
|
||||
const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());
|
||||
return Array.from(resolvedProviderMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a list of ResolvedProviders into a list where
|
||||
* each key is contained exactly once and multi providers
|
||||
* have been merged.
|
||||
*/
|
||||
export function mergeResolvedReflectiveProviders(
|
||||
providers: ResolvedReflectiveProvider[],
|
||||
normalizedProvidersMap: Map<number, ResolvedReflectiveProvider>):
|
||||
Map<number, ResolvedReflectiveProvider> {
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
const provider = providers[i];
|
||||
const existing = normalizedProvidersMap.get(provider.key.id);
|
||||
if (existing) {
|
||||
if (provider.multiProvider !== existing.multiProvider) {
|
||||
throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
|
||||
}
|
||||
if (provider.multiProvider) {
|
||||
for (let j = 0; j < provider.resolvedFactories.length; j++) {
|
||||
existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
||||
}
|
||||
} else {
|
||||
normalizedProvidersMap.set(provider.key.id, provider);
|
||||
}
|
||||
} else {
|
||||
let resolvedProvider: ResolvedReflectiveProvider;
|
||||
if (provider.multiProvider) {
|
||||
resolvedProvider = new ResolvedReflectiveProvider_(
|
||||
provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
|
||||
} else {
|
||||
resolvedProvider = provider;
|
||||
}
|
||||
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
||||
}
|
||||
}
|
||||
return normalizedProvidersMap;
|
||||
}
|
||||
|
||||
function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[] {
|
||||
providers.forEach(b => {
|
||||
if (b instanceof Type) {
|
||||
res.push({provide: b, useClass: b});
|
||||
|
||||
} else if (b && typeof b == 'object' && (b as any).provide !== undefined) {
|
||||
res.push(b as NormalizedProvider);
|
||||
|
||||
} else if (b instanceof Array) {
|
||||
_normalizeProviders(b, res);
|
||||
|
||||
} else {
|
||||
throw invalidProviderError(b);
|
||||
}
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
export function constructDependencies(
|
||||
typeOrFunc: any, dependencies: any[]): ReflectiveDependency[] {
|
||||
if (!dependencies) {
|
||||
return _dependenciesFor(typeOrFunc);
|
||||
} else {
|
||||
const params: any[][] = dependencies.map(t => [t]);
|
||||
return dependencies.map(t => _extractToken(typeOrFunc, t, params));
|
||||
}
|
||||
}
|
||||
|
||||
function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
|
||||
const params = reflector.parameters(typeOrFunc);
|
||||
|
||||
if (!params) return [];
|
||||
if (params.some(p => p == null)) {
|
||||
throw noAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
return params.map(p => _extractToken(typeOrFunc, p, params));
|
||||
}
|
||||
|
||||
function _extractToken(
|
||||
typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency {
|
||||
let token: any = null;
|
||||
let optional = false;
|
||||
|
||||
if (!Array.isArray(metadata)) {
|
||||
if (metadata instanceof Inject) {
|
||||
return _createDependency(metadata['token'], optional, null);
|
||||
} else {
|
||||
return _createDependency(metadata, optional, null);
|
||||
}
|
||||
}
|
||||
|
||||
let visibility: Self|SkipSelf = null;
|
||||
|
||||
for (let i = 0; i < metadata.length; ++i) {
|
||||
const paramMetadata = metadata[i];
|
||||
|
||||
if (paramMetadata instanceof Type) {
|
||||
token = paramMetadata;
|
||||
|
||||
} else if (paramMetadata instanceof Inject) {
|
||||
token = paramMetadata['token'];
|
||||
|
||||
} else if (paramMetadata instanceof Optional) {
|
||||
optional = true;
|
||||
|
||||
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
|
||||
visibility = paramMetadata;
|
||||
} else if (paramMetadata instanceof InjectionToken) {
|
||||
token = paramMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
token = resolveForwardRef(token);
|
||||
|
||||
if (token != null) {
|
||||
return _createDependency(token, optional, visibility);
|
||||
} else {
|
||||
throw noAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
}
|
||||
|
||||
function _createDependency(
|
||||
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency {
|
||||
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
|
||||
}
|
Reference in New Issue
Block a user