import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces"; import { each, key } from "./traits"; export interface DescriptorImplArgs { readonly lifetime: ILifetime>; readonly factory: (refs: Record) => NonNullable; readonly cleanup?: (item: NonNullable) => void; readonly overrides?: DescriptorMap; readonly dependencies?: DepsMap; } export const containerSelfDescriptor = () => Object.freeze({ level: 0, activate(context: IActivationContext) { return context.createChildContainer(); } }); export class DescriptorImpl implements Descriptor { private readonly _overrides?: DescriptorMap; private readonly _lifetime: ILifetime>; private readonly _factory: (refs: Record) => NonNullable; private readonly _cleanup?: (item: NonNullable) => void; private readonly _deps?: DepsMap; readonly hasOverrides: boolean; constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs) { this._lifetime = lifetime; this._factory = factory; if (cleanup) this._cleanup = cleanup; if (overrides) this._overrides = overrides; if (dependencies) this._deps = dependencies; this.hasOverrides = !!overrides; } activate(context: IActivationContext): NonNullable { const { has, get, initialize, store } = this._lifetime(context); if (has()) return get(); initialize(); if (this._overrides) each(this._overrides, (v, k) => context.register(k, v)); const resolve = ({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => { if (lazy) { return "default" in opts ? () => context.resolve(name, opts.default) : () => context.resolve(name); } else { return "default" in opts ? context.resolve(name, opts.default) : context.resolve(name); } }; const deps = this._deps; const refs = deps ? Object.keys(deps) .map(k => { const ref = deps[k]; return typeof ref !== "object" ? { [k]: resolve({ name: ref }) } : { [k]: resolve(ref) }; }) .reduce((a, p) => ({ ...a, ...p }), {}) : {}; try { // call the factory method const instance = (0, this._factory)(refs); // store the instance store(instance, this._cleanup); return instance; } catch (err) { context.fail(err); } } toString() { return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`; } }