import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces"; import { each, key } from "./traits"; export interface DescriptorImplArgs { lifetime: ILifetime; factory: (refs: Record) => NonNullable; cleanup?: (item: NonNullable) => void; overrides?: DescriptorMap; dependencies?: DepsMap; } 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 { if (this._lifetime.has()) return this._lifetime.get(); this._lifetime.initialize(context); 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 makeRefs = (deps: typeof this._deps) => 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 }), {} ): {}; const instance = this._factory.call(undefined, makeRefs(this._deps)); this._lifetime.store(instance, this._cleanup); return instance; } toString() { return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`; } }