Concatenate filter for nanoc

I just love nanoc. It might actually be my favorite way of making websites, because of the flexibility and speed a static site can give you.

One thing I sorely missed, was the option to do on-demand concatenation of asset files, like javascript or stylesheets.

It’s a simple task actually, so I rolled up my sleeves and got coding. I made a filter that will do exactly that. It turned out to be ridiculously easy.

Gotcha’s

This filter can only concatenate files in directories below the one that they are concatenated into. The reason for this being that the use case for the latter is quite rare, and I use nanoc items, so including files outside the project is not possible.

The code

put the following in lib/concat_filter.rb

require 'nanoc'

module Nanoc
  module Filters
    class ConcatFilter < Nanoc::Filter

      identifier :concat

      def run(content, args = {})
        return unless item[:require]
        rel_folder = File.dirname(@item.identifier.chop)
        includes = ""
        for name in item[:require] or []
          included_item = @items["#{rel_folder}/#{name}/"]
          includes << included_item.compiled_content + "\n;"
        end
        includes + content
      end
    end
  end
end

Add the necessary filter to your Rules file:

compile "/assets/js/application" do

  # filter coffeescript if item[:extension] == "coffee" # run some coffescript first
  filter :concat
  # filter uglify # for minification

end

compile "/assets/css/application" do

  filter :concat
  # filter :rainpress

end

You should now be able to concatenate items by adding them to the header yaml of a css, coffee, or js file. Add them as an array under the name “require”. Add the files without extension. Like this:

assets/js/application.js:

---
require:
- jquery
- bootstrap
- flexslider
---

alert("some amazing javascript!");

or this:

assets/css/application.css:

---
require:
- bootstrap
- flexslider
---

body {
  margin-top: 60px;
}

Don’t worry, the yaml header will be removed by the authorities! :)

If you want to add files from a subfolder, you just prepend the folder name to the filename, e.g. lib/jquery or something like that.

I like to block all javascript and css in the Rules, and then only let the concatenated files through. You can do that like this:

# Rules
compile '/assets/stylesheets/application' do
  filter :concat
  filter :rainpress
end

compile '/assets/javascripts/application' do
  filter :concat
  filter :uglify_js
end

compile '/assets/(javascripts|stylesheets)/*' do
  nil
end

# ...

route '/assets/stylesheets/application' do
  "/assets/stylesheets/application.css"
end

route '/assets/javascripts/application' do
  "/assets/javascripts/application.js"
end

route '/assets/(stylesheets|javascripts)/*' do
  nil
end

In the above example only application.js and application.css will be let through, and these two files will be minified as well. Totally ready for production!

The cool thing about this is that it integrates with the filter chain in nanoc. So you run your js through coffeescript (you’d have to do that before runnning the concatenate filter, though, since the coffeescript compiler doesn’t really dig javascript) and minify the concatenated file.

Remember: To run the extra filters above (coffeescript, rainpress, etc.) you’d have to install their gems first.

I hope this post was useful to you, if it was please share or comment!

Cheers!

Comments

comments powered by Disqus