@evertbouw/redux-toolkit
Version:
Redux toolkit but without immer
444 lines (367 loc) • 13.8 kB
JavaScript
import { combineReducers, applyMiddleware, createStore, compose } from 'redux';
export * from 'redux';
export { createSelector } from 'reselect';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunkMiddleware from 'redux-thunk';
import createImmutableStateInvariantMiddleware from 'redux-immutable-state-invariant';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
/**
* Returns true if the passed value is "plain" object, i.e. an object whose
* protoype is the root `Object.prototype`. This includes objects created
* using object literals, but not for instance for class instances.
*
* @param {any} value The value to inspect.
* @returns {boolean} True if the argument appears to be a plain object.
*/
function isPlainObject(value) {
if (typeof value !== 'object' || value === null) return false;
var proto = value;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(value) === proto;
}
/**
* Returns true if the passed value is "plain", i.e. a value that is either
* directly JSON-serializable (boolean, number, string, array, plain object)
* or `undefined`.
*
* @param val The value to check.
*
* @public
*/
function isPlain(val) {
return typeof val === 'undefined' || val === null || typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number' || Array.isArray(val) || isPlainObject(val);
}
/**
* @public
*/
function findNonSerializableValue(value, path, isSerializable, getEntries, ignoredPaths) {
if (path === void 0) {
path = [];
}
if (isSerializable === void 0) {
isSerializable = isPlain;
}
if (ignoredPaths === void 0) {
ignoredPaths = [];
}
var foundNestedSerializable;
if (!isSerializable(value)) {
return {
keyPath: path.join('.') || '<root>',
value: value
};
}
if (typeof value !== 'object' || value === null) {
return false;
}
var entries = getEntries != null ? getEntries(value) : Object.entries(value);
var hasIgnoredPaths = ignoredPaths.length > 0;
for (var _iterator = entries, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var _ref2 = _ref,
property = _ref2[0],
nestedValue = _ref2[1];
var nestedPath = path.concat(property);
if (hasIgnoredPaths && ignoredPaths.indexOf(nestedPath.join('.')) >= 0) {
continue;
}
if (!isSerializable(nestedValue)) {
return {
keyPath: nestedPath.join('.'),
value: nestedValue
};
}
if (typeof nestedValue === 'object') {
foundNestedSerializable = findNonSerializableValue(nestedValue, nestedPath, isSerializable, getEntries, ignoredPaths);
if (foundNestedSerializable) {
return foundNestedSerializable;
}
}
}
return false;
}
/**
* Creates a middleware that, after every state change, checks if the new
* state is serializable. If a non-serializable value is found within the
* state, an error is printed to the console.
*
* @param options Middleware options.
*
* @public
*/
function createSerializableStateInvariantMiddleware(options) {
if (options === void 0) {
options = {};
}
var _options = options,
_options$isSerializab = _options.isSerializable,
isSerializable = _options$isSerializab === void 0 ? isPlain : _options$isSerializab,
getEntries = _options.getEntries,
_options$ignoredActio = _options.ignoredActions,
ignoredActions = _options$ignoredActio === void 0 ? [] : _options$ignoredActio,
_options$ignoredPaths = _options.ignoredPaths,
ignoredPaths = _options$ignoredPaths === void 0 ? [] : _options$ignoredPaths;
return function (storeAPI) {
return function (next) {
return function (action) {
if (ignoredActions.length && ignoredActions.indexOf(action.type) !== -1) {
return next(action);
}
var foundActionNonSerializableValue = findNonSerializableValue(action, [], isSerializable, getEntries);
if (foundActionNonSerializableValue) {
var keyPath = foundActionNonSerializableValue.keyPath,
value = foundActionNonSerializableValue.value;
console.error("A non-serializable value was detected in an action, in the path: `" + keyPath + "`. Value:", value, '\nTake a look at the logic that dispatched this action: ', action, '\n(See https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)');
}
var result = next(action);
var state = storeAPI.getState();
var foundStateNonSerializableValue = findNonSerializableValue(state, [], isSerializable, getEntries, ignoredPaths);
if (foundStateNonSerializableValue) {
var _keyPath = foundStateNonSerializableValue.keyPath,
_value = foundStateNonSerializableValue.value;
console.error("A non-serializable value was detected in the state, in the path: `" + _keyPath + "`. Value:", _value, "\nTake a look at the reducer(s) handling this action type: " + action.type + ".\n(See https://redux.js.org/faq/organizing-state#can-i-put-functions-promises-or-other-non-serializable-items-in-my-store-state)");
}
return result;
};
};
};
}
function isBoolean(x) {
return typeof x === 'boolean';
}
/**
* Returns any array containing the default middleware installed by
* `configureStore()`. Useful if you want to configure your store with a custom
* `middleware` array but still keep the default set.
*
* @return The default middleware used by `configureStore()`.
*
* @public
*/
function getDefaultMiddleware(options) {
if (options === void 0) {
options = {};
}
var _options = options,
_options$thunk = _options.thunk,
thunk = _options$thunk === void 0 ? true : _options$thunk,
_options$immutableChe = _options.immutableCheck,
immutableCheck = _options$immutableChe === void 0 ? true : _options$immutableChe,
_options$serializable = _options.serializableCheck,
serializableCheck = _options$serializable === void 0 ? true : _options$serializable;
var middlewareArray = [];
if (thunk) {
if (isBoolean(thunk)) {
middlewareArray.push(thunkMiddleware);
} else {
middlewareArray.push(thunkMiddleware.withExtraArgument(thunk.extraArgument));
}
}
if (process.env.NODE_ENV !== 'production') {
if (immutableCheck) {
/* PROD_START_REMOVE_UMD */
var immutableOptions = {};
if (!isBoolean(immutableCheck)) {
immutableOptions = immutableCheck;
}
middlewareArray.unshift(createImmutableStateInvariantMiddleware(immutableOptions));
/* PROD_STOP_REMOVE_UMD */
}
if (serializableCheck) {
var serializableOptions = {};
if (!isBoolean(serializableCheck)) {
serializableOptions = serializableCheck;
}
middlewareArray.push(createSerializableStateInvariantMiddleware(serializableOptions));
}
}
return middlewareArray;
}
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
/**
* A friendly abstraction over the standard Redux `createStore()` function.
*
* @param config The store configuration.
* @returns A configured Redux store.
*
* @public
*/
function configureStore(options) {
var _ref = options || {},
_ref$reducer = _ref.reducer,
reducer = _ref$reducer === void 0 ? undefined : _ref$reducer,
_ref$middleware = _ref.middleware,
middleware = _ref$middleware === void 0 ? getDefaultMiddleware() : _ref$middleware,
_ref$devTools = _ref.devTools,
devTools = _ref$devTools === void 0 ? true : _ref$devTools,
_ref$preloadedState = _ref.preloadedState,
preloadedState = _ref$preloadedState === void 0 ? undefined : _ref$preloadedState,
_ref$enhancers = _ref.enhancers,
enhancers = _ref$enhancers === void 0 ? undefined : _ref$enhancers;
var rootReducer;
if (typeof reducer === 'function') {
rootReducer = reducer;
} else if (isPlainObject(reducer)) {
rootReducer = combineReducers(reducer);
} else {
throw new Error('"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers');
}
var middlewareEnhancer = applyMiddleware.apply(void 0, middleware);
var finalCompose = compose;
if (devTools) {
finalCompose = composeWithDevTools(_extends({
// Enable capture of stack traces for dispatched Redux actions
trace: !IS_PRODUCTION
}, typeof devTools === 'object' && devTools));
}
var storeEnhancers = [middlewareEnhancer];
if (Array.isArray(enhancers)) {
storeEnhancers = [middlewareEnhancer].concat(enhancers);
} else if (typeof enhancers === 'function') {
storeEnhancers = enhancers(storeEnhancers);
}
var composedEnhancer = finalCompose.apply(void 0, storeEnhancers);
return createStore(rootReducer, preloadedState, composedEnhancer);
}
function createAction(type, prepareAction) {
function actionCreator() {
if (prepareAction) {
var prepared = prepareAction.apply(void 0, arguments);
if (!prepared) {
throw new Error('prepareAction did not return an object');
}
return _extends({
type: type,
payload: prepared.payload
}, 'meta' in prepared && {
meta: prepared.meta
}, {}, 'error' in prepared && {
error: prepared.error
});
}
return {
type: type,
payload: arguments.length <= 0 ? undefined : arguments[0]
};
}
actionCreator.toString = function () {
return "" + type;
};
actionCreator.type = type;
actionCreator.match = function (action) {
return action.type === type;
};
return actionCreator;
}
/**
* Returns the action type of the actions created by the passed
* `createAction()`-generated action creator (arbitrary action creators
* are not supported).
*
* @param action The action creator whose action type to get.
* @returns The action type used by the action creator.
*
* @public
*/
function getType(actionCreator) {
return "" + actionCreator;
}
function executeReducerBuilderCallback(builderCallback) {
var actionsMap = {};
var builder = {
addCase: function addCase(typeOrActionCreator, reducer) {
var type = typeof typeOrActionCreator === 'string' ? typeOrActionCreator : typeOrActionCreator.type;
if (type in actionsMap) {
throw new Error('addCase cannot be called with two reducers for the same action type');
}
actionsMap[type] = reducer;
return builder;
}
};
builderCallback(builder);
return actionsMap;
}
function createReducer(initialState, mapOrBuilderCallback) {
var actionsMap = typeof mapOrBuilderCallback === 'function' ? executeReducerBuilderCallback(mapOrBuilderCallback) : mapOrBuilderCallback;
return function (state, action) {
if (state === void 0) {
state = initialState;
}
var reducer = actionsMap[action.type];
return reducer === undefined ? state : reducer(state, action);
};
}
function getType$1(slice, actionKey) {
return slice + "/" + actionKey;
}
/**
* A function that accepts an initial state, an object full of reducer
* functions, and a "slice name", and automatically generates
* action creators and action types that correspond to the
* reducers and state.
*
* The `reducer` argument is passed to `createReducer()`.
*
* @public
*/
function createSlice(options) {
var name = options.name,
initialState = options.initialState;
if (!name) {
throw new Error('`name` is a required option for createSlice');
}
var reducers = options.reducers || {};
var extraReducers = typeof options.extraReducers === 'undefined' ? {} : typeof options.extraReducers === 'function' ? executeReducerBuilderCallback(options.extraReducers) : options.extraReducers;
var reducerNames = Object.keys(reducers);
var sliceCaseReducersByName = {};
var sliceCaseReducersByType = {};
var actionCreators = {};
reducerNames.forEach(function (reducerName) {
var maybeReducerWithPrepare = reducers[reducerName];
var type = getType$1(name, reducerName);
var caseReducer;
var prepareCallback;
if ('reducer' in maybeReducerWithPrepare) {
caseReducer = maybeReducerWithPrepare.reducer;
prepareCallback = maybeReducerWithPrepare.prepare;
} else {
caseReducer = maybeReducerWithPrepare;
}
sliceCaseReducersByName[reducerName] = caseReducer;
sliceCaseReducersByType[type] = caseReducer;
actionCreators[reducerName] = prepareCallback ? createAction(type, prepareCallback) : createAction(type);
});
var finalCaseReducers = _extends({}, extraReducers, {}, sliceCaseReducersByType);
var reducer = createReducer(initialState, finalCaseReducers);
return {
name: name,
reducer: reducer,
actions: actionCreators,
caseReducers: sliceCaseReducersByName
};
}
export { configureStore, createAction, createReducer, createSerializableStateInvariantMiddleware, createSlice, findNonSerializableValue, getDefaultMiddleware, getType, isPlain };
//# sourceMappingURL=redux-toolkit.esm.js.map