Hakyll and vapaus.org
Vapaus.org is generated with Hakyll, which is a Haskell library for generating static sites. Mostly Vapaus.org is just a pile of static HTML files, but there are some more dynamic parts. For example, this page is generated from a Markdown file and the stylesheets are processed with Sass.
Many Hakyll users have published the sources for their websites. I haven’t, because I distantly remember that in my Git repo there might be some personal stuff that shouldn’t be published, but I haven’t bothered to go through the history.
Still, I’m want to share a few patterns from my main.hs. I’ve found them
useful and judging by the questions on #hakyll, I’m not the only one.
Static files
If you have a big pile of static files, here are some helpers. Use static to
handle individual files and use directory to apply it recursively to all
files under a directory.
static :: Pattern -> Rules ()
static f = match f $ do
route idRoute
compile copyFileCompiler
directory :: (Pattern -> Rules a) -> String -> Rules a
directory act f = act $ fromGlob $ f ++ "/**"
-- Here's an example:
main = hakyll $ do
mapM_ static ["humans.txt", "robots.txt"]
mapM_ (directory static) ["static/images", "algebra"].htaccess
By default, Hakyll ignores all files whose name begins with a dot. However, I
wanted to include a .htaccess file to set some MIME types and do some
redirects. You can tell Hakyll to allow certain dotfiels by using hakyllWith
instead of hakyll:
main = hakyllWith conf $ do
static ".htacess"
where
myIgnoreFile ".htaccess" = False
myIgnoreFile path = ignoreFile defaultConfiguration path
conf = defaultConfiguration { ignoreFile = myIgnoreFile }Git commit information
Like everyone else1, I store the source for this site in Git. If you scroll to the end of this page, you’ll notice a line with the hash, date and commit message of the last Git commit. I’m using it as a glorified last-modified stamp, although I’m not sure if it’s useful. If I had published my repo, I’d link to GitHub, so everyone could check the history of the page from there.
Useful or not, here’s how you generate it.
import Control.Applicative ((<$>))
import Data.Char (isSpace)
import Data.List (dropWhileEnd)
import Data.Monoid ((<>))
import System.Process (readProcess)
getGitVersion :: FilePath -> IO String
getGitVersion path = trim <$> readProcess "git" ["log", "-1", "--format=%h (%ai) %s", "--", path] ""
where
trim = dropWhileEnd isSpace
-- Field that contains the latest commit hash that hash touched the current item.
versionField :: String -> Context String
versionField name = field name $ \item -> unsafeCompiler $ do
let path = toFilePath $ itemIdentifier item
getGitVersion path
-- Field that contains the commit hash of HEAD.
headVersionField :: String -> Context String
headVersionField name = field name $ \_ -> unsafeCompiler $ getGitVersion ""
-- Example usage. Add `$versionField$` to your template.
main = hakyll $ do
let versionContext = versionField "versionInfo" <> defaultContext
match "*.markdown" $
route setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" versionContext
>>= relativizeUrlsCompiling CoffeeScript
How to compile and minimize CoffeeScript? With two unixFilters.
coffee :: Compiler (Item String)
coffee = getResourceString >>= withItemBody processCoffee
where
processCoffee = unixFilter "coffee" ["-c", "-s"] >=>
unixFilter "yuicompressor" ["--type", "js"]Formatting mathematics
If you want to have nicely-formatted math in your blogposts, the simplest way
to go is MathJax. Pandoc supports it, but you have to
enable it with pandocCompilerWith. Here’s an example:
main = hakyll $ do
-- MathJax's argument is not used when using Pandoc in Hakyll
let pandocOptions = defaultHakyllWriterOptions { writerHTMLMathMethod = MathJax "" }
match "*.markdown"
route $ setExtension "html"
compile $ pandocCompilerWith defaultHakyllReaderOptions pandocOptions
>>= loadAndApplyTemplate "templates/math.html" textContext
>>= relativizeUrlsYou also need to load MathJax’s JavaScript in your template. You can use MathJax CDN or you can choose your own copy of MathJax. If you choose to use the CDN, add this to your <head></head> block:
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>Now you can write mathematics inside dollar signs, just like in LaTeX. Here’s an example and the result:
$$ \int_0^r 2 \pi r \mathrm{d}r = \pi r^2 $$\[ \int_0^r 2 \pi r \mathrm{d}r = \pi r^2 \]
And more…
That’s about it. If you have anything to ask about Hakyll, come to the IRC
channel #hakyll on freenode.