diff --git a/src/container.ts b/src/container.ts index 8ce9bf9..384c582 100644 --- a/src/container.ts +++ b/src/container.ts @@ -7,7 +7,7 @@ import { isValueProvider, type Provider, } from "./provider"; -import {type Options, type Registration, Registry} from "./registry"; +import {type Registration, type RegistrationOptions, Registry} from "./registry"; import {Scope} from "./scope"; import {type Constructor, isConstructor, type Token, type TokenList} from "./token"; import {KeyedStack} from "./utils/keyed-stack"; @@ -22,8 +22,8 @@ export class Container { readonly parent?: Container; readonly registry: Registry; - defaultScope: Scope; autoRegister: boolean; + defaultScope: Scope; constructor(options?: ContainerOptions); constructor({ @@ -61,11 +61,15 @@ export class Container { } register(Class: Constructor): this; - register(token: Token, provider: Provider, options?: Options): this; + register( + token: Token, + provider: Provider, + options?: RegistrationOptions, + ): this; register( ...args: | [Constructor] - | [Token, Provider, Options?] + | [Token, Provider, RegistrationOptions?] ): this { if (args.length == 1) { const [Class] = args; @@ -83,10 +87,7 @@ export class Container { const Class = provider.useClass; const metadata = getMetadata(Class); provider = metadata.provider; - options = { - scope: metadata.scope, - ...options, - }; + options = {scope: metadata.scope, ...options}; } this.registry.set(token, {provider, options}); } @@ -143,19 +144,20 @@ export class Container { } private resolveValue(registration: Registration): Value { - if (isClassProvider(registration.provider)) { - const Class = registration.provider.useClass; + const provider = registration.provider; + if (isClassProvider(provider)) { + const Class = provider.useClass; return this.resolveScopedInstance(registration, () => new Class()); } - else if (isFactoryProvider(registration.provider)) { - const factory = registration.provider.useFactory; + else if (isFactoryProvider(provider)) { + const factory = provider.useFactory; return this.resolveScopedInstance(registration, factory); } - else if (isValueProvider(registration.provider)) { - const value = registration.provider.useValue; + else if (isValueProvider(provider)) { + const value = provider.useValue; return value; } - expectNever(registration.provider); + expectNever(provider); } private resolveScopedInstance(registration: Registration, instantiate: () => T): T { @@ -172,21 +174,25 @@ export class Container { }, () => this.resolveScopedInstance(registration, instantiate)); } - if (context.resolution.stack.has(registration.provider)) { - if (context.resolution.dependents.has(registration.provider)) { - return context.resolution.dependents.get(registration.provider); + const provider = registration.provider; + const options = registration.options; + + if (context.resolution.stack.has(provider)) { + const dependentRef = context.resolution.dependents.get(provider); + if (dependentRef) { + return dependentRef.current; } assert(false, ErrorMessage.CircularDependency); } - let resolvedScope = registration.options?.scope || this.defaultScope; + let resolvedScope = options?.scope || this.defaultScope; if (resolvedScope == Scope.Inherited) { const dependentFrame = context.resolution.stack.peek(); resolvedScope = dependentFrame?.scope || Scope.Transient; } - context.resolution.stack.push(registration.provider, { - provider: registration.provider, + context.resolution.stack.push(provider, { + provider, scope: resolvedScope, }); try { @@ -199,11 +205,12 @@ export class Container { return instance; } else if (resolvedScope == Scope.Resolution) { - if (context.resolution.instances.has(registration.provider)) { - return context.resolution.instances.get(registration.provider); + const instanceRef = context.resolution.instances.get(provider); + if (instanceRef) { + return instanceRef.current; } const instance = instantiate(); - context.resolution.instances.set(registration.provider, instance); + context.resolution.instances.set(provider, {current: instance}); return instance; } else if (resolvedScope == Scope.Transient) { diff --git a/src/index.ts b/src/index.ts index 95db2df..b2c63c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,8 +4,9 @@ export type {ClassDecorator, ClassFieldDecorator, ClassFieldInitializer} from ". export {AutoRegister, Inject, Injectable, InjectAll, Scoped} from "./decorators"; export {ErrorMessage} from "./errors"; export {inject, injectAll} from "./inject"; +export type {InstanceRef} from "./instance"; export type {ClassProvider, FactoryProvider, Provider, ValueProvider} from "./provider"; -export type {Cache, Options, Registration, Registry} from "./registry"; +export type {Registration, RegistrationOptions, Registry} from "./registry"; export {Build, Value} from "./registry"; export {Scope} from "./scope"; export type {Constructor, Token, TokenList} from "./token"; diff --git a/src/inject.ts b/src/inject.ts index e85d45a..86521b8 100644 --- a/src/inject.ts +++ b/src/inject.ts @@ -16,12 +16,13 @@ export namespace inject { assert(context, ErrorMessage.InjectOutsideOfContext); const currentFrame = context.resolution.stack.peek(); assert(currentFrame, ErrorMessage.InvariantViolation); - context.resolution.dependents.set(currentFrame.provider, thisArg); + const provider = currentFrame.provider; + context.resolution.dependents.set(provider, {current: thisArg}); try { return inject(...tokens); } finally { - context.resolution.dependents.delete(currentFrame.provider); + context.resolution.dependents.delete(provider); } } } diff --git a/src/injection-context.ts b/src/injection-context.ts index 8ac22b0..2879529 100644 --- a/src/injection-context.ts +++ b/src/injection-context.ts @@ -1,4 +1,5 @@ import type {Container} from "./container"; +import type {InstanceRef} from "./instance"; import type {Provider} from "./provider"; import type {Scope} from "./scope"; import {createContext} from "./utils/context"; @@ -10,12 +11,12 @@ export interface InjectionContext { } export interface Resolution { - stack: KeyedStack; - instances: Map; - dependents: Map; + stack: KeyedStack; + instances: Map; + dependents: Map; } -export interface Frame { +export interface ResolutionFrame { scope: ResolvedScope; provider: Provider; } diff --git a/src/instance.ts b/src/instance.ts new file mode 100644 index 0000000..28f60c3 --- /dev/null +++ b/src/instance.ts @@ -0,0 +1,3 @@ +export interface InstanceRef { + readonly current: T; +} diff --git a/src/registry.ts b/src/registry.ts index e43ef44..3e04030 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,20 +1,17 @@ import {assert} from "./errors"; import {ErrorMessage} from "./errors"; +import type {InstanceRef} from "./instance"; import {NullProvider, type Provider, UndefinedProvider} from "./provider"; import {Scope} from "./scope"; import {type Token, Type} from "./token"; export interface Registration { - cache?: Cache; - options?: Options; + options?: RegistrationOptions; + cache?: InstanceRef; provider: Provider; } -export interface Cache { - current: T; -} - -export interface Options { +export interface RegistrationOptions { scope?: Scope; }