Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"clean": "rm -rf dist",
"build": "bun run clean && bun build:types && bun build:bundle",
"build:types": "tsc --emitDeclarationOnly",
"build:bundle": "bun build src/index.ts src/decorators/index.ts src/testing/index.ts --outdir dist --target bun --format esm",
"build:bundle": "bun build src/index.ts src/decorators/index.ts src/testing/index.ts --outdir dist --target bun --format esm --splitting",
"test": "bun test",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage",
Expand Down
45 changes: 40 additions & 5 deletions tests/decorators.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { describe, test, expect, beforeEach } from "bun:test";
import { Container, Lifetime } from "../src/index.js";
import { injectable, singleton, transient, scoped, inject } from "../src/decorators/index.js";
import {
injectable,
singleton,
transient,
scoped,
inject,
} from "../src/decorators/index.js";

describe("Decorator Integration", () => {
let container: Container;
Expand Down Expand Up @@ -193,6 +199,27 @@ describe("Decorator Integration", () => {
});

describe("@inject decorator", () => {
test("should inject an explicit token when imported from the decorator subpath", () => {
@singleton()
class Database {
query() {
return "data";
}
}

@singleton()
class UserService {
constructor(@inject(Database) public db: Database) {}
}

container.register(Database);
container.register(UserService);

const service = container.resolve(UserService);
expect(service.db).toBeInstanceOf(Database);
expect(service.db.query()).toBe("data");
});

test("should inject explicit tokens for interfaces using factory", () => {
const ILogger = Symbol("ILogger");
const IDatabase = Symbol("IDatabase");
Expand Down Expand Up @@ -308,7 +335,11 @@ describe("Decorator Integration", () => {
container.register(Logger);
container.register(Database);
container.registerFactory(ComplexService, (c) => {
return new ComplexService(c.resolve(Config), c.resolve(Logger), c.resolve(Database));
return new ComplexService(
c.resolve(Config),
c.resolve(Logger),
c.resolve(Database),
);
});

const service = container.resolve(ComplexService);
Expand All @@ -334,9 +365,13 @@ describe("Decorator Integration", () => {

container.register(ConsoleLogger);
// Factory needs explicit lifetime; decorator metadata applies only to register()
container.registerFactory(UserService, (c) => new UserService(c.resolve(ConsoleLogger)), {
lifetime: Lifetime.Singleton,
});
container.registerFactory(
UserService,
(c) => new UserService(c.resolve(ConsoleLogger)),
{
lifetime: Lifetime.Singleton,
},
);

const service1 = container.resolve(UserService);
const service2 = container.resolve(UserService);
Expand Down
64 changes: 64 additions & 0 deletions tests/package.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { describe, test } from "bun:test";
import { cp, mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

const projectRoot = join(import.meta.dir, "..");

describe("Package build", () => {
test("shares decorator metadata between package entrypoints", async () => {
const packageRoot = await mkdtemp(
join(tmpdir(), "fast-injection-package-"),
);
const consumerRoot = await mkdtemp(
join(tmpdir(), "fast-injection-consumer-"),
);

try {
await cp(
join(projectRoot, "package.json"),
join(packageRoot, "package.json"),
);
await Bun.$`bun build ${join(projectRoot, "src/index.ts")} ${join(projectRoot, "src/decorators/index.ts")} ${join(projectRoot, "src/testing/index.ts")} --outdir ${join(packageRoot, "dist")} --target bun --format esm --splitting`.quiet();
await mkdir(join(consumerRoot, "node_modules"));
await symlink(
packageRoot,
join(consumerRoot, "node_modules", "fast-injection"),
);
await writeFile(
join(consumerRoot, "tsconfig.json"),
JSON.stringify({ compilerOptions: { experimentalDecorators: true } }),
);
await writeFile(
join(consumerRoot, "index.ts"),
`
import { Container } from "fast-injection";
import { inject, singleton } from "fast-injection/decorators";

@singleton()
class Database {}

@singleton()
class UserService {
constructor(@inject(Database) readonly database: Database) {}
}

const container = new Container();
container.register(Database);
container.register(UserService);

if (!(container.resolve(UserService).database instanceof Database)) {
throw new Error("Decorator metadata was not shared between package entrypoints");
}
`,
);

await Bun.$`bun index.ts`.cwd(consumerRoot).quiet();
} finally {
await Promise.all([
rm(packageRoot, { force: true, recursive: true }),
rm(consumerRoot, { force: true, recursive: true }),
]);
}
});
});
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"rootDir": "./src",
"outDir": "./dist",
"removeComments": true,
"noEmit": false,
Expand Down