├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── DEV_ONLY ├── App.js ├── index.js └── store.js ├── LICENSE ├── README.md ├── package.json ├── src ├── actions │ ├── index.js │ ├── localActions.js │ └── sessionActions.js ├── constants.js ├── index.js ├── reducers │ ├── index.js │ ├── localReducer.js │ └── sessionReducer.js └── utils.js ├── test ├── actions │ ├── index.js │ ├── localActions.js │ └── sessionActions.js ├── helpers │ └── setup-browser-env.js ├── index.js ├── reducers │ ├── index.js │ ├── localReducer.js │ └── sessionReducer.js └── utils.js ├── webpack ├── webpack.config.dev.js ├── webpack.config.js └── webpack.config.minified.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "lib": { 4 | "presets": [ 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "loose": true 9 | } 10 | ] 11 | ] 12 | }, 13 | "test": { 14 | "presets": [ 15 | [ 16 | "@babel/preset-env", 17 | { 18 | "loose": true 19 | } 20 | ] 21 | ] 22 | } 23 | }, 24 | "presets": [ 25 | [ 26 | "@babel/preset-env", 27 | { 28 | "loose": true, 29 | "modules": false 30 | } 31 | ] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["rapid7/browser", "rapid7/react"], 3 | "parser": "babel-eslint", 4 | "rules": {} 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .nyc_output 3 | coverage 4 | lib 5 | dist 6 | es 7 | node_modules 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea 3 | .nyc_output 4 | DEV_ONLY 5 | node_modules 6 | src 7 | test 8 | webpack 9 | .babelrc 10 | .eslintrc 11 | .gitignore 12 | .npmignore 13 | webpack.config.js 14 | webpack.config.minified.js 15 | webpack.config.dev.js 16 | yarn.lock 17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | scripts-prepend-node-path=true -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # redux-browser-storage CHANGELOG 2 | 3 | ## 1.1.0 4 | 5 | - Activate tree-shaking with `module` build 6 | - Replace `kari` with `unchanged` 7 | - Fix security issue with old version of `webpack-dev-server` 8 | 9 | ## 1.0.1 10 | 11 | - Update `kari` version to fix issue 12 | 13 | ## 1.0.0 14 | 15 | - Initial release 16 | -------------------------------------------------------------------------------- /DEV_ONLY/App.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import PropTypes from 'prop-types'; 3 | import React, {PureComponent} from 'react'; 4 | import {connect} from 'react-redux'; 5 | 6 | // actions 7 | import { 8 | localActions, 9 | sessionActions, 10 | } from '../src'; 11 | 12 | const createOnClickClearValues = (instance, type) => () => { 13 | const {clearLocalValues, clearSessionValues} = instance.props; 14 | 15 | const action = type === 'local' ? clearLocalValues : clearSessionValues; 16 | 17 | action(); 18 | }; 19 | 20 | const createOnClickDeleteValue = (instance, type) => (event) => { 21 | const {deleteLocalValues, deleteSessionValues} = instance.props; 22 | 23 | const key = event.currentTarget.dataset.key; 24 | const action = type === 'local' ? deleteLocalValues : deleteSessionValues; 25 | 26 | action(key); 27 | }; 28 | 29 | const createOnClickSetValue = (instance, type) => () => { 30 | const {setLocalValues, setSessionValues} = instance.props; 31 | 32 | const keyElement = instance[`${type}KeyElement`]; 33 | const valueElement = instance[`${type}ValueElement`]; 34 | const action = type === 'local' ? setLocalValues : setSessionValues; 35 | 36 | action({ 37 | [keyElement.value]: valueElement.value, 38 | }); 39 | 40 | keyElement.value = ''; 41 | valueElement.value = ''; 42 | 43 | keyElement.focus(); 44 | }; 45 | 46 | const createSetRef = (instance, name) => (element) => { 47 | instance[name] = element; 48 | }; 49 | 50 | const DELETE_BUTTON_STYLE = { 51 | marginLeft: 5, 52 | }; 53 | 54 | const SECTION_CONTAINER_STYLE = { 55 | display: 'inline-block', 56 | verticalAlign: 'top', 57 | width: '50%', 58 | }; 59 | 60 | const SPACING_STYLE = { 61 | marginTop: 15, 62 | }; 63 | 64 | class App extends PureComponent { 65 | static propTypes = { 66 | local: PropTypes.object.isRequired, 67 | session: PropTypes.object.isRequired, 68 | }; 69 | 70 | // instance values 71 | localKeyElement = null; 72 | localValueElement = null; 73 | sessionKeyElement = null; 74 | sessionValueElement = null; 75 | 76 | // instance methods 77 | onClickClearLocalValues = createOnClickClearValues(this, 'local'); 78 | onClickClearSessionValues = createOnClickClearValues(this, 'session'); 79 | onClickDeleteValueInLocal = createOnClickDeleteValue(this, 'local'); 80 | onClickDeleteValueInSession = createOnClickDeleteValue(this, 'session'); 81 | onClickSetValueInLocal = createOnClickSetValue(this, 'local'); 82 | onClickSetValueInSession = createOnClickSetValue(this, 'session'); 83 | setLocalKeyElementRef = createSetRef(this, 'localKeyElement'); 84 | setLocalValueElementRef = createSetRef(this, 'localValueElement'); 85 | setSessionKeyElementRef = createSetRef(this, 'sessionKeyElement'); 86 | setSessionValueElementRef = createSetRef(this, 'sessionValueElement'); 87 | 88 | render() { 89 | const {local, session} = this.props; 90 | 91 | return ( 92 |
93 |

App

94 | 95 |
96 | 102 | 103 | 109 |
110 | 111 |
112 |

Set in local

113 | 114 |
115 | 119 |
120 | 121 |
122 | 126 |
127 | 128 |
129 | 135 |
136 | 137 |
138 | {Object.keys(local).map((key) => ( 139 |
140 | {key}: {JSON.stringify(local[key])} 141 | 149 |
150 | ))} 151 |
152 |
153 | 154 |
155 |

Set in session

156 | 157 |
158 | 162 |
163 | 164 |
165 | 169 |
170 | 171 |
172 | 178 |
179 | 180 |
181 | {Object.keys(session).map((key) => ( 182 |
183 | {key}: {JSON.stringify(session[key])} 184 | 192 |
193 | ))} 194 |
195 |
196 |
197 | ); 198 | } 199 | } 200 | 201 | const mapStateToProps = ({local, session}) => { 202 | console.log('local', local); 203 | console.log('session', session); 204 | 205 | return { 206 | local, 207 | session, 208 | }; 209 | }; 210 | 211 | const mapDispatchToProps = { 212 | ...localActions, 213 | ...sessionActions, 214 | }; 215 | 216 | export default connect( 217 | mapStateToProps, 218 | mapDispatchToProps 219 | )(App); 220 | -------------------------------------------------------------------------------- /DEV_ONLY/index.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import React from 'react'; 3 | import {render} from 'react-dom'; 4 | import {Provider} from 'react-redux'; 5 | 6 | // app 7 | import App from './App'; 8 | 9 | // store 10 | import store from './store'; 11 | 12 | const div = document.createElement('div'); 13 | 14 | render( 15 | 16 | 17 | , 18 | div 19 | ); 20 | 21 | document.body.appendChild(div); 22 | -------------------------------------------------------------------------------- /DEV_ONLY/store.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import { 3 | combineReducers, 4 | createStore, 5 | } from 'redux'; 6 | 7 | // reducers 8 | import { 9 | localReducer, 10 | sessionReducer, 11 | } from '../src'; 12 | 13 | const reducers = combineReducers({ 14 | local: localReducer, 15 | session: sessionReducer, 16 | }); 17 | 18 | export default createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tony Quetano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-browser-storage 2 | 3 | Use redux to manage specific data saved in either localStorage or sessionStorage 4 | 5 | ## Table of contents 6 | * [Installation](#installation) 7 | * [Usage](#usage) 8 | * [API](#api) 9 | * [localActions](#localactions) 10 | * [clearLocalValues](#clearlocalvalues) 11 | * [deleteLocalValues](#deletelocalvalues) 12 | * [setLocalValues](#setlocalvalues) 13 | * [sessionActions](#sessionactions) 14 | * [clearSessionValues](#clearsessionvalues) 15 | * [deleteSessionValues](#deletesessionvalues) 16 | * [setSessionValues](#setsessionvalues) 17 | * [localReducer](#localreducer) 18 | * [sessionReducer](#sessionreducer) 19 | * [Development](#development) 20 | 21 | ## Installation 22 | 23 | ``` 24 | $ npm i redux-browser-storage --save 25 | ``` 26 | 27 | ## Usage 28 | 29 | Include the storage type(s) you want into your standard store creation, assigning to any name you would like. 30 | 31 | ```javascript 32 | import { 33 | combineReducers, 34 | createStore 35 | } from 'redux'; 36 | import { 37 | localReducer 38 | } from 'redux-browser-storage'; 39 | 40 | const reducers = combineReducers({ 41 | ...otherReducers, 42 | local: localReducer // or whatever key you want 43 | }); 44 | 45 | export default createStore(reducers); 46 | ``` 47 | 48 | Connect your component to redux, and when you want to update the values use the provided redux actions. 49 | 50 | ```javascript 51 | import React, { 52 | PureComponent 53 | } from 'react'; 54 | import { 55 | localActions 56 | } from 'redux-browser-storage'; 57 | import { 58 | connect 59 | } from 'redux-react'; 60 | 61 | const mapStateToProps = ({local}) => { 62 | return { 63 | local 64 | }; 65 | }; 66 | 67 | const mapDispatchToProps = { 68 | ...localActions 69 | }; 70 | 71 | @connect(mapStateToProps, mapDispatchToProps) 72 | class App extends PureComponent { 73 | onClickButton = () => { 74 | this.props.setLocalValues({ 75 | count: this.props.local.count + 1 76 | }); 77 | }; 78 | 79 | render() { 80 | return ( 81 | 87 | ) 88 | } 89 | } 90 | ``` 91 | 92 | All data values in your reducer are automatically synced to their respective browser storage, and the reducer's initial state is based off of existing values in that storage, so all saved values are automatically rehydrated on page load. 93 | 94 | ## API 95 | 96 | ### localActions 97 | 98 | #### clearLocalValues 99 | 100 | `clearLocalValues()` 101 | 102 | Clears the values in `localStorage` that were stored via `redux-browser-storage`. 103 | 104 | ```javascript 105 | onClickClear = () => { 106 | this.props.clearLocalValues(); 107 | }; 108 | ``` 109 | 110 | #### deleteLocalValues 111 | 112 | `deleteLocalValues(keys: (Array|string))` 113 | 114 | Deletes the value(s) at the location of `keys` in `localStorage`. Nested values are allowed by use of dot or bracket notation. 115 | 116 | ```javascript 117 | // standard 118 | deleteLocalValues('foo'); 119 | 120 | // Nested 121 | deleteLocalValues('foo.bar[0].baz'); 122 | 123 | // multiple 124 | deleteLocalValues(['foo', 'bar[0].baz']); 125 | ``` 126 | 127 | #### setLocalValues 128 | 129 | `setLocalValues(values: Object)` 130 | 131 | Sets the value(s) in `localStorage` based on the keys of the object passed. Nested values are allowed by use of dot or bracket notation. 132 | 133 | ```javascript 134 | // standard 135 | setLocalValues({ 136 | foo: 'bar' 137 | }); 138 | 139 | // Nested 140 | setLocalValues({ 141 | 'foo.bar[0]': 'baz' 142 | }); 143 | 144 | // multiple 145 | setLocalvalues({ 146 | foo: 'bar', 147 | 'bar[0]': 'baz' 148 | }); 149 | ``` 150 | 151 | ### sessionActions 152 | 153 | #### clearSessionValues 154 | 155 | `clearSessionValues()` 156 | 157 | Clears the values in `sessionStorage` that were stored via `redux-browser-storage`. 158 | 159 | ```javascript 160 | clearSessionValues(); 161 | ``` 162 | 163 | #### deleteSessionValues 164 | 165 | `deleteSessionValues(keys: (Array|string))` 166 | 167 | Deletes the value(s) at the location of `keys` in `sessionStorage`. Nested values are allowed by use of dot or bracket notation. 168 | 169 | ```javascript 170 | // standard 171 | deleteSessionValues('foo'); 172 | 173 | // Nested 174 | deleteSessionValues('foo.bar[0].baz'); 175 | 176 | // multiple 177 | deleteSessionValues(['foo', 'bar[0].baz']); 178 | ``` 179 | 180 | #### setSessionValues 181 | 182 | `setSessionValues(values: Object)` 183 | 184 | Sets the value(s) in `sessionStorage` based on the keys of the object passed. Nested values are allowed by use of dot or bracket notation. 185 | 186 | ```javascript 187 | // standard 188 | setSessionValues({ 189 | foo: 'bar' 190 | }); 191 | 192 | // Nested 193 | setSessionValues({ 194 | 'foo.bar[0]': 'baz' 195 | }); 196 | 197 | // multiple 198 | setSessionValues({ 199 | foo: 'bar', 200 | 'bar[0]': 'baz' 201 | }); 202 | ``` 203 | 204 | ### localReducer 205 | 206 | Handles storage of items in `localStorage`. This can be assigned to any key you'd like when using `combineReducers`, it is not prescriptive. 207 | 208 | ```javascript 209 | combineReducers({ 210 | permanentCache: localReducer 211 | }); 212 | ``` 213 | 214 | ### sessionReducer 215 | 216 | Handles storage of items in `sessionStorage`. This can be assigned to any key you'd like when using `combineReducers`, it is not prescriptive. 217 | 218 | ```javascript 219 | combineReducers({ 220 | temporaryCache: sessionReducer 221 | }); 222 | ``` 223 | 224 | ## Development 225 | 226 | Standard stuff, clone the repo and `npm i` to get the dependencies. npm scripts available: 227 | * `build` => builds the distributed JS with `NODE_ENV=development` and with sourcemaps 228 | * `build:minified` => builds the distributed JS with `NODE_ENV=production` and minified 229 | * `clean` => removes `lib` and `dist` folders 230 | * `dev` => runs the webpack dev server for the playground 231 | * `lint` => runs ESLint against files in the `src` folder 232 | * `lint:fix` => runs ESLint against files in the `src` folder and fixes any fixable issues discovered 233 | * `prepublish` => if in publish, runs `prepublish:compile` 234 | * `prepublish:compile` => runs the `lint`, `test:coverage`, `clean`, `transpile`, `build`, and `build:minified` scripts 235 | * `test` => run `ava` with NODE_ENV=test 236 | * `test:coverage` => run `ava` with `nyc` to calculate code coverage 237 | * `test:watch` => runs `test` but with persistent watcher 238 | * `transpile` => runs Babel against files in `src` to files in `lib` 239 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "tony.quetano@planttheidea.com", 3 | "ava": { 4 | "failFast": true, 5 | "files": [ 6 | "test/**/*.js" 7 | ], 8 | "require": [ 9 | "@babel/register", 10 | "test/helpers/setup-browser-env.js" 11 | ], 12 | "sources": [ 13 | "src/**/*.js" 14 | ], 15 | "verbose": true 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/planttheidea/redux-browser-storage/issues" 19 | }, 20 | "dependencies": { 21 | "namespace-constants": "^0.5.0", 22 | "redux-actions": "^2.0.3", 23 | "unchanged": "^1.5.2" 24 | }, 25 | "description": "Use redux to manage localStorage and sessionStorage data", 26 | "devDependencies": { 27 | "@babel/cli": "^7.0.0", 28 | "@babel/core": "^7.0.0", 29 | "@babel/plugin-proposal-class-properties": "^7.0.0", 30 | "@babel/plugin-proposal-decorators": "^7.0.0", 31 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0", 32 | "@babel/plugin-proposal-function-sent": "^7.0.0", 33 | "@babel/plugin-proposal-json-strings": "^7.0.0", 34 | "@babel/plugin-proposal-numeric-separator": "^7.0.0", 35 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 36 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 37 | "@babel/plugin-syntax-import-meta": "^7.0.0", 38 | "@babel/preset-env": "^7.0.0", 39 | "@babel/preset-react": "^7.0.0", 40 | "@babel/register": "^7.0.0", 41 | "ava": "^1.0.1", 42 | "babel-eslint": "^10.0.1", 43 | "babel-loader": "^8.0.0", 44 | "browser-env": "^3.2.5", 45 | "eslint": "^5.12.0", 46 | "eslint-config-rapid7": "^3.1.0", 47 | "eslint-friendly-formatter": "^4.0.1", 48 | "eslint-loader": "^2.1.1", 49 | "html-webpack-plugin": "^3.2.0", 50 | "in-publish": "^2.0.0", 51 | "ink-docstrap": "^1.3.0", 52 | "jsdoc": "^3.4.3", 53 | "jsdoc-babel": "^0.5.0", 54 | "lodash": "^4.17.11", 55 | "nyc": "^13.1.0", 56 | "optimize-js-plugin": "^0.0.4", 57 | "prop-types": "^15.5.10", 58 | "react": "^16.7.0", 59 | "react-dom": "^16.7.0", 60 | "react-redux": "^6.0.0", 61 | "redux": "^3.6.0", 62 | "rimraf": "^2.6.1", 63 | "sinon": "^7.2.2", 64 | "webpack": "^4.28.3", 65 | "webpack-cli": "^3.2.0", 66 | "webpack-dev-server": "^3.1.14" 67 | }, 68 | "homepage": "https://github.com/planttheidea/redux-browser-storage#readme", 69 | "keywords": [ 70 | "redux", 71 | "localStorage", 72 | "sessionStorage" 73 | ], 74 | "license": "MIT", 75 | "main": "lib/index.js", 76 | "module": "es/index.js", 77 | "name": "redux-browser-storage", 78 | "peerDependencies": { 79 | "redux": "^3.6.0" 80 | }, 81 | "repository": { 82 | "type": "git", 83 | "url": "git+https://github.com/planttheidea/redux-browser-storage.git" 84 | }, 85 | "scripts": { 86 | "build": "NODE_ENV=development webpack --progress --colors --config=webpack/webpack.config.js", 87 | "build:minified": "NODE_ENV=production webpack --progress --colors --config=webpack/webpack.config.minified.js", 88 | "clean": "rimraf lib && rimraf es && rimraf dist", 89 | "dev": "NODE_ENV=development webpack-dev-server --progress --config=webpack/webpack.config.dev.js", 90 | "lint": "NODE_ENV=test eslint src", 91 | "lint:fix": "NODE_ENV=test eslint src --fix", 92 | "prepublish": "in-publish && npm run prepublish:compile || echo", 93 | "prepublish:compile": "npm run lint && npm run test:coverage && npm run clean && npm run transpile:lib && npm run transpile:es && npm run build && npm run build:minified", 94 | "start": "npm run dev", 95 | "test": "NODE_PATH=. NODE_ENV=test ava", 96 | "test:coverage": "nyc npm test", 97 | "test:watch": "NODE_PATH=. NODE_ENV=test ava --watch", 98 | "transpile:es": "BABEL_ENV=es babel src --out-dir es", 99 | "transpile:lib": "BABEL_ENV=lib babel src --out-dir lib" 100 | }, 101 | "version": "1.1.0" 102 | } 103 | -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | // actions 2 | import { 3 | clearLocalValues, 4 | deleteLocalValues, 5 | setLocalValues, 6 | } from './localActions'; 7 | import { 8 | clearSessionValues, 9 | deleteSessionValues, 10 | setSessionValues, 11 | } from './sessionActions'; 12 | 13 | export {clearLocalValues}; 14 | export {deleteLocalValues}; 15 | export {setLocalValues}; 16 | 17 | const localActions = { 18 | clearLocalValues, 19 | deleteLocalValues, 20 | setLocalValues, 21 | }; 22 | 23 | export {localActions}; 24 | 25 | export {clearSessionValues}; 26 | export {deleteSessionValues}; 27 | export {setSessionValues}; 28 | 29 | const sessionActions = { 30 | clearSessionValues, 31 | deleteSessionValues, 32 | setSessionValues, 33 | }; 34 | 35 | export {sessionActions}; 36 | 37 | export default { 38 | localActions, 39 | sessionActions, 40 | }; 41 | -------------------------------------------------------------------------------- /src/actions/localActions.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import createConstants from 'namespace-constants'; 3 | import createAction from 'redux-actions/lib/createAction'; 4 | 5 | // constants 6 | import {LOCAL_STORAGE_KEY} from '../constants'; 7 | 8 | export const ACTION_TYPES = createConstants(LOCAL_STORAGE_KEY, [ 9 | 'CLEAR_LOCAL_VALUES', 10 | 'DELETE_LOCAL_VALUES', 11 | 'SET_LOCAL_VALUES', 12 | ]); 13 | 14 | export const clearLocalValues = createAction(ACTION_TYPES.CLEAR_LOCAL_VALUES); 15 | export const deleteLocalValues = createAction(ACTION_TYPES.DELETE_LOCAL_VALUES); 16 | export const setLocalValues = createAction(ACTION_TYPES.SET_LOCAL_VALUES); 17 | -------------------------------------------------------------------------------- /src/actions/sessionActions.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import createConstants from 'namespace-constants'; 3 | import createAction from 'redux-actions/lib/createAction'; 4 | 5 | // constants 6 | import {SESSION_STORAGE_KEY} from '../constants'; 7 | 8 | export const ACTION_TYPES = createConstants(SESSION_STORAGE_KEY, [ 9 | 'CLEAR_SESSION_VALUES', 10 | 'DELETE_SESSION_VALUES', 11 | 'SET_SESSION_VALUES', 12 | ]); 13 | 14 | export const clearSessionValues = createAction(ACTION_TYPES.CLEAR_SESSION_VALUES); 15 | export const deleteSessionValues = createAction(ACTION_TYPES.DELETE_SESSION_VALUES); 16 | export const setSessionValues = createAction(ACTION_TYPES.SET_SESSION_VALUES); 17 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @constant {string} LOCAL_STORAGE_KEY 3 | * @default 4 | */ 5 | export const LOCAL_STORAGE_KEY = 'REDUX_BROWSER_STORAGE:LOCAL'; 6 | 7 | /** 8 | * @constant {string} SESSION_STORAGE_KEY 9 | * @default 10 | */ 11 | export const SESSION_STORAGE_KEY = 'REDUX_BROWSER_STORAGE:SESSION'; 12 | 13 | /** 14 | * @constant {string} LOCAL_STORAGE_TYPE 15 | * @default 16 | */ 17 | export const LOCAL_STORAGE_TYPE = 'localStorage'; 18 | 19 | /** 20 | * @constant {string} SESSION_STORAGE_TYPE 21 | * @default 22 | */ 23 | export const SESSION_STORAGE_TYPE = 'sessionStorage'; 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // actions 2 | import { 3 | clearLocalValues, 4 | clearSessionValues, 5 | deleteLocalValues, 6 | deleteSessionValues, 7 | localActions, 8 | sessionActions, 9 | setLocalValues, 10 | setSessionValues, 11 | } from './actions'; 12 | 13 | // reducers 14 | import { 15 | localReducer, 16 | sessionReducer, 17 | } from './reducers'; 18 | 19 | export {clearLocalValues}; 20 | export {clearSessionValues}; 21 | export {deleteLocalValues}; 22 | export {deleteSessionValues}; 23 | export {setLocalValues}; 24 | export {setSessionValues}; 25 | 26 | export {localActions}; 27 | export {sessionActions}; 28 | 29 | export {localReducer}; 30 | export {sessionReducer}; 31 | 32 | export default { 33 | clearLocalValues, 34 | clearSessionValues, 35 | deleteLocalValues, 36 | deleteSessionValues, 37 | localActions, 38 | localReducer, 39 | sessionActions, 40 | sessionReducer, 41 | setLocalValues, 42 | setSessionValues, 43 | }; 44 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | // reducers 2 | import localReducer from './localReducer'; 3 | import sessionReducer from './sessionReducer'; 4 | 5 | export {localReducer}; 6 | export {sessionReducer}; 7 | 8 | export default { 9 | localReducer, 10 | sessionReducer, 11 | }; 12 | -------------------------------------------------------------------------------- /src/reducers/localReducer.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import handleActions from 'redux-actions/lib/handleActions'; 3 | 4 | // actions 5 | import {ACTION_TYPES} from '../actions/localActions'; 6 | 7 | // constants 8 | import { 9 | LOCAL_STORAGE_KEY, 10 | LOCAL_STORAGE_TYPE, 11 | } from '../constants'; 12 | 13 | // utils 14 | import { 15 | createHandleClearValues, 16 | createHandleDeleteValues, 17 | createHandleSetValues, 18 | getLocalStorage, 19 | } from '../utils'; 20 | 21 | export const INITIAL_STATE = getLocalStorage(); 22 | 23 | export default handleActions( 24 | { 25 | [ACTION_TYPES.CLEAR_LOCAL_VALUES]: createHandleClearValues(LOCAL_STORAGE_KEY, LOCAL_STORAGE_TYPE), 26 | [ACTION_TYPES.DELETE_LOCAL_VALUES]: createHandleDeleteValues(LOCAL_STORAGE_KEY, LOCAL_STORAGE_TYPE), 27 | [ACTION_TYPES.SET_LOCAL_VALUES]: createHandleSetValues(LOCAL_STORAGE_KEY, LOCAL_STORAGE_TYPE), 28 | }, 29 | INITIAL_STATE 30 | ); 31 | -------------------------------------------------------------------------------- /src/reducers/sessionReducer.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import handleActions from 'redux-actions/lib/handleActions'; 3 | 4 | // actions 5 | import {ACTION_TYPES} from '../actions/sessionActions'; 6 | 7 | // constants 8 | import { 9 | SESSION_STORAGE_KEY, 10 | SESSION_STORAGE_TYPE, 11 | } from '../constants'; 12 | 13 | // utils 14 | import { 15 | createHandleClearValues, 16 | createHandleDeleteValues, 17 | createHandleSetValues, 18 | getSessionStorage, 19 | } from '../utils'; 20 | 21 | export const INITIAL_STATE = getSessionStorage(); 22 | 23 | export default handleActions( 24 | { 25 | [ACTION_TYPES.CLEAR_SESSION_VALUES]: createHandleClearValues(SESSION_STORAGE_KEY, SESSION_STORAGE_TYPE), 26 | [ACTION_TYPES.DELETE_SESSION_VALUES]: createHandleDeleteValues(SESSION_STORAGE_KEY, SESSION_STORAGE_TYPE), 27 | [ACTION_TYPES.SET_SESSION_VALUES]: createHandleSetValues(SESSION_STORAGE_KEY, SESSION_STORAGE_TYPE), 28 | }, 29 | INITIAL_STATE 30 | ); 31 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import { 3 | remove, 4 | set, 5 | } from 'unchanged'; 6 | 7 | // constants 8 | import { 9 | LOCAL_STORAGE_KEY, 10 | LOCAL_STORAGE_TYPE, 11 | SESSION_STORAGE_KEY, 12 | SESSION_STORAGE_TYPE, 13 | } from './constants'; 14 | 15 | /** 16 | * @function createGetStorage 17 | * 18 | * @description 19 | * create a method that will retrieve the storage type requested 20 | * 21 | * @param {string} storageType the type of storage to retrieve 22 | * @param {string} storageKey the key used in local storage 23 | * @returns {function(): Object} the method that will retrieve the value in storage 24 | */ 25 | export const createGetStorage = (storageType, storageKey) => () => { 26 | try { 27 | const storageJson = window[storageType].getItem(storageKey); 28 | 29 | return storageJson ? JSON.parse(storageJson) : {}; 30 | } catch (error) { 31 | return {}; 32 | } 33 | }; 34 | 35 | /** 36 | * @function setStateInStorage 37 | * 38 | * @description 39 | * set the newState in the storage type requested 40 | * 41 | * @param {string} storageType the type of storage to retrieve 42 | * @param {string} storageKey the key used in local storage 43 | * @param {Object} newState the new state value to storewindow 44 | */ 45 | export const setStateInStorage = (storageType, storageKey, newState) => 46 | window[storageType].setItem(storageKey, JSON.stringify(newState)); 47 | 48 | /** 49 | * @function createHandleClearValues 50 | * 51 | * @description 52 | * create the handler for clearing of values in storage 53 | * 54 | * @param {string} storageKey the key used in local storage 55 | * @param {string} storageType the type of storage to retrieve 56 | * @returns {function(): Object} the clear values handler 57 | */ 58 | export const createHandleClearValues = (storageKey, storageType) => () => { 59 | const newState = {}; 60 | 61 | setStateInStorage(storageType, storageKey, newState); 62 | 63 | return newState; 64 | }; 65 | 66 | /** 67 | * @function createHandleDeleteValues 68 | * 69 | * @description 70 | * create the handler for deleting specific keys in state 71 | * 72 | * @param {string} storageKey the key used in local storage 73 | * @param {string} storageType the type of storage to retrieve 74 | * @returns {function(): Object} the delete values handler 75 | */ 76 | export const createHandleDeleteValues = (storageKey, storageType) => { 77 | const handleDeleteValues = (state, {payload: key}) => { 78 | if (Array.isArray(key)) { 79 | return key.reduce((newState, keyToRemove) => handleDeleteValues(newState, {payload: keyToRemove}), state); 80 | } 81 | 82 | const newState = remove(key, state); 83 | 84 | setStateInStorage(storageType, storageKey, newState); 85 | 86 | return newState; 87 | }; 88 | 89 | return handleDeleteValues; 90 | }; 91 | 92 | /** 93 | * @function createHandleSetValues 94 | * 95 | * @description 96 | * create the handler for setting specific keys in state 97 | * 98 | * @param {string} storageKey the key used in local storage 99 | * @param {string} storageType the type of storage to retrieve 100 | * @returns {function(): Object} the set values handler 101 | */ 102 | export const createHandleSetValues = (storageKey, storageType) => (state, {payload}) => { 103 | const newState = Object.keys(payload).reduce((newState, path) => set(path, payload[path], newState), state); 104 | 105 | setStateInStorage(storageType, storageKey, newState); 106 | 107 | return newState; 108 | }; 109 | 110 | export const getLocalStorage = createGetStorage(LOCAL_STORAGE_TYPE, LOCAL_STORAGE_KEY); 111 | export const getSessionStorage = createGetStorage(SESSION_STORAGE_TYPE, SESSION_STORAGE_KEY); 112 | -------------------------------------------------------------------------------- /test/actions/index.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | import _ from 'lodash'; 4 | 5 | // src 6 | import * as actions from 'src/actions/index'; 7 | import * as localActionsExports from 'src/actions/localActions'; 8 | import * as sessionActionsExports from 'src/actions/sessionActions'; 9 | 10 | test('if every localActions import that is an action has a corresponding export', (t) => { 11 | const {__esModule: moduleDeclarationIgnored, ACTION_TYPES: actionTypesIgnored, ...localActions} = localActionsExports; 12 | 13 | Object.keys(localActions).forEach((key) => { 14 | t.true(_.isFunction(actions[key])); 15 | }); 16 | }); 17 | 18 | test('if every sessionActions import that is an action has a corresponding export', (t) => { 19 | const { 20 | __esModule: moduleDeclarationIgnored, 21 | ACTION_TYPES: actionTypesIgnored, 22 | ...sessionActions 23 | } = sessionActionsExports; 24 | 25 | Object.keys(sessionActions).forEach((key) => { 26 | t.true(_.isFunction(actions[key])); 27 | }); 28 | }); 29 | 30 | test('if the default export has all the actions namespaced', (t) => { 31 | const { 32 | __esModule: localModuleDeclarationIgnored, 33 | ACTION_TYPES: localActionTypesIgnored, 34 | ...localActions 35 | } = localActionsExports; 36 | const { 37 | __esModule: sessionModuleDeclarationIgnored, 38 | ACTION_TYPES: sessionActionTypesIgnored, 39 | ...sessionActions 40 | } = sessionActionsExports; 41 | 42 | t.deepEqual(actions.default, { 43 | localActions, 44 | sessionActions, 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/actions/localActions.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | import _ from 'lodash'; 4 | 5 | // src 6 | import * as actions from 'src/actions/localActions'; 7 | 8 | test('if every constant has a corresponding camelCase action', (t) => { 9 | Object.keys(actions.ACTION_TYPES).forEach((key) => { 10 | t.true(_.isFunction(actions[_.camelCase(key)])); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/actions/sessionActions.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | import _ from 'lodash'; 4 | 5 | // src 6 | import * as actions from 'src/actions/sessionActions'; 7 | 8 | test('if every constant has a corresponding camelCase action', (t) => { 9 | Object.keys(actions.ACTION_TYPES).forEach((key) => { 10 | t.true(_.isFunction(actions[_.camelCase(key)])); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/helpers/setup-browser-env.js: -------------------------------------------------------------------------------- 1 | // external dependencies 2 | import browserEnv from 'browser-env'; 3 | 4 | browserEnv(); 5 | 6 | const createMockStorage = () => { 7 | let values = {}; 8 | 9 | return { 10 | clear() { 11 | values = {}; 12 | }, 13 | getItem(key) { 14 | return values[key]; 15 | }, 16 | setItem(key, value) { 17 | values[key] = value; 18 | }, 19 | }; 20 | }; 21 | 22 | window.localStorage = createMockStorage(); 23 | window.sessionStorage = createMockStorage(); 24 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | import _ from 'lodash'; 4 | 5 | // src 6 | import * as index from 'src/index'; 7 | import * as actionsExports from 'src/actions'; 8 | import * as reducersExports from 'src/reducers'; 9 | 10 | test('if each actions import has a reflective export', (t) => { 11 | const { 12 | __esModule: moduleDeclarationIgnored, 13 | default: defaultIgnored, 14 | localActions, 15 | localReducer: localReducerIgnored, 16 | sessionActions, 17 | sessionReducer: sessionReducerIgnored, 18 | ...actions 19 | } = actionsExports; 20 | 21 | Object.keys(actions).forEach((key) => { 22 | t.true(_.isFunction(index[key])); 23 | }); 24 | 25 | Object.keys(localActions).forEach((key) => { 26 | t.true(_.isFunction(localActions[key])); 27 | }); 28 | 29 | Object.keys(sessionActions).forEach((key) => { 30 | t.true(_.isFunction(sessionActions[key])); 31 | }); 32 | }); 33 | 34 | test('if each reducers import has a reflective export', (t) => { 35 | const {localReducer, sessionReducer} = reducersExports; 36 | 37 | t.is(index.localReducer, localReducer); 38 | t.is(index.sessionReducer, sessionReducer); 39 | }); 40 | -------------------------------------------------------------------------------- /test/reducers/index.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | 4 | // src 5 | import * as reducers from 'src/reducers/index'; 6 | import localReducer from 'src/reducers/localReducer'; 7 | import sessionReducer from 'src/reducers/sessionReducer'; 8 | 9 | test('if all reducers have a corresponding export', (t) => { 10 | t.is(reducers.localReducer, localReducer); 11 | t.is(reducers.sessionReducer, sessionReducer); 12 | }); 13 | 14 | test('if the default export has all the reducers', (t) => { 15 | t.deepEqual(reducers.default, { 16 | localReducer, 17 | sessionReducer, 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/reducers/localReducer.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | 4 | // src 5 | import reducer, {INITIAL_STATE} from 'src/reducers/localReducer'; 6 | 7 | test('if INITIAL_STATE is returned when no matching actions are found', (t) => { 8 | const result = reducer(undefined, {}); 9 | 10 | t.is(result, INITIAL_STATE); 11 | }); 12 | -------------------------------------------------------------------------------- /test/reducers/sessionReducer.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | 4 | // src 5 | import reducer, {INITIAL_STATE} from 'src/reducers/sessionReducer'; 6 | 7 | test('if INITIAL_STATE is returned when no matching actions are found', (t) => { 8 | const result = reducer(undefined, {}); 9 | 10 | t.is(result, INITIAL_STATE); 11 | }); 12 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | // test 2 | import test from 'ava'; 3 | import _ from 'lodash'; 4 | import sinon from 'sinon'; 5 | 6 | // src 7 | import * as utils from 'src/utils'; 8 | import * as constants from 'src/constants'; 9 | 10 | test('if createGetStorage will return the value in state if the key exists', (t) => { 11 | const object = { 12 | foo: 'bar', 13 | }; 14 | const storageKey = constants.LOCAL_STORAGE_KEY; 15 | const storageType = constants.LOCAL_STORAGE_TYPE; 16 | 17 | const getStorage = utils.createGetStorage(storageType, storageKey); 18 | 19 | t.true(_.isFunction(getStorage)); 20 | 21 | const stub = sinon.stub(window[storageType], 'getItem').returns(JSON.stringify(object)); 22 | 23 | const result = getStorage(); 24 | 25 | t.true(stub.calledOnce); 26 | t.true(stub.calledWith(storageKey)); 27 | 28 | t.deepEqual(result, object); 29 | 30 | stub.restore(); 31 | }); 32 | 33 | test('if createGetStorage will return an empty object if the key does not exist', (t) => { 34 | const storageKey = constants.LOCAL_STORAGE_KEY; 35 | const storageType = constants.LOCAL_STORAGE_TYPE; 36 | 37 | const getStorage = utils.createGetStorage(storageType, storageKey); 38 | 39 | t.true(_.isFunction(getStorage)); 40 | 41 | const stub = sinon.stub(window[storageType], 'getItem').returns(null); 42 | 43 | const result = getStorage(); 44 | 45 | t.true(stub.calledOnce); 46 | t.true(stub.calledWith(storageKey)); 47 | 48 | t.deepEqual(result, {}); 49 | 50 | stub.restore(); 51 | }); 52 | 53 | test('if createGetStorage will return an empty object if parsing the result throws an error', (t) => { 54 | const storageKey = constants.LOCAL_STORAGE_KEY; 55 | const storageType = constants.LOCAL_STORAGE_TYPE; 56 | 57 | const getStorage = utils.createGetStorage(storageType, storageKey); 58 | 59 | t.true(_.isFunction(getStorage)); 60 | 61 | const stub = sinon.stub(window[storageType], 'getItem').returns({}); 62 | 63 | const result = getStorage(); 64 | 65 | t.true(stub.calledOnce); 66 | t.true(stub.calledWith(storageKey)); 67 | 68 | t.deepEqual(result, {}); 69 | 70 | stub.restore(); 71 | }); 72 | 73 | test('if setStateInStorage will set the state in the storage specified', (t) => { 74 | const object = { 75 | foo: 'bar', 76 | }; 77 | const storageKey = constants.LOCAL_STORAGE_KEY; 78 | const storageType = constants.LOCAL_STORAGE_TYPE; 79 | 80 | const stub = sinon.stub(window[storageType], 'setItem'); 81 | 82 | utils.setStateInStorage(storageType, storageKey, object); 83 | 84 | t.true(stub.calledOnce); 85 | 86 | const args = stub.firstCall.args; 87 | 88 | t.is(args.length, 2); 89 | 90 | const [key, value] = args; 91 | 92 | t.is(key, storageKey); 93 | t.is(value, JSON.stringify(object)); 94 | 95 | stub.restore(); 96 | }); 97 | 98 | test('if createHandleClearValues will set the initial state passed both in the reducer and in storage', (t) => { 99 | const storageKey = constants.LOCAL_STORAGE_KEY; 100 | const storageType = constants.LOCAL_STORAGE_TYPE; 101 | 102 | const handleClearValues = utils.createHandleClearValues(storageKey, storageType); 103 | 104 | t.true(_.isFunction(handleClearValues)); 105 | 106 | const stub = sinon.stub(window[storageType], 'setItem'); 107 | 108 | const result = handleClearValues(); 109 | 110 | t.true(stub.called); 111 | 112 | const args = stub.firstCall.args; 113 | 114 | t.is(args.length, 2); 115 | 116 | const [key, value] = args; 117 | 118 | t.is(key, storageKey); 119 | t.is(value, JSON.stringify({})); 120 | 121 | t.deepEqual(result, {}); 122 | 123 | stub.restore(); 124 | }); 125 | 126 | test('if createHandleDeleteValues creates a reducer that will remove the key from the state passed and return it', (t) => { 127 | const storageKey = constants.LOCAL_STORAGE_KEY; 128 | const storageType = constants.LOCAL_STORAGE_TYPE; 129 | 130 | const handleDeleteValues = utils.createHandleDeleteValues(storageKey, storageType); 131 | 132 | t.true(_.isFunction(handleDeleteValues)); 133 | 134 | const currentState = { 135 | foo: { 136 | bar: 'baz', 137 | }, 138 | }; 139 | const action = { 140 | payload: 'foo.bar', 141 | }; 142 | 143 | const stub = sinon.stub(window[storageType], 'setItem'); 144 | 145 | const result = handleDeleteValues(currentState, action); 146 | const expectedResult = { 147 | foo: {}, 148 | }; 149 | 150 | t.true(stub.calledOnce); 151 | 152 | const args = stub.firstCall.args; 153 | 154 | t.is(args.length, 2); 155 | t.deepEqual(args, [storageKey, JSON.stringify(expectedResult)]); 156 | 157 | stub.restore(); 158 | 159 | t.deepEqual(result, expectedResult); 160 | }); 161 | 162 | test('if createHandleDeleteValues creates a reducer that will remove the keys from the state passed and return it', (t) => { 163 | const storageKey = constants.LOCAL_STORAGE_KEY; 164 | const storageType = constants.LOCAL_STORAGE_TYPE; 165 | 166 | const handleDeleteValues = utils.createHandleDeleteValues(storageKey, storageType); 167 | 168 | t.true(_.isFunction(handleDeleteValues)); 169 | 170 | const currentState = { 171 | bar: 'bar', 172 | foo: 'foo', 173 | }; 174 | const action = { 175 | payload: ['foo', 'bar'], 176 | }; 177 | 178 | const stub = sinon.stub(window[storageType], 'setItem'); 179 | 180 | const result = handleDeleteValues(currentState, action); 181 | 182 | t.true(stub.calledTwice); 183 | 184 | const firstArgs = stub.firstCall.args; 185 | 186 | t.is(firstArgs.length, 2); 187 | t.deepEqual(firstArgs, [ 188 | storageKey, 189 | JSON.stringify({ 190 | bar: 'bar', 191 | }), 192 | ]); 193 | 194 | const secondArgs = stub.secondCall.args; 195 | 196 | t.is(secondArgs.length, 2); 197 | t.deepEqual(secondArgs, [storageKey, JSON.stringify({})]); 198 | 199 | stub.restore(); 200 | 201 | t.deepEqual(result, {}); 202 | }); 203 | 204 | test('if createHandleSetValues creates a reducer that will add the items in state passed and return it', (t) => { 205 | const storageKey = constants.LOCAL_STORAGE_KEY; 206 | const storageType = constants.LOCAL_STORAGE_TYPE; 207 | 208 | const handlSetValues = utils.createHandleSetValues(storageKey, storageType); 209 | 210 | t.true(_.isFunction(handlSetValues)); 211 | 212 | const currentState = { 213 | foo: 'foo', 214 | }; 215 | const action = { 216 | payload: { 217 | bar: 'bar', 218 | }, 219 | }; 220 | 221 | const stub = sinon.stub(window[storageType], 'setItem'); 222 | 223 | const result = handlSetValues(currentState, action); 224 | const expectedResult = { 225 | ...currentState, 226 | ...action.payload, 227 | }; 228 | 229 | t.true(stub.calledOnce); 230 | 231 | const args = stub.firstCall.args; 232 | 233 | t.is(args.length, 2); 234 | t.deepEqual(args, [storageKey, JSON.stringify(expectedResult)]); 235 | 236 | stub.restore(); 237 | 238 | t.deepEqual(result, expectedResult); 239 | }); 240 | -------------------------------------------------------------------------------- /webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | 6 | const defaultConfig = require('./webpack.config'); 7 | 8 | const PORT = 3000; 9 | const ROOT = path.join(__dirname, '..'); 10 | 11 | module.exports = Object.assign({}, defaultConfig, { 12 | cache: true, 13 | 14 | devServer: { 15 | contentBase: './dist', 16 | host: 'localhost', 17 | inline: true, 18 | lazy: false, 19 | noInfo: false, 20 | port: PORT, 21 | quiet: false, 22 | stats: { 23 | colors: true, 24 | progress: true, 25 | }, 26 | watchOptions: { 27 | ignored: /node_modules/, 28 | }, 29 | }, 30 | 31 | entry: [path.join(ROOT, 'DEV_ONLY', 'index.js')], 32 | 33 | module: Object.assign({}, defaultConfig.module, { 34 | rules: defaultConfig.module.rules.map((rule) => { 35 | if (rule.loader !== 'babel-loader') { 36 | return rule; 37 | } 38 | 39 | return Object.assign({}, rule, { 40 | include: rule.include.concat([path.join(ROOT, 'DEV_ONLY')]), 41 | options: { 42 | plugins: ['@babel/plugin-proposal-class-properties'], 43 | presets: ['@babel/react'], 44 | }, 45 | }); 46 | }), 47 | }), 48 | 49 | output: Object.assign({}, defaultConfig.output, { 50 | publicPath: `http://localhost:${PORT}/`, 51 | }), 52 | 53 | plugins: defaultConfig.plugins.concat([new HtmlWebpackPlugin()]), 54 | }); 55 | -------------------------------------------------------------------------------- /webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const eslintFriendlyFormatter = require('eslint-friendly-formatter'); 4 | const path = require('path'); 5 | const webpack = require('webpack'); 6 | 7 | const ROOT = path.join(__dirname, '..'); 8 | 9 | module.exports = { 10 | devtool: '#source-map', 11 | 12 | entry: [path.resolve(ROOT, 'src', 'index.js')], 13 | 14 | mode: 'development', 15 | 16 | module: { 17 | rules: [ 18 | { 19 | enforce: 'pre', 20 | include: [path.resolve(ROOT, 'src')], 21 | loader: 'eslint-loader', 22 | options: { 23 | configFile: '.eslintrc', 24 | failOnError: true, 25 | failOnWarning: false, 26 | formatter: require('eslint-friendly-formatter'), 27 | }, 28 | test: /\.js$/, 29 | }, 30 | { 31 | include: [path.resolve(ROOT, 'src'), path.resolve(ROOT, 'DEV_ONLY')], 32 | loader: 'babel-loader', 33 | test: /\.js$/, 34 | }, 35 | ], 36 | }, 37 | 38 | output: { 39 | filename: 'redux-browser-storage.js', 40 | library: 'ReduxBrowserStorage', 41 | libraryTarget: 'umd', 42 | path: path.join(ROOT, 'dist'), 43 | umdNamedDefine: true, 44 | }, 45 | 46 | plugins: [new webpack.EnvironmentPlugin(['NODE_ENV'])], 47 | }; 48 | -------------------------------------------------------------------------------- /webpack/webpack.config.minified.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const webpack = require('webpack'); 4 | const OptimizeJsPlugin = require('optimize-js-plugin'); 5 | 6 | const defaultConfig = require('./webpack.config'); 7 | 8 | module.exports = Object.assign({}, defaultConfig, { 9 | devtool: undefined, 10 | 11 | mode: 'production', 12 | 13 | output: Object.assign({}, defaultConfig.output, { 14 | filename: 'redux-browser-storage.min.js', 15 | }), 16 | 17 | plugins: defaultConfig.plugins.concat([ 18 | new webpack.LoaderOptionsPlugin({ 19 | debug: false, 20 | minimize: true, 21 | }), 22 | new OptimizeJsPlugin({ 23 | sourceMap: false, 24 | }), 25 | ]), 26 | }); 27 | --------------------------------------------------------------------------------