wip
This commit is contained in:
parent
bb5218d2f6
commit
3aac2e092e
@ -63,7 +63,7 @@ import Updux from 'updux';
|
|||||||
const updux = new Updux({
|
const updux = new Updux({
|
||||||
initial: { counter: 0 },
|
initial: { counter: 0 },
|
||||||
mutations: {
|
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 Updux from 'updux';
|
||||||
import { produce } from 'Immer';
|
import { produce } from 'immer';
|
||||||
|
|
||||||
const updux = new Updux({
|
const updux = new Updux({
|
||||||
initial: { counter: 0 },
|
initial: { counter: 0 },
|
||||||
@ -91,7 +91,7 @@ can be used to wrap all mutations with it:
|
|||||||
|
|
||||||
```
|
```
|
||||||
import Updux from 'updux';
|
import Updux from 'updux';
|
||||||
import { produce } from 'Immer';
|
import { produce } from 'immer';
|
||||||
|
|
||||||
const updux = new Updux({
|
const updux = new Updux({
|
||||||
initial: { counter: 0 },
|
initial: { counter: 0 },
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@yanick/updeep": "link:../updeep",
|
"@yanick/updeep": "link:../updeep",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"moize": "^6.1.0",
|
"moize": "^6.1.0",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
@ -39,10 +41,10 @@
|
|||||||
"ts-node": "^8.10.2",
|
"ts-node": "^8.10.2",
|
||||||
"tsd": "^0.17.0",
|
"tsd": "^0.17.0",
|
||||||
"typedoc": "0.22.5",
|
"typedoc": "0.22.5",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/index.js",
|
"main": "dist/index.js",
|
||||||
"name": "updux",
|
"name": "updux",
|
||||||
"description": "Updeep-friendly Redux helper framework",
|
"description": "Updeep-friendly Redux helper framework",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -66,7 +68,6 @@
|
|||||||
"url": "https://github.com/yanick/updux/issues"
|
"url": "https://github.com/yanick/updux/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/yanick/updux#readme",
|
"homepage": "https://github.com/yanick/updux#readme",
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Updux } from './Updux';
|
import { Updux } from './Updux.js';
|
||||||
import { action } from './actions';
|
import { action } from './actions.js';
|
||||||
|
|
||||||
test('basic state', () => {
|
test('basic state', () => {
|
||||||
const alpha = new Updux({
|
const alpha = new Updux({
|
||||||
@ -96,8 +96,8 @@ test('basic selectors', () => {
|
|||||||
test('mutations', () => {
|
test('mutations', () => {
|
||||||
const alpha = new Updux({
|
const alpha = new Updux({
|
||||||
initial: { quux: 3 },
|
initial: { quux: 3 },
|
||||||
|
actions: { add: (x:number)=>x },
|
||||||
});
|
});
|
||||||
alpha.setAction('add');
|
|
||||||
alpha.setMutation('add', (toAdd) => (state) => ({
|
alpha.setMutation('add', (toAdd) => (state) => ({
|
||||||
quux: state.quux + toAdd,
|
quux: state.quux + toAdd,
|
||||||
}));
|
}));
|
||||||
@ -108,8 +108,10 @@ test('mutations', () => {
|
|||||||
bar: 4,
|
bar: 4,
|
||||||
},
|
},
|
||||||
subduxes: { alpha },
|
subduxes: { alpha },
|
||||||
|
actions: {
|
||||||
|
subtract: ()=>{},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
dux.setAction('subtract');
|
|
||||||
dux.setMutation('add', (toAdd) => (state) => ({
|
dux.setMutation('add', (toAdd) => (state) => ({
|
||||||
...state,
|
...state,
|
||||||
foo: state.foo + toAdd,
|
foo: state.foo + toAdd,
|
||||||
|
69
src/Updux.ts
69
src/Updux.ts
@ -1,15 +1,19 @@
|
|||||||
/* TODO change * for leftovers to +, change subscriptions to reactions */
|
/* TODO change * for leftovers to +, change subscriptions to reactions */
|
||||||
import moize from 'moize';
|
import moize from 'moize';
|
||||||
import u from '@yanick/updeep';
|
import u from 'updeep';
|
||||||
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
|
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 { buildInitial } from './buildInitial/index.js';
|
||||||
import { buildActions } from './buildActions';
|
import { buildActions } from './buildActions/index.js';
|
||||||
import { buildSelectors } from './buildSelectors';
|
import { buildSelectors } from './buildSelectors/index.js';
|
||||||
import { action } from './actions';
|
import { action } from './actions.js';
|
||||||
import { buildUpreducer } from './buildUpreducer';
|
import { buildUpreducer } from './buildUpreducer.js';
|
||||||
import { buildMiddleware, augmentMiddlewareApi } from './buildMiddleware';
|
import {
|
||||||
|
buildMiddleware,
|
||||||
|
augmentMiddlewareApi,
|
||||||
|
effectToMiddleware,
|
||||||
|
} from './buildMiddleware/index.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggregateDuxActions,
|
AggregateDuxActions,
|
||||||
@ -18,7 +22,9 @@ import {
|
|||||||
ItemsOf,
|
ItemsOf,
|
||||||
Reducer,
|
Reducer,
|
||||||
Upreducer,
|
Upreducer,
|
||||||
} from './types';
|
} from './types.js';
|
||||||
|
|
||||||
|
type Mutation<TState,TAction extends { payload?: any }> = (payload:TAction['payload'], action:TAction) => (state: TState) => TState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration object typically passed to the constructor of the class Updux.
|
* Configuration object typically passed to the constructor of the class Updux.
|
||||||
@ -99,6 +105,8 @@ export interface UpduxConfig<
|
|||||||
AggregateDuxState<TState, TSubduxes>,
|
AggregateDuxState<TState, TSubduxes>,
|
||||||
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
|
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
middlewareWrapper?: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Updux<
|
export class Updux<
|
||||||
@ -121,7 +129,11 @@ export class Updux<
|
|||||||
#mappedReaction = undefined;
|
#mappedReaction = undefined;
|
||||||
#upreducerWrapper = undefined;
|
#upreducerWrapper = undefined;
|
||||||
|
|
||||||
constructor(config: UpduxConfig<TState, TActions, TSelectors, TSubduxes>) {
|
#middlewareWrapper = undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
config: UpduxConfig<TState, TActions, TSelectors, TSubduxes>
|
||||||
|
) {
|
||||||
this.#initial = config.initial ?? {};
|
this.#initial = config.initial ?? {};
|
||||||
this.#subduxes = config.subduxes ?? {};
|
this.#subduxes = config.subduxes ?? {};
|
||||||
|
|
||||||
@ -165,6 +177,8 @@ export class Updux<
|
|||||||
this.#mappedReaction = config.mappedReaction;
|
this.#mappedReaction = config.mappedReaction;
|
||||||
|
|
||||||
this.#upreducerWrapper = config.upreducerWrapper;
|
this.#upreducerWrapper = config.upreducerWrapper;
|
||||||
|
|
||||||
|
this.#middlewareWrapper = config.middlewareWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
#memoInitial = moize(buildInitial);
|
#memoInitial = moize(buildInitial);
|
||||||
@ -185,10 +199,16 @@ export class Updux<
|
|||||||
this.#effects,
|
this.#effects,
|
||||||
this.actions,
|
this.actions,
|
||||||
this.selectors,
|
this.selectors,
|
||||||
this.#subduxes
|
this.#subduxes,
|
||||||
|
this.#middlewareWrapper,
|
||||||
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMiddlewareWrapper(wrapper: Function) {
|
||||||
|
this.#middlewareWrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
/** @member { unknown } */
|
/** @member { unknown } */
|
||||||
get initial(): AggregateDuxState<TState, TSubduxes> {
|
get initial(): AggregateDuxState<TState, TSubduxes> {
|
||||||
return this.#memoInitial(this.#initial, this.#subduxes);
|
return this.#memoInitial(this.#initial, this.#subduxes);
|
||||||
@ -206,6 +226,8 @@ export class Updux<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get subduxes() { return this.#subduxes }
|
||||||
|
|
||||||
get upreducer(): Upreducer<
|
get upreducer(): Upreducer<
|
||||||
AggregateDuxState<TState, TSubduxes>,
|
AggregateDuxState<TState, TSubduxes>,
|
||||||
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
|
ItemsOf<AggregateDuxActions<TActions, TSubduxes>>
|
||||||
@ -229,6 +251,11 @@ export class Updux<
|
|||||||
this.#reactions = [...this.#reactions, subscription];
|
this.#reactions = [...this.#reactions, subscription];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addReaction(reaction) {
|
||||||
|
this.#reactions = [...this.#reactions, reaction];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setAction(type, payloadFunc?: (...args: any) => any) {
|
setAction(type, payloadFunc?: (...args: any) => any) {
|
||||||
const theAction = action(type, payloadFunc);
|
const theAction = action(type, payloadFunc);
|
||||||
|
|
||||||
@ -246,7 +273,8 @@ export class Updux<
|
|||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMutation(name, mutation) {
|
setMutation<TAction extends keyof AggregateDuxActions<TActions,TSubduxes>>(name: TAction, mutation: Mutation<AggregateDuxState<TState, TSubduxes>,
|
||||||
|
ReturnType<AggregateDuxActions<TActions,TSubduxes>[TAction]>>) {
|
||||||
if (typeof name === 'function') name = name.type;
|
if (typeof name === 'function') name = name.type;
|
||||||
|
|
||||||
this.#mutations = {
|
this.#mutations = {
|
||||||
@ -257,11 +285,15 @@ export class Updux<
|
|||||||
return mutation;
|
return mutation;
|
||||||
}
|
}
|
||||||
|
|
||||||
addEffect(action, effect) {
|
addEffect<TType, E>(action: TType, effect: E): E {
|
||||||
this.#effects = [...this.#effects, [action, effect]];
|
this.#effects = [...this.#effects, [action, effect]];
|
||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
augmentMiddlewareApi(api) {
|
||||||
|
return augmentMiddlewareApi(api, this.actions, this.selectors);
|
||||||
|
}
|
||||||
|
|
||||||
splatSubscriber(store, inner, splatReaction) {
|
splatSubscriber(store, inner, splatReaction) {
|
||||||
const cache = {};
|
const cache = {};
|
||||||
|
|
||||||
@ -366,9 +398,12 @@ export class Updux<
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
unsub: () => results.forEach(({ unsub }) => unsub()),
|
unsub: () => results.forEach(({ unsub }) => unsub()),
|
||||||
subscriber: () => results.forEach(({ subscriber }) => subscriber()),
|
subscriber: () =>
|
||||||
|
results.forEach(({ subscriber }) => subscriber()),
|
||||||
subscriberRaw: (...args) =>
|
subscriberRaw: (...args) =>
|
||||||
results.forEach(({ subscriberRaw }) => subscriberRaw(...args)),
|
results.forEach(({ subscriberRaw }) =>
|
||||||
|
subscriberRaw(...args)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,4 +450,8 @@ export class Updux<
|
|||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
effectToMiddleware(effect) {
|
||||||
|
return effectToMiddleware(effect, this.actions, this.selectors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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' });
|
|
||||||
});
|
|
34
src/actions.test.ts
Normal file
34
src/actions.test.ts
Normal file
@ -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');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -37,7 +37,7 @@ export function action<
|
|||||||
payloadFunction?: TPayload,
|
payloadFunction?: TPayload,
|
||||||
transformer?: Function
|
transformer?: Function
|
||||||
): ActionGenerator<TType, TPayload> {
|
): ActionGenerator<TType, TPayload> {
|
||||||
const generator = function (...payloadArg) {
|
let generator : any= function (...payloadArg) {
|
||||||
const result: Action = { type };
|
const result: Action = { type };
|
||||||
|
|
||||||
if (payloadFunction) {
|
if (payloadFunction) {
|
||||||
@ -49,11 +49,12 @@ export function action<
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(transformer) {
|
||||||
|
const orig = generator;
|
||||||
|
generator = (...args: any) => transformer(orig(...args), args);
|
||||||
|
}
|
||||||
|
|
||||||
generator.type = type;
|
generator.type = type;
|
||||||
|
|
||||||
return (
|
return generator;
|
||||||
transformer
|
|
||||||
? (...args: any) => transformer(generator(...args), args)
|
|
||||||
: generator
|
|
||||||
) as any;
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { isPlainObject, mapValues } from 'lodash';
|
import { mapValues } from 'lodash-es';
|
||||||
|
import isPlainObject from 'lodash/isPlainObject.js';
|
||||||
import u from 'updeep';
|
import u from 'updeep';
|
||||||
|
|
||||||
export function buildInitial(initial, subduxes = {}) {
|
export function buildInitial(initial, subduxes = {}) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { buildInitial } from '.';
|
import { buildInitial } from './index.js';
|
||||||
|
|
||||||
test('basic', () => {
|
test('basic', () => {
|
||||||
expect(buildInitial({ a: 1 }, { b: { initial: { c: 2 } } })).toMatchObject({
|
expect(buildInitial({ a: 1 }, { b: { initial: { c: 2 } } })).toMatchObject({
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import u from 'updeep';
|
import u from 'updeep';
|
||||||
import { mapValues, map, get } from 'lodash';
|
import { mapValues, map, get } from 'lodash-es';
|
||||||
import { Updux } from '../Updux.js';
|
|
||||||
|
|
||||||
const middlewareFor = (type, middleware) => (api) => (next) => (action) => {
|
const middlewareFor = (type, middleware) => (api) => (next) => (action) => {
|
||||||
if (type !== '*' && action.type !== type) return next(action);
|
if (type !== '*' && action.type !== type) return next(action);
|
||||||
@ -66,7 +65,9 @@ export function buildMiddleware(
|
|||||||
effects = [],
|
effects = [],
|
||||||
actions = {},
|
actions = {},
|
||||||
selectors = {},
|
selectors = {},
|
||||||
sub = {}
|
sub = {},
|
||||||
|
wrapper = undefined,
|
||||||
|
dux = undefined,
|
||||||
) {
|
) {
|
||||||
let inner = map(sub, ({ middleware }, slice) =>
|
let inner = map(sub, ({ middleware }, slice) =>
|
||||||
slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined
|
slice !== '*' && middleware ? sliceMw(slice, middleware) : undefined
|
||||||
@ -76,7 +77,9 @@ export function buildMiddleware(
|
|||||||
effectToMiddleware(effect, actions, selectors)
|
effectToMiddleware(effect, actions, selectors)
|
||||||
);
|
);
|
||||||
|
|
||||||
const mws = [...local, ...inner];
|
let mws = [...local, ...inner];
|
||||||
|
|
||||||
|
if( wrapper ) mws = wrapper(mws,dux);
|
||||||
|
|
||||||
return composeMw(mws);
|
return composeMw(mws);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { map, mapValues, merge } from 'lodash';
|
import { map, mapValues, merge } from 'lodash-es';
|
||||||
|
|
||||||
export function buildSelectors(
|
export function buildSelectors(
|
||||||
localSelectors,
|
localSelectors,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import u from 'updeep';
|
import u from 'updeep';
|
||||||
import { mapValues } from 'lodash';
|
import { mapValues } from 'lodash-es';
|
||||||
|
|
||||||
export function buildUpreducer(
|
export function buildUpreducer(
|
||||||
initial,
|
initial,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import u from 'updeep';
|
import u from 'updeep';
|
||||||
import add from 'lodash/fp/add.js';
|
import add from 'lodash/fp/add.js';
|
||||||
|
|
||||||
import { Updux } from '.';
|
import { Updux } from './index.js';
|
||||||
|
|
||||||
test('README.md', () => {
|
test('README.md', () => {
|
||||||
const otherDux = new Updux({});
|
const otherDux = new Updux({});
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
export { Updux } from './Updux';
|
|
||||||
export { action } from './actions';
|
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { Updux, UpduxConfig } from './Updux.js';
|
||||||
|
export { action } from './actions.js';
|
||||||
|
export { AggregateDuxActions, AggregateDuxState } from './types.js';
|
@ -1,4 +1,4 @@
|
|||||||
import { Updux } from './Updux';
|
import { Updux } from './Updux.js';
|
||||||
|
|
||||||
test('initial', () => {
|
test('initial', () => {
|
||||||
const foo = new Updux({
|
const foo = new Updux({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { difference, omit } from 'lodash';
|
import { difference, omit } from 'lodash-es';
|
||||||
|
|
||||||
import { Updux } from './Updux';
|
import { Updux } from './Updux';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Updux } from './Updux';
|
import { Updux } from './Updux.js';
|
||||||
import { Action, action, ActionGenerator } from './actions';
|
import { Action, action, ActionGenerator } from './actions.js';
|
||||||
import { expectAssignable, expectType, expectNotType } from 'tsd';
|
import { expectAssignable, expectType, expectNotType } from 'tsd';
|
||||||
|
|
||||||
type SimplePayload = (x: number) => number;
|
type SimplePayload = (x: number) => number;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Action, ActionGenerator } from './actions';
|
import { Action, ActionGenerator } from './actions.js';
|
||||||
|
|
||||||
export type Dict<T> = Record<string, T>;
|
export type Dict<T> = Record<string, T>;
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"target": "es2020",
|
"target": "es2015",
|
||||||
"lib": ["es2020"],
|
"lib": ["es2020"],
|
||||||
"module": "ES2020",
|
"module": "esnext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
Loading…
Reference in New Issue
Block a user