addMutation returns the updux

This commit is contained in:
Yanick Champoux 2023-09-20 10:42:50 -04:00
parent e4083645bc
commit a22a121018
4 changed files with 84 additions and 78 deletions

View File

@ -1,58 +1,59 @@
{ {
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@yanick/updeep-remeda": "^2.2.0", "@yanick/updeep-remeda": "^2.2.0",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"expect-type": "^0.16.0", "expect-type": "^0.16.0",
"immer": "^9.0.15", "immer": "^9.0.15",
"json-schema-shorthand": "^2.0.0", "json-schema-shorthand": "^2.0.0",
"json-schema-to-ts": "^2.9.2", "json-schema-to-ts": "^2.9.2",
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"moize": "^6.1.6", "moize": "^6.1.6",
"redux": "^4.2.0", "redux": "^4.2.0",
"remeda": "^1.0.1", "remeda": "^1.0.1",
"updeep": "^1.2.1" "updeep": "^1.2.1"
}, },
"license": "MIT", "license": "MIT",
"module": "dist/index.js", "module": "dist/index.js",
"exports": { "types": "./dist/index.d.ts",
".": { "exports": {
"import": "./dist/index.js" ".": {
} "import": "./dist/index.js"
},
"name": "updux",
"description": "Updeep-friendly Redux helper framework",
"scripts": {
"docsify:serve": "docsify serve docs"
},
"version": "5.1.0",
"repository": {
"type": "git",
"url": "git+https://github.com/yanick/updux.git"
},
"keywords": [
"redux",
"updeep"
],
"author": "Yanick Champoux <yanick@babyl.ca> (http://techblog.babyl.ca)",
"bugs": {
"url": "https://github.com/yanick/updux/issues"
},
"homepage": "https://github.com/yanick/updux#readme",
"devDependencies": {
"@reduxjs/toolkit": "==2.0.0-alpha.5 ",
"@vitest/browser": "^0.23.1",
"@vitest/ui": "^0.23.1",
"docsify": "^4.13.1",
"eslint": "^8.22.0",
"eslint-plugin-no-only-tests": "^3.0.0",
"eslint-plugin-todo-plz": "^1.2.1",
"jsdoc-to-markdown": "^7.1.1",
"prettier": "^2.7.1",
"redux-toolkit": "^1.1.2",
"tsdoc-markdown": "^0.0.4",
"typescript": "^4.9.5",
"vite": "^4.2.1",
"vitest": "0.23.1"
} }
},
"name": "updux",
"description": "Updeep-friendly Redux helper framework",
"scripts": {
"docsify:serve": "docsify serve docs"
},
"version": "5.1.0",
"repository": {
"type": "git",
"url": "git+https://github.com/yanick/updux.git"
},
"keywords": [
"redux",
"updeep"
],
"author": "Yanick Champoux <yanick@babyl.ca> (http://techblog.babyl.ca)",
"bugs": {
"url": "https://github.com/yanick/updux/issues"
},
"homepage": "https://github.com/yanick/updux#readme",
"devDependencies": {
"@reduxjs/toolkit": "==2.0.0-alpha.5 ",
"@vitest/browser": "^0.23.1",
"@vitest/ui": "^0.23.1",
"docsify": "^4.13.1",
"eslint": "^8.22.0",
"eslint-plugin-no-only-tests": "^3.0.0",
"eslint-plugin-todo-plz": "^1.2.1",
"jsdoc-to-markdown": "^7.1.1",
"prettier": "^2.7.1",
"redux-toolkit": "^1.1.2",
"tsdoc-markdown": "^0.0.4",
"typescript": "^4.9.5",
"vite": "^4.2.1",
"vitest": "0.23.1"
}
} }

View File

@ -122,17 +122,17 @@ export default class Updux<D extends DuxConfig> {
matcher: A, matcher: A,
mutation: Mutation<DuxActions<D>[A] extends (...args: any) => infer P ? P : never, DuxState<D>>, mutation: Mutation<DuxActions<D>[A] extends (...args: any) => infer P ? P : never, DuxState<D>>,
terminal?: boolean, terminal?: boolean,
); ): Updux<D>;
addMutation<A extends Action<any>>( addMutation<A extends Action<any>>(
matcher: (action: A) => boolean, matcher: (action: A) => boolean,
mutation: Mutation<A, DuxState<D>>, mutation: Mutation<A, DuxState<D>>,
terminal?: boolean, terminal?: boolean,
); ): Updux<D>;
addMutation<A extends ActionCreator<any>>( addMutation<A extends ActionCreator<any>>(
actionCreator: A, actionCreator: A,
mutation: Mutation<ReturnType<A>, DuxState<D>>, mutation: Mutation<ReturnType<A>, DuxState<D>>,
terminal?: boolean, terminal?: boolean,
); ): Updux<D>;
addMutation(matcher, mutation, terminal = false) { addMutation(matcher, mutation, terminal = false) {
if (typeof matcher === 'string') { if (typeof matcher === 'string') {
@ -157,6 +157,8 @@ export default class Updux<D extends DuxConfig> {
mutation, mutation,
}, },
]; ];
return this;
} }
#defaultMutation; #defaultMutation;
@ -189,6 +191,7 @@ export default class Updux<D extends DuxConfig> {
options: Partial<{ options: Partial<{
preloadedState: DuxState<D>; preloadedState: DuxState<D>;
validate: boolean; validate: boolean;
buildMiddleware: (middleware: any[]) => any
}> = {}, }> = {},
): EnhancedStore<DuxState<D>> & AugmentedMiddlewareAPI<D> { ): EnhancedStore<DuxState<D>> & AugmentedMiddlewareAPI<D> {
const preloadedState = options.preloadedState; const preloadedState = options.preloadedState;
@ -199,7 +202,7 @@ export default class Updux<D extends DuxConfig> {
this.selectors, this.selectors,
); );
const middleware = [effects]; let middleware = [effects];
if (options.validate) { if (options.validate) {
middleware.unshift( middleware.unshift(
@ -207,6 +210,9 @@ export default class Updux<D extends DuxConfig> {
) )
} }
if (options.buildMiddleware)
middleware = options.buildMiddleware(middleware);
const store = configureStore({ const store = configureStore({
reducer: this.reducer, reducer: this.reducer,
preloadedState, preloadedState,
@ -253,16 +259,16 @@ export default class Updux<D extends DuxConfig> {
addEffect( addEffect(
actionType: keyof DuxActions<D>, actionType: keyof DuxActions<D>,
effect: EffectMiddleware<D>, effect: EffectMiddleware<D>,
): EffectMiddleware<D>; ): Updux<D>;
addEffect( addEffect(
actionCreator: { match: (action: any) => boolean }, actionCreator: { match: (action: any) => boolean },
effect: EffectMiddleware<D>, effect: EffectMiddleware<D>,
): EffectMiddleware<D>; ): Updux<D>;
addEffect( addEffect(
guardFunc: (action: AnyAction) => boolean, guardFunc: (action: AnyAction) => boolean,
effect: EffectMiddleware<D>, effect: EffectMiddleware<D>,
): EffectMiddleware<D>; ): Updux<D>;
addEffect(effect: EffectMiddleware<D>): EffectMiddleware<D>; addEffect(effect: EffectMiddleware<D>): Updux<D>;
addEffect(...args) { addEffect(...args) {
let effect; let effect;
if (args.length === 1) { if (args.length === 1) {
@ -294,10 +300,10 @@ export default class Updux<D extends DuxConfig> {
this.#effects = [...this.#effects, effect]; this.#effects = [...this.#effects, effect];
return effect; return this;
} }
get effects() { get effects(): any {
return this.memoBuildEffects(this.#effects, this.duxConfig.subduxes); return this.memoBuildEffects(this.#effects, this.duxConfig.subduxes);
} }

View File

@ -7,7 +7,7 @@ import { createAction } from './index.js';
test('addEffect', () => { test('addEffect', () => {
const dux = new Updux({}); const dux = new Updux({});
dux.addEffect((api) => (next) => (action) => { }); dux.addEffect((api) => (next) => (action) => {});
}); });
test('buildEffectsMiddleware', () => { test('buildEffectsMiddleware', () => {
@ -48,7 +48,7 @@ test('buildEffectsMiddleware', () => {
expect(seen).toEqual(0); expect(seen).toEqual(0);
const dispatch = vi.fn(); const dispatch = vi.fn();
mw({ getState: () => 'the state', dispatch })(() => { })({ mw({ getState: () => 'the state', dispatch })(() => {})({
type: 'noop', type: 'noop',
}); });
expect(seen).toEqual(1); expect(seen).toEqual(1);
@ -126,10 +126,10 @@ test('addEffect with actionCreator', () => {
const next = vi.fn(); const next = vi.fn();
const spy = vi.fn(); const spy = vi.fn();
const mw = dux.addEffect( const [mw] = dux.addEffect(
dux.actions.foo, dux.actions.foo,
(api) => (next) => (action) => next(spy(action)), (api) => (next) => (action) => next(spy(action)),
); ).effects;
mw({} as any)(next)(dux.actions.bar()); mw({} as any)(next)(dux.actions.bar());
expect(next).toHaveBeenCalled(); expect(next).toHaveBeenCalled();
@ -144,18 +144,18 @@ test('addEffect with actionCreator', () => {
test('addEffect with function', () => { test('addEffect with function', () => {
const dux = new Updux({ const dux = new Updux({
actions: { actions: {
foo: () => { }, foo: () => {},
bar: () => { }, bar: () => {},
}, },
}); });
const next = vi.fn(); const next = vi.fn();
const spy = vi.fn(); const spy = vi.fn();
const mw = dux.addEffect( const [mw] = dux.addEffect(
(action) => action.type[0] === 'f', (action) => action.type[0] === 'f',
(api) => (next) => (action) => next(spy(action)), (api) => (next) => (action) => next(spy(action)),
); ).effects;
mw({} as any)(next)(dux.actions.bar()); mw({} as any)(next)(dux.actions.bar());
expect(next).toHaveBeenCalled(); expect(next).toHaveBeenCalled();
@ -170,13 +170,13 @@ test('addEffect with function', () => {
test('catchall addEffect', () => { test('catchall addEffect', () => {
const dux = new Updux({ const dux = new Updux({
initialState: { initialState: {
a: 1 a: 1,
} },
}); });
const spy = vi.fn(); const spy = vi.fn();
dux.addEffect((api) => next => action => { dux.addEffect((api) => (next) => (action) => {
expectTypeOf(api.getState()).toMatchTypeOf<{ expectTypeOf(api.getState()).toMatchTypeOf<{
a: number; a: number;
}>(); }>();
@ -191,7 +191,6 @@ test('catchall addEffect', () => {
store.dispatch({ type: 'noop' }); store.dispatch({ type: 'noop' });
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
// TODO subdux effects // TODO subdux effects

View File

@ -48,7 +48,7 @@
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */ /* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */