Update 2016-08-15: Since I’ve written this, it looks like the jekyll-haml gem has seen a bit more love. I’m using it in templates just fine in this blog and have removed most of the stuff in this post. As a hint, be sure to use haml instead of include in your liquid markup.


I dislike writing HTML by hand if I can avoid it (opening/closing tags feel very verbose). Luckily, there are cool languages like HAML that make it a lot lighter and faster to write. Unfortunately, it seems that Jekyll doesn’t have any built in support for HAML.

I searched around for several solutions, but none of them worked quite how I wanted them to. I tried the jekyll-haml gem, but I had issues getting it to work in a layout and it seemed to be unmaintained. I also found a 2009 blog post by Raphael Stolt, but it seemed a bit involved for what I was trying to accomplish.

I eventually decided to make my own Rake tasks that would take care of this for me. This Rakefile finds any *.html.haml files in the project and then generates normal HTML. You can find it on the bottom of this post, or a possibly more up to date copy on the GitHub repo for this blog.

One downside of this approach is that we are introducing a new tool and building our site involves more than just jekyll, which can be an issue if you plan to host on something like GitHub pages. This isn’t a big deal to me since I’ll be hosting on my own server. Also an issue is that there isn’t a good way to tell VCS like Git to ignore HTML files generated from HAML.

To solve this problem use the rake task gitignore. This task will search for *.html.haml files in your project, and then add the name of the generated files to your .gitignore if not already present.

The Rakefile:

require 'find'
require 'haml'

task default: %w[haml gitignore build]

# Generate haml files
task :haml do
  forall_haml do |path|
    outfile = File.new(path[0..-6], 'w')
    engine = Haml::Engine.new(File.read(path))
    outfile.write(engine.render)
    outfile.close
  end
end

# Update gitignore to ignore html files generated from haml files
task :gitignore do
  orig_gitignore = File.read('./.gitignore')
  new_gitignore = File.new('./.gitignore', 'a')

  forall_haml do |path|
    ignore_path = path[2..-6]
    unless orig_gitignore.include? ignore_path
      new_gitignore.puts ignore_path
    end
  end

  new_gitignore.close
end

# Build the site with jekyll
task :build do
  `bundle exec jekyll build`
end

def forall_haml
  Find.find('./') do |path|
    # Exclude vendor
    if path.start_with? './vendor'
      Find.prune
    end

    # Only run on files
    if File::file? path
      if /\.html.haml$/.match path
        yield path
      end
    end
  end
end