Some more changes for the 0.3 release.
This commit is contained in:
parent
4561887cf4
commit
533f2c3b60
2 changed files with 16 additions and 215 deletions
215
README.markdown
215
README.markdown
|
@ -9,216 +9,9 @@ reference.
|
|||
|
||||
## Installation
|
||||
|
||||
cabal install hakyll
|
||||
cabal install hakyll
|
||||
|
||||
## Configuration
|
||||
## Tutorial and information
|
||||
|
||||
Inspired by [xmonad](http://xmonad.org), a small Haskell program is used as
|
||||
configuration file. In this file, you give instructions on how the site should
|
||||
be generated. In the rest of this document, we will examine a small example.
|
||||
|
||||
This is our directory layout:
|
||||
|
||||
|-- _cache
|
||||
|-- _site
|
||||
|-- favicon.ico
|
||||
|-- hakyll.hs
|
||||
|-- images
|
||||
| `-- foo.png
|
||||
|-- templates
|
||||
| |-- default.html
|
||||
| `-- sample.html
|
||||
`-- text.markdown
|
||||
|
||||
The `_cache` and `_site` directories will be automatically created by hakyll.
|
||||
|
||||
## Static files
|
||||
|
||||
Static files can be rendered using the `static` function. This function
|
||||
ensures the files will copied when you compile the site. Hakyll is smart enough
|
||||
to know when files have changed, and will check the modification time of a file
|
||||
before copying.
|
||||
|
||||
For convenience reasons, there is also a `staticDirectory` function, which works
|
||||
recursively.
|
||||
|
||||
main = do
|
||||
static "favicon.ico"
|
||||
staticDirectory "images"
|
||||
staticDirectory "css"
|
||||
|
||||
## Pages
|
||||
|
||||
Pages can be written in html, markdown, LaTeX, and basically anything pandoc
|
||||
supports. They can also contain metadata, which are always key-value mappings.
|
||||
|
||||
---
|
||||
author: Jasper Van der Jeugt
|
||||
title: A sample markdown post
|
||||
---
|
||||
# A sample markdown post
|
||||
|
||||
This is a sample markdown post. It supports pandoc extensions
|
||||
like code highlighting. For example:
|
||||
|
||||
~~~~{.haskell}
|
||||
main = putStrLn "Hello World!"
|
||||
~~~~
|
||||
|
||||
Metadata is always placed in the header of a file, and is delimited by a `---`
|
||||
string. The metadata can only contain simple key-value pairs. We can now read
|
||||
in this page using the `Text.Hakyll.Page.readPage` function. This will return a
|
||||
`Page`, which is actually just a `Map String ByteString`. In this example, the
|
||||
map would consist of the following key-value pairs:
|
||||
|
||||
- `author`: `Jasper Van der Jeugt`
|
||||
- `title`: `A sample markdown post`
|
||||
- `body`: The rest of the file (rendered to html).
|
||||
- `url`: `text.html` (the original filename was `text.markdown`, the extension
|
||||
was changed to html).
|
||||
|
||||
## Templates
|
||||
|
||||
In hakyll, there is a strict separation between pages and templates. Templates,
|
||||
for example, cannot contain metadata.
|
||||
|
||||
<h2> $title </h2>
|
||||
by <strong> $author </strong>
|
||||
|
||||
$body
|
||||
|
||||
Templates are rendered using the Haskell `Text.Template` library. This means
|
||||
that in your template, you can use `$identifier`, and it will be replaced by
|
||||
the value of `identifier`.
|
||||
|
||||
With this template we could, for example, render the file we saw in the
|
||||
previous section. It would go like this:
|
||||
|
||||
page <- readPage "text.markdown"
|
||||
rendered <- render "templates/sample.html" page
|
||||
writePage rendered
|
||||
|
||||
This reads in `text.markdown`, renders it and writes it to the site destination
|
||||
(`_site/text.html`). The result of a `render` action is an `IO Page`, the
|
||||
metadata will be copied from the original page, and the body will be replaced by
|
||||
the rendering result. This means we can combine rendering actions. Given another
|
||||
template `templates/default.html`:
|
||||
<html>
|
||||
<head>
|
||||
<title> $title </title>
|
||||
</head>
|
||||
<body>
|
||||
$body
|
||||
</body>
|
||||
</html>
|
||||
|
||||
We can now combine the rendering actions (I use `>>=` notation here):
|
||||
readPage "text.markdown" >>=
|
||||
render "templates/sample.html" >>=
|
||||
render "templates/default.html" >>=
|
||||
writePage
|
||||
|
||||
Jolly good fun and all that, but you can imagine that when we render over nine
|
||||
thousand posts, our generator will be busy for a while. That's why we have the
|
||||
`depends` function. It takes a url and a list of dependencies as arguments, and
|
||||
an IO action. The trick is that this IO action will only be executed if any of
|
||||
the dependencies is newer than the url given. In our example, we would write:
|
||||
|
||||
depends "text.html" ["text.markdown", "templates/sample.html", "templates/default.html"]
|
||||
(readPage "text.markdown" >>= render "templates/sample.html" >>=
|
||||
render "templates/default.html" >>= writePage)
|
||||
|
||||
Not exactly the prettiest code I've ever seen. Because rendering a page with a
|
||||
number of templates is very common, there's a `renderChain` function to do this
|
||||
for us. The above can be replaced by
|
||||
|
||||
renderChain ["templates/sample.html", "templates/default.html"] $
|
||||
createPagePath "text.markdown"
|
||||
|
||||
The `renderChain` function will automatically check dependencies and write the
|
||||
page. In fact, it is recommended that you __always__ use this, even when there's
|
||||
only one template in the chain.
|
||||
|
||||
## More advanced things
|
||||
|
||||
Sometimes, you want to create a `Page` from scratch, without reading from a
|
||||
file. There are functions to do that for you, and I suggest you read the
|
||||
documentation of `Text.Hakyll.Page`. As a more advanced example, I will explain
|
||||
the RSS system I wrote for my website.
|
||||
|
||||
|-- generate.hs
|
||||
|-- posts
|
||||
| `-- 2009-12-02-a-first-post.markdown
|
||||
`-- templates
|
||||
|-- rss.xml
|
||||
`-- rssitem.xml
|
||||
|
||||
Our post contains some metadata:
|
||||
|
||||
---
|
||||
title: A first post
|
||||
date: December 2, 2009
|
||||
about: A post describing the why and the how of the technical setup of this blog.
|
||||
---
|
||||
|
||||
# A first post
|
||||
|
||||
A first post describing the technical setup of this blog, for that is
|
||||
|
||||
The `templates/rssitem.xml` file is a template for rendering one post to an rss
|
||||
item:
|
||||
|
||||
<item>
|
||||
<title> $title </title>
|
||||
<link>http://jaspervdj.be/$url</link>
|
||||
<description> $about </description>
|
||||
</item>
|
||||
|
||||
Now a template for rendering the whole rss feed, `templates/rss.xml`:
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>jaspervdj - a personal blog</title>
|
||||
<link>http://jaspervdj.be/</link>
|
||||
<description>Personal blog of jaspervdj</description>
|
||||
$items
|
||||
</channel>
|
||||
</rss>
|
||||
|
||||
Alright, let's get coding. We first want a list of all posts, sorted so the most
|
||||
recent entries are first in the list.
|
||||
|
||||
postPaths <- liftM (L.reverse . L.sort) $ getRecursiveContents "posts"
|
||||
|
||||
Note that this sorting works because the posts have a
|
||||
`yyyy-mm-dd-title.extension` naming scheme. We want the paths as `PagePath` and
|
||||
not as `FilePath` or `String`, because `PagePath` is an instance of
|
||||
`Renderable`. Also, we only want the 5 most recent posts.
|
||||
|
||||
let renderablePosts = map createPagePath postPaths
|
||||
let recentItems = renderAndConcat "templates/postitem.html" $ take 5 renderablePosts
|
||||
|
||||
The `renderAndConcat` function takes a template and a list of `Renderable`
|
||||
items. It renders all renderables with the given template, and concatenates the
|
||||
result. Note that the concating and reading of pages is not executed yet,
|
||||
because of laziness. This helps us, since we want to use the modification
|
||||
timestamps of files, and not render everything every time.
|
||||
|
||||
let rssPage = createCustomPage "rss.xml"
|
||||
("templates/rssitem.xml" : postPaths) [("items", Right recentItems)]
|
||||
|
||||
We now created the custom rss page. The `createCustomPage` is a function that
|
||||
produces a `CustomPage`, which is an instance of `Renderable`. `"rss.xml"` is
|
||||
our destination url. We then give a list of extra dependencies that were used
|
||||
to generate the custom page, so Hakyll can check modification stamps. The last
|
||||
argument is the key-value mapping of our `CustomPage`. Note that the type for
|
||||
values is `Either String (IO ByteString)`. So, we can either give a simple
|
||||
string, or an IO action that results in a `ByteString`. The benefit of this is
|
||||
that the IO action will not be executed if the page in `_site/` is already
|
||||
up-to-date. Now, we only need to render it using our rss template.
|
||||
|
||||
renderChain ["templates/rss.xml"] rssPage
|
||||
|
||||
That's it. Now we have a rss feed that is generated only when it is not already
|
||||
up-to-date.
|
||||
More information can be found on
|
||||
[the hakyll homepage](http://jaspervdj.be/hakyll).
|
||||
|
|
16
hakyll.cabal
16
hakyll.cabal
|
@ -1,5 +1,5 @@
|
|||
Name: hakyll
|
||||
Version: 0.2
|
||||
Version: 0.3
|
||||
|
||||
Synopsis: A simple static site generator library.
|
||||
Description:
|
||||
|
@ -7,7 +7,7 @@ Description:
|
|||
creating blogs.
|
||||
Author: Jasper Van der Jeugt
|
||||
Maintainer: jaspervdj@gmail.com
|
||||
Homepage: http://github.com/jaspervdj/Hakyll
|
||||
Homepage: http://jaspervdj.be/hakyll
|
||||
License: BSD3
|
||||
License-File: LICENSE
|
||||
Category: Text
|
||||
|
@ -18,8 +18,16 @@ build-type: Simple
|
|||
library
|
||||
ghc-options: -Wall
|
||||
hs-source-dirs: src/
|
||||
build-depends: base >= 4 && < 5, template, filepath, directory, containers, bytestring,
|
||||
pandoc >= 1, regex-compat, network, mtl
|
||||
build-depends: base >= 4 && < 5,
|
||||
template >= 0.1,
|
||||
filepath >= 1.1,
|
||||
directory >= 1,
|
||||
containers >= 0.1,
|
||||
bytestring >= 0.9,
|
||||
pandoc >= 1,
|
||||
regex-compat >= 0.92,
|
||||
network >= 2,
|
||||
mtl >= 1.1
|
||||
exposed-modules: Text.Hakyll
|
||||
Text.Hakyll.Render
|
||||
Text.Hakyll.Renderable
|
||||
|
|
Loading…
Reference in a new issue