All files createStore.js

94.12% Statements 32/34
94.74% Branches 18/19
100% Functions 3/3
94.12% Lines 32/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126                                                                                                8x     8x 8x     8x 1x       8x 8x     8x 24x 24x 16x   24x   16x 16x 5x     16x   16x 12x     4x       4x                 8x 8x 8x 8x 2x   8x 8x   8x               8x 8x             8x 8x        
import createSagaMiddleware from 'redux-saga';
import { all } from 'redux-saga/effects';
import {
  applyMiddleware,
  combineReducers,
  createStore as storeCreator,
} from 'redux';
import get from './utils/get';
import set from './utils/set';
import flattenReducers from './utils/flattenReducers';
import composeEnhancers from './composeEnhancers';
import moduleRegistry from './moduleRegistry';
import getReducer from './getReducer';
import iterateDFS from './utils/iterateDFS';
 
/**
 * @typedef {Object} Module - Object representing module
 * @property {Object=} state - Initial state for the module
 * @property {Object<String, Function>=} dispatchers - Action dispatchers for the module
 * @property {Object<String, Generator>=} sagas - Sagas for the module
 * @property {Object<String, Function>=} mutations - Mutations for the module
 * @property {Object<String, Function>=} selectors - Selector for the module
 */
 
/**
 * Creates redux store
 * @example
 * import { createStore } from "redux-box";
 * import userModule from "./modules/user";
 * import marketplaceModule from "./modules/marketplace";
 *
 * createStore({userModule, marketplaceModule},{
 *  enableDevTools: () => true,
 *  devToolOptions: {}
 * })
 *
 * @param {Object<String, Module>} modules - Object containing all modules to be attached to store
 * @param {Function=} config.enableDevTools - (Optional)enable devtool conditionally
 * @param {Object} config -  Contains configuration for store
 * @param {Function[]} config.middlewares - Array of middlewares to be used in store
 * @param {Object<String, Function>=} config.reducers - (Optional) Object containing reducers to be used in store
 * @param {Generator[]} config.sagas - Array of watcher sagas to be used in store
 * @param {Object=} config.preloadedState - (Optional) Preloaded state for store
 * @param {Object=} config.devToolOptions - (Optional) options for redux dev tool
 * @param {Function=} config.decorateReducer - (Optional) decorator function for reducer formed by redux-box, has formed reducer as first argument
 * @returns {Object} store
 */
function createStore(modules, config = {}) {
  const rootModule = { modules };
 
  // Initialize the middleware array
  const sagaMiddleware = createSagaMiddleware();
  let middlewares = [sagaMiddleware];
 
  // push the provided middlewares in config object, to the middleware array
  if (get(config, 'middlewares.length', 0) > 0) {
    middlewares = middlewares.concat(config.middlewares);
  }
 
  // an object containing reducers for all modules, to  be fed to combineReducer
  let reducerMap = {};
  let sagas = [];
 
  // iterate through each module and push the sagas and reducers of each module in thier respective array
  iterateDFS(rootModule, [], (moduleNode, modulePath) => {
    moduleRegistry.register(modulePath, moduleNode);
    if (moduleNode.sagas) {
      sagas = sagas.concat(moduleNode.sagas);
    }
    if (moduleNode.mutations) {
      // first get the moduleReducer
      let moduleReducer = getReducer(moduleNode.mutations, moduleNode.state);
      if (moduleNode.decorateReducer)
        moduleReducer = moduleNode.decorateReducer(moduleReducer);
 
      // now  handle reducerMapping
      const reducerKeyValue = get(reducerMap, modulePath);
 
      if (!reducerKeyValue) {
        set(reducerMap, modulePath, moduleReducer);
      } else {
        const siblingReducer =
          typeof reducerKeyValue === 'object'
            ? combineReducers(reducerKeyValue)
            : reducerKeyValue;
 
        set(
          reducerMap,
          modulePath,
          flattenReducers(siblingReducer, moduleReducer)
        );
      }
    }
  });
 
  sagas = config.sagas ? sagas.concat(config.sagas) : sagas;
  reducerMap = Object.assign({}, reducerMap, config.reducers);
  let combinedReducer = combineReducers(reducerMap);
  if (config.decorateReducer) {
    combinedReducer = config.decorateReducer(combinedReducer);
  }
  const preloadedState = config.preloadedState || {};
  const composer = composeEnhancers(config);
  // initialize the store using preloaded state, reducers and middlewares
  const store = storeCreator(
    combinedReducer,
    preloadedState,
    composer(applyMiddleware(...middlewares))
  );
 
  // rootsaga
  function* rootSaga() {
    try {
      yield all(sagas);
    } catch (err) {
      console.error('[ERROR] Something went wrong in rootSaga: ', err);
      return 'ERROR_THROWN';
    }
  }
 
  sagaMiddleware.run(rootSaga);
  return store;
}
 
export default createStore;