32 min read

In this article by Kerri Shotts, author of the Mastering PhoneGap Mobile Application Development, you will learn the following topics:

  • Logology, our demonstration app
  • Why Gulp for Task Automation
  • Setting up your app’s directory structure
  • Installing Gulp
  • Creating your first Gulp configuration file
  • Performing substitutions
  • Executing Cordova tasks
  • Managing version numbers
  • Supporting ES2015
  • Linting your code
  • Minifying/uglifying your code

(For more resources related to this topic, see here.)

Before we begin

Before you continue with this article, ensure that you have the following tools installed. The version that was used in this article is listed as well, for your reference:

You’ll need to execute the following in each directory in order to build the projects:

# On Linux / Mac OS X
$ npm install && gulp init

% On Windows
>npm install
> gulp init

If you’re not intending to build the sample application in the code bundle, be sure to create a new directory that can serve as a container for all the work you’ll be doing in this article.. Just remember, each time you create a new directory and copy the prior version to it, you’ll need to execute npm install and gulp init to set things up.

About Logology

I’m calling it Logology—and if you’re familiar with any Greek words, you might have already guessed what the app will be: a dictionary. Now, I understand that this is not necessarily the coolest app, but it is sufficient for our purposes. It will help you learn how advanced mobile development is done.

By the time we’re done, the app will have the following features:

  • Search: The user will be able to search for a term
  • Browse: The user will be able to browse the dictionary
  • Responsive design: The app will size itself appropriately to any display size
  • Accessibility: The app will be usable even if the user has visual difficulties
  • Persistent storage: The app will persist settings and other user-generated information
  • File downloads: The app will be able to download new content

Although the app sounds relatively simple, it’s complex enough to benefit from task automation. Since it is useful to have task automation in place from the very beginning, we’ll install Gulp and verify that it is working with some simple files first, before we really get to the meat of implementing Logology. As such, the app we build in this article is very simple: it exists to verify that our tasks are working correctly. Once we have verified our workflow, we can go on to the more complicated project at hand.

You may think that working through this is very time-consuming, but it pays off in the long run. Once you have a workflow that you like, you can take that workflow and apply it to the other apps you may build in the future. This means that future apps can be started almost immediately (just copy the configuration from a previous app). Even if you don’t write other apps, the time you saved from having a task runner outweigh the initial setup time.

Why Gulpfor task automation?

Gulp (http://gulpjs.com) is a task automation utility using the Node.js platform. Unlike some other task runners, one configures Gulp by writing the JavaScript code. The configuration for Gulp is just like any other JavaScript file, which means that if you know JavaScript, you can start defining tasks quickly.

Gulp also uses the concept of “streams” (again, from Node.js). This makes Gulp very efficient. Plugins can be inserted within these steams to perform many different transformations, including beautification or uglification, transpilation (for example, ECMAScript 6 to ECMAScript 2015), concatenation, packaging, and much more.

If you’ve performed any sort of piping on the command line, Gulp should feel familiar, because it operates on a similar concept. The output from one process is piped to the next process, which performs any number of transformations, and so on, until the final output is written to another location.

Gulp also tries to run as many dependent tasks in parallel as possible. Ideally, this makes running Gulp tasks faster, although it really depends on how your tasks are structured. Other task runners such as Grunt will perform their task steps in sequence, which may result in slower output, although tracing the steps from input to output may be easier to follow when the steps are performed sequentially.

That’s not to say that Gulp is the best task runner—there are many that are quite good, and you may find that you prefer one of them over Gulp. The skills you learn in this article can easily be transferred to other task running and build systems.

Here are some other task runners that are useful:

Installing Gulp

Installing Gulp is easy, but is actually a two-step process. The first step is to install Gulp globally. This installs the command-line utility, but Gulp actually won’t work without also being installed locally within our project. If you aren’t familiar with Node.js, packages can be installed locally and/or globally. A locally installed package is local to the project’s root directory, while a globally installed package is specific to the developer’s machine. Project dependencies are tracked in package.json, which makes it easy to replicate your development setup on another machine.

Assuming you have Node.js installed and package.json created in your project directory, the installation of Gulp will go very easily. Be sure to be positioned in your project’s root directory and then execute the following:

$ npm install -g gulp
$ npm install --save-dev gulp

If you receive an error while running these commands on OS X, you may need to run them with sudo. For example: sudo install -g gulp.

You can usually ignore any WARN messages.

It’s a good idea to be positioned in your project’s root directory any time you execute an npm or gulp command. On Linux and OS X, these commands generally will locate the project’s root directory automatically, but this isn’t guaranteed on all platforms, so it’s better to be safe than sorry.

That’s it! Gulp itself is very easy to install, but most workflows will require additional plugins that work with Gulp. In addition, we’ll also install Cordova dependencies for this project.

First, let’s install the Cordova dependencies:

$ npm install --save-dev cordova-lib cordova-ios cordova-android

cordova-lib allows us to programmatically interact with Cordova. We can create projects, build them, and emulate them—everything we can do with the Cordova command line we can do with cordova-lib. cordova-ios and cordova-android refer to the iOS and Android platforms that cordova platform add ios android would add. We’ve made them dependencies for our project, so we can easily control the version we build with.

While starting a new project, it’s wise to start with the most recent version of Cordova and the requisite platforms. Once you begin, it’s usually a good practice to stick with a specific platform version unless there are serious bugs or the like.

Next, let’s install the Gulp plugins we’ll need:

$ npm install --save-dev babel-eslint
cordova-android cordova-ios cordova-lib
cordova-tasks gulp gulp-babel gulp-bump
gulp-concat gulp-eslint gulp-jscs gulp-notify
gulp-rename gulp-replace-task
gulp-sourcemaps gulp-uglify gulp-util
              merge-stream rimraf

These will take a few moments to install; but when you’re done, take a look in package.json. Notice that all the dependencies we added were also added to the devDependencies. This makes it easy to install all the project’s dependencies at a later date (say, on a new machine) simply by executing npm install.

Before we go on, let’s quickly go over what each of the above utility does. We’ll go over them in more detail as we progress through the remainder of this article.

  • gulp-babel: Converts ES2015 JavaScript into ES5. If you aren’t familiar with ES2015, it has several new features and an improved syntax that makes writing mobile apps that much easier. Unfortunately, because most browsers don’t yet natively support the ES2015 features and syntax, it must be transpiled to ES5 syntax. Of course, if you prefer other languages that can be compiled to ES5 JavaScript, you could use those as well (these would include CoffeeScript and similar).
  • gulp-bump: This small utility manages version numbers in package.json.
  • gulp-concat: Concatenates streams together. We can use this to bundle files together.
  • gulp-jscs: Performs the JavaScript code style checks against your code. Supports ES2015.
  • gulp-eslint: Lints your JavaScript code. Supports ES2015.
  • babel-eslint: Provides ES2015 support to gulp-eslint.
  • gulp-notify: This is an optional plugin, but it is handy especially when some of your tasks take a few seconds to run. This plugin will send a notification to your computer’s notification panel when something of import occurs. If the plugin can’t send it to your notification panel, it logs to the console.
  • gulp-rename: Renames streams.
  • gulp-replace-task: Performs search and replace within streams.
  • gulp-sourcemaps: When transpiling ES2015 to ES5, it can be helpful to have a map between the original source and the transpiled source. This plugin creates them as a part of the workflow.
  • gulp-uglify: Uglifies/minifies code. While useful for code obfuscation, it also reduces the size of your code.
  • gulp-util: Additional utilities for Gulp, such as logging.
  • merge-stream: Merges multiple tasks.
  • rimraf: Easy file deletion. Akin to rm on the command line.

Creating your first Gulp configuration file

Gulp tasks are defined by the contents of the project’s gulpfile.js file. This is a JavaScript program, so the same skills you have with JavaScript will apply here. Furthermore, it’s executed by Node.js, so if you have any Node.js knowledge, you can use it to your advantage.

This file should be placed in the root directory of your project, and must be named gulpfile.js.

The first few lines of your Gulp configuration file will require the Gulp plugins that you’ll need in order to complete your tasks. The following lines then specify how to perform various tasks. For example, a very simple configuration might look like this:

var gulp = require("gulp");
gulp.task("copy-files", function () {
gulp.src(["./src/**/*"])
      .pipe(gulp.dest("./build"));
});

This configuration only performs one task: it moves all the files contained within src/ to build/. In many ways, this is the simplest form of a build workflow, but it’s a bit too simple for our purposes.

Note the pattern we use to match all the files. If you need to see the documentation on what patterns are supported, see https://www.npmjs.com/package/glob.

To execute the task, one can execute gulp copy-files. Gulp would then execute the task and copy all the files from src/ to build/.

What makes Gulp so powerful is the concept of task composition. Tasks can depend on any number of other tasks, and those tasks can depend on yet more tasks. This makes it easy to create complex workflows out of simpler pieces. Furthermore, each task is asynchronous, so it is possible for many tasks with no shared dependencies to operate in parallel.

Each task, as you can see in the prior code, is comprised of selecting a series of source files (src()), optionally performing some additional processing on each file (via pipe()), and then writing those files to a destination path (dest()). If no additional processing is specified (as in the prior example), Gulp will simply copy the files that match the wildcard pattern. The beauty of streams, however, is that one can execute any number of transformations before the final data is saved to storage, and so workflows can become very complex.

Now that you’ve seen a simple task, let’s get into some more complicated tasks in the next section.

How to execute Cordova tasks

It’s tempting to use the Cordova command-line interface directly, but there’s a problem with this: there’s no great way to ensure that what you write will work across multiple platforms. If you are certain you’ll only work with a specific platform, you can go ahead and execute shell commands instead; but what we’re going to do is a bit more flexible.

The code in this section is inspired by https://github.com/kamrik/CordovaGulpTemplate.

The Cordova CLI is really just a thin wrapper around the cordova-lib project. Everything the Cordova CLI can do, cordova-lib can do as well.

Because the Cordova project will be a build artifact, we need to be able to create a Cordova project in addition to building the project. We’ll also need to emulate and run the app. To do this, we first require cordova-lib at the top of our Gulp configuration file (following the other require statements):

var cordovaLib = require("cordova-lib");
var cordova = cordovaLib.cordova.raw;
var rimraf = require("rimraf");

Next, let’s create the code to create a new Cordova project in the build directory:

var cordovaTasks = {
    // CLI: cordova create ./build com.example.app app_name
    //              --copy-from template_path
create: function create() {
return cordova.create(BUILD_DIR, pkg.cordova.id,
                              pkg.cordova.name,
                {
lib: {
www: {
url: path.join(__dirname,
                                    pkg.cordova.template),
link: false
                    }
                  }
                });
    }
}

Although it’s a bit more complicated than cordova create is on the command line, you should be able to see the parallels. The lib object that is passed is simply to provide a template for the project (equivalent to –copy-from on the command line). In our case, package.json specifies that this should come from the blank/ directory. If we don’t do this, all our apps would be created with the sample Hello World app that Cordova installs by default.

Our blank project template resides in ../blank, relative from the project root. Yours may reside elsewhere (since you’re apt to reuse the same template), so package.json can use whatever path you need. Or, you might want the template to be within your project’s root; in which case, package.json should use a path inside your project’s root directory.

We won’t create a task to use this just yet — we need to define several other methods to build and emulate Cordova apps:

var gutil = require("gulp-util");
var PLATFORM = gutil.env.platform ? gutil.env.platform :"ios";
                                                  // or android
var BUILD_MODE = gutil.env.mode ? gutil.env.mode :"debug";
                                                  // or release
var BUILD_PLATFORMS = (gutil.env.for ? gutil.env.for
                                    : "ios,android").split(",");
var TARGET_DEVICE = gutil.env.target ? "--target=" +
gutil.env.target :"";

var cordovaTasks = {
create: function create() {/* as above */},
cdProject: function cdProject() {
process.chdir(path.join(BUILD_DIR, "www"));
    },
cdUp: function cdUp() {
process.chdir("..");
    },
copyConfig: function copyConfig() {
return gulp.src([path.join([SOURCE_DIR], "config.xml")])
                .pipe(performSubstitutions())
                .pipe(gulp.dest(BUILD_DIR"));
    },
    // cordova plugin add ...
addPlugins: function addPlugins() {
cordovaTasks.cdProject();
return cordova.plugins("add", pkg.cordova.plugins)
            .then(cordovaTasks.cdUp);
    },
    // cordova platform add ...
addPlatforms: function addPlatforms() {
cordovaTasks.cdProject();
function transformPlatform(platform) {
return path.join(__dirname, "node_modules",
"cordova-" + platform);
        }
return cordova.platforms("add",
pkg.cordova.platforms.map(transformPlatform))
              .then(cordovaTasks.cdUp);
    },
    // cordova build <platforms> --release|debug
    //                           --target=...|--device
build: function build() {
var target = TARGET_DEVICE;
cordovaTasks.cdProject();
if (!target || target === "" ||
target === "--target=device") {
target = "--device";
      }
return cordova.build({platforms:BUILD_PLATFORMS,
options: ["--" + BUILD_MODE,
target] })
            .then(cordovaTasks.cdUp);
    },
    // cordova emulate ios|android --release|debug
emulate: function emulate() {
cordovaTasks.cdProject();
return cordova.emulate({ platforms: [PLATFORM],
options: ["--" + BUILD_MODE,
                                        TARGET_DEVICE] })
            .then(cordovaTasks.cdUp);
    },
    // cordova run ios|android --release|debug
run: function run() {
cordovaTasks.cdProject();
return cordova.run({ platforms: [PLATFORM],
options: ["--" + BUILD_MODE,
"--device",
                                    TARGET_DEVICE] })
            .then(cordovaTasks.cdUp);
    },
init: function() {
return this.create()
            .then(cordovaTasks.copyConfig)
            .then(cordovaTasks.addPlugins)
            .then(cordovaTasks.addPlatforms);
    }
};

Place cordovaTasks prior to projectTasks in your Gulp configuration.

If you aren’t familiar with promises, you might want to learn more about them. http://www.html5rocks.com/en/tutorials/es6/promises/ is a fantastic resource.

Before we explain the preceding code, there’s another change you need to make, and that’s to projectTasks.copyConfig, because we move copyConfig to cordovaTasks:

var projectTasks = { …,
copyConfig: function() {
return cordovaTasks.copyConfig();
    }, ...
}

Most of the earlier mentioned tasks should be fairly self-explanatory — they correspond directly with their Cordova CLI counterparts. A few, however, need a little more explanation.

  • cdProject / cdUp: These change the current working directory. All the cordova-lib commands after create need to be executed from within the Cordova project directory, not our project’s root directory. You should notice them in several of the tasks.
  • addPlatforms: The platforms are added directly from our project’s dependencies, rather than from the Cordova CLI. This allows us to control the platform versions we are using. As such, addPlatforms has to do a little more work to specify the actual directory name of each platform.
  • build: This executes the cordova build command. By default, the CLI will build every platform, but it’s possible that we might want to control the platforms that are built, hence the use of BUILD_PLATFORMS. On iOS, the build for an emulator is different than the build for a physical device, so we also need a way to specify that, which is what TARGET_DEVICE is for. This will look for emulators with the name specified for the TARGET_DEVICE, but we might want to build for a physical device; in which case, we will look for device (or no target specified at all) and switch over to the –device flag, which forces Cordova to build for a physical device.
  • init: This does the hard work of creating the Cordova project, copying the configuration file (and performing substitutions), adding plugins to the Cordova project, and then adding the platforms.

Now is also a good time to mention that we can specify various settings with switches on the Gulp command line. In the earlier snippet, we’re supporting the use of –platform to specify the platform to emulate or run, –mode to specify the build mode (debug or release), –for to determine what platforms Cordova will build for, and –target for specifying the target device. The code specifies sane defaults if these switches aren’t specified, but they also allow the developer extra control over the workflow, which is very useful. For example, we’ll be able to use commands like these:

$ gulp build --for ios,android --target device
$ gulp emulate --platform ios --target iPhone-6s
$ gulp run --platform ios --mode release

Next, let’s write the code to actually perform various Cordova tasks — it’s pretty simple:

var projectTasks = {
    ...,
init: function init() {
return cordovaTasks.init();
    },
emulateCordova: function emulateCordova() {
return cordovaTasks.emulate();
    },
runCordova: function runCordova() {
return cordovaTasks.run();
    },
buildCordova: function buildCordova() {
return cordovaTasks.build();
    },
clean: function clean(cb) {
rimraf(BUILD_DIR, cb);
    },
    ...
}
 
…
gulp.task("clean", projectTasks.clean);
gulp.task("init", ["clean"], projectTasks.init);
gulp.task("build", ["copy"], projectTasks.buildCordova);
gulp.task("emulate", ["copy"], projectTasks.emulateCordova);
gulp.task("run", ["copy"], projectTasks.runCordova);

There’s a catch with the cordovaTasks.create method — it will fail if anything is already in the build/ directory. As you can guess, this could easily happen, so we also created a projectTasks.clean method. This clean method uses rimraf to delete a specified directory. This is equivalent to using rm -rf build. We then build a Gulp task named init that depends on clean. So, whenever we execute gulp init, the old Cordova project will be removed and a new one will be created for us.

Finally, note that the build (and other) tasks all depend on copy. This means that all our files in src/ will be copied (and transformed, if necessary) to build/ prior to executing the desired Cordova command. As you can see, our tasks are already becoming very complex, while also remaining graspable when taken singularly.

This means we can now use the following tasks in Gulp:

$ gulp init                   # create the cordova project;
                              # cleaning first if needed
$ gulp clean                  # remove the cordova project
$ gulp build                  # copy src to build; apply
                              # transformations; cordova build
$ gulp build --mode release   # do the above, but build in
                              # release mode
$ gulp build --for ios        # only build for iOS
$ gulp build --target=device  # build device versions instead of
                              # emulator versions
$ gulp emulate --platform ios # copy src to build; apply
                              # transformations;
                              # cordova emulate ios
$ gulp emulate --platform ios --target iPhone-6
                              # same as above, but open the
                              # iPhone 6 emulator
$ gulp run --platform ios     # copy src to build;
                              # apply transformations;
                              # cordova run ios --device

Now, you’re welcome to use the earlier code as it is, or you can use an NPM package that takes care of the cordovaTasks portion for you. This has the benefit of drastically shortening your Gulp configuration. We’ve already included this package in our package.json file — it’s named cordova-tasks, and was created by the author, and shares a lot of similarities to the earlier code. To use it, the following needs to go at the top of our configuration file below all the other require statements:

var cordova = require("cordova-tasks");
var cordovaTasks = new cordova.CordovaTasks(
    {pkg: pkg, basePath: __dirname, buildDir: "build",
sourceDir: "src", gulp: gulp, replace: replace});

Then, you can remove the entire cordovaTasks object from your configuration file as well. The projectTasks section needs to change only slightly:

var projectTasks = {
init: function init() {
return cordovaTasks.init();
    },
emulateCordova: function emulateCordova() {
return cordovaTasks.emulate({buildMode: BUILD_MODE,
platform: PLATFORM, options: [TARGET_DEVICE]});
    },
runCordova: function runCordova() {
return cordovaTasks.run({buildMode: BUILD_MODE,
platform: PLATFORM, options: [TARGET_DEVICE]});
    },
buildCordova: function buildCordova() {
var target = TARGET_DEVICE;
if (!target || target === "" ||
target === "--target=device") {
target = "--device";
      }
return cordovaTasks.build({buildMode: BUILD_MODE,
platforms: BUILD_PLATFORMS,
options: [target]});
    },...
}

There’s one last thing to do: in copyCode change .pipe(performSubstitutions()) to .pipe(cordovaTasks.performSubstitutions()). This is because the cordova-tasks package automatically takes care of all of the substitutions that we need, including version numbers, plugins, platforms, and more.

This was one of the more complex sections, so if you’ve come this far, take a coffee break. Next, we’ll worry about managing version numbers.

Supporting ES2015

We’ve already mentioned ES2015 (or EcmaScript 2015) in this article. Now is the moment we actually get to start using it. First, though, we need to modify our copy-code task to transpile from ES2015 to ES5, or our code wouldn’t run on any browser that doesn’t support the new syntax (that is still quite a few mobile platforms).

There are several transpilers available: I prefer Babel (https://babeljs.io). There is a Gulp plugin that we can use that makes this transpilation transformation extremely simple. To do this, we need to add the following to the top of our Gulp configuration:

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

Source maps are an important piece of the debugging puzzle. Because our code will be transformed by the time it is running on our device, it makes debugging a little more difficult since line numbers and the like don’t match. Sourcemaps provides the browser with a map between your ES2015 code and the final result so that debugging is a lot easier.

Next, let’s modify our projectTasks.copyCode method:

var projectTasks = { …,
copyCode: function copyCode() {
var isRelease = (BUILD_MODE === "release");
gulp.src(CODE_FILES)
            .pipe(cordovaTasks.performSubstitutions())
            .pipe(isRelease ? gutil.noop() : sourcemaps.init())
            .pipe(babel())
            .pipe(concat("app.js"))
            .pipe(isRelease ? gutil.noop() : sourcemaps.write())
            .pipe(gulp.dest(CODE_DEST));
    },...
}

Our task is now a little more complex, but that’s only because we want to control when the source maps are generated. When babel() is called, it will convert ES2015 code to ES5 and also generate a sourcemap of those changes. This makes debugging easier, but it also increases the file size by quite a large amount. As such, when we’re building in release mode, we don’t want to include the sourcemaps, so we call gutil.noop instead, which will just do nothing.

The sourcemap functionality requires us to call sourcemaps.init prior to any Gulp plugin that might generate sourcemaps. After the plugin that creates the sourcemaps executes, we also have to call sourcemaps.write to save the sourcemap back to the stream. We could also write the sourcemap to a separate .map file by calling sourcemaps.write(“.”), but you do need to be careful about cleaning that file up while creating a release build.

babel is what is doing the actual hard work of converting ES2015 code to ES5. But it does need a little help in the form of a small support library. We’ll add this library to src/www/js/lib/ by copying it from the gulp-babel module:

$ cp node_modules/babel-core/browser-polyfill.js src/www/js/lib

If you don’t have the src/www/js/lib/directory yet, you’ll need to create it before executing the previous command.

Next, we need to edit src/www/index.html to include this script. While we’re at it, let’s make a few other changes:

<!DOCTYPE html>
<html>
<head>
<script src="cordova.js" type="text/javascript"></script>
<script src="./js/lib/browser-polyfill.js"
type="text/javascript"></script>
<script src="./js/app/app.js"
type="text/javascript"></script>
</head>
<body>
<p>This is static content...,
but below is dynamic content.</p>
<div id="demo"></div>
</body>
</html>

Finally, let’s write some ES2015 code in src/www/js/app/index.js:

function h ( elType, ...children ) {
let el = document.createElement(elType);
for (let child of children) {
if (typeof child !== "object") {
          el.textContent = child;
      } else if (child instanceof Array) {
child.forEach( el.appendChild.bind(el) );
      } else {
el.appendChild( child );
      }
  }
return el;
}
 
function startApp() {
document.querySelector("#demo").appendChild(
h("div",
h("ul", h("li", "Some information about this app..."),
h("li", "App name: {{{NAME}}}"),
h("li", "App version: {{{VERSION}}}")
      )
    )
  );
}
 
document.addEventListener("deviceready", startApp, false);

This article isn’t about how to write ES2015 code, so I won’t bore you with all the details. Suffice it to say, the previous generates a few list items when the app is run using a very simple form of DOM templating. But it does so using the …​ (spread) syntax for variable parameters, the for … of loop and let instead of var. Although it looks a lot like JavaScript, it’s definitely different enough that it will take some time to learn how best to use the new features.

Linting your code

You could execute a gulp emulate –platform ios (or android) right now, and the app should work. But how do we know our code will work when built? Better yet — how can we prevent a build if the code isn’t valid?

We do this by adding lint tasks to our Gulp configuration file. Linting is a lot like compiling — the linter checks your code for obvious errors and aborts if it finds any. There are various linters available (some better than others), but not all of them support ES2015 syntax yet. The best one that does is ESLint (http://www.eslint.org). Thankfully, there’s a very simple Gulp plugin that uses it.

We could stop at linting and be done, but code style is also important and can catch out potentially serious issues as well. As such, we’re also going to be using the JavaScript Code Style checker or JSCS (https://github.com/jscs-dev/node-jscs).

Let’s create tasks to lint and check our coding style. First, add the following to the top of our Gulp configuration:

var eslint = require("gulp-eslint");
var jscs = require("gulp-jscs");
var CONFIG_DIR = path.join(__dirname, "config");
var CODE_STYLE_FILES = [path.join(SOURCE_DIR, "www", "js", "app", "**", "*.js")];
var CODE_LINT_FILES = [path.join(SOURCE_DIR, "www", "js", "app", "**", "*.js")];

Now, let’s create the tasks

var projectTasks = {,
checkCodeStyle: function checkCodeStyle() {
return gulp.src(CODE_STYLE_FILES)
            .pipe(jscs({
configPath: path.join(CONFIG_DIR, "jscs.json"),
esnext: true
            }));
    },
lintCode: function lintCode() {
return gulp.src(CODE_LINT_FILES)
            .pipe(eslint(path.join(CONFIG_DIR, "eslint.json")))
            .pipe(eslint.format())
            .pipe(eslint.failOnError());
    }
}
…
gulp.task("lint", projectTasks.lintCode);
gulp.task("code-style", projectTasks.checkCodeStyle);

Now, before you run this, you’ll need two configuration files to tell each task what should be an error and what shouldn’t be. If you want to change the settings, you can do so — the sites for ESLint and JSCS have information on how to modify the configuration files.

config/eslint.json must contain “parser”: “babel-eslint” in order to force it to use ES2015 syntax. This is set for JSCS in the Gulp configuration, however.

config/jscs.json must exist and must not be empty. If you don’t need to specify any rules, use an empty JSON object ({}).

Now, if you were to execute gulp lint and our source code had a syntax error, you would receive an error message. The same goes for code styles — gulp code-style would generate an error if it didn’t like the look of the code.

Modify the build, emulate, and run tasks in the Gulp configuration as follows:

gulp.task("build", ["lint", "code-style", "copy"], 
projectTasks.buildCordova);
gulp.task("emulate", ["lint", "code-style", "copy"], 
projectTasks.emulateCordova);
gulp.task("run", ["lint", "code-style", "copy"], 
projectTasks.runCordova);

Now, if you execute gulp build and there is a linting or code style error, the build will fail with an error. This gives a little more assurance that our code is at least syntactically valid prior to distributing or running the code.

Linting and style checks do not guarantee your code works logically. It just ensures that there are no syntax or style errors. If your program responds incorrectly to a gesture or processes some data incorrectly, a linter won’t necessarily catch those issues.

Uglifying your code

Code uglification or minification sounds a bit painful, but it’s a really simple step we can add to our workflow that will reduce the size of our applications when we build in the release mode. Uglification also tends to obfuscate our code a little bit, but don’t rely on this for any security — obfuscation can be easily undone.

To add the code uglification, add the following to the top of our Gulp configuration:

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

We can then uglify our code by adding the following code immediately after .pipe(concat(“app.js”)) in our projectTasks.copyCode method:

Next, modify our projectTasks.copyCode method to look like this:

.pipe(isRelease ? uglify({preserveComments: "some"}) :
gutil.noop())

Notice that we added the uglify method call, but only if the build mode is release. This means that we’ll only trigger it if we execute gulp build –mode release.

You can, of course, specify additional options. If you want to see all the documentation, visit https://github.com/mishoo/UglifyJS2/. Our options include certain comments (the ones most likely to be license-related) while stripping out all the other comments.

Putting it all together

You’ve accomplished quite a bit, but there’s one last thing we want to mention: the default task. If gulp is run with no parameters, it looks for a default task to perform. This can be anything you want.

To specify this, just add the following to your Gulp configuration:

gulp.task("default", ["build"]);

Now, if you execute gulp with no specific task, you’ll actually start the build task instead. What you want to use for your default task is largely dependent upon your preferences.

Your Gulp configuration is now quite large and complex. We’ve added a few additional features to it (mostly for config.xml). We’ve also added several other features to the configuration, which you might want to investigate further:

  • BrowserSync for rapid iteration and testing
  • The ability to control whether or not the errors prevent further tasks from being executed
  • Help text

Summary

In this article, you’ve learned why a task runner is useful, how to install Gulp, and how to create several tasks of varying complexity to automate building your project and other useful tasks.

Resources for Article:


Further resources on this subject:


Packt

Share
Published by
Packt

Recent Posts

Top life hacks for prepping for your IT certification exam

I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…

3 years ago

Learn Transformers for Natural Language Processing with Denis Rothman

Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…

3 years ago

Learning Essential Linux Commands for Navigating the Shell Effectively

Once we learn how to deploy an Ubuntu server, how to manage users, and how…

3 years ago

Clean Coding in Python with Mariano Anaya

Key-takeaways:   Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…

3 years ago

Exploring Forms in Angular – types, benefits and differences   

While developing a web application, or setting dynamic pages and meta tags we need to deal with…

3 years ago

Gain Practical Expertise with the Latest Edition of Software Architecture with C# 9 and .NET 5

Software architecture is one of the most discussed topics in the software industry today, and…

3 years ago