On Github barmatz / gruntjs-presentation
grunt Running "jshint:gruntfile" (jshint) task >> 1 file lint free. Running "jshint:src" (jshint) task >> 1 file lint free. Running "jshint:test" (jshint) task >> 1 file lint free. Running "qunit:files" (qunit) task Testing test/tiny-pubsub.html....OK >> 4 assertions passed (23ms) Running "clean:files" (clean) task Cleaning "dist"...OK Running "concat:dist" (concat) task File "dist/ba-tiny-pubsub.js" created. Running "uglify:dist" (uglify) task File "dist/ba-tiny-pubsub.min.js" created. Uncompressed size: 389 bytes. Compressed size: 119 bytes gzipped (185 bytes minified). Done, without errors.
Grunt and Grunt plugins are installed and managed via npm, the Node.js package manager.
npm install -g grunt-cli
This will put the grunt command in your system path, allowing it to be run from any directory.
Note that installing grunt-cli does not install the Grunt task runner! This allows multiple versions of Grunt to be installed on the same machine simultaneously.
Installed grunt tasks can be listed by running grunt --help
The package.json is our manifest and goes in the project root directory.
When you run npm install it will install the correct version of each dependency listed therein.
{ "name": "my-project-name", "version": "0.1.0", "devDependencies": { "grunt": "~0.4.1", "grunt-contrib-jshint": "~0.6.3", "grunt-contrib-nodeunit": "~0.2.0", "grunt-contrib-uglify": "~0.2.2" } }
By using the following command npm will install the module locally and add it to the devDependencies section in your package.json file
npm isntall MODULE_NAME --save-dev
The gruntfile.js or gruntfile.coffee file is a valid JavaScript or CoffeeScript file that sits along side the package.json file in the root of the project.
A Gruntfile is comprised of the following parts:
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']); };
Every gruntfile (and gruntplugin) uses this basic format, and all of your Grunt code must be specified inside this function
module.exports = function(grunt) { // Do grunt-related things in here };
Most Grunt tasks rely on configuration data defined in an object passed to the grunt.initConfig method.
// Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } });
As long as a plugin is specified in package.json as a dependency, and has been installed via npm install, it may be enabled inside your gruntfile with a simple command.
// Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify');
You can configure Grunt to run one or more tasks by default by defining a default task
// Default task(s). grunt.registerTask('default', ['uglify']);
If your project requires tasks not provided by a Grunt plugin, you may define custom tasks right inside the gruntfile
module.exports = function(grunt) { // A very basic default task. grunt.registerTask('default', 'Log some stuff.', function() { grunt.log.write('Logging some stuff...').ok(); }); };
Custom project-specific tasks do not need to be defined in the gruntfile; they may be defined in external .js files and loaded via the grunt.loadTasks method.
When a task is run, Grunt looks for its configuration under a property of the same name.
Multi-tasks can have multiple configurations, defined using arbitrarily named targets.
grunt.initConfig({ concat: { foo: { // concat task "foo" target options and files go here. }, bar: { // concat task "bar" target options and files go here. }, }, uglify: { bar: { // uglify task "bar" target options and files go here. }, }, });
Inside a task configuration, an options property may be specified to override built-in defaults. In addition, each target may have an options property which is specific to that target. Target-level options will override task-level options.
grunt.initConfig({ concat: { options: { // Task-level options may go here, overriding task defaults. }, foo: { options: { // "foo" target options may go here, overriding task-level options. }, }, bar: { // No options specified; this target will use task-level options. }, }, });
Because most tasks perform file operations, Grunt has powerful abstractions for declaring on which files the task should operate.
All files formats support src and dest but the "Compact" and "Files Array" formats support a few additional properties
This form allows a single src-dest (source-destination) file mapping per-target.
grunt.initConfig({ jshint: { foo: { src: ['src/aa.js', 'src/aaa.js'] }, }, concat: { bar: { src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b.js' }, } });
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).
grunt.initConfig({ concat: { foo: { files: { 'dest/a.js': ['src/aa.js', 'src/aaa.js'], 'dest/a1.js': ['src/aa1.js', 'src/aaa1.js'] } } }, bar: { files: { 'dest/b.js': ['src/bb.js', 'src/bbb.js'], 'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'] }, } } });
This form supports multiple src-dest file mappings per-target, while also allowing additional properties per mapping.
grunt.initConfig({ concat: { foo: { files: [ {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'}, {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'} ] }, bar: { files: [ {src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b/', nonull: true}, {src: ['src/bb1.js', 'src/bbb1.js'], dest: 'dest/b1/', filter: 'isFile'} ] } });
The filter property can help you target files with a greater level of detail. Simply use a valid fs.Stats method name.
grunt.initConfig({ clean: { foo: { src: ['tmp/**/*'], filter: 'isFile' } } });
Or create your own filter function and return true or false whether the file should be matched.
grunt.initConfig({ clean: { foo: { src: ['tmp/**/*'], filter: function(filepath) { return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0); } } } });
When you want to process many individual files, a few additional properties may be used to build a files list dynamically.
Templates specified using <% %> delimiters will be automatically expanded when tasks read them from the config. Templates are expanded recursively until no more remain.
The entire config object is the context in which properties are resolved. Additionally, grunt and its methods are available inside templates, eg. <%= grunt.template.today('yyyy-mm-dd') %>.
Grunt has grunt.file.readJSON and grunt.file.readYAML methods for importing JSON and YAML data.
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, dist: { src: 'src/<%= pkg.name %>.js', dest: 'dist/<%= pkg.name %>.min.js' } } });
Grunt works fine on Windows, because Node.js and npm both work fine on Windows. Usually the problematic part is Cygwin, because it bundles an outdated version of Node.js.
The best way to avoid this issue is to use the msysGit installer to install the git binary and the Node.js installer to install the node and npm binaries, and to use the built-in Windows command prompt or PowerShell instead of Cygwin.