Running tasks

Up until this point, we have learnt how to configure and create tasks. Now it is time to run them!

Command-line

Some Node.js command-line tools, such as express, may also be used as a module, whereas Grunt may only be used via the command-line. Once we've globally installed the grunt-cli module, our system will have access to the grunt executable.

To run our newly loaded or created tasks, we need to provide Grunt with a list of task names as space-separated command-line arguments. This will result in Grunt executing each specified task in sequence; which means we can easily dictate the order of task execution. We could run foo then bar with:

$ grunt foo bar

Or, we run bar then foo with:

$ grunt bar foo

There is a special case, however, when we execute grunt on its own. Grunt interprets this as grunt default and subsequently will attempt to run the default task. Therefore, by registering a default task, we can make it easy to run our most common task. Similar to our previous example in the Task aliasing section, we could alias our build and test tasks as the default task with the following Gruntfile.js file:

//Code example 06-default-tasks
module.exports = function(grunt) {

  grunt.registerTask('build', function() {
    console.log('building...'),
  });

  grunt.registerTask('test', function() {
    console.log('testing...'),
  });

  grunt.registerTask('default', ['build', 'test']);
};

Now, we can simply run grunt, which should result in:

$ grunt
Running "build" task
building...

Running "test" task
testing...

Done, without errors.

We can run multitasks in a similar fashion; however, when we specify a multitask, Grunt will execute all of its targets. If we wanted to run a particular target then we can append it to the task name. So, if we wanted to run the foo task's target1 target, then we would execute grunt foo:target1. For example, let's convert our build and test tasks in the previous example to multitasks and test this out:

//Code example 07-default-multi-tasks
module.exports = function(grunt) {
  grunt.initConfig({
    build: {
      main: {},
      extra: {}
    },
    test: {
      main: {},
      extra: {}
    }
  });

  grunt.registerMultiTask('build', function() {
    console.log('building target ' + this.target + '...'),
  });

  grunt.registerMultiTask('test', function() {
    console.log('testing target ' + this.target + '...'),
  });

  grunt.registerTask('default', ['build:main', 'test:main']);
};

We can explicitly run the build task's main target and then the test task's main target with:

$ grunt build:main test:main
Running "build:main" (build) task
building target main...

Running "test:main" (test) task
testing target main...

Done, without errors.

However, extending from the previous example, we could also add these targets in our default task alias. As you can see in the previous code, we have placed targets inside our array of task names, and therefore, when we run grunt we should see the same output:

$ grunt
Running "build:main" (build) task
building target main...

Running "test:main" (test) task
testing target main...

Done, without errors.

Task arguments

Additionally, when we specify a task we may also include an optional, colon-separated, list of arguments. For example, the following Gruntfile.js defines a foo task, which prints its first and second parameters:

//Code example 08-task-args
module.exports = function(grunt) {

  grunt.registerTask('foo', function(p1, p2) {
    console.log('first parameter is: ' + p1);
    console.log('second parameter is: ' + p2);
  });

};

Now, we can run the foo task with the arguments bar and bazz using:

$ grunt foo:bar:bazz
Running "foo:bar:bazz" (foo) task
first parameter is: bar
second parameter is: bazz

Done, without errors.

However, when we wish to run a multitask, before we can specify arguments we must first specify the target. Let's convert the previous example's foo task into a multitask:

//Code Example 09-multi-task-args
module.exports = function(grunt) {

  grunt.initConfig({
    foo: {
      ping: {},
      pong: {}
    }
  });

  grunt.registerMultiTask('foo', function(p1, p2) {
    console.log('target is: ' + this.target);
    console.log('first parameter is: ' + p1);
    console.log('second parameter is: ' + p2);
  });

};

Similarly, but with the inclusion of ping as the target:

$ grunt foo:ping:bar:bazz
Running "foo:ping:bar:bazz" (foo) task
target is: ping
first parameter is: bar
second parameter is: bazz

Done, without errors.

With these examples in mind, we can see that we could create aliases which use tasks, multitasks, targets, and arguments all together, resulting in an extremely flexible build.

Tip

Terminology Tip—When invoking a function, we provide it with arguments. When inside a function, we use its parameters. We can remember this with arguments outside, parameters inside.

Runtime options

Not to be confused with configuration options, runtime options must be specified on the command-line in addition to our list of tasks. Runtime options are used to create Grunt-wide settings for a single execution of Grunt. Runtime options must be prefixed with at least one dash, "-", otherwise they will be seen as task name. Runtime options are best used when one or many tasks have a configuration setting that we wish to modify only some of the time. For instance, when we execute Grunt we can enable our optimize option to direct each task specified to run in an optimized mode. This could remove debug statements, compress output, and so on. Once we've specified a runtime option on the command-line, we can retrieve its value using the grunt.option function.

For example, let's say we have the following Gruntfile.js:

//Code example 10-runtime-opts
module.exports = function(grunt) {
  console.log('bar is: ' + grunt.option('bar'));
  grunt.registerTask('foo', function() {
    //nothing here...
  });
};

Now, if we run this empty foo task with no options, we'll see:

$ grunt foo 
bar is: undefined
Running "foo" task

Done, without errors.

Then, if we run this task again with the bar option set:

$ grunt foo --bar 
bar is: true
Running "foo" task

Done, without errors.

If we like, we can give the bar option a specific value using the =value suffix:

$ grunt foo --bar=42 
bar is: 42
Running "foo" task

Done, without errors.

In this case we are using the grunt.option function outside of the task. This is important since it means we can use our runtime options to assist with the configuration of our tasks. Note the console.log output occurs before the "Running "foo" task" output; this is because Grunt executes our Gruntfile.js in order to initialize our tasks and configuration, and only then the tasks specified on the command line are run in sequence.

For details on the Grunt Runtime Options API, refer to http://gswg.io#grunt-options. In Chapter 4, Grunt in Action, in the Step 5 – tasks and options section, we will review a technique to achieve environment specific builds through the use of runtime options.

Task help

When we are provided with an existing project for which there is no explicit documentation regarding the Grunt build, we can start off by listing the available tasks using the grunt --help command. When we use grunt.registerTask or grunt.registerMultiTask, we may optionally include a description. Let's review an example of this:

//Code example 11-task-help
module.exports = function(grunt) {
  grunt.registerTask('analyze',
    'Analyzes the source',
    function() {
      console.log('analyzing...'),
    }
  );

  grunt.registerMultiTask('compile',
    'Compiles the source',
    function() {
      console.log('compiling...'),
    }
  );

  grunt.registerTask('all',
    'Analyzes and compiles the source',
    ['analyze','compile']
  );

};

Now, if we run grunt --help, we should see the following excerpt within the output:

$ grunt --help
Grunt: The JavaScript Task Runner (v0.4.2)
Usage
 grunt [options] [task [task ...]]

Available tasks
       analyze  Analyzes the source
       compile  Compiles the source *
           all  Analyzes and compiles the source 

The Grunt static help text has been omitted, leaving only the dynamic text. Here, we can see that Grunt has listed each of our tasks, along with its description, and multitasks are suffixed with a star *. This is useful because it might not be obvious to those new to this build that the all task runs both the analyze task and the compile task.

Programmatically

Although it is possible to execute Grunt from another program, it is intended to be used as a command-line utility, and therefore its API is only to be used with the grunt executable. We can, however, programmatically run tasks within other tasks, allowing us to conditionally run a series of tasks.

The following example is very similar to Code example 04-linting from Chapter 1, Introducing Grunt. This time, however, instead of defining our JSHint rules inside our Gruntfile.js, we are defining them in a portable .jshintrc file. This is favorable to some as it provides the ability to use a company-wide JavaScript coding style:

//Code example 12-conditional-lint 
module.exports = function(grunt) {

  // Load the plugin that provides the "jshint" task.
  grunt.loadNpmTasks('grunt-contrib-jshint'),

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        jshintrc:'.jshintrc'
      },
      target1: 'src/**/*.js'
    }
  });
};

With this configuration, however, the jshint task will fail if the .jshintrc file is missing:

$ grunt jshint
Running "jshint:target1" (jshint) task
ERROR: Can't find config file: .jshintrc

Therefore, if we wanted to make our jshint task run only when we provide a .jshintrc file, then we could make another task that controls the execution of the jshint task:

// A new task to make "jshint" optional 
grunt.registerTask('check', function() {
  if(grunt.file.exists('.jshintrc')) {
    grunt.task.run('jshint'),
  }
});

In our new check task, we shall first verify that the .jshintrc exists, and then we'll programmatically run the jshint task using the grunt.task.run function. Now, when we run the check task without a .jshintrc file, Grunt should do nothing and report success:

$ grunt check
Running "check" task

Done, without errors.

Though, when we include our .jshintrc file along side our Gruntfile.js and rerun our check task, we should see the following:

$ grunt check
Running "check" task

Running "jshint:target1" (jshint) task
>> 1 file lint free.

Done, without errors.

For an example of a .jshintrc file, please refer to http://gswg.io#jshintrc-example. For a summary of JavaScript Linting, please return to the Static Analysis or Linting section of Chapter 1, Introducing Grunt.

Automatically

One of the most popular Grunt plugins is grunt-contrib-watch (http://gswg.io#grunt-contrib-watch) as it allows us to place Grunt in the background and have it automatically run our tasks as they're needed. Written by Kyle Robinson Young, the watch task instructs Grunt to watch a particular set of files for changes and execute a particular task or set of tasks in response. In the following example, we'll watch our source files, and then run our JavaScript concatenation task concat whenever any of these files are changed:

//Code example 13-watch
module.exports = function(grunt) {

  // Load the plugins that provide the "concat" and "watch" tasks.
  grunt.loadNpmTasks('grunt-contrib-concat'),
  grunt.loadNpmTasks('grunt-contrib-watch'),

  // Project configuration.
  grunt.initConfig({
    srcFiles: ["src/a.js", "src/b.js", "src/c.js"],
    concat: {
      target1: {
        files: {
          "build/abc.js": "<%= srcFiles %>"
        }
      }
    },
    watch: {
      target1: {
        files: "<%= srcFiles %>",
        tasks: ["concat"]
      }
    }
  });

  // Define the default task
  grunt.registerTask('default', ['concat', 'watch']);
};

At the top of our Gruntfile.js file, we'll load both the plugins that provide the concat and watch tasks. We will then configure them using a shared srcFiles property. This means we can modify our source files once, and all tasks using this set of files will stay current. This helps to keep our build DRY (http://gswg.io#dry) by creating a single source of truth. All targets of the watch task (only target1 in this case) require a tasks property that should specify a list of tasks to run when one of the target's files are changed. Finally, we'll provide a default task that runs concat followed by watch. Running grunt at this point should produce:

grunt
Running "concat:target1" (concat) task
File "build/abc.js" created.

Running "watch" task
Waiting...

At this point, our watch task is running and is Waiting... for one of our files to change; so if we modify and save src/b.js, we should see the following appended to our output:

OK
>> File "src/b.js" changed.

Running "concat:target1" (concat) task
File "build/abc.js" created.

Done, without errors.
Completed in 0.648s at Tue Sep 17 2013 21:57:52 GMT+1000 (EST)
Waiting... 

Our concat task was run, and our watch task is Waiting... again, ready for more changes. Since we are watching our source files, we can now minimize our terminal window and continue with our development workflow, knowing that Grunt is running in the background, taking care of the "grunt" work for us.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.48.204