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
Ad

More Related Content

What's hot (20)

Advanced WordPress Development Environments
Advanced WordPress Development EnvironmentsAdvanced WordPress Development Environments
Advanced WordPress Development Environments
Beau 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 Design
Christopher 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 Menace
Nicholas 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-unleashed
Peter 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
 
Advanced WordPress Development Environments
Advanced WordPress Development EnvironmentsAdvanced WordPress Development Environments
Advanced WordPress Development Environments
Beau 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
 
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 Design
Christopher 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 Menace
Nicholas 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-unleashed
Peter 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
 

Viewers also liked (15)

An Introduction to AngularJs Unittesting
An Introduction to AngularJs UnittestingAn Introduction to AngularJs Unittesting
An Introduction to AngularJs Unittesting
Inthra 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
 
An Introduction to AngularJs Unittesting
An Introduction to AngularJs UnittestingAn Introduction to AngularJs Unittesting
An Introduction to AngularJs Unittesting
Inthra 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
 
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
 
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
 
Ad

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
Positive 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 basics
Ben Lin
 
Txjs
TxjsTxjs
Txjs
Peter Higgins
 
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 apps
David 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 Capistrano
Almir 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
 
On secure application of PHP wrappers
On secure application  of PHP wrappersOn secure application  of PHP wrappers
On secure application of PHP wrappers
Positive 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 basics
Ben Lin
 
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 apps
David 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 Capistrano
Almir 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
 
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docxcase3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
case3h231diamond.gifcase3h231energy.jpgcase3h231moder.docx
tidwellveronique
 
Ad

Recently uploaded (20)

IntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdfIntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdf
Luiz Carneiro
 
some basics electrical and electronics knowledge
some basics electrical and electronics knowledgesome basics electrical and electronics knowledge
some basics electrical and electronics knowledge
nguyentrungdo88
 
Level 1-Safety.pptx Presentation of Electrical Safety
Level 1-Safety.pptx Presentation of Electrical SafetyLevel 1-Safety.pptx Presentation of Electrical Safety
Level 1-Safety.pptx Presentation of Electrical Safety
JoseAlbertoCariasDel
 
Introduction to Zoomlion Earthmoving.pptx
Introduction to Zoomlion Earthmoving.pptxIntroduction to Zoomlion Earthmoving.pptx
Introduction to Zoomlion Earthmoving.pptx
AS1920
 
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
inmishra17121973
 
Value Stream Mapping Worskshops for Intelligent Continuous Security
Value Stream Mapping Worskshops for Intelligent Continuous SecurityValue Stream Mapping Worskshops for Intelligent Continuous Security
Value Stream Mapping Worskshops for Intelligent Continuous Security
Marc Hornbeek
 
railway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forgingrailway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forging
Javad Kadkhodapour
 
Structural Response of Reinforced Self-Compacting Concrete Deep Beam Using Fi...
Structural Response of Reinforced Self-Compacting Concrete Deep Beam Using Fi...Structural Response of Reinforced Self-Compacting Concrete Deep Beam Using Fi...
Structural Response of Reinforced Self-Compacting Concrete Deep Beam Using Fi...
Journal of Soft Computing in Civil Engineering
 
Smart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptxSmart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptx
rushikeshnavghare94
 
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design ThinkingDT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DhruvChotaliya2
 
new ppt artificial intelligence historyyy
new ppt artificial intelligence historyyynew ppt artificial intelligence historyyy
new ppt artificial intelligence historyyy
PianoPianist
 
AI-assisted Software Testing (3-hours tutorial)
AI-assisted Software Testing (3-hours tutorial)AI-assisted Software Testing (3-hours tutorial)
AI-assisted Software Testing (3-hours tutorial)
Vəhid Gəruslu
 
Smart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineeringSmart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineering
rushikeshnavghare94
 
Artificial Intelligence (AI) basics.pptx
Artificial Intelligence (AI) basics.pptxArtificial Intelligence (AI) basics.pptx
Artificial Intelligence (AI) basics.pptx
aditichinar
 
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITYADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ijscai
 
Data Structures_Searching and Sorting.pptx
Data Structures_Searching and Sorting.pptxData Structures_Searching and Sorting.pptx
Data Structures_Searching and Sorting.pptx
RushaliDeshmukh2
 
theory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptxtheory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptx
sanchezvanessa7896
 
Degree_of_Automation.pdf for Instrumentation and industrial specialist
Degree_of_Automation.pdf for  Instrumentation  and industrial specialistDegree_of_Automation.pdf for  Instrumentation  and industrial specialist
Degree_of_Automation.pdf for Instrumentation and industrial specialist
shreyabhosale19
 
Raish Khanji GTU 8th sem Internship Report.pdf
Raish Khanji GTU 8th sem Internship Report.pdfRaish Khanji GTU 8th sem Internship Report.pdf
Raish Khanji GTU 8th sem Internship Report.pdf
RaishKhanji
 
fluke dealers in bangalore..............
fluke dealers in bangalore..............fluke dealers in bangalore..............
fluke dealers in bangalore..............
Haresh Vaswani
 
IntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdfIntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdf
Luiz Carneiro
 
some basics electrical and electronics knowledge
some basics electrical and electronics knowledgesome basics electrical and electronics knowledge
some basics electrical and electronics knowledge
nguyentrungdo88
 
Level 1-Safety.pptx Presentation of Electrical Safety
Level 1-Safety.pptx Presentation of Electrical SafetyLevel 1-Safety.pptx Presentation of Electrical Safety
Level 1-Safety.pptx Presentation of Electrical Safety
JoseAlbertoCariasDel
 
Introduction to Zoomlion Earthmoving.pptx
Introduction to Zoomlion Earthmoving.pptxIntroduction to Zoomlion Earthmoving.pptx
Introduction to Zoomlion Earthmoving.pptx
AS1920
 
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
211421893-M-Tech-CIVIL-Structural-Engineering-pdf.pdf
inmishra17121973
 
Value Stream Mapping Worskshops for Intelligent Continuous Security
Value Stream Mapping Worskshops for Intelligent Continuous SecurityValue Stream Mapping Worskshops for Intelligent Continuous Security
Value Stream Mapping Worskshops for Intelligent Continuous Security
Marc Hornbeek
 
railway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forgingrailway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forging
Javad Kadkhodapour
 
Smart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptxSmart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptx
rushikeshnavghare94
 
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design ThinkingDT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DhruvChotaliya2
 
new ppt artificial intelligence historyyy
new ppt artificial intelligence historyyynew ppt artificial intelligence historyyy
new ppt artificial intelligence historyyy
PianoPianist
 
AI-assisted Software Testing (3-hours tutorial)
AI-assisted Software Testing (3-hours tutorial)AI-assisted Software Testing (3-hours tutorial)
AI-assisted Software Testing (3-hours tutorial)
Vəhid Gəruslu
 
Smart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineeringSmart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineering
rushikeshnavghare94
 
Artificial Intelligence (AI) basics.pptx
Artificial Intelligence (AI) basics.pptxArtificial Intelligence (AI) basics.pptx
Artificial Intelligence (AI) basics.pptx
aditichinar
 
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITYADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ADVXAI IN MALWARE ANALYSIS FRAMEWORK: BALANCING EXPLAINABILITY WITH SECURITY
ijscai
 
Data Structures_Searching and Sorting.pptx
Data Structures_Searching and Sorting.pptxData Structures_Searching and Sorting.pptx
Data Structures_Searching and Sorting.pptx
RushaliDeshmukh2
 
theory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptxtheory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptx
sanchezvanessa7896
 
Degree_of_Automation.pdf for Instrumentation and industrial specialist
Degree_of_Automation.pdf for  Instrumentation  and industrial specialistDegree_of_Automation.pdf for  Instrumentation  and industrial specialist
Degree_of_Automation.pdf for Instrumentation and industrial specialist
shreyabhosale19
 
Raish Khanji GTU 8th sem Internship Report.pdf
Raish Khanji GTU 8th sem Internship Report.pdfRaish Khanji GTU 8th sem Internship Report.pdf
Raish Khanji GTU 8th sem Internship Report.pdf
RaishKhanji
 
fluke dealers in bangalore..............
fluke dealers in bangalore..............fluke dealers in bangalore..............
fluke dealers in bangalore..............
Haresh Vaswani
 

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