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