Skip to content

Commit

Permalink
Merge pull request #184 from inversify/refactor/add-binding-fluent-sy…
Browse files Browse the repository at this point in the history
…ntax

Add binding fluent syntax
  • Loading branch information
notaphplover authored Dec 14, 2024
2 parents c180a12 + 155382e commit 53bbc0c
Show file tree
Hide file tree
Showing 9 changed files with 1,424 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/container/libraries/container/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"url": "https://github.com/inversify/monorepo/issues"
},
"description": "InversifyJs container",
"dependencies": {
"@inversifyjs/common": "workspace:*",
"@inversifyjs/core": "workspace:*",
"@inversifyjs/reflect-metadata-utils": "workspace:*"
},
"devDependencies": {
"@eslint/js": "9.17.0",
"@jest/globals": "29.7.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

jest.mock('@inversifyjs/reflect-metadata-utils');

import {
getReflectMetadata,
updateReflectMetadata,
} from '@inversifyjs/reflect-metadata-utils';

import { getBindingId } from './getBindingId';

describe(getBindingId.name, () => {
describe('when called, and getReflectMetadata() returns undefined', () => {
let result: unknown;

beforeAll(() => {
(
getReflectMetadata as jest.Mock<typeof getReflectMetadata>
).mockReturnValueOnce(0);

result = getBindingId();
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call getReflectMetadata()', () => {
expect(getReflectMetadata).toHaveBeenCalledTimes(1);
expect(getReflectMetadata).toHaveBeenCalledWith(
Object,
'@inversifyjs/container/bindingId',
);
});

it('should call updateReflectMetadata()', () => {
expect(updateReflectMetadata).toHaveBeenCalledTimes(1);
expect(updateReflectMetadata).toHaveBeenCalledWith(
Object,
'@inversifyjs/container/bindingId',
0,
expect.any(Function),
);
});

it('should return default id', () => {
expect(result).toBe(0);
});
});

describe('when called, and getReflectMetadata() returns Number.MAX_SAFE_INTEGER', () => {
let result: unknown;

beforeAll(() => {
(
getReflectMetadata as jest.Mock<typeof getReflectMetadata>
).mockReturnValueOnce(Number.MAX_SAFE_INTEGER);

result = getBindingId();
});

afterAll(() => {
jest.clearAllMocks();
});

it('should call getReflectMetadata()', () => {
expect(getReflectMetadata).toHaveBeenCalledTimes(1);
expect(getReflectMetadata).toHaveBeenCalledWith(
Object,
'@inversifyjs/container/bindingId',
);
});

it('should call updateReflectMetadata()', () => {
expect(updateReflectMetadata).toHaveBeenCalledTimes(1);
expect(updateReflectMetadata).toHaveBeenCalledWith(
Object,
'@inversifyjs/container/bindingId',
Number.MAX_SAFE_INTEGER,
expect.any(Function),
);
});

it('should return default id', () => {
expect(result).toBe(Number.MAX_SAFE_INTEGER);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
getReflectMetadata,
updateReflectMetadata,
} from '@inversifyjs/reflect-metadata-utils';

const ID_METADATA: string = '@inversifyjs/container/bindingId';

export function getBindingId(): number {
const bindingId: number =
getReflectMetadata<number>(Object, ID_METADATA) ?? 0;

if (bindingId === Number.MAX_SAFE_INTEGER) {
updateReflectMetadata(
Object,
ID_METADATA,
bindingId,
() => Number.MIN_SAFE_INTEGER,
);
} else {
updateReflectMetadata(
Object,
ID_METADATA,
bindingId,
(id: number) => id + 1,
);
}

return bindingId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Writable<T> = {
-readonly [TKey in keyof T]: T[TKey];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Newable, ServiceIdentifier } from '@inversifyjs/common';
import {
BindingActivation,
BindingDeactivation,
BindingMetadata,
DynamicValueBuilder,
Factory,
Provider,
ResolutionContext,
} from '@inversifyjs/core';

export interface BindToFluentSyntax<T> {
to(type: Newable<T>): BindInWhenOnFluentSyntax<T>;
toSelf(): BindInWhenOnFluentSyntax<T>;
toConstantValue(value: T): BindWhenOnFluentSyntax<T>;
toDynamicValue(builder: DynamicValueBuilder<T>): BindInWhenOnFluentSyntax<T>;
toFactory(
factory: T extends Factory<unknown>
? (context: ResolutionContext) => T
: never,
): BindWhenOnFluentSyntax<T>;
toProvider(
provider: T extends Provider<unknown>
? (context: ResolutionContext) => T
: never,
): BindWhenOnFluentSyntax<T>;
toService(service: ServiceIdentifier<T>): void;
}

export interface BindInFluentSyntax<T> {
inSingletonScope(): BindWhenOnFluentSyntax<T>;
inTransientScope(): BindWhenOnFluentSyntax<T>;
inRequestScope(): BindWhenOnFluentSyntax<T>;
}

export interface BindInWhenOnFluentSyntax<T>
extends BindInFluentSyntax<T>,
BindWhenOnFluentSyntax<T> {}

export interface BindOnFluentSyntax<T> {
onActivation(activation: BindingActivation<T>): BindWhenFluentSyntax<T>;
onDeactivation(deactivation: BindingDeactivation<T>): BindWhenFluentSyntax<T>;
}

export interface BindWhenOnFluentSyntax<T>
extends BindWhenFluentSyntax<T>,
BindOnFluentSyntax<T> {}

export interface BindWhenFluentSyntax<T> {
when(
constraint: (metadata: BindingMetadata) => boolean,
): BindOnFluentSyntax<T>;
}
Loading

0 comments on commit 53bbc0c

Please sign in to comment.