1 | import type {
|
2 | Reducer,
|
3 | ReducersMapObject,
|
4 | Middleware,
|
5 | Action,
|
6 | AnyAction,
|
7 | StoreEnhancer,
|
8 | Store,
|
9 | Dispatch,
|
10 | PreloadedState,
|
11 | CombinedState,
|
12 | } from 'redux'
|
13 | import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
|
14 | import type { EnhancerOptions as DevToolsOptions } from './devtoolsExtension'
|
15 | import { composeWithDevTools } from './devtoolsExtension'
|
16 |
|
17 | import isPlainObject from './isPlainObject'
|
18 | import type {
|
19 | ThunkMiddlewareFor,
|
20 | CurriedGetDefaultMiddleware,
|
21 | } from './getDefaultMiddleware'
|
22 | import { curryGetDefaultMiddleware } from './getDefaultMiddleware'
|
23 | import type { DispatchForMiddlewares, NoInfer } from './tsHelpers'
|
24 |
|
25 | const IS_PRODUCTION = process.env.NODE_ENV === 'production'
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | export type ConfigureEnhancersCallback = (
|
33 | defaultEnhancers: readonly StoreEnhancer[]
|
34 | ) => StoreEnhancer[]
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | export interface ConfigureStoreOptions<
|
42 | S = any,
|
43 | A extends Action = AnyAction,
|
44 | M extends Middlewares<S> = Middlewares<S>
|
45 | > {
|
46 | |
47 |
|
48 |
|
49 |
|
50 | reducer: Reducer<S, A> | ReducersMapObject<S, A>
|
51 |
|
52 | |
53 |
|
54 |
|
55 |
|
56 | middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M
|
57 |
|
58 | /**
|
59 | * Whether to enable Redux DevTools integration. Defaults to `true`.
|
60 | *
|
61 | * Additional configuration can be done by passing Redux DevTools options
|
62 | */
|
63 | devTools?: boolean | DevToolsOptions
|
64 |
|
65 | /**
|
66 | * The initial state, same as Redux's createStore.
|
67 | * You may optionally specify it to hydrate the state
|
68 | * from the server in universal apps, or to restore a previously serialized
|
69 | * user session. If you use `combineReducers()` to produce the root reducer
|
70 | * function (either directly or indirectly by passing an object as `reducer`),
|
71 | * this must be an object with the same shape as the reducer map keys.
|
72 | */
|
73 | /*
|
74 | Not 100% correct but the best approximation we can get:
|
75 | - if S is a `CombinedState` applying a second `CombinedState` on it does not change anything.
|
76 | - if it is not, there could be two cases:
|
77 | - `ReducersMapObject<S, A>` is being passed in. In this case, we will call `combineReducers` on it and `CombinedState<S>` is correct
|
78 | - `Reducer<S, A>` is being passed in. In this case, actually `CombinedState<S>` is wrong and `S` would be correct.
|
79 | As we cannot distinguish between those two cases without adding another generic paramter,
|
80 | we just make the pragmatic assumption that the latter almost never happens.
|
81 | */
|
82 | preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>
|
83 |
|
84 | /**
|
85 | * The store enhancers to apply. See Redux's `createStore()`.
|
86 | * All enhancers will be included before the DevTools Extension enhancer.
|
87 | * If you need to customize the order of enhancers, supply a callback
|
88 | * function that will receive the original array (ie, `[applyMiddleware]`),
|
89 | * and should return a new array (such as `[applyMiddleware, offline]`).
|
90 | * If you only need to add middleware, you can use the `middleware` parameter instead.
|
91 | */
|
92 | enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback
|
93 | }
|
94 |
|
95 | type Middlewares<S> = ReadonlyArray<Middleware<{}, S>>
|
96 |
|
97 | /**
|
98 | * A Redux store returned by `configureStore()`. Supports dispatching
|
99 | * side-effectful _thunks_ in addition to plain actions.
|
100 | *
|
101 | * @public
|
102 | */
|
103 | export interface EnhancedStore<
|
104 | S = any,
|
105 | A extends Action = AnyAction,
|
106 | M extends Middlewares<S> = Middlewares<S>
|
107 | > extends Store<S, A> {
|
108 | /**
|
109 | * The `dispatch` method of your store, enhanced by all its middlewares.
|
110 | *
|
111 | * @inheritdoc
|
112 | */
|
113 | dispatch: DispatchForMiddlewares<M> & Dispatch<A>
|
114 | }
|
115 |
|
116 | /**
|
117 | * A friendly abstraction over the standard Redux `createStore()` function.
|
118 | *
|
119 | * @param config The store configuration.
|
120 | * @returns A configured Redux store.
|
121 | *
|
122 | * @public
|
123 | */
|
124 | export function configureStore<
|
125 | S = any,
|
126 | A extends Action = AnyAction,
|
127 | M extends Middlewares<S> = [ThunkMiddlewareFor<S>]
|
128 | >(options: ConfigureStoreOptions<S, A, M>): EnhancedStore<S, A, M> {
|
129 | const curriedGetDefaultMiddleware = curryGetDefaultMiddleware<S>()
|
130 |
|
131 | const {
|
132 | reducer = undefined,
|
133 | middleware = curriedGetDefaultMiddleware(),
|
134 | devTools = true,
|
135 | preloadedState = undefined,
|
136 | enhancers = undefined,
|
137 | } = options || {}
|
138 |
|
139 | let rootReducer: Reducer<S, A>
|
140 |
|
141 | if (typeof reducer === 'function') {
|
142 | rootReducer = reducer
|
143 | } else if (isPlainObject(reducer)) {
|
144 | rootReducer = combineReducers(reducer)
|
145 | } else {
|
146 | throw new Error(
|
147 | '"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers'
|
148 | )
|
149 | }
|
150 |
|
151 | let finalMiddleware = middleware
|
152 | if (typeof finalMiddleware === 'function') {
|
153 | finalMiddleware = finalMiddleware(curriedGetDefaultMiddleware)
|
154 |
|
155 | if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) {
|
156 | throw new Error(
|
157 | 'when using a middleware builder function, an array of middleware must be returned'
|
158 | )
|
159 | }
|
160 | }
|
161 | if (
|
162 | !IS_PRODUCTION &&
|
163 | finalMiddleware.some((item) => typeof item !== 'function')
|
164 | ) {
|
165 | throw new Error(
|
166 | 'each middleware provided to configureStore must be a function'
|
167 | )
|
168 | }
|
169 |
|
170 | const middlewareEnhancer = applyMiddleware(...finalMiddleware)
|
171 |
|
172 | let finalCompose = compose
|
173 |
|
174 | if (devTools) {
|
175 | finalCompose = composeWithDevTools({
|
176 |
|
177 | trace: !IS_PRODUCTION,
|
178 | ...(typeof devTools === 'object' && devTools),
|
179 | })
|
180 | }
|
181 |
|
182 | let storeEnhancers: StoreEnhancer[] = [middlewareEnhancer]
|
183 |
|
184 | if (Array.isArray(enhancers)) {
|
185 | storeEnhancers = [middlewareEnhancer, ...enhancers]
|
186 | } else if (typeof enhancers === 'function') {
|
187 | storeEnhancers = enhancers(storeEnhancers)
|
188 | }
|
189 |
|
190 | const composedEnhancer = finalCompose(...storeEnhancers) as any
|
191 |
|
192 | return createStore(rootReducer, preloadedState, composedEnhancer)
|
193 | }
|
194 |
|
\ | No newline at end of file |