orther
8/19/2015 - 11:39 PM

Gulpfile with LiveReload, Sass, and Browserify with Babelify (JSX harmony)

Gulpfile with LiveReload, Sass, and Browserify with Babelify (JSX harmony)

// ============================================================
//   $ npm install --save-dev gulp-util node-notifier gulp vinyl-source-stream vinyl-buffer gulp-uglify gulp-sourcemaps gulp-livereload browserify watchify babelify gulp-sass gulp-autoprefixer gulp-rename
// ============================================================

function Workflow()
{
    // Override workflow settings here
    this.input.directory = 'src';
    this.input.script    = 'js/main.jsx';
    this.input.style     = 'css/main.scss';

    this.output.directory = 'build';
    this.output.script    = 'js/client_platform.js';
    this.output.style     = 'css/main.css';

    this.watch.script = this.input.directory + '/' + this.input.script.replace(/.+?\.(.+)$/, '**/*.$1');
    this.watch.style  = this.input.directory + '/' + this.input.style.replace(/.+?\.(.+)$/, '**/*.$1');

    this.livereload.port = 35766;
}

// ============================================================
// ============================================================

Workflow.prototype.input = {
    directory: 'app',         // Default input directory root. Override with [this.input.directory]
    script:    'js/app.js',   // Default main script file. Override with [this.input.script]
    style:     'css/app.scss' // Default main stylesheet file. Override with [this.input.style]
};
Workflow.prototype.output = {
    directory: 'build',  // Default output directory. Override with [this.output.directory]
    script:    'app.js', // Default output script. Override with [this.output.script]
    style:     'app.css' // Default output stylesheet. Override with [this.output.style]
};
Workflow.prototype.watch = {
    // Key/value pairs for watching files and running tasks. Key is task name, value is glob to watch.
    update: 'build/**/*.html' // Key [update] does a live reload
};
Workflow.prototype.livereload = {
    // Key/value pairs for livereload options
    port: 35729
};

var workflow = new Workflow();

var production = process.argv.indexOf('--production') !== -1;

// ------------------------------------------------------------
//     Imports
// ------------------------------------------------------------
var utilities = require('gulp-util');
var notifier = require('node-notifier');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var livereload = require('gulp-livereload');
var browserify = require('browserify');
var watchify = require('watchify');
var babelify = require('babelify');
var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var rename = require('gulp-rename');

// ------------------------------------------------------------
//     Console
// ------------------------------------------------------------

var Console = {};

Console.notify = function (options)
{
    options.title = options.title || 'Done';
    options.message = options.message || 'Check your terminal!';
    options.sound = options.sound || 'Pop';
    options.icon = options.icon || 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-ios7-bell-128.png';

    notifier.notify(options);
}

Console.notifyOptimistic = function (options)
{
    options.contentImage = options.contentImage || 'https://www.secureauth.com/SecureAuth/media/Product/check-mark-11-512_4.png';

    Console.notify(options);
}

Console.notifyPessimistic = function (options)
{
    options.contentImage = options.contentImage || 'https://cdn4.iconfinder.com/data/icons/simplicio/128x128/notification_error.png';

    Console.notify(options);
}

Console.log = function ()
{
    utilities.log.apply(utilities, arguments);

    var args = [];

    for (var i = 0; i < arguments.length; i++) {

        args.push(arguments[i]);
    }

    Console.notifyOptimistic({
        message: args.join(' ')
    });
}

Console.error = function (error)
{
    utilities.log.call(utilities, utilities.colors.red(error));

    Console.notifyPessimistic({
        title:   error.name,
        message: error.message
    });

    this.emit('end');
}

// ------------------------------------------------------------
//     Script task
// ------------------------------------------------------------

var bundler = browserify({
    entries:      ['./' + workflow.input.directory + '/' + workflow.input.script],
    extensions:   ['.js','.jsx'],
    debug:        !production,
    transform:    [[babelify, {global: true}]],
    cache:        {},
    packageCache: {}
});

function bundle()
{
    var pipe = bundler.bundle()
        .on('error', Console.error)
        .pipe(source(workflow.input.script))
        .pipe(buffer());

    if (production) {
        pipe.pipe(uglify());
    } else {
        pipe = pipe.pipe(sourcemaps.init({loadMaps: true}));
    }

    pipe = pipe.pipe(rename(workflow.output.script));

    if (!production) {
        pipe = pipe.pipe(sourcemaps.write('./'));
    }

    return pipe
        .pipe(gulp.dest(workflow.output.directory))
        .pipe(livereload(workflow.livereload));
}

function watchBundle()
{
    bundler = watchify(bundler);

    bundler.on('update', bundle);
    bundler.on('log', Console.log);

    return bundle();
}

gulp.task('script', bundle);
gulp.task('watchScript', watchBundle);

// ------------------------------------------------------------
//     Style task
// ------------------------------------------------------------

var sassOptions = {
    style:     (production) ? 'compressed' : 'expanded',
    sourcemap: !production,
    require:   'sass-globbing'
}

gulp.task('style', function ()
{
    var pipe = gulp.src(workflow.input.directory + '/' + workflow.input.style);

    if (!production) {
        pipe = pipe.pipe(sourcemaps.init());
    }

    pipe = pipe.pipe(sass(sassOptions))
        .pipe(rename(workflow.output.style));

    if (!production) {
        pipe = pipe.pipe(sourcemaps.write('./'));
    }

    pipe.pipe(gulp.dest(workflow.output.directory))
        .pipe(livereload(workflow.livereload));
});

// ------------------------------------------------------------
//     Build task
// ------------------------------------------------------------

gulp.task('build', ['script', 'style']);

// ------------------------------------------------------------
//     Watch task
// ------------------------------------------------------------

gulp.task('watch', ['style', 'watchScript'], function ()
{
    livereload.listen(workflow.livereload);

    var watches = workflow.watch;

    for (var task in watches) {

        var glob = watches[task];

        if (task == 'update') {

            gulp.watch(glob, livereload.changed);

        } else {

            gulp.watch(glob, [task]);
        }
    }
});

// ------------------------------------------------------------
//     Default task
// ------------------------------------------------------------

gulp.task('default', ['watch']);