updux/src/reducer.ts
2024-08-10 09:07:23 -04:00

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;
}