Source: scopeAction.js

import scopeActionType from "./scopeActionType";

/**
 * Scopes an action creator/object/type.
 * 
 * the given action can be an action creator/object/type.
 * <p>
 *  The given action is a string: will be treated as action type.
 *  `scopeAction` will return an action object wich will contain the scoped action type aswell as some meta data.
 *  If not duck complient, the action type of the returned action object will remain unscoped and no meta data will be added
 * </p>
 * <p>
 *  The given action is an object: will be treated as action object.
 *  `scopeAction` will modify the object by scoping its action type aswell as adding some meta data.
 *   If not duck complient, the action object will be returned as it is.
 * </p>
 * <p>
 *  The given action is a function: will be treated as action creator.
 * `scopeAction` will return an action creator that will return the output of the given action creator but scopes the action type aswell as adding some meta data.
 *  If not duck complient, the given action creator will be returned as it is.
 * </p>
 * @example
 * // basic usage
 * ...
 * const scopedAction = scopeAction("reducerB", action)
 * 
 * // given action is an action type
 * const action = "app/reducerA/DO_STUFF";
 * 
 * console.log(scopeAction("reducerB", action));
 * // { 
 * //   type: "app/reducerB/DO_STUFF",
 * //   meta: { 
 * //     unscopedActionType: "app/reducerA/DO_STUFF" 
 * //   }
 * // }
 * 
 * // given action is an action object
 * const action = { 
 *  type: "app/reducerA/DO_STUFF",
 *  payload: 42
 * };
 * 
 * console.log(scopeAction("reducerB", action));
 * // { 
 * //   type: "app/reducerB/DO_STUFF",
 * //   payload: 42,
 * //   meta: {
 * //     unscopedActionType: "app/reducerA/DO_STUFF"
 * //   }
 * // }
 * 
 * // given action is an action creator
 * const action = payload => ({ type: "app/reducerA/DO_STUFF", payload })
 * console.log(action(42))
 * // {
 * //   type: "app/reducerA/DO_STUFF",
 * //   payload: 42
 * // }
 * const scopedAction = scopeAction("reducerB", action)
 * console.log(scopedAction(42));
 * // {
 * //   type: "app/reducerB/DO_STUFF",
 * //   payload: 42,
 * //   meta: {
 * //     unscopedActionType: "app/reducerA/DO_STUFF"
 * //   }
 * // }
 * 
 * @param {string} scope the scope: will replace the reducer part of the actions action type
 * @param {string|object|function} action the action (type/object/creator) that will be scoped
 * @returns {object|function} scoped action object or action creator
 */


const scopeAction = (scope, action) => {
  switch (typeof action) {
    case "object":
      return {
        ...action,
        type: scopeActionType(scope, action.type),
        meta: { ...action.meta, unscopedActionType: action.type }
      };
    case "string":
      return {
        type: scopeActionType(scope, action),
        meta: {
          unscopedActionType: action
        }
      };
    case "function":
      return (...args) => {
        const actionObject = action(...args);
        const scopedActionType = scopeActionType(scope, actionObject.type);
        return scopedActionType === actionObject.type
          ? actionObject
          : {
              ...actionObject,
              type: scopedActionType,
              meta: {
                ...actionObject.meta,
                unscopedActionType: actionObject.type
              }
            };
      };
    default:
      return action;
  }
};

export default scopeAction;