2019-10-17 15:15:10 +00:00
|
|
|
import fp from 'lodash/fp';
|
|
|
|
import u from 'updeep';
|
|
|
|
|
|
|
|
import { createStore, applyMiddleware } from 'redux';
|
|
|
|
|
|
|
|
function actionFor(type) {
|
|
|
|
return (payload = null, meta = null) => {
|
|
|
|
return fp.pickBy(v => v !== null)({type, payload, meta});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
function buildInitial({initial = {}, subduxes = {}}) {
|
2019-10-17 15:15:10 +00:00
|
|
|
let state = initial;
|
|
|
|
|
|
|
|
if (fp.isPlainObject(initial)) {
|
|
|
|
initial = fp.mergeAll([
|
2019-10-19 17:11:30 +00:00
|
|
|
fp.mapValues(fp.getOr({}, 'initial'), subduxes),
|
2019-10-17 15:15:10 +00:00
|
|
|
initial,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return initial;
|
|
|
|
}
|
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
function buildActions({mutations = {}, subduxes = {}}) {
|
|
|
|
let actions = fp.mergeAll(fp.map(fp.getOr({}, 'actions'), subduxes)) || {};
|
2019-10-17 15:15:10 +00:00
|
|
|
|
|
|
|
Object.keys(mutations).forEach(type => {
|
|
|
|
if (!actions[type]) {
|
|
|
|
actions[type] = actionFor(type);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return actions;
|
|
|
|
}
|
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
const composeMutations = mutations =>
|
|
|
|
mutations.reduce( (m1,m2) =>
|
2019-10-19 17:11:30 +00:00
|
|
|
(payload=null,action={}) => state => m2(payload,action)(
|
2019-10-19 21:54:46 +00:00
|
|
|
m1(payload,action)(state) ));
|
2019-10-17 15:15:10 +00:00
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
function buildMutations({mutations = {}, subduxes = {}}) {
|
|
|
|
// we have to differentiate the subduxes with '*' than those
|
|
|
|
// without, as the root '*' is not the same as any sub-'*'
|
2019-10-17 15:15:10 +00:00
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
const actions = fp.uniq( Object.keys(mutations).concat(
|
|
|
|
...Object.values( subduxes ).map( ({mutations}) => Object.keys(mutations) )
|
|
|
|
) );
|
2019-10-17 15:15:10 +00:00
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
let mergedMutations = {};
|
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
let [ globby, nonGlobby ] = fp.partition(
|
|
|
|
([_,{mutations}]) => mutations['*'],
|
|
|
|
Object.entries(subduxes)
|
|
|
|
);
|
2019-10-19 17:11:30 +00:00
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
globby = globby |> fp.fromPairs |> fp.mapValues(
|
|
|
|
({reducer}) => (_,action={}) => state =>
|
|
|
|
reducer(state,action) );
|
2019-10-19 17:11:30 +00:00
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
const globbyMutation = (payload,action) => u(
|
|
|
|
globby |> fp.mapValues( mut => mut(payload,action) )
|
|
|
|
);
|
2019-10-19 17:11:30 +00:00
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
actions.forEach( action => {
|
|
|
|
mergedMutations[action] = [ globbyMutation ]
|
|
|
|
});
|
2019-10-19 17:11:30 +00:00
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
nonGlobby.forEach( ([slice, {mutations,reducer}]) => {
|
2019-10-19 17:11:30 +00:00
|
|
|
Object.entries(mutations).forEach(([type,mutation]) => {
|
|
|
|
const localized = (payload=null,action={}) => u.updateIn( slice, mutation(payload,action) );
|
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
mergedMutations[type].push(localized);
|
2019-10-19 17:11:30 +00:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.entries(mutations).forEach(([type,mutation]) => {
|
2019-10-19 21:54:46 +00:00
|
|
|
mergedMutations[type].push(mutation);
|
2019-10-19 17:11:30 +00:00
|
|
|
});
|
|
|
|
|
2019-10-19 21:54:46 +00:00
|
|
|
return mergedMutations |> fp.mapValues( composeMutations );
|
2019-10-17 15:15:10 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
function buildMiddleware({effects={},subduxes={}},{actions}) {
|
2019-10-17 15:15:10 +00:00
|
|
|
return api => {
|
|
|
|
|
|
|
|
for ( let type in actions ) {
|
|
|
|
api.dispatch[type] = (...args) => api.dispatch( actions[type](...args) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return original_next => {
|
|
|
|
return [
|
|
|
|
...fp.toPairs(effects).map(([type,effect])=> {
|
|
|
|
return api => next => action => {
|
|
|
|
if( action.type !== type ) return next(action);
|
|
|
|
|
|
|
|
return effect(api)(next)(action);
|
|
|
|
};
|
|
|
|
}),
|
2019-10-19 17:11:30 +00:00
|
|
|
...fp.map( 'middleware', subduxes )
|
2019-10-17 15:15:10 +00:00
|
|
|
].filter(x=>x).reduceRight( (next,mw) => mw(api)(next), original_next )
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updux(config) {
|
|
|
|
const dux = {};
|
|
|
|
|
|
|
|
dux.actions = buildActions(config);
|
|
|
|
|
|
|
|
dux.initial = buildInitial(config);
|
|
|
|
|
|
|
|
dux.mutations = buildMutations(config);
|
|
|
|
|
2019-10-19 17:11:30 +00:00
|
|
|
dux.upreducer = (action={}) => state => {
|
2019-10-17 15:15:10 +00:00
|
|
|
if (state === null) state = dux.initial;
|
|
|
|
|
|
|
|
const a =
|
|
|
|
dux.mutations[action.type] ||
|
|
|
|
dux.mutations['*'] ||
|
|
|
|
(() => state => state);
|
|
|
|
|
|
|
|
return a(action.payload, action)(state);
|
|
|
|
};
|
|
|
|
|
|
|
|
dux.reducer = (state, action) => {
|
|
|
|
return dux.upreducer(action)(state);
|
|
|
|
};
|
|
|
|
|
|
|
|
dux.middleware = buildMiddleware(config,dux);
|
|
|
|
|
|
|
|
dux.createStore = () => {
|
|
|
|
const store = createStore( dux.reducer, dux.initial,
|
|
|
|
applyMiddleware( dux.middleware)
|
|
|
|
);
|
|
|
|
for ( let a in dux.actions ) {
|
|
|
|
store.dispatch[a] = (...args) => {
|
|
|
|
store.dispatch(dux.actions[a](...args))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return store;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return dux;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default updux;
|