-
-
Save marcamos/7436015 to your computer and use it in GitHub Desktop.
module.exports = function(grunt) { | |
"use strict"; | |
// ------------------------------------------------------------------------- | |
// #### Load plugins as needed #### | |
// Using a 'just in time' approach -- meaning: only load plugins when they | |
// are needed -- this will automatically find, then load, any and all | |
// plugins that are needed by the task currently being executed. It will | |
// scan the devDependencies object, in package.json, and match any of the | |
// following patterns automatically: | |
// - grunt-contrib-[task name] | |
// - grunt-[task name] | |
// - [task name] | |
// For any [task name] / [plugin name] pair that can't be found by the above | |
// patterns, you can find them manually by adding them as a second argument | |
// using the following pattern: | |
// - task name: 'plugin name' | |
// We've done that, below, with "replace: 'grunt-text-replace'", as it does | |
// not conform to the three patterns that are matched automatically. | |
// https://github.com/shootaroo/jit-grunt | |
// ------------------------------------------------------------------------- | |
require('jit-grunt')(grunt, { | |
replace: 'grunt-text-replace' | |
}); | |
// ------------------------------------------------------------------------- | |
// #### Display task execution time #### | |
// This module will display elapsed execution time of grunt tasks when they | |
// are finished, right on the command line. It's useful for keeping tabs on | |
// the performance of your Grunt configuration. | |
// https://github.com/sindresorhus/time-grunt | |
// ------------------------------------------------------------------------- | |
require('time-grunt')(grunt); | |
// ------------------------------------------------------------------------- | |
// #### Project configuration #### | |
// ------------------------------------------------------------------------- | |
grunt.initConfig({ | |
// --------------------------------------------------------------------- | |
// #### Variables #### | |
// --------------------------------------------------------------------- | |
sassPath: 'html/lib/sass/', | |
cssPath: 'html/lib/css/', | |
jsPath: 'html/lib/js/', | |
imgPath: 'html/lib/img/', | |
sassFileTypes: 'sass,scss', | |
imgFileTypes: 'gif,jpg,jpeg,png', | |
docFileTypes: '.html,.php', | |
// --------------------------------------------------------------------- | |
// #### Get data from package.json #### | |
// Get data from the package.json file and assign it to a pkg variable. | |
// --------------------------------------------------------------------- | |
pkg: grunt.file.readJSON('package.json'), | |
// --------------------------------------------------------------------- | |
// #### Build a reusable banner #### | |
// A banner variable that can be utilized later via "<%= banner %>". | |
// Property names (defined over in package.json) which are preceded by | |
// "pkg." will output the corresponding values for those properties. | |
// --------------------------------------------------------------------- | |
banner: '/*!\n' + | |
' * <%= pkg.name %> | version: <%= pkg.version %> | updated: <%= grunt.template.today("yyyy-mm-dd @ h:MM:ss TT") %>\n' + | |
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.owner.name %>\n' + | |
' */\n', | |
// --------------------------------------------------------------------- | |
// #### Task configuration #### | |
// --------------------------------------------------------------------- | |
// Task: Sass compiling. | |
// https://github.com/gruntjs/grunt-contrib-sass | |
sass: { | |
// Task-wide options. | |
options: {}, | |
// Target: development. | |
dev: { | |
// Target-specific options. | |
options: { | |
style: "expanded", | |
lineNumbers: true | |
}, | |
// Source file(s), in the order we want to compile them. | |
src: '<%= sassPath %>all.{<%= sassFileTypes %>}', | |
// Destination file. | |
dest: '<%= cssPath %>all.css' | |
}, | |
// Target: production. | |
prod: { | |
// Target-specific options. | |
options: { | |
style: "compressed", | |
// Utilize the banner variable defined above. | |
banner: '<%= banner %>' | |
}, | |
// Source file(s), in the order we want to compile them. | |
src: '<%= sassPath %>all.{<%= sassFileTypes %>}', | |
// Destination file. | |
dest: '<%= cssPath %>all.min.css' | |
} | |
}, | |
// Task: JavaScript hinting. | |
// https://github.com/gruntjs/grunt-contrib-jshint | |
jshint: { | |
// Task-wide options. | |
options: {}, | |
// Target: all. | |
all: { | |
// Target-specific options. | |
options: {}, | |
// Source file(s), in the order we want to hint them. | |
src: [ | |
'Gruntfile.js', | |
'<%= jsPath %>*.js', | |
'!<%= jsPath %>*.min.js', // ...but not minified files. | |
'!<%= jsPath %>all.js' // ...but not the concatenated file. | |
] | |
} | |
}, | |
// Task: JavaScript file concatenation. | |
// https://github.com/gruntjs/grunt-contrib-concat | |
concat: { | |
// Task-wide options. | |
options: {}, | |
// Target: all. | |
all: { | |
// Target-specific options. | |
options: { | |
// Separate each concatenated script with a semicolon. | |
separator: ';' | |
}, | |
// Source file(s), in the order we want to concatenate them. | |
src: [ | |
'<%= jsPath %>vendor/jquery-1.10.2.js', | |
'<%= jsPath %>plugins/*.js', | |
'<%= jsPath %>*.js', | |
'!<%= jsPath %>*.min.js', // ...but not minified files. | |
'!<%= jsPath %>all.js' // ...but not the concatenated file. | |
], | |
// Destination file. | |
dest: '<%= jsPath %>all.js', | |
// Warn if a given file is missing or invalid. | |
nonull: true | |
} | |
}, | |
// Task: JavaScript minification. | |
// https://github.com/gruntjs/grunt-contrib-uglify | |
uglify: { | |
// Task-wide options. | |
options: {}, | |
// Target: all. | |
all: { | |
// Target-specific options. | |
options: { | |
// Utilize the banner variable defined above. | |
banner: '<%= banner %>', | |
// Report the original vs. minified file-size. | |
report: 'min' | |
}, | |
// Source file(s), in the order we want to minify them. | |
src: '<%= jsPath %>all.js', | |
// Destination file. | |
dest: '<%= jsPath %>all.min.js' | |
} | |
}, | |
// Task: image minification. | |
// https://github.com/gruntjs/grunt-contrib-imagemin | |
imagemin: { | |
// Task-wide options. | |
options: {}, | |
// Target: all. | |
all: { | |
// Target-specific options. | |
options: {}, | |
// Files to minify. | |
files: [{ | |
// Allow for a dynamically-built file-list. | |
expand: true, | |
// The directory to start this task within. | |
cwd: '<%= imgPath %>', | |
// Relative to the path defined in "cwd", the sub- | |
// directories and file type(s) to work on. | |
src: ['**/*.{<%= imgFileTypes %>}'], | |
// Destination location. | |
dest: '<%= imgPath %>' | |
}] | |
} | |
}, | |
// Task: find and replace things in the project. | |
// https://github.com/yoniholmes/grunt-text-replace | |
replace: { | |
// Target: remove the Livereload script. | |
removeLivereload: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: The full script element needed for Livereload to | |
// function, plus a line-return. | |
from: /<script src="\/\/localhost:35729\/livereload\.js"><\/script>\n/m, | |
// To: Nothing. | |
to: '' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: add the Livereload script. | |
addLivereload: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: a closing body tag. | |
from: /<\/body>/, | |
// To: The full script element needed for Livereload to | |
// function, a line-return, and the closing body tag. | |
to: '<script src="//localhost:35729/livereload.js"></script>\n</body>' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: replace production-specific css file name with | |
// development-specific css file name. | |
devCssFile: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: "all.min.css". | |
from: /all\.min\.css/, | |
// To: "all.css". | |
to: 'all.css' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: replace development-specific css file name with | |
// production-specific css file name. | |
prodCssFile: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: "all.css". | |
from: /all\.css/, | |
// To: "all.min.css". | |
to: 'all.min.css' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: replace production-specific js file name with | |
// development-specific js file name. | |
devJsFile: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: "all.min.js". | |
from: /all\.min\.js/, | |
// To: "all.js". | |
to: 'all.js' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: replace development-specific js file name with | |
// production-specific js file name. | |
prodJsFile: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: "all.js". | |
from: /all\.js/, | |
// To: "all.min.js". | |
to: 'all.min.js' | |
}], | |
// Overwrite each file. | |
overwrite: true | |
}, | |
// Target: bust cache. This target assumes "?cb=[any number here]" | |
// has been appended to any file-path you'd like to cache-bust. For | |
// example, css files, js files, image files, etc.: | |
// <link rel="stylesheet" href="/lib/css/all.css?cb=1" /> | |
// <script src="/lib/js/all.min.js?cb=1"></script> | |
// <img src="/lib/img/photo.jpg?cb=1" alt="A photo" /> | |
cacheBust: { | |
// Scan all document file types inside of html/. | |
src: 'html/**/*{<%= docFileTypes %>}', | |
// Replacement(s) to make. | |
replacements: [{ | |
// From: "?cb=[any number here]". | |
from: /\?cb=[0-9]*/g, | |
// To: "?cb=[the current Unix epoch time]" | |
to: function() { | |
var uid = new Date().valueOf(); | |
return '?cb=' + uid; | |
} | |
}], | |
// Overwrite each file. | |
overwrite: true | |
} | |
}, | |
// Task: aside from creating notifications when something fails (which | |
// is this task's permanent/default behavior), also create the following | |
// notifications for these custom, non-failure events. The targets below | |
// are utilized by requesting them via targets found in the watch task. | |
// https://github.com/dylang/grunt-notify | |
notify: { | |
// Target: Sass. | |
sass: { | |
options: { | |
title: 'CSS', | |
message: 'Compiled successfully.' | |
} | |
}, | |
// Target: JavaScript. | |
js: { | |
options: { | |
title: 'JavaScript', | |
message: 'Hinted and concatenated successfully.' | |
} | |
}, | |
// Target: ready for production. | |
goProd: { | |
options: { | |
title: 'Ready for Production', | |
message: 'Ship it!' | |
} | |
}, | |
// Target: ready for development. | |
goDev: { | |
options: { | |
title: 'Ready for Development', | |
message: 'Make it better!' | |
} | |
} | |
}, | |
// Task: when something changes, run specific task(s). | |
// https://github.com/gruntjs/grunt-contrib-watch | |
watch: { | |
// Task-wide options. | |
options: {}, | |
// Target: Sass. | |
sass: { | |
// Target-specific options. | |
options: {}, | |
// Watch all sassFileTypes files inside of sassPath. | |
files: '<%= sassPath %>**/*.{<%= sassFileTypes %>}', | |
// Task(s) to run. | |
tasks: [ | |
'newer:sass:dev', | |
'notify:sass' | |
] | |
}, | |
// Target: css. | |
css: { | |
// Target-specific options. | |
options: { | |
// Utilize Livereload. | |
livereload: true | |
}, | |
// Watch all .css files inside of cssPath. | |
files: '<%= cssPath %>*.css' | |
}, | |
// Target: JavaScript. | |
js: { | |
// Target-specific options. | |
options: { | |
// Utilize Livereload. | |
livereload: true | |
}, | |
// Watch all JavaScript files inside of jsPath. | |
files: [ | |
'<%= jsPath %>*.js', | |
'!<%= jsPath %>*.min.js', // ...but not minified files. | |
'!<%= jsPath %>all.js' // ...but not the concatenated file. | |
], | |
// Task(s) to run. | |
tasks: [ | |
'newer:jshint:all', | |
'concat:all', | |
'notify:js' | |
] | |
}, | |
// Target: documents. | |
docs: { | |
// Target-specific options. | |
options: { | |
// Utilize Livereload. | |
livereload: true | |
}, | |
// Watch all docFileTypes inside of html/. | |
files: 'html/**/*{<%= docFileTypes %>}' | |
} | |
} | |
}); | |
// ------------------------------------------------------------------------- | |
// #### Define task aliases that run multiple tasks #### | |
// On the command-line, type "grunt [alias]" without the square brackets or | |
// double-quotes, and press return. The format is as follows: | |
// grunt.registerTask('alias', [ | |
// 'task1', | |
// 'task2:targetName', | |
// 'task3' | |
// ]); | |
// ------------------------------------------------------------------------- | |
// #### Task alias: "default" #### | |
// Task(s) to run when typing only "grunt" in the console. | |
grunt.registerTask('default', [ | |
'watch' | |
]); | |
// #### Task alias: "goProd" #### | |
// Tasks(s) to run when it's time to convert things from development mode to | |
// production mode. | |
grunt.registerTask('goProd', [ | |
'sass:prod', | |
'jshint:all', | |
'concat:all', | |
'uglify:all', | |
'imagemin:all', | |
'replace:removeLivereload', | |
'replace:prodCssFile', | |
'replace:prodJsFile', | |
'replace:cacheBust', | |
'notify:goProd' | |
]); | |
// #### Task alias: "goDev" #### | |
// Tasks(s) to run when it's time to convert things from production mode to | |
// development mode. | |
grunt.registerTask('goDev', [ | |
'replace:addLivereload', | |
'replace:devCssFile', | |
'replace:devJsFile', | |
'notify:goDev' | |
]); | |
}; |
{ | |
"name": "xyz-inc-marketing", | |
"description": "A marketing website for XYZ, Inc.", | |
"version": "0.1.0", | |
"author": { | |
"name": "The Outfit, Inc.", | |
"url": "http://fromtheoutfit.com", | |
"email": "[email protected]" | |
}, | |
"owner": { | |
"name": "XYZ, Inc.", | |
"url": "http://xyzinc.com", | |
"email": "[email protected]" | |
}, | |
"devDependencies": { | |
"grunt": "~0.4.1", | |
"grunt-contrib-concat": "~0.3.0", | |
"grunt-contrib-jshint": "~0.6.5", | |
"grunt-contrib-sass": "~0.5.1", | |
"grunt-contrib-uglify": "~0.2.7", | |
"grunt-contrib-watch": "~0.5.3", | |
"grunt-contrib-imagemin": "~0.3.0", | |
"grunt-text-replace": "~0.3.11", | |
"grunt-notify": "~0.2.17", | |
"grunt-newer": "~0.6.1", | |
"jit-grunt": "~0.2.1", | |
"time-grunt": "~0.2.9" | |
} | |
} |
Ditto. Never knew about the notify plugin. Definitely plan on stealing some of this.
Thanks gentlemen! Nothing worth changing/adding/removing struck you as you looked it over?
So I just found this today for loading all the grunt tasks instead of using matchdep. Testing on scrollNav and it's pretty nice https://github.com/sindresorhus/load-grunt-tasks
The only weird part to me is that you're naming the concatenated file .min
even though it hasn't been minified until uglify runs. Since you'll most likely be running these together it's not a big thing, just something that stood out to me.
So I just found this today for loading all the grunt tasks instead of using matchdep. Testing on scrollNav and it's pretty nice https://github.com/sindresorhus/load-grunt-tasks
Cool; the lazy person in me wonders what the advantage is?
The only weird part to me is that you're naming the concatenated file .min even though it hasn't been minified until uglify runs. Since you'll most likely be running these together it's not a big thing, just something that stood out to me.
Yeah, I agree(d). In the past hour or so, I've changed things around quite a bit. With regard to this thing you've pointed out, I ended up mimicking a pattern/process already used by the Sass task, where a dev-only JS file is maintained along-side a production-only JS file, then the replace
task is used to swap file-names whenever necessary.
Minimal difference, just allows you to specify grunt-
vs grunt-contrib-
tasks which you can obviously do with matchdep.
Ah, cool, thanks.
Didn't know about the notify plugin either. Makes watching all your files that much more worthwhile, if you're hinting/linting on a watch task. Are you not using compass with sass?
Are you not using compass with sass?
More often than not, yes. The test project that's associated with—but not visible here—this gruntfile doesn't use Compass, hence its absence above.
I just started using Grunt today and my Gruntfile.js looks sad compared to this. But there's so much great stuff here, I can't wait to put some of it to use. Thanks for putting this together!
You're very welcome! I hope it helps, and if you (or anybody) finds issues, please let me know. This is my first one, so it's far from perfect.
I switched things up a bit. The imagemin
task no longer runs automatically via the watch
task; I felt that was overkill. Instead, it now runs via the goProd
task alias because -- in my opinion -- image minification is not really needed until the project is ready for production.
This is pretty awesome, Marc. Lots of great stuff in here.
Being preferential to SCSS syntax myself, I've one small suggestion: to make it a bit more flexible, how about adding a sassFileTypes
variable and setting it to .sass,.scss
, and replacing all the instances of *.sass
In the tasks with *.{<%= sassFileTypes %>}
?
Not a bad idea! I'll do that very very soon, thanks @kimili.
@kimili: I've updated the Gruntfile to incorporate your suggestion. The only adjustment that was needed was the removal of the dot preceding each file extension. Thanks again for making that suggestion!
Awesome, Marc, thanks for making that tweak!
I've made a few adjustments. One of them introduces reporting on Grunt's execution time(s), and two of them aim to improve Grunt's performance in general:
- Three plugins added to package.json.
- Added time-grunt to provide task-execution-time reporting in the console.
- Swapped matchdep for jit-grunt, which will load plugins as they're needed by the tasks that require them, rather than load all of the plugins every time Grunt is run.
- Added grunt-newer -- here & here -- which ensures that Grunt only processes files that have changed since the last run, rather than process each and every file regardless of modification date.
@marcamos wow! This is really strong; love it. Especially digging the NOT operator used on line 351 (and elsewhere), that's something I wasn't aware could be done. Also, nice way to use variables for the file globbing patterns (
imgFileTypes
, etc.). At first I was like “why not use a real array?”, but then I saw how you used that.As for
grunt-notify
, that's awesome. I'm so hacking my own Gruntfile soon to take advantage of some of this.