     [file version.texi not included]

Copyright © 2015-2021 David Thompson

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A copy of the license is included in the section entitled ``GNU Free Documentation License''.

Haunt
*****

This document describes Haunt version VERSION, an extensible, functional static site generator.

Introduction
************

Haunt is a hackable static site generator written in Guile Scheme.  A static site generator assists an author with generating the HTML pages that they publish on the web.  Unlike ``content management systems'' such as Wordpress or Drupal, static site generators are not dynamic web applications (complete with a relational database) that build pages on-the-fly.  Instead, web pages are built in advance, on the author's computer, and copied to a web server when it is time to publish changes.  The consequence of this design is that the web server no longer needs to run a complex, potentially insecure web application that connects to a database to retrieve data.  Static files can be served easily by any generic web server.  Since there is no web application or database server to deal with, static websites are easier to maintain, more secure, and resistant to high web traffic (``slashdotting.'')  Furthermore, the entire website is stored in plain text, which allows the files to be version-controlled rather than kept in a relational database with no concept of history that needs to be backed up regularly.

At the time that Haunt was conceived, there existed literally hundreds of other static site generators.  Why add another one?  Haunt differentiates itself from most other static site generators in that it aspires to the Emacs philosophy of ``practical software freedom.'' Not only is the source code available under a Free Software license, as most static site generators are, it is designed to be easily hacked and extended without altering the core source code.  Haunt purposefully blurs the line between document and program, author and programmer, by embracing the notion of data as code.  A Haunt-based website is not simply data, but a computer program.  This design strategy encourages authors to automate repetitive tasks and empowers them to extend the software with their own ideas.

To make such a system work well, a general-purpose, extensible programming language is needed.  A traditional configuration file format simply will not do.  The programming language that we feel is best suited to this task is Scheme, a clean and elegant dialect of Lisp.  We believe that by giving authors the full expressive power of Scheme, they will be able to produce better websites and make better use of their time than with less programmable systems and less capable programming languages.  Authors should feel empowered to hack the system to make it do what they want rather than what some programmer decided they should want.  And perhaps most importantly, building websites with Haunt should be fun.

Websites written in Haunt are described as purely functional programs that accept ``posts'', text documents containing arbitrary metadata, as input and transform them into complete HTML pages using Scheme procedures.  Haunt has no opinion about what markup language authors should use to write their posts and will happily work with any format for which a ``reader'' procedure exists.  Likewise, Haunt also has no opinion about how authors should structure their sites, but has sane defaults.  Currently, there exist helpful ``builder'' procedures that do common tasks such as generating a blog or Atom feed.  While the built-in features may be enough for some, they surely will not be enough for all.  Haunt's Scheme API empowers authors to easily tweak existing components, write replacements, or add entirely new features that do things no else has thought to do yet.

Happy haunting!

Installation
************

Downloading
===========

Official Haunt source code release tarballs can be found under Releases in Haunt's website (https://dthompson.us/projects/haunt.html), along with their associated checksums.

Requirements
============

Haunt depends on the following packages:

  * GNU Guile (https://gnu.org/software/guile) version 2.0.11 or later

The following dependencies are optional:

  * Guile-Reader (http://www.nongnu.org/guile-reader/) version 0.6 or later (for Skribe support)

  * guile-commonmark (https://github.com/OrangeShark/guile-commonmark) version 0.1 or later (for CommonMark support)

  * rsync (https://rsync.samba.org/) version 0.2 or later (for rsync publishing)

  * hut (https://sr.ht/~emersion/hut/) version 0.2 or later (for Sourcehut publishing)

  * Tar (https://www.gnu.org/software/tar/) (for Sourcehut publishing)

Building
========

Haunt uses the standard GNU build system, so the basic installation procedure looks like this:

     ./configure
     make
     make install

This will install Haunt to /usr/local/.  This is not part of the default load path for GNU Guile if you installed it from your distribution's package manager (/usr is.) You may choose to change the prefix to your GNU Guile's location with ./configure --prefix=/usr or add /usr/local/ to GNU Guile's load path in your shell environment like this (replacing 3.0 with your GNU Guile version):

     export GUILE_LOAD_PATH="/usr/local/share/guile/site/3.0\
     ${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH"

     export GUILE_LOAD_COMPILED_PATH="/usr/local/lib/guile/3.0/site-ccache\
     ${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_COMPILED_LOAD_PATH"

(see Environment Variables (GNU Guile Reference Manual) for more details on GUILE_LOAD_PATH and GUILE_LOAD_COMPILED_PATH.)

Tutorial
********

The goal of this tutorial is to quickly create a barebones blog with Haunt in order to demonstrate the basic workflow and key concepts.

First, create a directory for the new site:

     mkdir haunt-tutorial
     cd haunt-tutorial

Next, create the site configuration file haunt.scm.  This is where all of the code for building the website will go.

Here's what a simple Haunt configuration looks like:

     (use-modules (haunt asset)
                  (haunt site)
                  (haunt builder blog)
                  (haunt builder atom)
                  (haunt reader skribe))

     (site #:title "My First Haunt Site"
           #:domain "example.com"
           #:default-metadata
           '((author . "Eva Luator")
             (email  . "eva@example.com"))
           #:readers (list skribe-reader)
           #:builders (list (blog)
                            (atom-feed)
                            (atom-feeds-by-tag)))

Haunt represents the full configuration of the website using the site procedure.  Site objects specify things like the site title, the default metadata to use for posts, which markup formats are supported, and which builders are used to generate web pages.

With the above code saved into the haunt.scm file, the next step is to create a posts directory and populate it with articles to publish.  Put the text below into a file named posts/hello.skr:

     (post
      :title "Hello, World!"
      :date (make-date* 2015 10 15)
      :tags '("hello")

      (h1 [Hello, World!])

      (p [This is my very first Skribe document!]))

This is a Skribe (http://www.nongnu.org/skribilo/doc/user-3.html#skribe-syntax) document.  Skribe is one of the built-in languages that Haunt knows how to work with.  It's basically Scheme, but with support for writing literal text without quoting it all by enclosing it in square brackets.  The code above defines a post named ``Hello, World!'' with a publishing date of 2015-10-15, whose contents are just a single heading and a paragraph.

To build the site, run haunt build to compile all of the HTML pages.  To view the results, run haunt serve and visit http://localhost:8080 in a web browser.  haunt serve is a handy utility that serves the contents of the website using Guile's built-in HTTP server.  Since the blog builder was specified in haunt.scm, the default index page is a simple listing of all posts, which for now is a single post.  Clicking on the post title will display a page with only that post's contents.

In addition to the basic blog builder, the haunt.scm file specifies two additional builders for Atom feeds.  The atom-feed builder creates a feed of all posts located at http://localhost:8080/feed.xml.  The atom-feeds-by-tag builder creates one feed for each unique tag specified in the post metadata.  There's only one tag right now, ``hello'', and its feed is located at http://localhost/feeds/tags/hello.xml.

Tweaking a post, rebuilding the site, and viewing the results in a web browser is the typical Haunt workflow.  However, having to run haunt build every after each edit is tedious.  To address this, run haunt serve --watch.  The Haunt web server, in addition to serving web pages, will now watch for changes to important files and automatically rebuild the site when they are edited.  This streamlines the workflow into an edit, save, view loop.

Now that we've introduced the basic utilities and concepts, continue reading this manual to learn more about Haunt's command-line and programming interfaces.

Command-line Interface
**********************

The Haunt command-line interface is composed of many subcommands.  The general syntax for all Haunt commands is:

     haunt SUBCOMMAND OPTIONS...

Invoking haunt build
====================

The haunt build command realizes a Haunt site configuration file by compiling web pages and copying static assets to the output directory.  For details on how to configure a Haunt site, see Sites.

Example:

     haunt build --config=haunt.scm

--config=CONFIGURATION-FILE
-c CONFIGURATION-FILE
     Load the Haunt site declaration from CONFIGURATION-FILE.

Invoking haunt serve
====================

The haunt serve command allows one to quickly view a local preview of the generated website before publishing the finished product to a remote web server.  When haunt serve runs, a local HTTP server is spawned.  Visit the server using a web browser to inspect the results of the build.  By default, the web server listens on port 8080, so the URL to visit would be http://localhost:8080.

While developing, it is common to rebuild the site frequently to view the results of incremental changes.  Rather than manually running haunt build (Invoking haunt build) each time changes are made, the --watch flag can be used to automatically rebuild the site when a source file changes on disk.

--config=CONFIGURATION-FILE
-c CONFIGURATION-FILE
     Load the Haunt site declaration from CONFIGURATION-FILE.

--port=PORT
-p PORT

     Listen for HTTP requests on PORT.  Defaults to 8080.

--host=HOST
-p HOST

     Listen for HTTP requests on HOST.  Accepts an IP address (IPv4 or IPv6), localhost or loopback to serve on the local loopback device (the default), or any to bind on all local available ports (useful if you want to show off your website to someone else on your LAN, or something.)

--watch
-w

     Automatically rebuild the site when source files change.

Invoking haunt new
==================

The haunt new command creates a starter template for a new website.

Usage:

     haunt new

By default, haunt will create a directory called blog.  To use a different name for your new blog, simply provide the name at the command line as follows:

     haunt new my-blog

Invoking haunt publish
======================

The haunt publish command deploys the site to a remote location.  The site must have already been built via haunt build before running haunt publish.

Usage:

     haunt publish

By default, Haunt uses the publisher named production defined for the current site.  However, Haunt supports multiple publishers. Consider the use case where an author wants to publish to a staging environment to confirm that everything looks good on a real web server before publishing to production.  To use a non-production publisher, simply say so at the command line:

     haunt publish staging

For details on how to configure publishers, see Publishers.

Programming Interface
*********************

Haunt is a fully-programmable system composed of several Guile Scheme modules.  This section documents the public API.

Sites
=====

     (use-modules (haunt site))

A site object defines all of the properties for a Haunt website: The site name, domain name, where blog posts are found, what post formats are understood, which procedures are used to build the site, where the output files are written to, etc.

 -- Procedure: site [#:title "This Place is Haunted"]
     [#:domain "example.com"] [#:posts-directory "posts"]
     [#:file-filter default-file-filter]
     [#:build-directory "site"] [#:default-metadata '()]
     [#:make-slug post-slug] [#:readers '()]
     [#:builders '()] [#:publishers '()]
     Create a new site object.  All arguments are optional:

     title
          The name of the site.

     posts-directory
          The directory where posts are found.

     file-filter
          A predicate procedure that returns #f when a post file should be ignored, and #t otherwise.  Emacs temporary files are ignored by default.

     build-directory
          The directory that generated pages are stored in.

     default-metadata
          An alist of arbitrary default metadata for posts whose keys are symbols.

     make-slug
          A procedure generating a file name slug from a post.

     readers
          A list of reader objects for processing posts.

     builders
          A list of procedures for building pages from posts.

     publishers
          A list of publisher objects for upload site contents to a remote location

 -- Procedure: site? OBJ
     Return #t if OBJ is a site object.

 -- Procedure: site-title SITE
     Return the title of SITE.

 -- Procedure: site-domain SITE
     Return the domain of SITE.

 -- Procedure: site-posts-directory SITE
     Return the posts directory for SITE.

 -- Procedure: site-file-filter SITE
     Return the file filter procedure for SITE.

 -- Procedure: site-build-directory SITE
     Return the build directory of SITE.

 -- Procedure: site-make-slug SITE
     Return the slug constructor for SITE.

 -- Procedure: site-readers SITE
     Return the list of reader procedures for SITE.

 -- Procedure: site-builders SITE
     Return the list of builder procedures for SITE.

 -- Procedure: site-publishers SITE
     Return the list of publisher objects for upload SITE contents to a remote location.

Posts
=====

     (use-modules (haunt post))

Posts represent the articles that are kept in a site's post directory and written in a markup format that Haunt can understand. See Readers for how files on disk can be transformed into posts.

 -- Procedure: make-post FILE-NAME METADATA SXML
     Create a new post object that represents the contents of the file FILE-NAME.  The body of the post, SXML, is represented as an SXML tree (see SXML (GNU Guile Reference Manual)) and the metadata is an association list (see Association Lists (GNU Guile Reference Manual)) of arbitrary key/value pairs.

 -- Procedure: post? OBJECT
     Return #t if OBJECT is a post.

 -- Procedure: post-file-name POST
     Return the file name for POST.

 -- Procedure: post-metadata POST
     Return the metadata association list for POST.

 -- Procedure: post-sxml POST
     Return the SXML tree for POST.

 -- Procedure: post-ref POST KEY
     Return the metadata value corresponding to KEY within POST.

 -- Procedure: post-slug post
     Return a URL slug suitable for the file name of POST.  If a custom slug metadata value exists for POST then that is returned.  Otherwise, a slug is automatically generated from the title metadata value.

The original post-slug procedure above has some less than ideal behavior.  One issue is that version numbers like ``1.2.3'' get transformed to ``123'' rather than something more sensible like ``1-2-3''.  Unfortunately, changing this behavior would mean breaking the URLs for existing Haunt sites.  Instead, users may opt-in to using post-slug-v2 by passing it as the #:make-slug argument to make-site.  See Sites for more information.

 -- Procedure: post-slug-v2 post
     Transform the title of POST into a URL slug.  This second revision does a better job than the original.  Like post-slug, if a custom slug metadata value exists for POST then that is returned.  Otherwise, a slug is automatically generated from the title metadata value.

 -- Variable: %default-date
     The default date of a post when no other date is specified in the metadata association list.

 -- Procedure: post-date POST
     Return the date for POST, or %default-date if no date is specified.

 -- Procedure: posts/reverse-chronological POSTS
     Sort POSTS, a list of posts, in reverse chronological order.

 -- Procedure: post-author POST
     Return the author of POST, or #f if no author is specified.

 -- Procedure: post-tags POST
     Return list of tags for POST, or the empty list if no tags are specified.

 -- Procedure: post-title POST
     Return the title of POST, or #f if no title is specified.

 -- Procedure: posts/group-by-tag POSTS
     Create an association list of tags mapped to the posts in the list POSTS that used them.

Readers
=======

Reader
------

     (use-modules (haunt reader))

The purpose of a reader is to translate the markup within a post file into an SXML tree representing the HTML structure and associate some metadata with it.

 -- Procedure: make-reader MATCHER PROC
     Create a new reader.  The reader is to be activated when MATCHER, a procedure that accepts a file name as its only argument, returns #t.  When a post file matches, the procedure PROC, which also accepts a file name as its only argument, reads the contents and returns a post object (see Posts).

 -- Procedure: reader? OBJECT
     Return #t if OBJECT is a reader.

 -- Procedure: reader-matcher READER
     Return the match procedure for READER.

 -- Procedure: reader-proc READER
     Return the read procedure for READER.

 -- Procedure: reader-match? READER FILE-NAME
     Return #t if FILE-NAME is a file supported by READER.

 -- Procedure: reader-find? readers file-name
     Return the first reader in READERS that can parse FILE-NAME, or #f if there is no such reader.

 -- Procedure: reader-read reader file-name
     Parse FILE-NAME using READER and return two values: an alist of metadata and an SXML tree.

 -- Procedure: read-post READER FILE-NAME [DEFAULT-METADATA]
     Read a post object from FILE-NAME using READER, merging its metadata with DEFAULT-METADATA, or the empty list if not specified.

 -- Procedure: read-posts DIRECTORY KEEP? READERS [DEFAULT-METADATA]
     Read all of the files in DIRECTORY that match KEEP? as post objects.  The READERS list must contain a matching reader for every post.

 -- Procedure: make-file-extension-matcher EXT
     Create a procedure that returns #t when a file name ends with ``.ext''.

 -- Procedure: sxml-reader
     A basic reader for posts written as Scheme code that evaluates to an an association list.  The special key content contains the post body as an SXML tree.

     Example:

          (use-modules (haunt utils))

          `((title . "Hello, world!")
            (date . ,(string->date* "2015-04-10 23:00"))
            (tags "foo" "bar")
            (summary . "Just a test")
            (content
             ((h2 "Hello!")
              (p "This is Haunt.  A static site generator for GNU Guile."))))

 -- Procedure: html-reader
     A basic reader for posts written in plain ol' HTML.  Metadata is encoded as the key: value pairs, one per line, at the beginning of the file.  A line with the --- sentinel marks the end of the metadata section and the rest of the file is encoded as HTML.

     Example:

          title: A Foo Walks Into a Bar
          date: 2015-04-11 20:00
          tags: bar
          ---
          <p>
            This is an example using raw HTML, because Guile doesn't have a
            Markdown parser.
          </p>

Texinfo
-------

     (use-modules (haunt reader texinfo))

 -- Procedure: texinfo-reader
     A reader for posts written in texinfo, the official documentation format of the GNU project.  Metadata is encoded as key: value pairs, one per line, at the beginning of the file.  A line with the --- sentinel marks the end of the metadata section and the rest of the file is encoded as HTML.

     Example:

          title: Hello, Texi!
          date: 2016-08-20 12:00
          tags: texinfo, foo
          ---

          @emph{Texinfo} is the official documentation format of the
          @url{http://www.gnu.org/, GNU project}.  It was invented by Richard
          Stallman and Bob Chassell many years ago, loosely based on Brian
          Reid's Scribe and other formatting languages of the time.  It is
          used by many non-GNU projects as well.

Skribe
------

     (use-modules (haunt reader skribe))

 -- Procedure: skribe-reader
     A reader for posts written in Skribe, a markup language with the full power of Scheme.  Skribe posts are created with the post expression with metadata encoded as :key expression pairs at the beginning of the post expression.  After the metadata section, the rest of the post expression is encoded as HTML.

     Example:

          (post
           :title "Hello, Skribe!"
           :date (make-date* 2016 08 20 12 00)
           :tags '("skribe" "foo" "baz")

           (h2 [This is a Skribe post])

           (p [Skribe is a ,(em [really]) cool document authoring format
               that provides all the power of Scheme whilst giving the user
               a means to write literal text without stuffing it into a
               string literal. If this sort of thing suits you, be sure to
               check out ,(anchor "Skribilo"
                                  "http://www.nongnu.org/skribilo/"), too.]))

CommonMark
----------

     (use-modules (haunt reader commonmark))

 -- Procedure: commonmark-reader
     A reader for posts written in CommonMark, a fully specified variant of Markdown.  Metadata is encoded as key: value pairs, one per line, at the beginning of the file.  A line with the --- sentinel marks the end of the metadata section and the rest of the file is encoded as HTML.

     Example:

          title: Hello, CommonMark!
          date: 2016-08-20 12:00
          tags: markdown, commonmark
          ---

          ## This is a CommonMark post

          CommonMark is a **strongly** defined, *highly* compatible
          specification of Markdown, learn more about CommomMark
          [here](http://commonmark.org/).

Builders
========

Builders are procedures that return one or more artifacts (see Artifacts) when applied.  A builder accepts two arguments: A site (see Sites) and a list of posts (see Posts).

Haunt comes with a few convenient builders to help users who want to create a simple blog with an Atom feed.

Static Assets
-------------

     (use-modules (haunt builder assets))

 -- Procedure: static-directory DIRECTORY [DEST]

     Create a builder procedure that recursively copies all of the files in DIRECTORY, a file name relative to a site's source directory, and copies them into DEST, a prefix relative to a site's target output directory.  By default, DEST is DIRECTORY.

Flat pages
----------

     (use-modules (haunt builder flat-pages))

Flat pages cover the simple case of converting a tree of files written in some markup language to full web pages.  Flat pages work great for the more informational parts of a website that don't require any fancy programming to generate, like an ``About me'' page.

 -- Procedure: flat-pages [directory "pages"] @
     [#:template ugly-page-template] @ [#:prefix "/"]

     Return a procedure that parses the files in DIRECTORY and returns a list of HTML pages, one for each file.  The files are parsed using the readers configured for the current site.

     Each flat page starts with a metadata header.  Only a single piece of metadata is used, though: the title.

     Here's what a flat page written in Markdown might look like:

          title: About me
          ---

          # About me

          Hello, I am Alice!  I'm a fictitious person made up for the purposes
          of demonstrating Haunt's flat page functionality.  I live here in this
          manual with my two cats: Bob and Carol.

     If the above text were saved to about.md in the flat pages directory, the resulting HTML page would be about.html.

     For ``pretty'' URLS (/about rather than /about.html), the special metadata item index: true can be specified in the page header:

          title: About me
          index: true
          ---

          # About me

          ...

     With the index metadata flag, Haunt will generate about/index.html rather than about.html.

     The content of each flat page is inserted into a complete HTML document by the TEMPLATE procedure.  This procedure takes three arguments:

       * the site object

       * the page title string (from the metadata header)

       * an SXML tree of the page body

     TEMPLATE should return a single value: a new SXML tree representing a complete HTML page that presumably wraps the page body.

     Conveniently, the signature of TEMPLATE matches the blog theme layout procedure so that it can be reused for flat pages.  See Blog for more information.

     The structure of DIRECTORY is preserved in the resulting pages and may be optionally nested within the directory PREFIX.  If no prefix is specified, the files are placed starting at the root of the site.

Blog
----

     (use-modules (haunt builder blog))

 -- Procedure: theme [#:name "Untitled"] [#:layout] [#:post-template]
     [#:collection-template] [#:pagination-template]
     Create a new theme named NAME.

     The procedure LAYOUT accepts three arguments: a site, a page title string, and an SXML tree.  Its purpose is to wrap the contents of a post with the theme's header/footer and return the complete SXML tree for a web page.

     The procedure POST-TEMPLATE accepts a single argument: a post. Its purpose is to return an SXML tree containing the contents of the post, applying any desired post-processing operations.

     The procedure COLLECTION-TEMPLATE accepts four arguments: a site, a title string, a list of posts, and a URL prefix string.  Its purpose is to return an SXML tree containing the body of the collection page.

     The procedure PAGINATION-TEMPLATE accepts four arguments: a site, an SXML tree, the file name of the previous page, and the file name of the next page.  Its purpose is to incorporate the given SXML tree into a larger document that incorporates previous/next page links.

 -- Procedure: theme? OBJECT
     Return #t if OBJECT is a theme object.

 -- Procedure: blog [#:theme] [#:prefix] [#:post-prefix]
     [#:collections `(("Recent Posts" "index.html" ,posts/reverse-chronological))]
     [#:posts-per-page]

     Create a builder procedure that transforms a list of posts into pages decorated by THEME, a theme object, whose URLs start with PREFIX.  Post pages may be nested deeper in the file hierarchy than collection pages by specifying the POST-PREFIX argument.

     Additionally, this builder creates pages that aggregate previews of many posts corresponding to what is specified in the list COLLECTIONS.  Each collection is a three element list in the form (title file-name filter).

     title
          The human readable name of the collection.

     file-name
          The HTML file that will contain the rendered collection.

     filter
          A procedure that accepts a list of posts as its only argument and returns a new list of posts.  The filter procedure is used to remove and/or sort the posts into the desired form for the collection.  For example, a filter could sort posts in reverse chronological order or select all posts that are written by a particular author.

     By default, a single collection is created that lists posts in reverse chronological order and writes to index.html.

     Also by default, collection pages are not paginated.  When there are a lot of posts in one or more collections, it is best to paginate them. To do so, pass the POSTS-PER-PAGE argument.

     The default theme is intended only for testing purposes.

Atom
----

     (use-modules (haunt builder atom))

 -- Procedure: atom-feed [#:file-name "feed.xml"] [#:subtitle "Recent Posts"]
     [#:filter posts/reverse-chronological]
     [#:last-updated (current-date)]
     [#:max-entries 20] [#:blog-prefix ""]
     Return a builder procedure that renders a site's posts as an Atom feed.  All arguments are optional:

     file-name:
          The page file name.

     subtitle
          The feed subtitle.

     filter
          The procedure called to manipulate the posts list before rendering.

     last-updated
          The feed last updated date.  Defaults to the current date.

     max-entries
          The maximum number of posts to render in the feed.

     blog-prefix
          The prefix for all post URLs, which is the combination of the blog's prefix and post prefix.  See Blog for more information.

 -- Procedure: atom-feeds-by-tag [#:prefix "feeds/tags"]
     [#:filter posts/reverse-chronological]
     [#:last-updated (current-date)]
     [#:max-entries 20] [#:blog-prefix ""]
     Return a builder procedure that renders an atom feed for every tag used in a post.  All arguments are optional:

     prefix
          The directory in which to write the feeds.

     filter
          The procedure called to manipulate the posts list before rendering.

     last-updated
          The feed last updated date.  Defaults to the current date.

     max-entries
          The maximum number of posts to render in each feed.

     blog-prefix
          The prefix for all post URLs, which is the combination of the blog's prefix and post prefix.  See Blog for more information.

RSS
---

     (use-modules (haunt builder rss))

 -- Procedure: rss-feed [#:file-name "rss-feed.xml"] [#:subtitle "Recent Posts"]
     [#:filter posts/reverse-chronological]
     [#:publication-date (current-date)]
     [#:max-entries 20] [#:blog-prefix ""]
     Return a builder procedure that renders a list of posts as an RSS feed.  All arguments are optional:

     file-name
          The page file name.

     subtitle
          The feed subtitle.

     filter
          The procedure called to manipulate the posts list before rendering.

     publication-date
          The feed publication date.  Defaults to the current date.

     max-entries
          The maximum number of posts to render in the feed.

     blog-prefix
          The prefix for all post URLs, which is the combination of the blog's prefix and post prefix.  See Blog for more information.

Redirects
---------

     (use-modules (haunt builder redirects))

The redirects builder creates pages that trigger browser redirects to another URL.  This allows for easily specifying redirects as part of a Haunt site configuration and without the need for modifying the configuration of the production web server that is hosting the site.

 -- Procedure: redirects specs
     Return a procedure that transforms a list of redirect tuples in SPECS, with the form (from to), into a list of pages that trigger a browser-initiated redirect.

     from values must be local page file names, not URLs, but TO values may be either local page file names or full URLs to other websites.

          (redirects '(("/about.html" "/about/me.html") ; local
                       ("/guile.html" "https://gnu.org/software/guile"))) ; remote

Publishers
==========

     (use-modules (haunt publisher))

The purpose of a publisher is to deploy a built site.  Haunt comes with some built-in publishers, but custom publishers can be created with the following interface.

 -- Procedure: make-publisher NAME PROC
     Create a new publisher.

 -- Procedure: publisher? OBJECT
     Return #t if OBJECT is a publisher.

 -- Procedure: publisher-name PUBLISHER
     Return the publisher name.

 -- Procedure: publisher-proc READER
     Return the publisher procedure for PUBLISHER.

 -- Procedure: publish PUBLISHER SITE
     Publish SITE with PUBLISHER.

 -- Procedure: run-command PROGRAM ARGS
     Run command PROGRAM with ARGS arguments.

Haunt comes with the following built-in publishers:

Rsync
-----

     (use-modules (haunt publisher rsync))

 -- Procedure: rsync-publisher [#:name 'production] [#:destination] @
     [#:user] [#:host] [#:name] [#:rsync] @ [#:flags '("--compress" "--delete" "--progress" "--recursive" "--verbose")]

     Return a new publisher named NAME that publishes a site to DESTINATION, either locally or to a remote host if HOST and/or USER arguments are specified.  Passing RSYNC overrides the rsync executable used.  Passing FLAGS overrides the set of command line flags used.

Sourcehut
---------

     (use-modules (haunt publisher sourcehut))

 -- Procedure: sourcehut-publisher [#:name 'production] [#:hut] [#:tar]
     Return a new publisher named NAME that publishes a site to sourcehut pages (https://srht.site/) using the site's configured domain.  Passing HUT and/or TAR overrides the default hut and tar executables used.

     For the publisher to work, the hut CLI tool that is used under the hood has to be configured.  One can do so by creating ~/.config/hut/config manually or by running the hut init command.  In both cases an OAuth access token needs to be generated via sourcehut meta (https://meta.sr.ht/oauth2).

Artifacts
=========

     (use-modules (haunt artifact))

Artifacts are objects that represent the output of a build.  A collection of artifacts forms a complete website.  Artifacts are quite simple: They contain a file name string that specifies where the artifact belongs in the build output directory, and a writer procedure that populates that file with data.

For example, making an artifact that writes ``Hello, world!'' to /hello.txt would look like this:

     (make-artifact "/hello.txt"
                    (lambda (output)
                      (call-with-output-file output
                        (lambda (port)
                          (display "Hello, world!\n" port)))))

Previous versions of Haunt made a distinction between pages, whose content is defined algorithmically, and assets, whose content is copied verbatim from an input file such as an image.  The artifact data type is a unifying primitive that replaces both pages and assets.

Artifacts that require serializing some input, such as SXML, should use serialize-artifact.  Artifacts that make a verbatim copy of an input file should use verbatim-artifact.  Unless you are implementing a custom builder, it's unlikely that these procedures will be need to used directly.

 -- Procedure: serialized-artifact destination obj serialize
     Return a new artifact whose writer serializes OBJ using the procedure SERIALIZE to the DESTINATION in the build output directory.

 -- Procedure: verbatim-artifact source destination
     Return a new artifact that copies the file SOURCE verbatim to DESTINATION within the build output directory.

Assets
======

     (use-modules (haunt asset))

Assets represent files on disk that should be copied verbatim to a site's output directory.  Common types of assets include CSS, JavaScript, images, and fonts.  It is often the case that there are entire directories full of static assets to copy over, thus there is a convenient directory-assets procedure.  However, it's unlikely that this procedure needs to be used directly.  See Static Assets for a convenient builder.

 -- Procedure: directory-assets DIRECTORY KEEP? DEST
     Create a list of asset objects to be stored within DEST for all files in DIRECTORY that match KEEP?, recursively.

Contributing
************

Haunt is developed using the Git version control system.  The official repository is hosted at https://git.dthompson.us/haunt.git.

Send patches and bug reports to davet@gnu.org.

GNU Free Documentation License
******************************

     [file fdl-1.3.texi not included]