Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
gulpfile.js with browserify, jshint, libsass, browserSync for livereload, image optimization and system notifications on errors
'use strict';
var gulp = require('gulp');
var gutil = require('gulp-util');
var del = require('del');
var uglify = require('gulp-uglify');
var gulpif = require('gulp-if');
var exec = require('child_process').exec;
var notify = require('gulp-notify');
var buffer = require('vinyl-buffer');
var argv = require('yargs').argv;
// sass
var sass = require('gulp-sass');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer-core');
var sourcemaps = require('gulp-sourcemaps');
// BrowserSync
var browserSync = require('browser-sync');
// js
var watchify = require('watchify');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
// image optimization
var imagemin = require('gulp-imagemin');
// linting
var jshint = require('gulp-jshint');
var stylish = require('jshint-stylish');
// testing/mocha
var mocha = require('gulp-mocha');
// gulp build --production
var production = !!argv.production;
// determine if we're doing a build
// and if so, bypass the livereload
var build = argv._.length ? argv._[0] === 'build' : false;
var watch = argv._.length ? argv._[0] === 'watch' : true;
// ----------------------------
// Error notification methods
// ----------------------------
var beep = function() {
var os = require('os');
var file = 'gulp/error.wav';
if (os.platform() === 'linux') {
// linux
exec("aplay " + file);
} else {
// mac
console.log("afplay " + file);
exec("afplay " + file);
}
};
var handleError = function(task) {
return function(err) {
beep();
notify.onError({
message: task + ' failed, check the logs..',
sound: false
})(err);
gutil.log(gutil.colors.bgRed(task + ' error:'), gutil.colors.red(err));
};
};
// --------------------------
// CUSTOM TASK METHODS
// --------------------------
var tasks = {
// --------------------------
// Delete build folder
// --------------------------
clean: function(cb) {
del(['build/'], cb);
},
// --------------------------
// Copy static assets
// --------------------------
assets: function() {
return gulp.src('./client/assets/**/*')
.pipe(gulp.dest('build/assets/'));
},
// --------------------------
// HTML
// --------------------------
// html templates (when using the connect server)
templates: function() {
gulp.src('templates/*.html')
.pipe(gulp.dest('build/'));
},
// --------------------------
// SASS (libsass)
// --------------------------
sass: function() {
return gulp.src('./client/scss/*.scss')
// sourcemaps + sass + error handling
.pipe(gulpif(!production, sourcemaps.init()))
.pipe(sass({
sourceComments: !production,
outputStyle: production ? 'compressed' : 'nested'
}))
.on('error', handleError('SASS'))
// generate .maps
.pipe(gulpif(!production, sourcemaps.write({
'includeContent': false,
'sourceRoot': '.'
})))
// autoprefixer
.pipe(gulpif(!production, sourcemaps.init({
'loadMaps': true
})))
.pipe(postcss([autoprefixer({browsers: ['last 2 versions']})]))
// we don't serve the source files
// so include scss content inside the sourcemaps
.pipe(sourcemaps.write({
'includeContent': true
}))
// write sourcemaps to a specific directory
// give it a file and save
.pipe(gulp.dest('build/css'));
},
// --------------------------
// Browserify
// --------------------------
browserify: function() {
var bundler = browserify('./client/js/index.js', {
debug: !production,
cache: {}
});
// determine if we're doing a build
// and if so, bypass the livereload
var build = argv._.length ? argv._[0] === 'build' : false;
if (watch) {
bundler = watchify(bundler);
}
var rebundle = function() {
return bundler.bundle()
.on('error', handleError('Browserify'))
.pipe(source('build.js'))
.pipe(gulpif(production, buffer()))
.pipe(gulpif(production, uglify()))
.pipe(gulp.dest('build/js/'));
};
bundler.on('update', rebundle);
return rebundle();
},
// --------------------------
// linting
// --------------------------
lintjs: function() {
return gulp.src([
'gulpfile.js',
'./client/js/index.js',
'./client/js/**/*.js'
]).pipe(jshint())
.pipe(jshint.reporter(stylish))
.on('error', function() {
beep();
});
},
// --------------------------
// Optimize asset images
// --------------------------
optimize: function() {
return gulp.src('./client/assets/**/*.{gif,jpg,png,svg}')
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
// png optimization
optimizationLevel: production ? 3 : 1
}))
.pipe(gulp.dest('./client/assets/'));
},
// --------------------------
// Testing with mocha
// --------------------------
test: function() {
return gulp.src('./client/**/*test.js', {read: false})
.pipe(mocha({
'ui': 'bdd',
'reporter': 'spec'
})
);
},
};
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: "./build"
},
port: process.env.PORT || 3000
});
});
gulp.task('reload-sass', ['sass'], function(){
browserSync.reload();
});
gulp.task('reload-js', ['browserify'], function(){
browserSync.reload();
});
gulp.task('reload-templates', ['templates'], function(){
browserSync.reload();
});
// --------------------------
// CUSTOMS TASKS
// --------------------------
gulp.task('clean', tasks.clean);
// for production we require the clean method on every individual task
var req = build ? ['clean'] : [];
// individual tasks
gulp.task('templates', req, tasks.templates);
gulp.task('assets', req, tasks.assets);
gulp.task('sass', req, tasks.sass);
gulp.task('browserify', req, tasks.browserify);
gulp.task('lint:js', tasks.lintjs);
gulp.task('optimize', tasks.optimize);
gulp.task('test', tasks.test);
// --------------------------
// DEV/WATCH TASK
// --------------------------
gulp.task('watch', ['assets', 'templates', 'sass', 'browserify', 'browser-sync'], function() {
// --------------------------
// watch:sass
// --------------------------
gulp.watch('./client/scss/**/*.scss', ['reload-sass']);
// --------------------------
// watch:js
// --------------------------
gulp.watch('./client/js/**/*.js', ['lint:js', 'reload-js']);
// --------------------------
// watch:html
// --------------------------
gulp.watch('./templates/**/*.html', ['reload-templates']);
gutil.log(gutil.colors.bgGreen('Watching for changes...'));
});
// build task
gulp.task('build', [
'clean',
'templates',
'assets',
'sass',
'browserify'
]);
gulp.task('default', ['watch']);
// gulp (watch) : for development and livereload
// gulp build : for a one off development build
// gulp build --production : for a minified production build
@alexbw

This comment has been minimized.

Copy link

alexbw commented Feb 22, 2015

Fantastic! You mentioned in a recent blog post that you access your site locally via port 8000. How are you configuring this proxy, and are you doing it in Vagrant?

@jterskine

This comment has been minimized.

Copy link

jterskine commented Mar 4, 2015

+1

@roganov

This comment has been minimized.

Copy link

roganov commented Mar 15, 2015

How do you deal with third-party styles and scripts (such as Twitter Bootstrap)? Do you put these files under client directory?

@mlouro

This comment has been minimized.

Copy link
Owner Author

mlouro commented Jul 9, 2015

@roganov, the third-party styles go into client/assets and get copied over to the build directory.

@alexbw, the proxy is setup via browserSync's options, it's not in this gist, see https://github.com/lincolnloop/generator-frigate/blob/master/app/templates/_gulp.config.js#L44-L46. When proxyOptions.proxy is set, browserSync will just forward requests it can't handle over to the address you define there.

@steppo40

This comment has been minimized.

Copy link

steppo40 commented Sep 11, 2015

+1

@deepflame

This comment has been minimized.

Copy link

deepflame commented Sep 18, 2015

nicely structured, well done. Thanks for sharing!

@Dovizu

This comment has been minimized.

Copy link

Dovizu commented Sep 27, 2015

I had to comment out var req = build ? ['clean'] : []; and replace it with var req = []; on line 221, because for some reason gulp build would stop right after clean.

$ gulp build
autoprefixer-core was deprecated. Use autoprefixer package.
[01:17:14] Using gulpfile /code/gulpfile.js
[01:17:14] Starting 'clean'...

If anyone knows why or has a better fix, please help!

@marlomgirardi

This comment has been minimized.

Copy link

marlomgirardi commented Sep 27, 2015

Hey! child_process isn't recommended.

New implementation:

//...
let exec = require('gulp-exec');
let autoprefixer = require('gulp-autoprefixer'); // old autoprefixer-core
//...

let beep = function() {
    let os = require('os');
    let error = gulp.src('path/error.wav');
    if (os.platform() === 'linux') {
        error.pipe(exec('aplay <%= file.path %>'));
    } else {
        // mac
        error.pipe(exec('afplay <%= file.path %>'));
    }
};

//...
@rafaelvasco

This comment has been minimized.

Copy link

rafaelvasco commented Sep 30, 2015

Many thanks for this ! Using it for everything now :D

@petulla

This comment has been minimized.

Copy link

petulla commented Oct 25, 2015

Thanks for this. Did you make the package.json available?

@kevinkucharczyk

This comment has been minimized.

Copy link

kevinkucharczyk commented Nov 5, 2015

I ran into the same problem as @Dovizu. Apparently new versions of del have changed in how they handle the callback. Here's an improved clean task which will run properly:

clean: function(callback) {
  del(['build/']).then(function() {
    callback();
  });
}
@tucq88

This comment has been minimized.

Copy link

tucq88 commented Nov 13, 2015

So the SASS changes you gonna reload the browser instead of inject only changes ?

@iamdash

This comment has been minimized.

Copy link

iamdash commented Jan 22, 2016

Awesome. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.