Skip to content

Commit

Permalink
Add filter by predicate (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
kibertoad authored May 30, 2024
1 parent c19aed0 commit 5863929
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 117 deletions.
2 changes: 0 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,4 @@ updates:
interval: daily
open-pull-requests-limit: 10
ignore:
- dependency-name: '@typescript-eslint/eslint-plugin'
- dependency-name: '@typescript-eslint/parser'
- dependency-name: '@types/node'
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ web_modules/
# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

Expand Down
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,60 @@ const awilixManager = new AwilixManager({
asyncDispose: true,
})

// This will return dependency1 and dependency2
// This will return a record with dependency1 and dependency2
const result1 = awilixManager.getWithTags(diContainer, ['queue'])
// This will return only dependency2
// This will return a record with only dependency2
const result2 = awilixManager.getWithTags(diContainer, ['queue', 'low-priority'])
```

## Fetching dependencies based on a predicate

In some cases you may want to get dependencies based on whether they satisfy some condition.
You can use `getByPredicate` method for that:

```js
import { AwilixManager } from 'awilix-manager'
import { asClass, createContainer } from 'awilix'

const diContainer = createContainer({
injectionMode: 'PROXY',
})

class QueueConsumerHighPriorityClass {
}

class QueueConsumerLowPriorityClass {
}

diContainer.register(
'dependency1',
asClass(QueueConsumerHighPriorityClass, {
lifetime: 'SINGLETON',
asyncInit: true,
}),
)
diContainer.register(
'dependency2',
asClass(QueueConsumerLowPriorityClass, {
lifetime: 'SINGLETON',
asyncInit: true,
}),
)

const awilixManager = new AwilixManager({
diContainer,
asyncInit: true,
asyncDispose: true,
})

// This will return a record with dependency1
const result1 = awilixManager.getByPredicate((entry) => entry instanceof QueueConsumerHighPriorityClass)
// This will return a record with dependency2
const result2 = awilixManager.getByPredicate((entry) => entry instanceof QueueConsumerLowPriorityClass))
```

Note that this will resolve all non-disabled dependencies within the container, even the ones without eager injection enabled.

## 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:
Expand Down
10 changes: 9 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"files": {
"ignore": ["coverage", "dist", "node_modules"]
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
Expand All @@ -13,13 +16,18 @@
"linter": {
"rules": {
"style": {
"recommended": true,
"noNonNullAssertion": "off"
},
"correctness": {},
"correctness": {
"recommended": true
},
"suspicious": {
"recommended": true,
"noExplicitAny": "off"
},
"complexity": {
"recommended": true,
"useOptionalChain": "off"
}
}
Expand Down
83 changes: 54 additions & 29 deletions lib/awilixManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {
type DisposableResolver,
asClass,
} from 'awilix'
import type { Resolver } from 'awilix/lib/resolvers'

declare module 'awilix' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface ResolverOptions<T> {
asyncInit?: boolean | string
asyncInitPriority?: number // lower means it gets initted earlier
Expand Down Expand Up @@ -67,10 +65,13 @@ export class AwilixManager {
await asyncDispose(this.config.diContainer)
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
getWithTags(diContainer: AwilixContainer, tags: string[]): Record<string, any> {
return getWithTags(diContainer, tags)
}

getByPredicate(predicate: (entity: any) => boolean): Record<string, any> {
return getByPredicate(this.config.diContainer, predicate)
}
}

export async function asyncInit(diContainer: AwilixContainer) {
Expand All @@ -91,51 +92,75 @@ export async function asyncInit(diContainer: AwilixContainer) {
return key1.localeCompare(key2)
})

for (const entry of dependenciesWithAsyncInit) {
const resolvedValue = diContainer.resolve(entry[0])
if (entry[1].asyncInit === true) {
for (const [key, description] of dependenciesWithAsyncInit) {
const resolvedValue = diContainer.resolve(key)
if (description.asyncInit === true) {
await resolvedValue.asyncInit(diContainer.cradle)
} else {
// @ts-ignore
await resolvedValue[entry[1].asyncInit](diContainer.cradle)
await resolvedValue[description.asyncInit](diContainer.cradle)
}
}
}

export function eagerInject(diContainer: AwilixContainer) {
const dependenciesWithEagerInject = Object.entries(diContainer.registrations).filter((entry) => {
return entry[1].eagerInject && entry[1].enabled !== false
})

for (const entry of dependenciesWithEagerInject) {
const resolvedComponent = diContainer.resolve(entry[0])
if (typeof entry[1].eagerInject === 'string') {
resolvedComponent[entry[1].eagerInject]()
const dependenciesWithEagerInject = Object.entries(diContainer.registrations).filter(
([_key, description]) => {
return description.eagerInject && description.enabled !== false
},
)

for (const [key, description] of dependenciesWithEagerInject) {
const resolvedComponent = diContainer.resolve(key)
if (typeof description.eagerInject === 'string') {
resolvedComponent[description.eagerInject]()
}
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function getWithTags(diContainer: AwilixContainer, tags: string[]): Record<string, any> {
const dependenciesWithTags = Object.entries(diContainer.registrations).filter((entry) => {
return (
entry[1].enabled !== false && tags.every((v) => entry[1].tags && entry[1].tags.includes(v))
)
})
const dependenciesWithTags = Object.entries(diContainer.registrations).filter(
([_key, description]) => {
return (
description.enabled !== false &&
tags.every((v) => description.tags && description.tags.includes(v))
)
},
)

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const resolvedComponents: Record<string, any> = {}
for (const entry of dependenciesWithTags) {
resolvedComponents[entry[0]] = diContainer.resolve(entry[0])
for (const [key] of dependenciesWithTags) {
resolvedComponents[key] = diContainer.resolve(key)
}

return resolvedComponents
}

export function getByPredicate(
diContainer: AwilixContainer,
predicate: (entity: any) => boolean,
): Record<string, any> {
const enabledDependencies = Object.entries(diContainer.registrations).filter(
([_key, description]) => {
return description.enabled !== false
},
)

const resolvedComponents: Record<string, any> = {}
for (const [key] of enabledDependencies) {
const resolvedElement = diContainer.resolve(key)
if (predicate(resolvedElement)) {
resolvedComponents[key] = resolvedElement
}
}

return resolvedComponents
}

export async function asyncDispose(diContainer: AwilixContainer) {
const dependenciesWithAsyncDispose = Object.entries(diContainer.registrations)
.filter((entry) => {
return entry[1].asyncDispose && entry[1].enabled !== false
.filter(([_key, description]) => {
return description.asyncDispose && description.enabled !== false
})
.sort((entry1, entry2) => {
const [key1, resolver1] = entry1
Expand All @@ -150,10 +175,10 @@ export async function asyncDispose(diContainer: AwilixContainer) {
return key1.localeCompare(key2)
})

for (const entry of dependenciesWithAsyncDispose) {
const resolvedValue = diContainer.resolve(entry[0])
for (const [key, description] of dependenciesWithAsyncDispose) {
const resolvedValue = diContainer.resolve(key)

const asyncDispose = entry[1].asyncDispose
const asyncDispose = description.asyncDispose

if (typeof asyncDispose === 'function') {
await asyncDispose(resolvedValue)
Expand Down
80 changes: 35 additions & 45 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,47 +1,37 @@
{
"name": "awilix-manager",
"description": "Wrapper over awilix to support more complex use-cases, such as async init and eager injection",
"version": "5.2.1",
"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 lint index.ts lib test biome.json && tsc --project tsconfig.lint.json --noEmit",
"lint:fix": "biome check --apply index.ts lib test biome.json",
"prepublishOnly": "npm run build:release"
},
"peerDependencies": {
"awilix": ">=9.0.0"
},
"devDependencies": {
"@biomejs/biome": "^1.5.3",
"@types/node": "^20.11.16",
"@vitest/coverage-v8": "^1.2.2",
"del-cli": "^5.0.0",
"typescript": "^5.3.3",
"vitest": "^1.2.2"
},
"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.2.1",
"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/*"]
}
Loading

0 comments on commit 5863929

Please sign in to comment.