diff --git a/packages/i18n/src/Translator.ts b/packages/i18n/src/Translator.ts index 59fb50e..f7bdd17 100644 --- a/packages/i18n/src/Translator.ts +++ b/packages/i18n/src/Translator.ts @@ -19,10 +19,10 @@ export interface TranslatorForModule { } export const Translator = defineService({ - deps: { - localeProvider: LocaleProvider, - }, - setup({ localeProvider }): Translator { + name: 'Translator', + setup({ inject }): Translator { + const localeProvider = inject(LocaleProvider); + const translator = (>function (value, params) { return translate(value, { params, diff --git a/packages/ioc/src/Container.ts b/packages/ioc/src/Container.ts index 910531c..3898b3a 100644 --- a/packages/ioc/src/Container.ts +++ b/packages/ioc/src/Container.ts @@ -1,22 +1,6 @@ -import { Constructor, Flatten } from '@nzyme/types'; - import { Executable } from './Executable.js'; -import { Factory } from './Factory.js'; import { Injectable } from './Injectable.js'; import { Resolvable } from './Resolvable.js'; -import { Service } from './Service.js'; - -export type ResolveDeps = { - [key: string]: Injectable | Constructor; -}; - -export type ResolveResult> = Flatten<{ - [K in keyof TDeps]: TDeps[K] extends Injectable - ? T - : TDeps[K] extends Constructor - ? Container - : never; -}>; export class Container { private instances = new Map(); @@ -28,11 +12,8 @@ export class Container { } public set(injectable: Injectable, instance: T): void; - public set( - injectable: Injectable, - service: Resolvable | Factory, - ): void; - public set(injectable: Injectable, instanceOrService: T | Service): void { + public set(injectable: Injectable, service: Resolvable): void; + public set(injectable: Injectable, instanceOrService: T | Resolvable): void { if (instanceOrService instanceof Resolvable) { if (instanceOrService.for !== injectable) { const injectableName = injectable.name ?? ''; @@ -68,9 +49,8 @@ export class Container { return this.resolveInstance(injectable); } - public execute(executable: Executable) { - const deps = this.resolveDeps(executable.deps); - return executable.execute(deps); + public execute(executable: Executable) { + return this.resolve(executable)(); } private resolveInstance(injectable: Injectable, scope?: Injectable): unknown { @@ -89,7 +69,7 @@ export class Container { return instance; } - instance = this.resolveResolvable(resolver, scope); + instance = resolver.resolve(this, scope); if (resolver.cached) { this.instances.set(injectable.symbol, instance); this.instances.set(resolver.symbol, instance); @@ -99,7 +79,7 @@ export class Container { } if (injectable instanceof Resolvable) { - instance = this.resolveResolvable(injectable as Resolvable, scope); + instance = injectable.resolve(this, scope); if (instance && injectable.cached) { this.instances.set(injectable.symbol, instance); @@ -112,28 +92,4 @@ export class Container { return instance; } - - private resolveResolvable(resolvable: Resolvable, scope?: Injectable) { - const deps = resolvable.deps ? this.resolveDeps(resolvable.deps) : {}; - return resolvable.resolve(deps, scope); - } - - private resolveDeps( - config: TDeps, - scope?: Injectable, - ): ResolveResult { - const result = {} as ResolveResult; - - for (const key in config) { - const injectable = config[key]; - if (injectable === Container) { - result[key as keyof TDeps] = this as ResolveResult[keyof TDeps]; - } else { - const value = this.resolveInstance(injectable as Injectable, scope); - result[key as keyof TDeps] = value as ResolveResult[keyof TDeps]; - } - } - - return result; - } } diff --git a/packages/ioc/src/Executable.ts b/packages/ioc/src/Executable.ts index 91054b7..5939762 100644 --- a/packages/ioc/src/Executable.ts +++ b/packages/ioc/src/Executable.ts @@ -1,9 +1,9 @@ -import { ResolveDeps, ResolveResult } from './Container.js'; -import { InjectableOptions } from './Injectable.js'; +import { Container } from './Container.js'; +import { Injectable, InjectableOptions } from './Injectable.js'; import { Resolvable } from './Resolvable.js'; -export class Executable extends Resolvable<() => T, TDeps> { - constructor(private readonly def: ExecutableDefinition) { +export class Executable extends Resolvable<() => T> { + constructor(private readonly def: ExecutableDefinition) { super(def); } @@ -11,23 +11,24 @@ export class Executable extends Resolvable<() => T return true; } - override resolve(deps: ResolveResult): () => T { - return () => this.def.setup(deps as ResolveResult); + public override resolve(container: Container) { + return () => + this.def.setup({ + container, + inject: injectable => container.resolve(injectable), + }); } +} - public execute(deps: ResolveResult): void { - this.def.setup(deps); - } +export interface ExecutableContext { + readonly container: Container; + readonly inject: (injectable: Injectable) => T; } -export interface ExecutableDefinition - extends InjectableOptions { - readonly deps: TDeps; - readonly setup: (deps: ResolveResult) => T; +export interface ExecutableDefinition extends InjectableOptions { + readonly setup: (ctx: ExecutableContext) => T; } -export function defineExecutable( - definition: ExecutableDefinition, -): Executable { +export function defineExecutable(definition: ExecutableDefinition): Executable { return new Executable(definition); } diff --git a/packages/ioc/src/Factory.ts b/packages/ioc/src/Factory.ts index 033499d..549d8a5 100644 --- a/packages/ioc/src/Factory.ts +++ b/packages/ioc/src/Factory.ts @@ -1,11 +1,9 @@ -import { EmptyObject } from '@nzyme/types'; - -import { ResolveDeps, ResolveResult } from './Container.js'; +import { Container } from './Container.js'; import { Injectable } from './Injectable.js'; import { Resolvable, ResolvableOptions } from './Resolvable.js'; -export class Factory extends Resolvable { - constructor(private readonly def: FactoryOptions) { +export class Factory extends Resolvable { + constructor(private readonly def: FactoryOptions) { super(def); } @@ -13,22 +11,25 @@ export class Factory extends Resolva return false; } - override resolve(deps: ResolveResult, scope?: Injectable): T { - return this.def.setup(deps as ResolveResult, scope); + public override resolve(container: Container, scope?: Injectable) { + return this.def.setup({ + container, + scope, + inject: injectable => container.resolve(injectable), + }); } +} - public create(deps: ResolveResult): T { - return this.def.setup(deps); - } +export interface FactoryContext { + readonly container: Container; + readonly scope?: Injectable; + readonly inject: (injectable: Injectable) => T; } -export interface FactoryOptions - extends ResolvableOptions { - readonly setup: (deps: ResolveResult, scope?: Injectable) => T; +export interface FactoryOptions extends ResolvableOptions { + readonly setup: (ctx: FactoryContext) => T; } -export function defineFactory( - definition: FactoryOptions, -): Factory { +export function defineFactory(definition: FactoryOptions): Factory { return new Factory(definition); } diff --git a/packages/ioc/src/Resolvable.ts b/packages/ioc/src/Resolvable.ts index d77b221..3a4d925 100644 --- a/packages/ioc/src/Resolvable.ts +++ b/packages/ioc/src/Resolvable.ts @@ -1,27 +1,18 @@ -import { EmptyObject } from '@nzyme/types'; - -import { ResolveDeps } from './Container.js'; +import { Container } from './Container.js'; import { Injectable, InjectableOptions } from './Injectable.js'; -export abstract class Resolvable< - T = unknown, - TDeps extends ResolveDeps = ResolveDeps, -> extends Injectable { +export abstract class Resolvable extends Injectable { public readonly for?: Injectable; - public readonly deps: TDeps; - constructor(def: ResolvableOptions) { + constructor(def: ResolvableOptions) { super(def); this.for = def.for; - this.deps = def.deps ?? ({} as TDeps); } public abstract get cached(): boolean; - public abstract resolve(deps: TDeps, scope?: Injectable): T | undefined; + public abstract resolve(container: Container, scope?: Injectable): T | undefined; } -export interface ResolvableOptions - extends InjectableOptions { - readonly deps?: TDeps; +export interface ResolvableOptions extends InjectableOptions { readonly for?: Injectable; } diff --git a/packages/ioc/src/Service.ts b/packages/ioc/src/Service.ts index 11bbdca..f6b782a 100644 --- a/packages/ioc/src/Service.ts +++ b/packages/ioc/src/Service.ts @@ -1,10 +1,9 @@ -import { EmptyObject } from '@nzyme/types'; - -import { ResolveDeps, ResolveResult } from './Container.js'; +import { Container } from './Container.js'; +import { Injectable } from './Injectable.js'; import { Resolvable, ResolvableOptions } from './Resolvable.js'; -export class Service extends Resolvable { - constructor(private readonly def: ServiceOptions) { +export class Service extends Resolvable { + constructor(private readonly def: ServiceOptions) { super(def); } @@ -12,22 +11,23 @@ export class Service extends Resolva return true; } - override resolve(deps: ResolveResult): T { - return this.def.setup(deps as ResolveResult); + public override resolve(container: Container) { + return this.def.setup({ + container, + inject: injectable => container.resolve(injectable), + }); } +} - public create(deps: ResolveResult): T { - return this.def.setup(deps); - } +export interface ServiceContext { + readonly container: Container; + readonly inject: (injectable: Injectable) => T; } -export interface ServiceOptions - extends ResolvableOptions { - readonly setup: (deps: ResolveResult) => T; +export interface ServiceOptions extends ResolvableOptions { + readonly setup: (ctx: ServiceContext) => T; } -export function defineService( - definition: ServiceOptions, -): Service { +export function defineService(definition: ServiceOptions): Service { return new Service(definition); } diff --git a/packages/ioc/tests/Factory.test.ts b/packages/ioc/tests/Factory.test.ts index 466cc07..e39aca9 100644 --- a/packages/ioc/tests/Factory.test.ts +++ b/packages/ioc/tests/Factory.test.ts @@ -6,8 +6,7 @@ test('resolve factory with no deps', () => { let count = 0; const factory = defineFactory({ - deps: {}, - setup({}) { + setup() { count++; return 'foo'; }, @@ -35,7 +34,7 @@ test('resolve factory registered as injectable', () => { const factory = defineFactory({ for: injectable, - setup({}) { + setup() { count++; return 'foo'; }, @@ -62,19 +61,15 @@ test('resolve service with factory dep', () => { let count = 0; const factory = defineFactory({ - deps: {}, - setup({}) { + setup() { count++; return 'foo'; }, }); const service = defineService({ - deps: { - factory, - }, - setup({ factory }) { - return factory + 'bar'; + setup({ inject }) { + return inject(factory) + 'bar'; }, }); diff --git a/packages/ioc/tests/Service.test.ts b/packages/ioc/tests/Service.test.ts index 932b258..91955f9 100644 --- a/packages/ioc/tests/Service.test.ts +++ b/packages/ioc/tests/Service.test.ts @@ -6,8 +6,7 @@ test('resolve service with no deps', () => { let count = 0; const service = defineService({ - deps: {}, - setup({}) { + setup() { count++; return 'foo'; }, @@ -28,20 +27,19 @@ test('resolve service with deps', () => { let service2Count = 0; let service3Count = 0; - const service1 = defineService({ + const Service1 = defineService({ name: 'service1', - setup({}) { + setup() { service1Count++; return 'service1'; }, }); - const service2 = defineService({ + const Service2 = defineService({ name: 'service2', - deps: { - service1, - }, - setup({ service1 }) { + + setup({ inject }) { + const service1 = inject(Service1); service2Count++; expect(service1).toBe('service1'); return 'service2'; @@ -50,11 +48,10 @@ test('resolve service with deps', () => { const service3 = defineService({ name: 'service3', - deps: { - service1, - service2, - }, - setup({ service1, service2 }) { + + setup({ inject }) { + const service1 = inject(Service1); + const service2 = inject(Service2); service3Count++; expect(service1).toBe('service1'); expect(service2).toBe('service2'); @@ -68,7 +65,7 @@ test('resolve service with deps', () => { expect(service2Count).toBe(0); expect(service3Count).toBe(0); - const service2Resolved = container.resolve(service2); + const service2Resolved = container.resolve(Service2); expect(service2Resolved).toBe('service2'); expect(service1Count).toBe(1); expect(service2Count).toBe(1); @@ -80,7 +77,7 @@ test('resolve service with deps', () => { expect(service2Count).toBe(1); expect(service3Count).toBe(1); - const service1Resolved = container.resolve(service1); + const service1Resolved = container.resolve(Service1); expect(service1Resolved).toBe('service1'); expect(service1Count).toBe(1); expect(service2Count).toBe(1); @@ -120,13 +117,13 @@ test('register service as injectable - resolve injectable first', () => { }); test('register service as injectable - resolve service first', () => { - const injectable = defineInjectable<{ foo: string }>({ + const Injectable = defineInjectable<{ foo: string }>({ name: 'foobar', }); let serviceCount = 0; - const service = defineService({ - for: injectable, + const Service = defineService({ + for: Injectable, name: 'service', setup() { serviceCount++; @@ -138,11 +135,11 @@ test('register service as injectable - resolve service first', () => { const container = new Container(); - container.set(injectable, service); - const serviceInstance1 = container.resolve(service); - const serviceInstance2 = container.resolve(service); - const injectableInstance1 = container.resolve(injectable); - const injectableInstance2 = container.resolve(injectable); + container.set(Injectable, Service); + const serviceInstance1 = container.resolve(Service); + const serviceInstance2 = container.resolve(Service); + const injectableInstance1 = container.resolve(Injectable); + const injectableInstance2 = container.resolve(Injectable); expect(injectableInstance1.foo).toBe('bar'); expect(injectableInstance2).toBe(injectableInstance1); diff --git a/packages/logging/src/ConsoleLogger.ts b/packages/logging/src/ConsoleLogger.ts index 1197e29..d1b96ee 100644 --- a/packages/logging/src/ConsoleLogger.ts +++ b/packages/logging/src/ConsoleLogger.ts @@ -70,7 +70,7 @@ export class ConsoleLogger implements Logger { export const ConsoleLoggerFactory = defineFactory({ name: 'ConsoleLogger', for: Logger, - setup(deps, scope) { + setup({ scope }) { return new ConsoleLogger(scope?.name); }, }); diff --git a/packages/logging/src/PrettyLogger.ts b/packages/logging/src/PrettyLogger.ts index 2cacf78..704fcdc 100644 --- a/packages/logging/src/PrettyLogger.ts +++ b/packages/logging/src/PrettyLogger.ts @@ -76,7 +76,7 @@ export class PrettyLogger implements Logger { export const PrettyLoggerFactory = defineFactory({ name: 'PrettyLogger', for: Logger, - setup(deps, scope) { + setup({ scope }) { return new PrettyLogger(scope?.name); }, });