Automate Your Tasks With GulpJS

Automate Your Tasks With GulpJS

This tutorial is a little more on the advanced side and assumes basic knowledge of linux, node, and sass. One thing you will want to pay attention to is your node version. Odd version numbers of Node.js are considered unstable development versions. You also want to make sure you running a version of node that's sufficient for the packages you are planning to use, this doesn't always mean the latest! Check your node version by typing node -v from the command line. Here is what I get.

node -v
//v4.0.0

A gulpfile (gulpfile.js) is just another node program/module and as such you can use all packages available on npm in it.

Once you have node set up, our first step is to install the gulp cli:

//this step actually installs gulp as a command on your SYSTEM
npm install --global gulp
gulp -v

Next you’ll need to add gulp in the devDependencies section of your package.json file on any of the projects you want to use it in. Make sure that you have your package.json created by manually creating it or typing npm init. To install gulp to your package.json, type the following:

//create package.json
npm init
//follow screen prompts to enter project name, version, description, etc.
 
//this step installs gulp to your PROJECT
npm install --save-dev gulp
 
//now install lots of other awesome packages!
npm install --save-dev gulp-sass
npm install --save-dev gulp-watch
npm install --save-dev gulp-uglify
npm install --save-dev gulp-minify-css
//etc.

Let's compare your package.json and one I'm using.

{
    "name": "username",
    "version": "1.0.0",
    "private": true,
    "description": "some description",
    "main": "index.js",
    "scripts": {
        "test": "some test message"
    },
    "dependencies": {},
    "repository": {},
    "keywords": [
        "automation",
        "theme",
        "node",
        "gulp",
        "uglify"
    ],
    "author": "User Name",
    "license": "ISC",
    "devDependencies": {
        "gulp": "^3.9.0",
        "gulp-clean": "^0.3.1",
        "gulp-concat": "^2.6.0",
        "gulp-jslint": "^0.2.2",
        "gulp-minify-css": "^1.2.1",
        "gulp-sass": "^2.0.4",
        "gulp-uglify": "^1.4.1",
        "gulp-watch": "^4.3.5",
        "run-sequence": "^1.1.4"
    }
}

With this, you should be able to run npm install from your project directory (where you have this package.json file) and npm will pull down all of your packages and place them into a directory called node_modules.

Now, we are almost ready to start crafting our gulpfile, but before we do, lets create a configuration file that we can use to point to our assets. We'll be using these paths over and over, so to keep your gulpfile readable, it's much cleaner to use a config. You'll obviously need to edit this file to use the directory structure YOU have chosen.

/*globals exports */
(function () {
    'use strict';
 
    exports.module = {
        "files": {
            "gulpfile": "gulpfile.js",
            "src": {
                "templates": [
                    "src/**/*.php",
                    "!src/lib/**/*.php"
                ],
                "images": "src/images/**/*.{png,svg,jpg,gif}",
                "js": [
                    "src/js/**/*.js"
                ],
                "scss": "src/scss/**/*.scss",
                "cssOutputDir": "src/css/",
                "css": "src/css/**/*.css",
                "lib": "src/lib/",
            },
            "dist": {
                "home": "assets/",
                "images": "assets/images/",
                "js": "assets/js/",
                "lib": "assets/lib/",
                "css": "assets/css/"
            }
        }
    };
 
}());

Now that you have your config file set up, it's time to start building the tasks that you need. The first thing you need to do is gain access to the node modules you imported with your package.json. Create shorthand variables for each of them like so:

/*globals console, require */
(function () {
    'use strict';
 
    var // package configuration
        conf = require('./gulpConfig.js').module,
 
        // gulp dependencies
        gulp = require('gulp'),
        runSequence = require('run-sequence'),
        clean = require('gulp-clean'),
        uglify = require('gulp-uglify'),
        jslint = require('gulp-jslint'),
        concat = require('gulp-concat'),
        sass = require('gulp-sass'),
        cssmin = require('gulp-minify-css');
 
}());

The gulp api is incredibly light containing 4 top level functions. They are gulp.task, gulp.src, gulp.dest, and gulp.watch.

gulp.task defines your tasks. It takes three arguments which are name, dependencies and a function.

Where name is a string, dependencies is an array of task names, and function is the function that performs your task. Dependencies are optional so gulp.task in it’s two forms are

    gulp.task('mytask', function() {
        //basic syntax of a gulp task
        //do stuff here
        //return ...
    });
 
    gulp.task('dependenttask', ['mytask'], function() {
        //gulp will start 'dependenttask' after 'mytask' has started
    });

One quirk, that is soon to be fixed, is that while gulp controls the order by which tasks start, it's asynchronous so tasks can accidentally step on each others toes sometimes. One place where lots of people get tripped up is when they are trying to have gulp run a "clean build" task before building a project. What happens is while you specify "clean-build" as a dependency task to your "build" related tasks, clean build isn't guaranteed to finish before the others begin. Ahh the race-conditions 🙂

To get around this, I use the run-sequence module. I'm only using this because gulp doesn't yet have native support for the synchronous nature I'm looking for. Anyway, you get the point.

gulp.src points to the files we want to use. It’s parameters are globs and an optional options object. It uses .pipe() for chaining it’s output into other plugins.

gulp.dest points to the output folder we want to write files to.

gulp.src and gulp.dest used to simply copy files looks like:

gulp.task('copy:templates', function() {
    // copy any html files in src/ to dist/
    gulp.src('src/*.html')
        .pipe(gulp.dest('dist'));
});

Gulp requires a "default" tasks, so be sure to start there. I'll be adding more to this tutorial, but that's all I have time for at the moment. Here's a well fleshed-out gulpfile to help you through your journey. Enjoy!

/*globals console, require */
(function () {
    'use strict';
 
    var // package configuration
        conf = require('./gulpConfig.js').module,
 
        // gulp dependencies
        gulp = require('gulp'),
        runSequence = require('run-sequence'),
        clean = require('gulp-clean'),
        uglify = require('gulp-uglify'),
        jslint = require('gulp-jslint'),
        concat = require('gulp-concat'),
        sass = require('gulp-sass'),
        cssmin = require('gulp-minify-css');
 
    function runLinter(fileGlob) {
        return gulp.src(fileGlob)
            .pipe(jslint({node: true}).on('error', function (error) {
                console.error(String(error));
            }));
    }
 
    gulp.task('clean-build', function () {
        return gulp.src(conf.files.dist.home + '*', {read: false})
            .pipe(clean());
    });
 
    gulp.task('gulpfile:jslint', function () {
        return runLinter(conf.files.gulpfile);
    });
 
    gulp.task('copy:images', function () {
        return gulp.src(conf.files.src.images)
            .pipe(gulp.dest(conf.files.dist.images));
    });
 
    gulp.task('copy:cssImageAssets', function () {
        return gulp.src([
            conf.files.src.lib + 'share/**/*.{png,svg,jpg,gif}'
        ])
            .pipe(gulp.dest(conf.files.dist.css));
    });
 
    gulp.task('copy:templates', function () {
        return gulp.src(conf.files.src.templates)
            .pipe(gulp.dest(conf.files.dist.home));
    });
 
    gulp.task('sass', function () {
        return gulp.src(conf.files.src.scss)
            .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
            .pipe(gulp.dest(conf.files.src.cssOutputDir));
    });
 
    gulp.task('concat:vendorjs', function () {
        return gulp.src([
            conf.files.src.lib + 'jquery/jquery-2.1.4.js',
            conf.files.src.lib + 'lettering/jquery.lettering.js',
            conf.files.src.lib + 'textillate/jquery.textillate.js',
            conf.files.src.lib + 'browserfy/browserfy.jquery.js',
            conf.files.src.lib + 'share/jquery.share.js'
        ])
            .pipe(concat({ path: 'vendor.js', stat: { mode: '0666' }}))
            .pipe(uglify())
            .pipe(gulp.dest(conf.files.dist.js));
    });
 
    gulp.task('concat:js', function () {
        return gulp.src(conf.files.src.js)
            .pipe(concat({ path: 'scripts.js', stat: { mode: '0666' }}))
            .pipe(uglify())
            .pipe(gulp.dest(conf.files.dist.js));
    });
 
    gulp.task('concat:css', function () {
        return gulp.src(conf.files.src.css)
            .pipe(concat({ path: 'app.css', stat: { mode: '0666' }}))
            .pipe(gulp.dest(conf.files.dist.css));
    });
 
    gulp.task('concat:vendorcss', function () {
        return gulp.src([
            conf.files.src.lib + 'animate/animate.css',
            conf.files.src.lib + 'share/jquery.share.css'
        ])
            .pipe(concat({ path: 'vendor.css', stat: { mode: '0666' }}))
            .pipe(cssmin())
            .pipe(gulp.dest(conf.files.dist.css));
    });
 
    gulp.task('default', function () {
        return console.log('start the engine');
    });
 
    gulp.task('test', ['gulpfile:jslint']);
 
    gulp.task('build', function () {
        runSequence(
            'clean-build',
            'test',
            'sass',
            'copy:cssImageAssets',
            'concat:css',
            'concat:vendorcss',
            'concat:js',
            'concat:vendorjs',
            ['copy:images', 'copy:templates']
        );
    });
 
    gulp.task('watch', function () {
        gulp.watch(conf.files.src.scss, ['sass']);
    });
 
}());