npm-redux-interfaces
Version:
Domain based Redux architecture and API
324 lines (277 loc) • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RI = undefined;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _redux = require('redux');
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var Store = function () {
var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
// Set the initial store passed in from the user:
var ClientStore = initial;
/**
* createReducerListener
*
* A method for creating a reducer listener and returning
* an API to be used to add new listner hooks and to update
* the existing ones with new state.
*/
var createReducerListener = function createReducerListener(interfaceName, reducerName) {
var currentValueInReducer = null;
/**
* hooks
*
* An IIFE that is responsible for keeping track
* of every listener hook. Listener hooks are
* instantiated through the `.listen` command.
*/
var hooks = function () {
var instances = [];
return {
getAll: function getAll() {
return instances;
},
add: function add(hook) {
instances.push(hook);
},
remove: function remove(position) {
if (instances[position]) {
instances[position] = null;
}
},
length: function length() {
return instances.length;
}
};
}();
return _defineProperty({}, reducerName, {
updateListeners: function updateListeners(newState) {
var nextReducerState = newState[interfaceName][reducerName];
var dataTypeOfNextState = typeof nextReducerState === 'undefined' ? 'undefined' : _typeof(nextReducerState);
var dataTypeOfCurrentState = typeof currentValueInReducer === 'undefined' ? 'undefined' : _typeof(currentValueInReducer);
var dataHasChanged = dataTypeOfNextState !== dataTypeOfCurrentState || JSON.stringify(nextReducerState) !== JSON.stringify(currentValueInReducer);
// Update every listener hook with the new value:
hooks.getAll().forEach(function (listener) {
if (listener) {
if (listener.numberOfCalls && listener.currentCalls === listener.numberOfCalls) {
listener.remove();
} else {
if (listener.alwaysUpdate || dataHasChanged) {
if (listener.actions && listener.actions.includes(newState._RI.lastAction.type)) {
listener.callback(nextReducerState);
listener.currentCalls = listener.currentCalls + 1;
}
}
}
}
});
currentValueInReducer = nextReducerState;
},
addListenerHook: function addListenerHook(callback) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var actions = arguments[2];
/**
* addListenerHook
*
* The method is called when adding a new listener hook.
* the `callback` argument is what gets called internally
* to update the listener with the new state on change.
*/
var listenerHookPosition = hooks.length();
var listenerHook = {
callback: callback,
numberOfCalls: params.numberOfCalls || null,
alwaysUpdate: params.alwaysUpdate || null,
currentCalls: 0,
actions: actions || null,
remove: function remove() {
hooks.remove(listenerHookPosition);
}
};
hooks.add(listenerHook);
return {
remove: listenerHook.remove
};
}
});
};
/**
* reducerSubscriptions
*
* An IIFE that is responsible for instantiating and
* keeping track of reducer subscriptions. A given reducer
* can have only one subscription. Each subscription can
* having multiple listener hooks.
*/
var reducerSubscriptions = function () {
var subscriptions = {};
return {
listen: function listen(interfaceName, reducerName, callback, params, actions) {
/**
* This function is executed in the context of a
* new listener being added to a reducer subscription.
*/
var publicListenerHookMethods = null;
if (!subscriptions.hasOwnProperty(interfaceName)) {
// if the interface does not yet have any reducer
// subscriptions, instantiate the first one:
subscriptions[interfaceName] = createReducerListener(interfaceName, reducerName);
publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions);
} else if (subscriptions.hasOwnProperty(interfaceName) && !subscriptions[interfaceName][reducerName]) {
// If the interface already has a subscription but
// no listeners for this reducer, add the reducer
// listener to the interface subscription:
subscriptions[interfaceName] = Object.assign({}, subscriptions[interfaceName], createReducerListener(interfaceName, reducerName, callback));
publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions);
} else {
// if the interface already has a subscription and
// the reducer already has a listener, add a new
// listener hook:
publicListenerHookMethods = subscriptions[interfaceName][reducerName].addListenerHook(callback, params, actions);
}
return publicListenerHookMethods;
},
updateSubscriptions: function updateSubscriptions(changesFromStore) {
/**
* An internal method for updating all reducer subscriptions
* with the new state object.
*
* This is called internally on every state change in the store.
*/
for (var namespace in subscriptions) {
var Interface = subscriptions[namespace];
for (var reducerName in Interface) {
Interface[reducerName].updateListeners(changesFromStore);
}
}
}
};
}();
var subscribe = function subscribe() {
/**
* subscribe
*
* A method to be called on every update made to the
* store. It is responsible for updating every reducer
* subscription with the new state.
*
* @return {null}
*/
/**
* handleChange
*
* A method for updating all of the subscriptions
* with the new state from Redux:
*/
var handleChange = function handleChange() {
var nextState = ClientStore.getState();
reducerSubscriptions.updateSubscriptions(nextState);
};
// Subscribe to the store passed from the user
// and save the disconnect method:
var unsubscribeCallback = void 0;
var createSubscriptionToStore = function createSubscriptionToStore(store) {
// returns method to unsubscribe;
unsubscribeCallback = store.subscribe(handleChange);
};
return Object.assign({}, reducerSubscriptions, {
createSubscriptionToStore: createSubscriptionToStore,
unsubscribeCallback: unsubscribeCallback
});
};
var StoreAPI = {
subscribe: subscribe(),
dispatch: function dispatch(action) {
return ClientStore.dispatch(action);
},
get: function get() {
return ClientStore;
},
set: function set(store) {
ClientStore = store;
StoreAPI.subscribe.createSubscriptionToStore(store);
}
};
return StoreAPI;
}();
var Reducer = function Reducer(store, interfaceName, reducerName) {
return {
getState: function getState() {
return eval('store.get().getState().' + interfaceName + '.' + reducerName);
},
listen: function listen(callback, params, actions) {
return store.subscribe.listen(interfaceName, reducerName, callback, params, actions);
}
};
};
var RootReducer = function () {
var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
var root_reducer = initial;
var reducerStore = {};
return {
addInterface: function addInterface(name, input) {
// Format the inputs:
var nextInterface = _defineProperty({}, name, input);
// Add the new interface:
reducerStore = Object.assign(reducerStore, nextInterface);
// Rebuild the root_reducer:
return root_reducer = (0, _redux.combineReducers)(reducerStore);
},
get: function get() {
return root_reducer;
}
};
}();
var mountInterface = function mountInterface(store, interfaceName, input) {
// Check if the interface name does not conflict:
if (!RI[interfaceName]) {
// Build the actions:
var actionsObj = {};
if (input.actions) {
actionsObj = Object.keys(input.actions).reduce(function (obj, action) {
obj[action] = function () {
var _input$actions;
return store.dispatch((_input$actions = input.actions)[action].apply(_input$actions, arguments));
};
return obj;
}, {});
}
// Build the reducers:
var reducersObj = {};
if (input.reducers) {
reducersObj = Object.keys(input.reducers).reduce(function (acc, reducerName) {
acc[reducerName] = new Reducer(store, interfaceName, reducerName);
return acc;
}, {});
// Add the interface's reducers to the RootReducer:
RootReducer.addInterface(interfaceName, (0, _redux.combineReducers)(input.reducers));
}
// Mount the interface:
return exports.RI = RI = Object.assign({}, RI, _defineProperty({}, interfaceName, Object.assign({}, actionsObj, reducersObj)));
}
// If the interface conflicts:
console.error('\n Interface \'' + interfaceName + '\' is already in use. Try a different name for your interface.\n ');
return err;
};
var RI = exports.RI = {
mount: function mount(namespace, input) {
return mountInterface(Store, namespace, input);
},
setStore: function setStore(input) {
return Store.set(input);
},
getStore: function getStore() {
return Store.get();
},
getRootReducer: function getRootReducer() {
return RootReducer.get();
}
};
RI.mount('_RI', {
reducers: {
lastAction: function lastAction(state, action) {
return action;
}
}
});