updux/src/index.ts

150 lines
3.6 KiB
TypeScript
Raw Normal View History

2019-10-17 15:15:10 +00:00
import fp from 'lodash/fp';
import u from 'updeep';
2019-10-21 18:16:47 +00:00
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
2019-10-17 15:15:10 +00:00
2019-10-20 15:30:36 +00:00
import buildMiddleware from './buildMiddleware';
2019-10-17 15:15:10 +00:00
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-20 15:30:36 +00:00
function buildActions({mutations = {}, effects = {}, subduxes = {}}) {
2019-10-19 17:11:30 +00:00
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);
}
});
2019-10-20 15:30:36 +00:00
Object.keys(effects).forEach(type => {
if (!actions[type]) {
actions[type] = actionFor(type);
}
});
2019-10-17 15:15:10 +00:00
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-21 18:16:47 +00:00
function buildMutations({mutations = {}, subduxes= {}}: any) {
2019-10-19 17:11:30 +00:00
// 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(
2019-10-21 18:16:47 +00:00
...Object.values( subduxes ).map( ({mutations = {}}:any) => Object.keys(mutations) )
2019-10-19 17:11:30 +00:00
) );
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(
2019-10-21 18:16:47 +00:00
([_,{mutations={}}]:any) => mutations['*'],
2019-10-19 21:54:46 +00:00
Object.entries(subduxes)
);
2019-10-19 17:11:30 +00:00
2019-10-21 15:40:08 +00:00
globby =
fp.flow([
fp.fromPairs,
fp.mapValues(
2019-10-19 21:54:46 +00:00
({reducer}) => (_,action={}) => state =>
2019-10-21 15:40:08 +00:00
reducer(state,action) ),
])(globby);
2019-10-19 17:11:30 +00:00
2019-10-19 21:54:46 +00:00
const globbyMutation = (payload,action) => u(
2019-10-21 18:16:47 +00:00
fp.mapValues( (mut:any) => mut(payload,action) )(globby)
2019-10-19 21:54:46 +00:00
);
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-21 18:16:47 +00:00
nonGlobby.forEach( ([slice, {mutations={},reducer={}}]:any) => {
2019-10-19 17:11:30 +00:00
Object.entries(mutations).forEach(([type,mutation]) => {
2019-10-21 18:16:47 +00:00
const localized = (payload=null,action={}) => u.updateIn( slice )( (mutation as any)(payload,action) );
2019-10-19 17:11:30 +00:00
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-21 15:40:08 +00:00
return fp.mapValues( composeMutations )(mergedMutations);
2019-10-17 15:15:10 +00:00
}
function updux(config) {
2019-10-21 18:16:47 +00:00
const actions = buildActions(config);
2019-10-17 15:15:10 +00:00
2019-10-21 18:16:47 +00:00
const initial = buildInitial(config);
2019-10-17 15:15:10 +00:00
2019-10-21 18:16:47 +00:00
const mutations = buildMutations(config);
2019-10-17 15:15:10 +00:00
2019-10-21 18:16:47 +00:00
const upreducer = (action={}) => state => {
if (state === null) state = initial;
2019-10-17 15:15:10 +00:00
const a =
2019-10-21 18:16:47 +00:00
mutations[(action as any).type] ||
mutations['*'] ||
2019-10-17 15:15:10 +00:00
(() => state => state);
2019-10-21 18:16:47 +00:00
return a((action as any).payload, action)(state);
2019-10-17 15:15:10 +00:00
};
2019-10-21 18:16:47 +00:00
const reducer = (state, action) => {
return upreducer(action)(state);
2019-10-17 15:15:10 +00:00
};
2019-10-21 18:16:47 +00:00
const middleware = buildMiddleware(
config.effects,
actions,
config.subduxes,
);
2019-10-17 15:15:10 +00:00
2019-10-21 18:16:47 +00:00
const createStore = () => {
const store = reduxCreateStore( reducer, initial,
applyMiddleware( middleware)
2019-10-17 15:15:10 +00:00
);
2019-10-21 18:16:47 +00:00
for ( let a in actions ) {
2019-10-17 15:15:10 +00:00
store.dispatch[a] = (...args) => {
2019-10-21 18:16:47 +00:00
store.dispatch(actions[a](...args))
2019-10-17 15:15:10 +00:00
};
}
return store;
}
2019-10-21 18:16:47 +00:00
return {
reducer,
upreducer,
middleware,
createStore,
actions,
mutations,
initial,
};
2019-10-17 15:15:10 +00:00
}
export default updux;