I always wrote the HTML for web sites myself because I like the flexibility to structure the code and the CSS in the way I want. At work, we use Mustache to generate HTML from templates. Mustache lets you write whatever text document you want in your own structure, replacing special tags with content from arbitrary JSON.

Being used to these tools, I thought it would be nice to combine them.

Combining Markdown and Mustache

I found marked to be a great library to parse Markdown. It's fast and has just the right feature set with sensible default settings. Playing a little with Marked and Mustache turned into that fun project named Blogdown.

Blogdown is a Node.js application, so you have to install Node first. To install the latest Blogdown version run:

npm install -g blogdown

Creating pages

The central idea is to store the marked result in the md property of a JavaScript object and pass it to a Mustache template.

To do so, create a file named src/index.md with this content:

# Welcome to Blogdown

This is *markdown*!

And a file named src/index.html with this content:

<html>
  <head><title>Basic Blogdown Example</title></head>
  <body>{{{md}}}</body>
</html>

If you installed blogdown globally, you can now run blogdown in your terminal which will create site/index.html with the parsed markdown replacing the {{{md}}} tag in the template. Note that three braces are used so that the HTML is preserved by Mustache. Using two braces escapes HTML characters.

Adding timestamps

If you want to show the date when a page was initially created or when it was last updated, you can use the power of moment.js to format timestamps the way you like. To do so, create a file named blogdown.json in the root of your site project:

{
  "dates"    : {
    "header" : "ddd, DD. MMMM YYYY",
    "footer" : "DD.MM.YYYY HH:mm:ss"
  }
}

This makes two date formats with three timestamps available in all your Mustache templates. E.g. the creation date with the header format can be included with {{dates.header.created}}. The available timestsamps are:

More information on the date formatter can be found in the moment documentation.

But how does Blogdown know when the content of a page was updated?

The blogdown.meta file

You might have already noticed that Blogdown creates a file named blogdown.meta. This file contains a list of pages being rendered with the sha of the markdown and the properties of the file (more on properties later), and finally the created, updated and rendered timestamps of the page. On each subsequent invokation, Blogdown will check on each page whether the content was updated and rerenders the page if needed.

You may want to add the file to source control to keep that information for later page updates.

EDIT: Starting with v0.6.0 blogdown only generates the meta file if it's called with --publish. The timestamps of new files will be set to "DRAFT" until they are published the first time.

Mustache partials support

You might find yourself including the snippets of HTML in mutliple pages. To avoid copy-pasting elements like the page navigation, you can use Mustaches "partials" with Blogdown. To use them, create a folder named template in your projects root directory and put some .html files in it. You can then reference these snippets from your templates by their file name:

{{> some-partial}}

Read more about partials in the Mustache readme.

Templates

In case the HTML for mutliple pages looks the same, you can create a template named template.html (or template.mustache alternatively). For each markdown file, Blogdown will search for a template in the same folder. If no template is present, it will search the parent directories.

JSON properties

To store information outside of a template or markdown file, you can create JSON files with the same name as the template. All properties will be available to Mustache. Use the dot notation to dig a property out of a JSON object:

<title>{{some.json.property}}</title>

Shared properties can be stored in a template.json file which will be inhertied by all templates in the same folder and in the sub folders. In case a template.json file and a json file with the page name is present, they get merged. In case a template.json file exists in a sub folder, it is merged with the parent folders template.json before rendering.

Pre defined JSON properties

These properties are always defined and can be used regardless of whether a JSON properties file was created:

Page lists

Blogdown provides an easy way to create lists of pages and reuse all the information from the refered pages JSON properties. Lists are configured in the blogdown.json configuration file. A list consists of a filter specifying a property and an expression to match, a sort criteria and a limit.

"lists"      : {
  "articles" : {
    "filter" : "file.path = blog/*",
    "sort"   : "file.created DESC",
    "limit"  : 10
  },
  "projects" : {
    "filter" : "file.path != projects/[a-z]+\\.html"
  }
}

Using page lists in Mustache templates

Blogdown will make all lists available to all pages under the specified name. In the above example, two lists can be accessed: "articles" and "projects".

To iterate over a list, use Mustaches "sections":

<ul>{{#articles}}
<li><a href="{{{file.path}}}">{{title}}</a></li>
{{/articles}}</ul>

file.path returns the relative path to the iterated page.

What's next?

Blogdown is released under the MIT license. You are welcome to fork the project on GitHub.

I'm really interested in your feedback. Please leave a comment below.


comments powered by Disqus