diff --git a/dependency-manager.ts b/dependency-manager.ts index 6b4e5e1..6de9fff 100644 --- a/dependency-manager.ts +++ b/dependency-manager.ts @@ -1,44 +1,53 @@ type Provider = (manager: DependencyManager) => Promise | T; -type Modules = Map = new () => T; + +// deno-lint-ignore no-explicit-any +type ModuleIdentifier = any; + +type Modules = Map; + // deno-lint-ignore no-explicit-any instance?: any; }>; export class DependencyManager { private modules: Modules = new Map(); - private resolving: Set = new Set(); + private resolving: Set = new Set(); // Enregistrer un module avec son initialisateur - register(name: string, provider: Provider): void { - if (this.modules.has(name)) { - throw new Error(`Module ${name} is already registered.`); + register(identifier: ModuleIdentifier, provider: Provider): void { + if (this.modules.has(identifier)) { + throw new Error(`Module ${identifier} is already registered.`); } - this.modules.set(name, { provider }); + this.modules.set(identifier, { provider }); } // Résoudre un module et ses dépendances - async resolve(name: string): Promise { - const module = this.modules.get(name); + async resolve(identifier: ClassType): Promise; + async resolve(identifier: ModuleIdentifier): Promise; + async resolve(identifier: ModuleIdentifier) { + const module = this.modules.get(identifier); if (!module) { - throw new Error(`Module ${name} is not registered.`); + throw new Error(`Module ${identifier} is not registered.`); } if (module.instance) { return module.instance; } - if (this.resolving.has(name)) { - throw new Error(`Circular dependency detected for module ${name}.`); + if (this.resolving.has(identifier)) { + throw new Error(`Circular dependency detected for module ${identifier}.`); } - this.resolving.add(name); + this.resolving.add(identifier); try { module.instance = await module.provider(this); return module.instance; } finally { - this.resolving.delete(name); + this.resolving.delete(identifier); } } } diff --git a/readme.md b/readme.md index 23a0add..02b4c1a 100644 --- a/readme.md +++ b/readme.md @@ -2,14 +2,16 @@ This lib provides a simplistic dependency manager. -## Features and limitations +## Features -- It resolves dependencies only when they are needed -- It detect circular dependency +- Resolves dependencies only when they are needed +- Detect circular dependency +- Accept any valid `Map` key as dependency identifier + - Infer the dependency type from the dependency identifier if the later is a class ## Usage -Create a manager: +1. Create a manager: ```typescript import { DependencyManager } from 'dependency-manager/mod.ts' @@ -17,48 +19,55 @@ import { DependencyManager } from 'dependency-manager/mod.ts' const manager = new DependencyManager() ``` -Create providers for your dependencies. -A provider is a function that is called when attempting to retrieve the dependency for the first time. -It receive the dependency manager, and return the dependency value, or a promise that resolves to it. +2. Register dependencies by giving the manager an identifier and a provider: ```typescript -// dependency-free provider -function loggerProvider() { - return console.log +manager.register('dependency-identifier', () => 'value'); +``` + +3. Get the dependency by giving the manager its identifier (always asynchrone): + +```typescript +const value = await manager.resolve('dependency-identifier'); +``` + +## Providers + +Providers are functions that are called when resolving the dependency for the first time. +They receive the dependency manager as parameter, and should return the dependency value, or a promise that resolves to it. + +Example of the registration of a dependency which provider uses another dependency: + +```typescript +async function provider(manager: DependencyManager) { + const value = await manager.resolve('dependencyIdentifier'); + return `The value is: ${valueA}`; } -type Logger = ReturnType; +manager.register('other-identifier', provider); +``` -// provider that need the 'logger' dependency -async function fooProvider(manager: DependencyManager) { - const logger = await manager.resolve('logger'); +## Identifiers and typing - return { - bar: () => logger('bar'), - baz: () => logger('baz'), - } +Any valid `Map` key can be used as identifier, but using a class allow the return value of the `resolve` method to be automatically typed: + +```typescript +class MyDependency {} + +manager.register(MyDependency, () => new MyDependency()); +const a = await manager.resolve(MyDependecy); //< `a` is of type `MyDependency` +``` + +Otherwise you can provide the type as type argument: + +```typescript +function provider() { + return 'foo' } -type Foo = Awaited>; +manager.register('my-dependency-identifier', provider); +const a = await manager.resolve('my-dependency-identifier'); //< `a` is of type `unknown` +const b = await manager.resolve('my-dependency-identifier'); //< `b` is of type `string` ``` -Register the dependency by passing the `register` method its name and provider: - -```typescript -manager.register('logger', loggerProvider); -manager.register('foo', fooProvider); -``` - -Retrieve dependencies by resolving them (always asynchrone, even if the provider was synchrone): - -```typescript -const foo = await manager.resolve('foo'); -foo.bar(); -foo.baz(); -``` - -## Typing - -Types should be explicitly defined when resolving a dependency. - ## Errors The `register` method throw an error if: