diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c577bd2..05298f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 20.x, 21.x] + node-version: [16.x, 18.x, 20.x, 22.x] steps: - name: Checkout Repository diff --git a/README.md b/README.md index 72f18e1..672c4fc 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ Note that this will resolve all non-disabled dependencies within the container, ## Mocking dependencies -Sometimes you may want to intentionally inject objects that do not fully conform to the type definition of an original class. For that you can use `asMockClass` resolver: +Sometimes you may want to intentionally inject objects that do not fully conform to the type definition of an original class. For that you can use `asMockClass` or `asMockFunction` resolvers: ```ts type DiContainerType = { diff --git a/biome.json b/biome.json index ddbf6d2..2f461ad 100644 --- a/biome.json +++ b/biome.json @@ -1,35 +1,22 @@ { - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "files": { - "ignore": ["coverage", "dist", "node_modules"] - }, - "javascript": { - "formatter": { - "arrowParentheses": "always", - "indentStyle": "space", - "semicolons": "asNeeded", - "trailingComma": "all", - "lineWidth": 100, - "quoteStyle": "single" - } - }, - "linter": { - "rules": { - "style": { - "recommended": true, - "noNonNullAssertion": "off" - }, - "correctness": { - "recommended": true - }, - "suspicious": { - "recommended": true, - "noExplicitAny": "off" - }, - "complexity": { - "recommended": true, - "useOptionalChain": "off" - } - } - } + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "extends": ["./node_modules/@kibertoad/biome-config/configs/biome-package.json"], + "overrides": [ + { + "include": ["**/*.ts"], + "linter": { + "rules": { + "performance": { + "noBarrelFile": "off" + }, + "complexity": { + "useOptionalChain": "off" + }, + "suspicious": { + "noExplicitAny": "off" + } + } + } + } + ] } diff --git a/index.ts b/index.ts index c63bf90..2be551d 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,7 @@ export { AwilixManager, asMockClass, + asMockFunction, eagerInject, asyncInit, asyncDispose, diff --git a/lib/awilixManager.ts b/lib/awilixManager.ts index b675a57..9c901b3 100644 --- a/lib/awilixManager.ts +++ b/lib/awilixManager.ts @@ -5,7 +5,9 @@ import { type Constructor, type DisposableResolver, asClass, + asFunction, } from 'awilix' +import type { FunctionReturning } from 'awilix/lib/container' declare module 'awilix' { interface ResolverOptions { @@ -34,6 +36,13 @@ export function asMockClass( return asClass(Type as Constructor, opts) } +export function asMockFunction( + fn: FunctionReturning, + opts?: BuildResolverOptions, +): BuildResolver & DisposableResolver { + return asFunction(fn as FunctionReturning, opts) +} + export class AwilixManager { public readonly config: AwilixManagerConfig diff --git a/package.json b/package.json index e25e536..2223eea 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,38 @@ { - "name": "awilix-manager", - "description": "Wrapper over awilix to support more complex use-cases, such as async init and eager injection", - "version": "5.3.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsc", - "build:release": "del-cli dist && del-cli coverage && npm run lint && npm run build", - "test": "vitest", - "test:coverage": "npm test -- --coverage", - "lint": "biome check . && tsc --project tsconfig.lint.json --noEmit", - "lint:fix": "biome check --apply .", - "prepublishOnly": "npm run build:release" - }, - "peerDependencies": { - "awilix": ">=9.0.0" - }, - "devDependencies": { - "@biomejs/biome": "^1.7.3", - "@types/node": "^20.12.13", - "@vitest/coverage-v8": "^1.6.0", - "del-cli": "^5.1.0", - "typescript": "^5.4.5", - "vitest": "^1.6.0" - }, - "engines": { - "node": ">=16" - }, - "repository": { - "type": "git", - "url": "git://github.com/kibertoad/awilix-manager.git" - }, - "keywords": ["init", "async", "eager", "awilix", "di"], - "homepage": "https://github.com/kibertoad/awilix-manager", - "files": ["README.md", "LICENSE", "dist/*"] + "name": "awilix-manager", + "description": "Wrapper over awilix to support more complex use-cases, such as async init and eager injection", + "version": "5.3.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "build:release": "del-cli dist && del-cli coverage && npm run lint && npm run build", + "test": "vitest", + "test:coverage": "npm test -- --coverage", + "lint": "biome check . && tsc --project tsconfig.lint.json --noEmit", + "lint:fix": "biome check --write .", + "prepublishOnly": "npm run build:release" + }, + "peerDependencies": { + "awilix": ">=9.0.0" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.2", + "@kibertoad/biome-config": "^1.2.0", + "@types/node": "^20.14.8", + "@vitest/coverage-v8": "^1.6.0", + "del-cli": "^5.1.0", + "typescript": "^5.5.2", + "vitest": "^1.6.0" + }, + "engines": { + "node": ">=16" + }, + "repository": { + "type": "git", + "url": "git://github.com/kibertoad/awilix-manager.git" + }, + "keywords": ["init", "async", "eager", "awilix", "di"], + "homepage": "https://github.com/kibertoad/awilix-manager", + "files": ["README.md", "LICENSE", "dist/*"] } diff --git a/test/awilixManager.spec.ts b/test/awilixManager.spec.ts index 84c09f8..7580523 100644 --- a/test/awilixManager.spec.ts +++ b/test/awilixManager.spec.ts @@ -5,6 +5,7 @@ import type { Resolver } from 'awilix/lib/resolvers' import { AwilixManager, asMockClass, + asMockFunction, asyncDispose, asyncInit, getByPredicate, @@ -112,6 +113,33 @@ describe('asMockClass', () => { }) }) +describe('asMockFunction', () => { + it('Supports passing a mock instance that does not fully implement the real class', () => { + type DiContainerType = { + asyncInitClass: AsyncInitClass + asyncInitClass2: AsyncInitClass + } + const diConfiguration: NameAndRegistrationPair = { + asyncInitClass: asClass(AsyncInitClass), + asyncInitClass2: asMockFunction(() => { + return new AsyncDisposeClass() + }), + } + + const diContainer = createContainer({ + injectionMode: 'PROXY', + }) + + for (const [dependencyKey, dependencyValue] of Object.entries(diConfiguration)) { + diContainer.register(dependencyKey, dependencyValue as Resolver) + } + + const { asyncInitClass, asyncInitClass2 } = diContainer.cradle + expect(asyncInitClass).toBeInstanceOf(AsyncInitClass) + expect(asyncInitClass2).toBeInstanceOf(AsyncDisposeClass) + }) +}) + describe('awilixManager', () => { describe('constructor', () => { it('throws an error if strictBooleanEnforced is set and undefined is passed', () => { @@ -338,7 +366,7 @@ describe('awilixManager', () => { expect(expectedItemNotFound).toStrictEqual({}) }) - it('execute awilixManager.getWithTags on registered dependencies with valid tags', async () => { + it('execute awilixManager.getWithTags on registered dependencies with valid tags', () => { const diContainer = createContainer({ injectionMode: 'PROXY', }) @@ -383,7 +411,7 @@ describe('awilixManager', () => { }) }) - it('does bit execute asyncInit on registered dependencies if disabled', async () => { + it('does not execute asyncInit on registered dependencies if disabled', async () => { const diContainer = createContainer({ injectionMode: 'PROXY', }) @@ -449,7 +477,7 @@ describe('awilixManager', () => { }) await manager.executeInit() - const { dependency1, dependency2 } = diContainer.cradle + const { dependency1: _1, dependency2: _2 } = diContainer.cradle expect(isInittedGlobal).toBe(true) }) @@ -482,7 +510,7 @@ describe('awilixManager', () => { }) await manager.executeInit() - const { dependency1, dependency2 } = diContainer.cradle + const { dependency1: _1, dependency2: _2 } = diContainer.cradle expect(isInittedGlobal).toBe(true) }) @@ -534,8 +562,9 @@ describe('awilixManager', () => { 'dependency1', asClass(AsyncDisposeClass, { lifetime: 'SINGLETON', - asyncDispose: async (instance) => { + asyncDispose: (instance) => { instance.isDisposed = true + return Promise.resolve() }, }), ) @@ -669,7 +698,7 @@ describe('awilixManager', () => { await asyncDispose(diContainer) - const { dependency1, dependency2 } = diContainer.cradle + const { dependency1: _1, dependency2: _2 } = diContainer.cradle expect(isDisposedGlobal).toBe(true) }) @@ -698,7 +727,7 @@ describe('awilixManager', () => { await asyncDispose(diContainer) - const { dependency1, dependency2 } = diContainer.cradle + const { dependency1: _1, dependency2: _2 } = diContainer.cradle expect(isDisposedGlobal).toBe(true) }) diff --git a/test/testContext.ts b/test/testContext.ts index fbaa4b2..186477e 100644 --- a/test/testContext.ts +++ b/test/testContext.ts @@ -1,6 +1,6 @@ -import type { AwilixContainer } from 'awilix' +import type { AwilixContainer, NameAndRegistrationPair } from 'awilix' import { Lifetime, asClass } from 'awilix' -import type { Resolver } from 'awilix/lib/resolvers' +import '../lib/awilixManager' export const SINGLETON_CONFIG = { lifetime: Lifetime.SINGLETON } @@ -27,7 +27,7 @@ export function registerDependencies( } } -type DiConfig = Record> +type DiConfig = NameAndRegistrationPair export interface Dependencies { module: SomeModule