New website built with Haggerston

The latest version of this site was built using Haggerston, a static site generator developed by myself and Kelvin Luck. It was named after the place it was built in.
For the past few years this site has been powered by Wordpress, but after several hacking attempts and an endless battle with spam I decided it was time to change. The new site is served as flat HTML files, generated locally using Haggerston and then uploaded to the remote server. No PHP or databases are involved, helping keep maintence and security issues to a minimum.
Leveraging the power of Grunt
Haggerston is built as a Grunt task. The reason we chose Grunt is because it can already do a lot of what is needed to generate a static site. It can copy files and folders, compile CSS, rsync to remote hosts, start local servers, and watch for file changes. The only task that's missing is one to generate HTML pages. This is where Haggerston comes in.
Haggerston is a relatively simple task, it's job is to turn a folder of .json files into .html files. Each json file specifies a template to use when rendering the html. If you've used javascript templating libraries before then the process should be familiar to you. All Haggerston is doing is turning templates into html files, and using the json files as data for the templates. Haggerston uses Swig as its templating language.
A basic example
Using Haggerston is as simple as dropping in an empty task into your Grunfile.js:
module.exports = function(grunt) {
grunt.initConfig({
haggerston: {}
});
grunt.loadNpmTasks('grunt-haggerston');
};
This makes the following assumptions: source files exist in a subfolder called src/content, templates exist in src/templates, and the generated site will be created in a subfolder called out. Each of these properties can be overridden in the config if necessary.
Let's take the url for this article as an example. The page exists at /articles/new-website-built-with-haggerston/, which started life in a source folder on my local machine like so:
src
└─┬ content/
├─┬ articles/
│ └─┬ new-website-built-with-haggerston/
│ └── index.json
└─┬ templates/
└── article.html
Running grunt haggerston from the command line will take each .json file from the content folder, render it against a chosen Swig template from the templates folder and export the .html into a corresponding structure in the output folder:
out
└─┬ articles/
└─┬ new-website-built-with-haggerston/
└── index.html
You choose which template to render the data against by specifying it in each json file. Let's take a look at the index.json file of this page as an example:
{
"template": "article.swig",
"templateData": {
"title": "New website built with Haggerston",
"date": "2013-04-27",
"blurb": "How I built my new website with Haggerston and Grunt",
"content": "index.md"
}
}
"template"specifies which Swig template will be used to render the.htmlfile. This is the only mandatory property of a Haggerston.jsonfile."templateData"is the data that will be sent to the Swig template when it's rendered. You can put anything you like inside it, or omit it entirely.
This process is extremely simple, but the key to making a good static site is knowing how to use templates. Swig supports something called template inheritance, and it allows templates to inherit and override blocks of markup from their parent. Check out the Swig documentation for an example of how to use it.
Using the concept of middleware
Each step in the Haggerston process is designed as middleware, an idea borrowed from connect that allows developers to intercept the flow of a process and manipulate the data being worked upon.
You may have noticed a property in the index.json example above:
"content": "index.md"
Haggerston is designed to find properties with values that end in '.md'. It then tries to locate the referenced markdown file, parse it into html, and replace the original property with the result. This means that when you use {{content}} in your template you're actually getting the html generated from the index.md file, not the string 'index.md'.
The part of Haggerston that does this is called the Markdown middleware. It's simply a function that gets called during the overall Haggerston Grunt task. Each middleware acts the same, and the function is given access to all the json data and rendered templates, meaning it can manipulate the pages in whatever way it likes before they're written to disk.
The basis for a piece of middleware looks like this:
module.exports = function() {
return function (pages, next, options) {
// Here you can manipulate the pages array in whatever way you want.
// Afterwards just call next(pages) to continue with the Haggerston process.
next(pages);
};
};
Haggerston comes with a default set of middleware modules that performs common tasks. Each middleware is executed sequentially, and is top-and-tailed with core tasks that cannot be moved. Here's an example of how the overall process works:
- Core process: Read
.jsonfiles into javascript objects - Middleware: Read markdown files and parse them into html markup
- Middleware: Render the template into html markup
- Middleware: Highlight any code blocks found in the rendered markup
- Core process: Write the rendered markup to
.htmlfiles.
You can write your own middleware and insert it at any point between the opening and closing core processes.
Limitations
Static sites will never be as powerful as dynamically generated sites, but they can make a good approximation. With some clever middleware and templating you can create common blog-like features such as categories, tags and pagination. Perhaps the most difficult thing to implement is a commenting system. The most popular solution is a third party service such as Disqus, which helps delegate the responsibility of maintenance and security issues to a third party.
Overall the decision to choose a static site over a dynamic one is down to how complex you think the site will be. For an infrequently updated blog run by a single user it's a good choice, and I'd recommend investigating the possibilities.


