import { forEach , getType } from '../utils' ; import { TraverseContext } from '../interface' ; export class Traversable < T > { private ctxgen : ( key : any , val : any , ctx : TraverseContext ) => T | boolean ; constructor ( private entities ) { this . ctxgen = ( ) => true ; } context ( fn ) { this . ctxgen = fn ; return this ; } keys ( target ) { switch ( typeof target ) { case 'string' : case 'boolean' : case 'number' : case 'undefined' : return [ ] ; default : if ( target instanceof Date ) { return [ ] ; } else if ( target && typeof target . keys === 'function' ) { return Array . from ( target . keys ( ) ) ; } else { return ( typeof target === 'object' && target !== null ) || Array . isArray ( target ) ? Object . keys ( target ) : [ ] ; } } } forEach ( eachFunc ) { const self = this ; let index = - 1 ; function walk ( node , path = [ ] , parents = [ ] ) { let advanced = true ; const children = self . keys ( node ) ; const parent = parents [ parents . length - 1 ] ; index ++ ; const defaultCtx = { isRoot : path . length === 0 , node , path : path , parent , children : children , key : path [ path . length - 1 ] , isLeaf : children . length === 0 , type : ( ) => getType ( node ) , skip : ( ) => advanced = false , index } ; const ret = self . ctxgen ( path [ path . length - 1 ] , node , defaultCtx ) ; if ( ret !== false ) { const ctx = typeof ret === 'object' ? { ... defaultCtx , ... ret } : defaultCtx ; eachFunc . call ( null , ctx , node ) ; } if ( ! advanced ) { return ; } else if ( ! defaultCtx . isLeaf ) { forEach ( node , ( val , key ) => { const nextPath = path . concat ( key ) ; const nextParents = Array . isArray ( node ) ? parents . concat ( [ node ] ) : parents . concat ( node ) ; walk ( val , nextPath , nextParents ) ; } ) ; } } walk ( this . entities ) ; } }