import { IObservableArray } from 'mobx' ; import { IType , IModelType , IArrayType , IStateTreeNode , types , ModelProperties , ModelInstanceType , ModelCreationType2 , ModelSnapshotType2 } from 'mobx-state-tree' ; /** Interface with all IModelType static and dynamic members,
  * callable to construct a specialized instance. */ export interface ModelInterface < PROPS extends ModelProperties , OTHERS , CustomC , CustomS > { new ( ) : IStateTreeNode & IModelType < PROPS , OTHERS , CustomC , CustomS > & ModelInstanceType < PROPS , OTHERS > ; } export let typeTag = '$' ; export function setTypeTag ( tag ? ) { typeTag = tag ; } function dummyGetter ( ) { } param model export function shim < PROPS extends ModelProperties , OTHERS , CustomC , CustomS > ( Model , Parent ? ) { function BaseClass ( ) { } if ( Parent && Parent . $proto ) { BaseClass . prototype = Parent . $proto ; BaseClass . prototype = new ( BaseClass as any ) ( ) ; BaseClass . prototype . $parent = Parent ; if ( BaseClass . prototype . $actions ) BaseClass . prototype . $actions = { } ; } else { BaseClass . prototype = { } ; } return ( BaseClass as any ) ; } /** Decorator for actions. By default the mst function treats methods as views. */ export function action ( target , key ) { ( target . $actions || ( target . $actions = { } ) ) [ key ] = true ; } interface MemberSpec < MemberType > { name : string ; value : MemberType ; } interface ClassyUnion < PROPS extends ModelProperties , OTHERS , CustomC , CustomS > extends IModelType < PROPS , OTHERS , CustomC , CustomS > { $proto : any ; $typeList : ( IModelType < any , any , any , any > | { dispatcher : ( ( snap : any ) => IModelType < any , any , any , any > ) ; } ) [ ] ; $typeTbl : { [ name ] : IModelType < any , any , any , any > ; } ; } const internalMembers = { constructor : true , $actions : true , $parent : true } ; export interface ClassyOptions { name ? : string ; sealed ? : boolean ; } function renameFunction ( func , name ) { Object . defineProperty ( func , 'name' , { value : name , writable : false } ) ; } try { renameFunction ( dummyGetter , 'dummy' ) ; } catch ( err ) { } const renamableFunctions = ( dummyGetter . name == 'dummy' ) ; param code param data param modelName export function mst < PROPS extends ModelProperties , OTHERS , CustomC , CustomS , TYPE > ( Code , Data , modelName ? , options ? ) { const viewList = [ ] ; const actionList = [ ] ; const descList = [ ] ; const memberTbl = Code . prototype ; const actionTbl = memberTbl . $actions ; const volatileTbl = { } ; if ( modelName && typeof ( modelName ) == 'object' ) { options = modelName ; modelName = options . name ; } else options = options || { } ; for ( let name of Object . getOwnPropertyNames ( memberTbl ) ) { if ( internalMembers [ name ] ) continue ; const desc = Object . getOwnPropertyDescriptor && Object . getOwnPropertyDescriptor ( memberTbl , name ) ; if ( ! desc || ( desc . configurable && desc . writable && ! desc . get && ! desc . set ) ) { const value = memberTbl [ name ] ; const spec = { name , value } ; if ( actionTbl && actionTbl [ name ] ) actionList . push ( spec ) ; else viewList . push ( spec ) ; } else { descList . push ( { name , value : desc } ) ; } } const instance = new Code ( ) ; const volatileList = Object . getOwnPropertyNames ( instance ) ; for ( let name of volatileList ) { volatileTbl [ name ] = instance [ name ] ; } if ( modelName ) Data = Data . named ( modelName ) ; let Model = Data ; Model = options . sealed ? Model : Model . preProcessSnapshot ( ( snap ) => snap || { } ) ; Model = Model . postProcessSnapshot ( ( snap ) => { if ( modelName && typeTag && Code . prototype . $parent ) snap [ typeTag ] = modelName ; return ( snap ) ; } ) . actions ( ( self ) => { const result = { } ; for ( let { name , value } of actionList ) { const method = function ( ) { return ( value . apply ( self , arguments ) ) ; } ; if ( renamableFunctions ) { renameFunction ( method , modelName + '.' + name ) ; } result [ name ] = method ; } return ( result ) ; } ) ; Model = ! ( viewList . length + descList . length ) ? Model : Model . views ( ( self ) => { const result = { } ; for ( let { name , value } of viewList ) { result [ name ] = function ( ) { return ( value . apply ( self , arguments ) ) ; } ; } for ( let { name , value } of descList ) { const desc = { } ; for ( let key of Object . getOwnPropertyNames ( value ) ) { ( desc as any ) [ key ] = ( value as any ) [ key ] ; } const { get , set } = desc ; if ( get ) { desc . get = ( ) => get . call ( self ) ; } else if ( set ) { desc . get = dummyGetter ; } if ( set ) desc . set = ( value ) => set . call ( self , value ) ; desc . enumerable = true ; Object . defineProperty ( result , name , desc ) ; } return ( result ) ; } ) ; Model = ! volatileList . length ? Model : Model . volatile ( ( self ) => volatileTbl ) ; return ( options . sealed ? Model as any : polymorphic ( Code , Model , modelName ) ) ; } export function polymorphic < PROPS extends ModelProperties , OTHERS , CustomC , CustomS , TYPE > ( Code , Model , modelName ? ) { const Union = types . late ( ( ) => types . union . apply ( types , Union . $typeList ) ) as any ; Union . $typeList = [ { dispatcher : ( snap ) => ( snap && typeTag && snap [ typeTag ] && Union . $typeTbl [ snap [ typeTag ] ] ) || Model } ] ; Union . $typeTbl = { } ; Union . $proto = Code . prototype ; for ( let mixin = Model ; mixin ; mixin = Object . getPrototypeOf ( mixin ) ) { for ( let key of Object . getOwnPropertyNames ( mixin ) ) { const desc = Object . getOwnPropertyDescriptor && Object . getOwnPropertyDescriptor ( mixin , key ) ; if ( ! desc || ( desc . configurable && desc . writable && ! desc . get && ! desc . set ) ) { const value = ! ( key in Union ) && mixin [ key ] ; if ( typeof ( value ) == 'function' ) { ( Union as { [ key ] : any ; } ) [ key ] = function ( ) { return ( value . apply ( Model , arguments ) ) ; } ; } } } } for ( let Class = Union ; Class ; Class = Class . $proto . $parent ) { const typeList = Class . $typeList ; const typeTbl = Class . $typeTbl ; if ( typeList ) typeList . push ( Model ) ; if ( typeTbl && modelName ) typeTbl [ modelName ] = Model ; } return ( Union ) ; } export type RecursiveType < PROPS > = PROPS & { children ? : RecursiveType < PROPS > [ ] | null ; } ; export function mstWithChildren < PROPS extends ModelProperties , OTHERS , CustomC , CustomS , TYPE > ( Code , Data , name ? ) { const Children = types . array ( types . late ( ( ) : any => Model ) ) ; const Branch = ( Data as any as IModelType < PROPS , TYPE , CustomC , CustomS > ) . props ( { children : types . maybe ( Children as any as IArrayType < IModelType < RecursiveType < PROPS > , OTHERS , RecursiveType < ModelCreationType2 < PROPS , CustomC > > , RecursiveType < ModelSnapshotType2 < PROPS , CustomS > > > > ) } ) ; const Model = mst ( Code , Branch , 'Node' ) as typeof Branch ; return ( { Model , Children } ) ; }