{"rowid": 18, "title": "Grunt for People Who Think Things Like Grunt are Weird and Hard", "contents": "Front-end developers are often told to do certain things:\n\n\n\tWork in as small chunks of CSS and JavaScript as makes sense to you, then concatenate them together for the production website.\n\tCompress your CSS and minify your JavaScript to make their file sizes as small as possible for your production website.\n\tOptimize your images to reduce their file size without affecting quality.\n\tUse Sass for CSS authoring because of all the useful abstraction it allows.\n\n\nThat\u2019s not a comprehensive list of course, but those are the kind of things we need to do. You might call them tasks.\n\nI bet you\u2019ve heard of Grunt. Well, Grunt is a task runner. Grunt can do all of those things for you. Once you\u2019ve got it set up, which isn\u2019t particularly difficult, those things can happen automatically without you having to think about them again.\n\nBut let\u2019s face it: Grunt is one of those fancy newfangled things that all the cool kids seem to be using but at first glance feels strange and intimidating. I hear you. This article is for you.\n\nLet\u2019s nip some misconceptions in the bud right away\n\nPerhaps you\u2019ve heard of Grunt, but haven\u2019t done anything with it. I\u2019m sure that applies to many of you. Maybe one of the following hang-ups applies to you.\n\nI don\u2019t need the things Grunt does\n\nYou probably do, actually. Check out that list up top. Those things aren\u2019t nice-to-haves. They are pretty vital parts of website development these days. If you already do all of them, that\u2019s awesome. Perhaps you use a variety of different tools to accomplish them. Grunt can help bring them under one roof, so to speak. If you don\u2019t already do all of them, you probably should and Grunt can help. Then, once you are doing those, you can keep using Grunt to do more for you, which will basically make you better at doing your job.\n\nGrunt runs on Node.js \u2014 I don\u2019t know Node\n\nYou don\u2019t have to know Node. Just like you don\u2019t have to know Ruby to use Sass. Or PHP to use WordPress. Or C++ to use Microsoft Word.\n\nI have other ways to do the things Grunt could do for me\n\nAre they all organized in one place, configured to run automatically when needed, and shared among every single person working on that project? Unlikely, I\u2019d venture.\n\nGrunt is a command line tool \u2014 I\u2019m just a designer\n\nI\u2019m a designer too. I prefer native apps with graphical interfaces when I can get them. But I don\u2019t think that\u2019s going to happen with Grunt1.\n\nThe extent to which you need to use the command line is:\n\n\n\tNavigate to your project\u2019s directory.\n\tType grunt and press Return.\n\n\nAfter set-up, that is, which again isn\u2019t particularly difficult.\n\nOK. Let\u2019s get Grunt installed\n\nNode is indeed a prerequisite for Grunt. If you don\u2019t have Node installed, don\u2019t worry, it\u2019s very easy. You literally download an installer and run it. Click the big Install button on the Node website.\n\nYou install Grunt on a per-project basis. Go to your project\u2019s folder. It needs a file there named package.json at the root level. You can just create one and put it there.\n\n package.json at root\n\nThe contents of that file should be this:\n\n{\n \"name\": \"example-project\",\n \"version\": \"0.1.0\",\n \"devDependencies\": {\n \"grunt\": \"~0.4.1\"\n }\n}\n\nFeel free to change the name of the project and the version, but the devDependencies thing needs to be in there just like that.\n\nThis is how Node does dependencies. Node has a package manager called NPM (Node packaged modules) for managing Node dependencies (like a gem for Ruby if you\u2019re familiar with that). You could even think of it a bit like a plug-in for WordPress.\n\nOnce that package.json file is in place, go to the terminal and navigate to your folder. Terminal rubes like me do it like this:\n\n Terminal rube changing directories\n\nThen run the command:\n\nnpm install\n\nAfter you\u2019ve run that command, a new folder called node_modules will show up in your project.\n\n Example of node_modules folder\n\nThe other files you see there, README.md and LICENSE are there because I\u2019m going to put this project on GitHub and that\u2019s just standard fare there.\n\nThe last installation step is to install the Grunt CLI (command line interface). That\u2019s what makes the grunt command in the terminal work. Without it, typing grunt will net you a \u201cCommand Not Found\u201d-style error. It is a separate installation for efficiency reasons. Otherwise, if you had ten projects you\u2019d have ten copies of Grunt CLI.\n\nThis is a one-liner again. Just run this command in the terminal:\n\nnpm install -g grunt-cli\n\nYou should close and reopen the terminal as well. That\u2019s a generic good practice to make sure things are working right. Kinda like restarting your computer after you install a new application was in the olden days.\n\nLet\u2019s make Grunt concatenate some files\n\nPerhaps in our project there are three separate JavaScript files:\n\n\n\tjquery.js \u2013 The library we are using.\n\tcarousel.js \u2013 A jQuery plug-in we are using.\n\tglobal.js \u2013 Our authored JavaScript file where we configure and call the plug-in.\n\n\nIn production, we would concatenate all those files together for performance reasons (one request is better than three). We need to tell Grunt to do this for us.\n\nBut wait. Grunt actually doesn\u2019t do anything all by itself. Remember Grunt is a task runner. The tasks themselves we will need to add. We actually haven\u2019t set up Grunt to do anything yet, so let\u2019s do that.\n\nThe official Grunt plug-in for concatenating files is grunt-contrib-concat. You can read about it on GitHub if you want, but all you have to do to use it on your project is to run this command from the terminal (it will henceforth go without saying that you need to run the given commands from your project\u2019s root folder):\n\nnpm install grunt-contrib-concat --save-dev\n\nA neat thing about doing it this way: your package.json file will automatically be updated to include this new dependency. Open it up and check it out. You\u2019ll see a new line:\n\n\"grunt-contrib-concat\": \"~0.3.0\"\n\nNow we\u2019re ready to use it. To use it we need to start configuring Grunt and telling it what to do.\n\nYou tell Grunt what to do via a configuration file named Gruntfile.js2\n\nJust like our package.json file, our Gruntfile.js has a very special format that must be just right. I wouldn\u2019t worry about what every word of this means. Just check out the format:\n\nmodule.exports = function(grunt) {\n\n // 1. All configuration goes here \n grunt.initConfig({\n pkg: grunt.file.readJSON('package.json'),\n\n concat: {\n // 2. Configuration for concatinating files goes here.\n }\n\n });\n\n // 3. Where we tell Grunt we plan to use this plug-in.\n grunt.loadNpmTasks('grunt-contrib-concat');\n\n // 4. Where we tell Grunt what to do when we type \"grunt\" into the terminal.\n grunt.registerTask('default', ['concat']);\n\n};\n\nNow we need to create that configuration. The documentation can be overwhelming. Let\u2019s focus just on the very simple usage example.\n\nRemember, we have three JavaScript files we\u2019re trying to concatenate. We\u2019ll list file paths to them under src in an array of file paths (as quoted strings) and then we\u2019ll list a destination file as dest. The destination file doesn\u2019t have to exist yet. It will be created when this task runs and squishes all the files together.\n\nBoth our jquery.js and carousel.js files are libraries. We most likely won\u2019t be touching them. So, for organization, we\u2019ll keep them in a /js/libs/ folder. Our global.js file is where we write our own code, so that will be right in the /js/ folder. Now let\u2019s tell Grunt to find all those files and squish them together into a single file named production.js, named that way to indicate it is for use on our real live website.\n\nconcat: { \n dist: {\n src: [\n 'js/libs/*.js', // All JS in the libs folder\n 'js/global.js' // This specific file\n ],\n dest: 'js/build/production.js',\n }\n}\n\nNote: throughout this article there will be little chunks of configuration code like above. The intention is to focus in on the important bits, but it can be confusing at first to see how a particular chunk fits into the larger file. If you ever get confused and need more context, refer to the complete file.\n\nWith that concat configuration in place, head over to the terminal, run the command:\n\ngrunt\n\nand watch it happen! production.js will be created and will be a perfect concatenation of our three files. This was a big aha! moment for me. Feel the power course through your veins. Let\u2019s do more things!\n\nLet\u2019s make Grunt minify that JavaScript\n\nWe have so much prep work done now, adding new tasks for Grunt to run is relatively easy. We just need to:\n\n\n\tFind a Grunt plug-in to do what we want\n\tLearn the configuration style of that plug-in\n\tWrite that configuration to work with our project\n\n\nThe official plug-in for minifying code is grunt-contrib-uglify. Just like we did last time, we just run an NPM command to install it:\n\nnpm install grunt-contrib-uglify --save-dev\n\nThen we alter our Gruntfile.js to load the plug-in:\n\ngrunt.loadNpmTasks('grunt-contrib-uglify');\n\nThen we configure it:\n\nuglify: {\n build: {\n src: 'js/build/production.js',\n dest: 'js/build/production.min.js'\n }\n}\n\nLet\u2019s update that default task to also run minification:\n\ngrunt.registerTask('default', ['concat', 'uglify']);\n\nSuper-similar to the concatenation set-up, right?\n\nRun grunt at the terminal and you\u2019ll get some deliciously minified JavaScript:\n\n Minified JavaScript\n\nThat production.min.js file is what we would load up for use in our index.html file.\n\nLet\u2019s make Grunt optimize our images\n\nWe\u2019ve got this down pat now. Let\u2019s just go through the motions. The official image minification plug-in for Grunt is grunt-contrib-imagemin. Install it:\n\nnpm install grunt-contrib-imagemin --save-dev\n\nRegister it in the Gruntfile.js:\n\ngrunt.loadNpmTasks('grunt-contrib-imagemin');\n\nConfigure it:\n\nimagemin: {\n dynamic: {\n files: [{\n expand: true,\n cwd: 'images/',\n src: ['**/*.{png,jpg,gif}'],\n dest: 'images/build/'\n }]\n }\n}\n\nMake sure it runs:\n\ngrunt.registerTask('default', ['concat', 'uglify', 'imagemin']);\n\nRun grunt and watch that gorgeous squishification happen:\n\n Squished images\n\nGotta love performance increases for nearly zero effort.\n\nLet\u2019s get a little bit smarter and automate\n\nWhat we\u2019ve done so far is awesome and incredibly useful. But there are a couple of things we can get smarter on and make things easier on ourselves, as well as Grunt:\n\n\n\tRun these tasks automatically when they should\n\tRun only the tasks needed at the time\n\n\nFor instance:\n\n\n\tConcatenate and minify JavaScript when JavaScript changes\n\tOptimize images when a new image is added or an existing one changes\n\n\nWe can do this by watching files. We can tell Grunt to keep an eye out for changes to specific places and, when changes happen in those places, run specific tasks. Watching happens through the official grunt-contrib-watch plugin.\n\nI\u2019ll let you install it. It is exactly the same process as the last few plug-ins we installed. We configure it by giving watch specific files (or folders, or both) to watch. By watch, I mean monitor for file changes, file deletions or file additions. Then we tell it what tasks we want to run when it detects a change.\n\nWe want to run our concatenation and minification when anything in the /js/ folder changes. When it does, we should run the JavaScript-related tasks. And when things happen elsewhere, we should not run the JavaScript-related tasks, because that would be irrelevant. So:\n\nwatch: {\n scripts: {\n files: ['js/*.js'],\n tasks: ['concat', 'uglify'],\n options: {\n spawn: false,\n },\n } \n}\n\nFeels pretty comfortable at this point, hey? The only weird bit there is the spawn thing. And you know what? I don\u2019t even really know what that does. From what I understand from the documentation it is the smart default. That\u2019s real-world development. Just leave it alone if it\u2019s working and if it\u2019s not, learn more.\n\nNote: Isn\u2019t it frustrating when something that looks so easy in a tutorial doesn\u2019t seem to work for you? If you can\u2019t get Grunt to run after making a change, it\u2019s very likely to be a syntax error in your Gruntfile.js. That might look like this in the terminal:\n\n Errors running Grunt\n\nUsually Grunt is pretty good about letting you know what happened, so be sure to read the error message. In this case, a syntax error in the form of a missing comma foiled me. Adding the comma allowed it to run.\n\nLet\u2019s make Grunt do our preprocessing\n\nThe last thing on our list from the top of the article is using Sass \u2014 yet another task Grunt is well-suited to run for us. But wait? Isn\u2019t Sass technically in Ruby? Indeed it is. There is a version of Sass that will run in Node and thus not add an additional dependency to our project, but it\u2019s not quite up-to-snuff with the main Ruby project. So, we\u2019ll use the official grunt-contrib-sass plug-in which just assumes you have Sass installed on your machine. If you don\u2019t, follow the command line instructions.\n\nWhat\u2019s neat about Sass is that it can do concatenation and minification all by itself. So for our little project we can just have it compile our main global.scss file:\n\nsass: {\n dist: {\n options: {\n style: 'compressed'\n },\n files: {\n 'css/build/global.css': 'css/global.scss'\n }\n } \n}\n\nWe wouldn\u2019t want to manually run this task. We already have the watch plug-in installed, so let\u2019s use it! Within the watch configuration, we\u2019ll add another subtask:\n\ncss: {\n files: ['css/*.scss'],\n tasks: ['sass'],\n options: {\n spawn: false,\n }\n}\n\nThat\u2019ll do it. Now, every time we change any of our Sass files, the CSS will automaticaly be updated.\n\nLet\u2019s take this one step further (it\u2019s absolutely worth it) and add LiveReload. With LiveReload, you won\u2019t have to go back to your browser and refresh the page. Page refreshes happen automatically and in the case of CSS, new styles are injected without a page refresh (handy for heavily state-based websites).\n\nIt\u2019s very easy to set up, since the LiveReload ability is built into the watch plug-in. We just need to:\n\n\nInstall the browser plug-in\nAdd to the top of the watch configuration:\n. watch: {\n options: {\n livereload: true,\n },\n scripts: { \n /* etc */\n\nRestart the browser and click the LiveReload icon to activate it.\nUpdate some Sass and watch it change the page automatically.\n\n\n Live reloading browser\n\nYum.\n\nPrefer a video?\n\nIf you\u2019re the type that likes to learn by watching, I\u2019ve made a screencast to accompany this article that I\u2019ve published over on CSS-Tricks: First Moments with Grunt\n\nLeveling up\n\nAs you might imagine, there is a lot of leveling up you can do with your build process. It surely could be a full time job in some organizations.\n\nSome hardcore devops nerds might scoff at the simplistic setup we have going here. But I\u2019d advise them to slow their roll. Even what we have done so far is tremendously valuable. And don\u2019t forget this is all free and open source, which is amazing.\n\nYou might level up by adding more useful tasks:\n\n\n\tRunning your CSS through Autoprefixer (A+ Would recommend) instead of a preprocessor add-ons.\n\tWriting and running JavaScript unit tests (example: Jasmine).\n\tBuild your image sprites and SVG icons automatically (example: Grunticon).\n\tStart a server, so you can link to assets with proper file paths and use services that require a real URL like TypeKit and such, as well as remove the need for other tools that do this, like MAMP.\n\tCheck for code problems with HTML-Inspector, CSS Lint, or JS Hint.\n\tHave new CSS be automatically injected into the browser when it ever changes.\n\tHelp you commit or push to a version control repository like GitHub.\n\tAdd version numbers to your assets (cache busting).\n\tHelp you deploy to a staging or production environment (example: DPLOY).\n\n\nYou might level up by simply understanding more about Grunt itself:\n\n\n\tRead Grunt Boilerplate by Mark McDonnell.\n\tRead Grunt Tips and Tricks by Nicolas Bevacqua.\n\tOrganize your Gruntfile.js by splitting it up into smaller files.\n\tCheck out other people\u2019s and projects\u2019 Gruntfile.js.\n\tLearn more about Grunt by digging into its source and learning about its API.\n\n\nLet\u2019s share\n\nI think some group sharing would be a nice way to wrap this up. If you are installing Grunt for the first time (or remember doing that), be especially mindful of little frustrating things you experience(d) but work(ed) through. Those are the things we should share in the comments here. That way we have this safe place and useful resource for working through those confusing moments without the embarrassment. We\u2019re all in this thing together!\n\n \n\n1 Maybe someday someone will make a beautiful Grunt app for your operating system of choice. But I\u2019m not sure that day will come. The configuration of the plug-ins is the important part of using Grunt. Each plug-in is a bit different, depending on what it does. That means a uniquely considered UI for every single plug-in, which is a long shot.\n\nPerhaps a decent middleground is this Grunt DevTools Chrome add-on.\n\n2 Gruntfile.js is often referred to as Gruntfile in documentation and examples. Don\u2019t literally name it Gruntfile \u2014 it won\u2019t work.", "year": "2013", "author": "Chris Coyier", "author_slug": "chriscoyier", "published": "2013-12-11T00:00:00+00:00", "url": "https://24ways.org/2013/grunt-is-not-weird-and-hard/", "topic": "code"}