Grunt: The Javascript Automation Task
As a web development evolved, I noticed that it is becomming really popular now that web developers prefer to split their project into managable module like separate between fontend and backend development which open up flexibility for both teams from frontend and backend to work in parallels with ...
As a web development evolved, I noticed that it is becomming really popular now that web developers prefer to split their project into managable module like separate between fontend and backend development which open up flexibility for both teams from frontend and backend to work in parallels with ease.
While this is certainly make our project easy to manage, frontend developers still need to deal with a lot of (boring if I must say) works like compiling(SASS, Coffee), linting, minification, concatenation ... etc, all of these tasks are repetitive and not related to actual work in development at all. It would great if we have some tool to manage all of these redundant works, that is where grunt comes into play.
Grunt is a javascript task runner that can be uses to automate all of the redundant works and makes life easier for web developers especially frontend developers that have to deal with (you know the nasty) javascript. So in this article I'm going to show you how to configure and use grunt so that you can apply it in your project and impress your boss.
Getting Started
Grunt can be install by using npm and Node.js versions >= 0.8.0. To get start first install grunt command like interface with following command.
$ npm install -g grunt-cli
After this package have been installed on your system you are now ready to use grunt.
Create a new project
Now create a directory, place a javascript file and named it Gruntfile.js in this directory. Open up Gruntfile.js and paste in following code.
module.exports = function(grunt) { // ... };
As you might have guess this is a grunt configuration file, all of your tasks will needed to be place in this function. Now the next step is to install grunt. For a new project you need to initialize package.json first.
$ npm init # or $ grunt-init
After that install grunt with this command.
$ npm install grunt --save-dev
Your First Task
There are two ways you can create a grunt task. One is to uses existing grunt plugin and another is to write your own task. There I'm going to use one of grunt plugin and write some configuration code to use that plugin to minify javascript file so that it can be used in production. To use that plugin in run the following command.
$ npm install grunt-contrib-uglify --save-dev
After this open up Gruntfile.js and put in the following code inside the function that we wrote previously.
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { build: { src: 'script/app.js', dest: 'build/app.min.js' } } }); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('default', ['uglify']);
Now you can run this task with command grunt and it will minify javascript file called app.js in script directory and output the result in file called app.min.js in build directory.
Configuring Task
Most Grunt tasks rely on configuration data defined in an object passed to the grunt.initConfig method.
In this example, grunt.file.readJSON('package.json') imports the JSON metadata stored in package.json into the grunt config so that later we can called grunt.loadNpmTasks to load grunt plugin. The value of uglify property of config object will be use by uglify task which is responsible for minify our javascript file. build is the target, each task can be configured have mulpile targets. We need something called file mapping in each target in order for grunt task to knows which file it should operates on and where to place the result.
There are several ways that we can specify this mapping.
Compact Format
The src and dest is an example of compact format. It tells uglify task that it should minifies the file specified in src and output that result in the file specified in dest.
Files Object Format
This form supports multiple src-dest mappings per-target, where the property name is the destination file, and its value is the source file(s). Any number of src-dest file mappings may be specified in this way, but additional properties may not be specified per mapping.
uglify: { build: { files: { 'build/controllers.min.js': [ 'script/controllers/home.js', 'script/controllers/post.js' ], 'build/services.min.js': [ 'script/services/auth.js', 'script/services/cart.js' ] } } }
Files Array Format
This form supports multiple src-dest file mappings per-target, while also allowing additional properties per mapping.
uglify: { build: { files: [ {src: ['a.js', 'b.js'], dest: 'build/test.js'}, {src: ['a1.js', 'b1.js'], dest: 'build/test1.js'} ] } }
Globbing Pattern
It is really tirining to specify file one by one, so grunt offers a pattern match that we can be use in any of the above file mapping.
- * matches any number of characters, but not /
- ? matches a single character, but not /
- ** matches any number of characters, including /, as long as it's the only thing in a path part
- {} allows for a comma-separated list of "or" expressions
- ! at the beginning of a pattern will negate the match
Build File Dynamically
We can combine the globbing pattern and file build files dymanically. A few additional properties are needed to achive that. expand Set to true to enable the following options:
- cwd All src matches are relative to (but don't include) this path.
- src Pattern(s) to match, relative to the cwd.
- dest Destination path prefix.
- ext Replace any existing extension with this value in generated dest paths.
- extDot Used to indicate where the period indicating the extension is located. Can take either 'first' (extension begins after the first period in the file name) or 'last' (extension begins after the last period), and is set by default to 'first' [Added in 0.4.3]
- flatten Remove all path parts from generated dest paths.
- rename This function is called for each matched src file, (after extension renaming and flattening). The dest and matched src path are passed in, and this function must return a new dest value. If the same dest is returned more than once, each src which used it will be added to an array of sources for it.
Example:
files: [{ expand: true, // Enable dynamic expansion. cwd: 'lib/', // Src matches are relative to this path. src: ['**/*.js'], // Actual pattern(s) to match. dest: 'build/', // Destination path prefix. ext: '.min.js', // Dest filepaths will have this extension. extDot: 'first' // Extensions in filenames begin after the first dot }]
Further Reading
With the knowledge that we just learned above I'm sure you can do pretty much anything you want in your project, but if you want to dive more deeper like how to write grunt plugin please refer to this link.