Using Jekyllrb with Grunt

While building my personal blog http://www.cheeyeo.uk, I made the decision to explore other blogging frameworks outwith of the traditional MVC framework structure.

After reading an article about Jekyllrb on the Thoughtworks Technology Radar, I made the decision to learn more about Jekyllrb by building this site using it.

One of the features lacking in using a static site generator is asset management. In Rails we have the Asset pipeline which carries out the compilation and minifcation process. Whilst using Jekyll I decided to roll my own using Grunt.js.

Step 1: Structuring the assets

I would need to structure my assets in such a way that ONLY the required files are included into the final build. In Jekyllrb, any directory can be included into the final _site folder if it is not excluded from _config.yml.

Rather than having to manually update the config file, I learnt that any folder which starts with an underscore will be automatically excluded from the _site folder. I created a _assets folder which will hold all my sass and js files. A _tmp directory is also created as a temporary holding area which will be made clear below.

Step 2: Making the gruntfile

The main grunt tasks I would require to handle the assets for this site includes the following:

  1. Compiling the javascript and sass files
  2. Minifying / compressing the assets for production

There are already many grunt plugins available, such as grunt-contrib-concat, grunt-contrib-uglify and grunt-contrib-sass.

Below is a snippet of my Gruntfile.js for handling js and sass files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),

    concat: {
      options: {
        separator: ';',
      },
      dist: {
        src: ['_assets/js/*.js','!_assets/js/modernizr.*'],
        dest: '_tmp/site.js'
      }
    },

    uglify: {
      my_target: {
        files: {
          '_tmp/site.min.js': ['_tmp/site.js']
        }
      }
    },

    sass: {
      global: {
        options: {
          style: "compressed"
        },
        files: {
          "_tmp/global.css": "_assets/scss/global.scss"
        }
      }
    },
    ...
   });

The concat task essentially joins the required js files together from _assets/js into _tmp/site.js which the uglify task picks up to generate the compressed js file into _tmp/site.min.js. Likewise, the sass task joins and compresses the css into _tmp/global.css

Step 3: Adding the assets to the build process

The trickiest task is to combine the Grunt.js workflow into the Jekyllrb structure.

I came across the jekyll-minibundle which both minifies and generates asset fingerprinting. Since the above Grunt tasks already does minification I am only using the asset fingerprinting.

This would allow me to cache my assets using Rack::StaticCache in my config.ru file. ( More on deploying your jekyllrb site in a separate article !)

Within the partials, I made the following declarations:

<link rel="stylesheet" href="{% ministamp _tmp/global.css /assets/global.css %}" type="text/css">

ministamp is a command from jekyll-minibundle. It generates a fingerprint of _tmp/global.css and moves into _site/assets/global.css once the site is being built.

If _tmp/global.css were to change, the fingerprint will change which means you get automatic asset cache expiry, just like the asset pipeline!

Conclusion

Jekyllrb is a very versatile system and there are many variations one can use to handle assets. Coupled with Grunt.js which also has a healthy ecosystem of plugins and tasks ready to use, handling assets in static websites need not be difficult.