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' +})