Showing posts with label workflow. Show all posts
Showing posts with label workflow. Show all posts

Monday, May 28, 2012

Using Grunt.js with CSS

For most of my apps, I've been using either a Makefile, shell scripts, Python scripts, or some combination of those as my build tool — taking care of tasks like concatenating files, minifying files, and linting my code. A few months ago, Ben Alman introduced Grunt, a build tool written in JS and designed for JS. I quite liked the idea of using JS for task automation, since it's a language I already know (bash scripting makes me want to bash my head against the wall) and it's the language that I write my apps in (at least the front ends). I finally got the opportunity to properly try it out last week, while porting my HTML5 slide editing app over from Closure to Bootstrap.

Grunt is designed primarily for JavaScript projects, like Node.JS or jQuery plugins, so it comes out of the box with tasks for concatenating files, linting JS files (via JSHint), compressing JS files (via UglifyJS), running a Node server, and running JS unit tests — no CSS-related tasks. For this web app, I decided to write the CSS using Less, a CSS pre-processor. While debugging locally, I include the .less file on the page and the less.js pre-processor, but for performance reasons on production, I wanted to just include a minified CSS file. So I needed 2 new tasks: one for processing the .less, and another for minifying the .css. Grunt has a formal way for developers to define and share new tasks via extensions, and luckily I found developers have already made grunt-less and grunt-css extensions.

To use the Grunt extensions, I first had to install them as node modules. I could have installed them simply by running npm install grunt-less inside my project folder, but at the suggestion of Ben, I instead created a package.json file with the modules listed as dependencies. Then I just ran npm install and it grabbed everything necessary. Now, since my code is open-source, I don't have to tell people what they need to install - just have to make sure they run install. Here's what my file looks like:

{
  "name": "SlideEditor",
  "version": "0.0.0",
  "dependencies" : {
    "grunt-less":  ">0.0.0",
    "grunt-css":   ">0.0.0"
  }
}

Once I did that, I just loaded the task definitions in my grunt.js file by sticking these lines in:

  grunt.loadNpmTasks('grunt-less');
  grunt.loadNpmTasks('grunt-css');

And now I could use the tasks! However, before I set them up, I spent a while figuring out the best directory structure for both my JS and CSS. When you're using any sort of build tool, it helps to have a sensible directory structure, like separate folders for the debug files versus the build files. For this app, I decided to put all the original files in a src folder with child folders for css and js, and for the build files, I just put them in css and js folders under the root. In the src/css folder, I have children folders less (for the .less file), app (for the processed less file), and libs (for any CSS files from 3rd party libraries). Here's what that looks like:

  • src
    • css
      • less
      • app
      • libs
    • js
      • app
      • libs
  • js
  • css

Okay, so finally, I put the Grunt extensions to work, setting up the less and cssmin tasks. Here's what all the CSS-related tasks look like in my grunt file:

  var SRC_CSS   = 'src/css/';
  var BUILD_CSS = 'css/';
  
  grunt.initConfig({
    // ...
    less: {
      css: {
        src: [SRC_CSS + 'less/base.less'],
        dest: SRC_CSS + 'app/base.css',
      }
    },
    concat: {
      css: {
        src: [SRC_CSS + 'libs/*.css',
              SRC_CSS + 'app/*.css'],
        dest: BUILD_CSS + 'css/all.css'
      }
    },
    cssmin: {
      css: {
        src: '',
        dest: BUILD_CSS + 'css/all-min.css'
      }
    },
    // ...
  });

You can check out my full grunt.js file here, to see how I use the CSS tasks and JS tasks together. Like any tool, Grunt takes a bit of time to learn, but at least for me, I find it much more approachable than the world of bash scripting. Try it out for your next project!

Friday, September 30, 2011

Pre-Deploy Git Check

Since I'm the only one developing EatDifferent, I've been content to store it in a Dropbox folder and use a local git repository for version control. But I got nervous thinking about what would happen if something went horribly wrong while I was unavailable (like on a plane), and decided I needed a backup collaborator -- someone who could quickly checkout the code and deploy a fix.

I probably could have shared the Dropbox folder with them, but I instead opted for a private Github plan for $7 a month, which lets me share my code repository with my backup collaborator. Plus, I now get to browse my code history and commits via Github's slick online interface.

I often forgot to git commit and push my changes, so I added this check to my Makefile which errors if there are uncommited changes. I run the check everytime I deploy my code to App Engine, to make sure the code always reflects what's deployed.

So far, the workflow seems to work...and now I can fly a plane in peace. ☺