combineReducers.js

import { combineReducers as combineReducersVanilla } from 'redux'
import Controller from './Controller'
import _Symbol from './utils/_Symbol'

export const __controllers = _Symbol('controllers')

/**
 * Wrapper around vanilla `combineReducers()` from `redux` package.
 * Adds support for Controller class instances in place of regular reducer
 * functions. Each instance encountered is replaced with the result of
 * `Controller.reducer()` call. The instance is then saved under __controllers
 * key of the returned function. __controllers is passed up the chain and
 * is intended to be used by the `createStore()`.
 */
function combineReducers (reducers) {
  // Preprocess reducers
  const reducerKeys = Object.keys(reducers)
  const resolvedReducers = { }
  const controllers = { }

  for (let i = 0; i < reducerKeys.length; ++i) {
    const key = reducerKeys[i]
    const reducer = reducers[key]

    if (typeof reducer === 'function') {
      // Assume ready to use reducer function

      // Combine controllers
      if (reducer[__controllers]) {
        controllers[key] = reducer[__controllers]
      }

      resolvedReducers[key] = reducer
    } else if (Controller.is(reducer)) {
      // Assume this is a Controller
      const controller = reducer
      controllers[key] = controller
      resolvedReducers[key] = controller.reducer()
    } else {
      // Let vanilla function to handle everything else
      resolvedReducers[key] = reducer
    }
  }

  const combinedReducer = combineReducersVanilla(resolvedReducers)
  if (Object.keys(controllers).length > 0) {
    Object.defineProperty(combinedReducer, __controllers, {
      value: controllers
    })
  }

  return combinedReducer
}

export default combineReducers