diff --git a/src/Updux.ts b/src/Updux.ts index 20221a2..d275700 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -8,23 +8,16 @@ import { import { configureStore, Reducer, - createAction, ActionCreator, - PrepareAction, - ActionCreatorWithPayload, ActionCreatorWithoutPayload, ActionCreatorWithPreparedPayload, } from '@reduxjs/toolkit'; -import { withPayload } from './actions.js'; -import { AggregateActions, Dux, UnionToIntersection } from './types.js'; +import { AggregateActions, Dux } from './types.js'; import { buildActions } from './buildActions.js'; - -type AggregateState = L; +import { buildInitial, AggregateState } from './initial.js'; type MyActionCreator = { type: string } & ((...args: any) => any); -type F = null extends any ? 'u' : 'n'; - type ResolveAction< ActionType extends string, ActionArg extends any, @@ -59,33 +52,37 @@ export default class Updux< T_LocalActions extends { [actionType: string]: any; } = {}, - SUBDUXES extends Record = {}, + T_Subduxes extends Record = {}, > { #localInitial: T_LocalState; #localActions: T_LocalActions; #localMutations: Record< string, - Mutation, AggregateState> + Mutation, AggregateState> > = {}; - #subduxes: SUBDUXES; + #subduxes: T_Subduxes; #name: string; - #actions: AggregateActions, SUBDUXES>; + #actions: AggregateActions, T_Subduxes>; + + #initial: AggregateState; constructor( config: Partial<{ initial: T_LocalState; actions: T_LocalActions; - subduxes: SUBDUXES; + subduxes: T_Subduxes; }>, ) { // TODO check that we can't alter the initial after the fact this.#localInitial = config.initial ?? ({} as T_LocalState); this.#localActions = config.actions ?? ({} as T_LocalActions); - this.#subduxes = config.subduxes ?? ({} as SUBDUXES); + this.#subduxes = config.subduxes ?? ({} as T_Subduxes); this.#actions = buildActions(this.#localActions, this.#subduxes); + + this.#initial = buildInitial(this.#localInitial, this.#subduxes); } get actions() { @@ -94,7 +91,7 @@ export default class Updux< // TODO memoize? get initial() { - return this.#localInitial; + return this.#initial; } createStore( @@ -114,7 +111,7 @@ export default class Updux< // TODO force the actionCreator to be one of the actions? mutation>( actionCreator: A, - mutation: Mutation>, + mutation: Mutation>, ) { this.#localMutations[(actionCreator as any).type] = mutation; } diff --git a/src/buildInitial.test.ts b/src/buildInitial.test.ts new file mode 100644 index 0000000..63743e8 --- /dev/null +++ b/src/buildInitial.test.ts @@ -0,0 +1,21 @@ +import { test, expect } from 'vitest'; +import { buildInitial } from './initial.js'; + +test('basic', () => { + expect( + buildInitial( + { a: 1 }, + { b: { initial: { c: 2 } }, d: { initial: 'e' } }, + ), + ).toEqual({ + a: 1, + b: { c: 2 }, + d: 'e', + }); +}); + +test('throw if subduxes and initial is not an object', () => { + expect(() => { + buildInitial(3, { bar: 'foo' }); + }).toThrow(); +}); diff --git a/src/initial.test.todo b/src/initial.test.todo deleted file mode 100644 index 0aa2a85..0000000 --- a/src/initial.test.todo +++ /dev/null @@ -1,46 +0,0 @@ -import { test, expect } from 'vitest'; - -import { Updux } from './Updux.js'; - -const bar = new Updux({ initial: 123 }); - -const foo = new Updux({ - initial: { root: 'abc' }, - subduxes: { - bar, - }, -}); - -test('single dux', () => { - const foo = new Updux({ - initial: { a: 1 }, - }); - - expect(foo.initial).toEqual({ a: 1 }); -}); - -test('initial value', () => { - expect(foo.initial).toEqual({ - root: 'abc', - bar: 123, - }); -}); - -test('splat initial', async () => { - const bar = new Updux({ - initial: { id: 0 }, - }); - - const foo = new Updux({ - subduxes: { '*': bar }, - }); - - expect(foo.initial).toEqual([]); - - expect( - new Updux({ - initial: 'overriden', - subduxes: { '*': bar }, - }).initial, - ).toEqual('overriden'); -}); diff --git a/src/initial.test.ts b/src/initial.test.ts index 32194e6..245632a 100644 --- a/src/initial.test.ts +++ b/src/initial.test.ts @@ -1,5 +1,15 @@ +import { expectType } from './tutorial.test.js'; import Updux from './Updux.js'; +const bar = new Updux({ initial: 123 }); + +const foo = new Updux({ + initial: { root: 'abc' }, + subduxes: { + bar, + }, +}); + test('default', () => { const { initial } = new Updux({}); @@ -29,3 +39,60 @@ test('initial to createStore', () => { b: 4, }); }); + +test('single dux', () => { + const foo = new Updux({ + initial: { a: 1 }, + }); + + expect(foo.initial).toEqual({ a: 1 }); +}); + +// TODO add 'check for no todo eslint rule' +test('initial value', () => { + expect(foo.initial).toEqual({ + root: 'abc', + bar: 123, + }); + + expectType<{ + root: string; + bar: number; + }>(foo.initial); +}); + +test('no initial', () => { + const dux = new Updux({}); + expectType<{}>(dux.initial); + expect(dux.initial).toEqual({}); +}); + +test('no initial for subdux', () => { + const dux = new Updux({ + subduxes: { + bar: new Updux({}), + baz: new Updux({ initial: 'potato' }), + }, + }); + expectType<{ bar: {}; baz: string }>(dux.initial); + expect(dux.initial).toEqual({ bar: {}, baz: 'potato' }); +}); + +test.todo('splat initial', async () => { + const bar = new Updux({ + initial: { id: 0 }, + }); + + const foo = new Updux({ + subduxes: { '*': bar }, + }); + + expect(foo.initial).toEqual([]); + + expect( + new Updux({ + initial: 'overriden', + subduxes: { '*': bar }, + }).initial, + ).toEqual('overriden'); +}); diff --git a/src/initial.ts b/src/initial.ts new file mode 100644 index 0000000..36aaf92 --- /dev/null +++ b/src/initial.ts @@ -0,0 +1,23 @@ +import u from '@yanick/updeep-remeda'; +import * as R from 'remeda'; + +type SubduxState = 'initial' extends keyof S ? S['initial'] : {}; + +export type AggregateState> = LOCAL & + (keyof SUBDUXES extends never + ? {} + : { + [Slice in keyof SUBDUXES]: Slice extends string + ? SubduxState + : never; + }); + +export function buildInitial(localInitial, subduxes) { + if (Object.keys(subduxes).length > 0 && typeof localInitial !== 'object') { + throw new Error( + "can't have subduxes when the initial value is not an object", + ); + } + + return u(localInitial, R.mapValues(subduxes, R.pathOr(['initial'], {}))); +} diff --git a/src/tutorial.test.ts b/src/tutorial.test.ts index 753e746..e763eb4 100644 --- a/src/tutorial.test.ts +++ b/src/tutorial.test.ts @@ -1,7 +1,7 @@ import Updux, { createAction, withPayload } from './index.js'; import u from '@yanick/updeep-remeda'; -const expectType = (value: T) => value; +export const expectType = (value: T) => value; test('initial state', () => { const initial = {