injectx
Version:
A dependency injection package for javascript/typescript functional programming
147 lines (146 loc) • 5.23 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SetDefaultContainer = exports.InjectIn = exports.GetContainer = exports.Container = exports.SupportedInjectableService = void 0;
require("reflect-metadata");
var SupportedInjectableService;
(function (SupportedInjectableService) {
SupportedInjectableService["function"] = "function";
SupportedInjectableService["class"] = "class";
SupportedInjectableService["object"] = "object";
SupportedInjectableService["other"] = "other";
})(SupportedInjectableService = exports.SupportedInjectableService || (exports.SupportedInjectableService = {}));
function isClass(v) {
const result = typeof v === 'function' && /^\s*class\s+/.test(v.toString());
return result;
}
function getInjectableType(param) {
if (typeof param === 'function' && !isClass(param)) {
return SupportedInjectableService.function;
}
else if (typeof param === 'function' &&
param !== null &&
param.constructor &&
param.constructor.name === 'Function' &&
isClass(param)) {
return SupportedInjectableService.class;
}
else if (typeof param === 'object' && param !== null) {
return SupportedInjectableService.object;
}
else {
return SupportedInjectableService.other;
}
}
var Scope;
(function (Scope) {
Scope["singleton"] = "singleton";
Scope["scoped"] = "scoped";
})(Scope || (Scope = {}));
class Container {
constructor(name) {
this.dependencies = new Map();
this.name = name.toString();
}
Bind(service, options = {}) {
if (service === undefined) {
return this;
}
const key = options.name || service?.name;
const injectableType = getInjectableType(service);
const settings = {
name: key,
service,
scope: options.scope || Scope.singleton,
type: injectableType,
};
this.dependencies.set(key, settings);
return this;
}
resolve(name) {
const service = this.dependencies.get(name);
if (!service) {
return null;
}
let instance;
if (service.type === SupportedInjectableService.class &&
service.scope === Scope.singleton &&
!service.instance) {
const dependencies = (service.service.constructor.prototype.constructorDependencies || []).map((name) => this.resolve(name));
if (dependencies?.length > 0) {
instance = new service.service(...dependencies);
}
else {
instance = new service.service();
}
const propDependencies = service.service.constructor.prototype.propDependencies || new Map();
propDependencies.forEach((value, key) => {
instance[key] = this.resolve(value);
});
service.instance = instance;
}
else {
instance = service.service;
}
return instance;
}
resolveAll() {
const dependencies = {};
this.dependencies.forEach((value, key) => {
dependencies[key.toString()] = this.resolve(key);
});
return dependencies;
}
}
exports.Container = Container;
function resolveDependencies(containerNames, args) {
const diContainers = containerNames.map((containerName) => GetContainer(containerName));
const dependencies = diContainers.length === 1
? diContainers[0].resolveAll()
: diContainers.reduce((acc, container) => {
acc[container.name] = container.resolveAll();
return acc;
}, {});
const mergedArgs = [{ ...dependencies }, ...args];
return mergedArgs;
}
class InjectX {
}
InjectX.containers = new Map();
InjectX.defaultContainerName = 'default';
function GetContainer(container = InjectX.defaultContainerName) {
if (!InjectX.containers.has(container.toString())) {
InjectX.containers.set(container, new Container(container.toString()));
}
return InjectX.containers.get(container);
}
exports.GetContainer = GetContainer;
function InjectIn(callback, options) {
const serializedCallbackName = callback.name
?.replace?.('_', '')
?.split('')
.map?.((char, i) => (i === 0 ? char.toLowerCase() : char))
?.join('') || callback.name;
const callbackName = options?.callbackName || serializedCallbackName;
const containerNames = options?.containers || [];
if (containerNames.length < 1) {
containerNames.push(InjectX.defaultContainerName);
}
const wrappedCallback = (...args) => {
const mergedArgs = resolveDependencies(containerNames, args);
const result = callback(...mergedArgs);
if (typeof result === 'function') {
return result(...args);
}
return result;
};
Object.defineProperty(wrappedCallback, 'name', {
value: callbackName,
writable: false,
});
return wrappedCallback;
}
exports.InjectIn = InjectIn;
function SetDefaultContainer(name) {
InjectX.defaultContainerName = name.toString();
}
exports.SetDefaultContainer = SetDefaultContainer;