redux-sacala 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -0
- package/build/redux-sacala.cjs +2 -0
- package/build/redux-sacala.cjs.map +1 -0
- package/build/redux-sacala.d.ts +49 -34
- package/build/redux-sacala.js +46 -74
- package/build/redux-sacala.js.map +1 -1
- package/package.json +28 -9
- package/.idea/modules.xml +0 -8
- package/.idea/redux-sacala.iml +0 -13
- package/.idea/vcs.xml +0 -6
- package/.idea/webResources.xml +0 -14
- package/build/redux-sacala.spec.d.ts +0 -1
- package/build/redux-sacala.spec.js +0 -42
- package/build/redux-sacala.spec.js.map +0 -1
- package/jest.config.js +0 -7
- package/src/redux-sacala.spec.ts +0 -63
- package/src/redux-sacala.ts +0 -124
- package/tsconfig.json +0 -15
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# redux-sacala
|
|
2
|
+
|
|
3
|
+
A lightweight utility to create Redux blocks with minimal boilerplate, featuring type-safe actions, reducers, and side effects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Minimal Boilerplate**: Define actions and reducers in one place.
|
|
8
|
+
- **Type Safety**: Full TypeScript support for state and action payloads.
|
|
9
|
+
- **Integrated Side Effects**: Middleware-based effects that have access to `dispatch` and `getState`.
|
|
10
|
+
- **Ducks Pattern**: Encourages grouping related logic together.
|
|
11
|
+
- **Extra Arguments**: Inject dependencies into your effects via middleware.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install redux-sacala
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
*Note: `redux` is a peer dependency.*
|
|
20
|
+
|
|
21
|
+
## Usage Example
|
|
22
|
+
|
|
23
|
+
### 1. Create a Redux Block
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createReduxBlock } from 'redux-sacala';
|
|
27
|
+
|
|
28
|
+
interface CounterState {
|
|
29
|
+
count: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface RootState {
|
|
33
|
+
counter: CounterState;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Define the block
|
|
37
|
+
const {
|
|
38
|
+
actions,
|
|
39
|
+
reducer,
|
|
40
|
+
createMiddleware
|
|
41
|
+
} = createReduxBlock<RootState, { logger: (msg: string) => void }>()({
|
|
42
|
+
name: 'counter',
|
|
43
|
+
initial: { count: 0 },
|
|
44
|
+
actions: {
|
|
45
|
+
increment(state) {
|
|
46
|
+
return { count: state.count + 1 };
|
|
47
|
+
},
|
|
48
|
+
setCount(state, payload: number) {
|
|
49
|
+
return { count: payload };
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
effects: (dispatch, getState) => ({
|
|
53
|
+
asyncIncrement(payload: number, { logger }) {
|
|
54
|
+
logger('Starting async increment');
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
dispatch(actions.increment());
|
|
57
|
+
logger('Incremented');
|
|
58
|
+
}, payload);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export { actions, reducer, createMiddleware };
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Configure the Store
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { createStore, combineReducers, applyMiddleware } from 'redux';
|
|
70
|
+
import { reducer, createMiddleware } from './counterBlock';
|
|
71
|
+
|
|
72
|
+
const rootReducer = combineReducers({
|
|
73
|
+
counter: reducer
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Create middleware with extra argument
|
|
77
|
+
const counterMiddleware = createMiddleware({
|
|
78
|
+
logger: (msg) => console.log(msg)
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const store = createStore(
|
|
82
|
+
rootReducer,
|
|
83
|
+
applyMiddleware(counterMiddleware)
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Dispatch Actions
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// These are type-safe
|
|
91
|
+
store.dispatch(actions.increment());
|
|
92
|
+
store.dispatch(actions.setCount(10));
|
|
93
|
+
store.dispatch(actions.asyncIncrement(1000)); // Delay of 1000ms
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API
|
|
97
|
+
|
|
98
|
+
### `createReduxBlock<GlobalState, ExtraArgument = undefined>()(options)`
|
|
99
|
+
|
|
100
|
+
Creates a Redux block. The double-call pattern is used for better TypeScript type inference.
|
|
101
|
+
|
|
102
|
+
#### Options
|
|
103
|
+
- `name`: `string`. The key under which the state is stored in the global state. Must match a key in `GlobalState`.
|
|
104
|
+
- `initial`: `GlobalState[name]`. The initial state of the block.
|
|
105
|
+
- `actions`: An object where each key is an action name and each value is a state handler: `(state: BlockState, payload: any) => BlockState`.
|
|
106
|
+
- `effects`: (Optional) A function `(dispatch, getState) => effectsMap`.
|
|
107
|
+
- `effectsMap` keys are action types.
|
|
108
|
+
- `effectsMap` values are effect handlers: `(payload: any, extraArgument: ExtraArgument) => void`.
|
|
109
|
+
|
|
110
|
+
#### Returns
|
|
111
|
+
- `name`: The name of the block.
|
|
112
|
+
- `reducer`: The Redux reducer for this block.
|
|
113
|
+
- `actions`: An object containing action creators for both `actions` and `effects`.
|
|
114
|
+
- `createMiddleware`: A function `(extraArgument: ExtraArgument) => Middleware` to create the Redux middleware for handling the effects.
|
|
115
|
+
|
|
116
|
+
## Development
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Install dependencies
|
|
120
|
+
npm install
|
|
121
|
+
|
|
122
|
+
# Run tests
|
|
123
|
+
npm test
|
|
124
|
+
|
|
125
|
+
# Build the project
|
|
126
|
+
npm run build
|
|
127
|
+
|
|
128
|
+
# Lint the code
|
|
129
|
+
npm run lint
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
Author: Andrey Monkin (monkin.andrey@gmail.com)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=e=>{const r={};for(const c in e)r[c]=e[c].bind(e);return r},a=(e,r)=>{const c={};for(const n in r)c[e+n]=r[n];return c},l=e=>r=>r===void 0?{type:e}:{type:e,payload:r},p=e=>r=>({type:e,payload:r}),f=(e,r,c)=>{const n=a(e+"/",u(c));return(o=r,s)=>{if(s&&s.type){const t=n[s.type];return t?t(o,s.payload):o}else return o}},y=(e,r)=>(c=>n=>{const o=a(e+"/",u(r(n.dispatch,n.getState)));return s=>t=>{t&&Object.prototype.hasOwnProperty.call(o,t.type)?o[t.type](t.payload,c):s(t)}}),i=()=>{throw new Error("Can't have access to 'dispatch' and 'getState' during initialization")},b=()=>({name:e,initial:r,actions:c,effects:n})=>{const o=Object.keys(c).reduce((t,d)=>(t[d]=l(`${e}/${d}`),t),{}),s=Object.keys(n?n(i,i):{}).reduce((t,d)=>(t[d]=p(`${e}/${d}`),t),{});return{name:e,reducer:f(e,r,c),createMiddleware:n?y(e,n):(()=>t=>d=>d),actions:{...o,...s}}};exports.createReduxBlock=b;
|
|
2
|
+
//# sourceMappingURL=redux-sacala.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redux-sacala.cjs","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Dispatch, Action, Reducer, Middleware, AnyAction, MiddlewareAPI } from \"redux\";\n\ntype UnknownToUndefined<T> = unknown extends T ? undefined : T;\n\ntype FirstArgument<F> = F extends (arg1: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;\ntype SecondArgument<F> = F extends (arg1: any, arg2: infer U, ...args: any[]) => any\n ? UnknownToUndefined<U>\n : undefined;\n\nconst bindAll = <T extends { [key: string]: Function }>(map: T): T => {\n const result = {} as any as T;\n for (const i in map) {\n result[i] = map[i].bind(map);\n }\n return result;\n};\n\nconst appendPrefix = <T>(prefix: string, map: { [key: string]: T }) => {\n const r: { [key: string]: any } = {};\n for (const i in map) {\n r[prefix + i] = map[i];\n }\n return r;\n};\n\n// Input\ntype ActionHandler<BlockState, Payload> = (state: BlockState, payload: Payload) => BlockState;\ntype ActionMap<BlockState> = { [action: string]: ActionHandler<BlockState, any> };\ntype EffectsMap<GlobalState, ExtraArgument> = (\n dispatch: Dispatch,\n getState: () => GlobalState,\n) => { [effect: string]: (payload: any, extraArgument: ExtraArgument) => any };\n\n// Output\ntype ActionCreator<Handler extends ActionHandler<any, any>> =\n undefined extends SecondArgument<Handler>\n ? () => Action<string>\n : (payload: SecondArgument<Handler>) => Action<string> & { payload: SecondArgument<Handler> };\n\ntype ActionCreatorMap<Actions extends ActionMap<any>> = {\n [name in keyof Actions]: ActionCreator<Actions[name]>;\n};\n\ntype EffectsCreatorMap<GlobalState, ExtraArgument, Map extends EffectsMap<GlobalState, ExtraArgument>> = {\n [key in keyof ReturnType<Map>]: undefined extends FirstArgument<ReturnType<Map>[key]>\n ? () => Action\n : (payload: FirstArgument<ReturnType<Map>[key]>) => Action;\n};\n\n// Transformation\nconst createActionCreator = (type: string) => (payload?: any) => (payload === undefined ? { type } : { type, payload });\nconst createEffectCreator = (type: string) => (payload: any) => ({ type, payload });\n\nconst createReducer = <BlockState>(prefix: string, initial: BlockState, actionMap: ActionMap<BlockState>): Reducer => {\n const actions: ActionMap<BlockState> = appendPrefix(prefix + \"/\", bindAll(actionMap));\n return (state: BlockState = initial, action?: AnyAction) => {\n if (action && action.type) {\n const handler: (state: BlockState, payload?: any) => BlockState = actions[action.type];\n if (handler) {\n return handler(state, action.payload);\n } else {\n return state;\n }\n } else {\n return state;\n }\n };\n};\n\ntype MiddlewareCreator<T> = T extends undefined ? () => Middleware : (argument: T) => Middleware;\n\nconst createMiddlewareCreator = <GlobalState, ExtraArgument>(\n prefix: string,\n effectsMap: EffectsMap<GlobalState, ExtraArgument>,\n): MiddlewareCreator<ExtraArgument> =>\n ((argument: ExtraArgument) => (store: MiddlewareAPI) => {\n const effects = appendPrefix(prefix + \"/\", bindAll(effectsMap(store.dispatch, store.getState)));\n return (next: Dispatch) => (action: Action<string> & { payload: any[] }) => {\n if (action && Object.prototype.hasOwnProperty.call(effects, action.type)) {\n effects[action.type](action.payload, argument);\n } else {\n next(action);\n }\n };\n }) as MiddlewareCreator<ExtraArgument>;\n\nconst fail = (): never => {\n throw new Error(\"Can't have access to 'dispatch' and 'getState' during initialization\");\n};\n\nexport const createReduxBlock =\n <GlobalState, ExtraArgument = undefined>() =>\n <\n Name extends keyof GlobalState & string,\n Actions extends ActionMap<GlobalState[Name]>,\n Effects extends EffectsMap<GlobalState, ExtraArgument>,\n >({\n name,\n initial,\n actions,\n effects,\n }: {\n name: Name;\n initial: GlobalState[Name];\n actions: Actions;\n effects?: Effects;\n }): {\n name: Name;\n reducer: Reducer<GlobalState[Name]>;\n actions: ActionCreatorMap<Actions> & EffectsCreatorMap<GlobalState, ExtraArgument, Effects>;\n } & ({} extends ReturnType<Effects> ? {} : { createMiddleware: MiddlewareCreator<ExtraArgument> }) => {\n const actionCreators = Object.keys(actions).reduce((r, key) => {\n r[key] = createActionCreator(`${name}/${key}`);\n return r;\n }, {} as any);\n const effectCreators = Object.keys(effects ? effects(fail, fail) : {}).reduce((r, key) => {\n r[key] = createEffectCreator(`${name}/${key}`);\n return r;\n }, {} as any);\n\n return {\n name,\n reducer: createReducer(name as string, initial, actions),\n createMiddleware: effects\n ? createMiddlewareCreator<GlobalState, ExtraArgument>(name as string, effects)\n : ((() => (_: MiddlewareAPI) => (next: Dispatch) => next) as MiddlewareCreator<ExtraArgument>),\n actions: {\n ...actionCreators,\n ...effectCreators,\n },\n };\n };\n"],"names":["bindAll","map","result","i","appendPrefix","prefix","r","createActionCreator","type","payload","createEffectCreator","createReducer","initial","actionMap","actions","state","action","handler","createMiddlewareCreator","effectsMap","argument","store","effects","next","fail","createReduxBlock","name","actionCreators","key","effectCreators","_"],"mappings":"gFASA,MAAMA,EAAkDC,GAAc,CAClE,MAAMC,EAAS,CAAA,EACf,UAAWC,KAAKF,EACZC,EAAOC,CAAC,EAAIF,EAAIE,CAAC,EAAE,KAAKF,CAAG,EAE/B,OAAOC,CACX,EAEME,EAAe,CAAIC,EAAgBJ,IAA8B,CACnE,MAAMK,EAA4B,CAAA,EAClC,UAAWH,KAAKF,EACZK,EAAED,EAASF,CAAC,EAAIF,EAAIE,CAAC,EAEzB,OAAOG,CACX,EA2BMC,EAAuBC,GAAkBC,GAAmBA,IAAY,OAAY,CAAE,KAAAD,CAAA,EAAS,CAAE,KAAAA,EAAM,QAAAC,CAAA,EACvGC,EAAuBF,GAAkBC,IAAkB,CAAE,KAAAD,EAAM,QAAAC,IAEnEE,EAAgB,CAAaN,EAAgBO,EAAqBC,IAA8C,CAClH,MAAMC,EAAiCV,EAAaC,EAAS,IAAKL,EAAQa,CAAS,CAAC,EACpF,MAAO,CAACE,EAAoBH,EAASI,IAAuB,CACxD,GAAIA,GAAUA,EAAO,KAAM,CACvB,MAAMC,EAA4DH,EAAQE,EAAO,IAAI,EACrF,OAAIC,EACOA,EAAQF,EAAOC,EAAO,OAAO,EAE7BD,CAEf,KACI,QAAOA,CAEf,CACJ,EAIMG,EAA0B,CAC5Bb,EACAc,KAEEC,GAA6BC,GAAyB,CACpD,MAAMC,EAAUlB,EAAaC,EAAS,IAAKL,EAAQmB,EAAWE,EAAM,SAAUA,EAAM,QAAQ,CAAC,CAAC,EAC9F,OAAQE,GAAoBP,GAAgD,CACpEA,GAAU,OAAO,UAAU,eAAe,KAAKM,EAASN,EAAO,IAAI,EACnEM,EAAQN,EAAO,IAAI,EAAEA,EAAO,QAASI,CAAQ,EAE7CG,EAAKP,CAAM,CAEnB,CACJ,GAEEQ,EAAO,IAAa,CACtB,MAAM,IAAI,MAAM,sEAAsE,CAC1F,EAEaC,EACT,IACA,CAIE,CACE,KAAAC,EACA,QAAAd,EACA,QAAAE,EACA,QAAAQ,CACJ,IASsG,CAClG,MAAMK,EAAiB,OAAO,KAAKb,CAAO,EAAE,OAAO,CAACR,EAAGsB,KACnDtB,EAAEsB,CAAG,EAAIrB,EAAoB,GAAGmB,CAAI,IAAIE,CAAG,EAAE,EACtCtB,GACR,CAAA,CAAS,EACNuB,EAAiB,OAAO,KAAKP,EAAUA,EAAQE,EAAMA,CAAI,EAAI,CAAA,CAAE,EAAE,OAAO,CAAClB,EAAGsB,KAC9EtB,EAAEsB,CAAG,EAAIlB,EAAoB,GAAGgB,CAAI,IAAIE,CAAG,EAAE,EACtCtB,GACR,CAAA,CAAS,EAEZ,MAAO,CACH,KAAAoB,EACA,QAASf,EAAce,EAAgBd,EAASE,CAAO,EACvD,iBAAkBQ,EACZJ,EAAoDQ,EAAgBJ,CAAO,GACzE,IAAOQ,GAAsBP,GAAmBA,GACxD,QAAS,CACL,GAAGI,EACH,GAAGE,CAAA,CACP,CAER"}
|
package/build/redux-sacala.d.ts
CHANGED
|
@@ -1,34 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import { Action } from 'redux';
|
|
2
|
+
import { Dispatch } from 'redux';
|
|
3
|
+
import { Middleware } from 'redux';
|
|
4
|
+
import { Reducer } from 'redux';
|
|
5
|
+
|
|
6
|
+
declare type ActionCreator<Handler extends ActionHandler<any, any>> = undefined extends SecondArgument<Handler> ? () => Action<string> : (payload: SecondArgument<Handler>) => Action<string> & {
|
|
7
|
+
payload: SecondArgument<Handler>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
declare type ActionCreatorMap<Actions extends ActionMap<any>> = {
|
|
11
|
+
[name in keyof Actions]: ActionCreator<Actions[name]>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare type ActionHandler<BlockState, Payload> = (state: BlockState, payload: Payload) => BlockState;
|
|
15
|
+
|
|
16
|
+
declare type ActionMap<BlockState> = {
|
|
17
|
+
[action: string]: ActionHandler<BlockState, any>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export declare const createReduxBlock: <GlobalState, ExtraArgument = undefined>() => <Name extends keyof GlobalState & string, Actions extends ActionMap<GlobalState[Name]>, Effects extends EffectsMap<GlobalState, ExtraArgument>>({ name, initial, actions, effects, }: {
|
|
21
|
+
name: Name;
|
|
22
|
+
initial: GlobalState[Name];
|
|
23
|
+
actions: Actions;
|
|
24
|
+
effects?: Effects;
|
|
25
|
+
}) => {
|
|
26
|
+
name: Name;
|
|
27
|
+
reducer: Reducer<GlobalState[Name]>;
|
|
28
|
+
actions: ActionCreatorMap<Actions> & EffectsCreatorMap<GlobalState, ExtraArgument, Effects>;
|
|
29
|
+
} & ({} extends ReturnType<Effects> ? {} : {
|
|
30
|
+
createMiddleware: MiddlewareCreator<ExtraArgument>;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
declare type EffectsCreatorMap<GlobalState, ExtraArgument, Map extends EffectsMap<GlobalState, ExtraArgument>> = {
|
|
34
|
+
[key in keyof ReturnType<Map>]: undefined extends FirstArgument<ReturnType<Map>[key]> ? () => Action : (payload: FirstArgument<ReturnType<Map>[key]>) => Action;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
declare type EffectsMap<GlobalState, ExtraArgument> = (dispatch: Dispatch, getState: () => GlobalState) => {
|
|
38
|
+
[effect: string]: (payload: any, extraArgument: ExtraArgument) => any;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
declare type FirstArgument<F> = F extends (arg1: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;
|
|
42
|
+
|
|
43
|
+
declare type MiddlewareCreator<T> = T extends undefined ? () => Middleware : (argument: T) => Middleware;
|
|
44
|
+
|
|
45
|
+
declare type SecondArgument<F> = F extends (arg1: any, arg2: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;
|
|
46
|
+
|
|
47
|
+
declare type UnknownToUndefined<T> = unknown extends T ? undefined : T;
|
|
48
|
+
|
|
49
|
+
export { }
|
package/build/redux-sacala.js
CHANGED
|
@@ -1,75 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const u = (e) => {
|
|
2
|
+
const r = {};
|
|
3
|
+
for (const c in e)
|
|
4
|
+
r[c] = e[c].bind(e);
|
|
5
|
+
return r;
|
|
6
|
+
}, a = (e, r) => {
|
|
7
|
+
const c = {};
|
|
8
|
+
for (const n in r)
|
|
9
|
+
c[e + n] = r[n];
|
|
10
|
+
return c;
|
|
11
|
+
}, l = (e) => (r) => r === void 0 ? { type: e } : { type: e, payload: r }, p = (e) => (r) => ({ type: e, payload: r }), f = (e, r, c) => {
|
|
12
|
+
const n = a(e + "/", u(c));
|
|
13
|
+
return (o = r, s) => {
|
|
14
|
+
if (s && s.type) {
|
|
15
|
+
const t = n[s.type];
|
|
16
|
+
return t ? t(o, s.payload) : o;
|
|
17
|
+
} else
|
|
18
|
+
return o;
|
|
19
|
+
};
|
|
20
|
+
}, h = (e, r) => ((c) => (n) => {
|
|
21
|
+
const o = a(e + "/", u(r(n.dispatch, n.getState)));
|
|
22
|
+
return (s) => (t) => {
|
|
23
|
+
t && Object.prototype.hasOwnProperty.call(o, t.type) ? o[t.type](t.payload, c) : s(t);
|
|
24
|
+
};
|
|
25
|
+
}), i = () => {
|
|
26
|
+
throw new Error("Can't have access to 'dispatch' and 'getState' during initialization");
|
|
27
|
+
}, y = () => ({
|
|
28
|
+
name: e,
|
|
29
|
+
initial: r,
|
|
30
|
+
actions: c,
|
|
31
|
+
effects: n
|
|
32
|
+
}) => {
|
|
33
|
+
const o = Object.keys(c).reduce((t, d) => (t[d] = l(`${e}/${d}`), t), {}), s = Object.keys(n ? n(i, i) : {}).reduce((t, d) => (t[d] = p(`${e}/${d}`), t), {});
|
|
34
|
+
return {
|
|
35
|
+
name: e,
|
|
36
|
+
reducer: f(e, r, c),
|
|
37
|
+
createMiddleware: n ? h(e, n) : (() => (t) => (d) => d),
|
|
38
|
+
actions: {
|
|
39
|
+
...o,
|
|
40
|
+
...s
|
|
5
41
|
}
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return r;
|
|
14
|
-
}
|
|
15
|
-
// Transformation
|
|
16
|
-
function createActionCreator(type) {
|
|
17
|
-
return (payload) => payload === undefined ? { type } : { type, payload };
|
|
18
|
-
}
|
|
19
|
-
function createEffectCreator(type) {
|
|
20
|
-
return (payload) => ({ type, payload });
|
|
21
|
-
}
|
|
22
|
-
function createReducer(prefix, initial, actionMap) {
|
|
23
|
-
const actions = appendPrefix(prefix + "/", bindAll(actionMap));
|
|
24
|
-
return (state = initial, action) => {
|
|
25
|
-
if (action && action.type) {
|
|
26
|
-
const handler = actions[action.type];
|
|
27
|
-
if (handler) {
|
|
28
|
-
return handler(state, action.payload);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
return state;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
return state;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function createMiddlewareCreator(prefix, effectsMap) {
|
|
40
|
-
return ((argument) => (store) => {
|
|
41
|
-
const effects = appendPrefix(prefix + "/", bindAll(effectsMap(store.dispatch, store.getState)));
|
|
42
|
-
return (next) => (action) => {
|
|
43
|
-
if (action && effects.hasOwnProperty(action.type)) {
|
|
44
|
-
effects[action.type](action.payload, argument);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
next(action);
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function fail() {
|
|
53
|
-
throw new Error("Can't have access to 'dispatch' and 'getState' during initialization");
|
|
54
|
-
}
|
|
55
|
-
export function createReduxBlock() {
|
|
56
|
-
return function applyConfig({ name, initial, actions, effects }) {
|
|
57
|
-
const actionCreators = Object.keys(actions).reduce((r, key) => {
|
|
58
|
-
r[key] = createActionCreator(`${name}/${key}`);
|
|
59
|
-
return r;
|
|
60
|
-
}, {});
|
|
61
|
-
const effectCreators = Object.keys(effects ? effects(fail, fail) : {}).reduce((r, key) => {
|
|
62
|
-
r[key] = createEffectCreator(`${name}/${key}`);
|
|
63
|
-
return r;
|
|
64
|
-
}, {});
|
|
65
|
-
return {
|
|
66
|
-
name,
|
|
67
|
-
reducer: createReducer(name, initial, actions),
|
|
68
|
-
createMiddleware: effects
|
|
69
|
-
? createMiddlewareCreator(name, effects)
|
|
70
|
-
: (() => ((_) => (next) => next)),
|
|
71
|
-
actions: Object.assign(Object.assign({}, actionCreators), effectCreators)
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=redux-sacala.js.map
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export {
|
|
45
|
+
y as createReduxBlock
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=redux-sacala.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redux-sacala.js","
|
|
1
|
+
{"version":3,"file":"redux-sacala.js","sources":["../src/redux-sacala.ts"],"sourcesContent":["import { Dispatch, Action, Reducer, Middleware, AnyAction, MiddlewareAPI } from \"redux\";\n\ntype UnknownToUndefined<T> = unknown extends T ? undefined : T;\n\ntype FirstArgument<F> = F extends (arg1: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;\ntype SecondArgument<F> = F extends (arg1: any, arg2: infer U, ...args: any[]) => any\n ? UnknownToUndefined<U>\n : undefined;\n\nconst bindAll = <T extends { [key: string]: Function }>(map: T): T => {\n const result = {} as any as T;\n for (const i in map) {\n result[i] = map[i].bind(map);\n }\n return result;\n};\n\nconst appendPrefix = <T>(prefix: string, map: { [key: string]: T }) => {\n const r: { [key: string]: any } = {};\n for (const i in map) {\n r[prefix + i] = map[i];\n }\n return r;\n};\n\n// Input\ntype ActionHandler<BlockState, Payload> = (state: BlockState, payload: Payload) => BlockState;\ntype ActionMap<BlockState> = { [action: string]: ActionHandler<BlockState, any> };\ntype EffectsMap<GlobalState, ExtraArgument> = (\n dispatch: Dispatch,\n getState: () => GlobalState,\n) => { [effect: string]: (payload: any, extraArgument: ExtraArgument) => any };\n\n// Output\ntype ActionCreator<Handler extends ActionHandler<any, any>> =\n undefined extends SecondArgument<Handler>\n ? () => Action<string>\n : (payload: SecondArgument<Handler>) => Action<string> & { payload: SecondArgument<Handler> };\n\ntype ActionCreatorMap<Actions extends ActionMap<any>> = {\n [name in keyof Actions]: ActionCreator<Actions[name]>;\n};\n\ntype EffectsCreatorMap<GlobalState, ExtraArgument, Map extends EffectsMap<GlobalState, ExtraArgument>> = {\n [key in keyof ReturnType<Map>]: undefined extends FirstArgument<ReturnType<Map>[key]>\n ? () => Action\n : (payload: FirstArgument<ReturnType<Map>[key]>) => Action;\n};\n\n// Transformation\nconst createActionCreator = (type: string) => (payload?: any) => (payload === undefined ? { type } : { type, payload });\nconst createEffectCreator = (type: string) => (payload: any) => ({ type, payload });\n\nconst createReducer = <BlockState>(prefix: string, initial: BlockState, actionMap: ActionMap<BlockState>): Reducer => {\n const actions: ActionMap<BlockState> = appendPrefix(prefix + \"/\", bindAll(actionMap));\n return (state: BlockState = initial, action?: AnyAction) => {\n if (action && action.type) {\n const handler: (state: BlockState, payload?: any) => BlockState = actions[action.type];\n if (handler) {\n return handler(state, action.payload);\n } else {\n return state;\n }\n } else {\n return state;\n }\n };\n};\n\ntype MiddlewareCreator<T> = T extends undefined ? () => Middleware : (argument: T) => Middleware;\n\nconst createMiddlewareCreator = <GlobalState, ExtraArgument>(\n prefix: string,\n effectsMap: EffectsMap<GlobalState, ExtraArgument>,\n): MiddlewareCreator<ExtraArgument> =>\n ((argument: ExtraArgument) => (store: MiddlewareAPI) => {\n const effects = appendPrefix(prefix + \"/\", bindAll(effectsMap(store.dispatch, store.getState)));\n return (next: Dispatch) => (action: Action<string> & { payload: any[] }) => {\n if (action && Object.prototype.hasOwnProperty.call(effects, action.type)) {\n effects[action.type](action.payload, argument);\n } else {\n next(action);\n }\n };\n }) as MiddlewareCreator<ExtraArgument>;\n\nconst fail = (): never => {\n throw new Error(\"Can't have access to 'dispatch' and 'getState' during initialization\");\n};\n\nexport const createReduxBlock =\n <GlobalState, ExtraArgument = undefined>() =>\n <\n Name extends keyof GlobalState & string,\n Actions extends ActionMap<GlobalState[Name]>,\n Effects extends EffectsMap<GlobalState, ExtraArgument>,\n >({\n name,\n initial,\n actions,\n effects,\n }: {\n name: Name;\n initial: GlobalState[Name];\n actions: Actions;\n effects?: Effects;\n }): {\n name: Name;\n reducer: Reducer<GlobalState[Name]>;\n actions: ActionCreatorMap<Actions> & EffectsCreatorMap<GlobalState, ExtraArgument, Effects>;\n } & ({} extends ReturnType<Effects> ? {} : { createMiddleware: MiddlewareCreator<ExtraArgument> }) => {\n const actionCreators = Object.keys(actions).reduce((r, key) => {\n r[key] = createActionCreator(`${name}/${key}`);\n return r;\n }, {} as any);\n const effectCreators = Object.keys(effects ? effects(fail, fail) : {}).reduce((r, key) => {\n r[key] = createEffectCreator(`${name}/${key}`);\n return r;\n }, {} as any);\n\n return {\n name,\n reducer: createReducer(name as string, initial, actions),\n createMiddleware: effects\n ? createMiddlewareCreator<GlobalState, ExtraArgument>(name as string, effects)\n : ((() => (_: MiddlewareAPI) => (next: Dispatch) => next) as MiddlewareCreator<ExtraArgument>),\n actions: {\n ...actionCreators,\n ...effectCreators,\n },\n };\n };\n"],"names":["bindAll","map","result","i","appendPrefix","prefix","r","createActionCreator","type","payload","createEffectCreator","createReducer","initial","actionMap","actions","state","action","handler","createMiddlewareCreator","effectsMap","argument","store","effects","next","fail","createReduxBlock","name","actionCreators","key","effectCreators","_"],"mappings":"AASA,MAAMA,IAAU,CAAwCC,MAAc;AAClE,QAAMC,IAAS,CAAA;AACf,aAAWC,KAAKF;AACZ,IAAAC,EAAOC,CAAC,IAAIF,EAAIE,CAAC,EAAE,KAAKF,CAAG;AAE/B,SAAOC;AACX,GAEME,IAAe,CAAIC,GAAgBJ,MAA8B;AACnE,QAAMK,IAA4B,CAAA;AAClC,aAAWH,KAAKF;AACZ,IAAAK,EAAED,IAASF,CAAC,IAAIF,EAAIE,CAAC;AAEzB,SAAOG;AACX,GA2BMC,IAAsB,CAACC,MAAiB,CAACC,MAAmBA,MAAY,SAAY,EAAE,MAAAD,EAAA,IAAS,EAAE,MAAAA,GAAM,SAAAC,EAAA,GACvGC,IAAsB,CAACF,MAAiB,CAACC,OAAkB,EAAE,MAAAD,GAAM,SAAAC,MAEnEE,IAAgB,CAAaN,GAAgBO,GAAqBC,MAA8C;AAClH,QAAMC,IAAiCV,EAAaC,IAAS,KAAKL,EAAQa,CAAS,CAAC;AACpF,SAAO,CAACE,IAAoBH,GAASI,MAAuB;AACxD,QAAIA,KAAUA,EAAO,MAAM;AACvB,YAAMC,IAA4DH,EAAQE,EAAO,IAAI;AACrF,aAAIC,IACOA,EAAQF,GAAOC,EAAO,OAAO,IAE7BD;AAAA,IAEf;AACI,aAAOA;AAAA,EAEf;AACJ,GAIMG,IAA0B,CAC5Bb,GACAc,OAEC,CAACC,MAA4B,CAACC,MAAyB;AACpD,QAAMC,IAAUlB,EAAaC,IAAS,KAAKL,EAAQmB,EAAWE,EAAM,UAAUA,EAAM,QAAQ,CAAC,CAAC;AAC9F,SAAO,CAACE,MAAmB,CAACP,MAAgD;AACxE,IAAIA,KAAU,OAAO,UAAU,eAAe,KAAKM,GAASN,EAAO,IAAI,IACnEM,EAAQN,EAAO,IAAI,EAAEA,EAAO,SAASI,CAAQ,IAE7CG,EAAKP,CAAM;AAAA,EAEnB;AACJ,IAEEQ,IAAO,MAAa;AACtB,QAAM,IAAI,MAAM,sEAAsE;AAC1F,GAEaC,IACT,MACA,CAIE;AAAA,EACE,MAAAC;AAAA,EACA,SAAAd;AAAA,EACA,SAAAE;AAAA,EACA,SAAAQ;AACJ,MASsG;AAClG,QAAMK,IAAiB,OAAO,KAAKb,CAAO,EAAE,OAAO,CAACR,GAAGsB,OACnDtB,EAAEsB,CAAG,IAAIrB,EAAoB,GAAGmB,CAAI,IAAIE,CAAG,EAAE,GACtCtB,IACR,CAAA,CAAS,GACNuB,IAAiB,OAAO,KAAKP,IAAUA,EAAQE,GAAMA,CAAI,IAAI,CAAA,CAAE,EAAE,OAAO,CAAClB,GAAGsB,OAC9EtB,EAAEsB,CAAG,IAAIlB,EAAoB,GAAGgB,CAAI,IAAIE,CAAG,EAAE,GACtCtB,IACR,CAAA,CAAS;AAEZ,SAAO;AAAA,IACH,MAAAoB;AAAA,IACA,SAASf,EAAce,GAAgBd,GAASE,CAAO;AAAA,IACvD,kBAAkBQ,IACZJ,EAAoDQ,GAAgBJ,CAAO,KACzE,MAAM,CAACQ,MAAqB,CAACP,MAAmBA;AAAA,IACxD,SAAS;AAAA,MACL,GAAGI;AAAA,MACH,GAAGE;AAAA,IAAA;AAAA,EACP;AAER;"}
|
package/package.json
CHANGED
|
@@ -1,24 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "redux-sacala",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"author": {
|
|
5
6
|
"name": "Andrey Monkin",
|
|
6
7
|
"email": "monkin.andrey@gmail.com"
|
|
7
8
|
},
|
|
8
9
|
"module": "build/redux-sacala.js",
|
|
9
|
-
"main": "build/redux-sacala.
|
|
10
|
+
"main": "build/redux-sacala.cjs",
|
|
10
11
|
"types": "build/redux-sacala.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./build/redux-sacala.d.ts",
|
|
15
|
+
"import": "./build/redux-sacala.js",
|
|
16
|
+
"require": "./build/redux-sacala.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
11
19
|
"peerDependencies": {
|
|
12
|
-
"redux": "^
|
|
20
|
+
"redux": "^5.0.1"
|
|
13
21
|
},
|
|
14
22
|
"devDependencies": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"@
|
|
18
|
-
"
|
|
23
|
+
"@eslint/js": "9.39.2",
|
|
24
|
+
"@types/node": "25.0.3",
|
|
25
|
+
"@typescript-eslint/eslint-plugin": "8.51.0",
|
|
26
|
+
"@typescript-eslint/parser": "8.51.0",
|
|
27
|
+
"eslint": "9.39.2",
|
|
28
|
+
"eslint-config-prettier": "10.1.8",
|
|
29
|
+
"prettier": "3.7.4",
|
|
30
|
+
"typescript": "5.9.3",
|
|
31
|
+
"typescript-eslint": "8.51.0",
|
|
32
|
+
"vite": "7.3.0",
|
|
33
|
+
"vite-plugin-dts": "4.5.4",
|
|
34
|
+
"vitest": "4.0.16"
|
|
19
35
|
},
|
|
20
36
|
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
37
|
+
"build": "vite build",
|
|
38
|
+
"prepublishOnly": "npm test && npm run build",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"lint": "eslint .",
|
|
41
|
+
"format": "prettier --write ."
|
|
23
42
|
}
|
|
24
43
|
}
|
package/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/redux-sacala.iml" filepath="$PROJECT_DIR$/.idea/redux-sacala.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|
package/.idea/redux-sacala.iml
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
<excludeFolder url="file://$MODULE_DIR$/build" />
|
|
9
|
-
</content>
|
|
10
|
-
<orderEntry type="inheritedJdk" />
|
|
11
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
12
|
-
</component>
|
|
13
|
-
</module>
|
package/.idea/vcs.xml
DELETED
package/.idea/webResources.xml
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="WebResourcesPaths">
|
|
4
|
-
<contentEntries>
|
|
5
|
-
<entry url="file://$PROJECT_DIR$">
|
|
6
|
-
<entryData>
|
|
7
|
-
<resourceRoots>
|
|
8
|
-
<path value="file://$PROJECT_DIR$/src" />
|
|
9
|
-
</resourceRoots>
|
|
10
|
-
</entryData>
|
|
11
|
-
</entry>
|
|
12
|
-
</contentEntries>
|
|
13
|
-
</component>
|
|
14
|
-
</project>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { createStore, combineReducers, applyMiddleware, } from "redux";
|
|
2
|
-
import { createReduxBlock } from "./redux-sacala";
|
|
3
|
-
const { actions: local, reducer: localReducer, createMiddleware: createLocalMiddleware, } = createReduxBlock()({
|
|
4
|
-
name: "local",
|
|
5
|
-
initial: { count: 0 },
|
|
6
|
-
actions: {
|
|
7
|
-
inc(state) {
|
|
8
|
-
return { count: state.count + 1 };
|
|
9
|
-
},
|
|
10
|
-
set(_, count) {
|
|
11
|
-
return { count };
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
effects: (dispatch, getState) => {
|
|
15
|
-
return {
|
|
16
|
-
incEffect() {
|
|
17
|
-
dispatch(local.inc());
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
function createMyStore() {
|
|
23
|
-
return createStore(combineReducers({
|
|
24
|
-
local: localReducer,
|
|
25
|
-
}), applyMiddleware(createLocalMiddleware(100)));
|
|
26
|
-
}
|
|
27
|
-
let store = createMyStore();
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
store = createMyStore();
|
|
30
|
-
});
|
|
31
|
-
describe("Store with reducer and middleware", () => {
|
|
32
|
-
it("Should be updated on action without payload", () => {
|
|
33
|
-
store.dispatch(local.inc());
|
|
34
|
-
store.dispatch(local.inc());
|
|
35
|
-
expect(store.getState()).toEqual({ local: { count: 2 } });
|
|
36
|
-
});
|
|
37
|
-
it("Should be updated on action with payload", () => {
|
|
38
|
-
store.dispatch(local.set(12));
|
|
39
|
-
expect(store.getState()).toEqual({ local: { count: 12 } });
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
//# sourceMappingURL=redux-sacala.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"redux-sacala.spec.js","sourceRoot":"","sources":["../src/redux-sacala.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,eAAe,EACf,eAAe,GAClB,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAUlD,MAAM,EACF,OAAO,EAAE,KAAK,EACd,OAAO,EAAE,YAAY,EACrB,gBAAgB,EAAE,qBAAqB,GAC1C,GAAG,gBAAgB,EAAmB,CAAC;IACpC,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;IACrB,OAAO,EAAE;QACL,GAAG,CAAC,KAAK;YACL,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QACtC,CAAC;QACD,GAAG,CAAC,CAAC,EAAE,KAAa;YAChB,OAAO,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC;KACJ;IACD,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC5B,OAAO;YACH,SAAS;gBACL,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAC1B,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC,CAAC;AAEH,SAAS,aAAa;IAClB,OAAO,WAAW,CAAC,eAAe,CAAC;QAC/B,KAAK,EAAE,YAAY;KACtB,CAAC,EAAE,eAAe,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,KAAK,GAAG,aAAa,EAAE,CAAC;AAE5B,UAAU,CAAC,GAAG,EAAE;IACZ,KAAK,GAAG,aAAa,EAAE,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACnD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5B,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAChD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/jest.config.js
DELETED
package/src/redux-sacala.spec.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createStore,
|
|
3
|
-
combineReducers,
|
|
4
|
-
applyMiddleware,
|
|
5
|
-
} from "redux";
|
|
6
|
-
import { createReduxBlock } from "./redux-sacala";
|
|
7
|
-
|
|
8
|
-
interface LocalState {
|
|
9
|
-
count: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface MyState {
|
|
13
|
-
local: LocalState;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const {
|
|
17
|
-
actions: local,
|
|
18
|
-
reducer: localReducer,
|
|
19
|
-
createMiddleware: createLocalMiddleware,
|
|
20
|
-
} = createReduxBlock<MyState, number>()({
|
|
21
|
-
name: "local",
|
|
22
|
-
initial: { count: 0 },
|
|
23
|
-
actions: {
|
|
24
|
-
inc(state) {
|
|
25
|
-
return { count: state.count + 1 };
|
|
26
|
-
},
|
|
27
|
-
set(_, count: number) {
|
|
28
|
-
return { count };
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
effects: (dispatch, getState) => {
|
|
32
|
-
return {
|
|
33
|
-
incEffect() {
|
|
34
|
-
dispatch(local.inc());
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
function createMyStore() {
|
|
41
|
-
return createStore(combineReducers({
|
|
42
|
-
local: localReducer,
|
|
43
|
-
}), applyMiddleware(createLocalMiddleware(100)));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let store = createMyStore();
|
|
47
|
-
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
store = createMyStore();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe("Store with reducer and middleware", () => {
|
|
53
|
-
it("Should be updated on action without payload", () => {
|
|
54
|
-
store.dispatch(local.inc());
|
|
55
|
-
store.dispatch(local.inc());
|
|
56
|
-
expect(store.getState()).toEqual({ local: { count: 2 } });
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("Should be updated on action with payload", () => {
|
|
60
|
-
store.dispatch(local.set(12));
|
|
61
|
-
expect(store.getState()).toEqual({ local: { count: 12 } });
|
|
62
|
-
});
|
|
63
|
-
});
|
package/src/redux-sacala.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { Dispatch, Action, Reducer, Middleware, AnyAction, MiddlewareAPI } from "redux";
|
|
2
|
-
|
|
3
|
-
type UnknownToUndefined<T> = unknown extends T ? undefined : T;
|
|
4
|
-
|
|
5
|
-
type FirstArgument<F> = F extends (arg1: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;
|
|
6
|
-
type SecondArgument<F> = F extends (arg1: any, arg2: infer U, ...args: any[]) => any ? UnknownToUndefined<U> : undefined;
|
|
7
|
-
|
|
8
|
-
function bindAll<T extends { [key: string]: Function }>(map: T): T {
|
|
9
|
-
const result = {} as any as T;
|
|
10
|
-
for (const i in map) {
|
|
11
|
-
result[i] = map[i].bind(map);
|
|
12
|
-
}
|
|
13
|
-
return result;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function appendPrefix<T>(prefix: string, map: { [key: string]: T }) {
|
|
17
|
-
const r: { [key: string]: any } = {};
|
|
18
|
-
for (const i in map) {
|
|
19
|
-
r[prefix + i] = map[i];
|
|
20
|
-
}
|
|
21
|
-
return r;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Input
|
|
25
|
-
type ActionHandler<BlockState, Payload> = (state: BlockState, payload: Payload) => BlockState;
|
|
26
|
-
type ActionMap<BlockState> = { [action: string]: ActionHandler<BlockState, any> };
|
|
27
|
-
type EffectsMap<GlobalState, ExtraArgument> = (dispatch: Dispatch, getState: () => GlobalState) => { [effect: string]: (payload: any, extraArgument: ExtraArgument) => any }
|
|
28
|
-
|
|
29
|
-
// Output
|
|
30
|
-
type ActionCreator<Handler extends ActionHandler<any, any>> = undefined extends SecondArgument<Handler>
|
|
31
|
-
? () => Action<string>
|
|
32
|
-
: (payload: SecondArgument<Handler>) => (Action<string> & { payload: SecondArgument<Handler> });
|
|
33
|
-
|
|
34
|
-
type ActionCreatorMap<Actions extends ActionMap<any>> = {
|
|
35
|
-
[name in keyof Actions]: ActionCreator<Actions[name]>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
type EffectsCreatorMap<GlobalState, ExtraArgument, Map extends EffectsMap<GlobalState, ExtraArgument>> = {
|
|
39
|
-
[key in keyof ReturnType<Map>]: (undefined extends FirstArgument<ReturnType<Map>[key]>
|
|
40
|
-
? () => Action
|
|
41
|
-
: (payload: FirstArgument<ReturnType<Map>[key]>) => Action
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Transformation
|
|
46
|
-
function createActionCreator(type: string) {
|
|
47
|
-
return (payload?: any) => payload === undefined ? { type } : { type, payload };
|
|
48
|
-
}
|
|
49
|
-
function createEffectCreator(type: string) {
|
|
50
|
-
return (payload: any) => ({ type, payload })
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function createReducer<BlockState>(prefix: string, initial: BlockState, actionMap: ActionMap<BlockState>): Reducer {
|
|
54
|
-
const actions: ActionMap<BlockState> = appendPrefix(prefix + "/", bindAll(actionMap));
|
|
55
|
-
return (state: BlockState = initial, action?: AnyAction) => {
|
|
56
|
-
if (action && action.type) {
|
|
57
|
-
const handler: (state: BlockState, payload?: any) => BlockState = actions[action.type];
|
|
58
|
-
if (handler) {
|
|
59
|
-
return handler(state, action.payload);
|
|
60
|
-
} else {
|
|
61
|
-
return state;
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
return state;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type MiddlewareCreator<T> = T extends undefined ? () => Middleware : (argument: T) => Middleware;
|
|
70
|
-
|
|
71
|
-
function createMiddlewareCreator<GlobalState, ExtraArgument>(prefix: string, effectsMap: EffectsMap<GlobalState, ExtraArgument>): MiddlewareCreator<ExtraArgument> {
|
|
72
|
-
return ((argument: ExtraArgument) => (store: MiddlewareAPI) => {
|
|
73
|
-
const effects = appendPrefix(prefix + "/", bindAll(effectsMap(store.dispatch, store.getState)));
|
|
74
|
-
return (next: Dispatch) => (action: Action<string> & { payload: any[] }) => {
|
|
75
|
-
if (action && effects.hasOwnProperty(action.type)) {
|
|
76
|
-
effects[action.type](action.payload, argument);
|
|
77
|
-
} else {
|
|
78
|
-
next(action);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}) as MiddlewareCreator<ExtraArgument>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function fail(): never {
|
|
85
|
-
throw new Error("Can't have access to 'dispatch' and 'getState' during initialization");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function createReduxBlock<GlobalState, ExtraArgument = undefined>() {
|
|
89
|
-
return function applyConfig<
|
|
90
|
-
Name extends (keyof GlobalState) & string,
|
|
91
|
-
Actions extends ActionMap<GlobalState[Name]>,
|
|
92
|
-
Effects extends EffectsMap<GlobalState, ExtraArgument>,
|
|
93
|
-
>({ name, initial, actions, effects }: {
|
|
94
|
-
name: Name;
|
|
95
|
-
initial: GlobalState[Name];
|
|
96
|
-
actions: Actions;
|
|
97
|
-
effects?: Effects;
|
|
98
|
-
}): {
|
|
99
|
-
name: Name;
|
|
100
|
-
reducer: Reducer<GlobalState[Name]>;
|
|
101
|
-
actions: ActionCreatorMap<Actions> & EffectsCreatorMap<GlobalState, ExtraArgument, Effects>;
|
|
102
|
-
} & ({} extends ReturnType<Effects> ? {} : { createMiddleware: MiddlewareCreator<ExtraArgument>; }) {
|
|
103
|
-
const actionCreators = Object.keys(actions).reduce((r, key) => {
|
|
104
|
-
r[key] = createActionCreator(`${name}/${key}`);
|
|
105
|
-
return r;
|
|
106
|
-
}, {} as any);
|
|
107
|
-
const effectCreators = Object.keys(effects ? effects(fail, fail) : {}).reduce((r, key) => {
|
|
108
|
-
r[key] = createEffectCreator(`${name}/${key}`);
|
|
109
|
-
return r;
|
|
110
|
-
}, {} as any);
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
name,
|
|
114
|
-
reducer: createReducer(name as string, initial, actions),
|
|
115
|
-
createMiddleware: effects
|
|
116
|
-
? createMiddlewareCreator<GlobalState, ExtraArgument>(name as string, effects)
|
|
117
|
-
: (() => ((_: MiddlewareAPI) => (next: Dispatch) => next)) as MiddlewareCreator<ExtraArgument>,
|
|
118
|
-
actions: {
|
|
119
|
-
...actionCreators,
|
|
120
|
-
...effectCreators
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"strict": true,
|
|
4
|
-
"noImplicitAny": true,
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"module": "ES2015",
|
|
7
|
-
"outDir": "build",
|
|
8
|
-
"sourceMap": true,
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"target": "es2015"
|
|
11
|
-
},
|
|
12
|
-
"include": [
|
|
13
|
-
"src/*.ts"
|
|
14
|
-
]
|
|
15
|
-
}
|