summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--README3
-rw-r--r--TODO.org24
-rw-r--r--doc/haunt.texi44
-rw-r--r--example/haunt.scm2
-rw-r--r--example/pages/about.md10
-rw-r--r--haunt/builder/flat-pages.scm34
-rw-r--r--haunt/html.scm25
-rw-r--r--haunt/ui.scm2
-rw-r--r--haunt/ui/new.scm149
-rw-r--r--tests/html.scm27
11 files changed, 304 insertions, 18 deletions
diff --git a/Makefile.am b/Makefile.am
index 1c08890..5ed9c87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -66,6 +66,7 @@ SOURCES = \
haunt/watch/fallback.scm \
haunt/ui.scm \
haunt/ui/build.scm \
+ haunt/ui/new.scm \
haunt/ui/publish.scm \
haunt/ui/serve.scm \
haunt/serve/mime-types.scm \
@@ -97,6 +98,7 @@ endif
TESTS = \
tests/helper.scm \
+ tests/html.scm \
tests/post.scm \
tests/utils.scm
diff --git a/README b/README
index 7fc6695..b65e301 100644
--- a/README
+++ b/README
@@ -37,6 +37,9 @@ simple, functional, and extensible.
Write a configuration file named =haunt.scm=. Add your posts to a
directory named =posts=. Then run =haunt build=!
+Alternatively, use the =haunt new= command to start a project from a
+template quickly.
+
To view your creation, run =haunt serve= and browse to
=localhost:8080=. For quicker development cycles, run =haunt serve
--watch= to automatically rebuild the site when things change.
diff --git a/TODO.org b/TODO.org
index f4b5004..20be260 100644
--- a/TODO.org
+++ b/TODO.org
@@ -1,4 +1,28 @@
* To-do list
+** Throw a better error when a reader is not found
+This is no good:
+
+#+BEGIN_SRC sh
+ $ haunt build
+ building pages in 'site'...
+ Backtrace:
+ 7 (primitive-load "/gnu/store/3c5n80ixbs198qk6dn42fiigph8s6fjs-haunt-0.3.0/bin/.haunt-real")
+ In haunt/ui.scm:
+ 136:6 6 (run-haunt-command _ . _)
+ In haunt/ui/build.scm:
+ 60:5 5 (haunt-build . _)
+ In haunt/site.scm:
+ 116:20 4 (build-site #<<site> title: "Spritely Institute" domain: "spritely.institute" scheme: https posts-directory: …>)
+ In ice-9/ftw.scm:
+ 505:39 3 (loop _ _ #(64768 11164290 16877 2 1000 998 0 4096 1727117337 1727117337 1727117337 4096 8 directory 493 # …) …)
+ In haunt/reader.scm:
+ 99:24 2 (leaf "posts/2022-08-17-blast-off-spritely-institutes-tech-tour.html" _ (#<<post> file-name: "posts/2022-0…> …))
+ 85:32 1 (read-post #f "posts/2022-08-17-blast-off-spritely-institutes-tech-tour.html" _)
+ 80:3 0 (reader-read #f "posts/2022-08-17-blast-off-spritely-institutes-tech-tour.html")
+
+ haunt/reader.scm:80:3: In procedure reader-read:
+ In procedure struct-vtable: Wrong type argument in position 1 (expecting struct): #f
+#+END_SRC
** Output XHTML5?
<cbaines> there's still code in the data service that was copied from Mumi
<cbaines> e.g. sxml->html should live somewhere else and be shared [10:34]
diff --git a/doc/haunt.texi b/doc/haunt.texi
index 6be2fb5..0ac016f 100644
--- a/doc/haunt.texi
+++ b/doc/haunt.texi
@@ -299,6 +299,7 @@ programming interfaces.
@menu
* Invoking haunt build:: Build the website.
* Invoking haunt serve:: Serve the website over HTTP.
+* Invoking haunt new:: Generating a blog template.
* Invoking haunt publish:: Publish the website.
@end menu
@@ -375,6 +376,25 @@ Automatically rebuild the site when source files change.
@end table
+@node Invoking haunt new
+@section Invoking @command{haunt new}
+
+The @command{haunt new} command creates a starter template for a new website.
+
+Usage:
+
+@example
+haunt new
+@end example
+
+By default, haunt will create a directory called @file{blog}. To use a
+different name for your new blog, simply provide the name at the
+command line as follows:
+
+@example
+haunt new my-blog
+@end example
+
@node Invoking haunt publish
@section Invoking @command{haunt publish}
@@ -864,7 +884,9 @@ 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.
-@deffn {Procedure} flat-pages directory [#:template] [#:prefix]
+@deffn {Procedure} flat-pages [directory "pages"] @@
+ [#:template ugly-page-template] @@
+ [#:prefix "/"]
Return a procedure that parses the files in @var{directory} and
returns a list of HTML pages, one for each file. The files are parsed
@@ -886,6 +908,26 @@ of demonstrating Haunt's flat page functionality. I live here in this
manual with my two cats: Bob and Carol.
@end example
+If the above text were saved to @file{about.md} in the flat pages
+directory, the resulting HTML page would be @file{about.html}.
+
+For ``pretty'' URLS (@file{/about} rather than @file{/about.html}),
+the special metadata item @code{index: true} can be specified in the
+page header:
+
+@example
+title: About me
+index: true
+---
+
+# About me
+
+...
+@end example
+
+With the @code{index} metadata flag, Haunt will generate
+@file{about/index.html} rather than @file{about.html}.
+
The content of each flat page is inserted into a complete HTML
document by the @var{template} procedure. This procedure takes three
arguments:
diff --git a/example/haunt.scm b/example/haunt.scm
index 2527c89..bcb58fd 100644
--- a/example/haunt.scm
+++ b/example/haunt.scm
@@ -2,6 +2,7 @@
(haunt builder blog)
(haunt builder atom)
(haunt builder assets)
+ (haunt builder flat-pages)
(haunt builder rss)
(haunt publisher rsync)
(haunt publisher sourcehut)
@@ -21,6 +22,7 @@
(atom-feed)
(atom-feeds-by-tag)
(rss-feed)
+ (flat-pages)
(static-directory "images"))
#:publishers (list (rsync-publisher #:name 'rsync
#:destination "/tmp/haunt-example")
diff --git a/example/pages/about.md b/example/pages/about.md
new file mode 100644
index 0000000..d841fa7
--- /dev/null
+++ b/example/pages/about.md
@@ -0,0 +1,10 @@
+title: About
+index: true
+---
+Scheme is a cool programming language.
+
+Guile is a cool Scheme implementation.
+
+Haunt is a cool static site generator written in Guile.
+
+Enough said.
diff --git a/haunt/builder/flat-pages.scm b/haunt/builder/flat-pages.scm
index 98913c0..85b3b53 100644
--- a/haunt/builder/flat-pages.scm
+++ b/haunt/builder/flat-pages.scm
@@ -1,5 +1,5 @@
;;; Haunt --- Static site generator for GNU Guile
-;;; Copyright © 2015 David Thompson <davet@gnu.org>
+;;; Copyright © 2015-2024 David Thompson <davet@gnu.org>
;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
;;;
;;; This file is part of Haunt.
@@ -35,9 +35,20 @@
#:use-module (srfi srfi-11)
#:export (flat-pages))
-(define* (flat-pages directory #:key
- template
- prefix)
+(define (ugly-page-template site title body)
+ `((doctype "html")
+ (head
+ (meta (@ (charset "utf-8")))
+ (title ,(string-append title " — " (site-title site))))
+ (body
+ (h1 ,(site-title site))
+ ,body)))
+
+(define* (flat-pages #:optional
+ (directory "pages")
+ #:key
+ (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. The structure of DIRECTORY
@@ -66,15 +77,18 @@ complete HTML page that presumably wraps the page body."
(string-append "." (file-extension file-name))))
(map (lambda (file-name)
(match (reader-find (site-readers site) file-name)
+ (#f (error "no reader available for page" file-name))
(reader
(let-values (((metadata body) (reader-read reader file-name)))
- (let* ((dir (substring (dirname file-name)
+ (let* ((title (or (assq-ref metadata 'title) "Untitled"))
+ (index? (equal? (assq-ref metadata 'index) "true"))
+ (dir (substring (dirname file-name)
(string-length directory)))
- (out (string-append (or prefix "/") dir
+ (out (string-append prefix dir
(if (string-null? dir) "" "/")
- (strip-extension file-name) ".html"))
- (title (or (assq-ref metadata 'title) "Untitled")))
+ (strip-extension file-name)
+ (if index? "/index" "")
+ ".html")))
(serialized-artifact out (template site title body)
- sxml->html))))
- (#f (error "no reader available for page" file-name))))
+ sxml->html))))))
src-files)))
diff --git a/haunt/html.scm b/haunt/html.scm
index 5891cd0..795c572 100644
--- a/haunt/html.scm
+++ b/haunt/html.scm
@@ -1,5 +1,6 @@
;;; Haunt --- Static site generator for GNU Guile
;;; Copyright © 2015 David Thompson <davet@gnu.org>
+;;; Copyright © 2024 Daniel Meißner <dan_m@posteo.de>
;;;
;;; This file is part of Haunt.
;;;
@@ -53,6 +54,13 @@
"Return #t if TAG is a void element."
(pair? (memq tag %void-elements)))
+(define %raw-elements
+ '(script style))
+
+(define (raw-element? tag)
+ "Return #t if TAG is a raw element."
+ (pair? (memq tag %raw-elements)))
+
(define %escape-chars
(alist->hash-table
'((#\" . "quot")
@@ -96,12 +104,17 @@ list ATTRS and the child nodes in BODY."
(display #\space port)
(attribute->html attr value port)))
attrs)
- (if (and (null? body) (void-element? tag))
- (display " />" port)
- (begin
- (display #\> port)
- (for-each (cut sxml->html <> port) body)
- (format port "</~a>" tag))))
+ (cond
+ ((and (null? body) (void-element? tag))
+ (display " />" port))
+ ((raw-element? tag)
+ (display #\> port)
+ (for-each (lambda (node) (display node port)) body)
+ (format port "</~a>" tag))
+ (else
+ (display #\> port)
+ (for-each (cut sxml->html <> port) body)
+ (format port "</~a>" tag))))
(define (doctype->html doctype port)
(format port "<!DOCTYPE ~a>" doctype))
diff --git a/haunt/ui.scm b/haunt/ui.scm
index a0e98c7..d512cc2 100644
--- a/haunt/ui.scm
+++ b/haunt/ui.scm
@@ -57,7 +57,7 @@
haunt-main))
(define commands
- '("build" "publish" "serve"))
+ '("build" "new" "publish" "serve"))
(define program-name (make-parameter 'haunt))
diff --git a/haunt/ui/new.scm b/haunt/ui/new.scm
new file mode 100644
index 0000000..7110e06
--- /dev/null
+++ b/haunt/ui/new.scm
@@ -0,0 +1,149 @@
+;;; Haunt --- Static site generator for GNU Guile
+;;; Copyright © 2024 jgart <jgart@dismail.de>
+;;;
+;;; This file is part of Haunt.
+;;;
+;;; Haunt is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; Haunt is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with Haunt. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Haunt new sub-command.
+;;
+;;; Code:
+
+(define-module (haunt ui new)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-37)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 pretty-print)
+ #:use-module (haunt ui)
+ #:use-module (haunt utils)
+ #:export (haunt-new))
+
+(define (show-help)
+ (format #t "Usage: haunt new [NAME]
+Create a new Haunt project.~%")
+ (display "
+ -p, --project-dir project directory")
+ (display "
+ -t, --title project title")
+ (display "
+ -d, --domain project domain")
+ (display "
+ -a, --author project author")
+ (display "
+ -e, --email project email")
+ (show-common-options-help)
+ (newline)
+ (display "
+ -h, --help display this help and exit")
+ (display "
+ -V, --version display version information and exit")
+ (newline))
+
+(define %options
+ (list (option '(#\h "help") #f #f
+ (lambda _
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda _
+ (show-version-and-exit "haunt new")))
+ (option '(#\p "project-dir") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'project-dir arg result)))
+ (option '(#\t "title") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'title arg result)))
+ (option '(#\d "domain") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'domain arg result)))
+ (option '(#\a "author") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'author arg result)))
+ (option '(#\e "email") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'email arg result)))))
+
+(define %default-options
+ (append '((project-dir . "blog")
+ (title . "Built with Guile")
+ (domain . "example.com")
+ (author . "Eva Luator")
+ (email . "eva@example.com"))
+ %default-common-options))
+
+(define (write-template project-dir build-file title domain author email)
+ (let ((path (string-append project-dir "/" build-file)))
+ (call-with-output-file path
+ (lambda (port)
+ (pretty-print
+ '(use-modules (haunt asset)
+ (haunt builder blog)
+ (haunt builder atom)
+ (haunt builder assets)
+ (haunt reader commonmark)
+ (haunt site))
+ port)
+ (newline port)
+ (pretty-print
+ `(site #:title ,title
+ #:domain ,domain
+ #:default-metadata
+ '((author . ,author)
+ (email . ,email))
+ #:readers (list commonmark-reader)
+ #:builders (list (blog)
+ (atom-feed)
+ (atom-feeds-by-tag)
+ (static-directory "images")))
+ port)))))
+
+(define %first-post-file-name "hello.md")
+
+(define %first-post-contents
+ "title: First post!
+date: 2018-03-13 18:00
+tags: hello
+summary: hello!
+---
+
+Hello, world!
+")
+
+(define (write-first-post project-dir)
+ (let* ((first-post-file %first-post-file-name)
+ (first-post-contents %first-post-contents)
+ (output-path
+ (string-append project-dir "/posts/" first-post-file)))
+ (call-with-output-file output-path
+ (lambda (port)
+ (display first-post-contents port)))))
+
+(define (build-template project-dir build-file title domain author email)
+ (mkdir-p (string-append project-dir "/images"))
+ (mkdir-p (string-append project-dir "/posts"))
+ (write-template project-dir build-file title domain author email)
+ (write-first-post project-dir)
+ (format #t "new blog was created in project directory ~a\n" project-dir))
+
+(define (haunt-new . args)
+ (let* ((opts (simple-args-fold args %options %default-options))
+ (project-dir (assq-ref opts 'project-dir))
+ (build-file (assq-ref opts 'config))
+ (title (assq-ref opts 'title))
+ (domain (assq-ref opts 'domain))
+ (author (assq-ref opts 'author))
+ (email (assq-ref opts 'email)))
+ (build-template project-dir build-file title domain author email)))
diff --git a/tests/html.scm b/tests/html.scm
new file mode 100644
index 0000000..b475bf5
--- /dev/null
+++ b/tests/html.scm
@@ -0,0 +1,27 @@
+;;; Haunt --- Static site generator for GNU Guile
+;;; Copyright © 2024 Daniel Meißner <dan_m@posteo.de>
+;;;
+;;; This file is part of Haunt.
+;;;
+;;; Haunt is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; Haunt is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with Haunt. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-html)
+ #:use-module (haunt html)
+ #:use-module (srfi srfi-64)
+ #:use-module (tests helper))
+
+(with-tests "html"
+ (test-equal "content of raw elements are not escaped"
+ "<script>console.log(\"Hello, world!\");</script>"
+ (sxml->html-string '(script "console.log(\"Hello, world!\");"))))