diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..506a504
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{json,js}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..2283fae
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,16 @@
+{
+ "env": {
+ "node": true,
+ "mocha": true,
+ "es6": true
+ },
+ "globals": {
+
+ },
+ "rules": {
+ "quotes": [2, "single"]
+ "semi": [2, "never"],
+ "curly": [2, "multi-line"],
+ "no-underscore-dangle": 0
+ }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..176a458
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..123ae94
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
+node_modules
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ee5ee23
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 C. T. Lin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1850532
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# electron-react-boilerplate
+
+> WIP
diff --git a/app/actions/.gitkeep b/app/actions/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/app.js b/app/app.js
new file mode 100644
index 0000000..e69de29
diff --git a/app/components/HelloWorld.jsx b/app/components/HelloWorld.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/app/dispatcher/.gitkeep b/app/dispatcher/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/app/stores/.gitkeep b/app/stores/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..b137580
--- /dev/null
+++ b/index.html
@@ -0,0 +1,10 @@
+
+
+
+ Hello Electron React!
+
+
+
+
+
+
diff --git a/lib/loaders-by-extension.js b/lib/loaders-by-extension.js
new file mode 100644
index 0000000..9b97585
--- /dev/null
+++ b/lib/loaders-by-extension.js
@@ -0,0 +1,29 @@
+function extsToRegExp(exts) {
+ return new RegExp('\\.(' + exts.map(function(ext) {
+ return ext.replace(/\./g, '\\.')
+ }).join('|') + ')(\\?.*)?$')
+}
+
+module.exports = function loadersByExtension(obj) {
+ var loaders = []
+ Object.keys(obj).forEach(function(key) {
+ var exts = key.split('|')
+ var value = obj[key]
+ var entry = {
+ extensions: exts,
+ test: extsToRegExp(exts),
+ }
+
+ if (Array.isArray(value)) {
+ entry.loaders = value
+ } else if (typeof value === 'string') {
+ entry.loader = value
+ } else {
+ Object.keys(value).forEach(function(valueKey) {
+ entry[valueKey] = value[valueKey]
+ })
+ }
+ loaders.push(entry)
+ })
+ return loaders
+};
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..ad0f2c5
--- /dev/null
+++ b/main.js
@@ -0,0 +1,28 @@
+var app = require('app')
+var BrowserWindow = require('browser-window')
+
+require('crash-reporter').start()
+
+var mainWindow = null
+
+app.on('window-all-closed', function() {
+ if (process.platform != 'darwin')
+ app.quit()
+})
+
+
+app.on('ready', function() {
+
+ mainWindow = new BrowserWindow({ width: 800, height: 600 })
+
+ mainWindow.loadUrl('file://' + __dirname + '/index.html')
+
+ mainWindow.on('closed', function() {
+ mainWindow = null
+ })
+
+ if (process.env.NODE_ENV !== 'production') {
+ mainWindow.openDevTools()
+ }
+
+})
diff --git a/make-webpack-config.js b/make-webpack-config.js
new file mode 100644
index 0000000..13d240f
--- /dev/null
+++ b/make-webpack-config.js
@@ -0,0 +1,172 @@
+var webpack = require('webpack')
+var path = require('path')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+var StatsPlugin = require('stats-webpack-plugin')
+var loadersByExtension = require('./lib/loaders-by-extension')
+
+
+
+module.exports = function(opts) {
+
+ var entry = [
+ 'webpack-dev-server/client?http://localhost:2992',
+ 'webpack/hot/only-dev-server',
+ './app/scripts/app.jsx'
+ ]
+
+
+
+ var loaders = [
+ { test: /\.(js|jsx)$/, loaders: [ 'react-hot', 'babel' ], exclude: /node_modules/ },
+ { test: /\.styl$/, loaders: [ 'style', 'css', 'stylus' ], exclude: /node_modules/ },
+ { test: /\.css$/, loaders: [ 'style', 'css' ], exclude: /node_modules/ },
+ { test: /\.json$/, loader: 'json', exclude: /node_modules/ },
+ { test: /\.(png|jpg)$/, loaders: [ 'url?limit=8192' ], exclude: /node_modules/ }
+ ]
+
+ var cssLoader = opts.minimize ? 'css-loader' : 'css-loader?localIdentName=[path][name]---[local]---[hash:base64:5]';
+
+ var stylesheetLoaders = {
+ 'css': cssLoader,
+ 'less': [ cssLoader, 'less-loader' ],
+ 'styl': [ cssLoader, 'stylus-loader' ],
+ 'scss|sass': [ cssLoader, 'sass-loader' ]
+ }
+
+ var additionalLoaders = [
+ // { test: /some-reg-exp$/, loader: 'any-loader' }
+ ]
+
+ var alias = {
+
+ }
+
+ var aliasLoader = {
+
+ }
+
+ var externals = [
+
+ ]
+
+ var modulesDirectories = [ 'node_modules' ]
+
+ var extensions = ['', '.web.js', '.js', '.jsx'];
+
+ var root = path.join(__dirname, 'app')
+
+ var publicPath = opts.devServer
+ ? 'http://localhost:2992/_assets/'
+ : '/_assets/'
+
+
+ var output = {
+ path: __dirname + '/public/js/',
+ filename: 'bundle.js',
+ publicPath: 'http://localhost:2992/',
+ contentBase: __dirname + '/public/'
+ }
+
+ var excludeFromStats = [
+ /node_modules[\\\/]react(-router)?[\\\/]/
+ ]
+
+
+ var plugins = [
+ new webpack.HotModuleReplacementPlugin()
+ ]
+
+ if (opts.prerender) {
+ plugins.push(new StatsPlugin(path.join(__dirname, 'build', 'stats.prerender.json'), {
+ chunkModules: true,
+ exclude: excludeFromStats
+ }));
+ aliasLoader['react-proxy$'] = 'react-proxy/unavailable';
+ aliasLoader['react-proxy-loader$'] = 'react-proxy-loader/unavailable';
+ externals.push(
+ /^react(\/.*)?$/,
+ /^reflux(\/.*)?$/,
+ 'superagent',
+ 'async'
+ );
+ plugins.push(new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }));
+ } else {
+ plugins.push(new StatsPlugin(path.join(__dirname, 'build', 'stats.json'), {
+ chunkModules: true,
+ exclude: excludeFromStats
+ }));
+ }
+
+ if (opts.commonsChunk) {
+ plugins.push(new webpack.optimize.CommonsChunkPlugin('commons', 'commons.js' + (opts.longTermCaching && !opts.prerender ? '?[chunkhash]' : '')))
+ }
+
+ // var asyncLoader = {
+ // test: require('./app/route-handlers/async').map(function(name) {
+ // return path.join(__dirname, 'app', 'route-handlers', name);
+ // }),
+ // loader: opts.prerender ? 'react-proxy-loader/unavailable' : 'react-proxy-loader'
+ // };
+
+ Object.keys(stylesheetLoaders).forEach(function(ext) {
+ var stylesheetLoader = stylesheetLoaders[ext];
+ if(Array.isArray(stylesheetLoader)) stylesheetLoader = stylesheetLoader.join('!');
+ if (opts.prerender) {
+ stylesheetLoaders[ext] = stylesheetLoader.replace(/^css-loader/, 'css-loader/locals');
+ } else if (opts.separateStylesheet) {
+ stylesheetLoaders[ext] = ExtractTextPlugin.extract('style-loader', stylesheetLoader);
+ } else {
+ stylesheetLoaders[ext] = 'style-loader!' + stylesheetLoader;
+ }
+ })
+
+ if (opts.separateStylesheet && !opts.prerender) {
+ plugins.push(new ExtractTextPlugin('[name].css' + (opts.longTermCaching ? '?[contenthash]' : '')));
+ }
+
+ if(opts.minimize && !opts.prerender) {
+ plugins.push(
+ new webpack.optimize.UglifyJsPlugin({
+ compressor: {
+ warnings: false
+ }
+ }),
+ new webpack.optimize.DedupePlugin()
+ )
+ }
+
+ if (opts.minimize) {
+ plugins.push(
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: JSON.stringify('production')
+ }
+ }),
+ new webpack.NoErrorsPlugin()
+ )
+ }
+
+ return {
+ entry: entry,
+ output: output,
+ target: opts.prerender ? 'node' : 'web',
+ module: {
+ loaders: [].concat(loadersByExtension(loaders)).concat(loadersByExtension(stylesheetLoaders)).concat(additionalLoaders)
+ },
+ devtool: opts.devtool,
+ debug: opts.debug,
+ resolve: {
+ root: root,
+ modulesDirectories: modulesDirectories,
+ extensions: extensions,
+ alias: alias
+ },
+ plugins: plugins,
+ devServer: {
+ stats: {
+ cached: false,
+ exclude: excludeFromStats
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ed49bbf
--- /dev/null
+++ b/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "electron-react-boilerplate",
+ "version": "0.1.0",
+ "description": "electron-react-boilerplate",
+ "main": "main.js",
+ "scripts": {
+ "test": "mocha",
+ "dev-server": "webpack-dev-server --config webpack-dev-server.config.js --progress --colors --port 2992 --inline",
+ "hot-dev-server": "webpack-dev-server --config webpack-hot-dev-server.config.js --hot --progress --colors --port 2992 --inline",
+ "build": "webpack --config webpack-production.config.js --progress --profile --colors",
+ "start-dev": "/Applications/Electron.app/Contents/MacOS/Electron .",
+ "start": "/Applications/Electron.app/Contents/MacOS/Electron ."
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/chentsulin/electron-react-boilerplate.git"
+ },
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/chentsulin/electron-react-boilerplate/issues"
+ },
+ "homepage": "https://github.com/chentsulin/electron-react-boilerplate#readme",
+ "devDependencies": {
+ "babel-loader": "^5.1.2",
+ "chai": "^2.3.0",
+ "css-loader": "^0.12.1",
+ "extract-text-webpack-plugin": "^0.8.0",
+ "mocha": "^2.2.5",
+ "proxyquire": "^1.4.0",
+ "sinon": "^1.14.1",
+ "stats-webpack-plugin": "^0.1.0",
+ "style-loader": "^0.12.2",
+ "webpack": "^1.9.7",
+ "webpack-dev-server": "^1.8.2"
+ },
+ "dependencies": {
+ "flux": "^2.0.3",
+ "react": "^0.13.3",
+ "react-router": "^0.13.3"
+ }
+}
diff --git a/test/example.js b/test/example.js
new file mode 100644
index 0000000..7803336
--- /dev/null
+++ b/test/example.js
@@ -0,0 +1,7 @@
+var chai = require('chai')
+
+describe('description', function () {
+ it('description', function () {
+ // body...
+ })
+})
diff --git a/webpack-dev-server.config.js b/webpack-dev-server.config.js
new file mode 100644
index 0000000..1012534
--- /dev/null
+++ b/webpack-dev-server.config.js
@@ -0,0 +1,5 @@
+module.exports = require('./make-webpack-config')({
+ devServer: true,
+ devtool: 'eval',
+ debug: true
+})
diff --git a/webpack-hot-dev-server.config.js b/webpack-hot-dev-server.config.js
new file mode 100644
index 0000000..bd58db6
--- /dev/null
+++ b/webpack-hot-dev-server.config.js
@@ -0,0 +1,6 @@
+module.exports = require('./make-webpack-config')({
+ devServer: true,
+ hotComponents: true,
+ devtool: 'eval',
+ debug: true
+})
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..73fa9e6
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,3 @@
+module.exports = require('./make-webpack-config')({})
+
+
diff --git a/webpack.config.production.js b/webpack.config.production.js
new file mode 100644
index 0000000..b826c27
--- /dev/null
+++ b/webpack.config.production.js
@@ -0,0 +1,7 @@
+module.exports = require('./make-webpack-config')({
+ // commonsChunk: true,
+ longTermCaching: true,
+ separateStylesheet: true,
+ minimize: true
+ // devtool: 'source-map'
+})