import * as utility from './util' ; export type Injector = { < T > ( Constructor ) : T ; later : ( fn : Function ) => number ; setOverride : ( Constructor : Function , instance : any ) => void ; } ; export type InjectorPartial = { < T > ( Constructor ) : ( T | undefined ) ; later ? : ( fn : Function ) => number ; setOverride ? : ( Constructor : Function , instance : any ) => void ; } ; export interface Constructor { new ( injector ) : Object ; } export interface InstanceMap extends Map < Constructor , any > { get < T > ( key ) : T | undefined ; set < T > ( key , value ) : this ; } interface MainExport { ( parent ? ) : Injector ; default : MainExport ; util : typeof utility ; } const createContainer = function ( parent ? ) { let parentInjector ; if ( parent instanceof Map ) { parentInjector = key => parent . get ( key ) ; } else if ( typeof parent === 'object' ) { parentInjector = key => parent [ key . name ] ; } else if ( typeof parent === 'function' ) { parentInjector = parent ; } else if ( typeof parent === 'undefined' ) { parentInjector = ( ) => undefined ; } else { throw new TypeError ( 'Parent injector must be a Map, object or function' ) ; } const cache = new Map ( ) ; const mapping = new Map ( ) ; let stack = new Set ( ) ; let queue = [ ] ; const construct = < T > ( Constructor ) : T => { const OverrideConstructor = mapping . get ( Constructor ) ; return new ( OverrideConstructor || Constructor ) ( reduct as Injector ) as T ; } ; const reduct = < T > ( Constructor ) : T => { if ( typeof Constructor !== 'function' ) { throw new TypeError ( 'Dependencies must be constructors/factories, but got: ' + typeof Constructor ) ; } if ( stack . has ( Constructor ) ) { const stackArray = utility . convertSetToArray ( stack ) ; stackArray . push ( Constructor ) ; const prettyStack = stackArray . map ( utility . printPrettyConstructor ) . join ( ' => ' ) ; throw new Error ( 'Circular dependency detected: ' + prettyStack ) ; } const cachedInstance = cache . get ( Constructor ) as T ; if ( cachedInstance ) { return cachedInstance ; } stack . add ( Constructor ) ; const instance = parentInjector ( Constructor ) || construct ( Constructor ) ; stack . delete ( Constructor ) ; cache . set ( Constructor , instance ) ; if ( queue . length ) { stack . add ( utility . printPrettyConstructor ( Constructor ) + ' (post)' ) ; const lastQueue = queue ; queue = [ ] ; lastQueue . forEach ( ( fn ) => fn ( ) ) ; } return instance ; } ; reduct . setOverride = ( Constructor , OverrideConstructor ) => { mapping . set ( Constructor , OverrideConstructor ) ; } ; reduct . later = ( fn ) => queue . push ( fn ) ; return reduct as Injector ; } as MainExport ; createContainer . default = createContainer ; createContainer . util = utility ; export default createContainer ; module . exports = createContainer ;