import { combineReducers , createStore , applyMiddleware , compose } from 'redux' ; import produce from "immer" ; import { useSelector } from 'react-redux' ; import { Provider } from 'react-redux' ; import * as React from 'react' ; const NamePrefix = 'Moox' ; function getType ( modelName , actionName ) { return NamePrefix + '/' + modelName + '/' + actionName ; } function getActionByTypeName ( type ) { return type . split ( '/' ) ; } export interface IActionFun < ParamType > { ( draftState , params , state ? ) : void ; } export type IModel < T , S > = { state : S ; $name ? : string ; immer ? : boolean ; actions : T ; } ; export const createModel = < T , S > ( model ) => { const { state , actions } = model ; return { state , actions } ; } ; const defaultMiddleware = [ ] ; interface IConfig { preloadedState ? : any ; middleware ? : any ; immer ? : boolean ; enhancer ? : any ; } function loadModel < T extends Record < string , IActionFun < P > > , S , P > ( name , model ) { model . $name = name ; return function reducer ( state = model . state , action ) { let types = getActionByTypeName ( action . type ) ; let actionName = types [ 2 ] ; type keyType = keyof T ; const fn = model . actions [ actionName as keyType ] ; type ParamType = Parameters < typeof fn > [ 1 ] ; let params = action . params as ParamType ; if ( types [ 0 ] !== NamePrefix ) { return state ; } else if ( types [ 1 ] !== model . $name ) { return state ; } if ( fn ) { if ( model . immer ) { return produce ( state , draftState => { return fn ( draftState , params , state ) ; } ) ; } return fn ( state , params ) ; } return state ; } ; } interface Ac < ParamType > { ( params ? ) : void ; } const loadActions = < T extends Record < string , IActionFun < any > > > ( store ) => ( name , actions ) => { const keys = Object . keys ( actions ) ; const res = { } ; keys . forEach ( key => { if ( typeof actions [ key ] === 'function' ) { res [ key ] = function actionCreator ( params ? ) { return store . dispatch ( { type : getType ( name , key ) , params } ) ; } ; } } ) ; return res ; } ; function moox < T extends Record < string , any > , S , MS extends { [ modelName ] : IModel < T , S > ; } > ( models , customConfig = { } ) { const reducers = { } ; const keys = Object . keys ( models ) ; const storeConfig = { middleware : [ ] , immer : true , enhancer : window . __REDUX_DEVTOOLS_EXTENSION__ && window . __REDUX_DEVTOOLS_EXTENSION__ ( { name : document . title } ) , ... customConfig } ; keys . forEach ( ( name ) => { models [ name ] . immer = typeof models [ name ] . immer === 'undefined' ? storeConfig . immer : models [ name ] . immer ; reducers [ name ] = loadModel ( name , models [ name ] ) ; } ) ; const middleware = defaultMiddleware . concat ( storeConfig . middleware ) ; const funMiddleware = applyMiddleware ( ... middleware ) ; const store = createStore ( combineReducers ( reducers ) , storeConfig . preloadedState , storeConfig . enhancer ? compose ( funMiddleware , storeConfig . enhancer ) : funMiddleware ) ; const Base = { getReducers : ( ) => reducers , getStore : ( ) => store , getState : ( ) => store . getState ( ) , useModel : useModel , getProvider : ( Application ) => ( ) => { return React . createElement ( Provider , { store : store } , React . createElement ( Application , null ) ) ; } } ; const Actions = { } ; keys . forEach ( name => { const res = loadActions ( store ) ( name , models [ name ] . actions ) ; Actions [ name ] = res ; } ) ; const Moox = { ... Base , ... Actions } ; return Moox ; } export const useModel = useSelector ; export default moox ;