├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── README.md ├── cfg ├── base.js ├── defaults.js ├── dev.js ├── dist.js └── test.js ├── jest-config.json ├── package.json ├── server.js ├── src ├── core │ ├── main.component.jsx │ ├── notFound.component.jsx │ └── styles │ │ ├── base.css │ │ ├── core.css │ │ └── someModule.scss ├── favicon.ico ├── feat1 │ ├── feat1.actions.js │ ├── feat1.container.jsx │ ├── feat1.reducer.js │ ├── feat1.styles.css │ └── test │ │ ├── feat1.action.test.js │ │ └── feat1.reducer.test.js ├── index.html ├── index.js └── shared │ ├── constants.js │ ├── index.reducer.js │ ├── index.store.js │ └── utils.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0", 5 | "react" 6 | ], 7 | "plugins": [ 8 | "transform-runtime" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "rules": { 4 | "block-scoped-var": 1, 5 | "brace-style": [ 6 | 1, 7 | "1tbs" 8 | ], 9 | "camelcase": [ 10 | 1, 11 | { 12 | "properties": "always" 13 | } 14 | ], 15 | "comma-dangle": 1, 16 | "comma-spacing": [ 17 | 1, 18 | { 19 | "before": false, 20 | "after": true 21 | } 22 | ], 23 | "comma-style": [ 24 | 1, 25 | "last" 26 | ], 27 | "curly": [ 28 | 2 29 | ], 30 | "eol-last": 0, 31 | "generator-star-spacing": [ 32 | 1, 33 | "after" 34 | ], 35 | "guard-for-in": 1, 36 | "indent": [ 37 | 1, 38 | 4, 39 | { 40 | "SwitchCase": 1 41 | } 42 | ], 43 | "no-console": 0, 44 | "no-eq-null": 2, 45 | "no-extra-parens": 0, 46 | "no-lonely-if": 1, 47 | "no-mixed-spaces-and-tabs": [ 48 | 1, 49 | "smart-tabs" 50 | ], 51 | "no-spaced-func": 1, 52 | "no-throw-literal": 2, 53 | "no-trailing-spaces": 0, 54 | "no-underscore-dangle": 0, 55 | "no-undefined": 2, 56 | "no-unused-vars": 1, 57 | "no-var": 1, 58 | "quotes": [ 59 | 0, 60 | "double", 61 | "avoid-escape" 62 | ], 63 | "radix": 1, 64 | "semi-spacing": [ 65 | 1, 66 | { 67 | "before": false, 68 | "after": true 69 | } 70 | ], 71 | "vars-on-top": 1, 72 | "wrap-iife": 1, 73 | "react/display-name": 0, 74 | "react/jsx-boolean-value": 1, 75 | "jsx-quotes": 1, 76 | "react/jsx-no-undef": 1, 77 | "react/jsx-sort-props": 1, 78 | "react/jsx-uses-react": 1, 79 | "react/jsx-uses-vars": 1, 80 | "react/no-did-mount-set-state": 1, 81 | "react/no-did-update-set-state": 1, 82 | "react/no-multi-comp": 1, 83 | "react/no-unknown-property": 1, 84 | "react/prop-types": 1, 85 | "react/react-in-jsx-scope": 1, 86 | "react/self-closing-comp": 1, 87 | "react/sort-comp": 1, 88 | "react/jsx-wrap-multilines": 1 89 | }, 90 | "env": { 91 | "es6": true, 92 | "browser": true, 93 | "node": true, 94 | "mocha": true 95 | }, 96 | "extends": "eslint:recommended", 97 | "ecmaFeatures": { 98 | "experimentalObjectRestSpread": true, 99 | "modules": true, 100 | "arrowFunctions": true, 101 | "blockBindings": true, 102 | "classes": true, 103 | "defaultParams": true, 104 | "forOf": true, 105 | "generators": true, 106 | "superInFunctions": true, 107 | "templateStrings": true, 108 | "jsx": true 109 | }, 110 | "plugins": [ 111 | "react" 112 | ], 113 | "parserOptions": { 114 | "sourceType": "module" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # node-waf configuration 17 | .lock-wscript 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 24 | node_modules 25 | 26 | .idea 27 | 28 | # Built Dependencies 29 | dist/ 30 | 31 | #Scripted generated marathon.json 32 | marathon.json 33 | 34 | *.vscode/ 35 | .DS_Store 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Seed React project 2 | 3 | > At the moment this repository is deprecated, although you can still use as reference the architecture and some parts of the code. I advocate to start using [React Create App](https://github.com/facebookincubator/create-react-app). Thanks! 4 | 5 | Yet another React boilerplate to rapidly and easily start a React stack project from scratch without the tedious configuration work. 6 | 7 | ### Main Modules/Feats 8 | - React 9 | - Hot module reload (https://github.com/gaearon/react-hot-loader) 10 | - Production performance build (http://stackoverflow.com/questions/22118915/how-to-turn-on-off-reactjs-development-mode) 11 | - Redux (https://github.com/reactjs/redux) 12 | - Redux-logger (https://github.com/evgenyrodionov/redux-logger) 13 | - Redux-thunk (https://github.com/gaearon/redux-thunk) 14 | - React Router (https://github.com/ReactTraining/react-router) 15 | - Babel/ES6/7 (https://github.com/babel/babel) 16 | - Webpack (https://github.com/webpack/webpack) 17 | - Webpack dashboard (https://github.com/FormidableLabs/webpack-dashboard) 18 | - CSS modules (https://github.com/css-modules/css-modules) 19 | - PostCSS or good ol SASS (https://github.com/postcss/postcss) 20 | - Autoprefixer (https://github.com/postcss/autoprefixer) 21 | - PostCSS-simple-var (https://github.com/postcss/postcss-simple-vars) 22 | - PostCSS-partial-import (postcss-partial-import) 23 | - Unit tests/Coverage 24 | - Jest (https://facebook.github.io/jest/) 25 | - and React/Redux modules for unit testing 26 | 27 | ### Project Structure 28 | 29 | Redux docs example of folder structure, it's good for todo examples but not for real world application so **(DO NOT FOLLOW THIS PATTERN)**. 30 | ``` 31 | | src 32 | | actions 33 | | all actions files 34 | | components 35 | | all components files 36 | | container 37 | | all containers files 38 | | constants 39 | | often one contant file? 40 | | reducers 41 | | all reducers files 42 | | store 43 | | one store file 44 | | 45 | ``` 46 | 47 | After building/architecting a couple of React/Redux applications I have found that the structure defined in this repo is the most productive and scalable so far. But as everything there is always space for improvements so please let me know your ideas. **(SUGGESTED PATTERN)** 48 | ``` 49 | | src 50 | | index.html 51 | | index.js (root route file) 52 | | shared 53 | | index.reducer.js 54 | | index.store.js 55 | | constants.js 56 | | utils.js 57 | | featureBla 58 | | featureBla.actions.js 59 | | featureBla.container.jsx 60 | | featureBla.reducer.js 61 | | featureBla.styles.css 62 | | test 63 | | featureBla.actions.test.js 64 | | featureBla.reducer.test.js 65 | | core (core UI components) 66 | | core.css 67 | | main.component.jsx 68 | | notFound.component.jsx 69 | | styles 70 | | base.css 71 | | grid.css 72 | | 73 | ``` 74 | -------------------------------------------------------------------------------- /cfg/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 5 | let defaultSettings = require('./defaults'); 6 | const srcPath = path.join(__dirname, '/../src'); 7 | const port = 8000; 8 | const publicPath = '/assets/'; 9 | 10 | // Additional npm or bower modules to include in builds 11 | // Add all foreign plugins you may need into this array 12 | // @example: 13 | // let npmBase = path.join(__dirname, '../node_modules'); 14 | // let additionalPaths = [ path.join(npmBase, 'react-bootstrap') ]; 15 | let additionalPaths = []; 16 | 17 | module.exports = { 18 | additionalPaths: additionalPaths, 19 | port: defaultSettings.port, 20 | debug: true, 21 | devtool: 'eval', 22 | output: { 23 | path: path.join(__dirname, '/../dist/assets'), 24 | filename: 'app.js', 25 | publicPath: defaultSettings.publicPath 26 | }, 27 | devServer: { 28 | contentBase: defaultSettings.srcPath, 29 | historyApiFallback: true, 30 | hot: true, 31 | port: port, 32 | publicPath: defaultSettings.publicPath, 33 | noInfo: false, 34 | quiet: true 35 | }, 36 | module: { 37 | preLoaders: [ 38 | { 39 | test: /\.(js|jsx)$/, 40 | include: srcPath, 41 | loader: 'eslint-loader' 42 | } 43 | ], 44 | loaders: [ 45 | { 46 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, 47 | loader: 'file-loader' 48 | }, 49 | { 50 | test: /\.html$/, 51 | loader: 'file?name=[name].[ext]' 52 | }, 53 | { 54 | test: /\.css$/, 55 | loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]-[local]___[hash:base64:5]!postcss' 56 | }, 57 | { 58 | test: /\.scss$/, 59 | loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]-[local]___[hash:base64:5]!sass' 60 | } 61 | ] 62 | }, 63 | resolve: { 64 | extensions: ['', '.js', '.jsx'], 65 | alias: { 66 | config: `${srcPath}/config/` + process.env.REACT_WEBPACK_ENV 67 | } 68 | }, 69 | postcss: function() { 70 | return [ 71 | require('autoprefixer'), 72 | require('postcss-simple-vars'), 73 | require('postcss-partial-import') 74 | ]; 75 | }, 76 | plugins: [ 77 | new ExtractTextPlugin(`./dist/assets/styles.css`) 78 | ] 79 | }; 80 | -------------------------------------------------------------------------------- /cfg/defaults.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const srcPath = path.join(__dirname, '/../src'); 5 | const port = 8000; 6 | 7 | module.exports = { 8 | srcPath: srcPath, 9 | publicPath: '/assets/', 10 | port: port 11 | }; 12 | -------------------------------------------------------------------------------- /cfg/dev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let path = require('path'); 4 | let webpack = require('webpack'); 5 | let Dashboard = require('webpack-dashboard'); 6 | let DashboardPlugin = require('webpack-dashboard/plugin'); 7 | let dashboard = new Dashboard(); 8 | let baseConfig = require('./base'); 9 | let defaultSettings = require('./defaults'); 10 | 11 | let config = Object.assign({}, baseConfig, { 12 | entry: [ 13 | 'webpack-dev-server/client?http://127.0.0.1:' + defaultSettings.port, 14 | 'webpack/hot/only-dev-server', 15 | './src/index' 16 | ], 17 | cache: true, 18 | devtool: 'eval-source-map', 19 | plugins: [ 20 | new DashboardPlugin(dashboard.setData), 21 | new webpack.HotModuleReplacementPlugin(), 22 | new webpack.DefinePlugin({ 23 | 'process.env.NODE_ENV': '"development"' 24 | }), 25 | new webpack.NoErrorsPlugin() 26 | ] 27 | }); 28 | 29 | // Add needed loaders to the defaults here 30 | config.module.loaders.push( 31 | { 32 | test: /\.(js|jsx)$/, 33 | loader: 'react-hot!babel-loader', 34 | include: [].concat(config.additionalPaths, [path.join(__dirname, '/../src')]) 35 | } 36 | ); 37 | 38 | 39 | module.exports = config; 40 | -------------------------------------------------------------------------------- /cfg/dist.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | 6 | const baseConfig = require('./base'); 7 | const defaultSettings = require('./defaults'); 8 | 9 | let config = Object.assign({}, baseConfig, { 10 | entry: path.join(__dirname, '../src/index'), 11 | cache: false, 12 | plugins: [ 13 | new webpack.optimize.DedupePlugin(), 14 | new webpack.DefinePlugin({ 15 | 'process.env.NODE_ENV': '"production"' 16 | }), 17 | new webpack.optimize.UglifyJsPlugin(), 18 | new webpack.optimize.OccurenceOrderPlugin(), 19 | new webpack.optimize.AggressiveMergingPlugin(), 20 | new webpack.NoErrorsPlugin() 21 | ] 22 | }); 23 | 24 | // Add needed loaders to the defaults here 25 | config.module.loaders.push({ 26 | test: /\.(js|jsx)$/, 27 | loader: 'babel', 28 | include: [].concat(config.additionalPaths, [defaultSettings.srcPath]) 29 | }); 30 | 31 | module.exports = config; 32 | -------------------------------------------------------------------------------- /cfg/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let path = require('path'); 4 | let srcPath = path.join(__dirname, '/../src/'); 5 | 6 | let baseConfig = require('./base'); 7 | 8 | module.exports = { 9 | devtool: 'eval', 10 | module: { 11 | preLoaders: [ 12 | { 13 | test: /\.(js|jsx)$/, 14 | loader: 'isparta-instrumenter-loader', 15 | include: [ 16 | path.join(__dirname, '/../src') 17 | ] 18 | } 19 | ], 20 | loaders: [ 21 | { 22 | test: /\.(png|jpg|gif|woff|woff2|css|sass|scss|less|styl)$/, 23 | loader: 'null-loader' 24 | }, 25 | { 26 | test: /\.(js|jsx)$/, 27 | loader: 'babel-loader', 28 | include: [].concat( 29 | baseConfig.additionalPaths, 30 | [ 31 | path.join(__dirname, '/../src'), 32 | path.join(__dirname, '/../test') 33 | ] 34 | ) 35 | } 36 | ] 37 | }, 38 | resolve: { 39 | extensions: [ '', '.js', '.jsx' ], 40 | alias: { 41 | config: srcPath + 'config/' + process.env.REACT_WEBPACK_ENV 42 | } 43 | }, 44 | plugins: [ 45 | 46 | ] 47 | }; 48 | -------------------------------------------------------------------------------- /jest-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testEnvironment": "node", 3 | "testPathDirs": [ 4 | "./src" 5 | ], 6 | "testRegex": ".*.test.js", 7 | "collectCoverageFrom": [ 8 | "**/*.js", 9 | "!src/index.js", 10 | "!src/shared/constants.js", 11 | "!src/shared/index.reducer.js", 12 | "!src/shared/index.store.js", 13 | "!**/node_modules/**" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seed-react-project", 3 | "private": true, 4 | "version": "0.0.1", 5 | "description": "Seed react architecture to help bootstrap new apps easily and faster", 6 | "main": "", 7 | "scripts": { 8 | "start": "node server.js --env=development", 9 | "test": "./node_modules/.bin/jest --config ./jest-config.json", 10 | "test:watch": "./node_modules/.bin/jest --watch --coverage --config ./jest-config.json", 11 | "serve": "node server.js --env=development", 12 | "serve:dist": "node server.js --env=production", 13 | "dist": "npm run copy & webpack --env=production", 14 | "lint": "eslint ./src", 15 | "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist", 16 | "clean": "rimraf dist/*", 17 | "release:major": "npm version major && npm publish && git push --follow-tags", 18 | "release:minor": "npm version minor && npm publish && git push --follow-tags", 19 | "release:patch": "npm version patch && npm publish && git push --follow-tags" 20 | }, 21 | "repository": "", 22 | "keywords": [], 23 | "author": "Michael Lancaster", 24 | "devDependencies": { 25 | "autoprefixer": "^6.5.4", 26 | "babel-core": "^6.20.0", 27 | "babel-eslint": "^7.1.0", 28 | "babel-jest": "^18.0.0", 29 | "babel-loader": "^6.2.9", 30 | "babel-plugin-react-css-modules": "^1.1.0", 31 | "babel-plugin-transform-runtime": "^6.5.2", 32 | "babel-polyfill": "^6.3.14", 33 | "babel-preset-es2015": "^6.0.15", 34 | "babel-preset-react": "^6.0.15", 35 | "babel-preset-stage-0": "^6.5.0", 36 | "babel-runtime": "^6.20.0", 37 | "bower-webpack-plugin": "^0.1.9", 38 | "chai": "^3.2.0", 39 | "copyfiles": "^1.0.0", 40 | "core-js": "^2.4.1", 41 | "css-loader": "^0.25.0", 42 | "eslint": "^3.0.0", 43 | "eslint-loader": "^1.0.0", 44 | "eslint-plugin-react": "^6.6.0", 45 | "extract-text-webpack-plugin": "^1.0.1", 46 | "fetch-mock": "^5.1.1", 47 | "file-loader": "^0.9.0", 48 | "glob": "^7.0.0", 49 | "isparta-instrumenter-loader": "^1.0.0", 50 | "jest": "^18.0.0", 51 | "minimist": "^1.2.0", 52 | "mocha": "^3.0.0", 53 | "node-sass": "^4.4.0", 54 | "null-loader": "^0.1.1", 55 | "open": "0.0.5", 56 | "phantomjs-prebuilt": "^2.0.0", 57 | "postcss-loader": "^1.2.1", 58 | "postcss-partial-import": "^2.1.0", 59 | "postcss-simple-vars": "^3.0.0", 60 | "react-addons-test-utils": "^15.0.0", 61 | "react-hot-loader": "^1.2.9", 62 | "redux-logger": "^2.6.1", 63 | "redux-mock-store": "^1.1.4", 64 | "rimraf": "^2.4.3", 65 | "sass-loader": "^4.1.1", 66 | "sinon": "^1.17.5", 67 | "style-loader": "^0.13.0", 68 | "url-loader": "^0.5.6", 69 | "webpack": "^1.14.0", 70 | "webpack-dashboard": "^0.2.0", 71 | "webpack-dev-server": "^1.16.2" 72 | }, 73 | "dependencies": { 74 | "lodash": "^4.15.0", 75 | "normalize.css": "^5.0.0", 76 | "react": "^15.0.0", 77 | "react-dom": "^15.0.0", 78 | "react-redux": "^4.4.5", 79 | "react-router": "^3.0.0", 80 | "redux": "^3.5.2", 81 | "redux-thunk": "^2.1.0", 82 | "whatwg-fetch": "^1.0.0" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /*eslint no-console:0 */ 2 | 'use strict'; 3 | require('core-js/fn/object/assign'); 4 | const webpack = require('webpack'); 5 | const WebpackDevServer = require('webpack-dev-server'); 6 | const config = require('./webpack.config'); 7 | const open = require('open'); 8 | new WebpackDevServer(webpack(config), config.devServer) 9 | .listen(config.port, 'localhost', (err) => { 10 | if (err) { 11 | console.log(err); 12 | } 13 | 14 | console.log('Listening at localhost:' + config.port); 15 | console.log('Opening your system browser...'); 16 | open('http://localhost:' + config.port); 17 | }); 18 | -------------------------------------------------------------------------------- /src/core/main.component.jsx: -------------------------------------------------------------------------------- 1 | require('normalize.css'); 2 | 3 | import { 4 | moduleTitle 5 | } from './styles/someModule.scss'; 6 | import styles from './styles/core.css'; 7 | import React, { Component } from 'react'; 8 | 9 | class Main extends Component { 10 | render() { 11 | return ( 12 |
the counter is: {counter}
21 | 22 |