8Modern web frameworks make it easy to create huge dynamic websites. Why would
9anyone still care about a static website?
11- Static websites are fast, because it's simply files served directly from the
13- Static websites are secure. Nobody has ever found an SQL injection in static
15- Static websites are easy to deploy. Just copy them to your webhost using
16 (S)FTP/rsync/scp and you are done. They work on all webhosts: no CGI or extra
17 modules needed for the web server.
22Hakyll is a [Haskell] library meant for creating small-to-medium sized static
23websites. It is a powerful publishing tool, precisely because of the power of
24Haskell. By using the awesome [pandoc] library, it is able to create your
25website from a large variety of input formats.
27[Haskell]: http://haskell.org/
28[pandoc]: http://johnmacfarlane.net/pandoc/
32- easy templating system;
33- a simple HTTP server for previewing and compiling your website on the go;
34- powerful syntax highlighting;
35- modules for common items such as tags and feeds;
41We're going to discuss a small brochure site to start with. You can find all
42code and files necessary to build this site
43[right here](http://github.com/jaspervdj/hakyll-examples/tree/master/brochure)
44-- feel free to look at them as we go trough the tutorial. To fetch all examples
45in order to play with them locally, use:
47 git clone git://github.com/jaspervdj/hakyll-examples.git
49or nagivate to the download menu on GitHub.
51Now, for this first tutorial, there's a number of files we will use:
53 about.rst A simple page written in RST format
54 code.lhs Another page with some code (which can be highlighted)
55 css Directory for CSS files
56 |- default.css The main CSS file
57 \- syntax.css CSS file for code syntax highlighting
58 hakyll.hs Our code to generate the site
59 images Directory for images
60 \- haskell-logo.png The logo of my favorite programming language
61 index.markdown A simple page in markdown format
62 templates Directory for templates
63 \- default.html The main template for the site
65By default, hakyll will compile everything to the `_site` directory. We can try
68 [jasper@phoenix] ghc --make hakyll.hs
69 [jasper@phoenix] ./hakyll build
71Instead of using `build`, we can also use `preview`, which will fire up a
72webserver serving the `_site` directory, so have a look!
74All files have been compiled, and their output has been placed in the `_site`
75directory as illustrated in this diagram:
77
79No magic is involved at all -- we will precisely study how and why our items are
80compiled like that. All of this is specified in the `hakyll.hs` file. You can
81view the full `hakyll.hs` file online [here][brochure-hakyll.hs], or you can
82look in the directory you cloned or downloaded.
84[brochure-hakyll.hs]: http://github.com/jaspervdj/hakyll-examples/blob/master/brochure/hakyll.hs
88Let's start of with the `images/haskell-logo.png` file, because the processing
89of this file is very simple: it is simply copied to the output directory. Let's
90look at the relevant lines in the `hakyll.hs` file:
95 compile copyFileCompiler
98The first line specifies we will describe the process for compiling everything
99in the `images/` folder: hakyll uses globs for this [^pattern].
101[^pattern]: A little caveat is that these globs are not `String`s but
102 `Pattern`s, so you need the `OverloadedStrings` extension.
104We can see two simple rules next: [route] and [compile].
106- [route] determines how the input file(s) get mapped to the output files.
107 [route] only deals with file names -- not with the actual content!
108- [compile], on the other hand, determines how the file content is processed.
110[route]: /reference/Hakyll-Core-Rules.html#v:route
111[compile]: /reference/Hakyll-Core-Rules.html#v:compile
113In this case, we select the [idRoute]: which means the file name will be kept
114the same (`_site` will always be prepended automatically). This explains the
115name of [idRoute]: much like the `id` function in Haskell, it also maps values
118[idRoute]: /reference/Hakyll-Core-Routes.html#v:idRoute
120For our compiler, we use [copyFileCompiler], meaning that we don't process the
121content at all, we just copy the file.
123[copyFileCompiler]: /reference/Hakyll-Core-Writable-CopyFile.html#v:copyFileCompiler
127If we look at how the two CSS files are processed, we see something which looks
133 compile compressCssCompiler
136Indeed, the only difference with the images is that have now chosen for
137[compressCssCompiler] -- a compiler which *does* process the content. Let's have
138a quick look at the type of [compressCssCompiler]:
140[compressCssCompiler]: /reference/Hakyll-Web-CompressCss.html#v:compressCssCompiler
143compressCssCompiler :: Compiler Resource String
146Intuitively, we can see this as a process which takes a `Resource` and produces
149- A `Resource` is simply the Hakyll representation of an item -- usually just a
151- The produced string is the processed CSS.
153We can wonder what Hakyll does with the resulting `String`. Well, it simply
154writes this to the file specified in the `route`! As you can see, routes and
155compilers work together to produce your site.
159Next, we can see that the templates are compiled:
162match "templates/*" $ compile templateCompiler
165Let's start with the basics: what is a template? An example template gives us a
171 <title>Hakyll Example - $title$</title>
181A template is a text file to lay our some content. The content it lays out is
182called a page -- we'll see that in the next section. The syntax for templates is
183intentionally very simplistic. You can bind some content by referencing the name
184of the content *field* by using `$field$`, and that's it.
186You might have noticed how we specify a compiler (`compile`), but we don't set
187any `route`. Why is this?
189We need to compile the template because we will need it later. If we compile a
190page later using `templates/default.html`, Hakyll needs to know what
191`templates/default.html` is. Note that we could move template compilation to the
192bottom of our code. The order doesn't matter -- Hakyll will determine that for
193you. But if you don't compile `templates/default.html` as a template, Hakyll
194will not be able to take it into account when deciding the compilation order.
196So, the `compile` needs to be there -- but why don't we set a `route` here?
197Precisely because we don't want to our template to end up anywhere in our site
198directory! We want to use it to lay out other items -- so we need to load
199(compile) it, but we don't want to give it a real destination.
201By using the `templates/*` pattern, we compile all templates in one go.
205The code for pages looks suspiciously more complicated:
208match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
209 route $ setExtension "html"
210 compile $ pageCompiler
211 >>> applyTemplateCompiler "templates/default.html"
212 >>> relativizeUrlsCompiler
215But we'll see shortly that this actually fairly straightforward. Let's begin by
216exploring what a *page* is.
224So, I decided to create a site using Hakyll and...
227A page consists of two parts: a body, and metadata. As you can see above, the
228syntax is not hard. The metadata part is completely optional, this is the same
229page without metadata:
232So, I decided to create a site using Hakyll and...
235Hakyll supports a number of formats for the page body. Markdown, HTML and RST
236are probably the most common. Hakyll will automatically guess the right format
237if you use the right extension for your page.
240match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
243We see a more complicated pattern here. Some sets of files cannot be described
244easily by just one pattern, and here the [list] function can help us out. In
245this case, we have three specific pages we want to compile.
247[list]: /reference/Hakyll-Core-Identifier-Pattern.html#v:list
250route $ setExtension "html"
253For our pages, we do not want to use `idRoute` -- after all, we want to generate
254`.html` files, not `.markdown` files or something similar! The [setExtension]
255route allows you to simply replace the extension of an item, which is what we
258[setExtension]: /reference/Hakyll-Core-Routes.html#v:setExtension
261compile $ pageCompiler
262 >>> applyTemplateCompiler "templates/default.html"
263 >>> relativizeUrlsCompiler
266How should we process these pages? [pageCompiler] is the default compiler for
267pages. [pageCompiler] does a few things:
269- It parses the page into body and metadata
270- It adds some extra metadata fields such as `$url$` and `$path$` (you shouldn't
271 worry about these for now)
272- It fill in possible `$key$`'s in it's own body
273- It renders the page using pandoc
275Which basically means that we end up with a `Page` that has the HTML content we
276want as body. But we don't just want the plain content on our website -- we want
277to decorate it with a template, for starters.
279[pageCompiler]: /reference/Hakyll-Web-Page.html#v:pageCompiler
281Different compilers can be chained in a pipeline-like way using Arrows. Arrows
282form a complicated subject, but fortunately, most Hakyll users need not be
283concerned with the details. If you are interested, you can find some information
284on the [Understanding arrows] page -- but the only thing you really *need* to
285know is that you can chain compilers using the `>>>` operator.
287[Understanding arrows]: http://en.wikibooks.org/wiki/Haskell/Understanding_arrows
289The `>>>` operator is a lot like a flipped function composition (`flip (.)`) in
290Haskell, with the important difference that `>>>` is more general and works on
291all Arrows -- including Hakyll compilers.
293Here, we apply three compilers sequentially:
2951. We load and render the page using `pageCompiler`
2962. We apply the template we previously loaded using [applyTemplateCompiler]
2973. We relativize the URL's on the page using [relativizeUrlsCompiler]
299[applyTemplateCompiler]: /reference/Hakyll-Web-Template.html#v:applyTemplateCompiler
300[relativizeUrlsCompiler]: /reference/Hakyll-Web-RelativizeUrls.html#v:relativizeUrlsCompiler
302Relativizing URL's is a very handy feature. It means that we can just use
303absolute URL's everywhere in our templates and code, e.g.:
306<link rel="stylesheet" type="text/css" href="/css/default.css" />
309Using the [relativizeUrlsCompiler], Hakyll will change this to:
312<link rel="stylesheet" type="text/css" href="css/default.css" />
315when we are compiling `index.html`, or
318<link rel="stylesheet" type="text/css" href="../css/default.css" />
321when we are compiling (some imaginary) `posts/foo.html`. So Hakyll will
322translate this to a relative URL for each page. This means we can host our site
323at `example.com` and `example.com/subdir` without changing a single line of
326More tutorials are in the works...
328Various tips and tricks
329-----------------------
331### Syntax highlighting
333Syntax highlighting is enabled by default in Hakyll. However, you also need to
334enable it in pandoc. If no syntax highlighting shows up, try
336 [jasper@phoenix] cabal install --reinstall -fhighlighting pandoc
340If you execute a `./hakyll build`, Hakyll will build your site incrementally.
341This means it will be very fast, but it will not pick up _all_ changes.
343- In case you edited `hakyll.hs`, you first want to compile it again.
344- It is generally recommended to do a `./hakyll rebuild` before you deploy your
347After rebuilding your site, all files will look as "modified" to the filesystem.
348This means that when you upload your site, it will usually transfer all files --
349this can generate more traffic than necessary, since it is possible that some
350files were not actually modified. If you use `rsync`, you can counter this using
351the `--checksum` option.
356### regex-pcre dependency on Mac OS
358Hakyll requires [regex-pcre], which might fail to build on Mac OS. To solve
359this problem, make sure the [pcre] C library is installed (via homebrew or
360macports). Then install [regex-pcre] using:
362 cabal install --extra-include-dirs=/usr/local/include regex-pcre
366 cabal install --extra-include-dirs=/opt/local/include regex-pcre
368...and proceed to install Hakyll the regular way.
370[regex-pcre]: http://hackage.haskell.org/package/regex-pcre
371[pcre]: http://www.pcre.org/
373### "File name does not match module name" on Mac OS
376 File name does not match module name:
380Is an error encountered on Mac OS when `hakyll.hs` is located on a
381case-insensitive filesystem. A workaround is to rename it to something that
382isn't the name of the module, for example, `site.hs`.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list