diff --git a/Taskfile.yaml b/Taskfile.yaml index 677cab8..a9d7b7c 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -2,11 +2,23 @@ version: '3' +vars: + PARENT_BRANCH: main + tasks: build: tsc checks: - deps: [test, build] + deps: [lint, test, build] + + integrate: + deps: [checks] + cmds: + - git is-clean + # did we had tests? + - git diff-ls {{.PARENT_BRANCH}} | grep test + - git checkout {{.PARENT_BRANCH}} + - git weld - test: vitest run src test:dev: vitest src diff --git a/src/Updux.test.ts b/src/Updux.test.ts index 1e5f0ea..4aab369 100644 --- a/src/Updux.test.ts +++ b/src/Updux.test.ts @@ -1,15 +1,7 @@ import { test, expect } from 'vitest'; -import Updux from './Updux'; +import Updux from './Updux.js'; test('subdux idempotency', () => { - // const c = new Updux({ - // initialState: 2, - // }); - // const b = new Updux({ - // subduxes: { - // c, - // }, - // }); const foo = new Updux({ subduxes: { a: new Updux({ initialState: 2 }), @@ -18,21 +10,4 @@ test('subdux idempotency', () => { let fooState = foo.reducer(undefined, { type: 'noop' }); expect(foo.reducer(fooState, { type: 'noop' })).toBe(fooState); - - return; - const store = foo.createStore(); - - const s1 = store.getState(); - console.log(s1); - - store.dispatch({ type: 'noop' }); - const s2 = store.getState(); - - expect(s2.a).toBe(s1.a); - - let bState = b.reducer(undefined, { type: 'noop' }); - expect(b.reducer(bState, { type: 'noop' })).toBe(bState); - - expect(s2.b).toBe(s1.b); - expect(s2).toBe(s1); }); diff --git a/src/Updux.ts b/src/Updux.ts index fd3ac49..baa5a6e 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -20,9 +20,14 @@ import { AggregateActions, AggregateSelectors, Dux } from './types.js'; import { buildActions } from './buildActions.js'; import { buildInitial, AggregateState } from './initial.js'; import { buildReducer, MutationCase } from './reducer.js'; -import { augmentGetState, augmentMiddlewareApi, buildEffectsMiddleware, EffectMiddleware } from './effects.js'; +import { + augmentGetState, + augmentMiddlewareApi, + buildEffectsMiddleware, + EffectMiddleware, +} from './effects.js'; import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore.js'; -import prepare from 'immer'; +import { produce } from 'immer'; type MyActionCreator = { type: string } & ((...args: any) => any); @@ -30,8 +35,8 @@ type XSel = R extends Function ? R : () => R; type CurriedSelector = S extends (...args: any) => infer R ? XSel : never; type CurriedSelectors = { - [key in keyof S]: CurriedSelector -} + [key in keyof S]: CurriedSelector; +}; type ResolveAction< ActionType extends string, @@ -40,10 +45,10 @@ type ResolveAction< ? ActionArg : ActionArg extends (...args: any) => any ? ActionCreatorWithPreparedPayload< - Parameters, - ReturnType, - ActionType - > + Parameters, + ReturnType, + ActionType + > : ActionCreatorWithoutPayload; type ResolveActions< @@ -51,21 +56,24 @@ type ResolveActions< [key: string]: any; }, > = { - [ActionType in keyof A]: ActionType extends string + [ActionType in keyof A]: ActionType extends string ? ResolveAction : never; - }; +}; -type Reaction = - (api: M) => (state: S, previousState: S, unsubscribe: () => void) => any; +type Reaction = ( + api: M, +) => (state: S, previousState: S, unsubscribe: () => void) => any; -type AugmentedMiddlewareAPI = - MiddlewareAPI, S> & { - dispatch: A, - getState: CurriedSelectors, - actions: A, - selectors: SELECTORS, - }; +type AugmentedMiddlewareAPI = MiddlewareAPI< + Dispatch, + S +> & { + dispatch: A; + getState: CurriedSelectors; + actions: A; + selectors: SELECTORS; +}; export type Mutation = Action, S = any> = ( payload: A extends { @@ -134,7 +142,7 @@ export default class Updux< .filter(([slice, { selectors }]) => selectors) .map(([slice, { selectors }]) => R.mapValues(selectors, (s) => (state = {}) => { - return s(state?.[slice]) + return s(state?.[slice]); }), ), ); @@ -157,25 +165,34 @@ export default class Updux< ...Object.entries(this.#subduxes).flatMap( ([slice, { effects }]) => { if (!effects) return []; - return effects.map(effect => (api) => effect({ - ...api, - getState: () => api.getState()[slice], - })) - } - ) - ] + return effects.map( + (effect) => (api) => + effect({ + ...api, + getState: () => api.getState()[slice], + }), + ); + }, + ), + ]; } get reactions(): any { - return [...this.#localReactions, - ...Object.entries(this.#subduxes).flatMap( - ([slice, { reactions }]) => reactions.map( - (r) => (api, unsub) => r({ - ...api, - getState: () => api.getState()[slice], - }, unsub) - ) - ) + return [ + ...this.#localReactions, + ...Object.entries(this.#subduxes).flatMap( + ([slice, { reactions }]) => + reactions.map( + (r) => (api, unsub) => + r( + { + ...api, + getState: () => api.getState()[slice], + }, + unsub, + ), + ), + ), ]; } @@ -192,7 +209,6 @@ export default class Updux< this.selectors, ); - const store = configureStore({ reducer: this.reducer as Reducer< AggregateState, @@ -208,7 +224,7 @@ export default class Updux< const action = (this.actions as any)[a](...args); dispatch(action); return action; - } + }; } store.getState = augmentGetState(store.getState, this.selectors); @@ -223,20 +239,16 @@ export default class Updux< (store as any).actions = this.actions; (store as any).selectors = this.selectors; - - return store as ToolkitStore< - AggregateState - > & AugmentedMiddlewareAPI< - AggregateState, - AggregateActions< - ResolveActions, - T_Subduxes - >, AggregateSelectors< - T_LocalSelectors, - T_Subduxes, - AggregateState - > - >; + return store as ToolkitStore> & + AugmentedMiddlewareAPI< + AggregateState, + AggregateActions, T_Subduxes>, + AggregateSelectors< + T_LocalSelectors, + T_Subduxes, + AggregateState + > + >; } get selectors(): AggregateSelectors< @@ -280,7 +292,7 @@ export default class Updux< matcher = matcher.match; } - const immerMutation = (...args) => prepare(mutation(...args)); + const immerMutation = (...args) => produce(mutation(...args)); this.#localMutations.push({ terminal, @@ -309,31 +321,27 @@ export default class Updux< this.actions, this.selectors, ); - } #localReactions: any[] = []; - addReaction(reaction: Reaction, - AugmentedMiddlewareAPI< + addReaction( + reaction: Reaction< AggregateState, - AggregateActions< - ResolveActions, - T_Subduxes - >, AggregateSelectors< - T_LocalSelectors, - T_Subduxes, - AggregateState + AugmentedMiddlewareAPI< + AggregateState, + AggregateActions, T_Subduxes>, + AggregateSelectors< + T_LocalSelectors, + T_Subduxes, + AggregateState + > > - > - >) { - + >, + ) { let previous: any; const memoized = (api: any) => { - api = augmentMiddlewareApi(api, - this.actions, - this.selectors - ); + api = augmentMiddlewareApi(api, this.actions, this.selectors); const r = reaction(api); return (unsub: () => void) => { const state = api.getState(); @@ -341,19 +349,21 @@ export default class Updux< let p = previous; previous = state; r(state, p, unsub); - } - } - ; - + }; + }; this.#localReactions.push(memoized); } // internal method REMOVE subscribeTo(store, subscription) { - const localStore = augmentMiddlewareApi({ - ...store, - subscribe: (subscriber) => this.subscribeTo(store, subscriber), // TODO not sure - }, this.actions, this.selectors); + const localStore = augmentMiddlewareApi( + { + ...store, + subscribe: (subscriber) => this.subscribeTo(store, subscriber), // TODO not sure + }, + this.actions, + this.selectors, + ); const subscriber = subscription(localStore); @@ -371,4 +381,3 @@ export default class Updux< return store.subscribe(memoSub); } } - diff --git a/src/buildSelectors/index.js b/src/buildSelectors/index.js index dfd7550..ff489f7 100644 --- a/src/buildSelectors/index.js +++ b/src/buildSelectors/index.js @@ -1,19 +1,28 @@ import { map, mapValues, merge } from 'lodash-es'; -export function buildSelectors(localSelectors, splatSelector = {}, subduxes = {}) { +export function buildSelectors( + localSelectors, + splatSelector = {}, + subduxes = {}, +) { const subSelectors = map(subduxes, ({ selectors }, slice) => { - if (!selectors) - return {}; - if (slice === '*') - return {}; + if (!selectors) return {}; + if (slice === '*') return {}; return mapValues(selectors, (func) => (state) => func(state[slice])); }); let splat = {}; for (const name in splatSelector) { splat[name] = - (state) => (...args) => { + (state) => + (...args) => { const value = splatSelector[name](state)(...args); const res = () => value; - return merge(res, mapValues(subduxes['*'].selectors, (selector) => () => selector(value))); + return merge( + res, + mapValues( + subduxes['*'].selectors, + (selector) => () => selector(value), + ), + ); }; } return merge({}, ...subSelectors, localSelectors, splat); diff --git a/src/effects.test.ts b/src/effects.test.ts index a6cde57..007abb4 100644 --- a/src/effects.test.ts +++ b/src/effects.test.ts @@ -39,7 +39,7 @@ test('buildEffectsMiddleware', () => { expect(seen).toEqual(0); const dispatch = vi.fn(); - mw({ getState: () => 'the state', dispatch })(() => { })({ + mw({ getState: () => 'the state', dispatch })(() => {})({ type: 'noop', }); expect(seen).toEqual(1); @@ -82,7 +82,7 @@ test('subdux', () => { }); let seen = 0; - bar.addEffect((api) => next => action => { + bar.addEffect((api) => (next) => (action) => { seen++; expect(api.getState()).toBe('bar state'); next(action); @@ -93,7 +93,7 @@ test('subdux', () => { loaded: true, }, subduxes: { - bar + bar, }, }); diff --git a/src/initial.test.ts b/src/initial.test.ts index 4446ee7..5dc76b2 100644 --- a/src/initial.test.ts +++ b/src/initial.test.ts @@ -34,7 +34,9 @@ test('initialState to createStore', () => { initialState, }); - expect(dux.createStore({ initialState: { a: 3, b: 4 } }).getState()).toEqual({ + expect( + dux.createStore({ initialState: { a: 3, b: 4 } }).getState(), + ).toEqual({ a: 3, b: 4, }); diff --git a/src/initial.ts b/src/initial.ts index 41eb872..91aa451 100644 --- a/src/initial.ts +++ b/src/initial.ts @@ -19,5 +19,8 @@ export function buildInitial(localInitial, subduxes) { ); } - return u(localInitial, R.mapValues(subduxes, R.pathOr(['initialState'], {}))); + return u( + localInitial, + R.mapValues(subduxes, R.pathOr(['initialState'], {})), + ); } diff --git a/src/reducer.ts b/src/reducer.ts index 1f7b956..c592d86 100644 --- a/src/reducer.ts +++ b/src/reducer.ts @@ -4,7 +4,7 @@ import * as R from 'remeda'; import { Dux } from './types.js'; import { Mutation } from './Updux.js'; import u from '@yanick/updeep-remeda'; -import prepare from 'immer'; +import { produce } from 'immer'; export type MutationCase = { matcher: (action: Action) => boolean; @@ -36,7 +36,7 @@ export function buildReducer( .forEach(({ mutation, terminal: t }) => { if (t) terminal = true; didSomething = true; - state = prepare( + state = produce( state, mutation((action as any).payload, action), ); diff --git a/src/selectors.test.ts b/src/selectors.test.ts index b7efb51..f637c88 100644 --- a/src/selectors.test.ts +++ b/src/selectors.test.ts @@ -19,8 +19,8 @@ test('basic selectors', () => { getY: ({ y }: { y: number }) => y, getYPlus: ({ y }) => - (incr: number) => - (y + incr) as number, + (incr: number) => + (y + incr) as number, }, }), },