Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filter by predicate #34

Merged
merged 2 commits into from
May 30, 2024
Merged
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: 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)
kibertoad marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading