Skip to content

Commit

Permalink
fix(di): move locals arg to DIInvokeOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Nov 15, 2024
1 parent 281b368 commit 71f20d0
Show file tree
Hide file tree
Showing 22 changed files with 93 additions and 93 deletions.
4 changes: 1 addition & 3 deletions docs/docs/injection-scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ Instance scope used on a provider tells the injector to create a new instance ea
With the functional API, you can also rebuild any service on the fly by calling @@inject@@ with the `rebuild` flag:

```typescript
import {inject, injector} from "@tsed/di";
import {inject} from "@tsed/di";

const myService = inject(MyService, {rebuild: true});
// similar to
const myService2 = injector().invoke(MyService, {rebuild: true});
```
2 changes: 1 addition & 1 deletion packages/di/src/common/decorators/autoInjectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function resolveAutoInjectableArgs(token: Type, args: unknown[]) {
list.push(args[i]);
} else {
const value = deps[i];
const instance = isArray(value) ? inj!.getMany(value[0], locals, {parent: token}) : inj!.invoke(value, locals, {parent: token});
const instance = isArray(value) ? inj!.getMany(value[0], {locals, parent: token}) : inj!.invoke(value, {locals, parent: token});

list.push(instance);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/di/src/common/fn/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import {invokeOptions, localsContainer} from "./localsContainer.js";
* @decorator
*/
export function inject<T>(token: TokenProvider<T>, opts?: Partial<Pick<InvokeOptions, "useOpts" | "rebuild" | "locals">>): T {
return injector().invoke(token, opts?.locals || localsContainer(), {
return injector().invoke(token, {
...opts,
...invokeOptions()
...invokeOptions(),
locals: opts?.locals || localsContainer()
});
}
2 changes: 1 addition & 1 deletion packages/di/src/common/fn/injectMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import {injector} from "./injector.js";
import {localsContainer} from "./localsContainer.js";

export function injectMany<T>(token: string | symbol, opts?: Partial<Pick<InvokeOptions, "useOpts" | "rebuild" | "locals">>): T[] {
return injector().getMany<T>(token, opts?.locals || localsContainer(), opts);
return injector().getMany<T>(token, {...opts, locals: opts?.locals || localsContainer()});
}
4 changes: 2 additions & 2 deletions packages/di/src/common/integration/di.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ describe("DI", () => {
expect(injector.invoke(ServiceInstance) === injector.invoke(ServiceInstance)).toEqual(false);

const locals = new LocalsContainer();
expect(injector.invoke(ServiceRequest, locals)).toEqual(injector.invoke(ServiceRequest, locals));
expect(injector.invoke(ServiceInstance, locals) === injector.invoke(ServiceInstance, locals)).toEqual(false);
expect(injector.invoke(ServiceRequest, {locals})).toEqual(injector.invoke(ServiceRequest, {locals}));
expect(injector.invoke(ServiceInstance, {locals}) === injector.invoke(ServiceInstance, {locals})).toEqual(false);
});
});

Expand Down
6 changes: 3 additions & 3 deletions packages/di/src/common/integration/request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ describe("DI Request", () => {
const locals = new LocalsContainer();

// WHEN
const result1: any = injector.invoke(ServiceRequest, locals);
const result2: any = injector.invoke(ServiceRequest, locals);
const serviceSingleton1: any = injector.invoke(ServiceSingleton, locals);
const result1: any = injector.invoke(ServiceRequest, {locals});
const result2: any = injector.invoke(ServiceRequest, {locals});
const serviceSingleton1: any = injector.invoke(ServiceSingleton, {locals});
const serviceSingleton2: any = injector.get(ServiceSingleton);

vi.spyOn(result1, "$onDestroy").mockResolvedValue(undefined);
Expand Down
8 changes: 4 additions & 4 deletions packages/di/src/common/interfaces/InvokeOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export interface InvokeOptions {
* Parent provider.
*/
parent?: TokenProvider;
/**
* Scope used by the injector to build the provider.
*/
scope: ProviderScope;
// /**
// * Scope used by the injector to build the provider.
// */
// scope: ProviderScope;
/**
* If true, the injector will rebuild the instance.
*/
Expand Down
3 changes: 1 addition & 2 deletions packages/di/src/common/interfaces/RegistrySettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ export interface RegistrySettings {
/**
*
* @param provider
* @param {Map<string | Function, any>} locals
* @param options
*/
onInvoke?(provider: Provider, locals: LocalsContainer, options: ResolvedInvokeOptions & {injector: InjectorService}): void;
onInvoke?(provider: Provider, options: ResolvedInvokeOptions): void;
}
4 changes: 2 additions & 2 deletions packages/di/src/common/interfaces/ResolvedInvokeOptions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type {LocalsContainer} from "../domain/LocalsContainer.js";
import type {Provider} from "../domain/Provider.js";
import type {ProviderScope} from "../domain/ProviderScope.js";
import type {TokenProvider} from "./TokenProvider.js";

export interface ResolvedInvokeOptions {
token: TokenProvider;
parent?: TokenProvider;
scope: ProviderScope;
deps: TokenProvider[];
imports: (TokenProvider | [TokenProvider])[];
provider: Provider;
locals: LocalsContainer;

construct(deps: TokenProvider[]): any;
}
6 changes: 3 additions & 3 deletions packages/di/src/common/registries/GlobalProviders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ describe("GlobalProviderRegistry", () => {
const locals = new LocalsContainer();
const resolvedOptions = {
token: provider.token,
injector: new InjectorService()
locals
} as any;

GlobalProviders.createRegistry("type:test", Provider, opts);
GlobalProviders.onInvoke(provider, locals, resolvedOptions);
GlobalProviders.onInvoke(provider, resolvedOptions);

expect(opts.onInvoke).toHaveBeenCalledWith(provider, locals, resolvedOptions);
expect(opts.onInvoke).toHaveBeenCalledWith(provider, resolvedOptions);
});
});
});
4 changes: 2 additions & 2 deletions packages/di/src/common/registries/GlobalProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ export class GlobalProviderRegistry extends Map<TokenProvider, Provider> {
return this;
}

onInvoke(provider: Provider, locals: LocalsContainer, options: ResolvedInvokeOptions & {injector: InjectorService}) {
onInvoke(provider: Provider, options: ResolvedInvokeOptions) {
const settings = this.#settings.get(provider.type);

if (settings?.onInvoke) {
settings.onInvoke(provider, locals, options);
settings.onInvoke(provider, options);
}
}

Expand Down
29 changes: 15 additions & 14 deletions packages/di/src/common/services/InjectorService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,17 @@ describe("InjectorService", () => {

// WHEN

const result1: any = injector.invoke(token, locals);
const result2: any = injector.invoke(token, locals, {rebuild: true});
const result1: any = injector.invoke(token, {locals});
const result2: any = injector.invoke(token, {locals, rebuild: true});

// THEN
expect(result1 !== result2).toEqual(true);
expect(injector.getProvider).toHaveBeenCalledWith(token);
expect(injector.get("alias")).toBeInstanceOf(token);

expect((injector as any).resolve).toHaveBeenCalledWith(token, locals, {rebuild: true});
expect((injector as any).invoke).toHaveBeenCalledWith(InjectorService, locals, {
expect((injector as any).resolve).toHaveBeenCalledWith(token, {locals, rebuild: true});
expect(injector.invoke).toHaveBeenCalledWith(InjectorService, {
locals,
parent: token
});
});
Expand All @@ -123,18 +124,18 @@ describe("InjectorService", () => {
const locals2 = new LocalsContainer(); // LocalContainer for the second request

// WHEN REQ1
const result1: any = injector.invoke(token, locals);
const result2: any = injector.invoke(token, locals);
const result1: any = injector.invoke(token, {locals});
const result2: any = injector.invoke(token, {locals});

// WHEN REQ2
const result3: any = injector.invoke(token, locals2);
const result3: any = injector.invoke(token, {locals: locals2});

// THEN
expect(result1).toEqual(result2);
expect(result2 !== result3).toEqual(true);

expect(injector.getProvider).toHaveBeenCalledWith(token);
expect((injector as any).resolve).toHaveBeenCalledWith(token, locals, {});
expect((injector as any).resolve).toHaveBeenCalledWith(token, {locals});
expect(locals.get(token)).toEqual(result1);
expect(locals2.get(token)).toEqual(result3);

Expand Down Expand Up @@ -162,14 +163,14 @@ describe("InjectorService", () => {
const locals = new LocalsContainer(); // LocalContainer for the first request

// WHEN REQ1
const result1: any = injector.invoke(token, locals);
const result2: any = injector.invoke(token, locals);
const result1: any = injector.invoke(token, {locals});
const result2: any = injector.invoke(token, {locals});

// THEN
expect(result1 !== result2).toEqual(true);

expect(injector.getProvider).toHaveBeenCalledWith(token);
expect((injector as any).resolve).toHaveBeenCalledWith(token, locals, {});
expect((injector as any).resolve).toHaveBeenCalledWith(token, {locals});
expect(locals.has(token)).toEqual(false);

return expect(injector.get).not.toHaveBeenCalled();
Expand All @@ -196,7 +197,7 @@ describe("InjectorService", () => {

// THEN
expect(result).toBeInstanceOf(token);
expect(GlobalProviders.onInvoke).toHaveBeenCalledWith(provider, expect.any(LocalsContainer), expect.anything());
expect(GlobalProviders.onInvoke).toHaveBeenCalledWith(provider, expect.anything());
});
it("should invoke the provider from container (2)", async () => {
// GIVEN
Expand All @@ -218,8 +219,8 @@ describe("InjectorService", () => {

// WHEN

const result1: any = injector.invoke(token, locals);
const result2: any = injector.invoke(token, locals);
const result1: any = injector.invoke(token, {locals});
const result2: any = injector.invoke(token, {locals});

// THEN
expect(result1).toEqual(result2);
Expand Down
62 changes: 30 additions & 32 deletions packages/di/src/common/services/InjectorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ export class InjectorService extends Container {
/**
* Return all instance of the same provider type
*/
getMany<Type = any>(type: any, locals?: LocalsContainer, options?: Partial<InvokeOptions>): Type[] {
getMany<Type = any>(type: any, options?: Partial<InvokeOptions>): Type[] {
return this.getProviders(type).map((provider) => {
return this.invoke(provider.token, locals, options)!;
return this.invoke(provider.token, options)!;
});
}

Expand Down Expand Up @@ -170,12 +170,11 @@ export class InjectorService extends Container {
* ```
*
* @param token The injectable class to invoke. Class parameters are injected according constructor signature.
* @param locals Optional object. If preset then any argument Class are read from this object first, before the `InjectorService` is consulted.
* @param options
* @param options {InvokeOptions} Optional options to invoke the class.
* @returns {Type} The class constructed.
*/
public invoke<Type = any>(token: TokenProvider<Type>, locals?: LocalsContainer, options: Partial<InvokeOptions> = {}): Type {
let instance: any = locals ? locals.get(token) : undefined;
public invoke<Type = any>(token: TokenProvider<Type>, options: Partial<InvokeOptions> = {}): Type {
let instance: any = options.locals ? options.locals.get(token) : undefined;

if (instance !== undefined) {
return instance;
Expand Down Expand Up @@ -203,7 +202,7 @@ export class InjectorService extends Container {
};

if (!provider || options.rebuild) {
instance = this.resolve(token, locals, options);
instance = this.resolve(token, options);

if (this.hasProvider(token)) {
set(instance);
Expand All @@ -212,7 +211,7 @@ export class InjectorService extends Container {
return instance;
}

instance = this.resolve(token, locals, options);
instance = this.resolve(token, options);

switch (this.scopeOf(provider)) {
case ProviderScope.SINGLETON:
Expand All @@ -238,11 +237,11 @@ export class InjectorService extends Container {
return instance;

case ProviderScope.REQUEST:
if (locals) {
locals.set(token, instance);
if (options.locals) {
options.locals.set(token, instance);

if (provider.hooks && provider.hooks.$onDestroy) {
locals.hooks.on("$onDestroy", (...args: any[]) => provider.hooks!.$onDestroy(instance, ...args));
options.locals.hooks.on("$onDestroy", (...args: any[]) => provider.hooks!.$onDestroy(instance, ...args));
}
}

Expand Down Expand Up @@ -434,12 +433,8 @@ export class InjectorService extends Container {
* @param options
* @private
*/
protected resolve<T>(
target: TokenProvider,
locals: LocalsContainer = new LocalsContainer(),
options: Partial<InvokeOptions> = {}
): T | Promise<T> {
const resolvedOpts = this.mapInvokeOptions(target, locals, options);
protected resolve<T>(target: TokenProvider, options: Partial<InvokeOptions> = {}): T | Promise<T> {
const resolvedOpts = this.mapInvokeOptions(target, options);

if (!resolvedOpts) {
return undefined as T;
Expand All @@ -448,7 +443,7 @@ export class InjectorService extends Container {
const {token, deps, construct, imports, provider} = resolvedOpts;

if (provider) {
GlobalProviders.onInvoke(provider, locals, {...resolvedOpts, injector: this});
GlobalProviders.onInvoke(provider, resolvedOpts);
}

let instance: any;
Expand All @@ -461,12 +456,18 @@ export class InjectorService extends Container {
currentDependency = {token, index, deps};

if (isArray(token)) {
return this.getMany(token[0], locals, options);
return this.getMany(token[0], options);
}

const useOpts = provider?.store?.get(`${DI_USE_PARAM_OPTIONS}:${index}`) || options.useOpts;

return isInheritedFrom(token, Provider, 1) ? provider : this.invoke(token, locals, {parent, useOpts});
return isInheritedFrom(token, Provider, 1)
? provider
: this.invoke(token, {
parent,
locals: options.locals,
useOpts
});
};

// Invoke manually imported providers
Expand All @@ -491,7 +492,7 @@ export class InjectorService extends Container {

if (instance && isClass(classOf(instance))) {
Reflect.defineProperty(instance, DI_INVOKE_OPTIONS, {
get: () => ({rebuild: options.rebuild, locals})
get: () => ({rebuild: options.rebuild, locals: options.locals})
});
}

Expand Down Expand Up @@ -542,17 +543,15 @@ export class InjectorService extends Container {
/**
* Create options to invoke a provider or class.
* @param token
* @param locals
* @param options
*/
private mapInvokeOptions(
token: TokenProvider,
locals: Map<TokenProvider, any>,
options: Partial<InvokeOptions>
): ResolvedInvokeOptions | false {
private mapInvokeOptions(token: TokenProvider, options: Partial<InvokeOptions>): ResolvedInvokeOptions | false {
const locals = options.locals || new LocalsContainer();

options.locals = locals;

let imports: (TokenProvider | [TokenProvider])[] | undefined = options.imports;
let deps: TokenProvider[] | undefined = options.deps;
let scope = options.scope;
let construct;

if (!token || token === Object) {
Expand All @@ -565,7 +564,7 @@ export class InjectorService extends Container {
provider = new Provider(token);

this.resolvers.forEach((resolver) => {
const result = resolver.get(token, locals.get(DI_USE_PARAM_OPTIONS));
const result = resolver.get(token, options.locals!.get(DI_USE_PARAM_OPTIONS));

if (result !== undefined) {
provider.useFactory = () => result;
Expand All @@ -575,7 +574,6 @@ export class InjectorService extends Container {
provider = this.getProvider(token)!;
}

scope = scope || this.scopeOf(provider);
deps = deps || provider.deps;
imports = imports || provider.imports;

Expand All @@ -598,11 +596,11 @@ export class InjectorService extends Container {

return {
token,
scope: scope || Store.from(token).get("scope") || ProviderScope.SINGLETON,
deps: deps! || [],
imports: imports || [],
construct,
provider
provider,
locals
};
}

Expand Down
2 changes: 0 additions & 2 deletions packages/di/src/node/domain/DIContext.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {injector, InjectorService, LocalsContainer} from "../../common/index.js";
import {logger} from "../fn/logger.js";
import {runInContext} from "../utils/asyncHookContext.js";
import {ContextLogger, ContextLoggerOptions} from "./ContextLogger.js";

export interface DIContextOptions extends Omit<ContextLoggerOptions, "dateStart"> {
Expand Down
Loading

0 comments on commit 71f20d0

Please sign in to comment.