on 877cb21 help sign in
hakyll/web/tutorials/02-basics.markdown
293 lines 11 kB history raw
1---
2title: The basics
3author: Jasper Van der Jeugt
4---
6## Let's get started!
8We're going to discuss a small brochure site to start with. You can find all
9code and files necessary to build this site
10[right here](http://github.com/jaspervdj/hakyll-examples/tree/master/brochure)
11-- feel free to look at them as we go trough the tutorial. To fetch all examples
12in order to play with them locally, use:
14 git clone git://github.com/jaspervdj/hakyll-examples.git
16or navigate to the download menu on GitHub.
18Now, for this first tutorial, there's a number of files we will use:
20 about.rst A simple page written in RST format
21 code.lhs Another page with some code (which can be highlighted)
22 css Directory for CSS files
23 |- default.css The main CSS file
24 \- syntax.css CSS file for code syntax highlighting
25 hakyll.hs Our code to generate the site
26 images Directory for images
27 \- haskell-logo.png The logo of my favorite programming language
28 index.markdown A simple page in markdown format
29 templates Directory for templates
30 \- default.html The main template for the site
32By default, hakyll will compile everything to the `_site` directory. We can try
33this like this:
35 [jasper@phoenix] ghc --make hakyll.hs
36 [jasper@phoenix] ./hakyll build
38Instead of using `build`, we can also use `preview`, which will fire up a
39webserver serving the `_site` directory, so have a look!
41All files have been compiled, and their output has been placed in the `_site`
42directory as illustrated in this diagram:
44![Brochure files](/images/brochure-files.png)
46No magic is involved at all -- we will precisely study how and why our items are
47compiled like that. All of this is specified in the `hakyll.hs` file. You can
48view the full `hakyll.hs` file online [here][brochure-hakyll.hs], or you can
49look in the directory you cloned or downloaded.
51[brochure-hakyll.hs]: http://github.com/jaspervdj/hakyll-examples/blob/master/brochure/hakyll.hs
53## Images
55Let's start of with the `images/haskell-logo.png` file, because the processing
56of this file is very simple: it is simply copied to the output directory. Let's
57look at the relevant lines in the `hakyll.hs` file:
59~~~~~{.haskell}
60match "images/*" $ do
61 route idRoute
62 compile copyFileCompiler
63~~~~~
65The first line specifies we will describe the process for compiling everything
66in the `images/` folder: hakyll uses globs for this [^pattern].
68[^pattern]: A little caveat is that these globs are not `String`s but
69 `Pattern`s, so you need the `OverloadedStrings` extension.
71We can see two simple rules next: [route] and [compile].
73- [route] determines how the input file(s) get mapped to the output files.
74 [route] only deals with file names -- not with the actual content!
75- [compile], on the other hand, determines how the file content is processed.
77[route]: /reference/Hakyll-Core-Rules.html#v:route
78[compile]: /reference/Hakyll-Core-Rules.html#v:compile
80In this case, we select the [idRoute]: which means the file name will be kept
81the same (`_site` will always be prepended automatically). This explains the
82name of [idRoute]: much like the `id` function in Haskell, it also maps values
83to themselves.
85[idRoute]: /reference/Hakyll-Core-Routes.html#v:idRoute
87For our compiler, we use [copyFileCompiler], meaning that we don't process the
88content at all, we just copy the file.
90[copyFileCompiler]: /reference/Hakyll-Core-Writable-CopyFile.html#v:copyFileCompiler
92## CSS
94If we look at how the two CSS files are processed, we see something which looks
95very familiar:
97~~~~~{.haskell}
98match "css/*" $ do
99 route idRoute
100 compile compressCssCompiler
101~~~~~
103Indeed, the only difference with the images is that have now chosen for
104[compressCssCompiler] -- a compiler which *does* process the content. Let's have
105a quick look at the type of [compressCssCompiler]:
107[compressCssCompiler]: /reference/Hakyll-Web-CompressCss.html#v:compressCssCompiler
109~~~~~{.haskell}
110compressCssCompiler :: Compiler Resource String
111~~~~~
113Intuitively, we can see this as a process which takes a `Resource` and produces
114a `String`.
116- A `Resource` is simply the Hakyll representation of an item -- usually just a
117 file on the disk.
118- The produced string is the processed CSS.
120We can wonder what Hakyll does with the resulting `String`. Well, it simply
121writes this to the file specified in the `route`! As you can see, routes and
122compilers work together to produce your site.
124## Templates
126Next, we can see that the templates are compiled:
128~~~~~{.haskell}
129match "templates/*" $ compile templateCompiler
130~~~~~
132Let's start with the basics: what is a template? An example template gives us a
133good impression:
135~~~~~
136<html>
137 <head>
138 <title>Hakyll Example - $$title$$</title>
139 </head>
140 <body>
141 <h1>$$title$$</h1>
143 $$body$$
144 </body>
145</html>
146~~~~~
148A template is a text file to lay out some content. The content it lays out is
149called a page -- we'll see that in the next section. The syntax for templates is
150intentionally very simplistic. You can bind some content by referencing the name
151of the content *field* by using `$$field$$`, and that's it.
153You might have noticed how we specify a compiler (`compile`), but we don't set
154any `route`. Why is this?
156We need to compile the template because we will need it later. If we compile a
157page later using `templates/default.html`, Hakyll needs to know what
158`templates/default.html` is. Note that we could move template compilation to the
159bottom of our code. The order doesn't matter -- Hakyll will determine that for
160you. But if you don't compile `templates/default.html` as a template, Hakyll
161will not be able to take it into account when deciding the compilation order.
163So, the `compile` needs to be there -- but why don't we set a `route` here?
164Precisely because we don't want to our template to end up anywhere in our site
165directory! We want to use it to lay out other items -- so we need to load
166(compile) it, but we don't want to give it a real destination.
168By using the `templates/*` pattern, we compile all templates in one go.
170## Pages
172The code for pages looks suspiciously more complicated:
174~~~~~~{.haskell}
175match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
176 route $ setExtension "html"
177 compile $ pageCompiler
178 >>> applyTemplateCompiler "templates/default.html"
179 >>> relativizeUrlsCompiler
180~~~~~~
182But we'll see shortly that this actually fairly straightforward. Let's begin by
183exploring what a *page* is.
185~~~~~~
186---
187title: Home
188author: Jasper
189---
191So, I decided to create a site using Hakyll and...
192~~~~~~
194A page consists of two parts: a body, and metadata. As you can see above, the
195syntax is not hard. The metadata part is completely optional, this is the same
196page without metadata:
198~~~~~~
199So, I decided to create a site using Hakyll and...
200~~~~~~
202Hakyll supports a number of formats for the page body. Markdown, HTML and RST
203are probably the most common. Hakyll will automatically guess the right format
204if you use the right extension for your page.
206~~~~~~{.haskell}
207match (list ["about.rst", "index.markdown", "code.lhs"]) $ do
208~~~~~~
210We see a more complicated pattern here. Some sets of files cannot be described
211easily by just one pattern, and here the [list] function can help us out. In
212this case, we have three specific pages we want to compile.
214[list]: /reference/Hakyll-Core-Identifier-Pattern.html#v:list
216~~~~~~{.haskell}
217route $ setExtension "html"
218~~~~~~
220For our pages, we do not want to use `idRoute` -- after all, we want to generate
221`.html` files, not `.markdown` files or something similar! The [setExtension]
222route allows you to simply replace the extension of an item, which is what we
223want here.
225[setExtension]: /reference/Hakyll-Core-Routes.html#v:setExtension
227~~~~~~{.haskell}
228compile $ pageCompiler
229 >>> applyTemplateCompiler "templates/default.html"
230 >>> relativizeUrlsCompiler
231~~~~~~
233How should we process these pages? [pageCompiler] is the default compiler for
234pages. [pageCompiler] does a few things:
236- It parses the page into body and metadata
237- It adds some extra metadata fields such as `$$url$$` and `$$path$$` (you
238 shouldn't worry about these for now)
239- It fill in possible `$$key$$`'s in it's own body
240- It renders the page using pandoc
242Which basically means that we end up with a `Page` that has the HTML content we
243want as body. But we don't just want the plain content on our website -- we want
244to decorate it with a template, for starters.
246[pageCompiler]: /reference/Hakyll-Web-Page.html#v:pageCompiler
248Different compilers can be chained in a pipeline-like way using Arrows. Arrows
249form a complicated subject, but fortunately, most Hakyll users need not be
250concerned with the details. If you are interested, you can find some information
251on the [Understanding arrows] page -- but the only thing you really *need* to
252know is that you can chain compilers using the `>>>` operator.
254[Understanding arrows]: http://en.wikibooks.org/wiki/Haskell/Understanding_arrows
256The `>>>` operator is a lot like a flipped function composition (`flip (.)`) in
257Haskell, with the important difference that `>>>` is more general and works on
258all Arrows -- including Hakyll compilers.
260Here, we apply three compilers sequentially:
2621. We load and render the page using `pageCompiler`
2632. We apply the template we previously loaded using [applyTemplateCompiler]
2643. We relativize the URL's on the page using [relativizeUrlsCompiler]
266[applyTemplateCompiler]: /reference/Hakyll-Web-Template.html#v:applyTemplateCompiler
267[relativizeUrlsCompiler]: /reference/Hakyll-Web-RelativizeUrls.html#v:relativizeUrlsCompiler
269Relativizing URL's is a very handy feature. It means that we can just use
270absolute URL's everywhere in our templates and code, e.g.:
272~~~~~{.haskell}
273<link rel="stylesheet" type="text/css" href="/css/default.css" />
274~~~~~
276Using the [relativizeUrlsCompiler], Hakyll will change this to:
278~~~~~{.haskell}
279<link rel="stylesheet" type="text/css" href="css/default.css" />
280~~~~~
282when we are compiling `index.html`, or
284~~~~~{.haskell}
285<link rel="stylesheet" type="text/css" href="../css/default.css" />
286~~~~~
288when we are compiling (some imaginary) `posts/foo.html`. So Hakyll will
289translate this to a relative URL for each page. This means we can host our site
290at `example.com` and `example.com/subdir` without changing a single line of
291code.
293More tutorials are in the works...