1 | import { Middleware } from 'redux'
|
2 | import {
|
3 | Action,
|
4 | Config,
|
5 | ExposedFunction,
|
6 | Models,
|
7 | NamedModel,
|
8 | ObjectNotAFunction,
|
9 | Plugin,
|
10 | RematchBag,
|
11 | RematchStore,
|
12 | ModelDispatcher,
|
13 | RematchDispatch,
|
14 | } from './types'
|
15 | import createReduxStore, {
|
16 | createModelReducer,
|
17 | createRootReducer,
|
18 | } from './reduxStore'
|
19 | import { createReducerDispatcher, createEffectDispatcher } from './dispatcher'
|
20 | import { validateModel } from './validate'
|
21 | import createRematchBag from './bag'
|
22 |
|
23 | export default function createRematchStore<
|
24 | TModels extends Models<TModels>,
|
25 | TExtraModels extends Models<TModels>
|
26 | >(config: Config<TModels, TExtraModels>): RematchStore<TModels, TExtraModels> {
|
27 |
|
28 | const bag = createRematchBag(config)
|
29 |
|
30 |
|
31 | bag.reduxConfig.middlewares.push(createEffectsMiddleware(bag))
|
32 |
|
33 |
|
34 | bag.forEachPlugin('createMiddleware', (createMiddleware) => {
|
35 | bag.reduxConfig.middlewares.push(createMiddleware(bag))
|
36 | })
|
37 |
|
38 | const reduxStore = createReduxStore(bag)
|
39 |
|
40 | let rematchStore = {
|
41 | ...reduxStore,
|
42 | name: config.name,
|
43 | addModel(model: NamedModel<TModels>) {
|
44 | validateModel(model)
|
45 | createModelReducer(bag, model)
|
46 | prepareModel(rematchStore, model)
|
47 | enhanceModel(rematchStore, bag, model)
|
48 | reduxStore.replaceReducer(createRootReducer(bag))
|
49 | reduxStore.dispatch({ type: '@@redux/REPLACE' })
|
50 | },
|
51 | } as RematchStore<TModels, TExtraModels>
|
52 |
|
53 | addExposed(rematchStore, config.plugins)
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | bag.models.forEach((model) => prepareModel(rematchStore, model))
|
63 | bag.models.forEach((model) => enhanceModel(rematchStore, bag, model))
|
64 |
|
65 | bag.forEachPlugin('onStoreCreated', (onStoreCreated) => {
|
66 | rematchStore = onStoreCreated(rematchStore, bag) || rematchStore
|
67 | })
|
68 |
|
69 | return rematchStore
|
70 | }
|
71 |
|
72 | function createEffectsMiddleware<
|
73 | TModels extends Models<TModels>,
|
74 | TExtraModels extends Models<TModels>
|
75 | >(bag: RematchBag<TModels, TExtraModels>): Middleware {
|
76 | return (store) =>
|
77 | (next) =>
|
78 | (action: Action): any => {
|
79 | if (action.type in bag.effects) {
|
80 |
|
81 | next(action)
|
82 |
|
83 |
|
84 | return (bag.effects as any)[action.type](
|
85 | action.payload,
|
86 | store.getState(),
|
87 | action.meta
|
88 | )
|
89 | }
|
90 |
|
91 | return next(action)
|
92 | }
|
93 | }
|
94 |
|
95 | function prepareModel<
|
96 | TModels extends Models<TModels>,
|
97 | TExtraModels extends Models<TModels>,
|
98 | TModel extends NamedModel<TModels>
|
99 | >(rematchStore: RematchStore<TModels, TExtraModels>, model: TModel): void {
|
100 | const modelDispatcher = {} as ModelDispatcher<TModel, TModels>
|
101 |
|
102 |
|
103 | rematchStore.dispatch[`${model.name}` as keyof RematchDispatch<TModels>] =
|
104 | modelDispatcher
|
105 |
|
106 | createReducerDispatcher(rematchStore, model)
|
107 | }
|
108 |
|
109 | function enhanceModel<
|
110 | TModels extends Models<TModels>,
|
111 | TExtraModels extends Models<TModels>,
|
112 | TModel extends NamedModel<TModels>
|
113 | >(
|
114 | rematchStore: RematchStore<TModels, TExtraModels>,
|
115 | bag: RematchBag<TModels, TExtraModels>,
|
116 | model: TModel
|
117 | ): void {
|
118 | createEffectDispatcher(rematchStore, bag, model)
|
119 |
|
120 | bag.forEachPlugin('onModel', (onModel) => {
|
121 | onModel(model, rematchStore)
|
122 | })
|
123 | }
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | function addExposed<
|
133 | TModels extends Models<TModels>,
|
134 | TExtraModels extends Models<TModels>
|
135 | >(
|
136 | store: RematchStore<TModels, TExtraModels>,
|
137 | plugins: Plugin<TModels, TExtraModels>[]
|
138 | ): void {
|
139 | plugins.forEach((plugin) => {
|
140 | if (!plugin.exposed) return
|
141 | const pluginKeys = Object.keys(plugin.exposed)
|
142 | pluginKeys.forEach((key) => {
|
143 | if (!plugin.exposed) return
|
144 | const exposedItem = plugin.exposed[key] as
|
145 | | ExposedFunction<TModels, TExtraModels>
|
146 | | ObjectNotAFunction
|
147 | const isExposedFunction = typeof exposedItem === 'function'
|
148 |
|
149 | store[key] = isExposedFunction
|
150 | ? (...params: any[]): any =>
|
151 | (exposedItem as ExposedFunction<TModels, TExtraModels>)(
|
152 | store,
|
153 | ...params
|
154 | )
|
155 | : Object.create(plugin.exposed[key])
|
156 | })
|
157 | })
|
158 | }
|