import { _Callback , AsyncCallbackFn , CallbackFn , Context , PositionType } from "./types" ; import { WalkNode } from "./node" ; function filterByFilters < T extends CallbackFn > ( cb , node ) { return cb . filters . every ( f => f ( node ) ) ; } function filterByNodeType < T extends CallbackFn > ( cb , node ) { return ! cb . nodeTypeFilters . length || cb . nodeTypeFilters . indexOf ( node . nodeType ) !== - 1 ; } function filterByKey < T extends CallbackFn > ( cb , node ) { return cb . keyFilters . length === 0 || ( typeof node . key === 'string' && cb . keyFilters . indexOf ( node . key ) !== - 1 ) ; } function execCallbacks ( callbacks , node ) { for ( let cb of callbacks ) { cb . callback ( node ) ; node . executedCallbacks . push ( cb ) ; } } async function execCallbacksAsync ( callbacks , node ) { for ( let cb of callbacks ) { await cb . callback ( node ) ; node . executedCallbacks . push ( cb ) ; } } const execCallbacksAsyncInParallel = async ( callbacks , node ) : Promise < void > => { await Promise . all ( callbacks . map ( cb => Promise . resolve ( cb . callback ( node ) ) . then ( ( ) => { node . executedCallbacks . push ( cb ) ; } ) ) ) ; } ; export class _CallbackStacker < T extends CallbackFn , Rt > { constructor ( private ctx , private executor ) { } public static forSync < T extends CallbackFn > ( ctx ) { return new _CallbackStacker < CallbackFn , void > ( ctx , execCallbacks ) ; } public static forAsync < T extends CallbackFn > ( ctx ) { return new _CallbackStacker < CallbackFn , void > ( ctx , ctx . config . parallelizeAsyncCallbacks ? execCallbacksAsyncInParallel : execCallbacksAsync ) ; } private _matchCallbacks ( node , position ) { if ( ! this . ctx . config . runCallbacks ) return [ ] ; if ( node . isRoot && ! this . ctx . config . rootObjectCallbacks ) return [ ] ; let callbacks = this . ctx . callbacksByPosition [ position ] ; return ( callbacks || [ ] ) . map ( cb => cb as _Callback < T > ) . filter ( cb => filterByFilters ( cb , node ) ) . filter ( cb => filterByNodeType ( cb , node ) ) . filter ( cb => filterByKey ( cb , node ) ) ; } private lookup : { [ key ] : { trigger : number ; fn : ( ) => Rt ; } ; } = { } ; public pushToStack ( node , position ) { const lastChild = node . children [ node . children . length - 1 ] ; const callbacks = this . _matchCallbacks ( node , position ) ; this . lookup [ lastChild . id ] = { trigger : node . id , fn : ( ) => this . executor ( callbacks , node ) } ; } public executeOne ( node , position ) { const callbacks = this . _matchCallbacks ( node , position ) ; return this . executor ( callbacks , node ) ; } public * execute ( nodeId ) { let next = this . lookup [ nodeId ] ; delete this . lookup [ nodeId ] ; while ( next ) { yield next . fn ( ) ; const trigger = next . trigger ; next = this . lookup [ trigger ] ; delete this . lookup [ trigger ] ; } } }