Jair Trejo

Baby steps with gulp.js

I am working on a little front-end project to learn Backbone.js, and this presentation by Addy Osmani convinced me to invest the time to set up a proper continuous build workflow.

I tried several templates, but they all seemed unnecesarily complicated, dependency heavy, and full of features I don’t really need. So I decided to build my own setup from scratch trying to keep things as simple as possible.

The goal was to have a build setup with:

  • HTML minification.
  • JavaScript and CSS minification and concatenation.
  • Source map support.
  • Simple client-side dependency installation with Bower.
  • Bootstrap as a less mixin library.
  • BrowserSync support. Any live reload tool would do, but BrowserSync looks cool.

The final template is available at http://github.com/jairtrejo/simple-gulp-template.

Initial setup

First I had to install nodejs, which was simple enough to do from the official website. Next I had to create a node package so that I could use npm to manage my development dependencies.

$ npm init
{% endsyntax%}

I answered the questions and it generated a `package.json` in my working directory.

Next I chose [gulp.js](http://gulpjs.com) as my build tool. I found it easier to understand than `grunt` and it seems faster, too. I installed it with:

```bash
$ sudo npm install -g gulp
$ npm install --save-dev gulp

This installs gulp globally (to be able to use the command line tool) and locally (to be able to require() it in our Gulpfile).

Finally I installed bower for fetching client-side packages.

$ sudo npm install -g bower
$ npm install --save-dev bower
{% endsyntax%}

## HTML minification

Just to check that everything was working I wrote a very simple `Gulpfile.js` with just one task to minify my HTML.

```js
var gulp       = require("gulp"),
    minifyHTML = require("gulp-minify-html");

var config = {
    paths: {
        html: {
            src:  ["src/**/*.html"],
            dest: "build"
        },
    }
}

gulp.task("html", function(){
    return gulp.src(config.paths.html.src)
        .pipe(minifyHTML())
        .pipe(gulp.dest(config.paths.html.dest));
});

First I require() the gulp API and the gulp-minify-html plugin.

Next I define a config var just to keep all magic values in one place.

Finally I define a task named html that gathers all files under src and all of its subdirectories, as specified by the src/**/*.html glob. It then pipes them through the minification task and finally outputs them to the build directory.

I had to install gulp-minify-html (and save it as a dependency) by running:

$ npm install --save-dev gulp-minify-html

And then I created a src directory and placed the following index.html inside.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Baby steps</title>
  </head>
  <body>
    <h1>Hello, world!</h1>
  </body>
</html>

I ran the task in the command line:

$ gulp html

And sure enough, a minified index.html appeared inside the build folder.

{% syntax html%}

Baby steps

Hello, world!


## JavaScript and CSS minification

Next up, I wrote tasks for concatenating and minifying my scripts and styles.

```javascript
var gulp       = require("gulp"),
    minifyHTML = require("gulp-minify-html"),
    concat     = require("gulp-concat"),
    uglify     = require("gulp-uglify"),
    cssmin     = require("gulp-cssmin");

var config = {
    paths: {
        html: {
            src:  ["src/**/*.html"],
            dest: "build"
        },
        javascript: {
            src:  ["src/js/**/*.js"],
            dest: "build/js"
        },
        css: {
            src: ["src/css/**/*.css"],
            dest: "build/css"
        }
    }
}

gulp.task("html", function(){
    return gulp.src(config.paths.html.src)
        .pipe(minifyHTML())
        .pipe(gulp.dest(config.paths.html.dest));
});

gulp.task("scripts", function(){
    return gulp.src(config.paths.javascript.src)
        .pipe(uglify())
        .pipe(concat("app.min.js"))
        .pipe(gulp.dest(config.paths.javascript.dest));
});

gulp.task("css", function(){
    return gulp.src(config.paths.css.src)
        .pipe(cssmin())
        .pipe(gulp.dest(config.paths.css.dest));
});

gulp.task("default", ["html", "scripts", "css"]);

They simply gather all the files and pass them through the relevant minifiers. For JavaScript files I also concatenate them with gulp-concat.

I added a CSS file in src/css and a JavaScript file in src/js.

/* src/css/main.css */
h1 {
  color: #c00;
}
/* src/js/app.js */
var msg = "Hello";

console && console.log(msg);

And ammended my index.html to include the resulting assets.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Baby steps</title>
    <link href="css/main.min.css" rel="stylesheet" />
  </head>
  <body>
    <h1>Hello, world!</h1>
    <script type="text/javascript" src="js/app.min.js"></script>
  </body>
</html>

I ran an http server on the build directory:

$ python -m SimpleHTTPServer 8000

And in a separate shell I installed all the new dependencies and ran my default task, that just combines all of the others:

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

Upon navigating to http://localhost:8000 a red “Hello, world!” greeted me in the browser and a “Hello” was printed to the JS console.

Source maps

If you check the console output in the browser, you’ll note that the message is said to come from app.min.js, line 1. Of course, app.min.js is the final, minified file with everything in one line. Wouldn’t it be cool to know where was the message logged in the original source code file?

Source maps let you know just that. They are a way to annotate generated code with a mapping of the corresponding places in the original source. There are source maps for both JavaScript and CSS, and it works not only with minifyers, but also with preprocessors and even compilers from other languages.

The gulp-sourcemaps plugin provides source map output for supported gulp transformations. It is very easy to use: You pass your gathered files through sourcemaps.init, apply the transformations and hand them to sourcemaps.write.

My scripts and css tasks ended up like this:

var sourcemaps = require("gulp-sourcemaps");

gulp.task("scripts", function () {
  return gulp
    .src(config.paths.javascript.src)
    .pipe(sourcemaps.init())
    .pipe(concat("app.min.js"))
    .pipe(uglify())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(config.paths.javascript.dest));
});

gulp.task("css", function () {
  return gulp
    .src(config.paths.css.src)
    .pipe(sourcemaps.init())
    .pipe(concat("main.min.css"))
    .pipe(cssmin())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(config.paths.css.dest));
});

And if we install gulp-sourcemaps and run the default task again:

$ npm install --save-dev gulp-sourcemaps
$ gulp

We can refresh the page and see that the line number in the original file is correctly reported.

Front-end package management with Bower

Bower is a very simple tool for downloading and managing front-end libraries. It lets you specify your library dependencies in a manifest file, fetches them and makes them available to your build tool.

I created the manifest file with:

$ bower init

And then proceeded to install my dependencies:

$ bower install --save backbone

Notice that it is --save and not --save-dev, because this component will eventually go into the build directory.

I now needed a task for copying the files to the build directory. The main-bower-files module checks your bower.json and recursively generates an src glob with all the main files specified by your production dependencies. The task feeds that to gulp to copy to the appropiate place.

var mainBowerFiles = require("main-bower-files");

gulp.task("bower", function () {
  return gulp
    .src(mainBowerFiles(), { base: "bower_components" })
    .pipe(gulp.dest(config.paths.bower.dest));
});

The index.html file should be amended to include this new front-end files. It can be done by hand, but that is cumbersome and prone to error, so I let the gulp-inject plugin help me with that. I had to insert placeholders in index.html for where the injected references will go:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Baby steps</title>
    <!-- bower:css -->
    <!-- endinject -->
    <link href="css/main.min.css" rel="stylesheet" />
  </head>
  <body>
    <h1>Hello, world!</h1>
    <!-- bower:js -->
    <!-- endinject -->
    <script type="text/javascript" src="js/app.min.js"></script>
  </body>
</html>

And I modified the HTML processing task to inject the references:

var mainBowerFiles = require("main-bower-files"),
  inject = require("gulp-inject");

gulp.task("html", function () {
  return gulp
    .src(config.paths.html.src)
    .pipe(
      inject(
        gulp.src(mainBowerFiles(), { read: false, cwd: "bower_components" }),
        { name: "bower", addPrefix: "lib" }
      )
    )
    .pipe(minifyHTML())
    .pipe(gulp.dest(config.paths.html.dest));
});

The inject function takes an src generated stream and some options, and inserts references to those assets into the piped HTML files.

The task generates the stream from the main-bower-files output, but sets bower_components as the cwd to start paths from inside there. The task is only interested in the paths, not the contents, so to speed things up the read: false option is passed.

The name option passed to inject specifies a name for this group of files in the placeholder, and the addPrefix option makes the paths start in the proper place inside build.

After installing the new dependencies I ran the default task again:

$ npm install --save-dev main-bower-files gulp-inject
$ gulp

And the references to Backbone and Underscore were correctly inserted into the HTML.

The cool thing is that this setup “just works” for other Bower packages. For instance, to use FontAwesome I just installed it:

$ bower install --save font-awesome

And I was able to use it right away:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Baby steps</title>
    <!-- bower:css -->
    <!-- endinject -->
    <link href="css/main.min.css" rel="stylesheet" />
  </head>
  <body>
    <h1>Hello, world! <i class="fa fa-thumbs-up"></i></h1>
    <!-- bower:js -->
    <!-- endinject -->
    <script type="text/javascript" src="js/app.min.js"></script>
  </body>
</html>

By running gulp again and refreshing the browser, the page gave me the thumbs up.

Bootstrap as a less mixin library

This is something I have wanted to do for a very long time. I love Bootstrap, it is a very cool default for quick projects and provides a great foundation for styling. But it’s unsemantic classes have allways rubbed me the wrong way. Take, for instance:

{% syntax html%}