UNPKG

injection-js

Version:

Dependency Injection library for JavaScript and TypeScript

322 lines 10.6 kB
/** * @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 { setCurrentInjector } from './injector_compatibility'; import { Self, SkipSelf } from './metadata'; import { cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError } from './reflective_errors'; import { ReflectiveKey } from './reflective_key'; import { 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 class ReflectiveInjector { /** * 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) { 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, parent) { 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, parent) { // tslint:disable-next-line:no-use-before-declare return new ReflectiveInjector_(providers, parent); } } // tslint:disable-next-line:class-name export class ReflectiveInjector_ { /** * Private */ constructor(_providers, _parent) { /** @internal */ this._constructionCounter = 0; this._providers = _providers; this._parent = _parent || null; 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, notFoundValue = THROW_IF_NOT_FOUND) { return this._getByKey(ReflectiveKey.get(token), null, notFoundValue); } get parent() { return this._parent; } resolveAndCreateChild(providers) { const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); return this.createChildFromResolved(ResolvedReflectiveProviders); } createChildFromResolved(providers) { const inj = new ReflectiveInjector_(providers); inj._parent = this; return inj; } resolveAndInstantiate(provider) { return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]); } instantiateResolved(provider) { return this._instantiateProviderContext(provider); } getProviderAtIndex(index) { if (index < 0 || index >= this._providers.length) { throw outOfBoundsError(index); } return this._providers[index]; } /** @internal */ _new(provider) { if (this._constructionCounter++ > this._getMaxNumberOfObjects()) { throw cyclicDependencyError(this, provider.key); } return this._instantiateProviderContext(provider); } _getMaxNumberOfObjects() { return this.objs.length; } _instantiateProviderContext(provider) { const previousInjector = setCurrentInjector(this); try { return this._instantiateProvider(provider); } finally { setCurrentInjector(previousInjector); } } _instantiateProvider(provider) { 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]); } } _instantiate(provider, ResolvedReflectiveFactory) { const factory = ResolvedReflectiveFactory.factory; let deps; try { deps = ResolvedReflectiveFactory.dependencies.map((dep) => this._getByReflectiveDependency(dep)); } catch (e) { if (e.addKey) { e.addKey(this, provider.key); } throw e; } let obj; try { obj = factory(...deps); } catch (e) { throw instantiationError(this, e, e.stack, provider.key); } return obj; } _getByReflectiveDependency(dep) { return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND); } _getByKey(key, visibility, notFoundValue) { // tslint:disable-next-line:no-use-before-declare if (key === INJECTOR_KEY) { return this; } if (visibility instanceof Self) { return this._getByKeySelf(key, notFoundValue); } else { return this._getByKeyDefault(key, notFoundValue, visibility); } } _getObjByKeyId(keyId) { 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, notFoundValue) { if (notFoundValue !== THROW_IF_NOT_FOUND) { return notFoundValue; } else { throw noProviderError(this, key); } } /** @internal */ _getByKeySelf(key, notFoundValue) { const obj = this._getObjByKeyId(key.id); return obj !== UNDEFINED ? obj : this._throwOrNull(key, notFoundValue); } /** @internal */ _getByKeyDefault(key, notFoundValue, visibility) { let inj; if (visibility instanceof SkipSelf) { inj = this._parent; } else { inj = this; } while (inj instanceof ReflectiveInjector_) { const inj_ = 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() { const providers = _mapProviders(this, (b) => ' "' + b.key.displayName + '" ').join(', '); return `ReflectiveInjector(providers: [${providers}])`; } toString() { return this.displayName; } } const INJECTOR_KEY = ReflectiveKey.get(Injector); function _mapProviders(injector, fn) { const res = new Array(injector._providers.length); for (let i = 0; i < injector._providers.length; ++i) { res[i] = fn(injector.getProviderAtIndex(i)); } return res; } //# sourceMappingURL=reflective_injector.js.map