From 3aac2e092e5982336c3f5b89c5a674caa4a746c3 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 24 Aug 2022 21:19:27 -0400 Subject: [PATCH] wip --- docs/recipes.md | 6 ++-- package.json | 9 ++--- src/Updux.test.ts | 10 +++--- src/Updux.ts | 69 ++++++++++++++++++++++++++++-------- src/actions.test.js | 13 ------- src/actions.test.ts | 34 ++++++++++++++++++ src/actions.ts | 13 +++---- src/buildInitial/index.ts | 3 +- src/buildInitial/test.ts | 2 +- src/buildMiddleware/index.ts | 11 +++--- src/buildSelectors/index.ts | 2 +- src/buildUpreducer.js | 2 +- src/documentation.test.ts | 2 +- src/index.js | 2 -- src/index.ts | 3 ++ src/initial.test.ts | 2 +- src/splat.test.js | 2 +- src/types.test.ts | 4 +-- src/types.ts | 2 +- tsconfig.json | 4 +-- 20 files changed, 132 insertions(+), 63 deletions(-) delete mode 100644 src/actions.test.js create mode 100644 src/actions.test.ts delete mode 100644 src/index.js create mode 100644 src/index.ts diff --git a/docs/recipes.md b/docs/recipes.md index 7a1cd48..4e45ad8 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -63,7 +63,7 @@ import Updux from 'updux'; const updux = new Updux({ initial: { counter: 0 }, mutations: { - add: (inc=1) => state => { counter: counter + inc } + add: (inc=1) => state => ({ counter: state.counter + inc }) } }); @@ -74,7 +74,7 @@ Converting it to Immer would look like: ``` import Updux from 'updux'; -import { produce } from 'Immer'; +import { produce } from 'immer'; const updux = new Updux({ initial: { counter: 0 }, @@ -91,7 +91,7 @@ can be used to wrap all mutations with it: ``` import Updux from 'updux'; -import { produce } from 'Immer'; +import { produce } from 'immer'; const updux = new Updux({ initial: { counter: 0 }, diff --git a/package.json b/package.json index a6bd8bf..3d875bb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { + "type": "module", + "types": "./dist", "dependencies": { "@yanick/updeep": "link:../updeep", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "lodash-es": "^4.17.21", "moize": "^6.1.0", "redux": "^4.0.5", @@ -39,10 +41,10 @@ "ts-node": "^8.10.2", "tsd": "^0.17.0", "typedoc": "0.22.5", - "typescript": "^4.4.3" + "typescript": "^4.4.4" }, "license": "MIT", - "main": "src/index.js", + "main": "dist/index.js", "name": "updux", "description": "Updeep-friendly Redux helper framework", "scripts": { @@ -66,7 +68,6 @@ "url": "https://github.com/yanick/updux/issues" }, "homepage": "https://github.com/yanick/updux#readme", - "types": "./dist/index.d.ts", "prettier": { "tabWidth": 4, "singleQuote": true, diff --git a/src/Updux.test.ts b/src/Updux.test.ts index 405c74d..f510143 100644 --- a/src/Updux.test.ts +++ b/src/Updux.test.ts @@ -1,5 +1,5 @@ -import { Updux } from './Updux'; -import { action } from './actions'; +import { Updux } from './Updux.js'; +import { action } from './actions.js'; test('basic state', () => { const alpha = new Updux({ @@ -96,8 +96,8 @@ test('basic selectors', () => { test('mutations', () => { const alpha = new Updux({ initial: { quux: 3 }, + actions: { add: (x:number)=>x }, }); - alpha.setAction('add'); alpha.setMutation('add', (toAdd) => (state) => ({ quux: state.quux + toAdd, })); @@ -108,8 +108,10 @@ test('mutations', () => { bar: 4, }, subduxes: { alpha }, + actions: { + subtract: ()=>{}, + } }); - dux.setAction('subtract'); dux.setMutation('add', (toAdd) => (state) => ({ ...state, foo: state.foo + toAdd, diff --git a/src/Updux.ts b/src/Updux.ts index 76f9848..586b803 100644 --- a/src/Updux.ts +++ b/src/Updux.ts @@ -1,15 +1,19 @@ /* TODO change * for leftovers to +, change subscriptions to reactions */ import moize from 'moize'; -import u from '@yanick/updeep'; +import u from 'updeep'; import { createStore as reduxCreateStore, applyMiddleware } from 'redux'; -import { get, map, mapValues, merge, difference } from 'lodash'; +import { get, map, mapValues, merge, difference } from 'lodash-es'; -import { buildInitial } from './buildInitial'; -import { buildActions } from './buildActions'; -import { buildSelectors } from './buildSelectors'; -import { action } from './actions'; -import { buildUpreducer } from './buildUpreducer'; -import { buildMiddleware, augmentMiddlewareApi } from './buildMiddleware'; +import { buildInitial } from './buildInitial/index.js'; +import { buildActions } from './buildActions/index.js'; +import { buildSelectors } from './buildSelectors/index.js'; +import { action } from './actions.js'; +import { buildUpreducer } from './buildUpreducer.js'; +import { + buildMiddleware, + augmentMiddlewareApi, + effectToMiddleware, +} from './buildMiddleware/index.js'; import { AggregateDuxActions, @@ -18,7 +22,9 @@ import { ItemsOf, Reducer, Upreducer, -} from './types'; +} from './types.js'; + +type Mutation = (payload:TAction['payload'], action:TAction) => (state: TState) => TState; /** * Configuration object typically passed to the constructor of the class Updux. @@ -99,6 +105,8 @@ export interface UpduxConfig< AggregateDuxState, ItemsOf> >; + + middlewareWrapper?: Function; } export class Updux< @@ -121,7 +129,11 @@ export class Updux< #mappedReaction = undefined; #upreducerWrapper = undefined; - constructor(config: UpduxConfig) { + #middlewareWrapper = undefined; + + constructor( + config: UpduxConfig + ) { this.#initial = config.initial ?? {}; this.#subduxes = config.subduxes ?? {}; @@ -165,6 +177,8 @@ export class Updux< this.#mappedReaction = config.mappedReaction; this.#upreducerWrapper = config.upreducerWrapper; + + this.#middlewareWrapper = config.middlewareWrapper; } #memoInitial = moize(buildInitial); @@ -185,10 +199,16 @@ export class Updux< this.#effects, this.actions, this.selectors, - this.#subduxes + this.#subduxes, + this.#middlewareWrapper, + this ); } + setMiddlewareWrapper(wrapper: Function) { + this.#middlewareWrapper = wrapper; + } + /** @member { unknown } */ get initial(): AggregateDuxState { return this.#memoInitial(this.#initial, this.#subduxes); @@ -206,6 +226,8 @@ export class Updux< ); } + get subduxes() { return this.#subduxes } + get upreducer(): Upreducer< AggregateDuxState, ItemsOf> @@ -229,6 +251,11 @@ export class Updux< this.#reactions = [...this.#reactions, subscription]; } + addReaction(reaction) { + this.#reactions = [...this.#reactions, reaction]; + } + + setAction(type, payloadFunc?: (...args: any) => any) { const theAction = action(type, payloadFunc); @@ -246,7 +273,8 @@ export class Updux< return func; } - setMutation(name, mutation) { + setMutation>(name: TAction, mutation: Mutation, + ReturnType[TAction]>>) { if (typeof name === 'function') name = name.type; this.#mutations = { @@ -257,11 +285,15 @@ export class Updux< return mutation; } - addEffect(action, effect) { + addEffect(action: TType, effect: E): E { this.#effects = [...this.#effects, [action, effect]]; return effect; } + augmentMiddlewareApi(api) { + return augmentMiddlewareApi(api, this.actions, this.selectors); + } + splatSubscriber(store, inner, splatReaction) { const cache = {}; @@ -366,9 +398,12 @@ export class Updux< return { unsub: () => results.forEach(({ unsub }) => unsub()), - subscriber: () => results.forEach(({ subscriber }) => subscriber()), + subscriber: () => + results.forEach(({ subscriber }) => subscriber()), subscriberRaw: (...args) => - results.forEach(({ subscriberRaw }) => subscriberRaw(...args)), + results.forEach(({ subscriberRaw }) => + subscriberRaw(...args) + ), }; } @@ -415,4 +450,8 @@ export class Updux< return store; } + + effectToMiddleware(effect) { + return effectToMiddleware(effect, this.actions, this.selectors); + } } diff --git a/src/actions.test.js b/src/actions.test.js deleted file mode 100644 index a5accb6..0000000 --- a/src/actions.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import { action } from './actions'; - -test('action generators', () => { - const foo = action('foo'); - - expect(foo.type).toEqual('foo'); - expect(foo()).toMatchObject({ type: 'foo' }); - - const bar = action('bar'); - - expect(bar.type).toEqual('bar'); - expect(bar()).toMatchObject({ type: 'bar' }); -}); diff --git a/src/actions.test.ts b/src/actions.test.ts new file mode 100644 index 0000000..cf8dbd5 --- /dev/null +++ b/src/actions.test.ts @@ -0,0 +1,34 @@ +import { action } from './actions.js'; +import u from 'updeep'; + +test('action generators', () => { + const foo = action('foo'); + + expect(foo.type).toEqual('foo'); + expect(foo()).toMatchObject({ type: 'foo' }); + + const bar = action('bar'); + + expect(bar.type).toEqual('bar'); + expect(bar()).toMatchObject({ type: 'bar' }); + + const action3 = action('a3', (x:number) => x); + + expect(action3(12)).toMatchObject({ + type: 'a3', + payload: 12, + }); + + const action4 = action('a4', undefined, u.updateIn('meta.x','yay')); + + expect(action4(13)).toMatchObject({ + type: 'a4', + payload: 13, + meta: {x:'yay'}, + }); + + expect(action4.type).toEqual('a4'); + + + +}); diff --git a/src/actions.ts b/src/actions.ts index 957ee4b..ce4fc93 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -37,7 +37,7 @@ export function action< payloadFunction?: TPayload, transformer?: Function ): ActionGenerator { - const generator = function (...payloadArg) { + let generator : any= function (...payloadArg) { const result: Action = { type }; if (payloadFunction) { @@ -49,11 +49,12 @@ export function action< return result; }; + if(transformer) { + const orig = generator; + generator = (...args: any) => transformer(orig(...args), args); + } + generator.type = type; - return ( - transformer - ? (...args: any) => transformer(generator(...args), args) - : generator - ) as any; + return generator; } diff --git a/src/buildInitial/index.ts b/src/buildInitial/index.ts index 568f7b0..c3e6dc8 100644 --- a/src/buildInitial/index.ts +++ b/src/buildInitial/index.ts @@ -1,4 +1,5 @@ -import { isPlainObject, mapValues } from 'lodash'; +import { mapValues } from 'lodash-es'; +import isPlainObject from 'lodash/isPlainObject.js'; import u from 'updeep'; export function buildInitial(initial, subduxes = {}) { diff --git a/src/buildInitial/test.ts b/src/buildInitial/test.ts index 74b935d..40a16ad 100644 --- a/src/buildInitial/test.ts +++ b/src/buildInitial/test.ts @@ -1,4 +1,4 @@ -import { buildInitial } from '.'; +import { buildInitial } from './index.js'; test('basic', () => { expect(buildInitial({ a: 1 }, { b: { initial: { c: 2 } } })).toMatchObject({ diff --git a/src/buildMiddleware/index.ts b/src/buildMiddleware/index.ts index 2f7a47e..433c531 100644 --- a/src/buildMiddleware/index.ts +++ b/src/buildMiddleware/index.ts @@ -1,6 +1,5 @@ import u from 'updeep'; -import { mapValues, map, get } from 'lodash'; -import { Updux } from '../Updux.js'; +import { mapValues, map, get } from 'lodash-es'; const middlewareFor = (type, middleware) => (api) => (next) => (action) => { if (type !== '*' && action.type !== type) return next(action); @@ -66,7 +65,9 @@ export function buildMiddleware( effects = [], actions = {}, selectors = {}, - sub = {} + sub = {}, + wrapper = undefined, + dux = undefined, ) { let inner = map(sub, ({ middleware }, slice) => slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined @@ -76,7 +77,9 @@ export function buildMiddleware( effectToMiddleware(effect, actions, selectors) ); - const mws = [...local, ...inner]; + let mws = [...local, ...inner]; + + if( wrapper ) mws = wrapper(mws,dux); return composeMw(mws); } diff --git a/src/buildSelectors/index.ts b/src/buildSelectors/index.ts index 36cbb35..3b58af7 100644 --- a/src/buildSelectors/index.ts +++ b/src/buildSelectors/index.ts @@ -1,4 +1,4 @@ -import { map, mapValues, merge } from 'lodash'; +import { map, mapValues, merge } from 'lodash-es'; export function buildSelectors( localSelectors, diff --git a/src/buildUpreducer.js b/src/buildUpreducer.js index 97f4f81..bbf788b 100644 --- a/src/buildUpreducer.js +++ b/src/buildUpreducer.js @@ -1,5 +1,5 @@ import u from 'updeep'; -import { mapValues } from 'lodash'; +import { mapValues } from 'lodash-es'; export function buildUpreducer( initial, diff --git a/src/documentation.test.ts b/src/documentation.test.ts index 319440f..84f5a1c 100644 --- a/src/documentation.test.ts +++ b/src/documentation.test.ts @@ -1,7 +1,7 @@ import u from 'updeep'; import add from 'lodash/fp/add.js'; -import { Updux } from '.'; +import { Updux } from './index.js'; test('README.md', () => { const otherDux = new Updux({}); diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 9484e74..0000000 --- a/src/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { Updux } from './Updux'; -export { action } from './actions'; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..fbe90c9 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export { Updux, UpduxConfig } from './Updux.js'; +export { action } from './actions.js'; +export { AggregateDuxActions, AggregateDuxState } from './types.js'; diff --git a/src/initial.test.ts b/src/initial.test.ts index f0188ea..4ad629e 100644 --- a/src/initial.test.ts +++ b/src/initial.test.ts @@ -1,4 +1,4 @@ -import { Updux } from './Updux'; +import { Updux } from './Updux.js'; test('initial', () => { const foo = new Updux({ diff --git a/src/splat.test.js b/src/splat.test.js index bb2ab45..24b19de 100644 --- a/src/splat.test.js +++ b/src/splat.test.js @@ -1,4 +1,4 @@ -import { difference, omit } from 'lodash'; +import { difference, omit } from 'lodash-es'; import { Updux } from './Updux'; diff --git a/src/types.test.ts b/src/types.test.ts index 05d436d..38d4b7d 100644 --- a/src/types.test.ts +++ b/src/types.test.ts @@ -1,5 +1,5 @@ -import { Updux } from './Updux'; -import { Action, action, ActionGenerator } from './actions'; +import { Updux } from './Updux.js'; +import { Action, action, ActionGenerator } from './actions.js'; import { expectAssignable, expectType, expectNotType } from 'tsd'; type SimplePayload = (x: number) => number; diff --git a/src/types.ts b/src/types.ts index 3e4460b..f7be509 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { Action, ActionGenerator } from './actions'; +import { Action, ActionGenerator } from './actions.js'; export type Dict = Record; diff --git a/tsconfig.json b/tsconfig.json index a48c5c5..28b1ccd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,9 +4,9 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", - "target": "es2020", + "target": "es2015", "lib": ["es2020"], - "module": "ES2020", + "module": "esnext", "moduleResolution": "Node", "strict": false, "sourceMap": true,