# Hakyll Hakyll is a simple static site generator in Haskell. It is mostly inspired by [Jekyll](http://github.com/mojombo/jekyll), but I like to believe it is simpler. ## Configuration Inspired by [xmonad](http://xmonad.org), a small Haskell program is used as configuration file. In this file, you give instructions on how the site should be generated. In the rest of this document, we will examine a small example. This is our directory layout: |-- _site |-- favicon.ico |-- hakyll.hs |-- images | `-- foo.png |-- templates | |-- default.html | `-- sample.html `-- text.markdown ## Static files Static files can be rendered using the `static` command. This command ensures the files will copied when you compile the site. For convenience reasons, there is also a `staticDirectory` command, which works recursively. main = do static "favicon.ico" staticDirectory "images" staticDirectory "css" ## Pages Pages can be written in html, markdown, LaTeX, and basically anything pandoc supports. They can also contain metadata, which are always key-value mappings. --- author: Jasper Van der Jeugt title: A sample markdown post --- # A sample markdown post This is a sample markdown post. It supports pandoc extensions like code highlighting. For example: ~~~~{.haskell} main = putStrLn "Hello World!" ~~~~ Metadata is always placed in the beginning of a file, and is delimited by a `---` string. The metadata can only contain simple key-value pairs. We can now read in this page using the `Text.Hakyll.Page.readPage` function. This will return a `Page`, which is actually just a `Map String ByteString`. In this example, the map would consist of the following key-value pairs: - `author`: `Jasper Van der Jeugt` - `title`: `A sample markdown post` - `body`: The rest of the file (rendered to html). - `url`: `text.html` (the original filename was `text.markdown`, the extension was changed to html). ## Templates In hakyll, there is a strict separation between pages and templates. Templates, for example, cannot contain metadata.

$title

by $author $body Templates are rendered using the Haskell `Text.Template` library. This means that in your template, you can use `$identifier`, and it will be replaced by the value of `identifier`. With this template we could, for example, render the file we saw in the previous section. It would go like this: page <- readPage "text.markdown" render <- renderPage "templates/sample.html" page Now, `render` will be a `Page` containing all metadata from `page`, but the `body` key would be replaced by the substitution. This means we can combine rendering actions. Given another template `templates/default.html`: $title $body We can now combine the rendering actions: page <- readPage "text.markdown" render <- (renderPage "templates/sample.html" page >>= renderPage "templates/default.html") Of course, you can't really do anything with the render if you don't write it to a file somewhere. That's why the function `renderAndWrite` exists: readPage "text.markdown" >>= renderPage "templates/sample.html" page >>= renderAndWrite "templates/default.html" Now, where will this file be written? In `_site/text.html`, of course! That's because the page still contains a key called `url`, which the renderAndWrite function uses to determine the file destination. ## More advanced things Sometimes, you want to create a `Page` from scratch, without reading from a file. There are functions to do that for you, and I suggest you read the documentation of `Text.Hakyll.Page`. As a more advanced example, I will explain the RSS system I wrote for my website. |-- generate.hs |-- posts | `-- 2009-12-02-a-first-post.markdown `-- templates |-- rss.xml `-- rssitem.xml Our post contains some metadata: --- title: A first post date: December 2, 2009 --- # A first post A first post describing the technical setup of this blog, for that is The `templates/rssitem.xml` file is a template for rendering one post to an rss item: $title http://jaspervdj.be/$url New blogpost: $title Now a template for rendering the whole rss feed, `templates/rss.xml`: jaspervdj - a personal blog http://jaspervdj.be/ Personal blog of jaspervdj $items Alright, let's get coding. -- Find all posts paths. postPaths <- liftM (L.reverse . L.sort) $ getRecursiveContents "posts" -- Read and render all posts with the rssitem.xml template -- Also, only render 5 posts. pages <- mapM readPage (take 5 postPaths) items <- mapM (renderPage "templates/rssitem.xml") pages -- Render the result renderAndWrite "templates/rss.xml" $ pageFromList [ ("items", concatPages items), ("url", "rss.xml") ] That's that. Now we have a nice rss feed.