├── .babelrc
├── .eslintrc
├── .gitignore
├── CONTRIBUTORS.md
├── README.md
├── app
├── index.jsx
└── main.css
├── karma.conf.js
├── package.json
├── tests
└── demo_test.js
├── webpack.config.js
└── webpack.parts.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "survivejs-kanban"
6 | ],
7 | "env": {
8 | "start": {
9 | "presets": [
10 | "react-hmre"
11 | ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended", "plugin:react/recommended"
4 | ],
5 | "parser": "babel-eslint",
6 | "env": {
7 | "browser": true,
8 | "node": true,
9 | "mocha": true
10 | },
11 | "plugins": [
12 | "react"
13 | ],
14 | "rules": {
15 | "quotes": [2, "single"],
16 | "no-debugger": 0,
17 | "no-console": 0,
18 | "no-unused-vars": 0,
19 | "react/display-name": 0,
20 | "react/prop-types": 0,
21 | "react/no-multi-comp": 0
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | coverage/
4 | .eslintcache
5 | npm-debug.log
6 |
7 |
--------------------------------------------------------------------------------
/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | ## Contributors
2 |
3 | * [François Constant](https://github.com/FrancoisConstant) - Added `npm-debug.log` to `.gitignore`.
4 | * [iparips](https://github.com/iparips) - Fixed dependency issue with `html-webpack-template`.
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-boilerplate - Boilerplate for "SurviveJS - React"
2 |
3 | See [SurviveJS - React](http://survivejs.com/react/introduction/) for the book.
4 |
5 | > If you use Vagrant or Cloud9, you'll need to tweak the development server port as instructed in **webpack.parts.js**.
6 |
7 | ## Getting Started
8 |
9 | 1. `npm i` - Install dependencies. This might take a while.
10 | 2. `npm start` - Run development build. If it doesn't start, make sure you aren't running anything else in the same port. In case you are on a Unix platform, you can try `PORT=3000 npm start`. It will pick up the port from the environment if it's set.
11 | 3. Surf to the port shown at terminal.
12 | 4. Start modifying the code. The browser should pick up the changes.
13 |
14 | ## Advanced Commands
15 |
16 | Beyond development, the boilerplate supports other tasks listed below:
17 |
18 | * `npm run build` - Generates a production build below `build/`. See the [Building with Webpack](http://survivejs.com/webpack/building-with-webpack/) part for more.
19 | * `npm run deploy` - Deploys the contents of the `build/` directory below the **gh-pages** branch.
20 | * `npm run test` - Runs `tests/` through Karma/Phantom/Mocha once.
21 | * `npm run test:tdd` - Runs `tests/` in a TDD mode (watches for changes and rebuilds).
22 | * `npm run test:lint` - Runs code through ESLint to spot code quality issues.
23 | * `npm run stats` - Generates Webpack build statistics. See the [Analyzing Build Statistics](http://survivejs.com/webpack/building-with-webpack/analyzing-build-statistics/) chapter.
24 |
--------------------------------------------------------------------------------
/app/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | if(process.env.NODE_ENV !== 'production') {
5 | React.Perf = require('react-addons-perf');
6 | }
7 |
8 | ReactDOM.render(
9 |
Hello world
,
10 | document.getElementById('app')
11 | );
12 |
--------------------------------------------------------------------------------
/app/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: cornsilk;
3 |
4 | font-family: sans-serif;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Reference: http://karma-runner.github.io/0.13/config/configuration-file.html
2 | module.exports = function karmaConfig (config) {
3 | config.set({
4 | frameworks: [
5 | // Reference: https://github.com/karma-runner/karma-mocha
6 | // Set framework to mocha
7 | 'mocha'
8 | ],
9 |
10 | reporters: [
11 | // Reference: https://github.com/mlex/karma-spec-reporter
12 | // Set reporter to print detailed results to console
13 | 'spec',
14 |
15 | // Reference: https://github.com/karma-runner/karma-coverage
16 | // Output code coverage files
17 | 'coverage'
18 | ],
19 |
20 | files: [
21 | // Reference: https://www.npmjs.com/package/phantomjs-polyfill
22 | // Needed because React.js requires bind and phantomjs does not support it
23 | 'node_modules/phantomjs-polyfill/bind-polyfill.js',
24 |
25 | // Grab all files in the tests directory that contain _test.
26 | 'tests/**/*_test.*'
27 | ],
28 |
29 | preprocessors: {
30 | // Reference: http://webpack.github.io/docs/testing.html
31 | // Reference: https://github.com/webpack/karma-webpack
32 | // Convert files with webpack and load sourcemaps
33 | 'tests/**/*_test.*': ['webpack', 'sourcemap'],
34 | 'app/**/*.*': 'coverage'
35 | },
36 |
37 | browsers: [
38 | // Run tests using PhantomJS
39 | 'PhantomJS'
40 | ],
41 |
42 | singleRun: true,
43 |
44 | // Configure code coverage reporter
45 | coverageReporter: {
46 | reporters: [
47 | // generates ./coverage/lcov.info
48 | {
49 | type: 'lcovonly',
50 | subdir: '.'
51 | },
52 | // generates ./coverage/coverage-final.json
53 | {
54 | type: 'json',
55 | subdir: '.'
56 | },
57 | // generates ./coverage/index.html
58 | {
59 | type: 'html',
60 | subdir: '.'
61 | }
62 | ]
63 | },
64 |
65 | // Test webpack config
66 | webpack: require('./webpack.config'),
67 |
68 | // Hide webpack build information from output
69 | webpackMiddleware: {
70 | noInfo: true
71 | }
72 | });
73 | };
74 |
75 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-boilerplate",
3 | "private": true,
4 | "version": "2.5.6",
5 | "description": "Boilerplate for [SurviveJS - React](http://survivejs.com/react/introduction/)",
6 | "scripts": {
7 | "stats": "webpack --profile --json > stats.json",
8 | "start": "webpack-dev-server",
9 | "deploy": "gh-pages -d build",
10 | "build": "webpack",
11 | "test": "karma start",
12 | "test:tdd": "npm run test -- --auto-watch --no-single-run",
13 | "test:lint": "eslint ./app ./tests --ext .js --ext .jsx --ignore-path .gitignore --ignore-pattern dist --cache"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "MIT",
18 | "devDependencies": {
19 | "babel-core": "^6.10.4",
20 | "babel-eslint": "^6.1.0",
21 | "babel-loader": "^6.2.4",
22 | "babel-preset-es2015": "^6.9.0",
23 | "babel-preset-react": "^6.5.0",
24 | "babel-preset-react-hmre": "^1.1.1",
25 | "babel-preset-survivejs-kanban": "^0.3.3",
26 | "clean-webpack-plugin": "^0.1.9",
27 | "css-loader": "^0.23.1",
28 | "eslint": "^3.13.1",
29 | "eslint-loader": "^1.3.0",
30 | "eslint-plugin-react": "^6.9.0",
31 | "expose-loader": "^0.7.1",
32 | "extract-text-webpack-plugin": "^1.0.1",
33 | "gh-pages": "^0.11.0",
34 | "html-webpack-plugin": "2.21.0",
35 | "html-webpack-template": "5.0.0",
36 | "isparta-loader": "^2.0.0",
37 | "karma": "^1.0.0",
38 | "karma-coverage": "^1.0.0",
39 | "karma-mocha": "^1.1.1",
40 | "karma-phantomjs-launcher": "^1.0.1",
41 | "karma-sourcemap-loader": "^0.3.7",
42 | "karma-spec-reporter": "0.0.26",
43 | "karma-webpack": "^1.7.0",
44 | "mocha": "^2.5.3",
45 | "npm-install-webpack-plugin": "4.0.4",
46 | "phantomjs-polyfill": "0.0.2",
47 | "phantomjs-prebuilt": "^2.1.7",
48 | "react-addons-perf": "^15.1.0",
49 | "react-addons-test-utils": "^15.1.0",
50 | "style-loader": "^0.13.1",
51 | "webpack": "^1.13.1",
52 | "webpack-dev-server": "^1.14.1",
53 | "webpack-merge": "^0.14.0",
54 | "webpack-validator": "^2.2.0"
55 | },
56 | "dependencies": {
57 | "react": "^15.1.0",
58 | "react-dom": "^15.1.0"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/demo_test.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert';
2 |
3 | describe('add', function() {
4 | it('adds', function() {
5 | assert.equal(1 + 1, 2);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const merge = require('webpack-merge');
3 | const validate = require('webpack-validator');
4 |
5 | const parts = require('./webpack.parts');
6 |
7 | const TARGET = process.env.npm_lifecycle_event;
8 | const ENABLE_POLLING = process.env.ENABLE_POLLING;
9 | const PATHS = {
10 | app: path.join(__dirname, 'app'),
11 | style: [
12 | path.join(__dirname, 'app', 'main.css')
13 | ],
14 | build: path.join(__dirname, 'build'),
15 | test: path.join(__dirname, 'tests')
16 | };
17 |
18 | process.env.BABEL_ENV = TARGET;
19 |
20 | const common = merge(
21 | {
22 | // Entry accepts a path or an object of entries.
23 | // We'll be using the latter form given it's
24 | // convenient with more complex configurations.
25 | entry: {
26 | app: PATHS.app
27 | },
28 | output: {
29 | path: PATHS.build,
30 | filename: '[name].js'
31 | },
32 | resolve: {
33 | extensions: ['', '.js', '.jsx']
34 | }
35 | },
36 | parts.indexTemplate({
37 | title: 'Kanban demo',
38 | appMountId: 'app'
39 | }),
40 | parts.loadJSX(PATHS.app),
41 | parts.lintJSX(PATHS.app)
42 | );
43 |
44 | var config;
45 |
46 | // Detect how npm is run and branch based on that
47 | switch(TARGET) {
48 | case 'build':
49 | case 'stats':
50 | config = merge(
51 | common,
52 | {
53 | devtool: 'source-map',
54 | entry: {
55 | style: PATHS.style
56 | },
57 | output: {
58 | // TODO: Set publicPath to match your GitHub project name
59 | // E.g., '/kanban-demo/'. Webpack will alter asset paths
60 | // based on this. You can even use an absolute path here
61 | // or even point to a CDN.
62 | //publicPath: ''
63 | path: PATHS.build,
64 | filename: '[name].[chunkhash].js',
65 | chunkFilename: '[chunkhash].js'
66 | }
67 | },
68 | parts.clean(PATHS.build),
69 | parts.setFreeVariable(
70 | 'process.env.NODE_ENV',
71 | 'production'
72 | ),
73 | parts.extractBundle({
74 | name: 'vendor',
75 | entries: ['react', 'react-dom']
76 | }),
77 | parts.minify(),
78 | parts.extractCSS(PATHS.style)
79 | );
80 | break;
81 | case 'test':
82 | case 'test:tdd':
83 | config = merge(
84 | common,
85 | {
86 | devtool: 'inline-source-map'
87 | },
88 | parts.loadIsparta(PATHS.app),
89 | parts.loadJSX(PATHS.test)
90 | );
91 | break;
92 | default:
93 | config = merge(
94 | common,
95 | {
96 | devtool: 'eval-source-map',
97 | entry: {
98 | style: PATHS.style
99 | }
100 | },
101 | parts.setupCSS(PATHS.style),
102 | parts.devServer({
103 | // Customize host/port here if needed
104 | host: process.env.HOST,
105 | port: process.env.PORT,
106 | poll: ENABLE_POLLING
107 | }),
108 | parts.enableReactPerformanceTools(),
109 | parts.npmInstall()
110 | );
111 | }
112 |
113 | module.exports = validate(config, {
114 | quiet: true
115 | });
116 |
--------------------------------------------------------------------------------
/webpack.parts.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const CleanWebpackPlugin = require('clean-webpack-plugin');
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const NpmInstallPlugin = require('npm-install-webpack-plugin');
6 |
7 | exports.indexTemplate = function(options) {
8 | return {
9 | plugins: [
10 | new HtmlWebpackPlugin({
11 | template: require('html-webpack-template'),
12 | title: options.title,
13 | appMountId: options.appMountId,
14 | inject: false
15 | })
16 | ]
17 | };
18 | }
19 |
20 | exports.loadJSX = function(include) {
21 | return {
22 | module: {
23 | loaders: [
24 | {
25 | test: /\.(js|jsx)$/,
26 | // Enable caching for extra performance
27 | loaders: ['babel?cacheDirectory'],
28 | include: include
29 | }
30 | ]
31 | }
32 | };
33 | }
34 |
35 | exports.loadIsparta = function(include) {
36 | return {
37 | module: {
38 | preLoaders: [
39 | {
40 | test: /\.(js|jsx)$/,
41 | loaders: ['isparta'],
42 | include: include
43 | }
44 | ]
45 | }
46 | };
47 | }
48 |
49 | exports.lintJSX = function(include) {
50 | return {
51 | module: {
52 | preLoaders: [
53 | {
54 | test: /\.(js|jsx)$/,
55 | loaders: ['eslint'],
56 | include: include
57 | }
58 | ]
59 | }
60 | };
61 | }
62 |
63 | exports.enableReactPerformanceTools = function() {
64 | return {
65 | module: {
66 | loaders: [
67 | {
68 | test: require.resolve('react'),
69 | loader: 'expose?React'
70 | }
71 | ]
72 | }
73 | };
74 | }
75 |
76 | exports.devServer = function(options) {
77 | const ret = {
78 | devServer: {
79 | // Enable history API fallback so HTML5 History API based
80 | // routing works. This is a good default that will come
81 | // in handy in more complicated setups.
82 | historyApiFallback: true,
83 |
84 | // Unlike the cli flag, this doesn't set
85 | // HotModuleReplacementPlugin!
86 | hot: true,
87 | inline: true,
88 |
89 | // Display only errors to reduce the amount of output.
90 | stats: 'errors-only',
91 |
92 | // Parse host and port from env to allow customization.
93 | //
94 | // If you use Vagrant or Cloud9, set
95 | // host: options.host || '0.0.0.0';
96 | //
97 | // 0.0.0.0 is available to all network devices
98 | // unlike default `localhost`.
99 | host: options.host, // Defaults to `localhost`
100 | port: options.port // Defaults to 8080
101 | },
102 | plugins: [
103 | // Enable multi-pass compilation for enhanced performance
104 | // in larger projects. Good default.
105 | new webpack.HotModuleReplacementPlugin({
106 | multiStep: true
107 | })
108 | ]
109 | };
110 |
111 | if(options.poll) {
112 | ret.watchOptions = {
113 | // Delay the rebuild after the first change
114 | aggregateTimeout: 300,
115 | // Poll using interval (in ms, accepts boolean too)
116 | poll: 1000
117 | };
118 | }
119 |
120 | return ret;
121 | }
122 |
123 | exports.setupCSS = function(paths) {
124 | return {
125 | module: {
126 | loaders: [
127 | {
128 | test: /\.css$/,
129 | loaders: ['style', 'css'],
130 | include: paths
131 | }
132 | ]
133 | }
134 | };
135 | }
136 |
137 | exports.minify = function() {
138 | return {
139 | plugins: [
140 | new webpack.optimize.UglifyJsPlugin({
141 | compress: {
142 | warnings: false
143 | }
144 | })
145 | ]
146 | };
147 | }
148 |
149 | exports.setFreeVariable = function(key, value) {
150 | const env = {};
151 | env[key] = JSON.stringify(value);
152 |
153 | return {
154 | plugins: [
155 | new webpack.DefinePlugin(env)
156 | ]
157 | };
158 | }
159 |
160 | exports.extractBundle = function(options) {
161 | const entry = {};
162 | entry[options.name] = options.entries;
163 |
164 | return {
165 | // Define an entry point needed for splitting.
166 | entry: entry,
167 | plugins: [
168 | // Extract bundle and manifest files. Manifest is
169 | // needed for reliable caching.
170 | new webpack.optimize.CommonsChunkPlugin({
171 | names: [options.name, 'manifest'],
172 |
173 | // options.name modules only
174 | minChunks: Infinity
175 | })
176 | ]
177 | };
178 | }
179 |
180 | exports.clean = function(path) {
181 | return {
182 | plugins: [
183 | new CleanWebpackPlugin([path], {
184 | root: process.cwd()
185 | })
186 | ]
187 | };
188 | }
189 |
190 | exports.extractCSS = function(paths) {
191 | return {
192 | module: {
193 | loaders: [
194 | // Extract CSS during build
195 | {
196 | test: /\.css$/,
197 | loader: ExtractTextPlugin.extract('style', 'css'),
198 | include: paths
199 | }
200 | ]
201 | },
202 | plugins: [
203 | // Output extracted CSS to a file
204 | new ExtractTextPlugin('[name].[chunkhash].css')
205 | ]
206 | };
207 | }
208 |
209 | exports.npmInstall = function(options) {
210 | options = options || {};
211 |
212 | return {
213 | plugins: [
214 | new NpmInstallPlugin(options)
215 | ]
216 | };
217 | }
218 |
--------------------------------------------------------------------------------