Merge branch 'subdux-selectors'
This commit is contained in:
commit
7280381ca8
111
package.json
111
package.json
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
26
src/Updux.ts
26
src/Updux.ts
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -5,7 +5,7 @@ import Updux, { createAction } from './index.js';
|
|||||||
describe('basic selectors', () => {
|
describe('basic selectors', () => {
|
||||||
type State = { x: number };
|
type State = { x: number };
|
||||||
|
|
||||||
const foo = new Updux({
|
const config = {
|
||||||
initialState: {
|
initialState: {
|
||||||
x: 1,
|
x: 1,
|
||||||
},
|
},
|
||||||
@ -19,12 +19,20 @@ describe('basic selectors', () => {
|
|||||||
getY: ({ y }: { y: number }) => y,
|
getY: ({ y }: { y: number }) => y,
|
||||||
getYPlus:
|
getYPlus:
|
||||||
({ y }) =>
|
({ y }) =>
|
||||||
(incr: number) =>
|
(incr: number) =>
|
||||||
(y + incr) as number,
|
(y + incr) as number,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// cause the type to fail
|
||||||
|
baz: new Updux({
|
||||||
|
selectors: {
|
||||||
|
getFromBaz: () => 'potato',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const foo = new Updux(config);
|
||||||
|
|
||||||
const sample = {
|
const sample = {
|
||||||
x: 4,
|
x: 4,
|
||||||
|
@ -3,23 +3,25 @@ import { DuxState, UnionToIntersection } from './types.js';
|
|||||||
|
|
||||||
type RebaseSelectors<SLICE, DUX> = DUX extends { selectors: infer S }
|
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;
|
: never;
|
||||||
|
|
||||||
type RebaseSelector<SLICE, S> = SLICE extends string
|
type RebaseSelector<SLICE, S> = SLICE extends string
|
||||||
? S extends (state: infer STATE) => infer R
|
? S extends (state: infer STATE) => infer R
|
||||||
? (state: Record<SLICE, STATE>) => R
|
? (state: Record<SLICE, STATE>) => R
|
||||||
: never
|
: never
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
type Values<X> = X[keyof X];
|
type Values<X> = X[keyof X];
|
||||||
|
|
||||||
export type DuxSelectors<D> = (D extends { selectors: infer S } ? S : {}) &
|
export type DuxSelectors<D> = (D extends { selectors: infer S } ? S : {}) &
|
||||||
(D extends { subduxes: infer SUB }
|
(D extends { subduxes: infer SUB }
|
||||||
? Values<{
|
? UnionToIntersection<
|
||||||
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
Values<{
|
||||||
}>
|
[key in keyof SUB]: RebaseSelectors<key, SUB[key]>;
|
||||||
|
}>
|
||||||
|
>
|
||||||
: {});
|
: {});
|
||||||
|
|
||||||
export function buildSelectors(localSelectors = {}, subduxes = {}) {
|
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'. */
|
// "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. */
|
||||||
|
Loading…
Reference in New Issue
Block a user