Compare commits

..

No commits in common. "master" and "v0.2.4" have entirely different histories.

10 changed files with 85 additions and 121 deletions

View File

@ -11,7 +11,7 @@ AC_CONFIG_FILES([pre-inst-env], [chmod +x pre-inst-env])
AC_CONFIG_FILES([test-env], [chmod +x test-env])
AC_CONFIG_FILES([scripts/haunt], [chmod +x scripts/haunt])
GUILE_PKG([3.0 2.2 2.0])
GUILE_PKG([2.2 2.0])
GUILE_PROGS
dnl Guile-reader is needed for Skribe support

View File

@ -803,7 +803,7 @@ files in @var{directory} that match @var{keep?}, recursively.
Builders are procedures that return one or more page objects
(@pxref{Pages}) when applied. A builder accepts two arguments: A site
(@pxref{Sites}) and a list of posts (@pxref{Posts}).
(@pxref{Sites} and a list of posts (@pxref{Posts}).
Haunt comes with a few convenient builders to help users who want to
create a simple blog with an Atom feed.

View File

@ -35,7 +35,6 @@
(gnu packages)
(gnu packages autotools)
(gnu packages guile)
(gnu packages guile-xyz)
(gnu packages pkg-config)
(gnu packages texinfo))

View File

@ -35,7 +35,6 @@
#:use-module (haunt utils)
#:use-module (haunt html)
#:use-module (haunt serve mime-types)
#:use-module (web uri)
#:export (make-enclosure
enclosure?
enclosure-title
@ -133,39 +132,40 @@
(sxml->xml sxml port))
(define (date->string* date)
"Convert date to RFC-3339 formatted string."
(date->string date "~Y-~m-~dT~H:~M:~SZ"))
"Convert date to ISO-8601 formatted string."
(date->string date "~4"))
(define* (post->atom-entry site post)
(define* (post->atom-entry site post #:key (blog-prefix ""))
"Convert POST into an Atom <entry> XML node."
(let ((uri (uri->string (site-post-url site post))))
`(entry
(title ,(post-ref post 'title))
(id ,uri)
(author
(name ,(post-ref post 'author))
,(let ((email (post-ref post 'email)))
(if email `(email ,email) '())))
(updated ,(date->string* (post-date post)))
(link (@ (href ,uri) (rel "alternate")))
(summary (@ (type "html"))
,(sxml->html-string (post-sxml post)))
,@(map (lambda (enclosure)
`(link (@ (rel "enclosure")
(title ,(enclosure-title enclosure))
(href ,(enclosure-url enclosure))
(type ,(enclosure-mime-type enclosure))
,@(map (match-lambda
((key . value)
(list key value)))
(enclosure-extra enclosure)))))
(post-ref-all post 'enclosure)))))
`(entry
(title ,(post-ref post 'title))
(author
(name ,(post-ref post 'author))
,(let ((email (post-ref post 'email)))
(if email `(email ,email) '())))
(updated ,(date->string* (post-date post)))
(link (@ (href ,(string-append blog-prefix "/"
(site-post-slug site post) ".html"))
(rel "alternate")))
(summary (@ (type "html"))
,(sxml->html-string (post-sxml post)))
,@(map (lambda (enclosure)
`(link (@ (rel "enclosure")
(title ,(enclosure-title enclosure))
(href ,(enclosure-url enclosure))
(type ,(enclosure-mime-type enclosure))
,@(map (match-lambda
((key . value)
(list key value)))
(enclosure-extra enclosure)))))
(post-ref-all post 'enclosure))))
(define* (atom-feed #:key
(file-name "feed.xml")
(subtitle "Recent Posts")
(filter posts/reverse-chronological)
(max-entries 20))
(max-entries 20)
(blog-prefix ""))
"Return a builder procedure that renders a list of posts as an Atom
feed. All arguments are optional:
@ -174,28 +174,25 @@ SUBTITLE: The feed subtitle
FILTER: The procedure called to manipulate the posts list before rendering
MAX-ENTRIES: The maximum number of posts to render in the feed"
(lambda (site posts)
(let ((uri (uri->string
(build-uri (site-scheme site)
#:host (site-domain site)
#:path (string-append "/" file-name)))))
(make-page file-name
`(feed (@ (xmlns "http://www.w3.org/2005/Atom"))
(title ,(site-title site))
(id ,uri)
(subtitle ,subtitle)
(updated ,(date->string* (current-date)))
(link (@ (href ,(string-append (site-domain site)
"/" file-name))
(rel "self")))
(link (@ (href ,(site-domain site))))
,@(map (cut post->atom-entry site <>)
(take-up-to max-entries (filter posts))))
sxml->xml*))))
(make-page file-name
`(feed (@ (xmlns "http://www.w3.org/2005/Atom"))
(title ,(site-title site))
(subtitle ,subtitle)
(updated ,(date->string* (current-date)))
(link (@ (href ,(string-append (site-domain site)
"/" file-name))
(rel "self")))
(link (@ (href ,(site-domain site))))
,@(map (cut post->atom-entry site <>
#:blog-prefix blog-prefix)
(take-up-to max-entries (filter posts))))
sxml->xml*)))
(define* (atom-feeds-by-tag #:key
(prefix "feeds/tags")
(filter posts/reverse-chronological)
(max-entries 20))
(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:
@ -209,6 +206,7 @@ MAX-ENTRIES: The maximum number of posts to render in each feed"
((atom-feed #:file-name (string-append prefix "/" tag ".xml")
#:subtitle (string-append "Tag: " tag)
#:filter filter
#:max-entries max-entries)
#:max-entries max-entries
#:blog-prefix blog-prefix)
site posts)))
tag-groups))))

View File

@ -69,12 +69,16 @@
" — " ,(date->string* (post-date post)))
(div ,(post-sxml post))))
(define (ugly-default-collection-template site title posts)
(define (ugly-default-collection-template site title posts prefix)
(define (post-uri post)
(string-append (or prefix "") "/"
(site-post-slug site post) ".html"))
`((h3 ,title)
(ul
,@(map (lambda (post)
`(li
(a (@ (href ,(site-post-path site post)))
(a (@ (href ,(post-uri post)))
,(post-ref post 'title)
" — "
,(date->string* (post-date post)))))
@ -95,8 +99,8 @@
(body ((theme-post-template theme) post)))
(with-layout theme site title body)))
(define (render-collection theme site title posts)
(let ((body ((theme-collection-template theme) site title posts)))
(define (render-collection theme site title posts prefix)
(let ((body ((theme-collection-template theme) site title posts prefix)))
(with-layout theme site title body)))
(define (date->string* date)
@ -113,24 +117,25 @@
(collections
`(("Recent Posts" "index.html" ,posts/reverse-chronological))))
"Return a procedure that transforms a list of posts into pages
decorated by THEME. The collection listing URL starts with PREFIX, and
the individual posts URLs start with POST-PREFIX."
(define (make-file-name prefix base-name)
decorated by THEME, whose URLs start with PREFIX."
(define (make-file-name base-name)
(if prefix
(string-append prefix "/" base-name)
base-name))
(lambda (site posts)
(define (post->page post)
(make-page (site-post-relative-path site post)
(render-post theme site post)
sxml->html))
(let ((base-name (string-append (site-post-slug site post)
".html")))
(make-page (make-file-name base-name)
(render-post theme site post)
sxml->html)))
(define collection->page
(match-lambda
((title file-name filter)
(make-page (make-file-name prefix file-name)
(render-collection theme site title (filter posts))
(make-page (make-file-name file-name)
(render-collection theme site title (filter posts) prefix)
sxml->html))))
(append (map post->page posts)

View File

@ -49,7 +49,7 @@
(display "<?xml version=\"1.0\" encoding=\"utf-8\"?>" port)
(sxml->xml sxml port))
(define* (post->rss-item site post)
(define* (post->rss-item site post #:key (blog-prefix ""))
"Convert POST into an RSS <item> node."
`(item
(title ,(post-ref post 'title))
@ -64,7 +64,8 @@
(string-append "(" author ")")
""))))
(pubDate ,(date->rfc822-str (post-date post)))
(link (@ (href ,(site-post-url site post))
(link (@ (href ,(string-append blog-prefix "/"
(site-post-slug site post) ".html"))
(rel "alternate")))
(description ,(sxml->html-string (post-sxml post)))
,@(map (lambda (enclosure)
@ -81,7 +82,8 @@
(file-name "rss-feed.xml")
(subtitle "Recent Posts")
(filter posts/reverse-chronological)
(max-entries 20))
(max-entries 20)
(blog-prefix ""))
"Return a builder procedure that renders a list of posts as an RSS
feed. All arguments are optional:
@ -99,6 +101,7 @@ MAX-ENTRIES: The maximum number of posts to render in the feed"
(description ,subtitle)
(pubDate ,(date->rfc822-str (current-date)))
(link (@ (href ,(site-domain site))))
,@(map (cut post->rss-item site <>)
,@(map (cut post->rss-item site <>
#:blog-prefix blog-prefix)
(take-up-to max-entries (filter posts)))))
sxml->xml*)))

View File

@ -105,17 +105,8 @@ post."
(define (read-html-post port)
(values (read-metadata-headers port)
(let loop ()
(let ((next-char (peek-char port)))
(cond
((eof-object? next-char)
'())
((char-set-contains? char-set:whitespace next-char)
(read-char port)
(loop))
(else
(match (xml->sxml port)
(('*TOP* sxml) (cons sxml (loop))))))))))
(match (xml->sxml port)
(('*TOP* sxml) sxml))))
(define html-reader
(make-reader (make-file-extension-matcher "html")

View File

@ -33,37 +33,30 @@
#:use-module (haunt page)
#:use-module (haunt post)
#:use-module (haunt asset)
#:use-module (web uri)
#:export (site
site?
site-title
site-domain
site-scheme
site-posts-directory
site-posts-output-directory
site-file-filter
site-build-directory
site-default-metadata
site-make-slug
site-readers
site-builders
site-post-path
site-post-relative-path
site-post-url
site-post-slug
build-site
make-file-filter
default-file-filter))
(define-record-type <site>
(make-site title domain scheme posts-directory posts-output-directory file-filter
build-directory default-metadata make-slug readers builders)
(make-site title domain posts-directory file-filter build-directory
default-metadata make-slug readers builders)
site?
(title site-title)
(domain site-domain)
(scheme site-scheme) ; https or http
(posts-directory site-posts-directory)
(posts-output-directory site-posts-output-directory)
(file-filter site-file-filter)
(build-directory site-build-directory)
(default-metadata site-default-metadata)
@ -74,9 +67,7 @@
(define* (site #:key
(title "This Place is Haunted")
(domain "example.com")
(scheme 'https)
(posts-directory "posts")
(posts-output-directory "")
(file-filter default-file-filter)
(build-directory "site")
(default-metadata '())
@ -86,10 +77,7 @@
"Create a new site object. All arguments are optional:
TITLE: The name of the site
DOMAIN: The domain that will host the site
SCHEME: Either 'https' or 'http' ('https' by default)
POSTS-DIRECTORY: The directory where posts are found
POSTS-OUTPUT-DIRECTORY: The directory to store the built posts in
FILE-FILTER: A predicate procedure that returns #f when a post file
should be ignored, and #f otherwise. Emacs temp files are ignored by
default.
@ -99,33 +87,13 @@ 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"
(make-site title domain scheme posts-directory posts-output-directory file-filter
build-directory default-metadata make-slug readers builders))
(make-site title domain posts-directory file-filter build-directory
default-metadata make-slug readers builders))
(define (site-post-slug site post)
"Return a slug string for POST using the slug generator for SITE."
((site-make-slug site) post))
(define (site-post-relative-path site post)
"Return a relative path to a POST, without the preceding slash `/'."
(let ((base-path (string-trim-right (site-posts-output-directory site)
(lambda (c) (equal? c #\/)))))
(string-append (if (equal? base-path "")
""
(string-append base-path "/"))
(site-post-slug site post)
".html")))
(define (site-post-path site post)
"Return a path to a POST, with the preceding slash `/'."
(string-append "/" (site-post-relative-path site post)))
(define (site-post-url site post)
"Return a full URL to a POST."
(build-uri (site-scheme site)
#:host (site-domain site)
#:path (site-post-path site post)))
(define (build-site site)
"Build SITE in the appropriate build directory."
(let ((posts (if (file-exists? (site-posts-directory site))

View File

@ -29,9 +29,7 @@
#:export (post
p blockquote em
h1 h2 h3 h4 h5 h6
section
nav aside
h1 h2 h3 h4
code pre strong
ul ol li dl dt dd
anchor
@ -62,9 +60,7 @@ contents from METADATA+SXML."
em strong
code samp pre kbd var
cite dfn abbr
h1 h2 h3 h4 h5 h6
section
nav aside
h1 h2 h3 h4
ul ol li dl dt dd)
(define (anchor text uri)

View File

@ -117,7 +117,11 @@ culture works available under the " ,%cc-by-sa-link " license.")))))))
" — " ,(date->string* (post-date post)))
(div ,(post-sxml post))))
#:collection-template
(lambda (site title posts)
(lambda (site title posts prefix)
(define (post-uri post)
(string-append "/" (or prefix "")
(site-post-slug site post) ".html"))
`(,(jumbotron
`((p "Haunt is a simple, functional, hackable static site
generator written in Guile Scheme that gives authors the ability to
@ -160,7 +164,7 @@ without needing to upload the generated files to a web server.")
(ul
,@(map (lambda (post)
`(li
(a (@ (href ,(site-post-path site post)))
(a (@ (href ,(post-uri post)))
,(post-ref post 'title)
" — "
,(date->string* (post-date post)))))