UNPKG

di-tory

Version:

Compose applications with dependency injection

87 lines (86 loc) 3.38 kB
import { Stack } from './stack/Stack.js'; import { DependencyResolutionError as DRError, DependencyResolutionErrorCode as DRErrType, } from './DependencyResolutionError.js'; import { getStore } from './async-scope.js'; import { propertyKeys } from './objects.js'; import { normalizeScope, overrideScope } from './scope.js'; const singletonInstances = new Map(); export const createModule = (privateResolvers, publicResolvers, initializers, params = {}) => { const resolvers = { ...privateResolvers, ...publicResolvers }; const resolutionStack = new Stack(); const moduleInstances = new Map(); const publicNames = new Set(propertyKeys(publicResolvers)); const self = new Proxy(Object.create(null), { get(_, prop) { return resolve(prop); }, }); const ensureAsyncInstances = () => { const store = 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 = 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 = overrideScope(parent.scope, scope); if (newParentScope != null) parent.scope = newParentScope; }; const resolve = (item) => { if (resolutionStack.length === 0) { transientInstances = new Map(); } const resolver = resolvers[item]; if (resolver == null) throw new DRError(DRErrType.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 { throw new DRError(DRErrType.CircularDependencyFailure, resolutionStack.toStringArray(), String(item)); } let instance; try { instance = resolver(self, params); } catch (err) { if (err instanceof DRError) throw err; throw new DRError(DRErrType.InstantiationFailure, currentStack, String(item), err); } resolutionStack.pop(); updateParentScope(resolver.scope); getInstances(resolver).set(item, instance); if (resolutionStack.length === 0) { initializers?.[item]?.call(instance, self, params); } return instance; }; return new Proxy(Object.create(null), { get(_, prop) { if (publicNames.has(prop)) return resolve(prop); throw new DRError(DRErrType.PrivateMemberAccessFailure, [], String(prop)); }, }); };