├── .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 |
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 |
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 |
--------------------------------------------------------------------------------