;;; wikme --- build a static wiki out of a folder of markdown files
(import (cmark)
(srfi-152)
(utf8)
(chicken irregex)
(chicken port)
(chicken string))
;;; Configuration
(define site-config
(make-parameter `((base-url . "https://www.example.com")
;; These default directories aren't .. great.
(source-dir . "src")
(output-dir . "out")
(transformers . ,(list commonmark->html
wikify-links))
(filename-transform
. (lambda (fname)
(md->index-html fname)))
(page-environment
. ((title
. ,(lambda (page)
(cdr (assq 'title (page-meta page)))))
(body
. ,(lambda (page)
(page-body page)))
(last_updated
. ,(lambda (page)
(cdr (assq 'last-updated (page-meta page))))))))))
(define (config-get x)
(if (assq x (site-config))
(cdr (assq x (site-config)))
#f))
;;; Templates
(define (render template env)
;;; Render TEMPLATE using ENV.
;; TEMPLATE is a string with {{placeholders}}; ENV is an alist of key-value
;; pairs to insert into the TEMPLATE's placeholders.
(string-substitute* template (env->replacements env)))
(define (env->replacements env)
;;; Convert an ENV alist of the form `((X . Y) ...) to '(("{{X}}" . "Y") ...).
;; X's are template variables and Y's are the values of those variables. In
;; the template, both "{{X}}" and "{{ X }}" will be replaced.
;; If Y is a thunk, call it.
(let loop ((env env)
(res '()))
(if (null? env)
res
(let* ((this (car env))
(rest (cdr env))
(key (->string (car this)))
(val (if (procedure? (cdr this))
((cdr this))
(->string (cdr this)))))
(loop (cdr env)
(append (list (cons (string-append "{{" key "}}") val)
(cons (string-append "{{ " key " }}") val))
env))))))
;;; Wiki links
(define wiki-link-sre
;;; An SRE for [[wiki-style links|with optional titles]].
'(: "[["
(submatch-named page (+ (~ "|")))
(? (submatch "|" (submatch-named title (*? nonl))))
"]]"))
(define (wikify-links text)
;;; Convert [[Wiki-style links]] to HTML style in TEXT.
(irregex-replace/all wiki-link-sre text
(lambda (m)
(let* ((page (irregex-match-substring m 'page))
(title (or (irregex-match-substring m 'title)
page)))
(string-append
"" title "")))))
(define (linkify pagename)
;;; Turn a page name into a link suitable for an tag.
(string-append (base-url) "/" (slugify pagename) "/index.html"))
(define (string-capitalize str)
;;; Capitalize the first word in STR, and ensure the rest of it is lowercase.
;; Stolen and adapted from MIT/GNU Scheme
(let* ((end (string-length str))
(str* (make-string end)))
(do ((i 0 (+ i 1)))
((= i end))
(string-set! str* i ((if (= i 0) char-upcase char-downcase)
(string-ref str i))))
str*))
(define (slugify str)
;;; Convert STR to a 'slug', that is, another string suitable for linking.
;; This function will return the input string, in sentence case, and with all
;; punctuation and spaces converted to a hypen.
(string-capitalize
(string-trim-both (irregex-replace/all '(+ (~ alnum)) str "-")
(lambda (c)
(char=? c #\-)))))
(define (unslugify slug)
;;; Convert a SLUG back into a normal string as best as possible.
;; Because information is lost in slugification, it's impossible to be sure
;; that the result of this procedure is totally accurate. That is,
;; slugification is not round-trippable.
(irregex-replace/all '("-") slug " "))
;;; Transform source
(define (transform source . transformers)
;;; Transform SOURCE to html by passing it through a series of TRANSFORMERS.
;; Each TRANSFORMER should be a one-argument procedure taking and returning a
;; string.
(let loop ((transformers transformers)
(output source))
(if (null? transformers)
output
(loop (cdr transformers)
((car transformers) output)))))
(define (md->index-html filename)
;;; Transform a FILENAME of the form dir/name.md to dir/name/index.html.
;; Uses source
)
;;; Pages
(define-record-type
;;; A wiki page is a mapping between source and body content, and between the
;;; page's origin and its destination files, wrapped together with some
;;; metadata.
(make-page source body origin destination meta)
page?
(source page-source ; source markup
(setter page-source))
(body page-body ; rendered page body
(setter page-source))
(origin page-origin ; file containing the markup
(setter page-origin))
(destination page-destination ; destination file
(setter page-destination))
(meta page-meta ; alist of metadata tags
(setter page-meta)))
(define (page-meta-ref key page)
;;; Get metadata KEY from PAGE.
(cdr (assq key (page-meta page))))
(define (file->page file
#!key
(transformers (config-get 'transformers))
(destination ))
;;; Create a from FILE.
;; Wraps make-page for easier use.
)
;;; Writing files
(define (publish file config)
;;; Publish FILE, using CONFIG.
;; CONFIG should be a configuration alist, which see above.
#f)