SlideShare a Scribd company logo
MASTERING GRUNT
S p e n c e r H a n d l e y
ABOUT ME
S P E N C E R H A N D L E Y
Mastering Grunt
VIDEO SERIES
@spencer414
www.spencerhand.ly
GRUNT BASICS
Minifying
Uglifying
Concatenating
Linting
CDNifying
Image Optimization
Replacing Compiling StylusWatch/Live Reload
much more…
USED BY
*shameless plug
REASONS TO USE IT
• Huge Community
• Strong Adoption
• Valuable Resume Boost
• Highly in Demand
• Easy and, dare I say fun to use
• All the cool kids use it
PLUGINS
4000+ today
GRUNTFILE
'use strict';
module.exports = function (grunt) {
require('jit-grunt')(grunt, {
});
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
grunt.initConfig({
});
grunt.registerTask('default', [
]);
};
INIT CONFIG
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.
},
},
});
When you run a task, Grunt looks here
for it’s configuration.
INIT CONFIG
Multi-tasks can have multiple
configurations, defined using
arbitrarily named "targets."
gruntjs.com
concat: {
foo: {
// You could run this with concat:foo
},
bar: {
// You could run this with concat:bar
},
}
INIT CONFIG
Multi-tasks can have multiple
configurations, defined using
arbitrarily named "targets."
gruntjs.com
concat: {
foo: {
// You could run this with concat:foo
},
bar: {
// You could run this with concat:bar
},
}
Simply running concat will integrate through all targets
TASK CONFIGURATION:
OPTIONS
Inside a task configuration, an options property
may be specified to override built-in defaults.
You can also pass options to each target.
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.
},
}
DEALING WITH FILES
COMPACT FORMAT
grunt.initConfig({
jshint: {
foo: {
src: ['src/aa.js', 'src/aaa.js']
},
},
concat: {
bar: {
src: ['src/bb.js', 'src/bbb.js'],
dest: 'dest/b.js',
},
},
});
Typically for read-only
tasks where the dest is
not needed. Like JShint
FILE OBJECT FORMAT
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'],
},
},
},
});
destination: [source files]
multiple src-dest mappings per-target
FILE ARRAY FORMAT
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'},
],
},
},
}); supports multiple src-dest file mappings per-target
while also allowing additional properties per mapping.
FILTER FUNCTION
grunt.initConfig({
clean: {
foo: {
src: ['tmp/**/*'],
filter: 'isFile',
},
},
});
Will clean only if the pattern matches an actual file:
Uses nodes valid fs.Stats method names
CUSTOM FILTER FUNCTION
grunt.initConfig({
clean: {
foo: {
src: ['tmp/**/*'],
filter: function(filepath) {
return (grunt.file.isDir(filepath) &&
require('fs').readdirSync(filepath).length === 0);
},
},
},
});
The following will only clean folders that are empty
You can create custom filters for specifying files
CUSTOM FILTER FUNCTION
grunt.initConfig({
clean: {
foo: {
src: ['tmp/**/*'],
filter: function(filepath) {
return (grunt.file.isDir(filepath) &&
require('fs').readdirSync(filepath).length === 0);
},
},
},
});
The following will only clean folders that are empty
You can create custom filters for specifying files
TANGENT TIME
GLOBBING BASICS
*
?
**
{}
!
matches any number of characters, but not /
matches a single character, but not /
matches any number of characters, including /, as long as
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
GLOBBING EXAMPLES
// You can specify single files:
{src: 'foo/this.js', dest: ...}
// Or arrays of files:
{src: ['foo/this.js', 'foo/that.js', 'foo/
the-other.js'], dest: ...}
// Or you can generalize with a glob pattern:
{src: 'foo/th*.js', dest: ...}
// All .js files, in foo/, in alpha order:
{src: ['foo/*.js'], dest: ...}
// Here, bar.js is first, followed by the
remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}
MORE EXAMPLES
// This single node-glob pattern:
{src: 'foo/{a,b}*.js', dest: ...}
// Could also be written like this:
{src: ['foo/a*.js', 'foo/b*.js'], dest: ...}
// All .js files, in foo/, in alpha order:
{src: ['foo/*.js'], dest: …}
// Here, bar.js is first, followed by the
remaining files, in alpha order:
{src: ['foo/bar.js', 'foo/*.js'], dest: ...}
MORE EXAMPLES
// All files except for bar.js, in alpha order:
{src: ['foo/*.js', '!foo/bar.js'], dest: ...}
// All files in alpha order, but with bar.js at the end.
{src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...}
// Templates may be used in filepaths or glob patterns:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// But they may also reference file lists defined elsewhere in the
config:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}
TEMPLATES IN GLOBS
// Templates may be used in filepaths or glob patterns:
{src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'}
// But they may also reference file lists defined elsewhere in the
config:
{src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}
<% %> are delimiters to specify templates
Additionally, grunt and its methods are available inside
templates, eg. <%= grunt.template.today('yyyy-mm-dd') %>.
grunt.initConfig({
uglify: {
static_mappings: {
// Because these src-dest file mappings are manually specified, every
// time a new file is added or removed, the Gruntfile has to be updated.
files: [
{src: 'lib/a.js', dest: 'build/a.min.js'},
{src: 'lib/b.js', dest: 'build/b.min.js'},
{src: 'lib/subdir/c.js', dest: 'build/subdir/c.min.js'},
{src: 'lib/subdir/d.js', dest: 'build/subdir/d.min.js'},
],
},
dynamic_mappings: {
// Grunt will search for "**/*.js" under "lib/" when the "uglify" task
// runs and build the appropriate src-dest file mappings then, so you
// don't need to update the Gruntfile when files are added or removed.
files: [
{
expand: true, // Enable dynamic expansion. Must be set to enable these options
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
},
],
},
},
});
DYNAMIC MAPPING
Enable dynamic expansion.
TEMPLATES EXAMPLE
grunt.initConfig({
concat: {
sample: {
options: {
banner: '/* <%= baz %> */n', // '/* abcde */n'
},
src: ['<%= qux %>', 'baz/*.js'], // [['foo/*.js', 'bar/*.js'],
'baz/*.js']
dest: 'build/<%= baz %>.js', // 'build/abcde.js'
},
},
// Arbitrary properties used in task configuration templates.
foo: 'c',
bar: 'b<%= foo %>d', // 'bcd'
baz: 'a<%= bar %>e', // 'abcde'
qux: ['foo/*.js', 'bar/*.js'],
});
IMPORTING EXTERNAL 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'
}
}
});
Here project metadata is imported into the
Grunt config from a package.json file,
CREATING TASKS
grunt.registerTask(taskName, [description, ] taskList)
SIMPLE EXAMPLE
grunt.registerTask('default', ['jshint', 'qunit', 'concat']);
Task Arguments
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
Here we create a task called dist
passing dist as our target
properties on each task.
Task Arguments
grunt.registerTask('dist', ['concat:dist', 'uglify:dist']);
Here we create a task called dist
passing dist as our target
properties on each task.
MULTI TASKS
grunt.registerMultiTask(taskName, [description, ] taskFunction)
MULTI TASKS
grunt.initConfig({
log: {
foo: [1, 2, 3],
bar: 'hello world',
baz: false
}
});
grunt.registerMultiTask('log', 'Log stuff.', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
What would happen if we ran grunt log?
foo: [1, 2, 3]
bar: 'hello world’
baz: false
What would happen if we ran grunt log?
foo: [1, 2, 3]
bar: 'hello world’
baz: false
What would happen if we ran grunt log?
When a basic task is run, Grunt doesn't
look at the configuration or environment
—it just runs the specified task function,
passing any specified colon-separated
arguments in as function arguments.
gruntjs.com
grunt.registerTask('foo', 'A sample task that logs stuff.',
function(arg1, arg2) {
if (arguments.length === 0) {
grunt.log.writeln(this.name + ", no args");
} else {
grunt.log.writeln(this.name + ", " + arg1 + " " + arg2);
}
});
This example task logs foo, testing 123
if Grunt is run via grunt foo:testing:123.
If the task is run without arguments as
grunt foo the task logs foo, no args.
grunt.registerTask('foo', 'My "foo" task.', function() {
// Enqueue "bar" and "baz" tasks, to run after "foo"
finishes, in-order.
grunt.task.run('bar', 'baz');
// Or:
grunt.task.run(['bar', 'baz']);
});
CUSTOM TASKS
If your tasks don't follow the "multi task" structure,
use a custom task.
grunt.registerTask('asyncfoo', 'My "asyncfoo" task.',
function() {
// Force task into async mode and grab a handle to the
"done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
CUSTOM TASKS
Example of an asynchronous Task
grunt.registerTask('asyncfoo', 'My "asyncfoo" task.',
function() {
// Force task into async mode and grab a handle to the
"done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
CUSTOM TASKS
Example of an asynchronous Task
Cool parts of tasks pt 1
Can reference their own name with this.name
Can fail if any errors were logged
// Fail by returning false if this task had errors
if (ifErrors) { return false; }
console.log(this.name)
Cool parts of tasks pt 2
Tasks can be dependent on the successful execution of other tasks.
grunt.registerTask('foo', 'My "foo" task.', function() {
return false;
});
grunt.registerTask('bar', 'My "bar" task.', function() {
// Fail task if "foo" task failed or never ran.
grunt.task.requires('foo');
// This code executes if the "foo" task ran successfully.
grunt.log.writeln('Hello, world.');
});
Tasks can access configuration properties.
grunt.registerTask('foo', 'My "foo" task.', function() {
// Log the property value. Returns null if the property is
undefined.
grunt.log.writeln('The meta.name property is: ' +
grunt.config('meta.name'));
// Also logs the property value. Returns null if the
property is undefined.
grunt.log.writeln('The meta.name property is: ' +
grunt.config(['meta', 'name']));
});
Cool parts of tasks pt 3
LET’S DIG INTO AN EXAMPLE
SHARED CHAT
https://tlk.io/gruntdemo
SETUP
npm install -g grunt-cli
npm install -g bower
https://nodejs.org/download/
Install Node
Install Grunt CLI and Bower
DEMO APP
https://github.com/spencer48/Grunt-Demo
Fork this on Git Hub
then…
git clone https://github.com/YOURUSERNAME/Grunt-Demo
INSTALL
npm install
bower install
ARCHITECTURE
app/
images/
scripts/
controllers/
services/
app.js
styles/
views/
index.html
bower_components/
node_modules/
test/
.tmp/
.saas-cache/
.bowerrc
bower.json
Gruntfile.js
package.json
README.md
GRUNTFILE
'use strict';
module.exports = function (grunt) {
require('jit-grunt')(grunt, {
});
var appConfig = {
app: require('./bower.json').appPath || 'app',
dist: 'dist'
};
grunt.initConfig({
});
grunt.registerTask('default', [
]);
};
INIT CONFIG
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.
},
},
});
When you run a task, Grunt looks here
for it’s configuration.
ADDING DEPENDENCIES
{
"name": "gruntdemo",
"devDependencies": {
"grunt": "^0.4.5",
"grunt-concurrent": "^1.0.0",
"grunt-contrib-connect": "^0.9.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-newer": "^1.1.0",
"jit-grunt": "^0.9.1",
"jshint-stylish": "^1.0.0",
"time-grunt": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
}
…then npm install
package.json
SETTING UP CONNECT
connect: {
options: {
port: 9000,
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect().use(
'/app/styles',
connect.static('./app/styles')
),
connect.static(appConfig.app)
];
}
}
},
}
SETTING UP WATCHwatch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep']
},
js: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
compass: {
files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
tasks: ['compass:server', 'autoprefixer:server']
},
gruntfile: {
files: ['Gruntfile.js']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/{,*/}*.html',
'.tmp/styles/{,*/}*.css',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
}
CONFIGURING JSHINT
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: {
src: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
]
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,*/}*.js']
}
}
{
"bitwise": true,
"browser": true,
"curly": true,
"eqeqeq": true,
"esnext": true,
"latedef": true,
"noarg": true,
"node": true,
"strict": true,
"undef": true,
"unused": true,
"globals": {
"angular": false
}
}
.jshintrcGruntFile.js
CONFIGURING CLEAN
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/{,*/}*',
'!<%= yeoman.dist %>/.git{,*/}*'
]
}]
},
server: '.tmp'
}
GruntFile.js
CONFIGURING WIREDEP
wiredep: {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: /..//
},
test: {
devDependencies: true,
src: '<%= karma.unit.configFile %>',
ignorePath: /..//,
fileTypes:{
js: {
block: /(([st]*)/{2}s*?bower:s*?(S*))(n|r|.)*?(/{2}s*endbower)/gi,
detect: {
js: /'(.*.js)'/gi
},
replace: {
js: ''{{filePath}}','
}
}
}
},
sass: {
src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
ignorePath: /(../){1,2}bower_components//
}
},
GruntFile.js
CONFIGURING COMPASS
compass: {
options: {
sassDir: '<%= yeoman.app %>/styles',
cssDir: '.tmp/styles',
generatedImagesDir: '.tmp/images/generated',
imagesDir: '<%= yeoman.app %>/images',
javascriptsDir: '<%= yeoman.app %>/scripts',
fontsDir: '<%= yeoman.app %>/styles/fonts',
importPath: './bower_components',
httpImagesPath: '/images',
httpGeneratedImagesPath: '/images/generated',
httpFontsPath: '/styles/fonts',
relativeAssets: false,
assetCacheBuster: false,
raw: 'Sass::Script::Number.precision = 10n'
},
dist: {
options: {
generatedImagesDir: '<%= yeoman.dist %>/images/generated'
}
},
server: {
options: {
sourcemap: true
}
}
}
GruntFile.js
CONFIGURING CLEAN
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/{,*/}*',
'!<%= yeoman.dist %>/.git{,*/}*'
]
}]
},
server: '.tmp'
}
GruntFile.js
CONFIGURING AUTOPREFIXER
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
server: {
options: {
map: true,
},
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
}
GruntFile.js
CONFIGURING FILE REV
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
},
GruntFile.js
CONFIGURING USEMIN
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
GruntFile.js
CONFIGURING USEMIN
// Performs rewrites based on filerev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'],
options: {
assetsDirs: [
'<%= yeoman.dist %>',
'<%= yeoman.dist %>/images',
'<%= yeoman.dist %>/styles'
],
patterns: {
js: [[/(images/[^''""]*.(png|jpg|jpeg|gif|webp|svg))/g,
'Replacing references to images']]
}
}
},
GruntFile.js
CONFIGURING IMG/SVG MIN
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}]
}
},
GruntFile.js
CONFIGURING HTML MIN
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
GruntFile.js
CONFIGURING NGTEMPATES
ngtemplates: {
dist: {
options: {
module: 'gruntdemoApp',
htmlmin: '<%= htmlmin.dist.options %>',
usemin: 'scripts/scripts.js'
},
cwd: '<%= yeoman.app %>',
src: 'views/{,*/}*.html',
dest: '.tmp/templateCache.js'
}
},
GruntFile.js
CONFIGURING NGANNOTATE
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.tmp/concat/scripts',
src: '*.js',
dest: '.tmp/concat/scripts'
}]
}
}
GruntFile.js
CONFIGURING NGANNOTATE
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'.htaccess',
'*.html',
'images/{,*/}*.{webp}',
'styles/fonts/{,*/}*.*'
]
}, {
expand: true,
cwd: '.tmp/images',
dest: '<%= yeoman.dist %>/images',
src: ['generated/*']
}, {
expand: true,
cwd: '.',
src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
dest: '<%= yeoman.dist %>'
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
GruntFile.js
Sorry for the tiny font :(
CONFIGURING KARMA
karma: {
unit: {
configFile: 'test/karma.conf.js',
singleRun: true
}
}
GruntFile.js
CONFIGURING CONCURRENT
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'compass:server'
],
test: [
'compass'
],
dist: [
'compass:dist',
'imagemin',
'svgmin'
]
},
GruntFile.js
TASK EXAMPLES
grunt.registerTask('build', [
'clean:dist',
'wiredep',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'ngtemplates',
'concat',
'ngAnnotate',
'copy:dist',
'cdnify',
'cssmin',
'uglify',
'filerev',
'usemin',
'htmlmin'
]);
BUILD
grunt.registerTask('test', [
'clean:server',
'wiredep',
'concurrent:test',
'autoprefixer',
'connect:test',
'karma'
]);
TEST
TASK EXAMPLES
grunt.registerTask('serve', 'Compile then start a connect web
server', function (target) {
if (target === 'dist') {
return grunt.task.run([‘build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'wiredep',
'concurrent:server',
'autoprefixer:server',
'connect:livereload',
'watch'
]);
});
SERVE
THANKS
QUESTIONS?
S P E N C E R H A N D L E Y
Mastering Grunt
VIDEO SERIES
@spencer414
www.spencerhand.ly
www.podclear.com

More Related Content

What's hot

Advanced WordPress Development Environments
Advanced WordPress Development EnvironmentsAdvanced WordPress Development Environments
Advanced WordPress Development EnvironmentsBeau Lebens
 
JavaScript performance patterns
JavaScript performance patternsJavaScript performance patterns
JavaScript performance patterns
Stoyan Stefanov
 
WordPress as the Backbone(.js)
WordPress as the Backbone(.js)WordPress as the Backbone(.js)
WordPress as the Backbone(.js)
Beau Lebens
 
JavaScript Performance Patterns
JavaScript Performance PatternsJavaScript Performance Patterns
JavaScript Performance Patterns
Stoyan Stefanov
 
High Performance Social Plugins
High Performance Social PluginsHigh Performance Social Plugins
High Performance Social Plugins
Stoyan Stefanov
 
Progressive Downloads and Rendering - take #2
Progressive Downloads and Rendering - take #2Progressive Downloads and Rendering - take #2
Progressive Downloads and Rendering - take #2
Stoyan Stefanov
 
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Dirk Ginader
 
Angular2 ecosystem
Angular2 ecosystemAngular2 ecosystem
Angular2 ecosystem
Kamil Lelonek
 
SocketStream
SocketStreamSocketStream
SocketStream
Paul Jensen
 
Choosing a Javascript Framework
Choosing a Javascript FrameworkChoosing a Javascript Framework
Choosing a Javascript Framework
All Things Open
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC Framework
Bo-Yi Wu
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service Worker
Chang W. Doh
 
[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web DesignChristopher Schmitt
 
Browser Wars Episode 1: The Phantom Menace
Browser Wars Episode 1: The Phantom MenaceBrowser Wars Episode 1: The Phantom Menace
Browser Wars Episode 1: The Phantom MenaceNicholas Zakas
 
Ruby MVC from scratch with Rack
Ruby MVC from scratch with RackRuby MVC from scratch with Rack
Ruby MVC from scratch with Rack
DonSchado
 
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Ryan Weaver
 
Enough with the JavaScript already!
Enough with the JavaScript already!Enough with the JavaScript already!
Enough with the JavaScript already!
Nicholas Zakas
 
HTML5 Web Workers-unleashed
HTML5 Web Workers-unleashedHTML5 Web Workers-unleashed
HTML5 Web Workers-unleashedPeter Lubbers
 
Rails Girls: Programming, Web Applications and Ruby on Rails
Rails Girls: Programming, Web Applications and Ruby on RailsRails Girls: Programming, Web Applications and Ruby on Rails
Rails Girls: Programming, Web Applications and Ruby on Rails
DonSchado
 
Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Rack
sickill
 

What's hot (20)

Advanced WordPress Development Environments
Advanced WordPress Development EnvironmentsAdvanced WordPress Development Environments
Advanced WordPress Development Environments
 
JavaScript performance patterns
JavaScript performance patternsJavaScript performance patterns
JavaScript performance patterns
 
WordPress as the Backbone(.js)
WordPress as the Backbone(.js)WordPress as the Backbone(.js)
WordPress as the Backbone(.js)
 
JavaScript Performance Patterns
JavaScript Performance PatternsJavaScript Performance Patterns
JavaScript Performance Patterns
 
High Performance Social Plugins
High Performance Social PluginsHigh Performance Social Plugins
High Performance Social Plugins
 
Progressive Downloads and Rendering - take #2
Progressive Downloads and Rendering - take #2Progressive Downloads and Rendering - take #2
Progressive Downloads and Rendering - take #2
 
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
Let Grunt do the work, focus on the fun! [Open Web Camp 2013]
 
Angular2 ecosystem
Angular2 ecosystemAngular2 ecosystem
Angular2 ecosystem
 
SocketStream
SocketStreamSocketStream
SocketStream
 
Choosing a Javascript Framework
Choosing a Javascript FrameworkChoosing a Javascript Framework
Choosing a Javascript Framework
 
CodeIgniter PHP MVC Framework
CodeIgniter PHP MVC FrameworkCodeIgniter PHP MVC Framework
CodeIgniter PHP MVC Framework
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service Worker
 
[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design
 
Browser Wars Episode 1: The Phantom Menace
Browser Wars Episode 1: The Phantom MenaceBrowser Wars Episode 1: The Phantom Menace
Browser Wars Episode 1: The Phantom Menace
 
Ruby MVC from scratch with Rack
Ruby MVC from scratch with RackRuby MVC from scratch with Rack
Ruby MVC from scratch with Rack
 
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
Finally, Professional Frontend Dev with ReactJS, WebPack & Symfony (Symfony C...
 
Enough with the JavaScript already!
Enough with the JavaScript already!Enough with the JavaScript already!
Enough with the JavaScript already!
 
HTML5 Web Workers-unleashed
HTML5 Web Workers-unleashedHTML5 Web Workers-unleashed
HTML5 Web Workers-unleashed
 
Rails Girls: Programming, Web Applications and Ruby on Rails
Rails Girls: Programming, Web Applications and Ruby on RailsRails Girls: Programming, Web Applications and Ruby on Rails
Rails Girls: Programming, Web Applications and Ruby on Rails
 
Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Rack
 

Viewers also liked

An Introduction to AngularJs Unittesting
An Introduction to AngularJs UnittestingAn Introduction to AngularJs Unittesting
An Introduction to AngularJs UnittestingInthra onsap
 
Angular js, Yeomon & Grunt
Angular js, Yeomon & GruntAngular js, Yeomon & Grunt
Angular js, Yeomon & Grunt
Richard Powell
 
Wrangling the WordPress Template Hierarchy Like a Boss
Wrangling the WordPress Template Hierarchy Like a BossWrangling the WordPress Template Hierarchy Like a Boss
Wrangling the WordPress Template Hierarchy Like a Boss
IntrepidRealist
 
GruntJS + Wordpress
GruntJS + WordpressGruntJS + Wordpress
GruntJS + Wordpress
Leonardo Balter
 
Grunt js and WordPress
Grunt js and WordPressGrunt js and WordPress
Grunt js and WordPress
WP Australia
 
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and GruntWordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
Brajeshwar Oinam
 
Metadata and me
Metadata and meMetadata and me
Metadata and me
Nick Sheppard
 
A Quick and Dirty D3.js Tutorial
A Quick and Dirty D3.js TutorialA Quick and Dirty D3.js Tutorial
A Quick and Dirty D3.js Tutorial
Young-Ho Kim
 
Come migliorare le performance di WordPress con il Visual Composer
Come migliorare le performance di WordPress con il Visual ComposerCome migliorare le performance di WordPress con il Visual Composer
Come migliorare le performance di WordPress con il Visual Composer
liciapelliconi.it
 
Javascript testing: tools of the trade
Javascript testing: tools of the tradeJavascript testing: tools of the trade
Javascript testing: tools of the trade
Juanma Orta
 
Using Composer to create manageable WordPress websites
Using Composer to create manageable WordPress websitesUsing Composer to create manageable WordPress websites
Using Composer to create manageable WordPress websites
Anna Ladoshkina
 
WordPress Database: What's behind those 12 tables
WordPress Database: What's behind those 12 tablesWordPress Database: What's behind those 12 tables
WordPress Database: What's behind those 12 tables
Mauricio Gelves
 
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
Christopher Bartling
 
WordPress Template Hierarchy
WordPress Template HierarchyWordPress Template Hierarchy
WordPress Template Hierarchy
Sarah Whinnem
 
Getting Started With Grunt for WordPress Development
Getting Started With Grunt for WordPress DevelopmentGetting Started With Grunt for WordPress Development
Getting Started With Grunt for WordPress Development
David Bisset
 

Viewers also liked (15)

An Introduction to AngularJs Unittesting
An Introduction to AngularJs UnittestingAn Introduction to AngularJs Unittesting
An Introduction to AngularJs Unittesting
 
Angular js, Yeomon & Grunt
Angular js, Yeomon & GruntAngular js, Yeomon & Grunt
Angular js, Yeomon & Grunt
 
Wrangling the WordPress Template Hierarchy Like a Boss
Wrangling the WordPress Template Hierarchy Like a BossWrangling the WordPress Template Hierarchy Like a Boss
Wrangling the WordPress Template Hierarchy Like a Boss
 
GruntJS + Wordpress
GruntJS + WordpressGruntJS + Wordpress
GruntJS + Wordpress
 
Grunt js and WordPress
Grunt js and WordPressGrunt js and WordPress
Grunt js and WordPress
 
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and GruntWordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
WordPress Theme Development Workflow with Node.js, Ruby, Sass, Bower and Grunt
 
Metadata and me
Metadata and meMetadata and me
Metadata and me
 
A Quick and Dirty D3.js Tutorial
A Quick and Dirty D3.js TutorialA Quick and Dirty D3.js Tutorial
A Quick and Dirty D3.js Tutorial
 
Come migliorare le performance di WordPress con il Visual Composer
Come migliorare le performance di WordPress con il Visual ComposerCome migliorare le performance di WordPress con il Visual Composer
Come migliorare le performance di WordPress con il Visual Composer
 
Javascript testing: tools of the trade
Javascript testing: tools of the tradeJavascript testing: tools of the trade
Javascript testing: tools of the trade
 
Using Composer to create manageable WordPress websites
Using Composer to create manageable WordPress websitesUsing Composer to create manageable WordPress websites
Using Composer to create manageable WordPress websites
 
WordPress Database: What's behind those 12 tables
WordPress Database: What's behind those 12 tablesWordPress Database: What's behind those 12 tables
WordPress Database: What's behind those 12 tables
 
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
 
WordPress Template Hierarchy
WordPress Template HierarchyWordPress Template Hierarchy
WordPress Template Hierarchy
 
Getting Started With Grunt for WordPress Development
Getting Started With Grunt for WordPress DevelopmentGetting Started With Grunt for WordPress Development
Getting Started With Grunt for WordPress Development
 

Similar to Mastering Grunt

On secure application of PHP wrappers
On secure application  of PHP wrappersOn secure application  of PHP wrappers
On secure application of PHP wrappersPositive Hack Days
 
Get Grulping with JavaScript Task Runners (Matt Gifford)
Get Grulping with JavaScript Task Runners (Matt Gifford)Get Grulping with JavaScript Task Runners (Matt Gifford)
Get Grulping with JavaScript Task Runners (Matt Gifford)
Future Insights
 
Node.js basics
Node.js basicsNode.js basics
Node.js basicsBen Lin
 
Txjs
TxjsTxjs
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs偉格 高
 
Couch db 浅漫游.
Couch db 浅漫游.Couch db 浅漫游.
Couch db 浅漫游.shyboyzk
 
Automating WordPress Theme Development
Automating WordPress Theme DevelopmentAutomating WordPress Theme Development
Automating WordPress Theme Development
Hardeep Asrani
 
Easy deployment & management of cloud apps
Easy deployment & management of cloud appsEasy deployment & management of cloud apps
Easy deployment & management of cloud appsDavid Cunningham
 
Practical Chef and Capistrano for Your Rails App
Practical Chef and Capistrano for Your Rails AppPractical Chef and Capistrano for Your Rails App
Practical Chef and Capistrano for Your Rails App
SmartLogic
 
Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
Codemotion
 
Rush, a shell that will yield to you
Rush, a shell that will yield to youRush, a shell that will yield to you
Rush, a shell that will yield to you
guestdd9d06
 
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
John Hann
 
Deploying Rails Applications with Capistrano
Deploying Rails Applications with CapistranoDeploying Rails Applications with Capistrano
Deploying Rails Applications with CapistranoAlmir Mendes
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
davidchubbs
 
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
Омские ИТ-субботники
 
Workflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.jsWorkflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.js
Davidson Fellipe
 
Intro to Ember.JS 2016
Intro to Ember.JS 2016Intro to Ember.JS 2016
Intro to Ember.JS 2016
Sandino Núñez
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
Nathan Smith
 
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docxcase3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
tidwellveronique
 
Couchdb
CouchdbCouchdb
Couchdb
Brian Smith
 

Similar to Mastering Grunt (20)

On secure application of PHP wrappers
On secure application  of PHP wrappersOn secure application  of PHP wrappers
On secure application of PHP wrappers
 
Get Grulping with JavaScript Task Runners (Matt Gifford)
Get Grulping with JavaScript Task Runners (Matt Gifford)Get Grulping with JavaScript Task Runners (Matt Gifford)
Get Grulping with JavaScript Task Runners (Matt Gifford)
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
 
Txjs
TxjsTxjs
Txjs
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
Couch db 浅漫游.
Couch db 浅漫游.Couch db 浅漫游.
Couch db 浅漫游.
 
Automating WordPress Theme Development
Automating WordPress Theme DevelopmentAutomating WordPress Theme Development
Automating WordPress Theme Development
 
Easy deployment & management of cloud apps
Easy deployment & management of cloud appsEasy deployment & management of cloud apps
Easy deployment & management of cloud apps
 
Practical Chef and Capistrano for Your Rails App
Practical Chef and Capistrano for Your Rails AppPractical Chef and Capistrano for Your Rails App
Practical Chef and Capistrano for Your Rails App
 
Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
 
Rush, a shell that will yield to you
Rush, a shell that will yield to youRush, a shell that will yield to you
Rush, a shell that will yield to you
 
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
Zero-config JavaScript apps with RaveJS -- SVCC fall 2014
 
Deploying Rails Applications with Capistrano
Deploying Rails Applications with CapistranoDeploying Rails Applications with Capistrano
Deploying Rails Applications with Capistrano
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
 
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
2017-03-11 02 Денис Нелюбин. Docker & Ansible - лучшие друзья DevOps
 
Workflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.jsWorkflow para desenvolvimento Web & Mobile usando grunt.js
Workflow para desenvolvimento Web & Mobile usando grunt.js
 
Intro to Ember.JS 2016
Intro to Ember.JS 2016Intro to Ember.JS 2016
Intro to Ember.JS 2016
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
 
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docxcase3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
 
Couchdb
CouchdbCouchdb
Couchdb
 

Recently uploaded

Vaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdfVaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdf
Kamal Acharya
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
MuhammadTufail242431
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
Kamal Acharya
 
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdfTop 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Teleport Manpower Consultant
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
Amil Baba Dawood bangali
 
MCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdfMCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdf
Osamah Alsalih
 
weather web application report.pdf
weather web application report.pdfweather web application report.pdf
weather web application report.pdf
Pratik Pawar
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Dr.Costas Sachpazis
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
R&R Consult
 
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
MdTanvirMahtab2
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
bakpo1
 
Event Management System Vb Net Project Report.pdf
Event Management System Vb Net  Project Report.pdfEvent Management System Vb Net  Project Report.pdf
Event Management System Vb Net Project Report.pdf
Kamal Acharya
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
AJAYKUMARPUND1
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
AhmedHussein950959
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
fxintegritypublishin
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
SamSarthak3
 
Democratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek AryaDemocratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek Arya
abh.arya
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
Robbie Edward Sayers
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
Jayaprasanna4
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
VENKATESHvenky89705
 

Recently uploaded (20)

Vaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdfVaccine management system project report documentation..pdf
Vaccine management system project report documentation..pdf
 
Halogenation process of chemical process industries
Halogenation process of chemical process industriesHalogenation process of chemical process industries
Halogenation process of chemical process industries
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
 
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdfTop 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
Top 10 Oil and Gas Projects in Saudi Arabia 2024.pdf
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
 
MCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdfMCQ Soil mechanics questions (Soil shear strength).pdf
MCQ Soil mechanics questions (Soil shear strength).pdf
 
weather web application report.pdf
weather web application report.pdfweather web application report.pdf
weather web application report.pdf
 
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
Sachpazis:Terzaghi Bearing Capacity Estimation in simple terms with Calculati...
 
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptxCFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
CFD Simulation of By-pass Flow in a HRSG module by R&R Consult.pptx
 
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
Industrial Training at Shahjalal Fertilizer Company Limited (SFCL)
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
 
Event Management System Vb Net Project Report.pdf
Event Management System Vb Net  Project Report.pdfEvent Management System Vb Net  Project Report.pdf
Event Management System Vb Net Project Report.pdf
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
 
Democratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek AryaDemocratizing Fuzzing at Scale by Abhishek Arya
Democratizing Fuzzing at Scale by Abhishek Arya
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
 

Mastering Grunt

  • 1. MASTERING GRUNT S p e n c e r H a n d l e y
  • 2. ABOUT ME S P E N C E R H A N D L E Y Mastering Grunt VIDEO SERIES @spencer414 www.spencerhand.ly
  • 5. REASONS TO USE IT • Huge Community • Strong Adoption • Valuable Resume Boost • Highly in Demand • Easy and, dare I say fun to use • All the cool kids use it
  • 7. GRUNTFILE 'use strict'; module.exports = function (grunt) { require('jit-grunt')(grunt, { }); var appConfig = { app: require('./bower.json').appPath || 'app', dist: 'dist' }; grunt.initConfig({ }); grunt.registerTask('default', [ ]); };
  • 8. INIT CONFIG 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. }, }, }); When you run a task, Grunt looks here for it’s configuration.
  • 9. INIT CONFIG Multi-tasks can have multiple configurations, defined using arbitrarily named "targets." gruntjs.com concat: { foo: { // You could run this with concat:foo }, bar: { // You could run this with concat:bar }, }
  • 10. INIT CONFIG Multi-tasks can have multiple configurations, defined using arbitrarily named "targets." gruntjs.com concat: { foo: { // You could run this with concat:foo }, bar: { // You could run this with concat:bar }, } Simply running concat will integrate through all targets
  • 11. TASK CONFIGURATION: OPTIONS Inside a task configuration, an options property may be specified to override built-in defaults. You can also pass options to each target. 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. }, }
  • 13. COMPACT FORMAT grunt.initConfig({ jshint: { foo: { src: ['src/aa.js', 'src/aaa.js'] }, }, concat: { bar: { src: ['src/bb.js', 'src/bbb.js'], dest: 'dest/b.js', }, }, }); Typically for read-only tasks where the dest is not needed. Like JShint
  • 14. FILE OBJECT FORMAT 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'], }, }, }, }); destination: [source files] multiple src-dest mappings per-target
  • 15. FILE ARRAY FORMAT 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'}, ], }, }, }); supports multiple src-dest file mappings per-target while also allowing additional properties per mapping.
  • 16. FILTER FUNCTION grunt.initConfig({ clean: { foo: { src: ['tmp/**/*'], filter: 'isFile', }, }, }); Will clean only if the pattern matches an actual file: Uses nodes valid fs.Stats method names
  • 17. CUSTOM FILTER FUNCTION grunt.initConfig({ clean: { foo: { src: ['tmp/**/*'], filter: function(filepath) { return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0); }, }, }, }); The following will only clean folders that are empty You can create custom filters for specifying files
  • 18. CUSTOM FILTER FUNCTION grunt.initConfig({ clean: { foo: { src: ['tmp/**/*'], filter: function(filepath) { return (grunt.file.isDir(filepath) && require('fs').readdirSync(filepath).length === 0); }, }, }, }); The following will only clean folders that are empty You can create custom filters for specifying files
  • 20. GLOBBING BASICS * ? ** {} ! matches any number of characters, but not / matches a single character, but not / matches any number of characters, including /, as long as 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
  • 21. GLOBBING EXAMPLES // You can specify single files: {src: 'foo/this.js', dest: ...} // Or arrays of files: {src: ['foo/this.js', 'foo/that.js', 'foo/ the-other.js'], dest: ...} // Or you can generalize with a glob pattern: {src: 'foo/th*.js', dest: ...} // All .js files, in foo/, in alpha order: {src: ['foo/*.js'], dest: ...} // Here, bar.js is first, followed by the remaining files, in alpha order: {src: ['foo/bar.js', 'foo/*.js'], dest: ...}
  • 22. MORE EXAMPLES // This single node-glob pattern: {src: 'foo/{a,b}*.js', dest: ...} // Could also be written like this: {src: ['foo/a*.js', 'foo/b*.js'], dest: ...} // All .js files, in foo/, in alpha order: {src: ['foo/*.js'], dest: …} // Here, bar.js is first, followed by the remaining files, in alpha order: {src: ['foo/bar.js', 'foo/*.js'], dest: ...}
  • 23. MORE EXAMPLES // All files except for bar.js, in alpha order: {src: ['foo/*.js', '!foo/bar.js'], dest: ...} // All files in alpha order, but with bar.js at the end. {src: ['foo/*.js', '!foo/bar.js', 'foo/bar.js'], dest: ...} // Templates may be used in filepaths or glob patterns: {src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'} // But they may also reference file lists defined elsewhere in the config: {src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...}
  • 24. TEMPLATES IN GLOBS // Templates may be used in filepaths or glob patterns: {src: ['src/<%= basename %>.js'], dest: 'build/<%= basename %>.min.js'} // But they may also reference file lists defined elsewhere in the config: {src: ['foo/*.js', '<%= jshint.all.src %>'], dest: ...} <% %> are delimiters to specify templates Additionally, grunt and its methods are available inside templates, eg. <%= grunt.template.today('yyyy-mm-dd') %>.
  • 25. grunt.initConfig({ uglify: { static_mappings: { // Because these src-dest file mappings are manually specified, every // time a new file is added or removed, the Gruntfile has to be updated. files: [ {src: 'lib/a.js', dest: 'build/a.min.js'}, {src: 'lib/b.js', dest: 'build/b.min.js'}, {src: 'lib/subdir/c.js', dest: 'build/subdir/c.min.js'}, {src: 'lib/subdir/d.js', dest: 'build/subdir/d.min.js'}, ], }, dynamic_mappings: { // Grunt will search for "**/*.js" under "lib/" when the "uglify" task // runs and build the appropriate src-dest file mappings then, so you // don't need to update the Gruntfile when files are added or removed. files: [ { expand: true, // Enable dynamic expansion. Must be set to enable these options 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 }, ], }, }, }); DYNAMIC MAPPING Enable dynamic expansion.
  • 26. TEMPLATES EXAMPLE grunt.initConfig({ concat: { sample: { options: { banner: '/* <%= baz %> */n', // '/* abcde */n' }, src: ['<%= qux %>', 'baz/*.js'], // [['foo/*.js', 'bar/*.js'], 'baz/*.js'] dest: 'build/<%= baz %>.js', // 'build/abcde.js' }, }, // Arbitrary properties used in task configuration templates. foo: 'c', bar: 'b<%= foo %>d', // 'bcd' baz: 'a<%= bar %>e', // 'abcde' qux: ['foo/*.js', 'bar/*.js'], });
  • 27. IMPORTING EXTERNAL 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' } } }); Here project metadata is imported into the Grunt config from a package.json file,
  • 30. Task Arguments grunt.registerTask('dist', ['concat:dist', 'uglify:dist']); Here we create a task called dist passing dist as our target properties on each task.
  • 31. Task Arguments grunt.registerTask('dist', ['concat:dist', 'uglify:dist']); Here we create a task called dist passing dist as our target properties on each task.
  • 33. MULTI TASKS grunt.initConfig({ log: { foo: [1, 2, 3], bar: 'hello world', baz: false } }); grunt.registerMultiTask('log', 'Log stuff.', function() { grunt.log.writeln(this.target + ': ' + this.data); }); What would happen if we ran grunt log?
  • 34. foo: [1, 2, 3] bar: 'hello world’ baz: false What would happen if we ran grunt log?
  • 35. foo: [1, 2, 3] bar: 'hello world’ baz: false What would happen if we ran grunt log?
  • 36. When a basic task is run, Grunt doesn't look at the configuration or environment —it just runs the specified task function, passing any specified colon-separated arguments in as function arguments. gruntjs.com
  • 37. grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2) { if (arguments.length === 0) { grunt.log.writeln(this.name + ", no args"); } else { grunt.log.writeln(this.name + ", " + arg1 + " " + arg2); } }); This example task logs foo, testing 123 if Grunt is run via grunt foo:testing:123. If the task is run without arguments as grunt foo the task logs foo, no args.
  • 38. grunt.registerTask('foo', 'My "foo" task.', function() { // Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order. grunt.task.run('bar', 'baz'); // Or: grunt.task.run(['bar', 'baz']); }); CUSTOM TASKS If your tasks don't follow the "multi task" structure, use a custom task.
  • 39. grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() { // Force task into async mode and grab a handle to the "done" function. var done = this.async(); // Run some sync stuff. grunt.log.writeln('Processing task...'); // And some async stuff. setTimeout(function() { grunt.log.writeln('All done!'); done(); }, 1000); }); CUSTOM TASKS Example of an asynchronous Task
  • 40. grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() { // Force task into async mode and grab a handle to the "done" function. var done = this.async(); // Run some sync stuff. grunt.log.writeln('Processing task...'); // And some async stuff. setTimeout(function() { grunt.log.writeln('All done!'); done(); }, 1000); }); CUSTOM TASKS Example of an asynchronous Task
  • 41. Cool parts of tasks pt 1 Can reference their own name with this.name Can fail if any errors were logged // Fail by returning false if this task had errors if (ifErrors) { return false; } console.log(this.name)
  • 42. Cool parts of tasks pt 2 Tasks can be dependent on the successful execution of other tasks. grunt.registerTask('foo', 'My "foo" task.', function() { return false; }); grunt.registerTask('bar', 'My "bar" task.', function() { // Fail task if "foo" task failed or never ran. grunt.task.requires('foo'); // This code executes if the "foo" task ran successfully. grunt.log.writeln('Hello, world.'); });
  • 43. Tasks can access configuration properties. grunt.registerTask('foo', 'My "foo" task.', function() { // Log the property value. Returns null if the property is undefined. grunt.log.writeln('The meta.name property is: ' + grunt.config('meta.name')); // Also logs the property value. Returns null if the property is undefined. grunt.log.writeln('The meta.name property is: ' + grunt.config(['meta', 'name'])); }); Cool parts of tasks pt 3
  • 44. LET’S DIG INTO AN EXAMPLE
  • 46. SETUP npm install -g grunt-cli npm install -g bower https://nodejs.org/download/ Install Node Install Grunt CLI and Bower
  • 47. DEMO APP https://github.com/spencer48/Grunt-Demo Fork this on Git Hub then… git clone https://github.com/YOURUSERNAME/Grunt-Demo
  • 50. GRUNTFILE 'use strict'; module.exports = function (grunt) { require('jit-grunt')(grunt, { }); var appConfig = { app: require('./bower.json').appPath || 'app', dist: 'dist' }; grunt.initConfig({ }); grunt.registerTask('default', [ ]); };
  • 51. INIT CONFIG 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. }, }, }); When you run a task, Grunt looks here for it’s configuration.
  • 52. ADDING DEPENDENCIES { "name": "gruntdemo", "devDependencies": { "grunt": "^0.4.5", "grunt-concurrent": "^1.0.0", "grunt-contrib-connect": "^0.9.0", "grunt-contrib-watch": "^0.6.1", "grunt-newer": "^1.1.0", "jit-grunt": "^0.9.1", "jshint-stylish": "^1.0.0", "time-grunt": "^1.0.0" }, "engines": { "node": ">=0.10.0" } } …then npm install package.json
  • 53. SETTING UP CONNECT connect: { options: { port: 9000, hostname: 'localhost', livereload: 35729 }, livereload: { options: { open: true, middleware: function (connect) { return [ connect.static('.tmp'), connect().use( '/bower_components', connect.static('./bower_components') ), connect().use( '/app/styles', connect.static('./app/styles') ), connect.static(appConfig.app) ]; } } }, }
  • 54. SETTING UP WATCHwatch: { bower: { files: ['bower.json'], tasks: ['wiredep'] }, js: { files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], tasks: ['newer:jshint:all'], options: { livereload: '<%= connect.options.livereload %>' } }, compass: { files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], tasks: ['compass:server', 'autoprefixer:server'] }, gruntfile: { files: ['Gruntfile.js'] }, livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ '<%= yeoman.app %>/{,*/}*.html', '.tmp/styles/{,*/}*.css', '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' ] } }
  • 55. CONFIGURING JSHINT jshint: { options: { jshintrc: '.jshintrc', reporter: require('jshint-stylish') }, all: { src: [ 'Gruntfile.js', '<%= yeoman.app %>/scripts/{,*/}*.js' ] }, test: { options: { jshintrc: 'test/.jshintrc' }, src: ['test/spec/{,*/}*.js'] } } { "bitwise": true, "browser": true, "curly": true, "eqeqeq": true, "esnext": true, "latedef": true, "noarg": true, "node": true, "strict": true, "undef": true, "unused": true, "globals": { "angular": false } } .jshintrcGruntFile.js
  • 56. CONFIGURING CLEAN clean: { dist: { files: [{ dot: true, src: [ '.tmp', '<%= yeoman.dist %>/{,*/}*', '!<%= yeoman.dist %>/.git{,*/}*' ] }] }, server: '.tmp' } GruntFile.js
  • 57. CONFIGURING WIREDEP wiredep: { app: { src: ['<%= yeoman.app %>/index.html'], ignorePath: /..// }, test: { devDependencies: true, src: '<%= karma.unit.configFile %>', ignorePath: /..//, fileTypes:{ js: { block: /(([st]*)/{2}s*?bower:s*?(S*))(n|r|.)*?(/{2}s*endbower)/gi, detect: { js: /'(.*.js)'/gi }, replace: { js: ''{{filePath}}',' } } } }, sass: { src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], ignorePath: /(../){1,2}bower_components// } }, GruntFile.js
  • 58. CONFIGURING COMPASS compass: { options: { sassDir: '<%= yeoman.app %>/styles', cssDir: '.tmp/styles', generatedImagesDir: '.tmp/images/generated', imagesDir: '<%= yeoman.app %>/images', javascriptsDir: '<%= yeoman.app %>/scripts', fontsDir: '<%= yeoman.app %>/styles/fonts', importPath: './bower_components', httpImagesPath: '/images', httpGeneratedImagesPath: '/images/generated', httpFontsPath: '/styles/fonts', relativeAssets: false, assetCacheBuster: false, raw: 'Sass::Script::Number.precision = 10n' }, dist: { options: { generatedImagesDir: '<%= yeoman.dist %>/images/generated' } }, server: { options: { sourcemap: true } } } GruntFile.js
  • 59. CONFIGURING CLEAN clean: { dist: { files: [{ dot: true, src: [ '.tmp', '<%= yeoman.dist %>/{,*/}*', '!<%= yeoman.dist %>/.git{,*/}*' ] }] }, server: '.tmp' } GruntFile.js
  • 60. CONFIGURING AUTOPREFIXER // Add vendor prefixed styles autoprefixer: { options: { browsers: ['last 1 version'] }, server: { options: { map: true, }, files: [{ expand: true, cwd: '.tmp/styles/', src: '{,*/}*.css', dest: '.tmp/styles/' }] }, dist: { files: [{ expand: true, cwd: '.tmp/styles/', src: '{,*/}*.css', dest: '.tmp/styles/' }] } } GruntFile.js
  • 61. CONFIGURING FILE REV // Renames files for browser caching purposes filerev: { dist: { src: [ '<%= yeoman.dist %>/scripts/{,*/}*.js', '<%= yeoman.dist %>/styles/{,*/}*.css', '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', '<%= yeoman.dist %>/styles/fonts/*' ] } }, GruntFile.js
  • 62. CONFIGURING USEMIN useminPrepare: { html: '<%= yeoman.app %>/index.html', options: { dest: '<%= yeoman.dist %>', flow: { html: { steps: { js: ['concat', 'uglifyjs'], css: ['cssmin'] }, post: {} } } } }, GruntFile.js
  • 63. CONFIGURING USEMIN // Performs rewrites based on filerev and the useminPrepare configuration usemin: { html: ['<%= yeoman.dist %>/{,*/}*.html'], css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'], options: { assetsDirs: [ '<%= yeoman.dist %>', '<%= yeoman.dist %>/images', '<%= yeoman.dist %>/styles' ], patterns: { js: [[/(images/[^''""]*.(png|jpg|jpeg|gif|webp|svg))/g, 'Replacing references to images']] } } }, GruntFile.js
  • 64. CONFIGURING IMG/SVG MIN imagemin: { dist: { files: [{ expand: true, cwd: '<%= yeoman.app %>/images', src: '{,*/}*.{png,jpg,jpeg,gif}', dest: '<%= yeoman.dist %>/images' }] } }, svgmin: { dist: { files: [{ expand: true, cwd: '<%= yeoman.app %>/images', src: '{,*/}*.svg', dest: '<%= yeoman.dist %>/images' }] } }, GruntFile.js
  • 65. CONFIGURING HTML MIN htmlmin: { dist: { options: { collapseWhitespace: true, conservativeCollapse: true, collapseBooleanAttributes: true, removeCommentsFromCDATA: true }, files: [{ expand: true, cwd: '<%= yeoman.dist %>', src: ['*.html'], dest: '<%= yeoman.dist %>' }] } }, GruntFile.js
  • 66. CONFIGURING NGTEMPATES ngtemplates: { dist: { options: { module: 'gruntdemoApp', htmlmin: '<%= htmlmin.dist.options %>', usemin: 'scripts/scripts.js' }, cwd: '<%= yeoman.app %>', src: 'views/{,*/}*.html', dest: '.tmp/templateCache.js' } }, GruntFile.js
  • 67. CONFIGURING NGANNOTATE ngAnnotate: { dist: { files: [{ expand: true, cwd: '.tmp/concat/scripts', src: '*.js', dest: '.tmp/concat/scripts' }] } } GruntFile.js
  • 68. CONFIGURING NGANNOTATE copy: { dist: { files: [{ expand: true, dot: true, cwd: '<%= yeoman.app %>', dest: '<%= yeoman.dist %>', src: [ '*.{ico,png,txt}', '.htaccess', '*.html', 'images/{,*/}*.{webp}', 'styles/fonts/{,*/}*.*' ] }, { expand: true, cwd: '.tmp/images', dest: '<%= yeoman.dist %>/images', src: ['generated/*'] }, { expand: true, cwd: '.', src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*', dest: '<%= yeoman.dist %>' }] }, styles: { expand: true, cwd: '<%= yeoman.app %>/styles', dest: '.tmp/styles/', src: '{,*/}*.css' } }, GruntFile.js Sorry for the tiny font :(
  • 69. CONFIGURING KARMA karma: { unit: { configFile: 'test/karma.conf.js', singleRun: true } } GruntFile.js
  • 70. CONFIGURING CONCURRENT // Run some tasks in parallel to speed up the build process concurrent: { server: [ 'compass:server' ], test: [ 'compass' ], dist: [ 'compass:dist', 'imagemin', 'svgmin' ] }, GruntFile.js
  • 72. TASK EXAMPLES grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { if (target === 'dist') { return grunt.task.run([‘build', 'connect:dist:keepalive']); } grunt.task.run([ 'clean:server', 'wiredep', 'concurrent:server', 'autoprefixer:server', 'connect:livereload', 'watch' ]); }); SERVE
  • 74. QUESTIONS? S P E N C E R H A N D L E Y Mastering Grunt VIDEO SERIES @spencer414 www.spencerhand.ly www.podclear.com