DI (dependency injection) lib. Cross-env with optional decorator API.
import { bindFactory } from './helpers'
import { Scope } from './Scope'
@injectable(Lifetime.SINGLETON)
class Db {
get(key: string) {
// some db adapter
}
}
@injectable()
class Service {
db = inject(DB)
userById(id: string) {
return this.db.get(id)
}
}
const module = new Module([
bindFactory(
() => ({
get() {
return {
id: '123',
name: 'John'
}
}
}),
DB
)
])
const scope = new Scope(module)
const mockedServiceInstance = scope.inject(Service)
mockedServiceInstance.userById('any key') // -> { id: '123', name: 'John' }
Inject provider in current scope, need to be used on provider initialization. Used outside scope will refer to globalScope.
function factory() {
return {
a: inject(Class1)
}
}
class Example {
a = inject(Class1)
constructor() {
this.b = inject(Class2)
}
method() {
// Injection outside of scope (running in globalScope)
const c = inject(Class3)
}
}
To provide type checking token
function is available.
import { bindFactory } from './helpers'
interface Config {
var: string
}
const config: Config = {
var: '123'
}
const token1 = token<Config>()
const token2 = token(config)
const token3 = token(config, 'config')
new Module([
bindValue(config, token1),
bindFactory(() => ({ var: '321' }), token2),
bindValue(config, token3),
])
// all three injections has same types
class Service {
c1 = inject(token1)
c2 = inject(token2)
c3 = inject(token3)
}
Lifecycle dispose hook in current scope.
class Service {
constructor() {
onDispose(() => {
return new Promise(res => {
setTimeout(res, 1000)
})
})
}
}
Trigger dispose in current scope.
import { onDispose } from './helpers'
// global scope dispose hook
onDispose(() => {
console.log('Say bye!')
})
// global scope dispose trigger
dispose()
Register class for globalModule
@injectable()
class Service {}
const serviceInstance = resolve(Service)
Mark method as onDispose
@injectable()
class Service {
constructor() {
// one way
onDispose(() => this.onDispose())
}
// secound way
@disposable
onDispose() {
console.log('disposed')
}
}
const serviceInstance = resolve(Service)
Module contains available providers to resolve
const module = new Module([
// clone entries from other module
otherModule,
// declare new entries or overite if exists in otherModule
bindClass(Service),
// or overite if already exists
bindClass(ServiceMock, Service)
])
Scope contain cached providers registered () in module. Gives possibility to dispose services created in scope.
const module = new Mdoule()
const scope = new Scope(module)
scope.inject(Service)
Hook triggered when scope is disposed.
@injectable()
class Service {
constructor() {
// scope.onDispose but for current scope
onDispose(() => {
console.log('disposed')
})
}
}
const scope = new Scope(globalModule)
scope.onDispose(() => {
console.log('disposed')
})
scope.dispose() // -> 'disposed'
Resolve value registered in module or cached in scope if exists.
class Service {
// inject of current scope
user = inject(User)
}
const module = new Module([Service])
const scope = new Scope(module)
const service = scope.inject(Service) // or module.resolve(Service, scope)
Dispose current scope, all registered onDispose hook will be triggered.
class Service {
constructor() {
onDispose(() => {
return new Promise(res => {
setTimeout(res, 1000)
})
})
}
}
const module = new Module([Service])
const scope = new Scope(module)
const service = scope.inject(Service) // create instance in cache
await scope.dispose() // wait for all onDispose hooks end
Services are created outside Scopes and require globally defined dependencies (by injectable). After resolution, instance is stored in declaration so if modules share same dependencies then will share also instance.
Default lifetime, cached per each scope. Disposed on scope.dispose()
Created each time when added by inject