Merge branch 'subdux-selectors'
This commit is contained in:
commit
7280381ca8
111
package.json
111
package.json
@ -1,58 +1,59 @@
|
||||
{
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@yanick/updeep-remeda": "^2.2.0",
|
||||
"ajv": "^8.12.0",
|
||||
"expect-type": "^0.16.0",
|
||||
"immer": "^9.0.15",
|
||||
"json-schema-shorthand": "^2.0.0",
|
||||
"json-schema-to-ts": "^2.9.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"moize": "^6.1.6",
|
||||
"redux": "^4.2.0",
|
||||
"remeda": "^1.0.1",
|
||||
"updeep": "^1.2.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"module": "dist/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"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"
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@yanick/updeep-remeda": "^2.2.0",
|
||||
"ajv": "^8.12.0",
|
||||
"expect-type": "^0.16.0",
|
||||
"immer": "^9.0.15",
|
||||
"json-schema-shorthand": "^2.0.0",
|
||||
"json-schema-to-ts": "^2.9.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"moize": "^6.1.6",
|
||||
"redux": "^4.2.0",
|
||||
"remeda": "^1.0.1",
|
||||
"updeep": "^1.2.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"module": "dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
26
src/Updux.ts
26
src/Updux.ts
@ -122,17 +122,17 @@ export default class Updux<D extends DuxConfig> {
|
||||
matcher: A,
|
||||
mutation: Mutation<DuxActions<D>[A] extends (...args: any) => infer P ? P : never, DuxState<D>>,
|
||||
terminal?: boolean,
|
||||
);
|
||||
): Updux<D>;
|
||||
addMutation<A extends Action<any>>(
|
||||
matcher: (action: A) => boolean,
|
||||
mutation: Mutation<A, DuxState<D>>,
|
||||
terminal?: boolean,
|
||||
);
|
||||
): Updux<D>;
|
||||
addMutation<A extends ActionCreator<any>>(
|
||||
actionCreator: A,
|
||||
mutation: Mutation<ReturnType<A>, DuxState<D>>,
|
||||
terminal?: boolean,
|
||||
);
|
||||
): Updux<D>;
|
||||
addMutation(matcher, mutation, terminal = false) {
|
||||
|
||||
if (typeof matcher === 'string') {
|
||||
@ -157,6 +157,8 @@ export default class Updux<D extends DuxConfig> {
|
||||
mutation,
|
||||
},
|
||||
];
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
#defaultMutation;
|
||||
@ -189,6 +191,7 @@ export default class Updux<D extends DuxConfig> {
|
||||
options: Partial<{
|
||||
preloadedState: DuxState<D>;
|
||||
validate: boolean;
|
||||
buildMiddleware: (middleware: any[]) => any
|
||||
}> = {},
|
||||
): EnhancedStore<DuxState<D>> & AugmentedMiddlewareAPI<D> {
|
||||
const preloadedState = options.preloadedState;
|
||||
@ -199,7 +202,7 @@ export default class Updux<D extends DuxConfig> {
|
||||
this.selectors,
|
||||
);
|
||||
|
||||
const middleware = [effects];
|
||||
let middleware = [effects];
|
||||
|
||||
if (options.validate) {
|
||||
middleware.unshift(
|
||||
@ -207,6 +210,9 @@ export default class Updux<D extends DuxConfig> {
|
||||
)
|
||||
}
|
||||
|
||||
if (options.buildMiddleware)
|
||||
middleware = options.buildMiddleware(middleware);
|
||||
|
||||
const store = configureStore({
|
||||
reducer: this.reducer,
|
||||
preloadedState,
|
||||
@ -253,16 +259,16 @@ export default class Updux<D extends DuxConfig> {
|
||||
addEffect(
|
||||
actionType: keyof DuxActions<D>,
|
||||
effect: EffectMiddleware<D>,
|
||||
): EffectMiddleware<D>;
|
||||
): Updux<D>;
|
||||
addEffect(
|
||||
actionCreator: { match: (action: any) => boolean },
|
||||
effect: EffectMiddleware<D>,
|
||||
): EffectMiddleware<D>;
|
||||
): Updux<D>;
|
||||
addEffect(
|
||||
guardFunc: (action: AnyAction) => boolean,
|
||||
effect: EffectMiddleware<D>,
|
||||
): EffectMiddleware<D>;
|
||||
addEffect(effect: EffectMiddleware<D>): EffectMiddleware<D>;
|
||||
): Updux<D>;
|
||||
addEffect(effect: EffectMiddleware<D>): Updux<D>;
|
||||
addEffect(...args) {
|
||||
let effect;
|
||||
if (args.length === 1) {
|
||||
@ -294,10 +300,10 @@ export default class Updux<D extends DuxConfig> {
|
||||
|
||||
this.#effects = [...this.#effects, effect];
|
||||
|
||||
return effect;
|
||||
return this;
|
||||
}
|
||||
|
||||
get effects() {
|
||||
get effects(): any {
|
||||
return this.memoBuildEffects(this.#effects, this.duxConfig.subduxes);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { createAction } from './index.js';
|
||||
test('addEffect', () => {
|
||||
const dux = new Updux({});
|
||||
|
||||
dux.addEffect((api) => (next) => (action) => { });
|
||||
dux.addEffect((api) => (next) => (action) => {});
|
||||
});
|
||||
|
||||
test('buildEffectsMiddleware', () => {
|
||||
@ -48,7 +48,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);
|
||||
@ -126,10 +126,10 @@ test('addEffect with actionCreator', () => {
|
||||
const next = vi.fn();
|
||||
const spy = vi.fn();
|
||||
|
||||
const mw = dux.addEffect(
|
||||
const [mw] = dux.addEffect(
|
||||
dux.actions.foo,
|
||||
(api) => (next) => (action) => next(spy(action)),
|
||||
);
|
||||
).effects;
|
||||
|
||||
mw({} as any)(next)(dux.actions.bar());
|
||||
expect(next).toHaveBeenCalled();
|
||||
@ -144,18 +144,18 @@ test('addEffect with actionCreator', () => {
|
||||
test('addEffect with function', () => {
|
||||
const dux = new Updux({
|
||||
actions: {
|
||||
foo: () => { },
|
||||
bar: () => { },
|
||||
foo: () => {},
|
||||
bar: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const next = vi.fn();
|
||||
const spy = vi.fn();
|
||||
|
||||
const mw = dux.addEffect(
|
||||
const [mw] = dux.addEffect(
|
||||
(action) => action.type[0] === 'f',
|
||||
(api) => (next) => (action) => next(spy(action)),
|
||||
);
|
||||
).effects;
|
||||
|
||||
mw({} as any)(next)(dux.actions.bar());
|
||||
expect(next).toHaveBeenCalled();
|
||||
@ -170,13 +170,13 @@ test('addEffect with function', () => {
|
||||
test('catchall addEffect', () => {
|
||||
const dux = new Updux({
|
||||
initialState: {
|
||||
a: 1
|
||||
}
|
||||
a: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const spy = vi.fn();
|
||||
|
||||
dux.addEffect((api) => next => action => {
|
||||
dux.addEffect((api) => (next) => (action) => {
|
||||
expectTypeOf(api.getState()).toMatchTypeOf<{
|
||||
a: number;
|
||||
}>();
|
||||
@ -191,7 +191,6 @@ test('catchall addEffect', () => {
|
||||
store.dispatch({ type: 'noop' });
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
// TODO subdux effects
|
||||
|
@ -5,7 +5,7 @@ import Updux, { createAction } from './index.js';
|
||||
describe('basic selectors', () => {
|
||||
type State = { x: number };
|
||||
|
||||
const foo = new Updux({
|
||||
const config = {
|
||||
initialState: {
|
||||
x: 1,
|
||||
},
|
||||
@ -19,12 +19,20 @@ describe('basic selectors', () => {
|
||||
getY: ({ y }: { y: number }) => y,
|
||||
getYPlus:
|
||||
({ y }) =>
|
||||
(incr: number) =>
|
||||
(y + incr) as number,
|
||||
(incr: number) =>
|
||||
(y + incr) as number,
|
||||
},
|
||||
}),
|
||||
// cause the type to fail
|
||||
baz: new Updux({
|
||||
selectors: {
|
||||
getFromBaz: () => 'potato',
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const foo = new Updux(config);
|
||||
|
||||
const sample = {
|
||||
x: 4,
|
||||
|
@ -3,23 +3,25 @@ import { DuxState, UnionToIntersection } from './types.js';
|
||||
|
||||
type RebaseSelectors<SLICE, DUX> = DUX extends { selectors: infer S }
|
||||
? {
|
||||
[key in keyof S]: RebaseSelector<SLICE, S[key]>;
|
||||
}
|
||||
[key in keyof S]: RebaseSelector<SLICE, S[key]>;
|
||||
}
|
||||
: never;
|
||||
|
||||
type RebaseSelector<SLICE, S> = SLICE extends string
|
||||
? S extends (state: infer STATE) => infer R
|
||||
? (state: Record<SLICE, STATE>) => R
|
||||
: never
|
||||
? (state: Record<SLICE, STATE>) => R
|
||||
: never
|
||||
: never;
|
||||
|
||||
type Values<X> = X[keyof X];
|
||||
|
||||
export type DuxSelectors<D> = (D extends { selectors: infer S } ? S : {}) &
|
||||
(D extends { subduxes: infer SUB }
|
||||
? Values<{
|
||||
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
||||
}>
|
||||
? UnionToIntersection<
|
||||
Values<{
|
||||
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
||||
}>
|
||||
>
|
||||
: {});
|
||||
|
||||
export function buildSelectors(localSelectors = {}, subduxes = {}) {
|
||||
|
@ -48,7 +48,7 @@
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* 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. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
|
Loading…
Reference in New Issue
Block a user