Sprockets style assets with Express and Handlebars

I've been using the connect-assets node library in my Node projects which goes some way at emulating the Rails assets pipeline (Sprockets). This
achieves a number of things, but the two most important features are the automatic compilation of your assets such as Stylus stylesheets and
Coffee Scripts and the automatic fingerprinting of the generated static resources.


Fingerprinting static resources is the best solution I've seen so far for managing version changes for static resources. Any sane website will want
clients to cache static resources, so that each time someone visits your site they don't need to download everything again. The challenge then is to
ensure that whenever you update a static resource, the clients are forced to fetch the updated resource. For example if we reference a stylesheet
like this in our HTML:

<link href="/css/chimpanzee-style.css" rel="stylesheet" type="text/css"/>

If this is cached by the users' browser, then (assuming we don't have any web server magic going on) the next time the user visits your site, they
will simply use the stylesheet cached by their browser. This means they are missing any changes you have subsequently made, so your site may
well look like donkey balls.

In the old days we used to monkey around like this to overcome the problem:

<link href="/css/chimpanzee-style.css?v=1" rel="stylesheet" type="text/css"/>

This has a number of limitations but will mostly work. The main pain in the arse is that if we do this manually this becomes quite painful and error prone,
and if we have this automated then every redeployment of the software we force numerous cache invalidations upon our clients which were unnecessary.

With fingerprinting we only ever invalidate the cache when the file changes, by appending the MD5 digest of the file to the end of the filename, so
our link will look like this:

<link href="/css/chimpanzee-style-908e25f4bf641868d8683022a5b62f54.css" rel="stylesheet" type="text/css"/>

The MD5 digest will only change when the file is changed, so the user will only be forced to fetch a new file when changes are made.

The only problem now is ensuring we generate the fingerprinted assets on deployment, and correctly linking to the fingerprinted resources in our HTML,
which connect-assets handles for us

Setting up connect-assets in a Zappa project

First install connect-assets:

npm install connect-assets

I'm using Stylus for my stylesheets, Handlebars as my template engine and Coffee Script for javascript so I'll install:

npm install stylus
npm install express-hbs
npm install coffee-script

I've created a very simple node package connect-assets-hbs-helpers with the helpers you'll need to help you reference static resources in your templates, you can install it with:

npm install connect-assets-hbs-helpers

Now you can set this up in Zappa something like this (shouldn't take too much imagination to fix this to work in plain Express):

hbs = require 'express-hbs'
require 'connect-assets-hbs-helpers'

require('zappajs') ->

  # Add handlebars template support
  @app.engine 'hbs', hbs.express3
    partialsDir:  "#{__dirname}/views/partials"
    defaultLayout: "#{__dirname}/views/layout/default.hbs"
  @set 'view engine': 'hbs'
  @use 'static': "#{__dirname}/../public"
  # Use connect-assets for Rails style assets pipeline for .styl, .coffee
  connectAssets = require('connect-assets')
  @use connectAssets({
      buildDir: "#{__dirname}/public"
      pathsOnly: true

Now we put all our Stylus stylesheets and Coffee script files and imgs to be fingerprinted and optionally compiled in our assets directory like so:


And now we can reference these in our layout something like this:

<!DOCTYPE html>
          {{{css 'bootstrap/bootstrap'}}}
          {{{css 'main'}}}
          {{{css 'bootstrap/bootstrap-responsive'}}}
          {{{js 'modernizr'}}
          {{{js 'main'}}}

In this example I'm also using plain CSS bootstrap files, that's no problem. In development mode connect-assets will render non-fingerprinted paths,
but in production the fingerprints will be added.

Image paths in stylesheets

Currently there is a bug in connect-assets when it comes to automagically fixing the img paths in Stylus stylesheets,
luckily some open source hero already patched it, so add this code underneath the code I showed for your Zappa setup:

_compileStyl = connectAssets.cssCompilers.styl.compileSync
connectAssets.cssCompilers.styl.compileSync = (sourcePath, source) ->
  css = _compileStyl.call connectAssets.cssCompilers.styl, sourcePath, source
  connectAssets.instance.fixCSSImagePaths css