Presented by Jason Lengstorf of Copter Labs
Twitter: @jlengstorf | Email: jason@copterlabs.com
A task runner (or build system) is a script that executes a list of actions, typically using one or more plugins.
We have a tendency to be:
(It’s what they’re built for.)
LESS, SCSS, CoffeeScript, etc.
Such as CSS and JavaScript.
Using JSHint, JSLint, etc.
For local development.
Refresh the browser when files change.
Output templates to static HTML, etc.
Reduce the size of JPGs, PNGs, etc.
Update package.json, bower.json, the Git version...
If you can do it with code, it can be a task.
App Source Code: http://git.io/2JuyAA
App Source Code: http://git.io/2JuyAA
Use npm to install grunt-cli:
npm install grunt-cli --save
Create Gruntfile.js and save it at the root of your project.
'use strict'; module.exports = function(grunt) { // Dynamically loads all required grunt tasks require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); // Configures the tasks that can be run grunt.initConfig({ }); });
Remember to install matchdep:
npm install matchdep --save-dev
// Install a plugin npm install grunt-pluginname // Install and save as a dependency npm install grunt-pluginname --save // Install and save as a dev dependency npm install grunt-pluginname --save-dev
// Matches a single file files: 'foo.js' // Matches an array of files files: [ 'foo.js', 'bar.js' ] // Matches all files in the dir files: '*' // Matches all files with a given extension in the dir files: '*.js' // Matches all files with a given extension in all dirs files: '**/*.js' // Matches all files w/extension in this dir and one dir deeper files: '{,*/}*.js' // These rules can be combined with fragments of paths files: 'src/js/{,*/}*.js'
Install the plugin (grunt-contrib-less):
npm install grunt-contrib-less --save-dev
Add configuration to your Gruntfile:
// Compiles LESS files to CSS less: { dev: { options: { cleancss: true // Minifies CSS output }, files: { 'src/app/css/combined-grunt.min.css': 'src/app/less/{,*/}*.less' } } },The plugin is saved for dev only because we don’t need to compile LESS in a production environment. Also, check out the wacky mess in the LESS filenames -- we’ll get to that on the next slide. Show the demo grunt testautoprefixer
/* This sucks to do while coding */ .manually { -webkit-box-shadow:inset 1px 1px 2px #111; box-shadow:inset 1px 1px 2px #111; } /* This isn't CSS */ .with-mixin { .box-shadow(1px, 1px, 2px, #111); }
Install the plugin (grunt-autoprefixer):
npm install grunt-autoprefixer --save-dev
Add configuration to your Gruntfile:
// Adds vendor prefixes to CSS autoprefixer: { dev: { src: 'src/app/css/combined-grunt.min.css' } },
.spinning-icon { animation: spinme 2s infinite ease-in-out; } @keyframes spinme { 0% { transform: rotate(0deg); } 50% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } }
(Has anyone ever done this?)
Install the plugin (grunt-uncss):
npm install grunt-uncss --save-dev
Add configuration to your Gruntfile:
// Removes unused CSS selectors uncss: { testuncss: { files: { 'demo/css/uncss-test-clean.css': 'demo/index.html' } } },Show the demo! grunt testuncss
Install the plugins (grunt-contrib-jshint & jshint-stylish):
npm install grunt-contrib-jshint --save-dev npm install jshint-stylish --save-dev
Add configuration to your Gruntfile:
jshint: { dev: { options: { jshintrc: true, reporter: require('jshint-stylish'), }, src: [ '{,*/}*.js', 'src/app/js/{,*/}*.js', '!src/app/js/combined*.js' ] } },Show the demo! grunt jshint:testfail grunt jshint:testpass
To avoid non-issue errors, you can create a new file called .jshintrc:
{ "node": true, "-W097": true, // Ignores "use strict" warning "browser": true, "devel": true, "validthis": true, // Avoids a warning using `this` "globals": { "angular": true } }
More info: JSHint Docs
Install the plugin (grunt-contrib-uglify):
npm install grunt-contrib-uglify --save-dev
Add configuration to your Gruntfile:
// Combines and minifies JS files uglify: { options: { mangle: false, compress: true, preserveComments: 'some' }, scripts: { files: { 'src/app/js/combined-grunt.min.js': [ 'src/app/js/{,*/}*.js', '!src/app/js/combined*.js' ] } } },The mangle: false setting is there to avoid breaking Angular. There are ways to work around this issue, but they’re a little too involved to include here.
Install the plugin (grunt-nodemon):
npm install grunt-nodemon --save-dev
Add configuration to your Gruntfile:
// Watches back-end files for changes, restarts the server nodemon: { dev: { script: 'src/server.js', options: { env: { PORT: 9000 }, ext: 'js,ejs,html', callback: function (nodemon) { nodemon.on('log', function (event) { console.log(event.colour); }); // opens browser on initial server start nodemon.on('config:update', function () { // Delay before server listens on port setTimeout(function() { require('open')('http://localhost:9000'); }, 1000); }); // refreshes browser when server reboots nodemon.on('restart', function () { // Delay before server listens on port setTimeout(function() { require('fs').writeFileSync('.rebooted', 'rebooted'); }, 1000); }); } } } },
Install the plugin (grunt-contrib-watch):
npm install grunt-contrib-watch --save-dev
Add configuration to your Gruntfile:
// Watches front-end files for changes and reruns tasks as needed watch: { todo: { // NOTE Uses the todo file list to save time if the list changes files: [ '<%= todo.src %>' ], tasks: [ 'todo' ] }, styles: { files: [ 'src/app/less/{,*/}*.less' ], tasks: [ 'less:dev', 'autoprefixer:dev' ], options: { livereload: true } }, scripts: { files: [ 'src/app/js/{,*/}*.js', '!src/app/js/combined*.js' ], tasks: [ 'jshint:dev', 'uglify:scripts' ] }, server: { files: ['.rebooted'], options: { livereload: true } } },
Install the plugin (grunt-concurrent):
npm install grunt-concurrent --save-dev
Add configuration to your Gruntfile:
// Allows us to run watch and nodemon concurrently with logging concurrent: { dev: { tasks: [ 'nodemon:dev', 'watch' ], options: { logConcurrentOutput: true } } },
// Compiles LESS/JS and checks for todos grunt.registerTask('default', [ 'less:dev', 'autoprefixer:dev', 'jshint:dev', 'uglify:scripts' ]);
This is outside of grunt.initConfig().
// Starts a server and runs nodemon and watch using concurrent grunt.registerTask('server', [ 'concurrent:dev' ]);
This is outside of grunt.initConfig().
Install Gulp:
npm install gulp --save-dev
Create a gulpfile.js:
// Loads required dependencies var gulp = require('gulp'), path = require('path'), rename = require('gulp-rename'), less = require('gulp-less'), prefix = require('gulp-autoprefixer'), jshint = require('gulp-jshint'), uglify = require('gulp-uglifyjs'), livereload = require('gulp-livereload'), server = require('tiny-lr')(), nodemon = require('gulp-nodemon'); // Sets up file paths for reuse in task/watch config var filepaths = { scripts: [ 'app/js/**/*.js', '!app/js/combined*.js' ] };Don’t forget to install the plugins with npm install [plugin name] --save-dev
// Handles task relating to styles gulp.task('styles', function(){ gulp.src('app/less/main.less') // Compiles LESS files to CSS and minifies it .pipe(less({ cleancss: true })) // Adds vendor prefixes to CSS .pipe(prefix()) // Renames the CSS file and saves it in the proper place .pipe(rename('combined-gulp.min.css')) .pipe(gulp.dest('app/css/')) // Triggers a live reload to show changes immediately .pipe(livereload()); });
Plugins: gulp-less, gulp-autoprefixer
// Handles tasks relating to scripts gulp.task('scripts', function(){ gulp.src(filepaths.scripts) // Checks JS files for issues .pipe(jshint()) .pipe(jshint.reporter('jshint-stylish')) // Combines and minifies scripts .pipe(uglify('combined-gulp.min.js', { mangle: false })) // Saves the minified JS .pipe(gulp.dest('app/js/')) // Triggers a live reload to show changes immediately .pipe(livereload()); });
Plugins: gulp-jshint, gulp-uglifyjs
// Watches back-end files for changes, restarts the server gulp.task('nodemon', function(){ nodemon({ script: 'server.js' }); });
Plugins: gulp-nodemon
// Watches front-end files for changes and reruns tasks as needed gulp.task('watch', [ 'nodemon' ], function(){ livereload.listen(); gulp.watch('app/less/**/*.less', [ 'styles' ]); gulp.watch(filepaths.scripts, [ 'scripts' ]); });
Plugins: none (watch is built in)
gulp.task('default', [ 'watch' ]);
So we can start with just gulp in the command line.
Presented by Jason Lengstorf of Copter Labs
Twitter: @jlengstorf | Email: jason@copterlabs.com