Gulp
The streaming build system

Consulting, training, products, professional services
contact@wearefractal.comOpen source is at github.com/wearefractal
Streams
Your application is not a big truck - it's a series of tubes
We should have some ways of connecting programs like garden hose--screw in another segment when it becomes necessary to massage data in another way. This is the way of IO also.- Doug McIlroy
Streams come to us from the earliest days of unix and have proven themselves over the decades as a dependable way to compose large systems out of small components that do one thing well.
You can then plug the output of one stream to the input of another and use libraries that operate abstractly on streams to institute higher-level flow control.- substack
Understanding flow control is what makes you a programmer
Read a comprehensive overview of streams by substack
Learning new flow control techniques will make you more efficient
Why streams?
Picture a build system in your head.
(It should take in files, modify them, and output the new ones)
You pictured this

You didn't picture this

Grunt
What's so bad?
- Plugins do multiple things
- Want a banner? Use the javascript minifier
- Plugins do things that don't need to be plugins
- Need to run your tests? Use a plugin
- Grunt config format is a mess that tries to do everything
- Not idiomatic with "the node way"
- Headache of temp files/folders due to bad flow control
Your build system should empower not impede
It should only manipulate files - let other libraries handle the rest.
Sample Gruntfile
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
qunit: {
files: ['test/**/*.html']
},
jshint: {
files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
// options here to override JSHint defaults
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);
};
-
Runs tests
-
Lints code
-
Concats javascript
-
Minifies it
-
Runs again if files are changed
Switching

Sample Gulpfile
var gulp = require('gulp');
var pkg = require('./package.json');
var concat = require('gulp-concat');
var minify = require('gulp-minify');
var jshint = require('gulp-jshint');
var spawn = require('child_process').spawn;
var scriptFiles = './src/**/*.js';
gulp.task('compile', function(){
// concat all scripts, minify, and output
gulp.src(scriptFiles)
.pipe(concat({fileName: pkg.name+".js"})
.pipe(minify())
.pipe(gulp.dest('./dist/'));
});
gulp.task('test', function(){
// lint our scripts
gulp.src(scriptFiles).pipe(jshint());
// run our tests
spawn('npm', ['test'], {stdio: 'inherit'});
});
gulp.task('default', function(){
gulp.run('test', 'compile');
gulp.watch(scriptFiles, function(){
gulp.run('test', 'compile');
});
});
-
Runs tests
-
Lints code
-
Concats javascript
-
Minifies it
-
Runs again if files are changed
What's the difference?
- With Gulp your build file is code, not config
- You use standard libraries to do things
- Plugins are simple and do one thing - most are a ~20 line function
- Tasks are executed with maximum concurrency
- I/O works the way you picture it
Gulp does nothing but provide some streams and a basic task system
Gulp has only 5 functions you need to learn

gulp.task(name, fn)
It registers the function with a name.
You can optionally specify some dependencies if other tasks need to run first.
gulp.run(tasks...)
Runs all tasks with maximum concurrency
gulp.watch(glob, fn)
Runs a function when a file that matches the glob changes
Included in core for simplicity
gulp.src(glob)
This returns a readable stream.
Takes a file system glob (like grunt) and starts emitting files that match.
This is piped to other streams
gulp.dest(folder)
This returns a writable stream
File objects piped to this are saved to the file system
Congratulations
You are now a Gulp expert
For guides on how to make your own plugins check the README