Skip to content

Instantly share code, notes, and snippets.

@geta6
Last active November 30, 2015 07:35
Show Gist options
  • Select an option

  • Save geta6/6cde69cd40ffb917d2e6 to your computer and use it in GitHub Desktop.

Select an option

Save geta6/6cde69cd40ffb917d2e6 to your computer and use it in GitHub Desktop.

Revisions

  1. geta6 revised this gist Nov 30, 2015. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions .babelrc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    {
    "stage": 0
    }
  2. geta6 revised this gist Nov 30, 2015. 2 changed files with 26 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions .eslintignore
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    build/**
    release/**
    node_modules/**
    23 changes: 23 additions & 0 deletions .eslintrc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    {
    "extends": "airbnb",
    "parser": "babel-eslint",
    "env": {
    "mocha": true
    },
    "rules": {
    "object-curly-spacing": [2, "never"],
    "jsx-quotes": [2, "prefer-single"],
    "space-before-function-paren": 0,
    "id-length": 0,
    "no-new": 0,
    "no-else-return": 0,
    "no-loop-func": 0,
    "no-unused-expressions": 0,
    "no-const-assign": 2,
    "react/display-name": 2,
    "react/jsx-boolean-value": 0
    },
    "globals": {
    "__DEV__": true
    }
    }
  3. geta6 created this gist Nov 30, 2015.
    180 changes: 180 additions & 0 deletions gulpfile.babel.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,180 @@
    import os from 'os';
    import cp from 'child_process';
    import path from 'path';
    import gulp from 'gulp';
    import util from 'gulp-util';
    import chalk from 'chalk';
    import packager from 'electron-packager';
    import ncp from 'ncp';
    import gaze from 'gaze';
    import del from 'del';
    import run from 'run-sequence';
    import replace from 'replace';
    import webpack from 'webpack';
    import get from 'lodash/object/get';
    import defaults from 'lodash/object/defaults';
    import {server} from 'electron-connect';
    import {version} from './package.json';

    /**
    * Environments and constants.
    */
    const DEBUG = !(process.argv.includes('--release') || process.argv.includes('release'));
    const WATCH = process.argv.includes('--watch') || process.argv.includes('watch');

    defaults(process.env, {
    NODE_ENV: DEBUG ? 'development' : 'production',
    ELECTRON: '0.35.1',
    ARCHITECTURE: 'all',
    });

    switch (process.env.NODE_ENV) {
    case 'production':
    defaults(process.env, {ENDPOINT: 'https://myproduct.production'});
    break;
    default:
    defaults(process.env, {ENDPOINT: 'http://myproduct.development'});
    break;
    }

    util.log(chalk.green(`NODE_ENV: ${process.env.NODE_ENV}`));
    util.log(chalk.green(`ENDPOINT: ${process.env.ENDPOINT}`));
    util.log(chalk.green(`ELECTRON: v${process.env.ELECTRON}`));

    let electron;

    /**
    * Helper functions.
    */
    const copy = (source, destination) => new Promise((resolve, reject) => {
    ncp(source, destination, err => err ? reject(err) : resolve());
    });

    const exec = command => new Promise((resolve, reject) => {
    cp.exec(command, (err, stdout, stderr) => err ? reject(err) : resolve({stdout, stderr}));
    });

    const watch = pattern => new Promise((resolve, reject) => {
    gaze(pattern, (err, watcher) => err ? reject(err) : resolve(watcher));
    });

    const packaging = opts => new Promise((resolve, reject) => {
    packager(opts, (err, appPath) => err ? reject(err) : resolve(appPath));
    });

    /**
    * Packages the project from source files into an application.
    */
    gulp.task('release', async () => {
    await del(['release/*', '!release/*.zip'], {dot: false});
    await new Promise(resolve => run('build', resolve));
    const appPaths = await packaging({
    dir: 'build',
    name: 'My Product',
    arch: 'x64',
    platform: process.env.ARCHITECTURE,
    version: process.env.ELECTRON,
    'app-bundle-id': 'production.myproduct',
    'app-version': version,
    asar: true,
    prune: true,
    overwrite: true,
    icon: './src/resource/icon',
    out: './release',
    cache: './tmp/cache',
    });
    if (os.platform() === 'darwin') {
    for (const index in appPaths) if (appPaths.hasOwnProperty(index)) {
    const origin = appPaths[index];
    const target = `${origin}-v${version}.zip`;
    process.stdout.write(`Archiving app for platform ${origin.replace(/^.+-(.+?)-([^-]+?)$/, '$1 $2')}`);
    const start = Date.now();
    await exec(`ditto -ck --rsrc --sequesterRsrc "${origin}" "${target}"`);
    const osize = (await exec(`du -sh "${origin}" | cut -f1`)).stdout;
    const tsize = (await exec(`du -sh "${target}" | cut -f1`)).stdout;
    process.stdout.write(` ${osize.trim()} -> ${tsize.trim()} (${Date.now() - start}ms)\n`);
    }
    } else {
    util.log('Archiver only works in osx.');
    }
    });

    /**
    * Compiles the project from source files into a distributable format and copies it to the output (build) folder.
    */
    gulp.task('build', async () => {
    await new Promise(resolve => run('clean', 'copy', 'bundle', resolve));
    });

    /**
    * Cleans up the output (build) directory.
    */
    gulp.task('clean', async () => {
    await del(['build/*'], {dot: false});
    });

    /**
    * Copies static files to the output (build) folder.
    */
    gulp.task('copy', async () => {
    await Promise.all([
    copy('src/resource', 'build/resource'),
    copy('src/core/index.html', 'build/index.html'),
    copy('package.json', 'build/package.json'),
    ]);

    replace({
    regex: '"main".*',
    replacement: '"main": "bundle.core.js",',
    paths: ['build/package.json'],
    recursive: false,
    silent: true,
    });

    if (WATCH) {
    const watcher = await watch('src/core/**/*.html');
    watcher.on('changed', async file => {
    util.log('[changed]', file);
    await copy(file, `build/${path.basename(file)}`);
    electron.reload();
    });
    }
    });

    /**
    * Bundles JavaScript into one or more packages ready to be used in a browser.
    */
    gulp.task('bundle', async () => {
    const config = require('./webpack.config.babel');
    await new Promise((resolve, reject) => {
    let count = 0;
    const bundler = webpack(config);
    const bundle = (error, stats) => {
    if (error) {
    reject(new util.PluginError('bundle', error.message));
    } else {
    util.log(stats.toString(config[0].stats));
    if (++count === (WATCH ? config.length : 1)) {
    if (WATCH) {
    electron = server.create({path: 'build', electron: require('electron-prebuilt')});
    electron.start();
    }
    resolve();
    } else if (WATCH && count > config.length) {
    const info = stats.toJson(config[0].stats);
    if (/\.core\.js$/.test(get(info, 'assetsByChunkName.main'))) {
    electron.restart();
    } else {
    electron.reload();
    }
    }
    }
    };

    if (WATCH) {
    bundler.watch(200, bundle);
    } else {
    bundler.run(bundle);
    }
    });
    });
    45 changes: 45 additions & 0 deletions package.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    {
    "name": "electron",
    "version": "1.0.0",
    "private": true,
    "description": "Desktop application for My Product",
    "main": "src/core/index.js",
    "engines": {
    "node": ">=4.2 <5.0"
    },
    "scripts": {
    "test": "eslint . && exit 0",
    "build": "gulp build",
    "start": "gulp build --watch",
    "release": "gulp release"
    },
    "dependencies": {},
    "devDependencies": {
    "babel": "^5.8.34",
    "babel-eslint": "^4.1.6",
    "babel-loader": "^5.4.0",
    "chalk": "^1.1.1",
    "css-loader": "^0.23.0",
    "del": "^2.1.0",
    "electron-connect": "^0.3.3",
    "electron-packager": "^5.1.1",
    "electron-prebuilt": "^0.35.1",
    "eslint": "^1.10.1",
    "eslint-config-airbnb": "^1.0.2",
    "eslint-plugin-react": "^3.10.0",
    "file-loader": "^0.8.5",
    "gaze": "^0.5.2",
    "gulp": "^3.9.0",
    "gulp-electron": "0.0.9",
    "gulp-util": "^3.0.7",
    "json-loader": "^0.5.4",
    "lodash": "^3.10.1",
    "ncp": "^2.0.0",
    "replace": "^0.3.0",
    "run-sequence": "^1.1.5",
    "style-loader": "^0.13.0",
    "stylus-loader": "^1.4.2",
    "url-loader": "^0.5.7",
    "webpack": "^1.12.9"
    }
    }
    86 changes: 86 additions & 0 deletions webpack.config.babel.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    import util from 'gulp-util';
    import chalk from 'chalk';
    import merge from 'lodash/object/merge';
    import webpack from 'webpack';

    const DEBUG = !(process.argv.includes('--release') || process.argv.includes('release')) || process.env.NODE_ENV !== 'production';
    const VERBOSE = process.argv.includes('--verbose') || process.argv.includes('verbose');

    util.log(chalk.green(`DEBUG: ${DEBUG ? 'on' : 'off'}`));
    util.log(chalk.green(`VERBOSE: ${VERBOSE ? 'on' : 'off'}`));

    const config = {
    output: {
    path: './build',
    libraryTarget: 'commonjs2',
    },

    cache: DEBUG,
    debug: DEBUG,

    devtool: DEBUG ? 'cheap-module-eval-source-map' : false,

    stats: {
    colors: true,
    reasons: DEBUG,
    hash: VERBOSE,
    version: VERBOSE,
    timings: true,
    warnings: VERBOSE,
    chunks: VERBOSE,
    chunkModules: VERBOSE,
    cached: VERBOSE,
    cachedAssets: VERBOSE,
    },

    target: 'node',

    node: {
    __dirname: false,
    },

    plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.ExternalsPlugin('commonjs', ['electron', 'screen', 'remote']),
    new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    'process.env.ENDPOINT': JSON.stringify(process.env.ENDPOINT),
    }),
    ...(DEBUG ? [] : [
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({compress: {warnings: VERBOSE}}),
    new webpack.optimize.AggressiveMergingPlugin(),
    ]),
    ],

    resolve: {
    extensions: ['', '.js', '.jsx'],
    },

    module: {
    loaders: [
    {test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel']},
    {test: /\.json$/, loaders: ['json']},
    {test: /\.styl$/, loaders: ['style', 'css', 'stylus']},
    {test: /\.png$/, loaders: ['url']},
    ],
    },
    };

    const scriptConfig = merge({}, config, {
    entry: './src/script/index.js',

    output: {
    filename: 'bundle.script.js',
    },
    });

    const coreConfig = merge({}, config, {
    entry: './src/core/index.js',

    output: {
    filename: 'bundle.core.js',
    },
    });

    export default [scriptConfig, coreConfig];