hakyll/web/tutorials/02-basics.markdown

268 lines
8.9 KiB
Markdown
Raw Normal View History

2011-02-12 14:16:42 +00:00
---
2011-06-13 16:26:04 +00:00
title: The basics
2011-10-09 10:36:35 +00:00
author: Jasper Van der Jeugt
2011-02-12 14:16:42 +00:00
---
2011-02-10 11:30:06 +00:00
2012-12-14 11:12:28 +00:00
Building and cleaning
---------------------
2011-02-10 11:30:06 +00:00
2012-12-14 11:12:28 +00:00
If you followed along with the previous tutorial, you should now have the
example site up and running. By running `./site build`, you created two
directories:
2011-06-13 06:51:25 +00:00
2012-12-14 11:12:28 +00:00
- `_site`, with your site as HTML files, ready to be deployed;
- `_cache`, which Hakyll uses internally.
2011-06-13 06:51:25 +00:00
2012-12-14 11:12:28 +00:00
`./site clean` removes these directories, and `./site rebuild` performs a
`clean` and then a `build`.
2011-06-13 06:51:25 +00:00
2012-12-14 11:12:28 +00:00
In general, it's only necessary to use `rebuild` when you made changes to your
`site.hs`, and not when you just made changes to the contents of your website.
2011-05-29 13:34:37 +00:00
2012-12-14 11:12:28 +00:00
Basic rules
-----------
2011-05-29 13:34:37 +00:00
2012-12-14 11:12:28 +00:00
TODO
2011-05-30 14:22:35 +00:00
2011-06-13 16:26:04 +00:00
## Images
2011-05-30 14:22:35 +00:00
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-06-13 16:26:04 +00:00
## CSS
2011-05-30 14:22:35 +00:00
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-13 16:26:04 +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>
2011-06-13 16:26:04 +00:00
<title>Hakyll Example - $$title$$</title>
2011-06-11 17:24:36 +00:00
</head>
<body>
2011-06-13 16:26:04 +00:00
<h1>$$title$$</h1>
2011-06-11 17:24:36 +00:00
2011-06-13 16:26:04 +00:00
$$body$$
2011-06-11 17:24:36 +00:00
</body>
</html>
2011-02-10 11:30:06 +00:00
~~~~~
2012-04-16 11:06:11 +00:00
A template is a text file to lay out some content. The content it lays out is
2011-06-11 17:24:36 +00:00
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
2011-06-13 16:26:04 +00:00
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-13 09:44:55 +00:00
We need to compile the template because we will need it later. If we compile a
page later using `templates/default.html`, Hakyll needs to know what
`templates/default.html` is. Note that we could move template compilation to the
bottom of our code. The order doesn't matter -- Hakyll will determine that for
you. But if you don't compile `templates/default.html` as a template, Hakyll
will not be able to take it into account when deciding the compilation order.
So, the `compile` needs to be there -- but why don't we set a `route` here?
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-13 16:26:04 +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-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-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-06-11 17:24:36 +00:00
~~~~~~
So, I decided to create a site using Hakyll and...
~~~~~~
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 18:32:26 +00:00
How should we process these pages? [pageCompiler] is the default compiler for
pages. [pageCompiler] does a few things:
- It parses the page into body and metadata
2011-06-13 16:26:04 +00:00
- It adds some extra metadata fields such as `$$url$$` and `$$path$$` (you
shouldn't worry about these for now)
- It fill in possible `$$key$$`'s in it's own body
2011-06-11 18:32:26 +00:00
- It renders the page using pandoc
Which basically means that we end up with a `Page` that has the HTML content we
want as body. But we don't just want the plain content on our website -- we want
to decorate it with a template, for starters.
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-06-13 09:44:55 +00:00
Using the [relativizeUrlsCompiler], Hakyll will change this to:
~~~~~{.haskell}
<link rel="stylesheet" type="text/css" href="css/default.css" />
~~~~~
when we are compiling `index.html`, or
~~~~~{.haskell}
<link rel="stylesheet" type="text/css" href="../css/default.css" />
~~~~~
when we are compiling (some imaginary) `posts/foo.html`. So 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...