Lesson Monday

Rather than adding a ton of <script> and <link> tags to our HTML file and being responsible for adding a new one every time we add another Bower dependency, let’s use gulp to join our front-end dependency files together, like we did with our custom script files. We're going to use npm to install another gulp package called bower-files to help us do this:

$ npm install bower-files --save-dev

Using Gulp to Manage Front-End Javascript Dependencies

Now, let's use it in our gulpfile.

gulpfile.js
var lib = require('bower-files')();

gulp.task('bowerJS', function () {
  return gulp.src(lib.ext('js').files)
    .pipe(concat('vendor.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest('./build/js'));
});

...

First, we require the bower-files package, which returns a function. We tell that function to immediately run by placing an empty set of parenthesis after the call to require: require('bower-files')();. When we run this bower-files function, it returns a collection of all the files relevant to the dependencies stored in our Bower manifest file bower.json.

Then in our task we use gulp.src to pull in all the JavaScript files, and output one concatenated, minified file called vendor.js that we will load in our index.html file. We are filtering out only the .js files by using the ext method built into bower-files. ext stands for "extension", and we pass it in the extension we want ('js') as an argument. Finally, we use the same gulp.dest method to put the finished file into our build/js directory. Let's just remove our extra script tags from index.html. Now our <head> section should look like this:

index.html
  <head>
    <script src="build/js/vendor.min.js"></script>
    <script type="text/javascript" src="build/js/app.js"></script>
    <title>Ping Pong</title>
  </head>

All we need to do is run gulp bowerJS every time we add a new JavaScript front-end dependency. Let's add our build and tmp folders to our .gitignore file at this point too. Since we will be rebuilding these as we develop, there is no need to keep them in our repository.

Using Gulp to Manage Front-End CSS Dependencies

Next, we will add a similar task to load all of our CSS dependencies, such as bootstrap.min.css, into a single file too. We'll call it vendor.css and we'll include it in our index.html file too. We will put it into the build folder in a new folder we'll create called css.

index.html
<head>
  <link rel="stylesheet" href="build/css/vendor.css">
  <script src="build/js/vendor.min.js"></script>
  <script type="text/javascript" src="build/js/app.js"></script>
</head>

Now here is what the task looks like:

gulpfile.js
...
gulp.task('bowerCSS', function () {
  return gulp.src(lib.ext('css').files)
    .pipe(concat('vendor.css'))
    .pipe(gulp.dest('./build/css'));
});
...

It is structured the same way, except we are only taking files that end in .css from our lib and we are placing the final file in a new folder called css inside of our build folder.

There's one more thing we need to add before this will work. This is a special case with Bootstrap CSS. Essentially, we need to tell the bower-files package where to find the Bootstrap files that we are interested in. We do this by passing an object into our initial call to the bower-files package with some initialization settings in it. Instead of this line:

gulpfile.js
var lib = require('bower-files')();
...

We want this:

gulpfile.js
var lib = require('bower-files')({
  "overrides":{
    "bootstrap" : {
      "main": [
        "less/bootstrap.less",
        "dist/css/bootstrap.css",
        "dist/js/bootstrap.js"
      ]
    }
  }
});
...

If you're interested in more information about this, feel free to check out this issue on the bootstrap GitHub.

Combining Gulp Tasks

Finally, let's combine both these 2 Bower tasks into one, since they can run in parallel. Let's add this task to the gulpfile after our two Bower tasks:

gulpfile.js
gulp.task('bower', ['bowerJS', 'bowerCSS']);

We have created a task we can now run with gulp bower any time we add a bower dependency.

This task has no callback function, but it has 2 dependency tasks: bowerJS and bowerCSS. Incidentally, it is important to note that the order of the tasks in the dependency array is ignored by gulp. So we can't use this method if we need one task to be completed before the next one in the array begins. Our bower task above will run both the bowerJS and bowerCSS tasks concurrently. This is nice because it is faster than running them one after the other! But if we need to run task1 before task2, then we must specify task1 as a dependency of task2, not just list them in order as part of a third master task.

Let's make sure that this bower task runs automatically when we build. Since we will always want to include our vendor files whether or not we are making a production build, we will just call the bower task using gulp.start at the end of our build task.

gulpfile.js
...
gulp.task('build', ['clean'], function(){
  if (buildProduction) {
    gulp.start('minifyScripts');
  } else {
    gulp.start('jsBrowserify');
  }
  gulp.start('bower');
});
...

This means it will be run in parallel to our other tasks, but since our bower task only deals with 3rd party packages installed with Bower, we can safely leave these tasks independent of our other development tasks.