ts-ioc-container
Version:
Typescript IoC container
150 lines (149 loc) • 5.35 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Container = void 0;
const IContainer_1 = require("./IContainer");
const EmptyContainer_1 = require("./EmptyContainer");
const ContainerDisposedError_1 = require("../errors/ContainerDisposedError");
const MetadataInjector_1 = require("../injector/MetadataInjector");
const utils_1 = require("../utils");
const ProviderMap_1 = require("./ProviderMap");
const AliasMap_1 = require("./AliasMap");
class Container {
isDisposed = false;
parent;
scopes = new Set();
instances = new Set();
tags;
providerMap = new ProviderMap_1.ProviderMap();
aliasMap = new AliasMap_1.AliasMap();
registrations = new Set();
onConstruct;
onDispose;
injector;
constructor(options = {}) {
this.injector = options.injector ?? new MetadataInjector_1.MetadataInjector();
this.parent = options.parent ?? new EmptyContainer_1.EmptyContainer();
this.tags = new Set(options.tags ?? []);
this.onConstruct = options.onConstruct ?? (() => { });
this.onDispose = options.onDispose ?? (() => { });
}
register(key, provider, { aliases = [] } = {}) {
this.validateContainer();
this.providerMap.register(key, provider);
this.aliasMap.deleteKeyFromAliases(key);
this.aliasMap.addAliases(key, aliases);
return this;
}
addRegistration(registration) {
this.registrations.add(registration);
registration.applyTo(this);
return this;
}
getRegistrations() {
return [...this.parent.getRegistrations(), ...this.registrations];
}
resolveByClass(token, { args = [] } = {}) {
this.validateContainer();
const instance = this.injector.resolve(this, token, { args });
this.instances.add(instance);
this.onConstruct(instance, this);
return instance;
}
resolveOne(keyOrAlias, options) {
return (0, IContainer_1.DEFAULT_CONTAINER_RESOLVER)(this, keyOrAlias, options);
}
resolveOneByKey(keyOrAlias, { args = [], child = this, lazy } = {}) {
this.validateContainer();
const provider = this.providerMap.findOneByKey(keyOrAlias);
return provider?.hasAccess({ invocationScope: child, providerScope: this })
? provider.resolve(this, { args, lazy })
: this.parent.resolveOneByKey(keyOrAlias, { args, child, lazy });
}
resolveOneByAlias(keyOrAlias, { args = [], child = this, lazy } = {}) {
this.validateContainer();
const key = this.aliasMap.findLastKeyByAlias(keyOrAlias);
const provider = key !== undefined ? this.providerMap.findOneByKeyOrFail(key) : undefined;
return provider?.hasAccess({ invocationScope: child, providerScope: this })
? provider.resolve(this, { args, lazy })
: this.parent.resolveOneByAlias(keyOrAlias, { args, child, lazy });
}
resolveMany(alias, { args = [], child = this, lazy, excludedKeys = new Set() } = {}) {
this.validateContainer();
const keys = [];
const deps = [];
for (const key of this.aliasMap.findManyKeysByAlias(alias).filter(utils_1.Filter.exclude(excludedKeys))) {
const provider = this.providerMap.findOneByKeyOrFail(key);
if (!provider.hasAccess({ invocationScope: child, providerScope: this })) {
continue;
}
keys.push(key);
deps.push(provider.resolve(this, { args, lazy }));
}
const parentDeps = this.parent.resolveMany(alias, {
args,
child,
lazy,
excludedKeys: new Set([...excludedKeys, ...keys]),
});
return [...deps, ...parentDeps];
}
createScope({ tags = [] } = {}) {
this.validateContainer();
const scope = new Container({
injector: this.injector,
parent: this,
tags,
onDispose: this.onDispose,
onConstruct: this.onConstruct,
});
scope.applyRegistrationsFrom(this);
this.scopes.add(scope);
return scope;
}
getScopes() {
return [...this.scopes];
}
removeScope(child) {
this.scopes.delete(child);
}
useModule(module) {
module.applyTo(this);
return this;
}
getParent() {
return this.parent;
}
getInstances() {
return [...this.instances];
}
hasTag(tag) {
return this.tags.has(tag);
}
dispose() {
this.validateContainer();
this.isDisposed = true;
// Detach from parent
this.parent.removeScope(this);
this.parent = new EmptyContainer_1.EmptyContainer();
// Reset the state
this.providerMap.destroy();
this.aliasMap.destroy();
this.instances.clear();
this.registrations.clear();
this.onDispose(this);
}
/**
* @private
*/
applyRegistrationsFrom(source) {
for (const registration of source.getRegistrations()) {
registration.applyTo(this);
}
}
validateContainer() {
if (this.isDisposed) {
throw new ContainerDisposedError_1.ContainerDisposedError('Container is already disposed');
}
}
}
exports.Container = Container;