├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .idea ├── .name ├── encodings.xml ├── hapi-react-starter-kit.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── jsLibraryMappings.xml ├── libraries │ └── hapi_react_starter_kit_node_modules.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── webResources.xml ├── README.md ├── client ├── public │ ├── assets │ │ └── logo_small.png │ └── index.html └── src │ ├── Counter.js │ ├── main.js │ └── styles │ └── styles.css ├── devServerHapi.js ├── package.json ├── webpack.config.common.js ├── webpack.config.dev.js └── webpack.config.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-0"], 3 | "env": { 4 | "development": { 5 | "presets": ["react-hmre"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | devServer.js 2 | devServerHapi.js 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true 5 | }, 6 | "parser": "babel-eslint", 7 | "rules": { 8 | "comma-dangle": [ 9 | 2, 10 | "never" 11 | ], 12 | "eol-last": 0, 13 | "id-length": 0, 14 | "key-spacing": [ 15 | 0, 16 | { 17 | "align": "value" 18 | } 19 | ], 20 | "jsx-quotes": [ 21 | 2, 22 | "prefer-single" 23 | ], 24 | "no-else-return": 0, 25 | "spaced-comment": 0, 26 | "no-console": 0, 27 | "no-trailing-spaces": 0, 28 | "indent": [ 29 | 2, 30 | 2, 31 | { 32 | "SwitchCase": 1 33 | } 34 | ], 35 | "no-undef": [ 36 | 2, 37 | { 38 | "typeof": false 39 | } 40 | ], 41 | "react/jsx-uses-react": 2, 42 | "react/jsx-uses-vars": 2, 43 | "react/react-in-jsx-scope": 2, 44 | "react/wrap-multilines": 0 45 | }, 46 | "plugins": [ 47 | "react" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | _dist/ 3 | dist/ 4 | .idea/workspace.xml 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | hapi-react-starter-kit -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/hapi-react-starter-kit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/libraries/hapi_react_starter_kit_node_modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Starter Kit (WORK IN PROGRESS) 2 | 3 | *Just another React Starter Kit by [Nils Hartmann](https://github.com/nilshartmann) and [Oliver Zeigermann](https://github.com/DJCordhose).* 4 | 5 | Our Starter Kit is based on the [react-transform-boilerplate](https://github.com/gaearon/react-transform-boilerplate) but differs 6 | in some points: 7 | * Hapi as Webserver (instead of Express) 8 | * Folder structure differs (it's planned to add server-side code as well so we wanted to have an explicit `client` folder) 9 | * Babel is pre-configured with stage-0 preset 10 | * ESlint runs with eslint-loader from webpack 11 | * ESlint uses config-airbnb plus personal config modifications 12 | * CSS Loader for webpack 13 | 14 | # How-to 15 | 16 | 1. Clone the repository 17 | 2. npm install 18 | 3. npm start 19 | 20 | Open your browser at `http://localhost:3000` to see the simple counter application. Modify the JavaScript- or CSS-files and they are re-compiled and re-loaded in the browser automatically. The state of the React component will not be lost during reload. 21 | 22 | ## Production build 23 | 24 | To create a production (minified, without hot loader) version of your application, just run: 25 | ``` 26 | npm run build:prod 27 | ``` 28 | 29 | The output goes to `public/dist/bundle.js`. You can point a webserver to `client/public` (i.e Pythons SimpleHTTPServer) and then open `index.html` to test your application 30 | 31 | # Modules 32 | 33 | Our starter kit contains the following modules: 34 | 35 | ## Babel 6 modules 36 | * `babel-core` Since Version 6 Babel does not contain any transformations (what Babel should do with your code, e.g. compiling JSX code to ES5). Instead the transformations have to be installed separately either as **plug-ins** or as group of plug-ins, called **presets** 37 | A babel **preset** contains one or more babel **plug-ins**. A preset also can contain the configuration of the contained plug-ins, making it very easy to add (complex) plug-ins and configurations to your own project 38 | * `babel-preset-es2015`: All transformations needed to compile ES6 to ES5 code 39 | * `babel-preset-react`: All transformations needed to compile React-specific (JSX) code 40 | * `babel-preset-stage-0` Support for TC39 state-0 features ("ES7") 41 | 42 | ## Webpack-related modules 43 | Webpack is a module bundler. It creates one or more target assets from a variety of input modules. Among others it can handle JavaScript and CSS-Files. 44 | The actual tasks, Webpack should perform, are implemented and configured with **loaders** 45 | 46 | We're using the following loaders: 47 | * `css-loader` and `style-loader`: process CSS files. The processed files can be imported from JavaScript source code. With this starter kit CSS files are also hotloaded, e.g. it's not necessary to reload the Browser page after modifying a CSS file. 48 | * `eslint-loader`: runs ESlint from webpack. 49 | * `babel-loader` Webpack **loader** that runs Babel from webpack 50 | 51 | ## Hapi modules 52 | We like to use Hapi as webserver in our projects. So we want Hapi to work together with webpack. We're using the following packages: 53 | 54 | * `hapi`: The actual Hapi webserver 55 | * `inert`: Handler for static files for Hapi (needed to server index.html file) 56 | * `hapi-webpack-plugin`: Embedds webpack middleware in Hapi. Supports hot reloading. 57 | 58 | Note: if you're using Hapi in your application (beside using it for the hotloading), move `hapi` (and `inert` if needed) 59 | from `devDependencies` to `dependencies` in your package.json file. 60 | 61 | ## Hotloading-related modules 62 | For React Hotloading we use the following modules: 63 | * `babel-plugin-react-transform` A Plug-in for babel, that acts as a "holder" for Babel transformations of React code. The Plug-in uses **Transforms** that can be configured by the user (see below) 64 | * `react-transform-hmr`: A **transform** enabling hot reloading and hot api replacement of react components 65 | * `react-transform-catch-errors`: A **transform** that renders compilation errors in components directly as an error message inside the affected component (in addition to the console where babel runs). Needs `webpack-hot-middleware` 66 | * `babel-preset-react-hmre`: A babel **preset** that includes the `babel-plugin-react-transform` along with two trasnforms (`react-transform-hmr` and `react-transform-catch-errors`), making it easy to embedd this plug-ins in own projects (just by updating .babelrc file) 67 | * `webpack-hot-middleware` webpack-dev-server replacement. Allows embedding webpack in your own Webserver while still providing hotloading features. 68 | * `eventsource-polyfill` a polyfill needed for IE to support hot loading 69 | 70 | ## Other modules 71 | * `rimraf`: cross-platform `rm -rf` node command 72 | 73 | 74 | # TODO 75 | 76 | * improve documentation 77 | * build for serverside code 78 | * better configuration (esp for paths) 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /client/public/assets/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nilshartmann/hapi-react-starter-kit/ce847453aae01462c220d6a313664812679e9969/client/public/assets/logo_small.png -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hapi React Starter Kit 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /client/src/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Counter extends React.Component { 4 | constructor() { 5 | super(); 6 | 7 | this.increment = ::this.increment; 8 | 9 | this.state = { 10 | count: 0 11 | }; 12 | } 13 | 14 | increment() { 15 | const { count } = this.state; 16 | this.setState({ 17 | count: count + 1 18 | }); 19 | } 20 | 21 | render() { 22 | return
23 | 24 |

Current: {this.state.count}

25 |
; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './styles/styles.css'; 5 | 6 | import Counter from './Counter'; 7 | 8 | ReactDOM.render(, document.getElementById('mount')); 9 | -------------------------------------------------------------------------------- /client/src/styles/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #6699cc; 3 | } 4 | -------------------------------------------------------------------------------- /devServerHapi.js: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------- 2 | // --- Nils Hartmann | http://nilshartmann.net --- 3 | // --------------------------------------------------------------------------- 4 | /** 5 | * Import dependencies 6 | */ 7 | const path = require('path'); 8 | const Server = require('hapi').Server; 9 | const Webpack = require('webpack'); 10 | const WebpackPlugin = require('hapi-webpack-plugin'); 11 | const config = require('./webpack.config.dev'); 12 | const Inert = require('inert'); 13 | 14 | /** 15 | * Create server 16 | */ 17 | const server = new Server(); 18 | server.connection({port: 3000}); 19 | server.register(Inert, () => { 20 | }); 21 | 22 | const publicPath = path.join(__dirname, 'client/public/'); 23 | console.log("PublicPath: " + publicPath); 24 | 25 | server.route({ 26 | method: 'GET', 27 | path: '/{param*}', 28 | handler: { 29 | directory: { 30 | path: publicPath 31 | } 32 | } 33 | }); 34 | server.ext('onPreResponse', (request, reply) => { 35 | if (request.response.isBoom && request.response.output.statusCode === 404) { 36 | // use index.html as fallback in case of 404 errors (esp to enable React Router with BrowserHistory) 37 | // (something like Webpack Devserver's --history-api-fallback option) 38 | return reply.file(path.join(__dirname, 'client/public/index.html')); 39 | } 40 | 41 | return reply.continue(); 42 | }); 43 | /** 44 | * Define constants 45 | */ 46 | const compiler = new Webpack(config); 47 | 48 | const assets = { 49 | noInfo: true, 50 | publicPath: config.output.publicPath 51 | }; 52 | 53 | const hot = {}; 54 | 55 | /** 56 | * Register plugin and start server 57 | */ 58 | server.register({ 59 | register: WebpackPlugin, 60 | options: {compiler, assets, hot} 61 | }, 62 | error => { 63 | if (error) { 64 | return console.error(error); 65 | } 66 | server.start(() => console.log('Server running at:', server.info.uri)); 67 | }); 68 | 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-react-starter-kit", 3 | "version": "0.0.1", 4 | "description": "React Starter Kit with Hot Reloading", 5 | "main": "index.js", 6 | "repository": "https://github.com/nilshartmann/hapi-react-starter-kit", 7 | "scripts": { 8 | "start": "node devServerHapi.js", 9 | "build:clean": "rimraf client/public/dist", 10 | "build:prod": "npm run build:clean && cross-env NODE_ENV=production webpack --config webpack.config.prod.js" 11 | }, 12 | "author": "Nils Hartmann, Oliver Zeigermann", 13 | "license": "ISC", 14 | "dependencies": { 15 | "react": "^15.0.0", 16 | "react-dom": "^15.0.2" 17 | }, 18 | "devDependencies": { 19 | "babel-core": "^6.4.0", 20 | "babel-eslint": "^5.0.0-beta6", 21 | "babel-loader": "^6.2.1", 22 | "babel-preset-es2015": "^6.3.13", 23 | "babel-preset-react": "^6.3.13", 24 | "babel-preset-react-hmre": "^1.0.1", 25 | "babel-preset-stage-0": "^6.3.13", 26 | "css-loader": "^0.23.1", 27 | "cross-env": "^1.0.7", 28 | "eslint": "^1.10.3", 29 | "eslint-config-airbnb": "^3.1.0", 30 | "eslint-loader": "^1.2.0", 31 | "eslint-plugin-react": "^3.14.0", 32 | "eventsource-polyfill": "^0.9.6", 33 | "hapi": "^12.1.0", 34 | "hapi-webpack-plugin": "^1.3.0", 35 | "inert": "^3.2.0", 36 | "rimraf": "^2.5.0", 37 | "style-loader": "^0.13.0", 38 | "webpack": "^1.12.10", 39 | "webpack-dev-middleware": "^1.4.0", 40 | "webpack-hot-middleware": "^2.6.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webpack.config.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: [ 5 | './client/src/main.js' 6 | ], 7 | resolve: { 8 | extensions: [ "", ".js", ".jsx"] 9 | }, 10 | output: { 11 | path: path.join(__dirname, 'client/public/dist'), 12 | filename: 'bundle.js', 13 | publicPath: '/dist' 14 | }, 15 | module: { 16 | loaders: [ 17 | { 18 | test: /\.jsx?/, 19 | loaders: ['babel'], 20 | include: path.join(__dirname, 'client/src') 21 | }, 22 | { 23 | test: /\.jsx?$/, 24 | include: path.join(__dirname, 'client/src'), 25 | loader: 'eslint' 26 | }, 27 | { 28 | test: /\.css$/, 29 | loader: 'style!css' 30 | } 31 | ] 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const common = require('./webpack.config.common'); 3 | 4 | const devConfig = Object.assign({}, common, { 5 | devtool: 'cheap-module-eval-source-map', 6 | entry: [ 7 | 'eventsource-polyfill', // necessary for hot reloading with IE 8 | 'webpack-hot-middleware/client' 9 | ].concat(common.entry), 10 | plugins: [ 11 | new webpack.HotModuleReplacementPlugin(), 12 | new webpack.NoErrorsPlugin() 13 | ] 14 | }); 15 | 16 | module.exports = devConfig; 17 | -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const common = require('./webpack.config.common'); 3 | 4 | const prodConfig = Object.assign({}, common, { 5 | plugins: [ 6 | new webpack.optimize.OccurenceOrderPlugin(), 7 | new webpack.DefinePlugin({ 8 | 'process.env': { 9 | NODE_ENV: JSON.stringify('production') 10 | } 11 | }), 12 | new webpack.optimize.UglifyJsPlugin({ 13 | compressor: { 14 | warnings: false 15 | } 16 | }) 17 | ] 18 | }); 19 | 20 | module.exports = prodConfig; 21 | --------------------------------------------------------------------------------