1+ import { IMutation } from 'proxy-state-tree'
2+
3+ import { IState } from './types'
4+
5+ export function rehydrateState ( target : any , source : any , classes : any = { } ) {
6+ if ( ! target || ! source ) {
7+ throw new Error ( `You have to pass a "target" and "source" object to rehydrate` )
8+ }
9+
10+ Object . keys ( source ) . forEach ( ( key ) => {
11+ const value = source [ key ]
12+ const classInstance = classes [ key ]
13+
14+ if ( typeof classInstance === 'function' && Array . isArray ( target [ key ] ) ) {
15+ target [ key ] = ( source [ key ] as any [ ] ) . map ( value => classInstance ( value ) )
16+ } else if ( typeof classInstance === 'function' && typeof target [ key ] === 'object' && target [ key ] !== null && target [ key ] . constructor . name === 'Object' ) {
17+ target [ key ] = Object . keys ( source [ key ] as any ) . reduce ( ( aggr , subKey ) => {
18+ aggr [ subKey ] = classInstance ( ( source [ key ] as any ) [ subKey ] )
19+
20+ return aggr
21+ } , { } )
22+ } else if ( typeof classInstance === 'function' ) {
23+ target [ key ] = classInstance ( source [ key ] )
24+ } else if ( typeof value === 'object' && ! Array . isArray ( value ) && value !== null ) {
25+ if ( ! target [ key ] ) target [ key ] = { }
26+ rehydrateState ( target [ key ] as IState , source [ key ] as IState , classes [ key ] )
27+ } else {
28+ target [ key ] = source [ key ]
29+ }
30+ } )
31+ }
32+
33+ export const SERIALIZE = Symbol ( 'SERIALIZE' )
34+
35+ export interface Serialize {
36+ [ SERIALIZE ] : boolean
37+ }
38+
39+ export type Serializable = Serialize | {
40+ toJSON : ( ) => {
41+ [ SERIALIZE ] : boolean
42+ }
43+ }
44+
45+ export type RehydrateClasses < T extends IState > = Pick < {
46+ [ P in keyof T ] : T [ P ] extends Serializable ? ( data : any ) => T [ P ] :
47+ T [ P ] extends Array < Serializable > ? ( data : any ) => T [ P ] [ keyof T [ P ] ] :
48+ T [ P ] extends { [ key : string ] : Serializable } ? ( data : any ) => T [ P ] [ keyof T [ P ] ] :
49+ T [ P ] extends IState ? RehydrateClasses < T [ P ] > :
50+ never
51+ } , { [ Key in keyof T ] : T [ Key ] extends Serializable | Array < Serializable > | { [ key : string ] : Serializable } ? Key : never } [ keyof T ] >
52+
53+
54+ export const rehydrate = < T extends IState > ( state : T , source : IMutation [ ] | IState , classes : RehydrateClasses < T > = { } as any ) => {
55+ if ( Array . isArray ( source ) ) {
56+ const mutations = source as IMutation [ ]
57+ mutations . forEach ( ( mutation ) => {
58+ const pathArray = mutation . path . split ( '.' )
59+ const key = pathArray . pop ( ) as string
60+ const target = pathArray . reduce ( ( aggr , key ) => aggr [ key ] , state as any )
61+ const classInstance = pathArray . reduce ( ( aggr , key ) => aggr [ key ] , classes as any )
62+
63+ if ( mutation . method === 'set' ) {
64+ if ( typeof classInstance === 'function' && Array . isArray ( mutation . args [ 0 ] ) ) {
65+ target [ key ] = mutation . args [ 0 ] . map ( ( arg ) => classInstance ( arg ) )
66+ } else if ( typeof classInstance === 'function' ) {
67+ target [ key ] = classInstance ( mutation . args [ 0 ] )
68+ } else {
69+ target [ key ] = mutation . args [ 0 ]
70+ }
71+ } else if ( mutation . method === 'unset' ) {
72+ delete target [ key ]
73+ } else {
74+ target [ key ] [ mutation . method ] . apply ( target [ key ] , typeof classInstance === 'function' ? mutation . args . map ( ( arg ) => {
75+ return typeof arg === 'object' && arg !== null ? classInstance ( arg ) : arg
76+ } ) : mutation . args )
77+ }
78+ } )
79+ } else {
80+ rehydrateState ( state , source , classes )
81+ }
82+ }
0 commit comments