injection-js
Version:
Dependency Injection library for JavaScript and TypeScript
328 lines • 11.7 kB
JavaScript
/**
* @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 { cyclicDependencyError, instantiationError, noProviderError, outOfBoundsError } from './reflective_errors';
import { ReflectiveKey } from './reflective_key';
import { resolveReflectiveProviders, } from './reflective_provider';
// Threshold for the dynamic version
var 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
*/
var ReflectiveInjector = /** @class */ (function () {
function 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.
*/
ReflectiveInjector.resolve = function (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}.
*/
ReflectiveInjector.resolveAndCreate = function (providers, parent) {
var 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
*/
ReflectiveInjector.fromResolvedProviders = function (providers, parent) {
// tslint:disable-next-line:no-use-before-declare
return new ReflectiveInjector_(providers, parent);
};
return ReflectiveInjector;
}());
export { ReflectiveInjector };
// tslint:disable-next-line:class-name
var ReflectiveInjector_ = /** @class */ (function () {
/**
* Private
*/
function ReflectiveInjector_(_providers, _parent) {
/** @internal */
this._constructionCounter = 0;
this._providers = _providers;
this._parent = _parent || null;
var len = _providers.length;
this.keyIds = new Array(len);
this.objs = new Array(len);
for (var i = 0; i < len; i++) {
this.keyIds[i] = _providers[i].key.id;
this.objs[i] = UNDEFINED;
}
}
ReflectiveInjector_.prototype.get = function (token, notFoundValue) {
if (notFoundValue === void 0) { notFoundValue = THROW_IF_NOT_FOUND; }
return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
};
Object.defineProperty(ReflectiveInjector_.prototype, "parent", {
get: function () {
return this._parent;
},
enumerable: false,
configurable: true
});
ReflectiveInjector_.prototype.resolveAndCreateChild = function (providers) {
var ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return this.createChildFromResolved(ResolvedReflectiveProviders);
};
ReflectiveInjector_.prototype.createChildFromResolved = function (providers) {
var inj = new ReflectiveInjector_(providers);
inj._parent = this;
return inj;
};
ReflectiveInjector_.prototype.resolveAndInstantiate = function (provider) {
return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
};
ReflectiveInjector_.prototype.instantiateResolved = function (provider) {
return this._instantiateProvider(provider);
};
ReflectiveInjector_.prototype.getProviderAtIndex = function (index) {
if (index < 0 || index >= this._providers.length) {
throw outOfBoundsError(index);
}
return this._providers[index];
};
/** @internal */
ReflectiveInjector_.prototype._new = function (provider) {
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
throw cyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
};
ReflectiveInjector_.prototype._getMaxNumberOfObjects = function () {
return this.objs.length;
};
ReflectiveInjector_.prototype._instantiateProvider = function (provider) {
if (provider.multiProvider) {
var res = new Array(provider.resolvedFactories.length);
for (var 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]);
}
};
ReflectiveInjector_.prototype._instantiate = function (provider, ResolvedReflectiveFactory) {
var _this = this;
var factory = ResolvedReflectiveFactory.factory;
var deps;
try {
deps = ResolvedReflectiveFactory.dependencies.map(function (dep) { return _this._getByReflectiveDependency(dep); });
}
catch (e) {
if (e.addKey) {
e.addKey(this, provider.key);
}
throw e;
}
var obj;
try {
obj = factory.apply(void 0, deps);
}
catch (e) {
throw instantiationError(this, e, e.stack, provider.key);
}
return obj;
};
ReflectiveInjector_.prototype._getByReflectiveDependency = function (dep) {
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
};
ReflectiveInjector_.prototype._getByKey = function (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);
}
};
ReflectiveInjector_.prototype._getObjByKeyId = function (keyId) {
for (var 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 */
ReflectiveInjector_.prototype._throwOrNull = function (key, notFoundValue) {
if (notFoundValue !== THROW_IF_NOT_FOUND) {
return notFoundValue;
}
else {
throw noProviderError(this, key);
}
};
/** @internal */
ReflectiveInjector_.prototype._getByKeySelf = function (key, notFoundValue) {
var obj = this._getObjByKeyId(key.id);
return obj !== UNDEFINED ? obj : this._throwOrNull(key, notFoundValue);
};
/** @internal */
ReflectiveInjector_.prototype._getByKeyDefault = function (key, notFoundValue, visibility) {
var inj;
if (visibility instanceof SkipSelf) {
inj = this._parent;
}
else {
inj = this;
}
while (inj instanceof ReflectiveInjector_) {
var inj_ = inj;
var 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);
}
};
Object.defineProperty(ReflectiveInjector_.prototype, "displayName", {
get: function () {
var providers = _mapProviders(this, function (b) { return ' "' + b.key.displayName + '" '; }).join(', ');
return "ReflectiveInjector(providers: [" + providers + "])";
},
enumerable: false,
configurable: true
});
ReflectiveInjector_.prototype.toString = function () {
return this.displayName;
};
return ReflectiveInjector_;
}());
export { ReflectiveInjector_ };
var INJECTOR_KEY = ReflectiveKey.get(Injector);
function _mapProviders(injector, fn) {
var res = new Array(injector._providers.length);
for (var i = 0; i < injector._providers.length; ++i) {
res[i] = fn(injector.getProviderAtIndex(i));
}
return res;
}
//# sourceMappingURL=reflective_injector.js.map