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'; export type MutationCase = { matcher: (action: rtk.AnyAction) => boolean; mutation: Mutation; terminal: boolean; }; export function buildReducer( initialStateState: unknown, mutations: MutationCase[] = [], defaultMutation?: Omit, subduxes: Record> = {}, ) { const subReducers = D.map(subduxes, D.getUnsafe('reducer')); // TODO matcherMutation // TODO defaultMutation // 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('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); } // frozen objects don't play well with immer // if (Object.isFrozen(state)) { // state = { ...(state as any) }; // } return active.reduce( (state, { mutation }) => mutation((action as any).payload, action)(state), state, ); }; return reducer; }