1 | import * as Redux from 'redux';
|
2 |
|
3 | function _extends() {
|
4 | _extends = Object.assign || function (target) {
|
5 | for (var i = 1; i < arguments.length; i++) {
|
6 | var source = arguments[i];
|
7 |
|
8 | for (var key in source) {
|
9 | if (Object.prototype.hasOwnProperty.call(source, key)) {
|
10 | target[key] = source[key];
|
11 | }
|
12 | }
|
13 | }
|
14 |
|
15 | return target;
|
16 | };
|
17 |
|
18 | return _extends.apply(this, arguments);
|
19 | }
|
20 |
|
21 | function createReduxStore(bag) {
|
22 | var _bag$reduxConfig;
|
23 |
|
24 | bag.models.forEach(function (model) {
|
25 | return createModelReducer(bag, model);
|
26 | });
|
27 | var rootReducer = createRootReducer(bag);
|
28 | var middlewares = Redux.applyMiddleware.apply(Redux, bag.reduxConfig.middlewares);
|
29 | var enhancers = bag.reduxConfig.devtoolComposer ? (_bag$reduxConfig = bag.reduxConfig).devtoolComposer.apply(_bag$reduxConfig, bag.reduxConfig.enhancers.concat([middlewares])) : composeEnhancersWithDevtools(bag.reduxConfig.devtoolOptions).apply(void 0, bag.reduxConfig.enhancers.concat([middlewares]));
|
30 | var createStore = bag.reduxConfig.createStore || Redux.createStore;
|
31 | var bagInitialState = bag.reduxConfig.initialState;
|
32 | var initialState = bagInitialState === undefined ? {} : bagInitialState;
|
33 | return createStore(rootReducer, initialState, enhancers);
|
34 | }
|
35 | function createModelReducer(bag, model) {
|
36 | var modelReducers = {};
|
37 | var modelReducerKeys = Object.keys(model.reducers);
|
38 | modelReducerKeys.forEach(function (reducerKey) {
|
39 | var actionName = isAlreadyActionName(reducerKey) ? reducerKey : model.name + "/" + reducerKey;
|
40 | modelReducers[actionName] = model.reducers[reducerKey];
|
41 | });
|
42 |
|
43 | var combinedReducer = function combinedReducer(state, action) {
|
44 | if (state === void 0) {
|
45 | state = model.state;
|
46 | }
|
47 |
|
48 | if (action.type in modelReducers) {
|
49 | return modelReducers[action.type](state, action.payload, action.meta);
|
50 | }
|
51 |
|
52 | return state;
|
53 | };
|
54 |
|
55 | var modelBaseReducer = model.baseReducer;
|
56 | var reducer = !modelBaseReducer ? combinedReducer : function (state, action) {
|
57 | if (state === void 0) {
|
58 | state = model.state;
|
59 | }
|
60 |
|
61 | return combinedReducer(modelBaseReducer(state, action), action);
|
62 | };
|
63 | bag.forEachPlugin('onReducer', function (onReducer) {
|
64 | reducer = onReducer(reducer, model.name, bag) || reducer;
|
65 | });
|
66 | bag.reduxConfig.reducers[model.name] = reducer;
|
67 | }
|
68 | function createRootReducer(bag) {
|
69 | var rootReducers = bag.reduxConfig.rootReducers;
|
70 | var mergedReducers = mergeReducers(bag.reduxConfig);
|
71 | var rootReducer = mergedReducers;
|
72 |
|
73 | if (rootReducers && Object.keys(rootReducers).length) {
|
74 | rootReducer = function rootReducer(state, action) {
|
75 | var actionRootReducer = rootReducers[action.type];
|
76 |
|
77 | if (actionRootReducer) {
|
78 | return mergedReducers(actionRootReducer(state, action), action);
|
79 | }
|
80 |
|
81 | return mergedReducers(state, action);
|
82 | };
|
83 | }
|
84 |
|
85 | bag.forEachPlugin('onRootReducer', function (onRootReducer) {
|
86 | rootReducer = onRootReducer(rootReducer, bag) || rootReducer;
|
87 | });
|
88 | return rootReducer;
|
89 | }
|
90 |
|
91 | function mergeReducers(reduxConfig) {
|
92 | var combineReducers = reduxConfig.combineReducers || Redux.combineReducers;
|
93 |
|
94 | if (!Object.keys(reduxConfig.reducers).length) {
|
95 | return function (state) {
|
96 | return state;
|
97 | };
|
98 | }
|
99 |
|
100 | return combineReducers(reduxConfig.reducers);
|
101 | }
|
102 |
|
103 | function composeEnhancersWithDevtools(devtoolOptions) {
|
104 | if (devtoolOptions === void 0) {
|
105 | devtoolOptions = {};
|
106 | }
|
107 |
|
108 | return !devtoolOptions.disabled && typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(devtoolOptions) : Redux.compose;
|
109 | }
|
110 |
|
111 | function isAlreadyActionName(reducerKey) {
|
112 | return reducerKey.indexOf('/') > -1;
|
113 | }
|
114 |
|
115 | var isObject = function isObject(obj) {
|
116 | return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
117 | };
|
118 | var ifDefinedIsFunction = function ifDefinedIsFunction(func) {
|
119 | return !func || typeof func === 'function';
|
120 | };
|
121 |
|
122 | var validate = function validate(runValidations) {
|
123 | if (process.env.NODE_ENV !== 'production') {
|
124 | var validations = runValidations();
|
125 | var errors = [];
|
126 | validations.forEach(function (validation) {
|
127 | var isInvalid = validation[0];
|
128 | var errorMessage = validation[1];
|
129 |
|
130 | if (isInvalid) {
|
131 | errors.push(errorMessage);
|
132 | }
|
133 | });
|
134 |
|
135 | if (errors.length > 0) {
|
136 | throw new Error(errors.join(', '));
|
137 | }
|
138 | }
|
139 | };
|
140 |
|
141 | var validateConfig = function validateConfig(config) {
|
142 | validate(function () {
|
143 | return [[!Array.isArray(config.plugins), 'init config.plugins must be an array'], [!isObject(config.models), 'init config.models must be an object'], [!isObject(config.redux.reducers), 'init config.redux.reducers must be an object'], [!Array.isArray(config.redux.middlewares), 'init config.redux.middlewares must be an array'], [!Array.isArray(config.redux.enhancers), 'init config.redux.enhancers must be an array of functions'], [!ifDefinedIsFunction(config.redux.combineReducers), 'init config.redux.combineReducers must be a function'], [!ifDefinedIsFunction(config.redux.createStore), 'init config.redux.createStore must be a function']];
|
144 | });
|
145 | };
|
146 | var validateModel = function validateModel(model) {
|
147 | validate(function () {
|
148 | return [[!model, 'model config is required'], [typeof model.name !== 'string', 'model "name" [string] is required'], [model.state === undefined && model.baseReducer === undefined, 'model "state" is required'], [!ifDefinedIsFunction(model.baseReducer), 'model "baseReducer" must be a function']];
|
149 | });
|
150 | };
|
151 | var validatePlugin = function validatePlugin(plugin) {
|
152 | validate(function () {
|
153 | return [[!ifDefinedIsFunction(plugin.onStoreCreated), 'Plugin onStoreCreated must be a function'], [!ifDefinedIsFunction(plugin.onModel), 'Plugin onModel must be a function'], [!ifDefinedIsFunction(plugin.onReducer), 'Plugin onReducer must be a function'], [!ifDefinedIsFunction(plugin.onRootReducer), 'Plugin onRootReducer must be a function'], [!ifDefinedIsFunction(plugin.createMiddleware), 'Plugin createMiddleware must be a function']];
|
154 | });
|
155 | };
|
156 | var validateModelReducer = function validateModelReducer(modelName, reducers, reducerName) {
|
157 | validate(function () {
|
158 | return [[!!reducerName.match(/\/.+\//), "Invalid reducer name (" + modelName + "/" + reducerName + ")"], [typeof reducers[reducerName] !== 'function', "Invalid reducer (" + modelName + "/" + reducerName + "). Must be a function"]];
|
159 | });
|
160 | };
|
161 | var validateModelEffect = function validateModelEffect(modelName, effects, effectName) {
|
162 | validate(function () {
|
163 | return [[!!effectName.match(/\//), "Invalid effect name (" + modelName + "/" + effectName + ")"], [typeof effects[effectName] !== 'function', "Invalid effect (" + modelName + "/" + effectName + "). Must be a function"]];
|
164 | });
|
165 | };
|
166 |
|
167 | var createActionDispatcher = function createActionDispatcher(rematch, modelName, actionName, isEffect) {
|
168 | return Object.assign(function (payload, meta) {
|
169 | var action = {
|
170 | type: modelName + "/" + actionName
|
171 | };
|
172 |
|
173 | if (typeof payload !== 'undefined') {
|
174 | action.payload = payload;
|
175 | }
|
176 |
|
177 | if (typeof meta !== 'undefined') {
|
178 | action.meta = meta;
|
179 | }
|
180 |
|
181 | return rematch.dispatch(action);
|
182 | }, {
|
183 | isEffect: isEffect
|
184 | });
|
185 | };
|
186 |
|
187 | var createReducerDispatcher = function createReducerDispatcher(rematch, model) {
|
188 | var modelDispatcher = rematch.dispatch[model.name];
|
189 | var modelReducersKeys = Object.keys(model.reducers);
|
190 | modelReducersKeys.forEach(function (reducerName) {
|
191 | validateModelReducer(model.name, model.reducers, reducerName);
|
192 | modelDispatcher[reducerName] = createActionDispatcher(rematch, model.name, reducerName, false);
|
193 | });
|
194 | };
|
195 | var createEffectDispatcher = function createEffectDispatcher(rematch, bag, model) {
|
196 | var modelDispatcher = rematch.dispatch[model.name];
|
197 | var effects = {};
|
198 |
|
199 | if (model.effects) {
|
200 | effects = typeof model.effects === 'function' ? model.effects(rematch.dispatch) : model.effects;
|
201 | }
|
202 |
|
203 | var effectKeys = Object.keys(effects);
|
204 | effectKeys.forEach(function (effectName) {
|
205 | validateModelEffect(model.name, effects, effectName);
|
206 | bag.effects[model.name + "/" + effectName] = effects[effectName].bind(modelDispatcher);
|
207 | modelDispatcher[effectName] = createActionDispatcher(rematch, model.name, effectName, true);
|
208 | });
|
209 | };
|
210 |
|
211 | function createRematchBag(config) {
|
212 | return {
|
213 | models: createNamedModels(config.models),
|
214 | reduxConfig: config.redux,
|
215 | forEachPlugin: function forEachPlugin(method, fn) {
|
216 | config.plugins.forEach(function (plugin) {
|
217 | if (plugin[method]) {
|
218 | fn(plugin[method]);
|
219 | }
|
220 | });
|
221 | },
|
222 | effects: {}
|
223 | };
|
224 | }
|
225 |
|
226 | function createNamedModels(models) {
|
227 | return Object.keys(models).map(function (modelName) {
|
228 | var model = createNamedModel(modelName, models[modelName]);
|
229 | validateModel(model);
|
230 | return model;
|
231 | });
|
232 | }
|
233 |
|
234 | function createNamedModel(name, model) {
|
235 | return _extends({
|
236 | name: name,
|
237 | reducers: {}
|
238 | }, model);
|
239 | }
|
240 |
|
241 | function createRematchStore(config) {
|
242 | var bag = createRematchBag(config);
|
243 | bag.reduxConfig.middlewares.push(createEffectsMiddleware(bag));
|
244 | bag.forEachPlugin('createMiddleware', function (createMiddleware) {
|
245 | bag.reduxConfig.middlewares.push(createMiddleware(bag));
|
246 | });
|
247 | var reduxStore = createReduxStore(bag);
|
248 |
|
249 | var rematchStore = _extends({}, reduxStore, {
|
250 | name: config.name,
|
251 | addModel: function addModel(model) {
|
252 | validateModel(model);
|
253 | createModelReducer(bag, model);
|
254 | prepareModel(rematchStore, model);
|
255 | enhanceModel(rematchStore, bag, model);
|
256 | reduxStore.replaceReducer(createRootReducer(bag));
|
257 | reduxStore.dispatch({
|
258 | type: '@@redux/REPLACE'
|
259 | });
|
260 | }
|
261 | });
|
262 |
|
263 | addExposed(rematchStore, config.plugins);
|
264 | bag.models.forEach(function (model) {
|
265 | return prepareModel(rematchStore, model);
|
266 | });
|
267 | bag.models.forEach(function (model) {
|
268 | return enhanceModel(rematchStore, bag, model);
|
269 | });
|
270 | bag.forEachPlugin('onStoreCreated', function (onStoreCreated) {
|
271 | rematchStore = onStoreCreated(rematchStore, bag) || rematchStore;
|
272 | });
|
273 | return rematchStore;
|
274 | }
|
275 |
|
276 | function createEffectsMiddleware(bag) {
|
277 | return function (store) {
|
278 | return function (next) {
|
279 | return function (action) {
|
280 | if (action.type in bag.effects) {
|
281 | next(action);
|
282 | return bag.effects[action.type](action.payload, store.getState(), action.meta);
|
283 | }
|
284 |
|
285 | return next(action);
|
286 | };
|
287 | };
|
288 | };
|
289 | }
|
290 |
|
291 | function prepareModel(rematchStore, model) {
|
292 | var modelDispatcher = {};
|
293 | rematchStore.dispatch["" + model.name] = modelDispatcher;
|
294 | createReducerDispatcher(rematchStore, model);
|
295 | }
|
296 |
|
297 | function enhanceModel(rematchStore, bag, model) {
|
298 | createEffectDispatcher(rematchStore, bag, model);
|
299 | bag.forEachPlugin('onModel', function (onModel) {
|
300 | onModel(model, rematchStore);
|
301 | });
|
302 | }
|
303 |
|
304 | function addExposed(store, plugins) {
|
305 | plugins.forEach(function (plugin) {
|
306 | if (!plugin.exposed) return;
|
307 | var pluginKeys = Object.keys(plugin.exposed);
|
308 | pluginKeys.forEach(function (key) {
|
309 | if (!plugin.exposed) return;
|
310 | var exposedItem = plugin.exposed[key];
|
311 | var isExposedFunction = typeof exposedItem === 'function';
|
312 | store[key] = isExposedFunction ? function () {
|
313 | for (var _len = arguments.length, params = new Array(_len), _key = 0; _key < _len; _key++) {
|
314 | params[_key] = arguments[_key];
|
315 | }
|
316 |
|
317 | return exposedItem.apply(void 0, [store].concat(params));
|
318 | } : Object.create(plugin.exposed[key]);
|
319 | });
|
320 | });
|
321 | }
|
322 |
|
323 | var count = 0;
|
324 | function createConfig(initConfig) {
|
325 | var _initConfig$name, _initConfig$redux$dev, _initConfig$redux;
|
326 |
|
327 | var storeName = (_initConfig$name = initConfig.name) != null ? _initConfig$name : "Rematch Store " + count;
|
328 | count += 1;
|
329 | var config = {
|
330 | name: storeName,
|
331 | models: initConfig.models || {},
|
332 | plugins: initConfig.plugins || [],
|
333 | redux: _extends({
|
334 | reducers: {},
|
335 | rootReducers: {},
|
336 | enhancers: [],
|
337 | middlewares: []
|
338 | }, initConfig.redux, {
|
339 | devtoolOptions: _extends({
|
340 | name: storeName
|
341 | }, (_initConfig$redux$dev = (_initConfig$redux = initConfig.redux) == null ? void 0 : _initConfig$redux.devtoolOptions) != null ? _initConfig$redux$dev : {})
|
342 | })
|
343 | };
|
344 | validateConfig(config);
|
345 | config.plugins.forEach(function (plugin) {
|
346 | if (plugin.config) {
|
347 | config.models = merge(config.models, plugin.config.models);
|
348 |
|
349 | if (plugin.config.redux) {
|
350 | config.redux.initialState = merge(config.redux.initialState, plugin.config.redux.initialState);
|
351 | config.redux.reducers = merge(config.redux.reducers, plugin.config.redux.reducers);
|
352 | config.redux.rootReducers = merge(config.redux.rootReducers, plugin.config.redux.reducers);
|
353 | config.redux.enhancers = [].concat(config.redux.enhancers, plugin.config.redux.enhancers || []);
|
354 | config.redux.middlewares = [].concat(config.redux.middlewares, plugin.config.redux.middlewares || []);
|
355 | config.redux.combineReducers = config.redux.combineReducers || plugin.config.redux.combineReducers;
|
356 | config.redux.createStore = config.redux.createStore || plugin.config.redux.createStore;
|
357 | }
|
358 | }
|
359 |
|
360 | validatePlugin(plugin);
|
361 | });
|
362 | return config;
|
363 | }
|
364 |
|
365 | function merge(original, extra) {
|
366 | return extra ? _extends({}, extra, original) : original;
|
367 | }
|
368 |
|
369 | var init = function init(initConfig) {
|
370 | var config = createConfig(initConfig || {});
|
371 | return createRematchStore(config);
|
372 | };
|
373 | var createModel = function createModel() {
|
374 | return function (mo) {
|
375 | return mo;
|
376 | };
|
377 | };
|
378 | var index = {
|
379 | init: init,
|
380 | createModel: createModel
|
381 | };
|
382 |
|
383 | export { createModel, index as default, init };
|
384 |
|