Skip to content
This repository was archived by the owner on Apr 21, 2024. It is now read-only.
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
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,51 @@ import { ... } from 'https://deno.land/x/lambda_ioc@[VERSION]/lambda-ioc/deno/in
## Example

```ts
import { createContainer } from '@coderspirit/lambda-ioc'
import {
constructor,
createContainer,
func
} from '@coderspirit/lambda-ioc'

function printNameAndAge(name: string, age: number) {
console.log(`${name} is aged ${age}`)
}

class Person {
constructor(
public readonly age: number,
public readonly name: string
) {}
}
const container = createContainer()
.registerValue('someAge', 5)
.registerValue('someName', 'Timmy')
// We can register functions
.register('fn', func(printNameAndAge, 'someName', 'someAge'))
// And constructors too
.register('Person', constructor(Person, 'someAge', 'someName'))
// For now it's always async, we'll improve its API to decide when to expose
// the registered dependencies synchronously or asynchronoyusly in a smart way.
const print = await container.resolve('fn')
const print = container.resolve('fn')
print() // Prints "Timmy is aged 5"

const person = container.resolve('Person')
console.print(person.age) // Prints "5"
console.print(person.name) // Prints "Timmy"
```

It is also possible to register and resolve asynchronous factories and
dependencies. They are not documented yet because some "helpers" are missing,
and therefore it's a bit more annoying to take advantage of that feature.

If you are curious, just try out:
- `registerAsync`
- `resolveAsync`

## Differences respect to Diddly

- First-class support for Deno.
- First-class support for asynchronous dependency resolution.
- The container interface has been split into `ReaderContainer` and
`WriterContainer`, making it easier to use precise types.
- More extense documentation.
61 changes: 34 additions & 27 deletions lambda-ioc/deno/combinators.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
ContainerKey,
DependencyFactory,
ReadableContainer,
SyncDependencyFactory,
} from './container.ts';
import { ParamsToResolverKeys, TupleO, Zip } from './util.ts';

Expand All @@ -13,13 +13,15 @@ export function singleton<
TVal,
TDependencies extends Record<ContainerKey, unknown>,
>(
factory: DependencyFactory<TVal, ReadableContainer<TDependencies>>,
): DependencyFactory<TVal, ReadableContainer<TDependencies>> {
let result: TVal | undefined
// eslint-disable-next-line @typescript-eslint/ban-types
factory: SyncDependencyFactory<TVal, ReadableContainer<TDependencies, {}>>,
// eslint-disable-next-line @typescript-eslint/ban-types
): SyncDependencyFactory<TVal, ReadableContainer<TDependencies, {}>> {
let result: Awaited<TVal> | undefined

return async (container) => {
return (container) => {
if (!result) {
result = await factory(container)
result = factory(container)
}
return result
}
Expand All @@ -37,17 +39,19 @@ export function func<
>(
fn: (...args: TParams) => Awaited<TReturn>,
...args: TDependencies
): DependencyFactory<() => TReturn, FuncContainer<TParams, TDependencies>> {
return async (container: FuncContainer<TParams, TDependencies>) => {
const argPromises = args.map((arg) =>
): SyncDependencyFactory<
() => TReturn,
SyncFuncContainer<TParams, TDependencies>
> {
return (container: SyncFuncContainer<TParams, TDependencies>) => {
const resolvedArgs = args.map((arg) =>
container.resolve(
// This is ugly as hell, but I did not want to apply ts-ignore
arg as Parameters<FuncContainer<TParams, TDependencies>['resolve']>[0],
arg as Parameters<
SyncFuncContainer<TParams, TDependencies>['resolve']
>[0],
),
)
const resolvedArgs = (await Promise.all(
argPromises as Promise<unknown>[],
)) as unknown as TParams
) as unknown as TParams

return () => fn(...resolvedArgs)
}
Expand All @@ -62,19 +66,18 @@ export function constructor<
TClass,
TDependencies extends ParamsToResolverKeys<TParams>,
>(
constructor: new (...args: TParams) => TClass,
constructor: new (...args: TParams) => Awaited<TClass>,
...args: TDependencies
): DependencyFactory<TClass, FuncContainer<TParams, TDependencies>> {
return async (container: FuncContainer<TParams, TDependencies>) => {
const argPromises = args.map((arg) =>
): SyncDependencyFactory<TClass, SyncFuncContainer<TParams, TDependencies>> {
return (container: SyncFuncContainer<TParams, TDependencies>) => {
const resolvedArgs = args.map((arg) =>
container.resolve(
// This is ugly as hell, but I did not want to apply ts-ignore
arg as Parameters<FuncContainer<TParams, TDependencies>['resolve']>[0],
arg as Parameters<
SyncFuncContainer<TParams, TDependencies>['resolve']
>[0],
),
)
const resolvedArgs = (await Promise.all(
argPromises as Promise<unknown>[],
)) as unknown as TParams
) as unknown as TParams

return new constructor(...resolvedArgs)
}
Expand All @@ -83,10 +86,14 @@ export function constructor<
// -----------------------------------------------------------------------------
// Private Types
// -----------------------------------------------------------------------------
type FuncContainer<
type SyncFuncContainer<
TParams extends readonly unknown[],
TDependencies extends ParamsToResolverKeys<TParams>,
TSyncDependencies extends ParamsToResolverKeys<TParams>,
> = ReadableContainer<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
TupleO<Extract<Zip<TDependencies, TParams>, readonly [ContainerKey, any][]>>
TupleO<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Extract<Zip<TSyncDependencies, TParams>, readonly [ContainerKey, any][]>
>,
// eslint-disable-next-line @typescript-eslint/ban-types
{}
>
Loading