On Github NickHeiner / gruntled-talk
angular.js-dc
Feburary 2014
@nickheiner and @opower
(Many examples taken straight from the grunt docs)
Work with smart people to solve hard problems using cool tech, like node and angular
We've saved over 3.7TWH of energy so far, abating over 4.6 billion lbs of CO2 emissions
Free massages, scooters and dogs in the office, meme-friendly culture
〉 Using grunt in your own projects
Building your own grunt tasks
Best practices
No more terrible bash spaghetti messes
No more using a tool designed for another ecosystem, like rake
No more ambiguous, global dev dependencies
No more re-inventing the wheel for common cases like CLI parsing, logging, etc
No more IDE-specific build steps that lock devs into one editor
# Required to use grunt on your system # You only need to do this once - better than gulp! $ npm install -g grunt-cli # Add grunt to the project you're current in $ npm install --save-dev grunt # Make a gruntfile $ touch Gruntfile.js
module.exports = function(grunt) { grunt.initConfig({}); };
You can natively use .coffee as well.
$ npm install --save-dev grunt-contrib-jshint
module.exports = (grunt) -> grunt.loadNpmTasks 'grunt-contrib-jshint' grunt.initConfig jshint: options: globals: angular: true files: ['scripts/**/*.js']
$ grunt jshint Running "jshint:files" (jshint) task >> 10 files lint free. Done, without errors.
module.exports = (grunt) -> grunt.loadNpmTasks 'grunt-contrib-jshint' grunt.initConfig jshint: options: global: angular: true scripts: files: ['scripts/**/*.js'] test: files: ['test/**/*.js']
$ grunt jshint:scripts Running "jshint:scripts" (jshint) task >> 10 files lint free. Done, without errors.
$ grunt jshint:test Running "jshint:test" (jshint) task >> 3 files lint free. Done, without errors.
$ grunt coffeelint:all jshint:all buildIndex sass:dist connect:livereload
Running "coffeelint:all" (coffeelint) task >> 1 file lint free. Running "jshint:all" (jshint) task >> 1 file lint free. Running "buildIndex" task Running "sass:dist" (sass) task File styles/styles.css created. Running "connect:livereload" (connect) task Started connect web server on 127.0.0.1:9000. Done, without errors.
grunt.registerTask('test', 'verify that the code is sound', [ 'jshint', // static analysis 'karma:unit', // run unit tests via karma 'launch-protractor-server', // launch a server for ptor tests to hit 'protractor' // run protractor tests ]); grunt.registerTask('publish', 'test and publish to npm', [ 'test', // be sure that we only publish good code 'build', // uglify, concat, whatever you need to do 'shell:publish' // run `npm publish` ]);
Config doesn't need to be task-specific
directories: { demo: 'dist' }, // some made-up build task build: { local: { src: 'scripts/**/*.js', dest: '<%= directories.demo %>' } }
pkg: require('./package'), // read package.json uglify: { options: { banner: '/* <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, // ... etc }
jshint: options: # default options for all targets node: true grunt: files: 'Gruntfile.js' # accept the defaults as-is test: options: # extend the defaults force: true files: 'test/**/*.js' src: options: # override and extend the defaults node: false, reporter: 'checkstyle' files: 'scripts/**/*.js'
jshint: { globbing: { src: ['src/**/*.js'] } srcDestPairs: { src: ['src/a.js', 'src/b.js'], dest: 'dest/c.js' } }
jshint: { filesObjectFormat: { 'dest/c.js': ['src/a.js', 'src/b.js'] 'dest/d.js': ['src/e.js', 'src/f.js'] }, filesArrayFormat: [ {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'}, {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'}, ] }
There are even more, but they are deprecated.
Using grunt in your own projects
〉Building your own grunt tasks
Best practices
grunt.registerTask( 'verify-file-exists', // task title 'Verify that a file still exists', // optional task description function() { // the task itself } );
grunt.registerTask( 'verify-file-exists', // task title 'Verify that a file still exists', // optional task description function() { // the task itself var file = grunt.config('verify-file-exists'); if (!grunt.file.exists(file)) { grunt.fail.fatal('Could not find ' + file); return; } grunt.log.ok(file + ' exists ;)') } );
grunt.initConfig({ 'verify-package-json-exists': 'package.json' });
grunt.registerMultiTask( 'verify-file-exists', // task title 'Verify that specified files exists', // optional task description function() { // the task itself var file = grunt.task.current.options().file; if (!grunt.file.exists(file)) { grunt.fail.fatal('Could not find ' + file); return; } grunt.log.ok(file + ' exists ;)') } );
grunt.initConfig 'verify-package-json-exists': pkg: options: file: 'package.json' bower: options: file: 'bower.json' travis: options: file: '.travis.yml'
var s3 = require('s3'); var util = require('util'); grunt.registerMultiTask('deploy_static_assets', function() { // tell grunt this may take a while var done = grunt.task.current.async(); s3.push('dev.opower.com').then(function success() { // To indicate success, call done() with no arguments. done(); }, function err(reason) { // To indicate failure, pass an Error to done. var reasonAsErr = util.isError(reason) ? reason : new Error(reason); done(reasonAsErr); }); });
grunt.registerMultiTask('deploy-static-assets', function() { var done = grunt.task.current.async(); s3.push('dev.opower.com'); // oops - forgot to ever call done() });
$ g exp Running "exp" task $
grunt.registerMultiTask('deploy-static-assets', function() { var done = grunt.task.current.async(); s3.push('dev.opower.com') .then(done, function onErr(reason) { // oops - calling done() with a value that is not an Error or false done('task failed'); }); });
$ g exp Running "exp" task Done, without errors
grunt.registerMultiTask('deploy_static_assets', function() { var done = grunt.task.current.async(); var opts = grunt.task.current.options({ bucket: 'dev.opower.com' }); s3.push(opts.bucket).then(done, function err(reason) { done(util.isError(reason) ? reason : new Error(reason)); }); });
grunt.initConfig({ 'deploy_static_assets': { dev: { options: { bucket: grunt.option('bucket') } } } });
$ grunt deploy_static_assets:dev --bucket "qa.opower.com"
grunt.registerMultiTask('deploy', function() { if (!grunt.option('skip-tests')) { grunt.task.run('test'); } if (grunt.option('minify')) { grunt.task.run('uglify'); } grunt.task.run('deploy-static-assets'); });
Using grunt in your own projects
Building your own grunt tasks
〉Best practices
Use @sindresorhus's excellent load-grunt-tasks library.
Before:
grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-mochatest'); grunt.loadNpmTasks('grunt-doge-script'); grunt.loadNpmTasks('grunt-s3'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-coffeelint'); grunt.loadNpmTasks('grunt-my-task'); grunt.loadNpmTasks('grunt-bitcoin-miner'); grunt.loadNpmTasks('grunt-dogecoin-miner'); grunt.loadNpmTasks('grunt-available-tasks');
After:
require('load-grunt-tasks')(grunt)
Gruntfile.js
grunt.registerTask('test', ['lint', 'unit', 'e2e']);
package.json
"scripts": { "test": "grunt test" }
$ npm test > gruntled@0.1.0 test /Users/nick.heiner/public-projects/gruntled-talk > grunt test Running "coffeelint:all" (coffeelint) task >> 1 file lint free. Running "jshint:all" (jshint) task >> 1 file lint free. Done, without errors.
Bad:
'deploy-static-assets': 'dev-or-qa': options: bucket: 'dev.opower.com' 'verify-static-assets': 'dev-or-qa': options: bucket: '<%= grunt.config(["deploy-static-assets", "dev-or-qa"]).options.bucket %>'
Good:
deploy_static_assets: ಠ_ಠ: options: bucket: 'dev.opower.com' verify_static_assets: dev_or_qa: options: bucket: '<%= deploy_static_assets.ಠ_ಠ.options.bucket %>'