Repetitive tasks suck. Let’s be honest here, we hate doing them most of the time. But also we’ll forget things and mess up our day. With code that’s pretty terrible.
Over time, I’ve managed to automate a lot of things. WordPress upgrades itself, including plugins and themes. My code changes are auto-deployed via Git when I update them. I use a cool Coda plugin to automatically generate css from multiple files.
I’ve now added Grunt to the mix. Grunt runs tasks for you so you can be lazy (efficient) but it’s not perfect. One of the things I love about my other tools is I don’t have to think about them. WordPress takes care of itself, git has hooks to know what it’s doing, and Coda monitors my files. I don’t have that for Grunt.
What you have, instead, is ‘grunt watch’ which is great but it only runs while you’ve told it to watch. You have to remember to activate it every time. And I understand why. Watching is a rather expensive (computer wise) tool. But it annoys me because I already struggle to remember that the way a tool I use works, I have to run this to checkout the code: git checkout [tag_name] -b [my_branch_name]
(which for some reason my brain thinks is ‘better’ than scripting a zip).
So why would I use Grunt?
On a site I run, I have a folder filled with scripts that are used on four separate CMS tools. One is WordPress. I’m often adding new scripts and I like to have the scripts separate because that’s easier for me. But that also means I have to edit all my CMS to add in the new JS. But with Grunt I don’t.
Instead, I have one simple Grunt file that says “Any js file in /content/code/js/dev/ should be combined into one mondo js file and then uglified to compress it down.”
Installing Grunt
I didn’t have Node installed, but I do use homebrew, so this was it for me:
brew install node
npm install -g grunt-cli
Okay. Now what?
My First Project
I knew I wanted to make a folder in my /content/ folder for this, so I made /content/project/
. In that folder I ran the command npm init
and answered the questions to generate my package.json
file. That was the easy part actually. After that had to decide what I wanted to do.
- Concatenate (combine) all my JS files into one.
- Compress those files to make them minified.
- Watch the files while I was working on them and automate that.
In order to do that, I need to install three grunt ‘plugins’: concat, uglify, and watch:
npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev
Doing it that way, with the save dev call, automatically adds it to my package.json file so it knows to call that.
Next I had to make a filed called Grunfile.js
and put my actual code in it. I’ve got a copy of the file below but you can break it up into some pretty basic sections. Here’s a sample file:
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
uglify: { ... }
});
grunt.registerTask('default', [] );
};
The first call is to load the npm (node) tasks, which means I’m telling it ‘include these plugins’.
The second call is my init command which runs when the ‘grunt’ command is used. In there I’ll put the name of my json file, where I’ve defined all my dependancies (not all get loaded by the Grunt file you see) and then I’ll make a ‘case’ for my code. That’s what the uglify: { ... }
bit is.
The last is registering the default tasks. At this point, if I want to do anything I would have to type grunt uglify
to do anything, so what you can do instead is make it this:
grunt.registerTask('default', ['uglify'] );
Now when I run ‘grunt’ it will automagically uglify. If you have multiple steps, put them in order of what you want to happen, and off you go.
At this point, I’m now a GruntJS rookie, but I can see why it’s amazing. One can use this in plugin development to make it easier to mush up all your code into something smaller to load.
My Scripts
For those wondering, here are my scripts:
package.json
{
"name": "jfo-content",
"version": "1.0.0",
"description": "JFO Content Scripts",
"main": "Gruntfile.js",
"dependencies": {
"grunt": "^0.4.5",
"grunt-contrib-uglify": "^0.7.0"
},
"devDependencies": {
"grunt-contrib-concat": "^0.5.0",
"grunt-contrib-uglify": "^0.7.0",
"grunt-contrib-watch": "^0.6.1"
},
"author": "Mika Epstein <ipstenu@halfelf.org>",
"license": "WTF"
}
Gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
dist: {
src: '../code/js/dev/*.js',
dest: '../code/js/content.js',
},
},
uglify: {
build: {
src: '../code/js/content.js',
dest: '../code/js/content.min.js',
}
},
watch: {
scripts: {
files: '../code/js/dev/*.js',
tasks: ['concat', 'uglify'],
}
}
});
grunt.registerTask('default', ['concat','uglify'] );
};
post_update (git)
I added in a line here to delete the package folder from my live site. That’s where all my Grunt stuff lives and I really don’t need it. It also nukes the dev folder. Sometimes. In some cases I leave that alone, since it’s not like I’m worried about space.
#!/bin/sh
export GIT_WORK_TREE=~/public_html/content/
git checkout -f master
rm -rf ~/public_html/content/package/
rm -rf ~/public_html/content/code/js/dev/