di-tory
Version:
Compose applications with dependency injection
92 lines (91 loc) • 4.15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createModule = void 0;
const Stack_js_1 = require("./stack/Stack.js");
const DependencyResolutionError_js_1 = require("./DependencyResolutionError.js");
const async_scope_js_1 = require("./async-scope.js");
const objects_js_1 = require("./objects.js");
const scope_js_1 = require("./scope.js");
const singletonInstances = new Map();
const createModule = (privateResolvers, publicResolvers, initializers, params = {}) => {
const resolvers = Object.assign(Object.assign({}, privateResolvers), publicResolvers);
const resolutionStack = new Stack_js_1.Stack();
const moduleInstances = new Map();
const publicNames = new Set((0, objects_js_1.propertyKeys)(publicResolvers));
const self = new Proxy(Object.create(null), {
get(_, prop) {
return resolve(prop);
},
});
const ensureAsyncInstances = () => {
const store = (0, async_scope_js_1.getStore)();
const asyncInstances = store.get(self);
if (asyncInstances != null)
return asyncInstances;
const newInstances = new Map();
store.set(self, newInstances);
return newInstances;
};
let transientInstances;
const getInstances = ({ scope }) => {
const normalizedScope = (0, scope_js_1.normalizeScope)(scope);
return (
// prettier-ignore
normalizedScope === 'async' ? ensureAsyncInstances() :
normalizedScope === 'singleton' ? singletonInstances :
normalizedScope === 'transient' ? transientInstances :
moduleInstances);
};
const updateParentScope = (scope) => {
const parentItem = resolutionStack.peek();
if (parentItem == null)
return;
const parent = resolvers[parentItem];
const newParentScope = (0, scope_js_1.overrideScope)(parent.scope, scope);
if (newParentScope != null)
parent.scope = newParentScope;
};
const resolve = (item) => {
var _a;
if (resolutionStack.length === 0) {
transientInstances = new Map();
}
const resolver = resolvers[item];
if (resolver == null)
throw new DependencyResolutionError_js_1.DependencyResolutionError(DependencyResolutionError_js_1.DependencyResolutionErrorCode.ResolverIsNotDefined, resolutionStack.toStringArray(), String(item));
const instances = getInstances(resolver);
if (instances.has(item))
return instances.get(item);
const currentStack = resolutionStack.toStringArray();
try {
resolutionStack.push(item);
}
catch (_b) {
throw new DependencyResolutionError_js_1.DependencyResolutionError(DependencyResolutionError_js_1.DependencyResolutionErrorCode.CircularDependencyFailure, resolutionStack.toStringArray(), String(item));
}
let instance;
try {
instance = resolver(self, params);
}
catch (err) {
if (err instanceof DependencyResolutionError_js_1.DependencyResolutionError)
throw err;
throw new DependencyResolutionError_js_1.DependencyResolutionError(DependencyResolutionError_js_1.DependencyResolutionErrorCode.InstantiationFailure, currentStack, String(item), err);
}
resolutionStack.pop();
updateParentScope(resolver.scope);
getInstances(resolver).set(item, instance);
if (resolutionStack.length === 0) {
(_a = initializers === null || initializers === void 0 ? void 0 : initializers[item]) === null || _a === void 0 ? void 0 : _a.call(instance, self, params);
}
return instance;
};
return new Proxy(Object.create(null), {
get(_, prop) {
if (publicNames.has(prop))
return resolve(prop);
throw new DependencyResolutionError_js_1.DependencyResolutionError(DependencyResolutionError_js_1.DependencyResolutionErrorCode.PrivateMemberAccessFailure, [], String(prop));
},
});
};
exports.createModule = createModule;