Wed, 24. April 2013
Markdown just feels right for writing documentation for software projects. In recent days, I got so used to the notation that I wanted to use it to update the content of my web page.
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.
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
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.
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?
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.
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.
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.
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.
These properties are always defined and can be used regardless of whether a JSON properties file was created:
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"
}
}
property = criteria
or property != criteria
property
, property ASC
or
property DESC
. The default sort order is ASC.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.
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.