Chapter 9. Task Runners

For many frontend developers, Sass was our first build system, or task runner. Before Sass, our workflow was to hit Save on our CSS file and then refresh the browser. With Sass, we took our first step on the journey toward automation nirvana. Some might say that we just added one more terminal command to our workflow, but for those first discovering Sass, that small task was well worth the effort.

When we discovered Compass, we not only found a trove of CSS3 mixins that saved us from visiting CSS3 Generator dozens of times a day, but we also learned about the power of a single configuration file for each project. With Compass, we could create a config.rb file that described the paths to important folders, specified settings for development versus production, and toggled important troubleshooting tools. This single file could be saved and shared with the rest of the team, making onboarding new developers much easier.

As the Sass community continued to grow, a large array of Compass plug-ins began to emerge, providing everything from advanced color functions to grid systems to modular scale to media query management. These tools, all distributed as gems, could be imported directly into any project with Compass.

Compass also allowed module developers to tap into the power of Ruby, accessing local files, performing Ruby functions, compiling Ruby templates, and passing the data back to the Sass file. This allowed people to write modules that used advanced math not possible in Sass alone. Compass also tapped into this functionality, allowing users to automatically generate sprite maps from folders of PNG files, or to query image files and return height and width values.

So now, with a single compass compile command, a developer could compile all of their Sass into a target CSS directory, while each image URL is resolved to the image folder, and font references resolved to the font folder. The Sass could be compiled using popular frameworks like Susy grids, Modular Scale, Breakpoint, and Color Schemer. It could do all of this while creating several custom image sprites and querying dozens of JPEG files to use their dimensions to set height and width properties in the CSS.

Compass was not only our first exposure to the world of build systems, but it was also the primary reason that many of us resisted moving over to a new breed of Node.js task runners that were quickly growing in popularity. After a quick introduction to tools like Grunt and Gulp, most of us (myself included) were quick to reply “Oh, just like Compass, but more complicated.” For a developer whose entire build process had been managed by Compass, setting up Grunt just to compile Sass felt like a completely unnecessary step. But once we got Gulp or Grunt set up to compile Sass, we started to turn our eyes to the host of other things we could do and quickly realized that our build processes would never be the same.

Going All In with Task Runners

Embracing Node.js-based task runners had a bit of a domino effect in my workflow. As a Grunt user, not only did I now have 13,000+ modules at my disposal, but these modules provided functionality in every possible area of my workflow, not just Sass and CSS. Here is a list of some of the things I now use a task runner to do:

  • Install required Ruby gems and Bower packages
  • Clear out temp folders
  • Create sym links
  • Compile Sass
  • Concatenate JavaScript
  • Load third-party JavaScript libraries
  • Compile Icon-font from SVGs
  • Process images, reducing file size, cropping to multiple sizes
  • Rsync files to remove server
  • Create Git tags
  • Run visual regression tests
  • Compile the style guide
  • Autoprefix vendor prefixes
  • Compile component library
  • Lint my Sass, CSS, JavaScript, JSON, and so on
  • Validate data based on a JSON schema
  • Spin up node and PHP servers
  • Live reload browsers when files change

The task runner empowers frontend architects to create a blueprint for the site’s creation. Each of the four pillars is wrapped in automation, because it is through automation that we can not only codify our process, but also enforce it. Linting helps to describe and enforce coding standards. Testing suites can be run locally by the developer or inside of continuous integration tools like Jenkins or TravisCI. New builds are compiled and pushed to production consistently with a single click. Documentation is automatically pulled from our code comments, template files, and schemas.

Diving in Deeper

When I first started diving into frontend architecture, before finding Grunt, I became really interested in creating Compass modules. I started off by creating small gems that would let me scaffold new projects, and thinking how I could use a gem to distribute a base set of styles to several websites at a time. While both of these use cases were valid, they express the limit of what I would have been capable of doing without learning a ton more Ruby. And after diving into a few basic Ruby tutorials, I realized that unless I wanted to write Ruby on Rails applications, there weren’t many resources for learning to program in Ruby, let alone write Compass modules in Ruby.

After moving to Grunt, one of my greatest wins was that I already knew the language that all of these modules were written in: JavaScript. After working with several modules for a few months, I found that they met 95% of my needs. Now, I’m no Node.js developer, but in several instances I was able to add a feature or fix a bug in a module and push that code upstream. In one case, with the Grunt-PhantomCSS module, I found that I needed to rewrite a majority of the project to fit my workflow. I forked the project, made the necessary changes, and pushed it up to GitHub, where other people found it and started to use it in their own projects. I wasn’t just a module user anymore, I was now a module author.

Getting Task Runners into Your Project

My first introduction to Grunt was being tossed into a large, established codebase full of dozens of modules and abstracted code organization—it was anything but easy for a beginner to understand. I eventually learned enough to start modifying the code, and then even add to it, but I’m someone that needs to understand my code from top to bottom before I can really accept and embrace it.

I then started tearing the project down, removing everything I could until I had the most bare-bones, basic setup that still worked. At that point, I understood each line of code in the project and could work my way back up to something more complex. Here is Grunt, at its bare minimum, compiling Sass:

module.exports = function(grunt) {
 grunt.loadNpmTasks('grunt-sass');  
 grunt.initConfig({
   sass: {
     options: {
     },
     dist: {
       src: 'sass/style.scss',
       dest: 'css/style.css',
     }
   }
 });
 grunt.registerTask('default', [
   'sass'
   ]);
};

The preceding code, which simply compiles your style.scss to style.css, has three distinct code areas:

Loading tasks
Grunt needs to know which of your Node tasks to import into the compile process. If you prefer the idea of just loading all of the packages listed in your package.json, the load-grunt-tasks module will make your life much easier.
Setting configuration
Each task that you load will have a list of configuration options that you can set. In this case, we are just setting the source and destination of the Sass task, but we could also turn on/off sourcemaps, set additional include paths, or change the CSS output style. Each task can also have multiple variations, or targets. You can set up a dev target that has sourcemaps turned on and an expanded output style, then set a dist target that has sourcemaps turned off and the output style set to compressed.
Register custom tasks
In this area, we are able to group various individual tasks into a custom parent task. The default task is what happens when you just type grunt into the command line. You can add more tasks, like Sass Lint, to the default task, or create other custom parent tasks like test or deploy.

Now let’s do the same thing in GulpJS:

var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('default', function () {
   gulp.src('./*.scss')
       .pipe(sass())
       .pipe(gulp.dest('./css'));
});


Gulp takes a bit of a different approach than Grunt in defining its tasks. Right off the bat, you can see there is a huge difference in code styles. Grunt is very configuration based, using object notation and defining tasks in one place, then running them in others. Gulp, on the other hand, chains tasks and configuration together and pipes the code from one operation to the next. Let’s walk through this code a bit:

Loading tasks
Instead of Grunt’s loadNpmTasks function, Gulp uses the traditional Node-based require() syntax. So at the top we load up gulp and gulp-sass, setting them as variables we’ll be using shortly.
Creating custom tasks
Again, we see the use of default to indicate what happens when you just type gulp in the command line. But instead of using grunt.registerTask to list the predefined tasks we want to run, we are going to build out the entire process right inside of this function.
The Gulp advantage
The Gulp approach focuses on small, concurrent functions that usually start with collecting a resource, and then passing it through several pipes before sending it to a final destination. There are two major advantages here. First, the concurrent nature of this approach means that processing our Sass isn’t blocking our other tasks from running. And second, pipes mean that Gulp can perform multiple operations on a single asset, whereas Grunt would have to write CSS to a temp folder, then perform a second operation on those temp assets.

Is There a Clear Winner?

So which approach is better? Well, that depends on your needs. Grunt is the incumbent in this field, and has the benefit of a larger number of modules. On the other hand, Gulp can be quite a bit faster in larger projects, and the ability to pipe code from one process to another can help keep your project from feeling like one big Rube Goldberg machine.

I’ve been happily using Grunt for the past few years, though I have been considering taking another look at Gulp and how it would work for my project. But in the end, both of them are great tools, and if there is module support to do the tasks that you need, your decision will probably come down to coding styles.

In the end, a task runner is just a tool. The job of a frontend architect is to create efficient and error-resistant workflows. So if your tooling is helping developers get up and running quickly, giving them a robust environment to quickly develop quality code and then deploy that code to testing, staging, and production environments, you’ve done your job regardless of your choice in framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.186.178