67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import * as R from 'remeda';
|
|
import u from '@yanick/updeep-remeda';
|
|
import * as rtk from '@reduxjs/toolkit';
|
|
import { DuxConfig, Mutation } from './types.js';
|
|
import { D } from '@mobily/ts-belt';
|
|
import Updux from './Updux.js';
|
|
import { AnyAction } from '@reduxjs/toolkit';
|
|
|
|
export type MutationCase = {
|
|
matcher: (action: rtk.AnyAction) => boolean;
|
|
mutation: Mutation;
|
|
terminal: boolean;
|
|
};
|
|
|
|
export function buildReducer(
|
|
initialStateState: unknown,
|
|
mutations: MutationCase[] = [],
|
|
defaultMutation?: Omit<MutationCase, 'matcher'>,
|
|
subduxes: Record<string, Updux<any>> = {},
|
|
inheritedReducer?: (state: any, action: AnyAction) => any,
|
|
) {
|
|
const subReducers = D.map(subduxes, D.getUnsafe('reducer'));
|
|
|
|
const reducer = (state = initialStateState, action: rtk.AnyAction) => {
|
|
if (!action?.type) throw new Error('reducer called with a bad action');
|
|
|
|
let active = mutations.filter(({ matcher }) => matcher(action));
|
|
|
|
if (active.length === 0 && defaultMutation)
|
|
active.push(defaultMutation as any);
|
|
|
|
if (!active.some(R.prop<any, any>('terminal')) && inheritedReducer) {
|
|
active.push({
|
|
mutation: (_payload, action) => (state) => {
|
|
return u(state, inheritedReducer(state, action));
|
|
},
|
|
} as any);
|
|
}
|
|
if (
|
|
!active.some(R.prop<any, any>('terminal')) &&
|
|
D.values(subReducers).length > 0
|
|
) {
|
|
active.push({
|
|
mutation: (payload, action) => (state) => {
|
|
return u(
|
|
state,
|
|
R.mapValues(
|
|
subReducers,
|
|
(reducer, slice) => (state) => {
|
|
return (reducer as any)(state, action);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
} as any);
|
|
}
|
|
|
|
return active.reduce(
|
|
(state, { mutation }) =>
|
|
mutation((action as any).payload, action)(state),
|
|
state,
|
|
);
|
|
};
|
|
|
|
return reducer;
|
|
}
|