import invariant from 'invariant'; import { isPlainObject } from './utils'; const hooks = [ 'onError', 'onStateChange', 'onAction', 'onHmr', 'onReducer', 'onEffect', 'extraReducers', 'extraEnhancers', '_handleActions', ]; export function filterHooks(obj) { return Object.keys(obj).reduce((memo, key) => { if (hooks.indexOf(key) > -1) { memo[key] = obj[key]; } return memo; }, {}); } export default class Plugin { constructor() { this._handleActions = null; this.hooks = hooks.reduce((memo, key) => { memo[key] = []; return memo; }, {}); } use(plugin) { invariant(isPlainObject(plugin), 'plugin.use: plugin should be plain object'); const { hooks } = this; for (const key in plugin) { if (Object.prototype.hasOwnProperty.call(plugin, key)) { invariant(hooks[key], `plugin.use: unknown plugin property: ${key}`); if (key === '_handleActions') { this._handleActions = plugin[key]; } else if (key === 'extraEnhancers') { hooks[key] = plugin[key]; } else { hooks[key].push(plugin[key]); } } } } apply(key, defaultHandler) { const { hooks } = this; const validApplyHooks = ['onError', 'onHmr']; invariant(validApplyHooks.indexOf(key) > -1, `plugin.apply: hook ${key} cannot be applied`); const fns = hooks[key]; return (...args) => { if (fns.length) { for (const fn of fns) { fn(...args); } } else if (defaultHandler) { defaultHandler(...args); } }; } get(key) { const { hooks } = this; invariant(key in hooks, `plugin.get: hook ${key} cannot be got`); if (key === 'extraReducers') { return getExtraReducers(hooks[key]); } else if (key === 'onReducer') { return getOnReducer(hooks[key]); } else { return hooks[key]; } } } function getExtraReducers(hook) { let ret = {}; for (const reducerObj of hook) { ret = { ...ret, ...reducerObj }; } return ret; } function getOnReducer(hook) { return function(reducer) { for (const reducerEnhancer of hook) { reducer = reducerEnhancer(reducer); } return reducer; }; }