Tag Archives: Gulp

More Gulp in Practice

In my introductory Gulp article, I explained how to set up Gulp, and how to use it to concatenate and minify JavaScript files. In this article, we’re going to see a bunch of techniques that you’ll find useful when working with Gulp in practice.

We’re going to start with the following package.json:

{
  "name": "gulptest",
  "version": "1.0.0",
  "description": "Learning to use Gulp.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Daniel D'Agostino",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-uglify": "^1.5.1"
  }
}

…and the following Gulpfile.js:

var gulp = require('gulp'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');
     
gulp.task('default', function() {
  return gulp.src('js/*.js')
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

This is where we left off in the previous Gulp article.

Restoring Packages

And as we have seen in the previous article, it takes a bit of work to set things up the first time. You have to install a bunch of packages, set up your package.json, and then write your Gulpfile. Fortunately, however, this needs to be done only once. We’ve been installing Gulp and the packages it requires using the –save-dev flag for a reason. Once they get added to the package.json, a person getting this file from source control needs only to run the following command:

npm install

We didn’t specify a package to install, so npm will look in your package.json file and install any packages you’re missing. No need to install all the required Gulp packages every time.

gulp-npm-install

It is still necessary, however, to install Gulp globally (npm install gulp -g) to be able to run the gulp command.

Watch

Some people think it’s a pain in the ass to have to run Gulp every time you change a script. Well, it turns out that there’s a Gulp plugin that will run tasks automatically when changes are detected.

All we need to do is add a watch task that will execute a particular task when changes to a specified set of files are detected:

gulp.task('watch', function() {
    gulp.watch('js/*.js', ['default']);
});

Try this by making a change to one of your JavaScript files (e.g. adding a space) and saving the file. watch detects this and runs the default task:

gulp-watch

JSHint

JavaScript is known to be a language with a lot of pitfalls, and there are many reasons why it takes extra effort to do things right. I’m not going to get into the details here; read JavaScript: The Good Parts (book) if you’re curious.

Fortunately, however, there’s a set of tools we can use to help us. These tools carry out a process called linting, which means syntactic analysis of the code. Just like with compiling just about any programming language, syntactic analysis reports any errors in your programming syntax. That is very useful in catching bugs early.

A linter called JSHint can be used by Gulp. In order to use it, we’ll need to install both jshint and gulp-jshint:

npm install jshint gulp-jshint --save-dev

With that done, add JSHint to your task:

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');
     
gulp.task('default', function() {
  return gulp.src('js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

gulp.task('watch', function() {
    gulp.watch('js/*.js', ['default']);
});

The order of placement of your jshint() is important. If you place it after your concat() call, you’ll see all errors coming from all.js, rather than the actual source filenames.

Now, you’d think jQuery library code would pass linting with flying colours, right? Right?

gulp-jshint-jquery

Development and Production Tasks

If we’re minifying our JavaScript, then how do we debug it? We can’t possibly read minified code.

In fact, we don’t have to. We can set up different tasks for Gulp to run in development and production. For instance:

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');
     
gulp.task('dev', function() {
  return gulp.src('js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    //.pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

gulp.task('prod', function() {
  return gulp.src('js/*.js')
    //.pipe(jshint())
    //.pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

gulp.task('watch', function() {
    gulp.watch('js/*.js', ['dev']);
});

Notice I’ve changed the name of the tasks, and commented out different parts of the pipeline in dev and prod. We don’t want to minify our JavaScript during development because we want to be able to debug it, and we don’t want to waste time linting it when preparing a release, because hopefully it turned out impeccable as a result of our development. 🙂

Then, just run:

gulp <taskname>

See:

gulp-named-tasks

Task Dependencies

Right, but now, we lost the ability to just run gulp without specifying the task name, because we no longer have a default task. We can fix this as follows:

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

Instead of specifying a function for the task to execute, we’re giving it an array of task names it is dependent on. In this case, we’re saying that the default task is dependent on the dev task. So Gulp will run dev first, then default:

gulp-dependencies

This is particularly useful when you want to chain tasks. For instance, your dev task will depend on separate tasks for processing JavaScript and CSS. Speaking of which…

CSS

We’ve mostly seen examples dealing with JavaScript files, but Gulp can do much more than that.

Dealing with CSS is not very different from what we’ve seen so far, but we need a separate plugin for minification:

npm install gulp-minify-css --save-dev

Remember to add a require() for this at the top:

    minifycss = require('gulp-minify-css'),

With that available, we can now create a separate task to concatenate and minify CSS:

gulp.task('dev-css', function() {
  return gulp.src('css/*.css')
    .pipe(concat('all.css'))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/'));
});

This is a good time to rename our former dev task to dev-js for clarity:

gulp.task('dev-js', function() {
  return gulp.src('js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    //.pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

Then, using the task dependency mechanism we have seen in the previous section, we can set things up so that both these tasks are run when invoking gulp dev or just gulp:

gulp.task('dev', ['dev-js', 'dev-css']);

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

And here you go:

gulp-css-with-dependencies

Notify

Rather than explaining this one, let’s install it and see what it does:

npm install gulp-notify --save-dev

Let’s update our Gulpfile to notify us when one of the core tasks is ready:

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    minifycss = require('gulp-minify-css'),
    notify = require('gulp-notify'),
    concat = require('gulp-concat');
     
gulp.task('dev-js', function() {
  return gulp.src('js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    //.pipe(uglify())
    .pipe(gulp.dest('dist/'))
    .pipe(notify({ message: 'dev-js task complete' }));
});

gulp.task('dev-css', function() {
  return gulp.src('css/*.css')
    .pipe(concat('all.css'))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/'))
    .pipe(notify({ message: 'dev-css task complete' }));
});

gulp.task('prod', function() {
  return gulp.src('js/*.js')
    //.pipe(jshint())
    //.pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/'))
    .pipe(notify({ message: 'prod task complete' }));
});

gulp.task('dev', ['dev-js', 'dev-css']);

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

gulp.task('watch', function() {
    gulp.watch('js/*.js', ['dev']);
});

This, by the way, is the final Gulpfile for this article (in case you want to copy it for faster setting up).

Here’s the result of running Gulp with this:

gulp-notify

Not only does it write to the console, but you get a notification from your system tray. Nice.

Semicolon insertion

If you just bump gulp.src onto the next line like this:

gulp.task('dev-js', function() {
  return
    gulp.src('js/*.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'))
    .pipe(concat('all.js'))
    //.pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

…your task will mysteriously not run. That’s because of a JavaScript feature called semicolon insertion. Basically, JavaScript sees fit to put a semicolon after your return statement, making your task ineffective. Just make sure your return statement isn’t isolated like this, and use the style in the previous sections.

Getting more out of Gulp

There are many other useful plugins for Gulp that you can use. Check out Mark Goodyear’s article for examples involving SASS and image manipulation among other things.

The final package.json

Here you go. This should save you some time setting things up. Remember: npm install.

{
  "name": "gulptest",
  "version": "1.0.0",
  "description": "Learning to use Gulp.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Daniel D'Agostino",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-jshint": "^2.0.0",
    "gulp-minify-css": "^1.2.3",
    "gulp-notify": "^2.2.0",
    "gulp-uglify": "^1.5.1",
    "jshint": "^2.8.0"
  }
}

A Gentle Introduction to Gulp

We’re at the end of 2015, and web technology has changed quite  a bit since I started in 2002. Nowadays, for the front end stuff, there is a whole family of tools based on the node.js package manager (npm) that you can use to streamline and automate your workflow.

In this article (based on Windows), we’ll learn to use Gulp to do routine tasks such as concatenating and minifying JavaScript tasks. There’s another tool called Grunt with a similar purpose, and you’ll find all sorts of discussions on the internet comparing Grunt vs Gulp. Basically, Grunt is the older of the two and has a bigger community – an important factor considering that these tools are plugin-driven. However, I’m covering Gulp here as I felt it was more intuitive. For this small demonstration it has all the plugins we need, and performance (a common point of comparison) isn’t even a factor.

Setting up Gulp

The first thing we need is to install node.js:

install-nodejs

There’s a chance you might already have node.js, if you installed it with Visual Studio 2015.

Once you have installed node.js, you should have npm in your path. Open a command prompt, and install Gulp using the following command:

npm install gulp -g

-g means globally, and thanks to this, gulp should now be in your path.

Next, we want to create a package.json file. This is a kind of project file for node.js-related stuff. We can use npm for this too:

npm init

npm will ask a bunch of questions in order to set up the package.json file, suggesting possible answers where it makes sense to do so. name and version are required, but you can leave the rest empty if you like:

npm-init

Next, we need to install Gulp locally in our project:

npm install gulp --save-dev

This installs Gulp; –save-dev updates the package.json with a devDependencies field:

{
  "name": "gulptest",
  "version": "1.0.0",
  "description": "Learning to use Gulp.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Daniel D'Agostino",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^3.9.0"
  }
}

Plugins and the Gulp file

Gulp itself doesn’t do anything; it is just configured to run tasks. Its capabilities come from the plugins you install, and you configure it to do stuff using a Gulp file. For this simple example, we’re just going to use a few plugins:

npm install gulp-concat gulp-uglify --save-dev

Once again, –save-dev updates your devDependencies in package.json:

  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-uglify": "^1.5.1"
  }

Next, create a file called gulpfile.js, and put the following code in it:

var gulp = require('gulp'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');
    
gulp.task('default', function() {
  return gulp.src('js/*.js')
    .pipe(concat('all.js'))
    .pipe(gulp.dest('dist/'));
});

To test this out, I downloaded jquery and jquery-ui, and put the uncompressed Javascript files in a “js” folder. Having created the Gulpfile above, all you need is to run Gulp:

gulp

You should find a folder called dist, with a file called all.js in it, containing the contents of the files originally in the js folder:

gulp-concat

Concatenating JavaScript is good for performance because the browser only needs to make a single request, rather than having to retrieve several small files. But we can do even better by minifying the JavaScript (using the gulp-uglify plugin). Just add the following line:

var gulp = require('gulp'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat');
    
gulp.task('default', function() {
  return gulp.src('js/*.js')
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/'));
});

Run Gulp again, and you’ll find that all.js has been updated. In fact, it’s much smaller now, and it’s completely illegible:

gulp-uglify

Conclusion and Further Reading

The purpose of this article was to get you set up with Gulp, and see something working with the least possible hassle. Mark Goodyear’s article (on which this article is partly based) covers a lot of other common operations to carry out with Gulp. If you need to do anything particular – linting your JavaScript files, minifying your CSS, using Less, etc, there’s probably a plugin for it.

Beyond that, all you need to know is how to use Gulp effectively as part of your build process.

  • Running Gulp without arguments makes it look for the “default” task. You can pass the name of a task to run as an argument, allowing you to run a variety of operations.
  • How do you debug your minified JavaScript? You don’t. Use separate tasks for development and for release, and minify only in your release task.
  • Ideally these tasks should be run automatically as part of your continuous integration.
  • An ASP .NET 5 (formerly known as vNext) project in Visual Studio 2015 can easily integrate with npm tools, and you can configure it to run your tasks when you build.
  • Not using Windows? These command line tools are easy to use on other platforms (although installing npm will obviously be different).

Update 8th January 2016: Check out “More Gulp in Practice“, the followup to this article.