2019-08-12 17:05:47 +03:00
|
|
|
![Logo](https://raw.githubusercontent.com/srid/rib/master/assets/rib.png)
|
2019-08-09 20:27:48 +03:00
|
|
|
|
|
|
|
# rib
|
|
|
|
|
2019-08-12 17:05:47 +03:00
|
|
|
[![BSD3](https://img.shields.io/badge/License-BSD-blue.svg)](https://en.wikipedia.org/wiki/BSD_License)
|
|
|
|
[![Hackage](https://img.shields.io/hackage/v/rib.svg)](https://hackage.haskell.org/package/rib)
|
2019-11-24 01:15:32 +03:00
|
|
|
[![built with nix](https://builtwithnix.org/badge.svg)](https://builtwithnix.org)
|
2019-08-12 17:05:47 +03:00
|
|
|
|
2019-08-09 20:27:48 +03:00
|
|
|
Rib is a Haskell library for writing your own **static site generator**.
|
|
|
|
|
|
|
|
How does it compare to Hakyll?
|
|
|
|
|
|
|
|
- Use the [Shake](https://shakebuild.com/) build system
|
|
|
|
- Builtin support for using Haskell DSL to define the HTML
|
|
|
|
([Lucid](https://chrisdone.com/posts/lucid2/)) & CSS
|
|
|
|
([Clay](http://fvisser.nl/clay/)) of your site
|
|
|
|
- Like Hakyll, Rib uses [Pandoc](https://pandoc.org/) for parsing the source
|
2019-11-24 03:07:39 +03:00
|
|
|
documents. It also supports [MMark](https://github.com/mmark-md/mmark) if you need a lightweight alternative.
|
|
|
|
- Remain as simple as possible to use (see example below)
|
2019-08-09 20:27:48 +03:00
|
|
|
- Optional Nix based workflow for easily reproducible environment
|
|
|
|
|
|
|
|
Rib prioritizes the use of *existing* tools over reinventing them, and enables
|
|
|
|
the user to compose them as they wish instead of having to write code to fit a
|
|
|
|
custom framework.
|
|
|
|
|
|
|
|
Here is how your code may look like if you were to generate your static site
|
|
|
|
using Rib:
|
|
|
|
|
2019-11-24 04:40:52 +03:00
|
|
|
``` haskell
|
|
|
|
-- First we shall define two datatypes to represent our pages. One, the page
|
|
|
|
-- itself. Second, the metadata associated with each document.
|
|
|
|
|
2019-11-24 03:09:31 +03:00
|
|
|
-- | A generated page is either an index of documents, or an individual document.
|
|
|
|
data Page doc
|
|
|
|
= Page_Index [Document doc]
|
|
|
|
| Page_Doc (Document doc)
|
|
|
|
|
|
|
|
-- | Type representing the metadata in our Markdown documents
|
|
|
|
--
|
|
|
|
-- Note that if a field is not optional (i.e., not Maybe) it must be present.
|
2019-11-24 04:40:52 +03:00
|
|
|
data Meta
|
|
|
|
= Meta
|
|
|
|
{ title :: Text,
|
|
|
|
description :: Maybe Text
|
|
|
|
}
|
2019-11-24 03:09:31 +03:00
|
|
|
deriving (Show, Eq, Generic, FromJSON)
|
|
|
|
|
2019-11-24 04:40:52 +03:00
|
|
|
-- | Main entry point to our generator.
|
|
|
|
--
|
|
|
|
-- `Rib.run` handles CLI arguments, and takes three parameters here.
|
|
|
|
--
|
|
|
|
-- 1. Directory `a`, from which static files will be read.
|
|
|
|
-- 2. Directory `b`, under which target files will be generated.
|
|
|
|
-- 3. Shake build action to run.
|
|
|
|
--
|
|
|
|
-- In the shake build action you would expect to use the utility functions
|
|
|
|
-- provided by Rib to do the actual generation of your static site.
|
2019-11-24 03:09:31 +03:00
|
|
|
main :: IO ()
|
|
|
|
main = Rib.run [reldir|a|] [reldir|b|] $ do
|
|
|
|
-- Copy over the static files
|
|
|
|
Rib.buildStaticFiles [[relfile|static/**|]]
|
|
|
|
-- Build individual markdown files, generating .html for each.
|
|
|
|
--
|
|
|
|
-- NOTE: We use TypeApplications to specify the type of the `doc` type
|
|
|
|
-- variable, as used in the `Markup doc` constraint in the functions below.
|
|
|
|
-- There are currently two possible values: `MMark` (if you choose to use the
|
|
|
|
-- `mmark` parser) and `Pandoc` (if using pandoc).
|
|
|
|
posts <- Rib.buildHtmlMulti @MMark [relfile|*.md|] (renderPage . Page_Doc)
|
|
|
|
-- Build an index.html linking to the aforementioned files.
|
2019-11-24 04:40:52 +03:00
|
|
|
Rib.buildHtml [relfile|index.html|]
|
|
|
|
$ renderPage
|
|
|
|
$ Page_Index posts
|
2019-11-24 03:09:31 +03:00
|
|
|
where
|
2019-11-24 04:40:52 +03:00
|
|
|
-- Define your site HTML here
|
|
|
|
renderPage :: Markup doc => Page doc -> Html ()
|
|
|
|
renderPage page = with html_ [lang_ "en"] $ do
|
|
|
|
head_ $ do
|
|
|
|
meta_ [httpEquiv_ "Content-Type", content_ "text/html; charset=utf-8"]
|
|
|
|
title_ $ case page of
|
|
|
|
Page_Index _ -> "My website!"
|
|
|
|
Page_Doc doc -> toHtml $ title $ Rib.getDocumentMeta doc
|
|
|
|
style_ [type_ "text/css"] $ Clay.render pageStyle
|
|
|
|
body_
|
|
|
|
$ with div_ [id_ "thesite"]
|
|
|
|
$ do
|
|
|
|
-- Main content
|
|
|
|
with a_ [href_ "/"] "Back to Home"
|
|
|
|
hr_ []
|
|
|
|
case page of
|
|
|
|
Page_Index docs ->
|
|
|
|
div_ $ forM_ docs $ \doc -> li_ $ do
|
|
|
|
let meta = Rib.getDocumentMeta doc
|
|
|
|
b_ $ with a_ [href_ (Rib.getDocumentUrl doc)] $ toHtml $ title meta
|
|
|
|
case description meta of
|
|
|
|
Just s -> em_ $ small_ $ toHtml s
|
|
|
|
Nothing -> mempty
|
|
|
|
Page_Doc doc ->
|
|
|
|
with article_ [class_ "post"] $ do
|
|
|
|
h1_ $ toHtml $ title $ Rib.getDocumentMeta doc
|
|
|
|
Rib.renderDoc doc
|
|
|
|
-- Define your site CSS here
|
2019-11-24 03:09:31 +03:00
|
|
|
pageStyle :: Css
|
|
|
|
pageStyle = div # "#thesite" ? do
|
|
|
|
marginLeft $ pct 20
|
|
|
|
marginTop $ em 4
|
|
|
|
"h1" ? do
|
|
|
|
fontSize $ em 2.3
|
|
|
|
```
|
|
|
|
|
|
|
|
(View full [`Main.hs`](https://github.com/srid/rib-sample/blob/master/Main.hs) at rib-sample)
|
2019-08-09 20:27:48 +03:00
|
|
|
|
|
|
|
## Getting Started
|
|
|
|
|
|
|
|
The easiest way to get started with [Rib](/) is to [use the
|
|
|
|
template](https://help.github.com/en/articles/creating-a-repository-from-a-template)
|
|
|
|
repository, [**rib-sample**](https://github.com/srid/rib-sample), from Github.
|
|
|
|
|
|
|
|
### Directory structure
|
|
|
|
|
|
|
|
Let's look at what's in the template repository:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
$ git clone https://github.com/srid/rib-sample.git mysite
|
|
|
|
...
|
|
|
|
$ cd mysite
|
|
|
|
$ ls -F
|
2019-10-26 18:50:53 +03:00
|
|
|
a/ default.nix Main.hs README.md rib-sample.cabal
|
2019-08-09 20:27:48 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
The three key items here are:
|
|
|
|
|
|
|
|
1. `Main.hs`: Haskell source containing the DSL of the HTML/CSS of your site.
|
|
|
|
1. `a/`: The source content (eg: Markdown sources and static files)
|
2019-10-26 18:50:53 +03:00
|
|
|
1. `b/`: The target directory, excluded from the git repository, will contain
|
|
|
|
_generated_ content (i.e., the HTML files, and copied over static content)
|
2019-08-09 20:27:48 +03:00
|
|
|
|
|
|
|
The template repository comes with a few sample posts under `a/`, and a basic
|
|
|
|
HTML layout and CSS style defined in `Main.hs`.
|
|
|
|
|
|
|
|
### Run the site
|
2019-07-01 01:06:16 +03:00
|
|
|
|
2019-08-09 20:27:48 +03:00
|
|
|
Now let's run them all.
|
2019-08-03 20:33:48 +03:00
|
|
|
|
2019-08-09 20:27:48 +03:00
|
|
|
Clone the sample repository locally, install [Nix](https://nixos.org/nix/) and
|
|
|
|
run your site as follows:
|
2019-07-02 16:27:37 +03:00
|
|
|
|
2019-08-09 20:27:48 +03:00
|
|
|
```shell
|
|
|
|
nix-shell --run 'ghcid -T main'
|
|
|
|
```
|
|
|
|
|
2019-08-22 03:02:37 +03:00
|
|
|
(Note even though the author recommends it Nix is strictly not required; you may
|
|
|
|
simply run `ghcid -T main` instead of the above command if you do not wish to
|
|
|
|
use Nix.)
|
|
|
|
|
2019-08-09 20:27:48 +03:00
|
|
|
Running this command gives you a local HTTP server at http://localhost:8080/
|
|
|
|
(serving the generated files) that automatically reloads when either the content
|
|
|
|
(`a/`) or the HTML/CSS/build-actions (`Main.hs`) changes. Hot reload, in other
|
|
|
|
words.
|
|
|
|
|
|
|
|
### How Rib works
|
|
|
|
|
|
|
|
How does the aforementioned nix-shell command work?
|
|
|
|
|
|
|
|
1. `nix-shell` will run the given command in a shell environment with all of our
|
|
|
|
dependencies (notably the Haskell ones including the `rib` library itself)
|
|
|
|
installed.
|
|
|
|
|
|
|
|
1. [`ghcid`](https://github.com/ndmitchell/ghcid) will compile your `Main.hs`
|
|
|
|
and run its `main` function.
|
|
|
|
|
2019-11-24 03:07:39 +03:00
|
|
|
1. `Main.hs:main` in turn calls `Rib.App.run` which takes as argument your custom Shake action that will build the static site.
|
2019-08-09 20:27:48 +03:00
|
|
|
|
|
|
|
1. `Rib.App.run`: this parses the CLI arguments and runs the rib CLI "app" which
|
|
|
|
can be run in one of a few modes --- generating static files, watching the
|
|
|
|
`a/` directory for changes, starting HTTP server for the `b/` directory. By
|
|
|
|
default---without any explicit arguments---this will run the Shake build
|
|
|
|
action passed as argument on every file change and spin up a HTTP server.
|
|
|
|
|
|
|
|
Run that command, and visit http://localhost:8080 to view your site.
|
|
|
|
|
|
|
|
### Editing workflow
|
|
|
|
|
|
|
|
Now try making some changes to the content, say `a/first-post.md`. You should
|
|
|
|
see it reflected when you refresh the page. Or change the HTML or CSS of your
|
|
|
|
site in `Main.hs`; this will trigger `ghcid` to rebuild the Haskell source and
|
|
|
|
restart the server.
|
|
|
|
|
|
|
|
### What's next?
|
|
|
|
|
|
|
|
Great, by now you should have your static site generator ready and running! What
|
|
|
|
more can you do? Surely you may have specific needs; and this usually translates
|
2019-11-24 03:07:39 +03:00
|
|
|
to running custom Shake actions during the build. Rib provides helper functions in `Rib.Shake` to make this easier.
|
2019-08-09 20:27:48 +03:00
|
|
|
|
2019-11-24 03:07:39 +03:00
|
|
|
Rib recommends writing your Shake actions in the style of being
|
2019-08-09 20:27:48 +03:00
|
|
|
[forward-defined](http://hackage.haskell.org/package/shake-0.18.3/docs/Development-Shake-Forward.html)
|
|
|
|
which adds to the simplicity of the entire thing.
|
2019-08-07 03:55:09 +03:00
|
|
|
|
2019-08-03 20:26:45 +03:00
|
|
|
## Examples
|
2019-07-02 16:27:37 +03:00
|
|
|
|
2019-08-03 20:33:48 +03:00
|
|
|
* [rib-sample](https://github.com/srid/rib-sample): Use this to get started with
|
|
|
|
your own site.
|
2019-07-17 03:06:58 +03:00
|
|
|
|
2019-11-19 23:19:16 +03:00
|
|
|
* Author's own website. Live at https://www.srid.ca/
|