|
|
import { ActivationContext } from "./ActivationContext";
|
|
|
import { ContainerBuilder } from "./ContainerBuilder";
|
|
|
import { LifetimeManager, emptySlot } from "./LifetimeManager";
|
|
|
import { DescriptorMap, IContainerBuilder, IDestroyable, ILifetimeManager, ILifetimeSlot, ServiceLocator } from "../typings/interfaces";
|
|
|
|
|
|
let nextId = 1;
|
|
|
|
|
|
export class Container<S> implements ServiceLocator<S>, IDestroyable {
|
|
|
private readonly _services: DescriptorMap<S>;
|
|
|
|
|
|
private readonly _scope: ILifetimeManager[];
|
|
|
|
|
|
private readonly _containerId = `container-${nextId++}`;
|
|
|
|
|
|
private readonly _slot: ILifetimeSlot<this>;
|
|
|
|
|
|
private _disposed: boolean;
|
|
|
|
|
|
constructor(services: DescriptorMap<S>, parentScope: ILifetimeManager[]) {
|
|
|
this._services = services;
|
|
|
this._disposed = false;
|
|
|
this._scope = parentScope.concat(new LifetimeManager());
|
|
|
|
|
|
// If this container is created inside the parent container scope,
|
|
|
// allocated lifetime slot
|
|
|
this._slot = parentScope.length ?
|
|
|
parentScope[parentScope.length - 1].slot(this._containerId) :
|
|
|
emptySlot<this>();
|
|
|
|
|
|
// store the container reference in the lifetime slot
|
|
|
this._slot.store(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
private _assertNotDestroyed() {
|
|
|
if (this._disposed)
|
|
|
throw new Error("The container is destroyed");
|
|
|
}
|
|
|
|
|
|
createChildBuilder(): IContainerBuilder<S, keyof S> {
|
|
|
this._assertNotDestroyed();
|
|
|
|
|
|
return new ContainerBuilder(this._services, this._scope);
|
|
|
}
|
|
|
|
|
|
resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
|
|
|
resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
|
|
|
resolve<K extends keyof S, T>(name: K, def?: T) {
|
|
|
this._assertNotDestroyed();
|
|
|
|
|
|
const context = new ActivationContext(this, this._scope, this._services);
|
|
|
|
|
|
return arguments.length === 1 ?
|
|
|
context.resolve(name, []) :
|
|
|
context.resolve(name, [], def);
|
|
|
}
|
|
|
|
|
|
destroy() {
|
|
|
if (this._disposed)
|
|
|
return;
|
|
|
this._disposed = true;
|
|
|
|
|
|
// destroy own scope
|
|
|
this._scope[this._scope.length - 1].destroy();
|
|
|
|
|
|
// release lifetime slot if the container is destroyed before the parent
|
|
|
// container. If this container is destroyed during the parent container
|
|
|
// cleanup procedure this call will have no effect.
|
|
|
this._slot.remove();
|
|
|
}
|
|
|
}
|
|
|
|