├── .bithoundrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .publishrc ├── .travis.yml ├── .yo-rc.json ├── LICENSE ├── README.md ├── _config.yml ├── example ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .prettierrc ├── .yo-rc.json ├── README.md ├── docs │ └── create-redux-app │ │ └── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── actions │ │ └── counter.js │ ├── assets │ │ └── logo.svg │ ├── components │ │ ├── Counter.js │ │ └── Header.js │ ├── constants │ │ └── ActionTypes.js │ ├── containers │ │ └── CounterContainer.js │ ├── index.js │ ├── reducers │ │ ├── counter.js │ │ └── index.js │ ├── routes │ │ └── index.js │ ├── store │ │ ├── index.js │ │ ├── storeDev.js │ │ └── storeProd.js │ ├── styles │ │ └── globalStyles.css │ ├── tests │ │ └── CounterContainer.test.js │ └── utils │ │ └── serviceWorker.js └── yarn.lock ├── generators └── app │ ├── index.js │ └── templates │ ├── README.md │ ├── docs │ └── create-redux-app │ │ └── README.md │ ├── editorconfig │ ├── eslintignore │ ├── eslintrc.json │ ├── gitattributes │ ├── gitignore │ ├── package.json │ ├── prettierrc │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── actions │ └── counter.js │ ├── assets │ └── logo.svg │ ├── components │ ├── Counter.js │ └── Header.js │ ├── constants │ └── actionTypes.js │ ├── containers │ └── CounterContainer.js │ ├── index.js │ ├── reducers │ ├── counter.js │ └── index.js │ ├── routes │ └── index.js │ ├── store │ ├── index.js │ ├── storeDev.js │ └── storeProd.js │ ├── styles │ └── globalStyles.css │ ├── tests │ └── CounterContainer.test.js │ └── utils │ └── serviceWorker.js ├── package-lock.json ├── package.json ├── test └── app.spec.js └── yarn.lock /.bithoundrc: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "example" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.publishrc: -------------------------------------------------------------------------------- 1 | { 2 | "validations": { 3 | "vulnerableDependencies": true, 4 | "uncommittedChanges": true, 5 | "untrackedFiles": false, 6 | "sensitiveData": false, 7 | "branch": false, 8 | "gitTag": false 9 | }, 10 | "confirm": false, 11 | "publishTag": "y", 12 | "prePublishScript": "npm test", 13 | "postPublishScript": "" 14 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.10.1" 4 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-node": { 3 | "promptValues": { 4 | "authorName": "jonidelv", 5 | "authorEmail": "hi@jonidelv.me" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 jonidelv 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generator create-redux-app 2 | 3 | [![NPM](https://nodei.co/npm/generator-create-redux-app.png?downloads=true)](https://nodei.co/npm/generator-create-redux-app/) 4 | 5 | > This generator add **redux**, **emotion-js** and other useful libraries and tools like **react-router**, in top of the most common React starter [Create React App](https://github.com/facebookincubator/create-react-app). 6 | Below you will find some information on how to perform common tasks. 7 | 8 | 9 | ## Installation 10 | 11 | First, install [Yeoman](http://yeoman.io) and generator-create-redux-app using [npm](https://www.npmjs.com/) ( **You’ll need to have Node >= 6.10.3 on your machine** [node.js](https://nodejs.org/)). 12 | 13 | ```bash 14 | npm install -g yo 15 | npm install -g generator-create-redux-app 16 | ``` 17 | 18 | Then generate your new project: 19 | 20 | ```bash 21 | mkdir project-name 22 | cd project-name 23 | yo create-redux-app 24 | ``` 25 | 26 | Once the installation is done, you can run some commands inside the project folder: 27 | 28 | ### `npm start` or `yarn start` 29 | 30 | Runs the app in development mode.
31 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 32 | 33 | The page will reload if you make edits.
34 | You will see the build errors and lint warnings in the console. 35 | 36 | ### `npm test` or `yarn test` 37 | 38 | Runs the test watcher in an interactive mode.
39 | By default, runs tests related to files changes since the last commit. 40 | 41 | [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) 42 | 43 | ### `npm run build` or `yarn build` 44 | 45 | Builds the app for production to the `build` folder.
46 | It correctly bundles React in production mode and optimizes the build for the best performance. 47 | 48 | The build is minified and the filenames include the hashes.
49 | Your app is ready to be deployed! 50 | 51 | 52 | ## User Guide 53 | 54 | - [Folder Structure](#folder-structure) 55 | - [Redux Dev Tools](#redux-dev-tools) 56 | - [Git Hooks](#git-hooks) 57 | - [Prettier](#prettier) 58 | - [ESLint](#eslint) 59 | - [Routing](#routing) 60 | - [Emotion Js](#emotion-js) 61 | - [Adding Sass Preprocessor](#adding-sass-preprocessor) 62 | - [Redux Store](#redux-store) 63 | - [Create React App config](#create-react-app-config) 64 | 65 | 66 | ## Folder Structure 67 | 68 | create-redux-app override create-redux-app folder structure. 69 | Once the generator runs your project folders should look like this: 70 | 71 | ``` 72 | my-app/ 73 | docs/ 74 | public/ 75 | index.html 76 | favicon.ico 77 | src/ 78 | actions/ 79 | assets/ 80 | components/ 81 | constants/ 82 | containers/ 83 | reducers/ 84 | routes/ 85 | store/ 86 | tests/ 87 | styles/ 88 | utils/ 89 | index.js 90 | ``` 91 | 92 | For the project to build, **these files must exist with exact filenames**: 93 | 94 | * `public/index.html` is the page template; 95 | * `src/index.js` is the JavaScript entry point. 96 | 97 | You can delete or rename the other files. 98 | 99 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.
100 | You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them. 101 | 102 | Only files inside `public` can be used from `public/index.html`.
103 | Read instructions below for using assets from JavaScript and HTML. 104 | 105 | You can, however, create more top-level directories.
106 | They will not be included in the production build so you can use them for things like documentation. 107 | 108 | 109 | ## Redux Dev Tools 110 | 111 | Create Redux App use [Redux DevTools Extension](http://extension.remotedev.io/). It provides access to the most popular monitors, is easy to configure and to filter actions. 112 | 113 | ### Installation 114 | 115 | #### 1. For Chrome 116 | - from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd); 117 | - or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`; 118 | - or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`. 119 | 120 | #### 2. For Firefox 121 | - from [Mozilla Add-ons](https://addons.mozilla.org/en-US/firefox/addon/remotedev/); 122 | - or build it with `npm i && npm run build:firefox` and [load the extension's folder](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) `./build/firefox` (just select a file from inside the dir). 123 | 124 | #### 3. For Electron 125 | - just specify `REDUX_DEVTOOLS` in [`electron-devtools-installer`](https://github.com/GPMDP/electron-devtools-installer). 126 | 127 | #### 4. For other browsers and non-browser environment 128 | - use [`remote-redux-devtools`](https://github.com/zalmoxisus/remote-redux-devtools). 129 | 130 | 131 | ## Git Hooks 132 | 133 | We use [Husky](https://github.com/typicode/husky) to create Git Hooks. There is a pre commit hook than run prettier to ensure good code format. You can also create a prepush hook.
134 | ``` 135 | // Edit package.json 136 | 137 | "husky": { 138 | "hooks": { 139 | "pre-commit": "pretty-quick --staged" 140 | } 141 | }, 142 | ``` 143 | 144 | ### Uninstall 145 | 146 | ```bash 147 | npm uninstall husky --save-dev 148 | ``` 149 | And delete the `husky` key in`package.json` 150 | 151 | 152 | ## Prettier 153 | 154 | You can add/remove rules if you want, just edit the `.prettierrc` file. Prettier runs in a precommit hooks to ensure good code formating with [pretty-quick](https://prettier.io/docs/en/precommit.html#option-2-pretty-quick-https-githubcom-azz-pretty-quick). 155 | ``` 156 | // Edit package.json 157 | 158 | "scripts": { 159 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 160 | "format:changed": "pretty-quick", 161 | "format:staged": "pretty-quick --staged", 162 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check" 163 | }, 164 | "husky": { 165 | "hooks": { 166 | "pre-commit": "pretty-quick --staged" 167 | } 168 | }, 169 | ``` 170 | 171 | ### Uninstall 172 | 173 | ```bash 174 | npm uninstall eslint-config-prettier pretty-quick prettier --save-dev 175 | ``` 176 | Delete 177 | ``` 178 | "scripts": { 179 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 180 | "format:changed": "pretty-quick", 181 | "format:staged": "pretty-quick --staged", 182 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 183 | }, 184 | "husky": { 185 | "hooks": { 186 | "pre-commit": "pretty-quick --staged" 187 | } 188 | }, 189 | ``` 190 | 191 | ## ESLint 192 | 193 | You can add/remove rules or even extend plugins if you want. We extend **airbnb** ESLint rules. 194 | ``` 195 | // Edit eslintrc.json 196 | 197 | { 198 | "extends": ["airbnb", "prettier", "prettier/react"], 199 | "plugins": ["prettier"], 200 | "parser": "babel-eslint", 201 | "parserOptions": { 202 | "ecmaVersion": 2016, 203 | "sourceType": "module" 204 | }, 205 | "env": { 206 | "es6": true, 207 | "jest": true, 208 | "browser": true, 209 | "node": true 210 | }, 211 | "globals": { 212 | "DEBUG": false 213 | }, 214 | "rules": { 215 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 216 | "import/no-extraneous-dependencies": 0, 217 | "import/no-unresolved": 0, 218 | "import/extensions": 0, 219 | "import/prefer-default-export": 0, 220 | "import/first": 0 221 | } 222 | } 223 | ``` 224 | 225 | 226 | ## Routing 227 | 228 | The best option for routing is [React Router](https://reacttraining.com/react-router/) specifically its new version for the web [react-router-dom](https://reacttraining.com/react-router/web/guides/quick-start).
229 | `src/routes/index.js` is the starter point of the app, where all the routes are specified and render the containers and components. Specify here all your routes, redirects, transitions, etc. 230 | 231 | 232 | ## Emotion Js 233 | 234 | [emotion-js](https://emotion.sh/) allow you to write actual CSS code in your JavaScript to style your components, 235 | removing the mapping between components and styles. 236 | 237 | See the 238 | [official documentation](https://emotion.sh/docs/introduction) 239 | for more information! 240 | 241 | 242 | ## Adding Sass Preprocessor 243 | 244 | Can I use Sass with this boilerplate? yes, although we advise against it and **do not support this**. We selected 245 | [`styled-components`](https://github.com/styled-components/styled-components) 246 | over Sass because its approach is more powerful: instead of trying to 247 | give a styling language programmatic abilities, it pulls logic and configuration 248 | out into JS where we believe those features belong. 249 | 250 | If you _really_ still want (or need) to use Sass [then...](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc) 251 | 252 | 253 | ## Redux Store 254 | 255 | The Redux store is created this way so you can use it anywhere, even outside redux, in any js file. 256 | 257 | ```js 258 | const { default: store } = process.env.NODE_ENV === 'production' 259 | ? require('./storeProd') 260 | : require('./storeDev') 261 | 262 | module.exports = store() 263 | ``` 264 | 265 | ### Usage 266 | 267 | ```js 268 | import store from './store' 269 | 270 | store.getState() // Get the state 271 | store.dispatch() // Dispatch actions 272 | ``` 273 | 274 | ## Create React App config 275 | 276 | You can find the most recent version of the create-react-app guide [here](https://facebook.github.io/create-react-app/). 277 | 278 | 279 | ## License 280 | 281 | [MIT License](https://github.com/jonidelv/generator-create-redux-app/blob/master/LICENSE) 282 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /example/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /example/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ -------------------------------------------------------------------------------- /example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "prettier", "prettier/react"], 3 | "plugins": ["prettier"], 4 | "parser": "babel-eslint", 5 | "parserOptions": { 6 | "ecmaVersion": 2016, 7 | "sourceType": "module" 8 | }, 9 | "env": { 10 | "es6": true, 11 | "jest": true, 12 | "browser": true, 13 | "node": true 14 | }, 15 | "globals": { 16 | "DEBUG": false 17 | }, 18 | "rules": { 19 | "react/jsx-filename-extension": [ 20 | 1, 21 | { 22 | "extensions": [".js", ".jsx"] 23 | } 24 | ], 25 | "import/no-extraneous-dependencies": 0, 26 | "import/no-unresolved": 0, 27 | "import/extensions": 0, 28 | "import/prefer-default-export": 0, 29 | "import/first": 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text eol=lf 23 | *.coffee text 24 | *.json text 25 | *.htm text 26 | *.html text 27 | *.xml text 28 | *.svg text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text 38 | *.bat text 39 | 40 | # templates 41 | *.ejs text 42 | *.hbt text 43 | *.jade text 44 | *.haml text 45 | *.hbs text 46 | *.dot text 47 | *.tmpl text 48 | *.phtml text 49 | 50 | # server config 51 | .htaccess text 52 | .nginx.conf text 53 | 54 | # git config 55 | .gitattributes text 56 | .gitignore text 57 | .gitconfig text 58 | 59 | # code analysis config 60 | .jshintrc text 61 | .jscsrc text 62 | .jshintignore text 63 | .csslintrc text 64 | 65 | # misc config 66 | *.yaml text 67 | *.yml text 68 | .editorconfig text 69 | 70 | # build config 71 | *.npmignore text 72 | *.bowerrc text 73 | 74 | # Heroku 75 | Procfile text 76 | .slugignore text 77 | 78 | # Documentation 79 | *.md text 80 | LICENSE text 81 | AUTHORS text 82 | 83 | 84 | # 85 | ## These files are binary and should be left untouched 86 | # 87 | 88 | # (binary is a macro for -text -diff) 89 | *.png binary 90 | *.jpg binary 91 | *.jpeg binary 92 | *.gif binary 93 | *.ico binary 94 | *.mov binary 95 | *.mp4 binary 96 | *.mp3 binary 97 | *.flv binary 98 | *.fla binary 99 | *.swf binary 100 | *.gz binary 101 | *.zip binary 102 | *.7z binary 103 | *.ttf binary 104 | *.eot binary 105 | *.woff binary 106 | *.pyc binary 107 | *.pdf binary 108 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /example/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "trailingComma": "all", 5 | "singleQuote": true, 6 | "arrowParens": "always", 7 | "jsxBracketSameLine": false, 8 | "tabWidth": 2, 9 | "useTabs": false 10 | } 11 | -------------------------------------------------------------------------------- /example/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-create-redux-app": { 3 | "promptValues": { 4 | "authorName": "jonidelv" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | [![generator-create-redux-app](https://img.shields.io/badge/built%20with-generator--create--redux--app-brightgreen.svg)](https://github.com/jonidelv/generator-create-redux-app) 3 | 4 | This project was generated with [Create Redux App](https://github.com/jonidelv/generator-create-redux-app). Refer to `docs/create-redux-app` to find more information on how to perform common tasks. 5 | 6 | Once the installation is done, you can run some commands inside the project folder: 7 | 8 | ### `npm start` or `yarn start` 9 | 10 | Runs the app in development mode.
11 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 12 | 13 | The page will reload if you make edits.
14 | You will see the build errors and lint warnings in the console. 15 | 16 | ### `npm test` or `yarn test` 17 | 18 | Runs the test watcher in an interactive mode.
19 | By default, runs tests related to files changes since the last commit. 20 | 21 | [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) 22 | 23 | ### `npm run build` or `yarn build` 24 | 25 | Builds the app for production to the `build` folder.
26 | It correctly bundles React in production mode and optimizes the build for the best performance. 27 | 28 | The build is minified and the filenames include the hashes.
29 | Your app is ready to be deployed! 30 | -------------------------------------------------------------------------------- /example/docs/create-redux-app/README.md: -------------------------------------------------------------------------------- 1 | # Generator create-redux-app 2 | 3 | This project was bootstrapped with [Create Redux App](https://github.com/jonidelv/generator-create-redux-app). Here you can find information on how to perform common tasks. 4 | 5 | ## Installation 6 | 7 | First, install [Yeoman](http://yeoman.io) and generator-create-redux-app using [npm](https://www.npmjs.com/) ( **You’ll need to have Node >= 6.10.3 on your machine** [node.js](https://nodejs.org/)). 8 | 9 | ```bash 10 | npm install -g yo 11 | npm install -g generator-create-redux-app 12 | ``` 13 | 14 | Then generate your new project: 15 | 16 | ```bash 17 | mkdir project-name 18 | cd project-name 19 | yo create-redux-app 20 | ``` 21 | 22 | Once the installation is done, you can run some commands inside the project folder: 23 | 24 | ### `npm start` or `yarn start` 25 | 26 | Runs the app in development mode.
27 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 28 | 29 | The page will reload if you make edits.
30 | You will see the build errors and lint warnings in the console. 31 | 32 | ### `npm test` or `yarn test` 33 | 34 | Runs the test watcher in an interactive mode.
35 | By default, runs tests related to files changes since the last commit. 36 | 37 | [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) 38 | 39 | ### `npm run build` or `yarn build` 40 | 41 | Builds the app for production to the `build` folder.
42 | It correctly bundles React in production mode and optimizes the build for the best performance. 43 | 44 | The build is minified and the filenames include the hashes.
45 | Your app is ready to be deployed! 46 | 47 | 48 | ## User Guide 49 | 50 | - [Folder Structure](#folder-structure) 51 | - [Redux Dev Tools](#redux-dev-tools) 52 | - [Git Hooks](#git-hooks) 53 | - [Prettier](#prettier) 54 | - [ESLint](#eslint) 55 | - [Routing](#routing) 56 | - [Emotion Js](#emotion-js) 57 | - [Adding Sass Preprocessor](#adding-sass-preprocessor) 58 | - [Redux Store](#redux-store) 59 | - [Create React App config](#create-react-app-config) 60 | 61 | 62 | ## Folder Structure 63 | 64 | create-redux-app override create-redux-app folder structure. 65 | Once the generator runs your project folders should look like this: 66 | 67 | ``` 68 | my-app/ 69 | docs/ 70 | public/ 71 | index.html 72 | favicon.ico 73 | src/ 74 | actions/ 75 | assets/ 76 | components/ 77 | constants/ 78 | containers/ 79 | reducers/ 80 | routes/ 81 | store/ 82 | tests/ 83 | styles/ 84 | utils/ 85 | index.js 86 | ``` 87 | 88 | For the project to build, **these files must exist with exact filenames**: 89 | 90 | * `public/index.html` is the page template; 91 | * `src/index.js` is the JavaScript entry point. 92 | 93 | You can delete or rename the other files. 94 | 95 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.
96 | You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them. 97 | 98 | Only files inside `public` can be used from `public/index.html`.
99 | Read instructions below for using assets from JavaScript and HTML. 100 | 101 | You can, however, create more top-level directories.
102 | They will not be included in the production build so you can use them for things like documentation. 103 | 104 | 105 | ## Redux Dev Tools 106 | 107 | Create Redux App use [Redux DevTools Extension](http://extension.remotedev.io/). It provides access to the most popular monitors, is easy to configure and to filter actions. 108 | 109 | ### Installation 110 | 111 | #### 1. For Chrome 112 | - from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd); 113 | - or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`; 114 | - or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`. 115 | 116 | #### 2. For Firefox 117 | - from [Mozilla Add-ons](https://addons.mozilla.org/en-US/firefox/addon/remotedev/); 118 | - or build it with `npm i && npm run build:firefox` and [load the extension's folder](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) `./build/firefox` (just select a file from inside the dir). 119 | 120 | #### 3. For Electron 121 | - just specify `REDUX_DEVTOOLS` in [`electron-devtools-installer`](https://github.com/GPMDP/electron-devtools-installer). 122 | 123 | #### 4. For other browsers and non-browser environment 124 | - use [`remote-redux-devtools`](https://github.com/zalmoxisus/remote-redux-devtools). 125 | 126 | 127 | ## Git Hooks 128 | 129 | We use [Husky](https://github.com/typicode/husky) to create Git Hooks. There is a pre commit hook than run prettier to ensure good code format. You can also create a prepush hook.
130 | ``` 131 | // Edit package.json 132 | 133 | "husky": { 134 | "hooks": { 135 | "pre-commit": "pretty-quick --staged" 136 | } 137 | }, 138 | ``` 139 | 140 | ### Uninstall 141 | 142 | ```bash 143 | npm uninstall husky --save-dev 144 | ``` 145 | And delete the `husky` key in`package.json` 146 | 147 | 148 | ## Prettier 149 | 150 | You can add/remove rules if you want, just edit the `.prettierrc` file. Prettier runs in a precommit hooks to ensure good code formating with [pretty-quick](https://prettier.io/docs/en/precommit.html#option-2-pretty-quick-https-githubcom-azz-pretty-quick). 151 | ``` 152 | // Edit package.json 153 | 154 | "scripts": { 155 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 156 | "format:changed": "pretty-quick", 157 | "format:staged": "pretty-quick --staged", 158 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check" 159 | }, 160 | "husky": { 161 | "hooks": { 162 | "pre-commit": "pretty-quick --staged" 163 | } 164 | }, 165 | ``` 166 | 167 | ### Uninstall 168 | 169 | ```bash 170 | npm uninstall eslint-config-prettier pretty-quick prettier --save-dev 171 | ``` 172 | Delete 173 | ``` 174 | "scripts": { 175 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 176 | "format:changed": "pretty-quick", 177 | "format:staged": "pretty-quick --staged", 178 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 179 | }, 180 | "husky": { 181 | "hooks": { 182 | "pre-commit": "pretty-quick --staged" 183 | } 184 | }, 185 | ``` 186 | 187 | ## ESLint 188 | 189 | You can add/remove rules or even extend plugins if you want. We extend **airbnb** ESLint rules. 190 | ``` 191 | // Edit eslintrc.json 192 | 193 | { 194 | "extends": ["airbnb", "prettier", "prettier/react"], 195 | "plugins": ["prettier"], 196 | "parser": "babel-eslint", 197 | "parserOptions": { 198 | "ecmaVersion": 2016, 199 | "sourceType": "module" 200 | }, 201 | "env": { 202 | "es6": true, 203 | "jest": true, 204 | "browser": true, 205 | "node": true 206 | }, 207 | "globals": { 208 | "DEBUG": false 209 | }, 210 | "rules": { 211 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 212 | "import/no-extraneous-dependencies": 0, 213 | "import/no-unresolved": 0, 214 | "import/extensions": 0, 215 | "import/prefer-default-export": 0, 216 | "import/first": 0 217 | } 218 | } 219 | ``` 220 | 221 | 222 | ## Routing 223 | 224 | The best option for routing is [React Router](https://reacttraining.com/react-router/) specifically its new version for the web [react-router-dom](https://reacttraining.com/react-router/web/guides/quick-start).
225 | `src/routes/index.js` is the starter point of the app, where all the routes are specified and render the containers and components. Specify here all your routes, redirects, transitions, etc. 226 | 227 | 228 | ## Emotion Js 229 | 230 | [emotion-js](https://emotion.sh/) allow you to write actual CSS code in your JavaScript to style your components, 231 | removing the mapping between components and styles. 232 | 233 | See the 234 | [official documentation](https://emotion.sh/docs/introduction) 235 | for more information! 236 | 237 | 238 | ## Adding Sass Preprocessor 239 | 240 | Can I use Sass with this boilerplate? yes, although we advise against it and **do not support this**. We selected 241 | [`styled-components`](https://github.com/styled-components/styled-components) 242 | over Sass because its approach is more powerful: instead of trying to 243 | give a styling language programmatic abilities, it pulls logic and configuration 244 | out into JS where we believe those features belong. 245 | 246 | If you _really_ still want (or need) to use Sass [then...](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc) 247 | 248 | 249 | ## Redux Store 250 | 251 | The Redux store is created this way so you can use it anywhere, even outside redux, in any js file. 252 | 253 | ```js 254 | const { default: store } = process.env.NODE_ENV === 'production' 255 | ? require('./storeProd') 256 | : require('./storeDev') 257 | 258 | module.exports = store() 259 | ``` 260 | 261 | ### Usage 262 | 263 | ```js 264 | import store from './store' 265 | 266 | store.getState() // Get the state 267 | store.dispatch() // Dispatch actions 268 | ``` 269 | 270 | ## Create React App config 271 | 272 | You can find the most recent version of the create-react-app guide [here](https://facebook.github.io/create-react-app/). 273 | 274 | 275 | ## License 276 | 277 | [MIT License](https://github.com/jonidelv/generator-create-redux-app/blob/master/LICENSE) 278 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "description": "generator create-redux-app example", 4 | "version": "0.1.0", 5 | "author": "jonidelv", 6 | "dependencies": { 7 | "@emotion/core": "^10.0.35", 8 | "@emotion/styled": "^10.0.35", 9 | "prop-types": "^15.7.2", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-redux": "^7.2.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "^3.4.3", 15 | "redux": "^4.0.5", 16 | "redux-devtools-extension": "^2.13.2", 17 | "redux-thunk": "^2.3.0" 18 | }, 19 | "devDependencies": { 20 | "eslint-config-airbnb": "^15.1.0", 21 | "eslint-config-prettier": "^2.9.0", 22 | "eslint-plugin-import": "^2.8.0", 23 | "eslint-plugin-jsx-a11y": "^5.1.1", 24 | "eslint-plugin-prettier": "^2.6.0", 25 | "eslint-plugin-react": "^7.4.0", 26 | "husky": "^3.1.0", 27 | "prettier": "^2.1.2", 28 | "pretty-quick": "^2.0.1" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test --env=jsdom", 34 | "eject": "react-scripts eject", 35 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 36 | "format:changed": "pretty-quick", 37 | "format:staged": "pretty-quick --staged", 38 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 39 | "lint": "eslint **/*.js --quiet", 40 | "lint-fix": "eslint --fix" 41 | }, 42 | "husky": { 43 | "hooks": { 44 | "pre-commit": "format:staged" 45 | } 46 | }, 47 | "browserslist": [ 48 | ">0.2%", 49 | "not dead", 50 | "not ie <= 11", 51 | "not op_mini all" 52 | ], 53 | "engines": { 54 | "npm": ">=4", 55 | "node": ">=6" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonidelv/generator-create-redux-app/b11ecf30f9adf9b115936f0dfb742e1fc15d8b32/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 25 | Example 26 | 27 | 28 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Example", 3 | "name": "Example", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /example/src/actions/counter.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../constants/actionTypes' 2 | import store from '../store' 3 | 4 | export function incrementIfOdd() { 5 | const { counter } = store.getState() 6 | if (counter % 2 === 0) return 7 | store.dispatch({ type: ActionTypes.INCREMENT_COUNTER }) 8 | } -------------------------------------------------------------------------------- /example/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from '@emotion/styled' 4 | 5 | const Intro = styled.p` 6 | font-size: large; 7 | ` 8 | 9 | function Counter({ increment, incrementIfOdd, decrement, counter }) { 10 | return ( 11 |
12 | 13 | To get started, edit src/routes/index.js 14 | and save to reload. 15 | 16 |

17 | Clicked: {counter} times {' '} 18 | 19 |

20 |
21 | ) 22 | } 23 | 24 | Counter.propTypes = { 25 | increment: PropTypes.func.isRequired, 26 | incrementIfOdd: PropTypes.func.isRequired, 27 | decrement: PropTypes.func.isRequired, 28 | counter: PropTypes.number.isRequired, 29 | } 30 | 31 | export default Counter 32 | -------------------------------------------------------------------------------- /example/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import logo from '../assets/logo.svg' 3 | import styled from '@emotion/styled' 4 | import { keyframes } from '@emotion/core' 5 | 6 | const rotate360 = keyframes` 7 | from { 8 | transform: rotate(0deg); 9 | } 10 | to { 11 | transform: rotate(360deg); 12 | } 13 | ` 14 | 15 | const TopBar = styled.div` 16 | background-color: #222; 17 | height: 150px; 18 | padding: 20px; 19 | color: #fff; 20 | 21 | .redux-logo { 22 | animation: ${rotate360} infinite 20s linear; 23 | height: 80px; 24 | } 25 | ` 26 | 27 | function Header() { 28 | return ( 29 | 30 | logo 31 |

Welcome to Create Redux App

32 |
33 | ) 34 | } 35 | 36 | export default Header 37 | -------------------------------------------------------------------------------- /example/src/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Counter 3 | INCREMENT_COUNTER: 'INCREMENT_COUNTER', 4 | DECREMENT_COUNTER: 'DECREMENT_COUNTER', 5 | } 6 | 7 | -------------------------------------------------------------------------------- /example/src/containers/CounterContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Counter from '../components/Counter' 4 | import ActionTypes from '../constants/actionTypes' 5 | import { incrementIfOdd } from '../actions/counter' 6 | import { connect } from 'react-redux' 7 | 8 | class CounterContainer extends React.Component { 9 | static propTypes = { 10 | dispatch: PropTypes.func.isRequired, 11 | counter: PropTypes.number.isRequired, 12 | } 13 | 14 | increment = () => { 15 | this.props.dispatch({ type: ActionTypes.INCREMENT_COUNTER }) 16 | } 17 | 18 | decrement = () => { 19 | this.props.dispatch({ type: ActionTypes.DECREMENT_COUNTER }) 20 | } 21 | 22 | incrementIfOdd = () => { 23 | incrementIfOdd() 24 | } 25 | 26 | render() { 27 | return ( 28 | 34 | ) 35 | } 36 | } 37 | 38 | function mapStateToProps(state) { 39 | return { 40 | counter: state.counter, 41 | } 42 | } 43 | 44 | export default connect(mapStateToProps)(CounterContainer) 45 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Provider } from 'react-redux' 4 | import store from './store' 5 | import Routes from './routes' 6 | import './styles/globalStyles.css' 7 | import * as serviceWorker from './utils/serviceWorker' 8 | 9 | render( 10 | 11 | 12 | , 13 | document.getElementById('root'), 14 | ) 15 | // If you want your app to work offline and load faster, you can change 16 | // unregister() to register() below. Note this comes with some pitfalls. 17 | // Learn more about service workers: http://bit.ly/CRA-PWA 18 | serviceWorker.unregister() 19 | -------------------------------------------------------------------------------- /example/src/reducers/counter.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../constants/actionTypes' 2 | 3 | const initialState = 0 4 | 5 | export default function counter(state = initialState, action) { 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT_COUNTER: 8 | return state + 1 9 | case ActionTypes.DECREMENT_COUNTER: 10 | return state - 1 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import counter from './counter' 3 | 4 | const rootReducer = combineReducers({ 5 | counter, 6 | }) 7 | 8 | export default rootReducer 9 | -------------------------------------------------------------------------------- /example/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CounterContainer from '../containers/CounterContainer' 3 | import Header from '../components/Header' 4 | import { Router, Route, Switch } from 'react-router-dom' 5 | import { createBrowserHistory } from 'history' 6 | import styled from '@emotion/styled' 7 | 8 | const Container = styled.div` 9 | text-align: center; 10 | ` 11 | export const history = createBrowserHistory() 12 | 13 | function Routes() { 14 | return ( 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export default Routes 27 | -------------------------------------------------------------------------------- /example/src/store/index.js: -------------------------------------------------------------------------------- 1 | const { default: store } = process.env.NODE_ENV === 'production' 2 | ? require('./storeProd') 3 | : require('./storeDev') 4 | 5 | // Exporting the store, then use it anywhere like store.getState() or store.dispatch() 6 | module.exports = store() -------------------------------------------------------------------------------- /example/src/store/storeDev.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import ReduxThunk from 'redux-thunk' 3 | import { composeWithDevTools } from 'redux-devtools-extension' 4 | import rootReducer from '../reducers' 5 | 6 | export default function configureStore(initialState = {}) { 7 | const middlewares = [ReduxThunk] 8 | const enhancers = [ 9 | applyMiddleware(...middlewares), 10 | // other store enhancers if any 11 | ] 12 | const composeEnhancers = composeWithDevTools({ 13 | // other compose enhancers if any 14 | // Specify here other options if needed 15 | }) 16 | const store = createStore(rootReducer, initialState, composeEnhancers(...enhancers)) 17 | if (module.hot) { 18 | // Enable Webpack hot module replacement for reducers 19 | module.hot.accept('../reducers', () => { 20 | /* eslint-disable global-require */ 21 | const nextReducer = require('../reducers').default 22 | store.replaceReducer(nextReducer) 23 | }) 24 | } 25 | 26 | return store 27 | } 28 | -------------------------------------------------------------------------------- /example/src/store/storeProd.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import ReduxThunk from 'redux-thunk' 3 | import rootReducer from '../reducers' 4 | 5 | const middlewares = [ReduxThunk] 6 | const enhancer = [applyMiddleware(...middlewares)] 7 | 8 | export default function configureStore(initialState = {}) { 9 | return createStore(rootReducer, initialState, ...enhancer) 10 | } 11 | -------------------------------------------------------------------------------- /example/src/styles/globalStyles.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | padding: 0; 9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", 10 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 11 | sans-serif; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | code { 17 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 18 | monospace; 19 | } 20 | -------------------------------------------------------------------------------- /example/src/tests/CounterContainer.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import CounterContainer from '../containers/CounterContainer' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | }) 9 | -------------------------------------------------------------------------------- /example/src/utils/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read http://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/), 19 | ) 20 | 21 | export function register(config) { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 29 | return 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Let's check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl, config) 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit http://bit.ly/CRA-PWA', 45 | ) 46 | }) 47 | } else { 48 | // Is not localhost. Just register service worker 49 | registerValidSW(swUrl, config) 50 | } 51 | }) 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl, config) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then((registration) => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing 61 | if (installingWorker == null) { 62 | return 63 | } 64 | installingWorker.onstatechange = () => { 65 | if (installingWorker.state === 'installed') { 66 | if (navigator.serviceWorker.controller) { 67 | // At this point, the updated precached content has been fetched, 68 | // but the previous service worker will still serve the older 69 | // content until all client tabs are closed. 70 | console.log( 71 | 'New content is available and will be used when all ' + 72 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.', 73 | ) 74 | 75 | // Execute callback 76 | if (config && config.onUpdate) { 77 | config.onUpdate(registration) 78 | } 79 | } else { 80 | // At this point, everything has been precached. 81 | // It's the perfect time to display a 82 | // "Content is cached for offline use." message. 83 | console.log('Content is cached for offline use.') 84 | 85 | // Execute callback 86 | if (config && config.onSuccess) { 87 | config.onSuccess(registration) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | }) 94 | .catch((error) => { 95 | console.error('Error during service worker registration:', error) 96 | }) 97 | } 98 | 99 | function checkValidServiceWorker(swUrl, config) { 100 | // Check if the service worker can be found. If it can't reload the page. 101 | fetch(swUrl) 102 | .then((response) => { 103 | // Ensure service worker exists, and that we really are getting a JS file. 104 | const contentType = response.headers.get('content-type') 105 | if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { 106 | // No service worker found. Probably a different app. Reload the page. 107 | navigator.serviceWorker.ready.then((registration) => { 108 | registration.unregister().then(() => { 109 | window.location.reload() 110 | }) 111 | }) 112 | } else { 113 | // Service worker found. Proceed as normal. 114 | registerValidSW(swUrl, config) 115 | } 116 | }) 117 | .catch(() => { 118 | console.log('No internet connection found. App is running in offline mode.') 119 | }) 120 | } 121 | 122 | export function unregister() { 123 | if ('serviceWorker' in navigator) { 124 | navigator.serviceWorker.ready.then((registration) => { 125 | registration.unregister() 126 | }) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | const Generator = require('yeoman-generator') 2 | const path = require('path') 3 | const to = require('to-case') 4 | const yosay = require('yosay') 5 | 6 | const projectGenerator = Generator.extend({ 7 | prompting: { 8 | welcome() { 9 | this.log( 10 | yosay( 11 | "'Allo 'allo! This generator add redux, " + 12 | 'emotion-js and some useful tools and libraries like ' + 13 | 'react-router to the most common ' + 14 | 'React starter Create React App' 15 | ) 16 | ) 17 | }, 18 | ask() { 19 | return this.prompt([ 20 | { 21 | name: 'projectName', 22 | type: 'input', 23 | message: 'Project name:', 24 | default: path.basename(this.destinationPath()), 25 | }, 26 | { 27 | name: 'projectDescription', 28 | type: 'input', 29 | message: 'Project description:', 30 | }, 31 | { 32 | name: 'projectVersion', 33 | type: 'input', 34 | message: 'Project version:', 35 | default: '0.1.0', 36 | }, 37 | { 38 | name: 'authorName', 39 | type: 'input', 40 | message: 'Author name:', 41 | store: true, 42 | }, 43 | ]).then(answers => { 44 | this.projectName = answers.projectName 45 | this.projectDescription = answers.projectDescription 46 | this.projectVersion = answers.projectVersion 47 | this.authorName = answers.authorName 48 | }) 49 | }, 50 | }, 51 | 52 | writing: { 53 | public() { 54 | this.fs.copyTpl(this.templatePath('public/index.html'), this.destinationPath('public/index.html'), { 55 | projectName: to.title(this.projectName), 56 | }) 57 | this.fs.copyTpl(this.templatePath('public/manifest.json'), this.destinationPath('public/manifest.json'), { 58 | projectName: to.title(this.projectName), 59 | }) 60 | this.fs.copy(this.templatePath('public/favicon.ico'), this.destinationPath('public/favicon.ico')) 61 | }, 62 | 63 | readme() { 64 | this.fs.copyTpl(this.templatePath('README.md'), this.destinationPath('README.md'), { 65 | projectName: to.title(this.projectName), 66 | }) 67 | }, 68 | 69 | gitignore() { 70 | this.fs.copy(this.templatePath('gitignore'), this.destinationPath('.gitignore')) 71 | }, 72 | 73 | gitattributes() { 74 | this.fs.copy(this.templatePath('gitattributes'), this.destinationPath('.gitattributes')) 75 | }, 76 | 77 | editorconfig() { 78 | this.fs.copy(this.templatePath('editorconfig'), this.destinationPath('.editorconfig')) 79 | }, 80 | 81 | prettierrc() { 82 | this.fs.copy(this.templatePath('prettierrc'), this.destinationPath('.prettierrc')) 83 | }, 84 | 85 | eslintignore() { 86 | this.fs.copy(this.templatePath('eslintignore'), this.destinationPath('.eslintignore')) 87 | }, 88 | 89 | eslintrc() { 90 | this.fs.copy(this.templatePath('eslintrc.json'), this.destinationPath('.eslintrc.json')) 91 | }, 92 | 93 | docs() { 94 | this.fs.copy(this.templatePath('docs'), this.destinationPath('docs')) 95 | }, 96 | 97 | src() { 98 | this.fs.copy(this.templatePath('src'), this.destinationPath('src')) 99 | }, 100 | 101 | packageJSON() { 102 | this.fs.copyTpl(this.templatePath('package.json'), this.destinationPath('package.json'), { 103 | projectName: this.projectName, 104 | projectDescription: this.projectDescription, 105 | projectVersion: this.projectVersion, 106 | authorName: this.authorName, 107 | }) 108 | }, 109 | }, 110 | 111 | install() { 112 | this.installDependencies({ 113 | npm: true, 114 | bower: false, 115 | yarn: true, 116 | }) 117 | }, 118 | }) 119 | 120 | module.exports = projectGenerator 121 | -------------------------------------------------------------------------------- /generators/app/templates/README.md: -------------------------------------------------------------------------------- 1 | # <%= projectName %> 2 | [![generator-create-redux-app](https://img.shields.io/badge/built%20with-generator--create--redux--app-brightgreen.svg)](https://github.com/jonidelv/generator-create-redux-app) 3 | 4 | This project was generated with [Create Redux App](https://github.com/jonidelv/generator-create-redux-app). Refer to `docs/create-redux-app` to find more information on how to perform common tasks. 5 | 6 | Once the installation is done, you can run some commands inside the project folder: 7 | 8 | ### `npm start` or `yarn start` 9 | 10 | Runs the app in development mode.
11 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 12 | 13 | The page will reload if you make edits.
14 | You will see the build errors and lint warnings in the console. 15 | 16 | ### `npm test` or `yarn test` 17 | 18 | Runs the test watcher in an interactive mode.
19 | By default, runs tests related to files changes since the last commit. 20 | 21 | [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) 22 | 23 | ### `npm run build` or `yarn build` 24 | 25 | Builds the app for production to the `build` folder.
26 | It correctly bundles React in production mode and optimizes the build for the best performance. 27 | 28 | The build is minified and the filenames include the hashes.
29 | Your app is ready to be deployed! 30 | -------------------------------------------------------------------------------- /generators/app/templates/docs/create-redux-app/README.md: -------------------------------------------------------------------------------- 1 | # Generator create-redux-app 2 | 3 | This project was bootstrapped with [Create Redux App](https://github.com/jonidelv/generator-create-redux-app). Here you can find information on how to perform common tasks. 4 | 5 | ## Installation 6 | 7 | First, install [Yeoman](http://yeoman.io) and generator-create-redux-app using [npm](https://www.npmjs.com/) ( **You’ll need to have Node >= 6.10.3 on your machine** [node.js](https://nodejs.org/)). 8 | 9 | ```bash 10 | npm install -g yo 11 | npm install -g generator-create-redux-app 12 | ``` 13 | 14 | Then generate your new project: 15 | 16 | ```bash 17 | mkdir project-name 18 | cd project-name 19 | yo create-redux-app 20 | ``` 21 | 22 | Once the installation is done, you can run some commands inside the project folder: 23 | 24 | ### `npm start` or `yarn start` 25 | 26 | Runs the app in development mode.
27 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 28 | 29 | The page will reload if you make edits.
30 | You will see the build errors and lint warnings in the console. 31 | 32 | ### `npm test` or `yarn test` 33 | 34 | Runs the test watcher in an interactive mode.
35 | By default, runs tests related to files changes since the last commit. 36 | 37 | [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) 38 | 39 | ### `npm run build` or `yarn build` 40 | 41 | Builds the app for production to the `build` folder.
42 | It correctly bundles React in production mode and optimizes the build for the best performance. 43 | 44 | The build is minified and the filenames include the hashes.
45 | Your app is ready to be deployed! 46 | 47 | 48 | ## User Guide 49 | 50 | - [Folder Structure](#folder-structure) 51 | - [Redux Dev Tools](#redux-dev-tools) 52 | - [Git Hooks](#git-hooks) 53 | - [Prettier](#prettier) 54 | - [ESLint](#eslint) 55 | - [Routing](#routing) 56 | - [Emotion Js](#emotion-js) 57 | - [Adding Sass Preprocessor](#adding-sass-preprocessor) 58 | - [Redux Store](#redux-store) 59 | - [Create React App config](#create-react-app-config) 60 | 61 | 62 | ## Folder Structure 63 | 64 | create-redux-app override create-redux-app folder structure. 65 | Once the generator runs your project folders should look like this: 66 | 67 | ``` 68 | my-app/ 69 | docs/ 70 | public/ 71 | index.html 72 | favicon.ico 73 | src/ 74 | actions/ 75 | assets/ 76 | components/ 77 | constants/ 78 | containers/ 79 | reducers/ 80 | routes/ 81 | store/ 82 | tests/ 83 | styles/ 84 | utils/ 85 | index.js 86 | ``` 87 | 88 | For the project to build, **these files must exist with exact filenames**: 89 | 90 | * `public/index.html` is the page template; 91 | * `src/index.js` is the JavaScript entry point. 92 | 93 | You can delete or rename the other files. 94 | 95 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.
96 | You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them. 97 | 98 | Only files inside `public` can be used from `public/index.html`.
99 | Read instructions below for using assets from JavaScript and HTML. 100 | 101 | You can, however, create more top-level directories.
102 | They will not be included in the production build so you can use them for things like documentation. 103 | 104 | 105 | ## Redux Dev Tools 106 | 107 | Create Redux App use [Redux DevTools Extension](http://extension.remotedev.io/). It provides access to the most popular monitors, is easy to configure and to filter actions. 108 | 109 | ### Installation 110 | 111 | #### 1. For Chrome 112 | - from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd); 113 | - or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`; 114 | - or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`. 115 | 116 | #### 2. For Firefox 117 | - from [Mozilla Add-ons](https://addons.mozilla.org/en-US/firefox/addon/remotedev/); 118 | - or build it with `npm i && npm run build:firefox` and [load the extension's folder](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) `./build/firefox` (just select a file from inside the dir). 119 | 120 | #### 3. For Electron 121 | - just specify `REDUX_DEVTOOLS` in [`electron-devtools-installer`](https://github.com/GPMDP/electron-devtools-installer). 122 | 123 | #### 4. For other browsers and non-browser environment 124 | - use [`remote-redux-devtools`](https://github.com/zalmoxisus/remote-redux-devtools). 125 | 126 | 127 | ## Git Hooks 128 | 129 | We use [Husky](https://github.com/typicode/husky) to create Git Hooks. There is a pre commit hook than run prettier to ensure good code format. You can also create a prepush hook.
130 | ``` 131 | // Edit package.json 132 | 133 | "husky": { 134 | "hooks": { 135 | "pre-commit": "pretty-quick --staged" 136 | } 137 | }, 138 | ``` 139 | 140 | ### Uninstall 141 | 142 | ```bash 143 | npm uninstall husky --save-dev 144 | ``` 145 | And delete the `husky` key in`package.json` 146 | 147 | 148 | ## Prettier 149 | 150 | You can add/remove rules if you want, just edit the `.prettierrc` file. Prettier runs in a precommit hooks to ensure good code formating with [pretty-quick](https://prettier.io/docs/en/precommit.html#option-2-pretty-quick-https-githubcom-azz-pretty-quick). 151 | ``` 152 | // Edit package.json 153 | 154 | "scripts": { 155 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 156 | "format:changed": "pretty-quick", 157 | "format:staged": "pretty-quick --staged", 158 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check" 159 | }, 160 | "husky": { 161 | "hooks": { 162 | "pre-commit": "pretty-quick --staged" 163 | } 164 | }, 165 | ``` 166 | 167 | ### Uninstall 168 | 169 | ```bash 170 | npm uninstall eslint-config-prettier pretty-quick prettier --save-dev 171 | ``` 172 | Delete 173 | ``` 174 | "scripts": { 175 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 176 | "format:changed": "pretty-quick", 177 | "format:staged": "pretty-quick --staged", 178 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 179 | }, 180 | "husky": { 181 | "hooks": { 182 | "pre-commit": "pretty-quick --staged" 183 | } 184 | }, 185 | ``` 186 | 187 | ## ESLint 188 | 189 | You can add/remove rules or even extend plugins if you want. We extend **airbnb** ESLint rules. 190 | ``` 191 | // Edit eslintrc.json 192 | 193 | { 194 | "extends": ["airbnb", "prettier", "prettier/react"], 195 | "plugins": ["prettier"], 196 | "parser": "babel-eslint", 197 | "parserOptions": { 198 | "ecmaVersion": 2016, 199 | "sourceType": "module" 200 | }, 201 | "env": { 202 | "es6": true, 203 | "jest": true, 204 | "browser": true, 205 | "node": true 206 | }, 207 | "globals": { 208 | "DEBUG": false 209 | }, 210 | "rules": { 211 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 212 | "import/no-extraneous-dependencies": 0, 213 | "import/no-unresolved": 0, 214 | "import/extensions": 0, 215 | "import/prefer-default-export": 0, 216 | "import/first": 0 217 | } 218 | } 219 | ``` 220 | 221 | 222 | ## Routing 223 | 224 | The best option for routing is [React Router](https://reacttraining.com/react-router/) specifically its new version for the web [react-router-dom](https://reacttraining.com/react-router/web/guides/quick-start).
225 | `src/routes/index.js` is the starter point of the app, where all the routes are specified and render the containers and components. Specify here all your routes, redirects, transitions, etc. 226 | 227 | 228 | ## Emotion Js 229 | 230 | [emotion-js](https://emotion.sh/) allow you to write actual CSS code in your JavaScript to style your components, 231 | removing the mapping between components and styles. 232 | 233 | See the 234 | [official documentation](https://emotion.sh/docs/introduction) 235 | for more information! 236 | 237 | 238 | ## Adding Sass Preprocessor 239 | 240 | Can I use Sass with this boilerplate? yes, although we advise against it and **do not support this**. We selected 241 | [`styled-components`](https://github.com/styled-components/styled-components) 242 | over Sass because its approach is more powerful: instead of trying to 243 | give a styling language programmatic abilities, it pulls logic and configuration 244 | out into JS where we believe those features belong. 245 | 246 | If you _really_ still want (or need) to use Sass [then...](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc) 247 | 248 | 249 | ## Redux Store 250 | 251 | The Redux store is created this way so you can use it anywhere, even outside redux, in any js file. 252 | 253 | ```js 254 | const { default: store } = process.env.NODE_ENV === 'production' 255 | ? require('./storeProd') 256 | : require('./storeDev') 257 | 258 | module.exports = store() 259 | ``` 260 | 261 | ### Usage 262 | 263 | ```js 264 | import store from './store' 265 | 266 | store.getState() // Get the state 267 | store.dispatch() // Dispatch actions 268 | ``` 269 | 270 | ## Create React App config 271 | 272 | You can find the most recent version of the create-react-app guide [here](https://facebook.github.io/create-react-app/). 273 | 274 | 275 | ## License 276 | 277 | [MIT License](https://github.com/jonidelv/generator-create-redux-app/blob/master/LICENSE) 278 | -------------------------------------------------------------------------------- /generators/app/templates/editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /generators/app/templates/eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ -------------------------------------------------------------------------------- /generators/app/templates/eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "prettier", "prettier/react"], 3 | "plugins": ["prettier"], 4 | "parser": "babel-eslint", 5 | "parserOptions": { 6 | "ecmaVersion": 2016, 7 | "sourceType": "module" 8 | }, 9 | "env": { 10 | "es6": true, 11 | "jest": true, 12 | "browser": true, 13 | "node": true 14 | }, 15 | "globals": { 16 | "DEBUG": false 17 | }, 18 | "rules": { 19 | "react/jsx-filename-extension": [ 20 | 1, 21 | { 22 | "extensions": [".js", ".jsx"] 23 | } 24 | ], 25 | "import/no-extraneous-dependencies": 0, 26 | "import/no-unresolved": 0, 27 | "import/extensions": 0, 28 | "import/prefer-default-export": 0, 29 | "import/first": 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /generators/app/templates/gitattributes: -------------------------------------------------------------------------------- 1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes 2 | 3 | # Handle line endings automatically for files detected as text 4 | # and leave all files detected as binary untouched. 5 | * text=auto 6 | 7 | # 8 | # The above will handle all files NOT found below 9 | # 10 | 11 | # 12 | ## These files are text and should be normalized (Convert crlf => lf) 13 | # 14 | 15 | # source code 16 | *.php text 17 | *.css text 18 | *.sass text 19 | *.scss text 20 | *.less text 21 | *.styl text 22 | *.js text eol=lf 23 | *.coffee text 24 | *.json text 25 | *.htm text 26 | *.html text 27 | *.xml text 28 | *.svg text 29 | *.txt text 30 | *.ini text 31 | *.inc text 32 | *.pl text 33 | *.rb text 34 | *.py text 35 | *.scm text 36 | *.sql text 37 | *.sh text 38 | *.bat text 39 | 40 | # templates 41 | *.ejs text 42 | *.hbt text 43 | *.jade text 44 | *.haml text 45 | *.hbs text 46 | *.dot text 47 | *.tmpl text 48 | *.phtml text 49 | 50 | # server config 51 | .htaccess text 52 | .nginx.conf text 53 | 54 | # git config 55 | .gitattributes text 56 | .gitignore text 57 | .gitconfig text 58 | 59 | # code analysis config 60 | .jshintrc text 61 | .jscsrc text 62 | .jshintignore text 63 | .csslintrc text 64 | 65 | # misc config 66 | *.yaml text 67 | *.yml text 68 | .editorconfig text 69 | 70 | # build config 71 | *.npmignore text 72 | *.bowerrc text 73 | 74 | # Heroku 75 | Procfile text 76 | .slugignore text 77 | 78 | # Documentation 79 | *.md text 80 | LICENSE text 81 | AUTHORS text 82 | 83 | 84 | # 85 | ## These files are binary and should be left untouched 86 | # 87 | 88 | # (binary is a macro for -text -diff) 89 | *.png binary 90 | *.jpg binary 91 | *.jpeg binary 92 | *.gif binary 93 | *.ico binary 94 | *.mov binary 95 | *.mp4 binary 96 | *.mp3 binary 97 | *.flv binary 98 | *.fla binary 99 | *.swf binary 100 | *.gz binary 101 | *.zip binary 102 | *.7z binary 103 | *.ttf binary 104 | *.eot binary 105 | *.woff binary 106 | *.pyc binary 107 | *.pdf binary 108 | -------------------------------------------------------------------------------- /generators/app/templates/gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | -------------------------------------------------------------------------------- /generators/app/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= projectName %>", 3 | "description": "<%= projectDescription %>", 4 | "version": "<%= projectVersion %>", 5 | "author": "<%= authorName %>", 6 | "dependencies": { 7 | "@emotion/core": "^10.0.35", 8 | "@emotion/styled": "^10.0.27", 9 | "prop-types": "^15.7.2", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-redux": "^7.2.1", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "^3.4.3", 15 | "redux": "^4.0.5", 16 | "redux-devtools-extension": "^2.13.2", 17 | "redux-thunk": "^2.3.0" 18 | }, 19 | "devDependencies": { 20 | "eslint-config-airbnb": "^15.1.0", 21 | "eslint-config-prettier": "^2.9.0", 22 | "eslint-plugin-import": "^2.8.0", 23 | "eslint-plugin-jsx-a11y": "^5.1.1", 24 | "eslint-plugin-prettier": "^2.6.0", 25 | "eslint-plugin-react": "^7.4.0", 26 | "husky": "^3.1.0", 27 | "prettier": "^2.1.2", 28 | "pretty-quick": "^2.0.1" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test --env=jsdom", 34 | "eject": "react-scripts eject", 35 | "format": "prettier --write '**/*.{js,jsx,json,md}'", 36 | "format:changed": "pretty-quick", 37 | "format:staged": "pretty-quick --staged", 38 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 39 | "lint": "eslint **/*.js --quiet", 40 | "lint-fix": "eslint --fix" 41 | }, 42 | "husky": { 43 | "hooks": { 44 | "pre-commit": "format:staged" 45 | } 46 | }, 47 | "browserslist": [ 48 | ">0.2%", 49 | "not dead", 50 | "not ie <= 11", 51 | "not op_mini all" 52 | ], 53 | "engines": { 54 | "npm": ">=4", 55 | "node": ">=6" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /generators/app/templates/prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "trailingComma": "all", 5 | "singleQuote": true, 6 | "arrowParens": "always", 7 | "jsxBracketSameLine": false, 8 | "tabWidth": 2, 9 | "useTabs": false 10 | } 11 | -------------------------------------------------------------------------------- /generators/app/templates/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonidelv/generator-create-redux-app/b11ecf30f9adf9b115936f0dfb742e1fc15d8b32/generators/app/templates/public/favicon.ico -------------------------------------------------------------------------------- /generators/app/templates/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 16 | 25 | <%= projectName %> 26 | 27 | 28 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /generators/app/templates/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "<%= projectName %>", 3 | "name": "<%= projectName %>", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /generators/app/templates/src/actions/counter.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../constants/actionTypes' 2 | import store from '../store' 3 | 4 | export function incrementIfOdd() { 5 | const { counter } = store.getState() 6 | if (counter % 2 === 0) return 7 | store.dispatch({ type: ActionTypes.INCREMENT_COUNTER }) 8 | } -------------------------------------------------------------------------------- /generators/app/templates/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styled from '@emotion/styled' 4 | 5 | const Intro = styled.p` 6 | font-size: large; 7 | ` 8 | 9 | function Counter({ increment, incrementIfOdd, decrement, counter }) { 10 | return ( 11 |
12 | 13 | To get started, edit src/routes/index.js 14 | and save to reload. 15 | 16 |

17 | Clicked: {counter} times {' '} 18 | 19 |

20 |
21 | ) 22 | } 23 | 24 | Counter.propTypes = { 25 | increment: PropTypes.func.isRequired, 26 | incrementIfOdd: PropTypes.func.isRequired, 27 | decrement: PropTypes.func.isRequired, 28 | counter: PropTypes.number.isRequired, 29 | } 30 | 31 | export default Counter 32 | -------------------------------------------------------------------------------- /generators/app/templates/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import logo from '../assets/logo.svg' 3 | import styled from '@emotion/styled' 4 | import { keyframes } from '@emotion/core' 5 | 6 | const rotate360 = keyframes` 7 | from { 8 | transform: rotate(0deg); 9 | } 10 | to { 11 | transform: rotate(360deg); 12 | } 13 | ` 14 | 15 | const TopBar = styled.div` 16 | background-color: #222; 17 | height: 150px; 18 | padding: 20px; 19 | color: #fff; 20 | 21 | .redux-logo { 22 | animation: ${rotate360} infinite 20s linear; 23 | height: 80px; 24 | } 25 | ` 26 | 27 | function Header() { 28 | return ( 29 | 30 | logo 31 |

Welcome to Create Redux App

32 |
33 | ) 34 | } 35 | 36 | export default Header 37 | -------------------------------------------------------------------------------- /generators/app/templates/src/constants/actionTypes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Counter 3 | INCREMENT_COUNTER: 'INCREMENT_COUNTER', 4 | DECREMENT_COUNTER: 'DECREMENT_COUNTER', 5 | } 6 | 7 | -------------------------------------------------------------------------------- /generators/app/templates/src/containers/CounterContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Counter from '../components/Counter' 4 | import ActionTypes from '../constants/actionTypes' 5 | import { incrementIfOdd } from '../actions/counter' 6 | import { connect } from 'react-redux' 7 | 8 | class CounterContainer extends React.Component { 9 | static propTypes = { 10 | dispatch: PropTypes.func.isRequired, 11 | counter: PropTypes.number.isRequired, 12 | } 13 | 14 | increment = () => { 15 | this.props.dispatch({ type: ActionTypes.INCREMENT_COUNTER }) 16 | } 17 | 18 | decrement = () => { 19 | this.props.dispatch({ type: ActionTypes.DECREMENT_COUNTER }) 20 | } 21 | 22 | incrementIfOdd = () => { 23 | incrementIfOdd() 24 | } 25 | 26 | render() { 27 | return ( 28 | 34 | ) 35 | } 36 | } 37 | 38 | function mapStateToProps(state) { 39 | return { 40 | counter: state.counter, 41 | } 42 | } 43 | 44 | export default connect(mapStateToProps)(CounterContainer) 45 | -------------------------------------------------------------------------------- /generators/app/templates/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Provider } from 'react-redux' 4 | import store from './store' 5 | import Routes from './routes' 6 | import './styles/globalStyles.css' 7 | import * as serviceWorker from './utils/serviceWorker' 8 | 9 | render( 10 | 11 | 12 | , 13 | document.getElementById('root'), 14 | ) 15 | // If you want your app to work offline and load faster, you can change 16 | // unregister() to register() below. Note this comes with some pitfalls. 17 | // Learn more about service workers: http://bit.ly/CRA-PWA 18 | serviceWorker.unregister() 19 | -------------------------------------------------------------------------------- /generators/app/templates/src/reducers/counter.js: -------------------------------------------------------------------------------- 1 | import ActionTypes from '../constants/actionTypes' 2 | 3 | const initialState = 0 4 | 5 | export default function counter(state = initialState, action) { 6 | switch (action.type) { 7 | case ActionTypes.INCREMENT_COUNTER: 8 | return state + 1 9 | case ActionTypes.DECREMENT_COUNTER: 10 | return state - 1 11 | default: 12 | return state 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generators/app/templates/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import counter from './counter' 3 | 4 | const rootReducer = combineReducers({ 5 | counter, 6 | }) 7 | 8 | export default rootReducer 9 | -------------------------------------------------------------------------------- /generators/app/templates/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CounterContainer from '../containers/CounterContainer' 3 | import Header from '../components/Header' 4 | import { Router, Route, Switch } from 'react-router-dom' 5 | import { createBrowserHistory } from 'history' 6 | import styled from '@emotion/styled' 7 | 8 | const Container = styled.div` 9 | text-align: center; 10 | ` 11 | export const history = createBrowserHistory() 12 | 13 | function Routes() { 14 | return ( 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export default Routes 27 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/index.js: -------------------------------------------------------------------------------- 1 | const { default: store } = process.env.NODE_ENV === 'production' 2 | ? require('./storeProd') 3 | : require('./storeDev') 4 | 5 | // Exporting the store, then use it anywhere like store.getState() or store.dispatch() 6 | module.exports = store() -------------------------------------------------------------------------------- /generators/app/templates/src/store/storeDev.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import ReduxThunk from 'redux-thunk' 3 | import { composeWithDevTools } from 'redux-devtools-extension' 4 | import rootReducer from '../reducers' 5 | 6 | export default function configureStore(initialState = {}) { 7 | const middlewares = [ReduxThunk] 8 | const enhancers = [ 9 | applyMiddleware(...middlewares), 10 | // other store enhancers if any 11 | ] 12 | const composeEnhancers = composeWithDevTools({ 13 | // other compose enhancers if any 14 | // Specify here other options if needed 15 | }) 16 | const store = createStore(rootReducer, initialState, composeEnhancers(...enhancers)) 17 | if (module.hot) { 18 | // Enable Webpack hot module replacement for reducers 19 | module.hot.accept('../reducers', () => { 20 | /* eslint-disable global-require */ 21 | const nextReducer = require('../reducers').default 22 | store.replaceReducer(nextReducer) 23 | }) 24 | } 25 | 26 | return store 27 | } 28 | -------------------------------------------------------------------------------- /generators/app/templates/src/store/storeProd.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import ReduxThunk from 'redux-thunk' 3 | import rootReducer from '../reducers' 4 | 5 | const middlewares = [ReduxThunk] 6 | const enhancer = [applyMiddleware(...middlewares)] 7 | 8 | export default function configureStore(initialState = {}) { 9 | return createStore(rootReducer, initialState, ...enhancer) 10 | } 11 | -------------------------------------------------------------------------------- /generators/app/templates/src/styles/globalStyles.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | padding: 0; 9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", 10 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 11 | sans-serif; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | code { 17 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 18 | monospace; 19 | } 20 | -------------------------------------------------------------------------------- /generators/app/templates/src/tests/CounterContainer.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import CounterContainer from '../containers/CounterContainer' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | }) 9 | -------------------------------------------------------------------------------- /generators/app/templates/src/utils/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read http://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/), 19 | ) 20 | 21 | export function register(config) { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href) 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 29 | return 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Let's check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl, config) 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit http://bit.ly/CRA-PWA', 45 | ) 46 | }) 47 | } else { 48 | // Is not localhost. Just register service worker 49 | registerValidSW(swUrl, config) 50 | } 51 | }) 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl, config) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then((registration) => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing 61 | if (installingWorker == null) { 62 | return 63 | } 64 | installingWorker.onstatechange = () => { 65 | if (installingWorker.state === 'installed') { 66 | if (navigator.serviceWorker.controller) { 67 | // At this point, the updated precached content has been fetched, 68 | // but the previous service worker will still serve the older 69 | // content until all client tabs are closed. 70 | console.log( 71 | 'New content is available and will be used when all ' + 72 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.', 73 | ) 74 | 75 | // Execute callback 76 | if (config && config.onUpdate) { 77 | config.onUpdate(registration) 78 | } 79 | } else { 80 | // At this point, everything has been precached. 81 | // It's the perfect time to display a 82 | // "Content is cached for offline use." message. 83 | console.log('Content is cached for offline use.') 84 | 85 | // Execute callback 86 | if (config && config.onSuccess) { 87 | config.onSuccess(registration) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | }) 94 | .catch((error) => { 95 | console.error('Error during service worker registration:', error) 96 | }) 97 | } 98 | 99 | function checkValidServiceWorker(swUrl, config) { 100 | // Check if the service worker can be found. If it can't reload the page. 101 | fetch(swUrl) 102 | .then((response) => { 103 | // Ensure service worker exists, and that we really are getting a JS file. 104 | const contentType = response.headers.get('content-type') 105 | if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { 106 | // No service worker found. Probably a different app. Reload the page. 107 | navigator.serviceWorker.ready.then((registration) => { 108 | registration.unregister().then(() => { 109 | window.location.reload() 110 | }) 111 | }) 112 | } else { 113 | // Service worker found. Proceed as normal. 114 | registerValidSW(swUrl, config) 115 | } 116 | }) 117 | .catch(() => { 118 | console.log('No internet connection found. App is running in offline mode.') 119 | }) 120 | } 121 | 122 | export function unregister() { 123 | if ('serviceWorker' in navigator) { 124 | navigator.serviceWorker.ready.then((registration) => { 125 | registration.unregister() 126 | }) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-create-redux-app", 3 | "version": "1.2.4", 4 | "description": "Add redux, emotion-js and other useful libraries like react-router in top of create-react-app", 5 | "homepage": "https://github.com/jonidelv/generator-create-redux-app", 6 | "author": { 7 | "name": "jonidelv", 8 | "email": "hi@jonidelv.me" 9 | }, 10 | "files": [ 11 | "generators" 12 | ], 13 | "main": "generators/app/index.js", 14 | "keywords": [ 15 | "redux", 16 | "react", 17 | "starter", 18 | "scaffolding", 19 | "create-react-app", 20 | "yeoman-generator" 21 | ], 22 | "devDependencies": { 23 | "extend": "^3.0.2", 24 | "growl": "^1.10.0", 25 | "just-extend": "^4.0.0", 26 | "lodash": "^4.17.11", 27 | "mocha": "^3.4.2", 28 | "np": "^5.0.3", 29 | "yeoman-assert": "^3.0.0", 30 | "yeoman-test": "^1.7.0" 31 | }, 32 | "dependencies": { 33 | "deep-extend": "^0.6.0", 34 | "to-case": "^2.0.0", 35 | "yeoman-generator": "^1.1.1", 36 | "yosay": "^2.0.0" 37 | }, 38 | "scripts": { 39 | "test": "mocha --bail test/*.spec.js", 40 | "release": "np" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/jonidelv/generator-create-redux-app/issues" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/jonidelv/generator-create-redux-app.git" 48 | }, 49 | "license": "MIT" 50 | } 51 | -------------------------------------------------------------------------------- /test/app.spec.js: -------------------------------------------------------------------------------- 1 | /* global describe before it */ 2 | 3 | const helpers = require('yeoman-test') 4 | const assert = require('yeoman-assert') 5 | const path = require('path') 6 | 7 | describe('generator-create-redux-app', () => { 8 | describe('Run yeoman generator-create-redux-app ', () => { 9 | before(() => 10 | helpers 11 | .run(path.join(__dirname, '../generators/app')) 12 | .withPrompts({ 13 | projectName: 'projectName', 14 | projectDescription: 'projectDescription', 15 | projectVersion: 'projectVersion', 16 | authorName: 'authorName', 17 | }) 18 | .toPromise() 19 | ) 20 | 21 | // test included files 22 | ;[ 23 | { 24 | desc: 'generates an index.html file', 25 | files: ['public/index.html'], 26 | }, 27 | { 28 | desc: 'generates a favicon.ico file', 29 | files: ['public/favicon.ico'], 30 | }, 31 | { 32 | desc: 'generates a manifest.json file', 33 | files: ['public/manifest.json'], 34 | }, 35 | { 36 | desc: 'generates a docs folder', 37 | files: ['docs'], 38 | }, 39 | { 40 | desc: 'generates a src folder', 41 | files: ['src'], 42 | }, 43 | { 44 | desc: 'generates an .editorconfig file', 45 | files: ['.editorconfig'], 46 | }, 47 | { 48 | desc: 'generates a .gitignore file', 49 | files: ['.gitignore'], 50 | }, 51 | { 52 | desc: 'generates a .eslintignore file', 53 | files: ['.eslintignore'], 54 | }, 55 | { 56 | desc: 'generates a .prettierrc file', 57 | files: ['.prettierrc'], 58 | }, 59 | { 60 | desc: 'generates a eslintrc.json file', 61 | files: ['.eslintrc.json'], 62 | }, 63 | { 64 | desc: 'generates a package.json file', 65 | files: ['package.json'], 66 | }, 67 | { 68 | desc: 'generates a README.md file', 69 | files: ['README.md'], 70 | }, 71 | ].forEach(fileCase => { 72 | it(fileCase.desc, () => { 73 | assert.file(fileCase.files) 74 | }) 75 | }) 76 | }) 77 | }) 78 | --------------------------------------------------------------------------------