|
|
import { Container } from "./Container";
|
|
|
import { DescriptorBuilder } from "./DescriptorBuilder";
|
|
|
import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces";
|
|
|
import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
|
|
|
import { isDestroyable } from "./traits";
|
|
|
|
|
|
/**
|
|
|
* Container builder used to prepare service descriptors and create a IoC container
|
|
|
*/
|
|
|
export class ContainerBuilder<S, U extends keyof S> implements
|
|
|
IContainerBuilder<S, U> {
|
|
|
|
|
|
private _pending = 1;
|
|
|
|
|
|
private readonly _services: DescriptorMap<S>;
|
|
|
|
|
|
private readonly _lifetimeManager = new LifetimeManager();
|
|
|
|
|
|
private readonly _lifetime: ILifetime<IDestroyable>;
|
|
|
|
|
|
constructor(parentServices?: DescriptorMap<S>, lifetime?: ILifetime<IDestroyable>) {
|
|
|
this._services = Object.create(parentServices ? parentServices : null) as DescriptorMap<S>;
|
|
|
this._lifetimeManager = new LifetimeManager();
|
|
|
this._lifetime = lifetime ?? emptyLifetime();
|
|
|
}
|
|
|
createServiceBuilder<K extends U>(name: K):
|
|
|
IDescriptorBuilder<S, S[K], Record<never, never>, U> {
|
|
|
|
|
|
return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
|
|
|
|
|
|
}
|
|
|
|
|
|
build(): ServiceLocator<S> {
|
|
|
this._assertBuilding();
|
|
|
if (!this._complete())
|
|
|
throw new Error("The configuration didn't complete.");
|
|
|
|
|
|
const lifetime = this._lifetime;
|
|
|
|
|
|
const detach = isDestroyable(lifetime) ? () => lifetime.destroy() : () => void (0);
|
|
|
|
|
|
const container = new Container(this._services, this._lifetimeManager, detach);
|
|
|
lifetime.store(container);
|
|
|
|
|
|
return container;
|
|
|
}
|
|
|
|
|
|
private readonly _register = <K extends U>(name: K) =>
|
|
|
(descriptor: Descriptor<S, S[K]>) => {
|
|
|
this._complete();
|
|
|
this._services[name] = descriptor;
|
|
|
};
|
|
|
|
|
|
private readonly _fail = (ex: unknown) => {
|
|
|
throw ex;
|
|
|
};
|
|
|
|
|
|
private _assertBuilding() {
|
|
|
if (!this._pending)
|
|
|
throw new Error("The descriptor builder is finalized");
|
|
|
}
|
|
|
|
|
|
private _complete() {
|
|
|
return !(--this._pending);
|
|
|
}
|
|
|
|
|
|
}
|