@gongt/ts-stl-client
Version:
201 lines • 7.96 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const check_environment_1 = require("@gongt/ts-stl-library/check-environment");
const create_logger_1 = require("@gongt/ts-stl-library/debug/create-logger");
const levels_1 = require("@gongt/ts-stl-library/debug/levels");
function callAll(reducers) {
return function callAllWrapper(part, state, action) {
let changed = false;
for (const reducer of reducers) {
debug('[all] call reducer (%s): %s', reducers.length, reducer.displayName || reducer.name);
const r = reducer(part, state, action);
if (r) {
debug(' state changed.');
changed = true;
}
}
return changed;
};
}
function callOnlyOne(reducers, storeName) {
return function callOneWrapper(part, state, action) {
for (const reducer of reducers) {
debug('[one] call reducer: %s -> %s', storeName, reducer.displayName || reducer.name);
const ret = reducer(state[storeName], action.payload, action);
if (ret) {
debug(' action handled: %O', ret);
if (state[storeName] !== ret) {
state[storeName] = ret;
}
return true;
}
}
return false;
};
}
function combineAll(reducers) {
const ret = {};
debugStart('Init Actions');
const debugEnd = debugStart.enabled ? console.groupEnd.bind(console) : () => null;
const statics = [];
for (const act of Object.keys(reducers)) {
ret[act] = {};
const allCallbackCurrentAct = [];
for (const sto of Object.keys(reducers[act])) {
const globals = reducers[act][sto].global;
const locals = reducers[act][sto].local;
if (locals.length) {
ret[act][sto] = callOnlyOne(locals, sto);
if (debugStart.enabled) {
statics.push({ action: act, store: sto, type: 'local', listeners: locals.length, });
}
}
if (globals.length) {
allCallbackCurrentAct.push(callOnlyOne(globals, sto));
if (debugStart.enabled) {
statics.push({ action: act, store: sto, type: 'global', listeners: globals.length, });
}
}
}
if (allCallbackCurrentAct.length) {
ret[act]['*'] = callAll(allCallbackCurrentAct);
if (debugStart.enabled) {
statics.push({ action: act, store: '*', type: 'global', listeners: allCallbackCurrentAct.length, });
}
}
}
if (debugStart.enabled) {
console.table(statics);
}
debugEnd();
return ret;
}
function createStructure(reducers) {
const data = {};
for (const info of reducers) {
const { actionName: act, storeName: sto } = info;
if (!data[act]) {
data[act] = {};
}
if (!data[act][sto]) {
data[act][sto] = { local: [], global: [] };
}
const mod = info.global ? 'global' : 'local';
data[act][sto][mod].push(info.callback);
}
return data;
}
function reducerFromNative(native) {
if (native['__wrappedNativeReducer']) {
return native;
}
const nativeReducerWrapper = function (state, action, rawAction) {
debug('call native reducer: %s[%O]', native['displayName'] || native.name, native);
const ret = native(state, rawAction);
if (ret === state) {
return undefined;
}
return ret;
};
nativeReducerWrapper['displayName'] = `nativeReduce(${native['displayName'] || native.name})`;
nativeReducerWrapper['__wrappedNativeReducer'] = true;
return nativeReducerWrapper;
}
exports.reducerFromNative = reducerFromNative;
const debugStart = create_logger_1.createLogger(levels_1.LOG_LEVEL.DEBUG, 'reducer');
if (check_environment_1.IS_CLIENT) {
debugStart.fn = console.groupCollapsed.bind(console);
}
const debug = create_logger_1.createLogger(levels_1.LOG_LEVEL.INFO, 'reducer');
function MyCombineReducers(reducers, toplevel) {
for (const item of reducers) {
if (!item.actionName || !item.storeName || !item.callback) {
console.error(item);
throw new TypeError('final check: invalid reducer. more info see console.');
}
}
const reducersData = createStructure(reducers);
const reducersCombine = combineAll(reducersData);
// const storeList = reducers.map(info => info.storeName);
return (state, action) => {
if (!action || !action.type) {
throw new TypeError('dispatching action without type.');
}
debugStart('action: %s @ %s', action.type, action.virtualStorage || 'Nil');
const debugEnd = debugStart.enabled ? console.groupEnd.bind(console) : () => null;
debug(action);
try {
if (toplevel) { // topLevel: original redux top level style reducer
const newState = toplevel(state, action);
if (state !== newState) {
debug('handled by top level reducer - finish');
debugEnd();
return newState;
}
}
let { type: actionType, virtualStorage: store, payload } = action;
if (!reducersCombine[actionType]) {
debug('unknown action - finish');
debugEnd();
return state;
}
if (!action.hasOwnProperty('payload')) {
debug('action has no payload');
debugEnd();
return state; // this is normal action
}
let changed = false;
const currentReducers = reducersCombine[actionType];
const storeName = store;
debug('sub store name: %s', storeName);
/**
* global reducers:
* only call reducer witch:
* 1. action is watching by reducer
* if any reducer modified the state:
* NO any more reducer will called within this virtual store
* every action may or may-not call multiple global reducer.
* every action will call at most only one global reducer on each virtual store
*/
const global = currentReducers['*'];
if (global) { // global: call any store's reducer, but
debug(' -> call global reducers');
const thisChanged = global(storeName, state, action);
changed = thisChanged || changed;
}
else {
debug(' -> no global reducer');
}
/**
* local reducers:
* only call reducer witch:
* 1. action is watching by reducer
* 2. action.store equals to reducer.store
* if any reducer modified the state:
* NO any more reducer will called
* every action will only call one local reducer.
*/
const local = currentReducers[store];
if (local) {
debug(' -> call local reducers');
const thisChanged = local(storeName, state, action);
changed = thisChanged || changed;
}
else {
debug(' -> no local reducer');
}
debug('state %s', changed ? 'changed' : 'NOT changed');
debugEnd();
return changed ? Object.assign({}, state) : state;
}
catch (e) {
debugEnd();
debugEnd();
debugEnd();
debug('error while processing action: %s', e ? e.message : 'no info');
throw e;
}
};
}
exports.MyCombineReducers = MyCombineReducers;
//# sourceMappingURL=combine-reducers.js.map