@mikrokit/di
Version:
A lightweight TypeScript dependency injection container that uses only strip-tipes compliant methodologies and does not rely on reflect-metadata
241 lines (238 loc) • 7.53 kB
JavaScript
// src/utils.ts
var mergeDefaults = (defaults, overrides) => {
if (!overrides) {
return defaults;
}
return { ...defaults, ...overrides };
};
// src/module.ts
var _Module = class _Module {
constructor(moduleName, providers) {
this.moduleName = moduleName;
this.providers = providers ?? /* @__PURE__ */ new Map();
}
provide(token, factory, options) {
const normalizedOptions = mergeDefaults(
_Module.DEFAULT_PROVIDE_OPTIONS,
options
);
const normalizedToken = "token" in token ? token.token : token;
const normalizedFactory = typeof token === "function" && !factory ? token : factory;
if (normalizedToken._.group) {
this.provideGroupToken(
normalizedToken,
normalizedFactory
);
} else {
this.provideSingleToken(
normalizedToken,
normalizedFactory,
normalizedOptions
);
}
return this;
}
import(module) {
for (const token of module.providers.keys()) {
const ownProvider = this.providers.get(token);
const importedModuleProvider = module.providers.get(token);
if (!ownProvider) {
this.providers.set(token, importedModuleProvider);
continue;
}
if (ownProvider.group && importedModuleProvider.group) {
ownProvider.factories.push(...importedModuleProvider.factories);
continue;
}
if (ownProvider.group && !importedModuleProvider.group) {
throw new Error(
`Trying to re-provide group-type provider ${token.toString()} that was already provided as single-type provider`
);
}
throw new Error(
"Trying to re-provide single-type provider for " + token.toString()
);
}
return this;
}
provideGroupToken(token, factory) {
const existingProviderDefinition = this.providers.get(token);
if (existingProviderDefinition && existingProviderDefinition.group !== token._.group) {
throw new Error(
`Trying to re-provide group-type provider ${token.toString} that was already provided as single-type provider`
);
}
if (existingProviderDefinition) {
existingProviderDefinition.factories.push(factory);
return;
}
const providerDefinition = {
group: true,
factories: [factory]
};
this.providers.set(token, providerDefinition);
}
provideSingleToken(token, factory, options) {
if (this.providers.has(token)) {
const existingProvider = this.providers.get(token);
if (existingProvider.group !== token._.group) {
throw new Error(
`Trying to re-provide single-type provider ${token.toString()} as group-type provider`
);
}
if (!options.override) {
throw new Error(
"Trying to re-provide single-type provider for " + token.toString()
);
}
}
this.providers.set(token, {
group: false,
factory
});
}
};
_Module.DEFAULT_PROVIDE_OPTIONS = {
override: false
};
var Module = _Module;
var createModule = () => {
return new Module();
};
// src/container.ts
var ProvideScope = /* @__PURE__ */ ((ProvideScope2) => {
ProvideScope2["TRANSIENT"] = "transient";
ProvideScope2["SINGLETON"] = "singleton";
return ProvideScope2;
})(ProvideScope || {});
var Container = class _Container extends Module {
constructor(parentContainer, injectionItem) {
super(parentContainer?.moduleName, parentContainer?.providers);
this.instantiatedSingleSingletonProviders = parentContainer?.instantiatedSingleSingletonProviders ?? /* @__PURE__ */ new Map();
this.instantiatedGroupSingletonProviders = parentContainer?.instantiatedGroupSingletonProviders ?? /* @__PURE__ */ new Map();
this.injectionStack = injectionItem ? [...parentContainer?.injectionStack ?? [], injectionItem] : [];
}
static createEmpty() {
return new _Container();
}
async inject(token, scope = "singleton" /* SINGLETON */) {
const normalizedToken = "token" in token ? token.token : token;
if (this.injectionStack.includes(normalizedToken)) {
throw new Error(
`Provider ${normalizedToken.toString()} is already being instantiated. This error can be caused by either a circular dependency or not awaiting the inject calls`
);
}
if (normalizedToken._.group) {
const result2 = await this.injectGroup(
normalizedToken,
scope
);
return result2;
}
const result = await this.injectSingle(
normalizedToken,
scope
);
return result;
}
async injectSingle(token, scope) {
if (scope === "singleton" /* SINGLETON */) {
const instantiatedProvider = this.instantiatedSingleSingletonProviders.get(token);
if (instantiatedProvider) {
return instantiatedProvider;
}
}
const definition = this.providers.get(token);
if (!definition) {
throw new Error(
"No factory available for the specified token " + token.toString()
);
}
if (token._.group !== definition.group) {
throw new Error(
`Trying to inject group-type provider ${token.toString()} as single-type provider`
);
}
const result = await definition.factory(new _Container(this, token));
if (scope === "singleton" /* SINGLETON */) {
this.instantiatedSingleSingletonProviders.set(token, result);
}
return result;
}
async injectGroup(token, scope) {
if (scope === "singleton" /* SINGLETON */) {
const instantiatedProviders = this.instantiatedGroupSingletonProviders.get(token);
if (instantiatedProviders) {
return instantiatedProviders;
}
}
const definition = this.providers.get(token);
if (!definition) {
return [];
}
if (token._.group !== definition.group) {
throw new Error(
`Trying to inject single-type provider ${token.toString()} as group-type provider`
);
}
const result = [];
for (const factory of definition.factories) {
result.push(await factory(new _Container(this, token)));
}
if (scope === "singleton" /* SINGLETON */) {
this.instantiatedGroupSingletonProviders.set(token, result);
}
return result;
}
};
var createContainer = () => {
return Container.createEmpty();
};
// src/helpers.ts
var createProviderToken = (_factory, name) => {
const symbol = Symbol(name);
const metadata = {
provided: void 0,
group: false
};
return Object.assign(symbol, { _: metadata });
};
var createGroupProviderToken = (name) => {
const symbol = Symbol(name);
const metadata = {
provided: void 0,
group: true
};
return Object.assign(symbol, { _: metadata });
};
var defineProviderFactory = (factory) => factory;
var defineStaticProviderFactory = (staticValue) => () => staticValue;
var attachProviderToken = (factory, token) => {
return Object.assign(factory, {
token
});
};
function defineStaticProvider(staticValue, token) {
return defineProvider(defineStaticProviderFactory(staticValue), token);
}
function defineProvider(factory, tokenOrName) {
const token = typeof tokenOrName === "string" || !tokenOrName ? createProviderToken(factory, tokenOrName) : tokenOrName;
return Object.assign(factory, {
token
});
}
export {
Container,
Module,
ProvideScope,
attachProviderToken,
createContainer,
createGroupProviderToken,
createModule,
createProviderToken,
defineProvider,
defineProviderFactory,
defineStaticProvider,
defineStaticProviderFactory
};
//# sourceMappingURL=index.js.map