├── .gitignore ├── src ├── renderer │ ├── bootstrap.js │ ├── index.html │ └── components │ │ └── main.jsx ├── styles │ └── main.scss ├── browser │ └── menu │ │ └── appMenu.js ├── app.js └── assets │ └── images │ └── electron.svg ├── bower.json ├── package.json ├── README.md └── gulpfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | node_modules/ 4 | bower_components/ 5 | .serve/ 6 | dist/ 7 | release/ 8 | -------------------------------------------------------------------------------- /src/renderer/bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import polyfill from 'babel/polyfill'; 4 | import React from 'react'; 5 | import {Main} from './components/main'; 6 | 7 | React.render(React.createElement(Main), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | cursor: default; 4 | -webkit-user-select: none; 5 | } 6 | 7 | a, a:hover, a:visited, a:active, a:focus { 8 | text-decoration: none; 9 | } 10 | 11 | .main { 12 | margin-top: 15px; 13 | text-align: center; 14 | } 15 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-jsx-babel-boilerplate", 3 | "version": "1.0.0", 4 | "authors": [ 5 | "Quramy" 6 | ], 7 | "license": "MIT", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "bootstrap": "3.3.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/browser/menu/appMenu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import app from 'app'; 4 | import Menu from 'menu'; 5 | import MenuItem from 'menu-item'; 6 | 7 | let template = [{ 8 | label: 'Electron App', 9 | submenu: [{ 10 | label: 'Quit', 11 | accelerator: 'Command+Q', 12 | click: function () {app.quit()} 13 | }] 14 | }]; 15 | 16 | let appMenu = Menu.buildFromTemplate(template); 17 | 18 | module.exports = appMenu; 19 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electron App 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | require('babel/polyfill'); 3 | 4 | import app from 'app'; 5 | import BrowserWindow from 'browser-window'; 6 | import crashReporter from 'crash-reporter'; 7 | import Menu from 'menu'; 8 | import appMenu from './browser/menu/appMenu'; 9 | 10 | let mainWindow = null; 11 | if(process.env.NODE_ENV === 'develop'){ 12 | crashReporter.start(); 13 | //appMenu.append(devMenu); 14 | } 15 | 16 | app.on('window-all-closed', () => { 17 | app.quit(); 18 | }); 19 | 20 | app.on('ready', () => { 21 | //Menu.setApplicationMenu(appMenu); 22 | mainWindow = new BrowserWindow({ 23 | width: 580, 24 | height: 365 25 | }); 26 | mainWindow.loadUrl('file://' + __dirname + '/renderer/index.html'); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /src/renderer/components/main.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import shell from 'shell'; 5 | 6 | export class Main extends React.Component { 7 | state = { 8 | message: 'Hello, Electron' 9 | } 10 | constructor () { 11 | super(); 12 | this.openGithub = ::this.openGithub 13 | } 14 | openGithub () { 15 | shell.openExternal('https://github.com/Quramy/electron-jsx-babel-boilerplate'); 16 | } 17 | render() { 18 | return ( 19 |
20 |
21 |

{this.state.message}

22 | 23 |

Provided by electron-jsx-babel-boilerplate

24 |
25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-jsx-babel-boilerplate", 3 | "version": "1.0.0", 4 | "description": "Electron boilerplate project with React and babel", 5 | "main": ".serve/app.js", 6 | "scripts": { 7 | "install": "bower install", 8 | "start": "gulp serve", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [ 12 | "electron", 13 | "react", 14 | "babel" 15 | ], 16 | "author": "Quramy", 17 | "license": "MIT", 18 | "dependencies": { 19 | "lodash": "^3.9.3", 20 | "react": "^0.13.3" 21 | }, 22 | "devDependencies": { 23 | "babel": "^5.4.7", 24 | "browserify": "^10.2.4", 25 | "del": "^1.1.1", 26 | "electron-connect": "^0.3.0", 27 | "electron-packager": "^4.1.3", 28 | "gulp": "^3.8.11", 29 | "gulp-babel": "^5.1.0", 30 | "gulp-flatten": "0.0.4", 31 | "gulp-if": "^1.2.5", 32 | "gulp-inject": "^1.2.0", 33 | "gulp-load-plugins": "^0.10.0", 34 | "gulp-minify-css": "^1.1.6", 35 | "gulp-plumber": "^1.0.1", 36 | "gulp-sass": "^2.0.1", 37 | "gulp-sourcemaps": "^1.5.2", 38 | "gulp-uglify": "^1.2.0", 39 | "gulp-useref": "^1.2.0", 40 | "gulp-watch": "^4.2.4", 41 | "main-bower-files": "^2.8.0", 42 | "merge2": "^0.3.5", 43 | "optimist": "^0.6.1", 44 | "vinyl-buffer": "^1.0.0", 45 | "vinyl-source-stream": "^1.1.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # electron-jsx-babel-boilerplate 2 | 3 | This is a sample repository of [Electron](http://electron.atom.io/) application. 4 | 5 | This boilerplate includes the following build tasks: 6 | 7 | * Transpillation [React](https://facebook.github.io/react/) JSX and ES6 style JavaScript files with [Babel](https://babeljs.io/) 8 | * Compilation scss files 9 | * Livereload when you change source files 10 | * Packaging distribution apllicatoins for each platforms(win32, darwin, linux) 11 | 12 | ## Install 13 | 14 | Clone this repository, so execute the following command. 15 | 16 | ```bash 17 | cd electron-jsx-babel-boilerplate 18 | npm install -g bower gulp electron-prebuilt 19 | npm install 20 | ``` 21 | 22 | ## Run application 23 | ### With file watch and livereload 24 | 25 | ```bash 26 | gulp serve 27 | ``` 28 | 29 | ### Pre-packaging app 30 | 31 | ```bash 32 | gulp build;electron dist 33 | ``` 34 | 35 | ## Package application 36 | 37 | ```bash 38 | gulp package 39 | ``` 40 | 41 | ## Copy this boilerplate 42 | 43 | ```bash 44 | gulp boilerplate -o {DIST_DIR} 45 | ``` 46 | 47 | ## Directory structure 48 | 49 | ``` 50 | + .serve/ Compiled files 51 | + dist/ Application for distribution 52 | - release/ Packaged applications for platforms 53 | |+ darwin/ 54 | |+ linux/ 55 | |+ win32/ 56 | - src/ Source directory 57 | |- assets/ 58 | |+ images/ 59 | |- browser/ For browser process scripts 60 | |+ menu/ 61 | |- renderer/ For renderer process scripts and resources 62 | |+ components/ React components 63 | | bootstrap.js Entry point for render process 64 | | index.html 65 | |- styles/ SCSS directory 66 | | main.scss 67 | | app.js Entry point for browser process 68 | bower.json 69 | gulpfile.js 70 | package.json 71 | ``` 72 | 73 | -------------------------------------------------------------------------------- /src/assets/images/electron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | background 4 | 5 | 6 | 7 | Layer 1 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var $ = require('gulp-load-plugins')(); 5 | var _ = require('lodash'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | var del = require('del'); 9 | var mainBowerFiles = require('main-bower-files'); 10 | var electronServer = require('electron-connect').server; 11 | var packager = require('electron-packager'); 12 | var merge = require('merge2'); 13 | var browserify = require('browserify'); 14 | var source = require('vinyl-source-stream'); 15 | var buffer = require('vinyl-buffer'); 16 | var packageJson = require('./package.json'); 17 | var optimist = require('optimist'); 18 | 19 | var srcDir = 'src'; // source directory 20 | var serveDir = '.serve'; // directory for serve task 21 | var distDir = 'dist'; // directory for serve:dist task 22 | var releaseDir = 'release'; // directory for application packages 23 | 24 | // Compile *.scss files with sourcemaps 25 | gulp.task('compile:styles', function () { 26 | return gulp.src([srcDir + '/styles/**/*.scss']) 27 | .pipe($.sourcemaps.init()) 28 | .pipe($.sass()) 29 | .pipe($.sourcemaps.write('.')) 30 | .pipe(gulp.dest(serveDir + '/styles')) 31 | ; 32 | }); 33 | 34 | // Inject *.css(compiled and depedent) files into *.html 35 | gulp.task('inject:css', ['compile:styles'], function() { 36 | return gulp.src(srcDir + '/**/*.html') 37 | .pipe($.inject(gulp.src(mainBowerFiles().concat([serveDir + '/styles/**/*.css'])), { 38 | relative: true, 39 | ignorePath: ['../../.serve', '..'], 40 | addPrefix: '..' 41 | })) 42 | .pipe(gulp.dest(serveDir)) 43 | ; 44 | }); 45 | 46 | // Copy assets 47 | gulp.task('misc', function () { 48 | return gulp.src(srcDir + '/assets/**/*') 49 | .pipe(gulp.dest(serveDir + '/assets')) 50 | .pipe(gulp.dest(distDir + '/assets')) 51 | ; 52 | }); 53 | 54 | // Incremental compile ES6, JSX files with sourcemaps 55 | gulp.task('compile:scripts:watch', function (done) { 56 | gulp.src('src/**/*.{js,jsx}') 57 | .pipe($.watch('src/**/*.{js,jsx}', {verbose: true})) 58 | .pipe($.plumber()) 59 | .pipe($.sourcemaps.init()) 60 | .pipe($.babel({stage: 0})) 61 | .pipe($.sourcemaps.write('.')) 62 | .pipe(gulp.dest(serveDir)) 63 | ; 64 | done(); 65 | }); 66 | 67 | // Compile scripts for distribution 68 | gulp.task('compile:scripts', function () { 69 | return gulp.src('src/**/*.{js,jsx}') 70 | .pipe($.babel({stage: 0})) 71 | .pipe($.uglify()) 72 | .pipe(gulp.dest(distDir)) 73 | ; 74 | }); 75 | 76 | // Make HTML and concats CSS files. 77 | gulp.task('html', ['inject:css'], function () { 78 | var assets = $.useref.assets({searchPath: ['bower_components', serveDir + '/styles']}); 79 | return gulp.src(serveDir + '/renderer/**/*.html') 80 | .pipe(assets) 81 | .pipe($.if('*.css', $.minifyCss())) 82 | .pipe(assets.restore()) 83 | .pipe($.useref()) 84 | .pipe(gulp.dest(distDir + '/renderer')) 85 | ; 86 | }); 87 | 88 | // Copy fonts file. You don't need to copy *.ttf nor *.svg nor *.otf. 89 | gulp.task('copy:fonts', function () { 90 | return gulp.src('bower_components/**/fonts/*.woff') 91 | .pipe($.flatten()) 92 | .pipe(gulp.dest(distDir + '/fonts')) 93 | ; 94 | }); 95 | 96 | // Minify dependent modules. 97 | gulp.task('bundle:dependencies', function () { 98 | var streams = [], dependencies = []; 99 | var defaultModules = ['assert', 'buffer', 'console', 'constants', 'crypto', 'domain', 'events', 'http', 'https', 'os', 'path', 'punycode', 'querystring', 'stream', 'string_decoder', 'timers', 'tty', 'url', 'util', 'vm', 'zlib'], 100 | electronModules = ['app', 'auto-updater', 'browser-window', 'content-tracing', 'dialog', 'global-shortcut', 'ipc', 'menu', 'menu-item', 'power-monitor', 'protocol', 'tray', 'remote', 'web-frame', 'clipboard', 'crash-reporter', 'native-image', 'screen', 'shell']; 101 | 102 | // Because Electron's node integration, bundle files don't need to include browser-specific shim. 103 | var excludeModules = defaultModules.concat(electronModules); 104 | 105 | for(var name in packageJson.dependencies) { 106 | dependencies.push(name); 107 | } 108 | 109 | // create a list of dependencies' main files 110 | var modules = dependencies.map(function (dep) { 111 | var packageJson = require(dep + '/package.json'); 112 | var main; 113 | if(!packageJson.main) { 114 | main = ['index.js']; 115 | }else if(Array.isArray(packageJson.main)){ 116 | main = packageJson.main; 117 | }else{ 118 | main = [packageJson.main]; 119 | } 120 | return {name: dep, main: main.map(function (it) {return path.basename(it);})}; 121 | }); 122 | 123 | // add babel/polyfill module 124 | modules.push({name: 'babel', main: ['polyfill.js']}); 125 | 126 | // create bundle file and minify for each main files 127 | modules.forEach(function (it) { 128 | it.main.forEach(function (entry) { 129 | var b = browserify('node_modules/' + it.name + '/' + entry, { 130 | detectGlobal: false, 131 | standalone: entry 132 | }); 133 | excludeModules.forEach(function (moduleName) {b.exclude(moduleName)}); 134 | streams.push(b.bundle() 135 | .pipe(source(entry)) 136 | .pipe(buffer()) 137 | .pipe($.uglify()) 138 | .pipe(gulp.dest(distDir + '/node_modules/' + it.name)) 139 | ); 140 | }); 141 | streams.push( 142 | // copy modules' package.json 143 | gulp.src('node_modules/' + it.name + '/package.json') 144 | .pipe(gulp.dest(distDir + '/node_modules/' + it.name)) 145 | ); 146 | }); 147 | 148 | return merge(streams); 149 | }); 150 | 151 | // Write a package.json for distribution 152 | gulp.task('packageJson', ['bundle:dependencies'], function (done) { 153 | var json = _.cloneDeep(packageJson); 154 | json.main = 'app.js'; 155 | fs.writeFile(distDir + '/package.json', JSON.stringify(json), function (err) { 156 | done(); 157 | }); 158 | }); 159 | 160 | // Package for each platforms 161 | gulp.task('package', ['win32', 'darwin', 'linux'].map(function (platform) { 162 | var taskName = 'package:' + platform; 163 | gulp.task(taskName, ['build'], function (done) { 164 | packager({ 165 | dir: distDir, 166 | name: 'ElectronApp', 167 | arch: 'x64', 168 | platform: platform, 169 | out: releaseDir + '/' + platform, 170 | version: '0.28.1' 171 | }, function (err) { 172 | done(); 173 | }); 174 | }); 175 | return taskName; 176 | })); 177 | 178 | // Delete generated directories. 179 | gulp.task('clean', function (done) { 180 | del([serveDir, distDir, releaseDir], function () { 181 | done(); 182 | }); 183 | }); 184 | 185 | gulp.task('serve', ['inject:css', 'compile:scripts:watch', 'compile:styles', 'misc'], function () { 186 | var electron = electronServer.create(); 187 | electron.start(); 188 | gulp.watch(['bower.json', srcDir + '/renderer/index.html'], ['inject:css']); 189 | gulp.watch([serveDir + '/app.js', serveDir + '/browser/**/*.js'], electron.restart); 190 | gulp.watch([serveDir + '/styles/**/*.css', serveDir + '/renderer/**/*.html', serveDir + '/renderer/**/*.js'], electron.reload); 191 | }); 192 | 193 | gulp.task('build', ['html', 'compile:scripts', 'packageJson', 'copy:fonts', 'misc']); 194 | 195 | gulp.task('serve:dist', ['build'], function () { 196 | electronServer.create({path: distDir}).start(); 197 | }); 198 | 199 | gulp.task('boilerplate', function () { 200 | var outDir = optimist.argv.o || optimist.argv.out; 201 | if (!outDir) { 202 | console.log('usage: gulp boilerplate -o {outdir}'); 203 | return; 204 | } 205 | var configStream = gulp.src(['bower.json', 'package.json', 'gulpfile.js', '.gitignore']).pipe(gulp.dest(outDir)); 206 | var srcStream = gulp.src(['src/**/*']).pipe(gulp.dest(outDir + '/src')); 207 | return merge([configStream, srcStream]); 208 | }); 209 | 210 | gulp.task('default', ['build']); 211 | 212 | --------------------------------------------------------------------------------