Gulp.js
The streaming build system
Table of contents
- What is Gulp?
- Grunt VS Gulp
- The Gulp API
- What's next?
- Hands On Gulp
What is Gulp?
Gulp is a build framework, much like Grunt, Assetic, or Capistrano, but whose particularity is to work by streams, or pipes.
This way, it privilegies code over configuration,
making building tasks fairly simple and easily maintainable.
Example
gulp.task('task', function(){
return gulp.src('**/*.file', {cwd: 'app/'})
.pipe(coffee())
.pipe(concat())
.pipe(uglify())
.pipe(gulp.dest('dist/'))
});
- Easy to configure
- Plain Old Node Modules
- Chainable syntax
- Really fast
What is a stream?
Pipes
Think of a stream as a flow of data flowing into pipes.
Data comes raw into the pipe, then passes a serial of processors one after another, to finally return the desired result.
Think of a build system in your head
Not this...
This is what comes into mind:
1) Grunt plugins
- Grunt relies too much on plugins
- Plugin to clean, to open a browser...
- Not compatible with node modules
- Everything needs to be under initConfig so...
- Want to run a simple command? (ex: bower)
- Well you can't!
2) Configuration all over the place
grunt.initConfig({
htmlmin: {
options: {
collapseWhitespace: true,
removeComments: false
},
docs: {
files: [{
expand: true,
cwd: '<%= yo.pages %>',
src: ['*.html'],//, 'views/{,*/}*.html'],
dest: '<%= yo.pages %>'
}]
}
},
copy: {
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
dest: '<%= yo.dist %>',
src: '{,*/}*.css'
}]
},
docs: {
files: [{
expand: true,
cwd: '<%= yo.docs %>/',
dest: '<%= yo.pages %>',
src: [
'images/*',
'1.0/**/*'
]
}]
}
}
});
- Configuration for everything
- A lot of options, needing people to look at the npm documentation almost every time
- Subtasks need to be defined inside the configuration object
- Ex: concat:dist, concat:dev, concat:docs ...
- Configuration values are primitive types (strings, numbers)... so we need the ugly "<%= yo.src %>"...
2) Configuration all over the place
3) Temporary folders
3) Temporary folders
- Grunt is a mess to understand
- Should we compile to .tmp or dist?
- So we first copy to .tmp then dist?
- Much slower
- Grunt needs to check the filesystem for each task
- First write into tmp, then write into dist
So is Grunt so bad?
- Grunt has a much larger community than Gulp (for now)
- Gulp is still in its early phases, meaning there could be bugs
- Grunt is less error-prone: If something fails, you can immediately know where it is.
There are people who prefer configuration over code (especially non-Node developers).
Grunt advantages
The Gulp API
gulp.task
It all starts with a task
gulp.task('compass', ['clean'], function(){
// Process data inside...
});
- Registers a task "compass"
- Has a dependency, "clean", that will run before this task.
- Gulp tasks run with maximal concurrency
gulp.src
Drink it up!
gulp.task('compass', ['clean'], function(){
return gulp.src('**/*.scss', {cwd: 'app/styles'})
// what to do with this stream
});
- The first argument is a glob (pattern with *) of the files to drink
- The second argument is a list of options (see node-glob)
- Returns a stream that can be passed through pipes
.pipe
This is not a pipe
gulp.task('compass', ['clean'], function(){
return gulp.src('**/*.scss', {cwd: 'app/styles'})
.pipe(compass())
.pipe(autoprefixer('last 1 update'))
.pipe(concat('style.css'))
// Now we need to "spit out" the result
});
- The pipe takes a function that will process the current stream and return a new stream
- Can be chained (which is the whole point)
- Easy to understand what is going on
.pipe
gulp.dest
Spill the beans!
gulp.task('compass', ['clean'], function(){
return gulp.src('**/*.scss', {cwd: 'app/styles'})
.pipe(compass())
.pipe(autoprefixer('last 1 update'))
.pipe(concat('style.css'))
.pipe(gulp.dest('dist'));
});
- Write out the processed stream into the destination
- Returns a stream... meaning you can still chain!
gulp.dest
gulp.task('compass', ['clean'], function(){
return gulp.src('**/*.scss', {cwd: 'app/styles'})
.pipe(compass())
.pipe(autoprefixer('last 1 update'))
.pipe(concat('style.css'))
.pipe(gulp.dest('dist'))
// Immediately after writing style.css, create a minified version
.pipe(rename(function (path) { path.extname = '.min.css'; }))
// uglify
.pipe(csso())
.pipe(gulp.dest('dist'));
});
gulp.watch
The watchers
gulp.task('watch', function(){
gulp.watch('**/*.scss', ['clean', 'compass']).pipe(connect.reload());
gulp.watch('**/*.js', ['clean', 'compile']).pipe(connect.reload());
});
- Yes, gulp.watch also return streams, meaning that you can also chain them
- Incorporated into the gulp-core
That's it!
Now everyone can build up gulp tasks without reading pages of documentation!
Example 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');
});
});
Rules of thumb
- All gulp functions and requires are set at the top
- Sequential tasks (build, serve...) are set at the bottom
- Declare all sources at the top so you can use something like "src.scripts" instead of "**/*.js"
- LAZY LOADING!!!
- Since gulp is a simple node module, put each task in its own file and use module.exports!
- gulpfile.js will require the necessary tasks.
- Don't forget to listen to error events to prevent the watchers to stop!
Useful gulp plugins
- gulp-size: show the size of the compiled file
- gulp-sourcemaps: generate sourcemaps (annotated comments useful to debug inside a concatenated file)
- gulp-ngtemplate: put all templates inside the $templateCache
- gulp-autoprefixer: automatically generate css prefixes (moz-, webkit-...) when necessary
- gulp-changed: run the task only when files change
- wiredep: automatically inserts bower_components inside html
- gulp-plumber: prevent errors to stop gulp
What's next?
Gulp 4.0
- Parallel and Sequential Tasks
- Better task management
- Implicit error management (no need for plumber!)
- Import and export recipes...
Available Plugins
- Sublime Text/TextMate plugin
- PHPStorm 8/Webstorm 9
- Oh My Zsh plugin
- Chrome Dev Tools extension
- Yeoman generators
- Even in Google Web Starter Kit!
- Make your own gulpfile with gulpFiction
- Search for app boilerplates with Slush
Sheelot?
Gulp.js
By Elior Boukhobza
Gulp.js
Gulp.js, the streaming build framework
- 1,464