Grunt is a popular new task automation framework built upon the Node.js platform. It offers a wide range of features that allow you to streamline your project workflow and save time and energy by automating repetitive tasks, such as checking code quality, running tests, compiling templates and code, publishing to various types of services, and much more.
Task automation has been around since the beginning of software development and can probably be seen as a prominent reason for it being around at all. We're mostly writing programs to automate repetitive tasks after all.
Grunt itself is, for the most part, only a highly pluggable framework that provides a consistent interface for configuring automated tasks. The actual logic of the tasks is provided by a large variety of modules called plugins, which make use of this framework and usually tend to specialize in a certain set of functionalities.
At the time of writing the Grunt project is more than 3 years old, has over 3,000 plugins available in the npm public package registry, and provides tools and guides for creating or contributing to existing plugin projects.
A vast number of projects are currently making active use of Grunt in various ways, some of the most notable being the Yeoman, Modernizr, AngularJS, and JQuery projects.
In order to make use of a Grunt configuration file, the Grunt command-line interface (CLI) tool needs to be installed.
Command-line tools such as the Grunt CLI are usually installed globally. This means that they are installed on top of the Node.js installation that is currently active in your terminal, and not in the current project path, as is usually the case.
Tip
In this book, we'll work with version 0.4.x of Grunt, which requires Node.js version 0.8.x or higher.
The following steps will take us through installing the Grunt CLI and testing for its successful installation.
Assuming that you already have a global installation of Node.js, the following is the command to install the Grunt CLI:
$ npm install --global grunt-cli
If the installation was successful, the
grunt
command should now be available on the terminal. Test this by typinggrunt
in your terminal and confirm that it returns a message similar to the following:grunt-cli: The grunt command line interface. (v0.1.13) Fatal error: Unable to find local grunt. If you're seeing this message, either a Gruntfile wasn't found or grunt hasn't been installed locally to your project. For more information about installing and configuring grunt, please see the Getting Started guide: http://gruntjs.com/getting-started
The npm install
command looks up the grunt-cli
package on npm's public package registry, and proceeds to download and install it once it is found.
Using the -g
argument along with the install
command indicates that the package we'd like to install, should be installed globally, meaning it should be installed on the version of Node.js that is currently active in our terminal.
In a default Node.js setup, a folder for executable binaries will automatically be added as a path that should be scanned by the terminal for executable commands. This makes the grunt
command automatically available after the installation of this package, as its executable binary is provided and indicated in the package's installation information.
For a project to make use of the Grunt framework, it will require the installation of its libraries and the setting up a bare minimum configuration file. The libraries provide the framework and tools required by all Grunt plugins, and the configuration file provides a starting point from which we can start loading plugins and adjusting their behavior.
It's usually a good idea for a project to be packaged in a way to help keep track of dependencies, binaries, scripts, maintainers, and other important information. The standard package format for Node.js-based projects is CommonJS.
At the heart of the CommonJS package, lies the package.json
file. This file contains everything important about the package and is stored in the JSON format. The simplest way to create a package.json
file is to use the npm init
command. This command will ask a series of questions and generate a package.json
file based on the answers provided. Here's an example of the questions that are asked when you run the command:
name: (grunt-book) myproject version: (0.0.0) description: My first Grunt project. entry point: (index.js) test command: git repository: keywords: author: license: (ISC)
After these questions are answered, a package.json
file will be generated in the current path with the following contents:
{ "name": "myproject", "version": "0.0.0", "description": "My first Grunt project.", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
The following steps take us through installing the Grunt framework libraries on our project and creating a bare minimum configuration file.
First, we'll install the Grunt libraries in the current project path, and have it added to our project dependencies. This can all be done by using the following command:
$ npm install --save grunt
Tip
Due to our use of the
--save
flag with theinstall
command, the Grunt package will be added to the dependency list of the project's package. This can be confirmed by taking a look at thedependencies
property inside thepackage.json
file.The
--save-dev
flag is also available for use with theinstall
command when you'd like the installed packages to be added to thedevDependencies
property, which lists the dependencies to set up a development environment.Next, we'll set up an empty configuration file that would, at the very least, allow Grunt to run and also provides a place for future task configurations. Let's create a file called
Gruntfile.js
in the root directory of our project with the following contents:module.exports = function (grunt) { grunt.initConfig({}); grunt.registerTask('default', []); };
Now that we have the Grunt libraries installed and a basic configuration file set up, we can use the
grunt
command to test that it's all working as expected. Running thegrunt
command in the terminal should now produce output similar to the following:Done, without errors.
When the Grunt CLI tool is used, it always looks for the nearest file named Gruntfile.js
, from which it then attempts to load configurations. Inside the configuration file, there is an exported function that receives one argument. This argument is an object that provides us with access to the Grunt framework to load, create, and configure tasks.
At this point, we have no tasks loaded or created, and no configurations defined. Our default
task is also set to do nothing, so running the grunt
command did nothing except report that it was successfully completed.
All of the functionality that can be provided by Grunt is housed in the plugins that are made available in the form of Node.js packages. In this recipe, we'll run through the process of installing a plugin, which will prepare us for all the recipes that are to follow.
For our example, we'll install the contrib-jshint (0.10.0)
plugin. The same steps used to install this plugin can be used to install any of the other plugins available in the plugin package index.
In this example, we'll work with the basic project structure we created in the Setting up Grunt on a project recipe of this chapter Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through installing a plugin on our project and loading the tasks it contains:
The first step of installing a plugin is to install the package that contains it in the current project path. For our example, we'll install the
contrib-jshint
plugin that is contained in thegrunt-contrib-jshint
package. We can install this package and add it to our project dependencies by using the following command:$ npm install --save grunt-contrib-jshint
Next, we'll have to load the tasks contained in the plugin package so they can be used in our configuration. This is done using the
loadNpmTasks
function, provided to us by thegrunt
object that is passed to us in the configuration file. After adding this, our configuration file should look similar to the following:module.exports = function (grunt) { grunt.initConfig({}); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.registerTask('default', []); };
Now that we have the package installed and its tasks loaded, we can use the loaded tasks in our configuration. For our example, we had the
jshint
task loaded, which enables us to use it in a manner similar to the following:module.exports = function (grunt) { grunt.initConfig({ jshint: { sample: { files: 'src/*.js' } } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.registerTask('default', []); };
As you start to make use of more and more Grunt plugins, you will soon start to wonder whether there is a way to optimize the process a little. Fortunately, someone else has already gone down this road before and created the load-grunt-tasks
utility that automates the loading of tasks from all the packages mentioned in your project dependencies. This means that we no longer need to add a loadNpmTasks
call for each plugin we install.
The following steps illustrate the usage of this utility, continuing from the work we did earlier in the main recipe:
Install the utility's package in the current project path and add it to your dependencies by using the following command:
$ npm install --save load-grunt-tasks
Now we can use it in our configuration file by importing the package and passing the
grunt
object to it. Now that we're making use of this utility, we can also remove all theloadNpmTasks
we were using to load our plugins. This should result in a configuration file similar to the following:module.exports = function (grunt) { require('load-grunt-tasks')(grunt); grunt.initConfig({ jshint: { sample: { files: 'src/*.js' } } }); grunt.registerTask('default', []); };
A simple web server will always come in handy during the development process of web-based projects. They can be easily set up and used to serve web content from your local machine, so you don't have to worry about constantly deploying your experimental changes to a remote service provider.
We'll make use of the contrib-connect
(0.8.0)
plugin, which provides us with the ability to set up and run a simple web server based on the Connect server framework. By default, it will only serve files from a directory, but it has the added benefit of being able to make use of the many Connect middleware plugins available.
Tip
You can read more about the Connect server framework and its middleware plugins at the following URL:
In this example, we'll work with the basic project structure we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through setting up a development server that serves files from a directory located in our project directory.
We'll start by installing the package containing the
contrib-connect
plugin and loading its tasks as given in the instructions provided in the Installing a plugin recipe of this chapter.With the
connect
task loaded from the plugin, we can make use of it in our configuration. To do this, we add the following to our configuration:connect: { server: { options: { base: 'www', keepalive: true } } }
Tip
The
base
option indicates in which directory the server should look for the files that are requested. Everything that you'd like to serve with your development server can be placed in this directory. This includes HTML pages, JavaScript source files, style sheets, images, and so on.The
keepalive
option keeps the server running even if the requested task has finished. This is usually preferred if you're running theconnect
task on its own, but is not required if another task will be running indefinitely after it has completed.Let's add a simple file that we'd like to have served from our server so that we have something to test it with. Create a directory called
www
in the project root, and then a file inside it calledindex.html
, with the following contents:<html> <head> <title>Test Page</title> </head> <body> <h1>This is a test page.</h1> </body> </html>
Now we can run our web server using the
grunt connect
command, which will produce output indicating that our server started, along with a URL of where it can be reached:Running "connect:server" (connect) task Waiting forever... Started connect web server on http://0.0.0.0:8000
Finally, we can use our favorite browser to pay a visit to the URL mentioned in the output. This would show us our example page, as served through our running server:
The connect
task provides many useful configuration options that allow us to automatically open a browser, specify the port and hostname where the server should run, serve files from more than one directory, use other Connect middleware plugins, and adding extra functionality to the created server.
In order to automatically open our favorite web browser on the default URL that our web server provides, we can set the open
option to true
as we do in the following example:
options: { base: 'www', keepalive: true, open: true }
When we'd like to use a different web browser from the default one to open a URL other than the default one, we can provide an object to the open
option that specifies exactly what we'd like. The following code specifies a URL, the browser that should be used to open it, and a callback function that should be called once it's been opened:
options: { base: 'www', keepalive: true, open: { target: 'http://localhost:8000/test.html', appName: 'firefox', callback: function() { console.log('Test URL opened in Firefox!'); } } }
Tip
Downloading the example code
You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
The default port used by the connect task is 8000
, but if you'd like to use a different one, the port
option can be used to specify which one you'd like it to be, which is shown in the following code snippet:
options: { port: 3000, base: 'www', keepalive: true }
In case you are not sure about which ports will be available when you start the server, it can be quite useful for you to have the server automatically select an open port. Setting the useAvailablePort
option to true
will enable this behavior. The code snippet for this is as follows:
options: { base: 'www', keepalive: true, useAvailablePort: true }
In the case that you'd like the server to attach to a specific hostname and not just the default 0.0.0.0
, you can make use of the hostname
option in the following manner:
options: { base: 'www', keepalive: true, hostname: 'something.else.com' }
If you have more than one directory that contains the files that you'd like to be serving, then the base
option can be provided with an array of directory names to look up content. Here is a code snippet for your reference:
options: { base: ['www', 'other'], keepalive: true }
Tip
When using an array for the base
option, the server will look up the requested resources in each of the directories, from left to right, and return a resource as soon as it's found. If each of the two directories in the example contained an index.html
file, a request to the root URL would return the index.html
file in the www
directory.
If we'd like to use one of the many existing middleware plugins available for the Connect framework, we can set the middleware
option to a function that modifies the middleware stack by adding the desired middleware to it.
First, we'll need to install the middleware that we'd like to use, which in our case is packaged along with the Connect server framework. We can install the framework package using the following command:
$ npm install --save connect
Now, we alter the options of the
server
target in theconnect
task so that it adds thecompress
middleware to the stack:options: { base: 'www', keepalive: true, middleware: function(connect, options, middlewares) { middlewares.push( require('connect').middleware.compress() ); return middlewares; } }
There are times that it would be useful to work with the server that is created by the connect
task. A good example of this is when we'd like to enable our server to handle Socket.IO interactions. This can be done by providing a function to the onCreateServer
option that works with the created server in whichever way you like:
options: { base: 'www', keepalive: true, onCreateServer: function(server, connect, options) { var io = require('socket.io').listen(server); io.sockets.on('connect', function (socket) { // do something with socket }); } }
Another common requirement for development environments is the need to automatically run specific tasks when certain files are changed. This is especially useful when you'd like to monitor the quality of changing code in real time, or recompile altered resources as soon as they change, so that the effect of the changes are reflected without any manual intervention.
The contirb-watch
(0.6.1)
plugin allows us to keep a watch on a specific set of files, and run a specified set of tasks when file events are observed.
In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through setting up a watch
task that initiates a code quality analysis on a JavaScript source file each time a change to the file is observed.
We'll start by installing the package that contains the
contrib-watch
plugin and loading its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.For our example, we'll make use of the
jshint
task to analyze the quality of a JavaScript source file. Let's install thecontrib-jshint
plugin and load its tasks by following the instructions provided in the Installing a Plugin recipe of this chapter.We'll also need a JavaScript source file that we can watch for changes and perform a quality analysis on. Let's create a file called
sample.js
in our project root and provide it with the following contents:var sample = 'Sample'; console.log(sample);
Now, we can set up the example
jshint
task, which we'll run using thewatch
task by adding the following to our configuration:jshint: { sample: { src: ['sample.js'] } }
With the plugin installed and the sample task configured, we can now configure a target on the
watch
task, which will run thejshint
task every time the sample file calledsample.js
changes. This is done by adding the following to our configuration:watch: { sample: { files: ['sample.js'], tasks: ['jshint'] } }
Finally, we can start the task using the
grunt watch
command, which should produce the following output to confirm that it's running:Running "watch" task Waiting...
To test our setup, we can now make some changes to the
sample.js
file and save them. This should produce output informing us of the file event similar to the following:Running "watch" task Waiting... >> File "sample.js" changed. Running "jshint:sample" (jshint) task >> 1 file lint free. Done, without errors. Completed in 1.0s at Wed Jan 1 2014 00:00:00 GMT - Waiting...
The watch
task plugin provides many more useful configuration options that allow us to watch more than one file, run a series of tasks, prevent process spawning for task runs, enable the interruption of task runs, specify the waiting period before rerunning tasks, run tasks only on specific file events, allow tasks to kill the watcher process, and run tasks once when the watcher starts up.
In case we'd like to watch more than one file, the pattern-matching capabilities of the standard Grunt files
configuration can be used. The following configuration example will observe all the files in the project root or any of its sub directories with the txt
extension:
watch: { sample: { files: ['**/*.txt'], tasks: ['sample'] } }
In case we'd like to run more than one task each time a file event is observed, we can just add the tasks to the array passed to the tasks
configuration:
watch: { sample: { files: ['sample.txt'], tasks: ['sample', 'another', 'finally'] } }
The default behavior of the watch
task is to start each of the triggered tasks in their own child process. This prevents failing triggered tasks from causing the watch
task itself to fail. As a side effect, it also clones the context of the watcher process for each task. This behavior can however be disabled by setting the spawn
option to false
, which triggers tasks a little faster and allows them to share a context between them. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { spawn: false } } }
The default behavior for the watcher is to wait for the completion of the tasks triggered by the previous change, before waiting for changes again. By setting the interrupt
option to true
, the watcher will stop running tasks when a change is detected and start rerunning them. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { interrupt: true } } }
The default period the watcher will wait before checking for file changes after a previous task run is 500ms
. This amount of time can be changed by setting the debounceDelay
option to the number of milliseconds you'd like for it to wait. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { debounceDelay: 1000 } } }
In addition to being changed, files can also be added and deleted. The default behavior of the watcher is to observe all these events, but if you'd like it to run tasks only on specific events, the event
option can be set to either changed
, added
, deleted
, or all
.
The following example will only start the sample
task if a file named sample.txt
is added or deleted to the same path as the configuration file:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { event: ['added', 'deleted'] } } }
Warnings and failures that are raised by the tasks started by the watch task will, by default, not interrupt its execution. Setting the forever
option to false
will disable this behavior and allow child tasks to stop the watcher process on warnings and failures. The following demonstrates the configuration for this:
watch: { options: { forever: false }, sample: { files: ['sample.txt'], tasks: ['sample'] } }
In the case that you'd like to run the tasks specified in the tasks
option once the watcher starts up, and not only once file events are observed, you can set the atBegin
option to true
. The following demonstrates the configuration for this:
watch: { sample: { files: ['sample.txt'], tasks: ['sample'], options: { atBegin: true } } }
Once you've got a development server running that serves the pages, code, and assets that make up your web application, you will notice that you constantly have to refresh the browser each time you wish to observe a change that was made.
This is where the LiveReload tool and its constituent libraries come in handy. It's designed to automatically reload the browser's contents if a page or code file is changed, and even apply changes to CSS and images live, without refreshing the browser's contents.
We can set up LiveReload for our project with the help of two plugins that are discussed in other parts of this chapter. The development server provided by the contrib-connect (0.8.0)
plugin can be configured to accept LiveReload triggers, and the contrib-watch (0.6.1)
plugin to send them file events.
In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
We also make use of the contrib-connect
and contrib-watch
plugins in this recipe, which have each been discussed separately in the Setting up a basic web server and Watching files for changes recipes of this chapter. These are to be referred to if you are not yet familiar with the plugins they discuss.
The following steps take us through setting up a watch task that will automatically trigger a refresh when it observes changes to an HTML page that is served from a local development server.
We'll start by installing the packages containing the
contrib-connect
andcontrib-watch
plugins and loading their tasks by following the instructions provided in the Installing a plugin recipe of this chapter.For the sake of our example, we'll create a file called
index.html
in thewww
directory, which will be the file that we wish to view in a browser and have automatically updated when changes are made to it. The contents for this file follows:<html> <head> <title>LiveReload Test</title> </head> <body> <h1>First there was this.</h1> </body> </html>
Next, we'll set up our development server, which will serve the
index.html
file from thewww
directory. Along with the standard configuration, we'll set thelivereload
option totrue
, indicating that the development server should enable the browser to receive LiveReload triggers. This is all done by adding the following to our configuration:connect: { dev: { options: { base: 'www', livereload: true } } }
Tip
Providing the
true
value for thelivereload
option includesconnect-livereload
in the middleware stack of the connect server. The middleware in turn inserts a snippet of code in the HTML code of the pages served, which enables the browser to accept LiveReload triggers.The
keepalive
option can be excluded from this configuration due to the watcher process that will continue to run after the server is started. This means that the Grunt process will not end, which would also have stopped the server it started.Now, we'll set up a watcher to observe file events in the
www/index.html
file. Along with the standard configuration, we'll set thelivereload
option totrue
, indicating that the appropriate LiveReload triggers should be sent whenever changes are observed. This is done by adding the following to our configuration:watch: { www: { files: ['www/index.html'], options: { livereload: true } } }
Finally, we can start our server and watcher using the
grunt connect watch
command, which should produce output indicating the start of both:Running "connect:dev" (connect) task Started connect web server on http://0.0.0.0:8000 Running "watch" task Waiting...
Now, we can use our favorite browser to open the URL mentioned in the output, which should show us our sample page as served by the server:
Let's try out the LiveReload functionality by changing the
www/index.html
file and saving it. This action should produce the following output in the terminal in which the server and watcher were started:>> File "www/index.html" changed. Completed in 0.001s at Wed Jan 01 2014 00:00:00 GMT – Waiting...
Switching back to our browser that currently has the
http://0.0.0.0:8000
URL open, we should now see the updated page, without having initiated a manual refresh:
When running a task that processes files in one way or another, you'll soon realize that you probably don't want it to process all the files each time it is performed. This is especially true when the amount of files the task has to process becomes quite large.
This is where the newer (0.7.0)
plugin can help out by ensuring that only the files that have changed since the task's previous run are processed, each time it is called to run. It can be used with any plugin that makes use of the standard files
configuration and becomes especially useful when using a watcher to rerun tasks each time a file change is detected.
In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through making use of the newer
plugin to check the code quality of only the JavaScript source files that have changed since the last run.
We'll start by installing the package containing the
newer
plugin and loading its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.For the purpose of our example, we'll also install the
contrib-jshint
plugin and load its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.With all the required plugins installed, we can now add sample configuration for the
jshint
task, which will perform a code quality check on all the JavaScript files in thesrc
directory. This is done by adding the following to our configuration:jshint: { sample: { src: 'src/**/*.js' } }
Now we can run the
jshint
task a couple of times using thegrunt jshint
command to observe its behavior. Each time we run it, we'll see that it scans all the files in thesrc
directory. This should produce the same output each time, similar to the following:Running "jshint:sample" (jshint) task >> 3 files lint free.
In order to make use of the
newer
plugin, we prependnewer:
to the name of the task we wish to run. On running thegrunt newer:jshint
command the first time, thenewer
plugin will cache the timestamps of the files that have been processed. This produces output similar to the following:Running "newer:jshint" (newer) task Running "newer:jshint:sample" (newer) task Running "jshint:sample" (jshint) task >> 3 files lint free. Running "newer-postrun:jshint:sample:.cache" (newer-postrun) task
When we run the
grunt newer:jshint
command again, we'll see that no files are processed by thejshint
task, which produces output similar to the following:Running "newer:jshint" (newer) task Running "newer:jshint:sample" (newer) task No newer files to process.
Now, we can change one of the files in the
src
directory and run the command again to see that the changed file is processed again:Running "newer:jshint" (newer) task Running "newer:jshint:sample" (newer) task Running "jshint:sample" (jshint) task >> 1 file lint free. Running "newer-postrun:jshint:sample:1:.cache" (newer-postrun) task
Processing only changed files becomes especially useful when making use of the watch
task provided by the contrib-watch
plugin. A watch
task will rerun its indicated tasks every time it observes a file change, which can happen quite often during development, and can take quite a bit of time if the tasks target a large number of files.
The following steps provide an example of how to use the newer
plugin in conjunction with contrib-watch
and continues with the work we did in the main recipe:
We'll start by installing the package that contains the
contrib-watch
plugin and loading its tasks by following the instructions provided in the Installing a plugin recipe of this chapter.Now, we'll add a
watch
task, which will run thejshint
task when it observes changes on any of the JavaScript files contained in thesrc
directory. We'll also prepend thejshint
task withnewer:
to indicate that we only want to process the files that actually changed. This is done by adding the following to our configuration:watch: { jshint: { files: ['src/**/*.js'], tasks: ['newer:jshint'] } }
Now, we can start the
watch
task by using thegrunt watch
command in the terminal. This should produce output similar to the following, indicating that the watcher is running:Running "watch" task Waiting...
If we now change and save a file in the
src
directory, we should see output similar to the following, indicating that only the file that was changed has been processed by thejshint
task:>> File "src/file.js" changed. Running "newer:jshint" (newer) task Running "newer:jshint:sample" (newer) task Running "jshint:sample" (jshint) task >> 1 file lint free. Running "newer-postrun:jshint:sample:1:.cache" (newer-postrun) task
In most coding practices, it's best practice to keep logic and data separated as much as possible. The same rule applies to the Grunt configuration logic and the data that it makes use of.
A very common use case for using data from an external source is the use of the project information contained in the package.json
file. Information such as project names and version numbers might not change too often, but when they do, we'd probably prefer not having to look for them everywhere in the project.
Fortunately, the Grunt framework provides us with functions that allow us to easily read data from external files and store them in the configuration. This stored data can then also be easily used with the help of the string templates that are automatically processed for all configurations.
In this example, we'll work with the basic project structure that we created in the Setting up Grunt on a project recipe of this chapter. Be sure to refer to it if you are not yet familiar with it's contents.
The following steps take us through making use of data from the package.json
file when generating an optimized version of a JavaScript source file.
For our example, we'll make use of the
contib-uglify
plugin, which can be used to compress JavaScript source files. Let's install it and load its tasks for using the instructions provided in the Installing a Plugin recipe of this chapter.We'll also need a simple JavaScript source file for the sake of our example. Let's create a file called
sample.js
in the root of our project directory and fill it with the following code:module.exports = function () { var sample = 'Sample'; console.log(sample); };
Next, we'll import the data contained in our project's
pacakge.json
file by making use of thegrunt.file.readJSON
function and assigning its result to a property calledpkg
in our configuration. After adding this, our configuration object should look similar to the following:{ pkg: grunt.file.readJSON('package.json') }
Now that we have the data imported form our project package, we can set up an
uglify
task, which will compress a JavaScript source file calledsample.js
, using the version number contained in thepackage.json
file as part of the resulting file's name. This is done by adding the following to our configuration:uglify: { sample: { files: { 'sample_<%= pkg.version %>.js': 'sample.js' } } }
Finally, we can use the
grunt uglify
command to test our setup, which should produce output similar to the following:Running "uglify:sample" (uglify) task File sample_0.0.0.js created: 81 B → 57 B
We can now also take a look at our newly generated compressed version of the
sample.js
file in thesample_0.0.0.js
file, which should have content similar to the following:module.exports = function(){var a="Sample";console.log(a)};
The YAML format provides another popular way to store human readable data and can also be easily imported into our configuration. The following example, based on the work we did in the main recipe, demonstrates this functionality:
First, we'll create a simple YAML file for the purpose of our example. Let's create
sample.yaml
in our project root and give it the following content:version: 0.0.0
Now all we need to do is change the call to
grunt.file.readJSON
to import our sample YAML file instead. We do this by changing thepkg
configuration to the following:pkg: grunt.file.readYAML('sample.yaml')
If we now run the Grunt
uglify
command, we should see the same result as before, with output similar to the following:Running "uglify:sample" (uglify) task File sample_0.0.0.js created: 81 B → 57 B