@launchtray/tsyringe-async
Version:
Lightweight dependency injection container for JavaScript/TypeScript, with asynchronous resolution
397 lines (396 loc) • 18.1 kB
JavaScript
import { __awaiter, __generator, __read, __spread, __values } from "tslib";
import { isClassProvider, isFactoryProvider, isNormalToken, isTokenProvider, isValueProvider } from "./providers";
import { isProvider } from "./providers/provider";
import { isTokenDescriptor } from "./providers/injection-token";
import Registry from "./registry";
import Lifecycle from "./types/lifecycle";
import ResolutionContext from "./resolution-context";
import { formatErrorCtor } from "./error-helpers";
import { callInitializers } from "./decorators/initializer";
export var typeInfo = new Map();
var InternalDependencyContainer = (function () {
function InternalDependencyContainer(parent) {
this.parent = parent;
this._registry = new Registry();
}
InternalDependencyContainer.prototype.register = function (token, providerOrConstructor, options) {
if (options === void 0) { options = { lifecycle: Lifecycle.Transient }; }
var provider;
if (!isProvider(providerOrConstructor)) {
provider = { useClass: providerOrConstructor };
}
else {
provider = providerOrConstructor;
}
if (options.lifecycle === Lifecycle.Singleton ||
options.lifecycle == Lifecycle.ContainerScoped ||
options.lifecycle == Lifecycle.ResolutionScoped) {
if (isValueProvider(provider) || isFactoryProvider(provider)) {
throw new Error("Cannot use lifecycle \"" + Lifecycle[options.lifecycle] + "\" with ValueProviders or FactoryProviders");
}
}
this._registry.set(token, { provider: provider, options: options });
return this;
};
InternalDependencyContainer.prototype.registerType = function (from, to) {
if (isNormalToken(to)) {
return this.register(from, {
useToken: to
});
}
return this.register(from, {
useClass: to
});
};
InternalDependencyContainer.prototype.registerInstance = function (token, instance) {
return this.register(token, {
useValue: instance
});
};
InternalDependencyContainer.prototype.registerSingleton = function (from, to) {
if (isNormalToken(from)) {
if (isNormalToken(to)) {
return this.register(from, {
useToken: to
}, { lifecycle: Lifecycle.Singleton });
}
else if (to) {
return this.register(from, {
useClass: to
}, { lifecycle: Lifecycle.Singleton });
}
throw new Error('Cannot register a type name as a singleton without a "to" token');
}
var useClass = from;
if (to && !isNormalToken(to)) {
useClass = to;
}
return this.register(from, {
useClass: useClass
}, { lifecycle: Lifecycle.Singleton });
};
InternalDependencyContainer.prototype.resolve = function (token, context) {
if (context === void 0) { context = new ResolutionContext(); }
return __awaiter(this, void 0, void 0, function () {
var registration, resolved;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
registration = this.getRegistration(token);
if (!registration && isNormalToken(token)) {
throw new Error("Attempted to resolve unregistered dependency token: \"" + token.toString() + "\"");
}
if (!registration) return [3, 2];
return [4, this.resolveRegistration(registration, context)];
case 1: return [2, _a.sent()];
case 2: return [4, this.construct(token, context)];
case 3:
resolved = _a.sent();
return [4, callInitializers(this, resolved)];
case 4:
_a.sent();
return [2, resolved];
}
});
});
};
InternalDependencyContainer.prototype.resolveRegistration = function (registration, context) {
if (registration.options.lifecycle === Lifecycle.ResolutionScoped &&
context.scopedResolutions.has(registration)) {
return context.scopedResolutions.get(registration);
}
var resolutionPromise = this.resolveRegistrationHelper(registration, context);
if (registration.options.lifecycle === Lifecycle.ResolutionScoped) {
context.scopedResolutions.set(registration, resolutionPromise);
}
return resolutionPromise;
};
InternalDependencyContainer.prototype.resolveRegistrationHelper = function (registration, context) {
return __awaiter(this, void 0, void 0, function () {
var isSingleton, isContainerScoped, returnInstance, resolved, _a, _b, _c, _d, _e, _f;
return __generator(this, function (_g) {
switch (_g.label) {
case 0:
isSingleton = registration.options.lifecycle === Lifecycle.Singleton;
isContainerScoped = registration.options.lifecycle === Lifecycle.ContainerScoped;
returnInstance = isSingleton || isContainerScoped;
if (!isValueProvider(registration.provider)) return [3, 1];
resolved = registration.provider.useValue;
return [3, 17];
case 1:
if (!isTokenProvider(registration.provider)) return [3, 7];
if (!returnInstance) return [3, 4];
_b = registration.instance;
if (_b) return [3, 3];
_c = registration;
return [4, this.resolve(registration.provider.useToken, context)];
case 2:
_b = (_c.instance = _g.sent());
_g.label = 3;
case 3:
_a = _b;
return [3, 6];
case 4: return [4, this.resolve(registration.provider.useToken, context)];
case 5:
_a = _g.sent();
_g.label = 6;
case 6:
resolved = _a;
return [3, 17];
case 7:
if (!isClassProvider(registration.provider)) return [3, 13];
if (!returnInstance) return [3, 10];
_e = registration.instance;
if (_e) return [3, 9];
_f = registration;
return [4, this.construct(registration.provider.useClass, context)];
case 8:
_e = (_f.instance = _g.sent());
_g.label = 9;
case 9:
_d = _e;
return [3, 12];
case 10: return [4, this.construct(registration.provider.useClass, context)];
case 11:
_d = _g.sent();
_g.label = 12;
case 12:
resolved = _d;
return [3, 17];
case 13:
if (!isFactoryProvider(registration.provider)) return [3, 15];
return [4, registration.provider.useFactory(this)];
case 14:
resolved = _g.sent();
return [3, 17];
case 15: return [4, this.construct(registration.provider, context)];
case 16:
resolved = _g.sent();
_g.label = 17;
case 17: return [4, callInitializers(this, resolved)];
case 18:
_g.sent();
return [2, resolved];
}
});
});
};
InternalDependencyContainer.prototype.resolveAll = function (token, context) {
if (context === void 0) { context = new ResolutionContext(); }
return __awaiter(this, void 0, void 0, function () {
var registrations, instances, registrations_1, registrations_1_1, item, _a, _b, e_1_1, resolved;
var e_1, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
registrations = this.getAllRegistrations(token);
if (!registrations && isNormalToken(token)) {
registrations = [];
}
if (!registrations) return [3, 9];
instances = [];
_d.label = 1;
case 1:
_d.trys.push([1, 6, 7, 8]);
registrations_1 = __values(registrations), registrations_1_1 = registrations_1.next();
_d.label = 2;
case 2:
if (!!registrations_1_1.done) return [3, 5];
item = registrations_1_1.value;
_b = (_a = instances).push;
return [4, this.resolveRegistration(item, context)];
case 3:
_b.apply(_a, [_d.sent()]);
_d.label = 4;
case 4:
registrations_1_1 = registrations_1.next();
return [3, 2];
case 5: return [3, 8];
case 6:
e_1_1 = _d.sent();
e_1 = { error: e_1_1 };
return [3, 8];
case 7:
try {
if (registrations_1_1 && !registrations_1_1.done && (_c = registrations_1.return)) _c.call(registrations_1);
}
finally { if (e_1) throw e_1.error; }
return [7];
case 8: return [2, instances];
case 9: return [4, this.construct(token, context)];
case 10:
resolved = _d.sent();
return [4, callInitializers(this, resolved)];
case 11:
_d.sent();
return [2, [resolved]];
}
});
});
};
InternalDependencyContainer.prototype.isRegistered = function (token, recursive) {
if (recursive === void 0) { recursive = false; }
return (this._registry.has(token) ||
(recursive &&
(this.parent || false) &&
this.parent.isRegistered(token, true)));
};
InternalDependencyContainer.prototype.reset = function () {
this._registry.clear();
};
InternalDependencyContainer.prototype.clearInstances = function () {
var e_2, _a;
try {
for (var _b = __values(this._registry.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), token = _d[0], registrations = _d[1];
this._registry.setAll(token, registrations
.filter(function (registration) { return !isValueProvider(registration.provider); })
.map(function (registration) {
registration.instance = undefined;
return registration;
}));
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
};
InternalDependencyContainer.prototype.createChildContainer = function () {
var e_3, _a;
var childContainer = new InternalDependencyContainer(this);
try {
for (var _b = __values(this._registry.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read(_c.value, 2), token = _d[0], registrations = _d[1];
if (registrations.some(function (_a) {
var options = _a.options;
return options.lifecycle === Lifecycle.ContainerScoped;
})) {
childContainer._registry.setAll(token, registrations.map(function (registration) {
if (registration.options.lifecycle === Lifecycle.ContainerScoped) {
return {
provider: registration.provider,
options: registration.options
};
}
return registration;
}));
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_3) throw e_3.error; }
}
return childContainer;
};
InternalDependencyContainer.prototype.getRegistration = function (token) {
if (this.isRegistered(token)) {
return this._registry.get(token);
}
if (this.parent) {
return this.parent.getRegistration(token);
}
return null;
};
InternalDependencyContainer.prototype.getAllRegistrations = function (token) {
if (this.isRegistered(token)) {
return this._registry.getAll(token);
}
if (this.parent) {
return this.parent.getAllRegistrations(token);
}
return null;
};
InternalDependencyContainer.prototype.construct = function (ctor, context) {
return __awaiter(this, void 0, void 0, function () {
var paramInfo, idx, params, paramInfo_1, paramInfo_1_1, param, resolver, _a, _b, e_4_1;
var e_4, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (typeof ctor === "undefined") {
throw new Error("Attempted to construct an undefined constructor. Could mean a circular dependency problem.");
}
if (ctor.length === 0) {
return [2, new ctor()];
}
paramInfo = typeInfo.get(ctor);
if (!paramInfo || paramInfo.length === 0) {
throw new Error("TypeInfo not known for \"" + ctor.name + "\"");
}
idx = 0;
params = [];
_d.label = 1;
case 1:
_d.trys.push([1, 6, 7, 8]);
paramInfo_1 = __values(paramInfo), paramInfo_1_1 = paramInfo_1.next();
_d.label = 2;
case 2:
if (!!paramInfo_1_1.done) return [3, 5];
param = paramInfo_1_1.value;
resolver = this.resolveParams(context, ctor);
_b = (_a = params).push;
return [4, resolver(param, idx)];
case 3:
_b.apply(_a, [_d.sent()]);
idx++;
_d.label = 4;
case 4:
paramInfo_1_1 = paramInfo_1.next();
return [3, 2];
case 5: return [3, 8];
case 6:
e_4_1 = _d.sent();
e_4 = { error: e_4_1 };
return [3, 8];
case 7:
try {
if (paramInfo_1_1 && !paramInfo_1_1.done && (_c = paramInfo_1.return)) _c.call(paramInfo_1);
}
finally { if (e_4) throw e_4.error; }
return [7];
case 8: return [2, new (ctor.bind.apply(ctor, __spread([void 0], params)))()];
}
});
});
};
InternalDependencyContainer.prototype.resolveParams = function (context, ctor) {
var _this = this;
return function (param, idx) { return __awaiter(_this, void 0, void 0, function () {
var _a, e_5;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_b.trys.push([0, 7, , 8]);
if (!isTokenDescriptor(param)) return [3, 5];
if (!param.multiple) return [3, 2];
return [4, this.resolveAll(param.token)];
case 1:
_a = _b.sent();
return [3, 4];
case 2: return [4, this.resolve(param.token, context)];
case 3:
_a = _b.sent();
_b.label = 4;
case 4: return [2, _a];
case 5: return [4, this.resolve(param, context)];
case 6: return [2, _b.sent()];
case 7:
e_5 = _b.sent();
throw new Error(formatErrorCtor(ctor, idx, e_5));
case 8: return [2];
}
});
}); };
};
return InternalDependencyContainer;
}());
export var instance = new InternalDependencyContainer();
export default instance;