Skip to content

Commit

Permalink
fix(di): make injector really a singleton
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Nov 25, 2024
1 parent 319ff69 commit db7b2ca
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 46 deletions.
2 changes: 0 additions & 2 deletions packages/di/src/common/decorators/inject.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ describe("@Inject()", () => {
expect(instance.test).toBeInstanceOf(InjectorService);
});
it("should inject service and async factory", async () => {
// const inj = injector({rebuild: true});

// GIVEN
class Test {
constructor(public type: string) {}
Expand Down
14 changes: 6 additions & 8 deletions packages/di/src/common/decorators/lazyInject.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {catchAsyncError, classOf, nameOf} from "@tsed/core";

import {inject} from "../fn/inject.js";
import {injector} from "../fn/injector.js";
import type {MyLazyModule} from "./__mock__/lazy.module.js";
import {Injectable} from "./injectable.js";
Expand All @@ -13,14 +14,13 @@ describe("LazyInject", () => {
lazy: Promise<MyLazyModule>;
}

const inj = injector({rebuild: true});
const service = await inj.invoke<MyInjectable>(MyInjectable);
const nbProviders = inj.getProviders().length;
const service = inject<MyInjectable>(MyInjectable);
const nbProviders = injector().getProviders().length;

const lazyService = await service.lazy;

expect(nameOf(classOf(lazyService))).toEqual("MyLazyModule");
expect(nbProviders).not.toEqual(inj.getProviders().length);
expect(nbProviders).not.toEqual(injector().getProviders().length);
});

it("should throw an error when the module doesn't exists", async () => {
Expand All @@ -31,8 +31,7 @@ describe("LazyInject", () => {
lazy?: Promise<MyLazyModule>;
}

const inj = injector({rebuild: true});
const service = await inj.invoke<MyInjectable>(MyInjectable);
const service = inject<MyInjectable>(MyInjectable);
const error = await catchAsyncError(() => service.lazy);

expect(error?.message).toContain("Failed to load url lazy-module");
Expand All @@ -46,8 +45,7 @@ describe("LazyInject", () => {
lazy?: Promise<MyLazyModule>;
}

const inj = injector({rebuild: true});
const service = await inj.invoke<MyInjectable>(MyInjectable);
const service = inject(MyInjectable);
const lazyService = await service.lazy;

expect(lazyService).toEqual(undefined);
Expand Down
27 changes: 5 additions & 22 deletions packages/di/src/common/fn/injector.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {InjectorService} from "../services/InjectorService.js";

let globalInjector: InjectorService | undefined;
let globalInjector: InjectorService = new InjectorService();

type InjectorFnOpts = {rebuild?: boolean; logger?: any; settings?: Partial<TsED.Configuration>};
/**
* Create or return the existing injector service.
* Return the existing injector service.
*
* Example:
*
Expand All @@ -17,27 +16,11 @@ type InjectorFnOpts = {rebuild?: boolean; logger?: any; settings?: Partial<TsED.
* }
* ```
*/
export function injector(opts?: InjectorFnOpts): InjectorService {
if (!globalInjector || opts?.rebuild) {
globalInjector = new InjectorService();

if (opts && opts.logger) {
globalInjector.logger = opts.logger;
}

if (opts?.settings) {
globalInjector.settings.set(opts.settings);
}
}

export function injector(): InjectorService {
return globalInjector;
}

export function hasInjector() {
return !!globalInjector;
}

export async function destroyInjector() {
await globalInjector?.destroy();
globalInjector = undefined;
await globalInjector.destroy();
globalInjector = new InjectorService();
}
4 changes: 2 additions & 2 deletions packages/di/src/common/utils/providerBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "../registries/ProviderRegistry.js";
import {Store, type Type} from "@tsed/core";

import {ProviderType} from "../domain/ProviderType.js";
import {hasInjector, injector} from "../fn/injector.js";
import {injector} from "../fn/injector.js";
import type {ProviderOpts} from "../interfaces/ProviderOpts.js";
import type {TokenProvider} from "../interfaces/TokenProvider.js";
import {GlobalProviders} from "../registries/GlobalProviders.js";
Expand All @@ -26,7 +26,7 @@ export function providerBuilder<Provider, Picked extends keyof Provider>(props:
options: Partial<ProviderOpts<Type>> = {}
): ProviderBuilder<Token, Provider, Pick<Provider, Picked>> => {
const merged = {
global: !hasInjector() || injector().isLoaded(),
global: !injector().isLoaded(),
...options,
...baseOpts,
provide: token
Expand Down
15 changes: 5 additions & 10 deletions packages/di/src/node/services/DITest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
createContainer,
destroyInjector,
DI_INJECTABLE_PROPS,
hasInjector,
inject,
injector,
InjectorService,
Expand Down Expand Up @@ -39,11 +38,9 @@ export class DITest {
* Create a new injector with the right default services
*/
static createInjector(settings: any = {}): InjectorService {
const inj = injector({
rebuild: true,
logger: $log,
settings: DITest.configure(settings)
});
const inj = injector();
injector().logger = $log;
inj.settings.set(DITest.configure(settings));

setLoggerConfiguration();

Expand All @@ -54,10 +51,8 @@ export class DITest {
* Resets the test injector of the test context, so it won't pollute your next test. Call this in your `tearDown` logic.
*/
static async reset() {
if (hasInjector()) {
await destroyInjector();
cleanAllLocalsContainer();
}
await destroyInjector();
cleanAllLocalsContainer();
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/di/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export default defineConfig(
...presets.test.coverage,
thresholds: {
statements: 98.69,
branches: 97.2,
branches: 97.18,
functions: 97.02,
lines: 98.69
}
}
}
}
);
);

0 comments on commit db7b2ca

Please sign in to comment.