(import (chicken base)
utf8
spiffy
nrepl
srfi-18
(chicken file)
(chicken io)
(chicken port)
(chicken format)
(chicken pretty-print)
(chicken string)
lowdown
sxml-transforms
matchable
(chicken irregex)
srfi-13
intarweb
uri-common
(chicken process)
(chicken process-context))
(define *PORT* (string->number
(or (get-environment-variable "PORT")
"8080")))
(define *WEBROOT* (or (get-environment-variable "WEBROOT")
"dist"))
(define *SRCROOT* (or (get-environment-variable "SRCROOT")
"pages"))
(define *TEMPLATE* (or (get-environment-variable "TEMPLATE")
"template.html"))
(define (pipe x . fns)
(match fns
[() x]
[(fn . rest) (apply pipe (fn x) rest)]))
;; Convenience macro (@ expr ...) that expands to (lambda (x) (expr ... x))
(define-syntax @
(syntax-rules ()
((_ expr ...)
(lambda (x) (expr ... x)))))
(define-syntax thunk
(syntax-rules ()
((_ expr ...)
(lambda () (expr ...)))))
(define (replace from to str)
(irregex-replace/all from str to))
(define (html-unescape html)
(pipe html
(@ replace """ "\"")
(@ replace "'" "'")
(@ replace "&" "&")
(@ replace "<" "<")
(@ replace ">" ">")))
(define (highlight ext code)
(define tmp-file-path (create-temporary-file ext))
(with-output-to-file tmp-file-path
(lambda () (display code)))
(define result
(receive (input _ _) (process (format "highlight ~A -O html -f" tmp-file-path))
(read-string #f input)))
(define result (capture ,(format "highlight ~A -O html -f" tmp-file-path)))
(delete-file tmp-file-path)
result)
(define (replace-code-block content return)
(define irx (irregex "^%lang (\\S+)%\\n?" 's))
(define lang-match
(irregex-search irx content))
(if (not lang-match)
(return content))
(define lang
(irregex-match-substring lang-match 1))
(pipe content
(@ replace irx "")
(@ html-unescape)
(@ highlight lang)
(@ string-trim-both)))
(define (convert-md-to-html filepath)
(with-output-to-string
(lambda () (markdown->html (open-input-file filepath)))))
(define (highlight-code-blocks html)
(define (highlight-match m)
(format "~A"
(call/cc (@ replace-code-block (irregex-match-substring m 1)))))
(irregex-replace/all (irregex "(.*?)" 's) html
highlight-match))
(define (insert-to-page-tmpl tmpl-path html)
(format (read-string #f (open-input-file tmpl-path))
html))
(define (compile-file filepath prev-result)
(define (inner return)
(define initial-srcroot (string-append "^" *SRCROOT*))
(define replaced-filepath (replace initial-srcroot *WEBROOT* filepath))
(if (directory-exists? filepath)
(begin
(print (format "Creating corresponding directory: ~A" filepath))
(create-directory replaced-filepath #t)
(return (void))))
(if (not (irregex-search "\\.md$" filepath))
(begin
(print (format "Copying non-md file: ~A" filepath))
(copy-file filepath replaced-filepath)
(return (void))))
(print (format "Compiling markdown: ~A" filepath))
(define out-path (pipe filepath
(@ replace "\\.md" ".html")
(@ replace initial-srcroot *WEBROOT*)))
(define out-content (pipe filepath
(@ convert-md-to-html)
(@ highlight-code-blocks)
(@ insert-to-page-tmpl *TEMPLATE*)))
(with-output-to-file out-path
(thunk display out-content)))
(call/cc inner))
(define (recompile)
(create-directory "dist" #t)
(define (delete-path path)
(if (directory-exists? path)
(delete-directory path #t)
(delete-file path)))
(for-each delete-path (glob (string-append *WEBROOT* "/*")))
(find-files "pages"
action: compile-file))
(thread-start!
(lambda ()
(receive (input _ _) (process (format "fswatch -o ~A" *SRCROOT*))
(let loop ()
(print (format "Monitored directory has changed (# of changes: ~A)" (read-line input)))
(recompile)
(loop)))))
(thread-start!
(lambda ()
(print "starting nrepl on port 1234")
(nrepl-prompt (lambda () (display "#;0> ")))
(nrepl 1234)))
(recompile)
(define (map-last proc lst)
(if (null? (cdr lst))
(list (proc (car lst)))
(cons (car lst) (map-last proc (cdr lst)))))
(define (app cont)
(define uri (request-uri (current-request)))
(define path (uri-path uri))
(define (append-html-if-needed fragment)
(define has-ext? (@ irregex-search "\\.\\S+$"))
(match fragment
["" "index.html"]
;; if path ends in an extension, do nothing
[(? has-ext?) fragment]
;; otherwise, add .html
[_ (string-append fragment ".html")]))
(define (string-intercalate delim lst)
(string-intersperse lst delim))
(define static-file-path (pipe path
(@ map-last append-html-if-needed)
(@ cdr)
(@ string-intercalate "/")))
(print (format "Serving file ~A" static-file-path))
(send-static-file static-file-path))
(print (format "Serving ~A on port ~A, using ~A as html template" *WEBROOT* *PORT* *TEMPLATE*))
(server-port *PORT*)
(root-path *WEBROOT*)
(vhost-map `((".*" . ,(@ app))))
(start-server)