import { extendObservable , computed , action , isObservableProp , flow , toJS , } from 'mobx' ; import { getInstance , record , goto } from './di' ; import { isGenerator , assertKeyValid , throwError } from '../__internal' ; import { plugins } from './plugin' ; import { model as modelFn } from '../types' ; function getInstanceByOkeen ( transfer ) { function getInsByModel ( ) { const Neeko = function ( ) { } ; let ins = new Neeko ( ) ; ins = transfer ( Neeko . prototype , ins ) ; Neeko . prototype . $new = ( ) => getInstanceByOkeen ( transfer ) ; Object . keys ( Neeko . prototype ) . forEach ( ( method ) => { const boundedMethod = Neeko . prototype [ method ] . bind ( ins ) ; Object . keys ( Neeko . prototype [ method ] ) . forEach ( ( key ) => { boundedMethod [ key ] = Neeko . prototype [ method ] [ key ] ; } ) ; Neeko . prototype [ method ] = boundedMethod ; } ) ; return ins ; } const ins = getInstance < any > ( { } , getInsByModel ) ; return ins ; } function bindOptions ( ins , opts , cb ) { for ( const key in opts ) { assertKeyValid ( ins , key , ( ) => { cb ( key , opts ) ; } ) ; } } function transferState ( ins , _state = { } ) { let opts = _state ; if ( typeof _state === 'function' ) { opts = _state . call ( undefined ) ; } bindOptions ( ins , opts , ( key ) => { extendObservable ( ins , { [ key ] : opts [ key ] } ) ; } ) ; } function transferRef ( ins , _ref = { } ) { bindOptions ( ins , _ref , ( key ) => { ins [ key ] = _ref [ key ] ; } ) ; } function transferComputed ( ins , _computed = { } ) { bindOptions ( ins , _computed , ( key ) => { const computedKey = computed ( _computed [ key ] . bind ( ins ) ) ; Object . defineProperty ( ins , key , { get ( ) { return computedKey . get ( ) ; } , } ) ; } ) ; } function transferReducers ( proto ) { let updateTask = [ ] ; const update = function ( stateOrUpdater , nextTick = false ) { const ins = this ; let state = { } ; if ( typeof stateOrUpdater === 'function' ) { const tmpState = { } ; for ( const key in ins ) { if ( isObservableProp ( ins , key ) ) { Object . defineProperty ( tmpState , key , { get ( ) { if ( typeof state [ key ] === 'undefined' ) { state [ key ] = toJS ( ins [ key ] ) ; } return state [ key ] ; } , set ( v ) { state [ key ] = v ; } , } ) ; } } stateOrUpdater ( tmpState ) ; } else { state = stateOrUpdater ; } for ( const key in state ) { if ( ! isObservableProp ( ins , key ) ) { throwError ( `cannot update key (${ key }) not in state` ) ; } } const task = ( ) => { for ( const key in state ) { ins [ key ] = state [ key ] ; } } ; updateTask . push ( task ) ; const runAllTask = ( ) => { updateTask . forEach ( ( task ) => { task ( ) ; } ) ; updateTask = [ ] ; } ; const taskWithAction = action ( runAllTask . bind ( ins ) ) ; if ( nextTick ) { Promise . resolve ( ) . then ( ( ) => taskWithAction ( ) ) ; } else { taskWithAction ( ) ; } } ; proto . $update = update ; } export function isEffect ( fn ) { return typeof fn === 'function' && ! ! fn . __isEffect ; } export function setEffect ( ins , key , fn ) { const fixedFn = fn ; fixedFn . __isEffect = true ; Object . setPrototypeOf ( ins , { [ key ] : fn , } ) ; } export function setEffectProto ( proto , key , fn ) { const fixedFn = isGenerator ( fn ) ? flow ( fn ) : fn ; fixedFn . __isEffect = true ; proto [ key ] = fixedFn ; } function transferEffects ( proto , _effects = { } ) { bindOptions ( proto , _effects , ( key ) => { setEffectProto ( proto , key , _effects [ key ] ) ; } ) ; } function transferRecord ( prop , ins ) { extendObservable ( ins , { $unstable_recordLength : 0 } ) ; extendObservable ( ins , { $unstable_recordIndex : 0 } ) ; const $record = ( ) => { const { index , length } = record ( { blackList : [ '$unstable_recordLength' , '$unstable_recordIndex' ] , } ) ; ins . $update ( { $unstable_recordIndex : index , $unstable_recordLength : length , } ) ; } ; const $goto = ( fn ) => { const { success , index } = goto ( fn ) ; if ( success ) { ins . $update ( { $unstable_recordIndex : index , } ) ; } } ; prop . $unstable_record = $record ; prop . $unstable_goto = $goto ; } const model = ( options ) => { const ins = getInstanceByOkeen ( ( proto , ins ) => { transferReducers ( proto ) ; transferEffects ( proto , options . effects ) ; transferRecord ( proto , ins ) ; transferState ( ins , options . state ) ; transferComputed ( ins , options . computed ) ; transferRef ( ins , options . ref ) ; plugins . forEach ( ( { key , type , fn } ) => { if ( type === 'internal' ) { fn ( ins , options [ key ] ) ; } else { fn ( ins , options . plugins && options . plugins [ key ] ) ; } } ) ; return ins ; } ) ; return ins as any ; } ; export default model ;