diff --git a/.changeset/flat-parents-design.md b/.changeset/flat-parents-design.md new file mode 100644 index 00000000..98005ca2 --- /dev/null +++ b/.changeset/flat-parents-design.md @@ -0,0 +1,5 @@ +--- +"@inversifyjs/core": patch +--- + +Removed unexpected `LegacyQueryableString` diff --git a/.changeset/large-walls-smell.md b/.changeset/large-walls-smell.md new file mode 100644 index 00000000..3ab5abd2 --- /dev/null +++ b/.changeset/large-walls-smell.md @@ -0,0 +1,5 @@ +--- +"@inversifyjs/core": minor +--- + +Added `targetName` decorator diff --git a/.changeset/thirty-nails-smile.md b/.changeset/thirty-nails-smile.md new file mode 100644 index 00000000..c1a2ecf3 --- /dev/null +++ b/.changeset/thirty-nails-smile.md @@ -0,0 +1,5 @@ +--- +"@inversifyjs/core": patch +--- + +Updated `BindToFluentSyntaxImplementation.to` to set binding scope if found in class metadata diff --git a/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.spec.ts b/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.spec.ts index 319e221f..895a8aa5 100644 --- a/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.spec.ts +++ b/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.spec.ts @@ -1,5 +1,7 @@ import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; +jest.mock('@inversifyjs/core'); + jest.mock('../actions/getBindingId'); jest.mock('../calculations/isAnyAncestorBindingMetadata'); jest.mock('../calculations/isAnyAncestorBindingMetadataWithName'); @@ -22,9 +24,11 @@ import { bindingScopeValues, BindingType, bindingTypeValues, + ClassMetadata, ConstantValueBinding, DynamicValueBuilder, Factory, + getClassMetadata, InstanceBinding, MetadataName, MetadataTag, @@ -35,6 +39,7 @@ import { } from '@inversifyjs/core'; import { Writable } from '../../common/models/Writable'; +import { ClassMetadataFixtures } from '../../metadata/fixtures/ClassMetadataFixtures'; import { getBindingId } from '../actions/getBindingId'; import { isAnyAncestorBindingMetadata } from '../calculations/isAnyAncestorBindingMetadata'; import { isAnyAncestorBindingMetadataWithName } from '../calculations/isAnyAncestorBindingMetadataWithName'; @@ -152,8 +157,6 @@ describe(BindInFluentSyntaxImplementation.name, () => { }); describe(BindToFluentSyntaxImplementation.name, () => { - class Foo {} - let bindingIdFixture: number; let dynamicValueBuilderfixture: DynamicValueBuilder; @@ -204,30 +207,6 @@ describe(BindToFluentSyntaxImplementation.name, () => { NewableFunction, ] >([ - [ - '.to()', - ( - bindToFluentSyntaxImplementation: BindToFluentSyntaxImplementation, - ): unknown => bindToFluentSyntaxImplementation.to(Foo), - (): Binding => ({ - cache: { - isRight: false, - value: undefined, - }, - id: bindingIdFixture, - implementationType: Foo, - isSatisfiedBy: expect.any(Function) as unknown as ( - metadata: BindingMetadata, - ) => boolean, - moduleId: containerModuleIdFixture, - onActivation: undefined, - onDeactivation: undefined, - scope: defaultScopeFixture, - serviceIdentifier: serviceIdentifierFixture, - type: bindingTypeValues.Instance, - }), - BindWhenOnFluentSyntaxImplementation, - ], [ '.toConstantValue()', ( @@ -452,10 +431,14 @@ describe(BindToFluentSyntaxImplementation.name, () => { ); }); - describe('when called', () => { + describe('when called, and getClassMetadata() returns ClassMetadata with undefined scope', () => { let result: unknown; beforeAll(() => { + ( + getClassMetadata as jest.Mock + ).mockReturnValueOnce(ClassMetadataFixtures.withScopeUndefined); + result = bindToFluentSyntaxImplementation.toSelf(); }); @@ -463,6 +446,13 @@ describe(BindToFluentSyntaxImplementation.name, () => { jest.clearAllMocks(); }); + it('should call getClassMetadata()', () => { + expect(getClassMetadata).toHaveBeenCalledTimes(1); + expect(getClassMetadata).toHaveBeenCalledWith( + serviceIdentifierFixture, + ); + }); + it('should call callback()', () => { const expectedBinding: InstanceBinding = { cache: { @@ -490,6 +480,60 @@ describe(BindToFluentSyntaxImplementation.name, () => { expect(result).toBeInstanceOf(BindInWhenOnFluentSyntaxImplementation); }); }); + + describe('when called, and getClassMetadata() returns ClassMetadata with scope', () => { + let classMetadataFixture: ClassMetadata; + + let result: unknown; + + beforeAll(() => { + classMetadataFixture = ClassMetadataFixtures.withScopeRequest; + + ( + getClassMetadata as jest.Mock + ).mockReturnValueOnce(classMetadataFixture); + + result = bindToFluentSyntaxImplementation.toSelf(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it('should call getClassMetadata()', () => { + expect(getClassMetadata).toHaveBeenCalledTimes(1); + expect(getClassMetadata).toHaveBeenCalledWith( + serviceIdentifierFixture, + ); + }); + + it('should call callback()', () => { + const expectedBinding: InstanceBinding = { + cache: { + isRight: false, + value: undefined, + }, + id: getBindingId(), + implementationType: Foo, + isSatisfiedBy: expect.any(Function) as unknown as ( + metadata: BindingMetadata, + ) => boolean, + moduleId: containerModuleIdFixture, + onActivation: undefined, + onDeactivation: undefined, + scope: classMetadataFixture.scope as BindingScope, + serviceIdentifier: serviceIdentifierFixture, + type: bindingTypeValues.Instance, + }; + + expect(callbackMock).toHaveBeenCalledTimes(1); + expect(callbackMock).toHaveBeenCalledWith(expectedBinding); + }); + + it('should return expected result', () => { + expect(result).toBeInstanceOf(BindInWhenOnFluentSyntaxImplementation); + }); + }); }); }); diff --git a/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.ts b/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.ts index 3cbbaae6..0a6c8e49 100644 --- a/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.ts +++ b/packages/container/libraries/container/src/binding/models/BindingFluentSyntaxImplementation.ts @@ -8,11 +8,13 @@ import { bindingScopeValues, BindingType, bindingTypeValues, + ClassMetadata, ConstantValueBinding, DynamicValueBinding, DynamicValueBuilder, Factory, FactoryBinding, + getClassMetadata, InstanceBinding, MetadataName, MetadataTag, @@ -96,6 +98,8 @@ export class BindToFluentSyntaxImplementation } public to(type: Newable): BindInWhenOnFluentSyntax { + const classMetadata: ClassMetadata = getClassMetadata(type); + const binding: InstanceBinding = { cache: { isRight: false, @@ -107,7 +111,7 @@ export class BindToFluentSyntaxImplementation moduleId: this.#containerModuleId, onActivation: undefined, onDeactivation: undefined, - scope: this.#defaultScope, + scope: classMetadata.scope ?? this.#defaultScope, serviceIdentifier: this.#serviceIdentifier, type: bindingTypeValues.Instance, }; @@ -124,25 +128,7 @@ export class BindToFluentSyntaxImplementation ); } - const binding: InstanceBinding = { - cache: { - isRight: false, - value: undefined, - }, - id: getBindingId(), - implementationType: this.#serviceIdentifier as Newable, - isSatisfiedBy: BindingConstraintUtils.always, - moduleId: this.#containerModuleId, - onActivation: undefined, - onDeactivation: undefined, - scope: this.#defaultScope, - serviceIdentifier: this.#serviceIdentifier, - type: bindingTypeValues.Instance, - }; - - this.#callback(binding); - - return new BindInWhenOnFluentSyntaxImplementation(binding); + return this.to(this.#serviceIdentifier as Newable); } public toConstantValue(value: T): BindWhenOnFluentSyntax { diff --git a/packages/container/libraries/container/src/metadata/fixtures/ClassMetadataFixtures.ts b/packages/container/libraries/container/src/metadata/fixtures/ClassMetadataFixtures.ts new file mode 100644 index 00000000..c65539c1 --- /dev/null +++ b/packages/container/libraries/container/src/metadata/fixtures/ClassMetadataFixtures.ts @@ -0,0 +1,35 @@ +import { bindingScopeValues, ClassMetadata } from '@inversifyjs/core'; + +export class ClassMetadataFixtures { + public static get any(): ClassMetadata { + const fixture: ClassMetadata = { + constructorArguments: [], + lifecycle: { + postConstructMethodName: undefined, + preDestroyMethodName: undefined, + }, + properties: new Map(), + scope: undefined, + }; + + return fixture; + } + + public static get withScopeRequest(): ClassMetadata { + const fixture: ClassMetadata = { + ...ClassMetadataFixtures.any, + scope: bindingScopeValues.Request, + }; + + return fixture; + } + + public static get withScopeUndefined(): ClassMetadata { + const fixture: ClassMetadata = { + ...ClassMetadataFixtures.any, + scope: undefined, + }; + + return fixture; + } +} diff --git a/packages/container/libraries/core/src/index.ts b/packages/container/libraries/core/src/index.ts index a56fd30f..f9f1ea74 100644 --- a/packages/container/libraries/core/src/index.ts +++ b/packages/container/libraries/core/src/index.ts @@ -38,6 +38,7 @@ import { optional } from './metadata/decorators/optional'; import { postConstruct } from './metadata/decorators/postConstruct'; import { preDestroy } from './metadata/decorators/preDestroy'; import { tagged } from './metadata/decorators/tagged'; +import { targetName } from './metadata/decorators/targetName'; import { unmanaged } from './metadata/decorators/unmanaged'; import { ClassElementMetadata } from './metadata/models/ClassElementMetadata'; import { ClassElementMetadataKind } from './metadata/models/ClassElementMetadataKind'; @@ -71,7 +72,6 @@ import { OptionalGetOptions } from './resolution/models/OptionalGetOptions'; import { ResolutionContext } from './resolution/models/ResolutionContext'; import { ResolutionParams } from './resolution/models/ResolutionParams'; import { Resolved } from './resolution/models/Resolved'; -import { LegacyQueryableString } from './string/models/LegacyQueryableString'; export type { BaseBinding, @@ -98,7 +98,6 @@ export type { GetOptionsTagConstraint, InstanceBinding, LeafBindingNode, - LegacyQueryableString, ManagedClassElementMetadata, MetadataName, MetadataTag, @@ -145,5 +144,6 @@ export { resolveModuleDeactivations, resolveServiceDeactivations, tagged, + targetName, unmanaged, }; diff --git a/packages/container/libraries/core/src/string/models/LegacyQueryableString.ts b/packages/container/libraries/core/src/string/models/LegacyQueryableString.ts deleted file mode 100644 index bb1a1a94..00000000 --- a/packages/container/libraries/core/src/string/models/LegacyQueryableString.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface LegacyQueryableString { - startsWith(searchString: string): boolean; - endsWith(searchString: string): boolean; - contains(searchString: string): boolean; - equals(compareString: string): boolean; - value(): string; -} diff --git a/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.spec.ts b/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.spec.ts deleted file mode 100644 index 00b63473..00000000 --- a/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { beforeAll, describe, expect, it } from '@jest/globals'; - -import { LegacyQueryableStringImpl } from './LegacyQueryableStringImpl'; - -describe(LegacyQueryableStringImpl.name, () => { - let stringFixture: string; - let legacyQueryableStringImpl: LegacyQueryableStringImpl; - - beforeAll(() => { - stringFixture = 'string-fixture'; - - legacyQueryableStringImpl = new LegacyQueryableStringImpl(stringFixture); - }); - - describe('.startsWith', () => { - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = legacyQueryableStringImpl.startsWith(stringFixture); - }); - - it('should return expected result', () => { - expect(result).toBe(true); - }); - }); - }); - - describe('.endsWith', () => { - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = legacyQueryableStringImpl.endsWith(stringFixture); - }); - - it('should return expected result', () => { - expect(result).toBe(true); - }); - }); - }); - - describe('.contains', () => { - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = legacyQueryableStringImpl.contains(stringFixture); - }); - - it('should return expected result', () => { - expect(result).toBe(true); - }); - }); - }); - - describe('.equals', () => { - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = legacyQueryableStringImpl.equals(stringFixture); - }); - - it('should return expected result', () => { - expect(result).toBe(true); - }); - }); - }); - - describe('.value', () => { - describe('when called', () => { - let result: unknown; - - beforeAll(() => { - result = legacyQueryableStringImpl.value(); - }); - - it('should return expected result', () => { - expect(result).toBe(stringFixture); - }); - }); - }); -}); diff --git a/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.ts b/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.ts deleted file mode 100644 index b3438f58..00000000 --- a/packages/container/libraries/core/src/string/models/LegacyQueryableStringImpl.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { LegacyQueryableString } from './LegacyQueryableString'; - -export class LegacyQueryableStringImpl implements LegacyQueryableString { - readonly #str: string; - - constructor(str: string) { - this.#str = str; - } - - public startsWith(searchString: string): boolean { - return this.#str.startsWith(searchString); - } - - public endsWith(searchString: string): boolean { - return this.#str.endsWith(searchString); - } - - public contains(searchString: string): boolean { - return this.#str.includes(searchString); - } - - public equals(compareString: string): boolean { - return this.#str === compareString; - } - - public value(): string { - return this.#str; - } -}