Fix circular dependencies in migrator
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2021-09-21 13:42:06 -05:00
parent 074a3187eb
commit 5940b6e2b3
24 changed files with 2818 additions and 2729 deletions

View File

@@ -1,4 +1,4 @@
import {DependencyKey, InstanceRef, Instantiable, isInstantiable, StaticClass} from './types'
import {DependencyKey, InstanceRef, Instantiable, isInstantiable, StaticClass, TypedDependencyKey} from './types'
import {AbstractFactory} from './factory/AbstractFactory'
import {collect, Collection, globalRegistry, logIfDebugging} from '../util'
import {Factory} from './factory/Factory'
@@ -7,7 +7,7 @@ import {ClosureFactory} from './factory/ClosureFactory'
import NamedFactory from './factory/NamedFactory'
import SingletonFactory from './factory/SingletonFactory'
import {InvalidDependencyKeyError} from './error/InvalidDependencyKeyError'
import {ContainerBlueprint} from './ContainerBlueprint'
import {ContainerBlueprint, ContainerResolutionCallback} from './ContainerBlueprint'
export type MaybeFactory<T> = AbstractFactory<T> | undefined
export type MaybeDependency = any | undefined
@@ -17,18 +17,36 @@ export type ResolvedDependency = { paramIndex: number, key: DependencyKey, resol
* A container of resolve-able dependencies that are created via inversion-of-control.
*/
export class Container {
/**
* Given a Container instance, apply the ContainerBlueprint to it.
* @param container
*/
public static realizeContainer<T extends Container>(container: T): T {
ContainerBlueprint.getContainerBlueprint()
.resolve()
.map(factory => container.registerFactory(factory))
ContainerBlueprint.getContainerBlueprint()
.resolveConstructable()
.map((factory: StaticClass<AbstractFactory<any>, any>) => container.registerFactory(container.make(factory)))
ContainerBlueprint.getContainerBlueprint()
.resolveResolutionCallbacks()
.map((listener: {key: TypedDependencyKey<any>, callback: ContainerResolutionCallback<any>}) => {
container.onResolve(listener.key)
.then(value => listener.callback(value))
})
return container
}
/**
* Get the global instance of this container.
*/
public static getContainer(): Container {
const existing = <Container | undefined> globalRegistry.getGlobal('extollo/injector')
if ( !existing ) {
const container = new Container()
ContainerBlueprint.getContainerBlueprint()
.resolve()
.map(factory => container.registerFactory(factory))
const container = Container.realizeContainer(new Container())
globalRegistry.setGlobal('extollo/injector', container)
return container
}
@@ -48,6 +66,12 @@ export class Container {
*/
protected instances: Collection<InstanceRef> = new Collection<InstanceRef>()
/**
* Collection of callbacks waiting for a dependency key to be resolved.
* @protected
*/
protected waitingResolveCallbacks: Collection<{ key: DependencyKey, callback: (t: unknown) => unknown }> = new Collection<{key: DependencyKey; callback:(t: unknown) => unknown}>();
constructor() {
this.registerSingletonInstance<Container>(Container, this)
this.registerSingleton('injector', this)
@@ -172,6 +196,26 @@ export class Container {
return this.instances.where('key', '=', key).isNotEmpty()
}
/**
* Get a Promise that resolves the first time the given dependency key is resolved
* by the application. If it has already been resolved, the Promise will resolve immediately.
* @param key
*/
onResolve<T>(key: TypedDependencyKey<T>): Promise<T> {
if ( this.hasInstance(key) ) {
return new Promise<T>(res => res(this.make<T>(key)))
}
// Otherwise, we haven't instantiated an instance with this key yet,
// so put it onto the waitlist.
return new Promise<T>(res => {
this.waitingResolveCallbacks.push({
key,
callback: (res as (t: unknown) => unknown),
})
})
}
/**
* Returns true if the container has a factory for the given key.
* @param {DependencyKey} key
@@ -234,6 +278,15 @@ export class Container {
value: newInstance,
})
this.waitingResolveCallbacks = this.waitingResolveCallbacks.filter(waiter => {
if ( waiter.key === key ) {
waiter.callback(newInstance)
return false
}
return true
})
return newInstance
}