2011-02-12 14:16:42 +00:00
|
|
|
---
|
|
|
|
title: Tutorial
|
|
|
|
---
|
2011-02-10 11:30:06 +00:00
|
|
|
|
|
|
|
Why static websites?
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
Modern web frameworks make it easy to create huge dynamic websites. Why would
|
|
|
|
anyone still care about a static website?
|
|
|
|
|
|
|
|
- Static websites are fast, because it's simply files served directly from the
|
|
|
|
hard disk.
|
2011-02-28 15:13:53 +00:00
|
|
|
- Static websites are secure. Nobody has ever found an SQL injection in static
|
2011-02-10 11:30:06 +00:00
|
|
|
pages.
|
|
|
|
- Static websites are easy to deploy. Just copy them to your webhost using
|
|
|
|
(S)FTP/rsync/scp and you are done. They work on all webhosts: no CGI or extra
|
|
|
|
modules needed for the web server.
|
|
|
|
|
|
|
|
Why Hakyll?
|
|
|
|
-----------
|
|
|
|
|
|
|
|
Hakyll is a [Haskell] library meant for creating small-to-medium sized static
|
|
|
|
websites. It is a powerful publishing tool, precisely because of the power of
|
|
|
|
Haskell. By using the awesome [pandoc] library, it is able to create your
|
|
|
|
website from a large variety of input formats.
|
|
|
|
|
|
|
|
[Haskell]: http://haskell.org/
|
2011-02-28 09:36:38 +00:00
|
|
|
[pandoc]: http://johnmacfarlane.net/pandoc/
|
2011-02-10 11:30:06 +00:00
|
|
|
|
|
|
|
Features include:
|
|
|
|
|
|
|
|
- easy templating system;
|
|
|
|
- a simple HTTP server for previewing and compiling your website on the go;
|
|
|
|
- powerful syntax highlighting;
|
|
|
|
- modules for common items such as tags and feeds;
|
|
|
|
- easily extensible.
|
|
|
|
|
2011-05-30 14:22:35 +00:00
|
|
|
Let's get started!
|
|
|
|
------------------
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-05-29 13:34:37 +00:00
|
|
|
We're going to discuss a small brochure site to start with. You can find all
|
|
|
|
code and files necessary to build this site [right here](/examples/brochure.zip)
|
2011-06-11 17:24:36 +00:00
|
|
|
-- feel free to look at them as we go trough the tutorial, in fact, it might be
|
|
|
|
very learnful to have a closer look at the files as we discuss them. There's a
|
|
|
|
number of files we will use:
|
2011-05-29 13:34:37 +00:00
|
|
|
|
2011-05-30 14:22:35 +00:00
|
|
|
about.rst A simple page written in RST format
|
|
|
|
code.lhs Another page with some code (which can be highlighted)
|
|
|
|
css Directory for CSS files
|
|
|
|
|- default.css The main CSS file
|
|
|
|
\- syntax.css CSS file for code syntax highlighting
|
|
|
|
hakyll.hs Our code to generate the site
|
|
|
|
images Directory for images
|
|
|
|
\- haskell-logo.png The logo of my favorite programming language
|
|
|
|
index.markdown A simple page in markdown format
|
|
|
|
templates Directory for templates
|
|
|
|
\- default.html The main template for the site
|
2011-05-29 13:34:37 +00:00
|
|
|
|
|
|
|
By default, hakyll will compile everything to the `_site` directory. We can try
|
|
|
|
this like this:
|
|
|
|
|
|
|
|
[jasper@phoenix] ghc --make hakyll.hs
|
|
|
|
[jasper@phoenix] ./hakyll build
|
|
|
|
|
2011-05-30 14:22:35 +00:00
|
|
|
Instead of using `build`, we can also use `preview`, which will fire up a
|
|
|
|
webserver serving the `_site` directory, so have a look!
|
|
|
|
|
|
|
|
All files have been compiled, and their output has been placed in the `_site`
|
|
|
|
directory as illustrated in this diagram:
|
|
|
|
|
2011-05-29 13:34:37 +00:00
|
|
|
![Brochure files](/images/brochure-files.png)
|
|
|
|
|
2011-05-30 14:22:35 +00:00
|
|
|
No magic is involved at all -- we will precisely study how and why our items are
|
|
|
|
compiled like that. All of this is specified in the `hakyll.hs` file.
|
|
|
|
|
|
|
|
### Images
|
|
|
|
|
|
|
|
Let's start of with the `images/haskell-logo.png` file, because the processing
|
|
|
|
of this file is very simple: it is simply copied to the output directory. Let's
|
|
|
|
look at the relevant lines in the `hakyll.hs` file:
|
|
|
|
|
|
|
|
~~~~~{.haskell}
|
|
|
|
match "images/*" $ do
|
|
|
|
route idRoute
|
|
|
|
compile copyFileCompiler
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
The first line specifies we will describe the process for compiling everything
|
|
|
|
in the `images/` folder: hakyll uses globs for this [^pattern].
|
|
|
|
|
|
|
|
[^pattern]: A little caveat is that these globs are not `String`s but
|
|
|
|
`Pattern`s, so you need the `OverloadedStrings` extension.
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
We can see two simple rules next: [route] and [compile].
|
2011-05-30 14:22:35 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
- [route] determines how the input file(s) get mapped to the output files.
|
|
|
|
[route] only deals with file names -- not with the actual content!
|
|
|
|
- [compile], on the other hand, determines how the file content is processed.
|
2011-05-30 14:22:35 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[route]: /reference/Hakyll-Core-Rules.html#v:route
|
|
|
|
[compile]: /reference/Hakyll-Core-Rules.html#v:compile
|
|
|
|
|
|
|
|
In this case, we select the [idRoute]: which means the file name will be kept
|
2011-05-30 14:22:35 +00:00
|
|
|
the same (`_site` will always be prepended automatically). This explains the
|
2011-06-11 17:24:36 +00:00
|
|
|
name of [idRoute]: much like the `id` function in Haskell, it also maps values
|
2011-05-30 14:22:35 +00:00
|
|
|
to themselves.
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[idRoute]: /reference/Hakyll-Core-Routes.html#v:idRoute
|
|
|
|
|
|
|
|
For our compiler, we use [copyFileCompiler], meaning that we don't process the
|
2011-05-30 14:22:35 +00:00
|
|
|
content at all, we just copy the file.
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[copyFileCompiler]: /reference/Hakyll-Core-Writable-CopyFile.html#v:copyFileCompiler
|
|
|
|
|
2011-05-30 14:22:35 +00:00
|
|
|
### CSS
|
|
|
|
|
|
|
|
If we look at how the two CSS files are processed, we see something which looks
|
|
|
|
very familiar:
|
|
|
|
|
|
|
|
~~~~~{.haskell}
|
|
|
|
match "css/*" $ do
|
|
|
|
route idRoute
|
|
|
|
compile compressCssCompiler
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Indeed, the only difference with the images is that have now chosen for
|
2011-06-11 17:24:36 +00:00
|
|
|
[compressCssCompiler] -- a compiler which *does* process the content. Let's have
|
|
|
|
a quick look at the type of [compressCssCompiler]:
|
|
|
|
|
|
|
|
[compressCssCompiler]: /reference/Hakyll-Web-CompressCss.html#v:compressCssCompiler
|
2011-05-30 14:22:35 +00:00
|
|
|
|
|
|
|
~~~~~{.haskell}
|
|
|
|
compressCssCompiler :: Compiler Resource String
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Intuitively, we can see this as a process which takes a `Resource` and produces
|
|
|
|
a `String`.
|
|
|
|
|
|
|
|
- A `Resource` is simply the Hakyll representation of an item -- usually just a
|
|
|
|
file on the disk.
|
|
|
|
- The produced string is the processed CSS.
|
|
|
|
|
|
|
|
We can wonder what Hakyll does with the resulting `String`. Well, it simply
|
|
|
|
writes this to the file specified in the `route`! As you can see, routes and
|
|
|
|
compilers work together to produce your site.
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
### Templates
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Next, we can see that the templates are compiled:
|
2011-02-10 11:30:06 +00:00
|
|
|
|
|
|
|
~~~~~{.haskell}
|
2011-06-11 17:24:36 +00:00
|
|
|
match "templates/*" $ compile templateCompiler
|
2011-02-10 11:30:06 +00:00
|
|
|
~~~~~
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Let's start with the basics: what is a template? An example template gives us a
|
|
|
|
good impression:
|
2011-02-28 09:36:38 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Hakyll Example - $title$</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>$title$</h1>
|
|
|
|
|
|
|
|
$body$
|
|
|
|
</body>
|
|
|
|
</html>
|
2011-02-10 11:30:06 +00:00
|
|
|
~~~~~
|
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
A template is a text file to lay our some content. The content it lays out is
|
|
|
|
called a page -- we'll see that in the next section. The syntax for templates is
|
|
|
|
intentionally very simplistic. You can bind some content by referencing the name
|
|
|
|
of the content *field* by using `$field$`, and that's it.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
You might have noticed how we specify a compiler (`compile`), but we don't set
|
|
|
|
any `route`. Why is this?
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Precisely because we don't want to our template to end up anywhere in our site
|
|
|
|
directory! We want to use it to lay out other items -- so we need to load
|
|
|
|
(compile) it, but we don't want to give it a real destination.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
By using the `templates/*` pattern, we compile all templates in one go.
|
2011-02-12 15:54:31 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
### Pages
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
The code for pages looks suspiciously more complicated:
|
2011-04-05 15:34:00 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~{.haskell}
|
|
|
|
match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
|
|
|
|
route $ setExtension "html"
|
|
|
|
compile $ pageCompiler
|
|
|
|
>>> applyTemplateCompiler "templates/default.html"
|
|
|
|
>>> relativizeUrlsCompiler
|
|
|
|
~~~~~~
|
2011-04-05 15:34:00 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
But we'll see shortly that this actually fairly straightforward. Let's begin by
|
|
|
|
exploring what a *page* is.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~
|
|
|
|
---
|
|
|
|
title: Home
|
|
|
|
author: Jasper
|
|
|
|
---
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
So, I decided to create a site using Hakyll and...
|
|
|
|
~~~~~~
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
A page consists of two parts: a body, and metadata. As you can see above, the
|
|
|
|
syntax is not hard. The metadata part is completely optional, this is the same
|
|
|
|
page without metadata:
|
2011-04-11 07:07:30 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~
|
|
|
|
So, I decided to create a site using Hakyll and...
|
|
|
|
~~~~~~
|
2011-04-11 07:07:30 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Hakyll supports a number of formats for the page body. Markdown, HTML and RST
|
|
|
|
are probably the most common. Hakyll will automatically guess the right format
|
|
|
|
if you use the right extension for your page.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~{.haskell}
|
|
|
|
match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
|
|
|
|
~~~~~~
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
We see a more complicated pattern here. Some sets of files cannot be described
|
|
|
|
easily by just one pattern, and here the [list] function can help us out. In
|
|
|
|
this case, we have three specific pages we want to compile.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[list]: /reference/Hakyll-Core-Identifier-Pattern.html#v:list
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~{.haskell}
|
|
|
|
route $ setExtension "html"
|
|
|
|
~~~~~~
|
2011-02-12 19:33:46 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
For our pages, we do not want to use `idRoute` -- after all, we want to generate
|
|
|
|
`.html` files, not `.markdown` files or something similar! The [setExtension]
|
|
|
|
route allows you to simply replace the extension of an item, which is what we
|
|
|
|
want here.
|
2011-02-12 19:33:46 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[setExtension]: /reference/Hakyll-Core-Routes.html#v:setExtension
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
~~~~~~{.haskell}
|
|
|
|
compile $ pageCompiler
|
|
|
|
>>> applyTemplateCompiler "templates/default.html"
|
|
|
|
>>> relativizeUrlsCompiler
|
|
|
|
~~~~~~
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
How should we process these pages? A simple compiler such as [pageCompiler],
|
|
|
|
which renders the page, is not enough, we also want to apply our template.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[pageCompiler]: /reference/Hakyll-Web-Page.html#v:pageCompiler
|
|
|
|
|
|
|
|
Different compilers can be chained in a pipeline-like way using Arrows. Arrows
|
|
|
|
form a complicated subject, but fortunately, most Hakyll users need not be
|
|
|
|
concerned with the details. If you are interested, you can find some information
|
|
|
|
on the [Understanding arrows] page -- but the only thing you really *need* to
|
|
|
|
know is that you can chain compilers using the `>>>` operator.
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[Understanding arrows]: http://en.wikibooks.org/wiki/Haskell/Understanding_arrows
|
2011-02-26 10:10:21 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
The `>>>` operator is a lot like a flipped function composition (`flip (.)`) in
|
|
|
|
Haskell, with the important difference that `>>>` is more general and works on
|
|
|
|
all Arrows -- including Hakyll compilers.
|
2011-02-26 10:10:21 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Here, we apply three compilers sequentially:
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
1. We load and render the page using `pageCompiler`
|
|
|
|
2. We apply the template we previously loaded using [applyTemplateCompiler]
|
|
|
|
3. We relativize the URL's on the page using [relativizeUrlsCompiler]
|
2011-02-10 11:30:06 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
[applyTemplateCompiler]: /reference/Hakyll-Web-Template.html#v:applyTemplateCompiler
|
|
|
|
[relativizeUrlsCompiler]: /reference/Hakyll-Web-RelativizeUrls.html#v:relativizeUrlsCompiler
|
2011-02-12 19:33:46 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
Relativizing URL's is a very handy feature. It means that we can just use
|
|
|
|
absolute URL's everywhere in our templates and code, e.g.:
|
2011-02-10 11:30:06 +00:00
|
|
|
|
|
|
|
~~~~~{.haskell}
|
2011-06-11 17:24:36 +00:00
|
|
|
<link rel="stylesheet" type="text/css" href="/css/default.css" />
|
2011-02-10 11:30:06 +00:00
|
|
|
~~~~~
|
2011-02-26 10:07:46 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
And Hakyll will translate this to a relative URL for each page. This means we
|
|
|
|
can host our site at `example.com` and `example.com/subdir` without changing a
|
|
|
|
single line of code.
|
2011-02-28 09:36:38 +00:00
|
|
|
|
2011-06-11 17:24:36 +00:00
|
|
|
More tutorials are in the works...
|
2011-02-28 09:36:38 +00:00
|
|
|
|
|
|
|
Various tips and tricks
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
### Syntax highlighting
|
|
|
|
|
|
|
|
Syntax highlighting is enabled by default in Hakyll. However, you also need to
|
|
|
|
enable it in pandoc. If no syntax highlighting shows up, try
|
|
|
|
|
|
|
|
[jasper@phoenix] cabal install --reinstall -fhighlighting pandoc
|
|
|
|
|
|
|
|
### When to rebuild
|
|
|
|
|
|
|
|
If you execute a `./hakyll build`, Hakyll will build your site incrementally.
|
|
|
|
This means it will be very fast, but it will not pick up _all_ changes.
|
|
|
|
|
|
|
|
- In case you edited `hakyll.hs`, you first want to compile it again.
|
|
|
|
- It is generally recommended to do a `./hakyll rebuild` before you deploy your
|
|
|
|
site.
|
|
|
|
|
|
|
|
After rebuilding your site, all files will look as "modified" to the filesystem.
|
|
|
|
This means that when you upload your site, it will usually transfer all files --
|
|
|
|
this can generate more traffic than necessary, since it is possible that some
|
|
|
|
files were not actually modified. If you use `rsync`, you can counter this using
|
|
|
|
the `--checksum` option.
|
2011-03-07 14:20:26 +00:00
|
|
|
|
|
|
|
Problems
|
|
|
|
--------
|
|
|
|
|
2011-05-18 13:57:13 +00:00
|
|
|
### regex-pcre dependency on Mac OS
|
2011-03-07 14:20:26 +00:00
|
|
|
|
2011-05-18 13:57:13 +00:00
|
|
|
Hakyll requires [regex-pcre], which might fail to build on Mac OS. To solve
|
2011-03-07 14:25:28 +00:00
|
|
|
this problem, make sure the [pcre] C library is installed (via homebrew or
|
|
|
|
macports). Then install [regex-pcre] using:
|
|
|
|
|
|
|
|
cabal install --extra-include-dirs=/usr/local/include regex-pcre
|
2011-03-07 14:20:26 +00:00
|
|
|
|
2011-03-08 12:05:01 +00:00
|
|
|
or
|
|
|
|
|
|
|
|
cabal install --extra-include-dirs=/opt/local/include regex-pcre
|
|
|
|
|
2011-03-07 14:25:28 +00:00
|
|
|
...and proceed to install Hakyll the regular way.
|
2011-03-07 14:20:26 +00:00
|
|
|
|
|
|
|
[regex-pcre]: http://hackage.haskell.org/package/regex-pcre
|
|
|
|
[pcre]: http://www.pcre.org/
|
2011-05-18 13:57:13 +00:00
|
|
|
|
|
|
|
### "File name does not match module name" on Mac OS
|
|
|
|
|
|
|
|
Hakyll.hs:1:1:
|
|
|
|
File name does not match module name:
|
|
|
|
Saw: `Main'
|
|
|
|
Expected: `Hakyll'
|
|
|
|
|
|
|
|
Is an error encountered on Mac OS when `hakyll.hs` is located on a
|
|
|
|
case-insensitive filesystem. A workaround is to rename it to something that
|
|
|
|
isn't the name of the module, for example, `site.hs`.
|