Angular Project Blackjack: 8 – Gulp Tasks

(This post is part of my “from scratch” AngularJS project. If you are feeling lost, the first post is here.)

Now that we have gulp setup and running and merging our files via gulp-concat, we don’t need to stop there. Let’s go ahead and make some more tasks to run and make our lives easier.

Testing

Since our unit tests are ready to be run from the command line via karma, we can add them as a gulp task. Karma is actually built into gulp, so there are no extra packages to install! In our gulpfile.js, let’s create a karma variable and a task to run our tests:

var karma = require('karma').server;

gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

We’re telling gulp to run our tests once and where to locate our karma configuration. Since gulp is powered by node, we can use the global variable __dirname to get the path that gulp is running from. You should now be able to run gulp test and watch our tests run!

Building

If you’ve completed the previous post and done any sort of debugging on it,
one thing you may have noticed is that debugging a concatenated app.js is very
confusing. We’ve optimized our project for our end users but we’ve made life difficult
for ourselves. This is where the whole “source” vs “release” builds comes in. What
we can do is have a separate folder that our production version will go into.
There are a lot of names used for these production folders: prod, release, deploy,
but we will use build in our project.

The first thing we’ll do to prepare our build is to change the js files that our index.html
loads depending on environment. We will do this by using gulp injection (not to be confused
with Angular’s dependency injection). Install the package with npm install gulp-inject --save-dev.
Next, we’ll need to prepare our index.html file for injection. This is done by inserting html comment markers that gulp will use:

<!DOCTYPE html>
<html data-ng-app="blackjack">
<head lang="en">
    <meta charset="UTF-8">
    <title>Blackjack</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <style>
        /* This helps the ng-show/ng-hide animations start at the right place. */
        /* Since Angular has this but needs to load, this gives us the class early. */
        .ng-hide {
            display: none!important;
        }
    </style>

    <!-- bower:css -->
    <!-- endinject -->

    <!-- inject:css -->
    <!-- endinject -->


</head>
<body>
    <div data-ng-include="'app/layout/shell.html'"></div>

    <!-- bower:js -->
    <!-- endinject -->

    <!-- Begin: Source JS -->
    <!-- inject:js -->
    <!-- endinject -->
    <!-- End: Source JS -->

</body>
</html>

You’ll notice that all of our files are gone! Don’t fret, they will be back when our task is completed. We’ve got a few different injection types in our index: bower:css, inject:css, bower:js, and inject:js.

Our injections will be separate based on if they are bower components or our code. The bower components that are injected will be the same on both environments (although, we could use the minified versions of each in the production environment, but it is a bit more configuration than I’d care to throw at this post!). What will change is our code that is getting injected. We want the concatenated version of our js files to go in the production release, while we want our files to be individually included for the development environment.

One more thing we want to do is to make sure our build folder could easily be deployed to a server. This means we want all the files necessary to serve our application within the build folder. There will be a few tasks that copy the necessary files to the build folder. Let’s look at our gulpfile.js:

var gulp = require('gulp');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');
var karma = require('karma').server;
var del = require('del');
var inject = require('gulp-inject');
var bowerFiles = require('main-bower-files');

//Configuration
var buildFolder = __dirname + '/build/';

//Tasks
gulp.task('test', function (done) {
    karma.start({
        configFile: __dirname + '/karma.config.js',
        singleRun: true
    }, done);
});

gulp.task('index',['clean'],function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'));
});

// Production Build Tasks
/**
 * Injects release files into index.html
 */
gulp.task('inject', ['concat', 'css', 'html', 'vendor'], function(){
    return gulp.src('./src/index.html')
        .pipe(gulp.dest('./build'))
        .pipe(inject(gulp.src('./build/vendor/*.js', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/vendor/*.css', {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./build/app.js', {read: false}), {relative: true}))
        .pipe(inject(gulp.src('./build/content/*.css', {read: false}), {relative: true}))
        .pipe(gulp.dest('./build'));
});

/**
 * Copies vendor files from bower to the build/vendor folder
 */
gulp.task('vendor', function () {
    return gulp.src(bowerFiles())
        .pipe(gulp.dest('./build/vendor'));
});

/**
 * Cleans the build folder
 */
gulp.task('clean', function() {
    del(buildFolder + '**');
});

/**
 * Concats application js files into app.js
 */
gulp.task('concat', function () {
    return gulp.src(['src/app/**/*.module.js', 'src/app/**/*.js', '!src/app/**/*.spec.js'])
        .pipe(concat('app.js'))
        .pipe(ngAnnotate())
        .pipe(gulp.dest(buildFolder));
});

/**
 * Copies application css to build folder
 */
gulp.task('css', function () {
    return gulp.src('./src/content/*.css')
        .pipe(gulp.dest('./build/content'));
});

/**
 * Copies html files to build folder
 */
gulp.task('html', function () {
    return gulp.src('./src/app/**/*.html')
        .pipe(gulp.dest('./build/app'));
});

// Development Build Tasks
/**
 * Updates the index.html file
 * with all the injections
 */
gulp.task('injectDev', function(){
    return gulp.src('./src/index.html')
        .pipe(inject(gulp.src(['./src/app/**/*.module.js','./src/app/**/*.js','!./src/app/**/*.spec.js'], {read: false}), {relative: true, name: ''}))
        .pipe(inject(gulp.src(bowerFiles(), {read: false}), {name: 'bower', relative: true}))
        .pipe(inject(gulp.src('./src/content/*.css',{read:false}), {relative: true}))
        .pipe(gulp.dest('./src'));
});




gulp.task('build', ['inject']);

gulp.task('buildDev', ['injectDev']);

We now have two main tasks: build and buildDev. As you can see, these execute a series of sub tasks in our gulp file. One nice thing I found was the npm package main-bower-files. This uses your bower.json dependencies and builds a list of files for use in gulp. It makes it much easier to include bower files into your injections.

With these build tasks complete, we should be able to run http-server from either the src folder or the build folder and have our application work as expected.

Here is the result of our work.

Up Next: Playing the game

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s