70 lines
2.1 KiB
TypeScript
70 lines
2.1 KiB
TypeScript
type Provider<T> = (manager: DependencyManager) => Promise<T> | T;
|
|
|
|
type ClassType<T> = new () => T;
|
|
|
|
// deno-lint-ignore no-explicit-any
|
|
type ModuleIdentifier = any;
|
|
|
|
type Modules = Map<ModuleIdentifier, {
|
|
// deno-lint-ignore no-explicit-any
|
|
provider: Provider<any>;
|
|
// deno-lint-ignore no-explicit-any
|
|
instance?: any;
|
|
}>;
|
|
|
|
export class DependencyManager {
|
|
private modules: Modules = new Map();
|
|
private resolving: Set<ModuleIdentifier> = new Set();
|
|
|
|
/**
|
|
* Set the given provider to be used to resolve the dependency identified by the given identifier.
|
|
*
|
|
* Throw an error if the identifier is already used.
|
|
*
|
|
* @param identifier the identifier that will be used to ask the manager for the resolved dependency.
|
|
* @param provider a function that receive the dependency manager as param and return the dependency. Can be asynchronous.
|
|
*/
|
|
register<T>(identifier: ModuleIdentifier, provider: Provider<T>): void {
|
|
if (this.modules.has(identifier)) {
|
|
throw new Error(`Module ${identifier} is already registered.`);
|
|
}
|
|
this.modules.set(identifier, { provider });
|
|
}
|
|
|
|
/**
|
|
* Return the dependency matching the given identifier.
|
|
* If the dependency has not been resolved yet, resolve it first.
|
|
*
|
|
* Throw an error if:
|
|
* - no provider has been registred with that identifier
|
|
* - a circular dependency is detected
|
|
*
|
|
* @param identifier the identifier used to register the provider
|
|
*/
|
|
async resolve<T>(identifier: ClassType<T>): Promise<T>;
|
|
async resolve<T>(identifier: ModuleIdentifier): Promise<T>;
|
|
async resolve(identifier: ModuleIdentifier) {
|
|
const module = this.modules.get(identifier);
|
|
if (!module) {
|
|
throw new Error(`Module ${identifier} is not registered.`);
|
|
}
|
|
|
|
if (module.instance) {
|
|
return module.instance;
|
|
}
|
|
|
|
if (this.resolving.has(identifier)) {
|
|
throw new Error(`Circular dependency detected for module ${identifier}.`);
|
|
}
|
|
|
|
this.resolving.add(identifier);
|
|
|
|
try {
|
|
module.instance = await module.provider(this);
|
|
return module.instance;
|
|
} finally {
|
|
this.resolving.delete(identifier);
|
|
}
|
|
}
|
|
}
|