Skip to content

Commit

Permalink
Merge pull request #2856 from tsedio/feat-injector
Browse files Browse the repository at this point in the history
Feat injector
  • Loading branch information
Romakita authored Oct 9, 2024
2 parents 1a1d31f + 82019db commit 732df3a
Show file tree
Hide file tree
Showing 63 changed files with 1,149 additions and 821 deletions.
2 changes: 1 addition & 1 deletion packages/di/.barrelsby.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"directory": ["./src/common", "./src/node"],
"exclude": ["**/__mock__", "**/__mocks__", "**/*.spec.ts"],
"exclude": ["**/__mock__", "**/__mocks__", "**/*.spec.ts", "localsContainer.ts"],
"delete": true
}
22 changes: 1 addition & 21 deletions packages/di/src/common/decorators/autoInjectable.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {catchError} from "@tsed/core";
import {Logger} from "@tsed/logger";
import {beforeEach} from "vitest";

Expand Down Expand Up @@ -105,9 +104,8 @@ describe("AutoInjectable", () => {
class Test {
@Inject(Logger)
logger: Logger;

private value: string;
instances?: InterfaceGroup[];
private value: string;

constructor(initialValue: string, @Inject(TOKEN_GROUPS) instances?: InterfaceGroup[]) {
this.value = initialValue;
Expand All @@ -134,22 +132,4 @@ describe("AutoInjectable", () => {
new Test(["item1", "item2", "item3"], "group");
});
});
describe("when the instance is created outside of an injection context", () => {
it("should throw an error", () => {
@AutoInjectable()
class Test {
@Inject(Logger)
logger: Logger;

foo() {
this.logger.info("test");
}
}

const error = catchError(() => new Test());

expect(error).toBeInstanceOf(Error);
expect(error?.message).toEqual("InjectorService instance is not created yet.");
});
});
});
8 changes: 3 additions & 5 deletions packages/di/src/common/decorators/autoInjectable.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {isArray, type Type} from "@tsed/core";

import {LocalsContainer} from "../domain/LocalsContainer.js";
import {injector} from "../fn/injector.js";
import type {TokenProvider} from "../interfaces/TokenProvider.js";
import {InjectorService} from "../services/InjectorService.js";
import {getConstructorDependencies} from "../utils/getConstructorDependencies.js";

function resolveAutoInjectableArgs(token: Type, args: unknown[]) {
const inj = injector();
const locals = new LocalsContainer();
const injector = InjectorService.getInstance();
const deps: TokenProvider[] = getConstructorDependencies(token);
const list: any[] = [];
const length = Math.max(deps.length, args.length);
Expand All @@ -17,9 +17,7 @@ function resolveAutoInjectableArgs(token: Type, args: unknown[]) {
list.push(args[i]);
} else {
const value = deps[i];
const instance = isArray(value)
? injector!.getMany(value[0], locals, {parent: token})
: injector!.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
108 changes: 40 additions & 68 deletions packages/di/src/common/decorators/constant.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {DITest} from "../../node/index.js";
import {Constant, constant} from "./constant.js";
import {Constant} from "./constant.js";

describe("@Constant()", () => {
beforeEach(() =>
Expand All @@ -10,86 +10,58 @@ describe("@Constant()", () => {
})
);
afterEach(() => DITest.reset());
describe("when decorator is used as property decorator", () => {
it("should create a getter", async () => {
// WHEN
class Test {
@Constant("logger.level", "default value")
test: string;
}

// THEN
it("should create a getter", async () => {
// WHEN
class Test {
@Constant("logger.level", "default value")
test: string;
}

const test = await DITest.invoke<Test>(Test);

expect(test.test).toEqual("off");
});
it("should create a getter with default value", async () => {
// WHEN
class Test {
@Constant("logger.test", "default value")
test: string;
}
// THEN

// THEN
const test = await DITest.invoke<Test>(Test);

const test = await DITest.invoke<Test>(Test);

expect(test.test).toEqual("default value");
});
it("shouldn't be possible to modify injected value from injector.settings", async () => {
// WHEN
class Test {
@Constant("logger.level")
test: string;
}
expect(test.test).toEqual("off");
});
it("should create a getter with default value", async () => {
// WHEN
class Test {
@Constant("logger.test", "default value")
test: string;
}

// THEN
// THEN

const test = await DITest.invoke<Test>(Test);
const test = await DITest.invoke<Test>(Test);

test.test = "new value";
expect(test.test).toEqual("default value");
});
it("shouldn't be possible to modify injected value from injector.settings", async () => {
// WHEN
class Test {
@Constant("logger.level")
test: string;
}

expect(test.test).toEqual("off");
});
it("should create a getter with native default value", async () => {
// WHEN
class Test {
@Constant("logger.test")
test: string = "default prop";
}
// THEN

// THEN
const test = await DITest.invoke<Test>(Test);

const test = await DITest.invoke<Test>(Test);
test.test = "new value";

expect(test.test).toEqual("default prop");
});
expect(test.test).toEqual("off");
});
describe("when constant is used as default value initializer", () => {
it("should inject constant to the property", async () => {
// WHEN
class Test {
test: string = constant("logger.level", "default value");
}

// THEN

const test = await DITest.invoke<Test>(Test);

expect(test.test).toEqual("off");
});
it("should return the default value if expression is undefined", async () => {
// WHEN
class Test {
test: string = constant("logger.test", "default value");
}
it("should create a getter with native default value", async () => {
// WHEN
class Test {
@Constant("logger.test")
test: string = "default prop";
}

// THEN
// THEN

const test = await DITest.invoke<Test>(Test);
const test = await DITest.invoke<Test>(Test);

expect(test.test).toEqual("default value");
});
expect(test.test).toEqual("default prop");
});
});
8 changes: 1 addition & 7 deletions packages/di/src/common/decorators/constant.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import {catchError, deepClone} from "@tsed/core";

import {InjectorService} from "../services/InjectorService.js";

export function constant<Type>(expression: string): Type | undefined;
export function constant<Type>(expression: string, defaultValue: Type | undefined): Type;
export function constant<Type>(expression: string, defaultValue?: Type | undefined): Type | undefined {
return InjectorService.getInstance().settings.get(expression, defaultValue);
}
import {constant} from "../fn/constant.js";

export function bindConstant(target: any, propertyKey: string | symbol, expression: string, defaultValue?: any) {
const symbol = Symbol();
Expand Down
Loading

0 comments on commit 732df3a

Please sign in to comment.