aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2024-02-06 16:26:36 +0200
committerJan Tuomi <jans.tuomi@gmail.com>2024-02-08 09:02:42 +0200
commit4e322c636bbc3e8064342931fe604bc6ddbe5f4e (patch)
treeb75cb0bf167b8c194f9e5b62e0ce81c694156303
parent9e8ff330d473ff5b540bbb9c6e66be4ac05ede81 (diff)
Implement basic site generation from .md
-rw-r--r--app.scm100
-rw-r--r--article.md42
-rw-r--r--index.html80
3 files changed, 222 insertions, 0 deletions
diff --git a/app.scm b/app.scm
new file mode 100644
index 0000000..7b22dbf
--- /dev/null
+++ b/app.scm
@@ -0,0 +1,100 @@
+(import (chicken base)
+ utf8
+ spiffy
+ nrepl
+ srfi-18
+ (chicken file)
+ (chicken io)
+ (chicken port)
+ (chicken format)
+ (chicken pretty-print)
+ lowdown
+ sxml-transforms
+ matchable
+ (chicken irregex)
+ shell
+ srfi-13
+ intarweb
+ html-parser)
+
+(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 ()
+ ((_ fn-body expr ...)
+ (lambda (x) (fn-body expr ... x)))))
+
+(define (replace from to str)
+ (irregex-replace/all from str to))
+
+(define (html-unescape html)
+ (pipe html
+ (@ replace "&quot;" "\"")
+ (@ replace "&#39;" "'")
+ (@ replace "&amp;" "&")
+ (@ replace "&lt;" "<")
+ (@ replace "&gt;" ">")))
+
+(define (highlight ext code)
+ (define tmp-file-path (create-temporary-file ext))
+ (with-output-to-file tmp-file-path
+ (lambda () (display code)))
+ (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 "<code>~A</code>"
+ (call/cc (@ replace-code-block (irregex-match-substring m 1)))))
+
+ (irregex-replace/all (irregex "<code>(.*?)</code>" 's) html
+ highlight-match))
+
+(define (insert-to-page-tmpl tmpl-path html)
+ (format (read-string #f (open-input-file tmpl-path))
+ html))
+
+(define (app c)
+ (define html-text
+ (pipe "article.md"
+ (@ convert-md-to-html)
+ (@ highlight-code-blocks)
+ (@ insert-to-page-tmpl "index.html")))
+
+ (send-response body: html-text
+ headers: '((content-type #(text/html ((charset . utf-8)))))))
+
+(thread-start!
+ (lambda ()
+ (print "starting nrepl on port 1234")
+ (nrepl-prompt (lambda () (display "#;0> ")))
+ (nrepl 1234)))
+
+(vhost-map `((".*" . ,(lambda (c) (app c)))))
+
+(start-server)
diff --git a/article.md b/article.md
new file mode 100644
index 0000000..bc0d9db
--- /dev/null
+++ b/article.md
@@ -0,0 +1,42 @@
+# Article title
+
+Some text
+
+[Link text](http://example.com)
+
+Paragraph with inline _Python code_: `%lang py% import foo`, wonder what this looks like.
+
+## Subtitle
+
+ %lang c%
+ int main() {
+ return 0;
+ }
+
+### Let's try a Lisp
+
+ %lang scm%
+ (define foo (+ 1 2 3 4))
+
+### And Rust
+
+ %lang rs%
+ const STARTING_MISSILES: i32 = 8;
+ const READY_AMOUNT: i32 = 2;
+
+ fn main() {
+ let mut missiles: i32 = STARTING_MISSILES;
+ let ready: i32 = READY_AMOUNT;
+ println!("Firing {} of my {} missiles...", ready, missiles);
+ missiles = missiles - ready;
+ println!("{} missiles left", missiles);
+ }
+
+## client side jäsä (juuso commission)
+
+ %lang js%
+ alert("beenis");
+
+> This is a quote
+>
+> > Alas poor Yorick
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..b1e4892
--- /dev/null
+++ b/index.html
@@ -0,0 +1,80 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <title>Test</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;1,300&display=swap" rel="stylesheet">
+ <style>
+ /* highlight theme: Kwrite Editor */
+ body.hl { background-color:#e0eaee; }
+ pre.hl { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace; white-space: pre-wrap; }
+ .hl.num { color:#b07e00; }
+ .hl.esc { color:#ff00ff; }
+ .hl.sng { color:#bf0303; }
+ .hl.pps { color:#818100; }
+ .hl.slc { color:#838183; font-style:italic; }
+ .hl.com { color:#838183; font-style:italic; }
+ .hl.ppc { color:#008200; }
+ .hl.opt { color:#000000; }
+ .hl.ipl { color:#0057ae; }
+ .hl.lin { color:#555555; user-select: none; }
+ .hl.hvr { cursor:help; }
+ .hl.erm { color:#ff0000; font-weight:bold; border:solid 1px red; margin-left: 3em; }
+ .hl.err { color:#ff0000; font-weight:bold; }
+ .hl.kwa { color:#000000; font-weight:bold; }
+ .hl.kwb { color:#0057ae; }
+ .hl.kwc { color:#000000; }
+ .hl.kwd { color:#010181; }
+ .hl.kwe { color:#0d5bc3; }
+ .hl.kwf { color:#750dc3; }
+
+ body {
+ font-size: 20px;
+ margin: 20px auto;
+ padding: 10px 15px;
+ max-width: 800px;
+
+ font-family: "Merriweather", serif;
+ font-optical-sizing: auto;
+ font-weight: 400;
+ font-style: normal;
+
+ line-height: 35px;
+ }
+
+ code {
+ padding: 8px 10px;
+ background-color: #eee;
+ }
+
+ pre {
+ background-color: #eee;
+ padding: 8px 10px;
+ overflow-x: scroll;
+ }
+ pre code {
+ background-color: initial;
+ padding: 0;
+ }
+
+ a {
+ color: #0090a0;
+ }
+
+ blockquote {
+ display: block;
+ background-color: #0090a020;
+ background-opacity: 0.9;
+ border-left: 10px solid #0090a0;
+ margin: 15px 0;
+ padding: 2px 10px;
+ quotes: "\201C""\201D""\2018""\2019";
+ }
+ </style>
+</head>
+<body>
+ ~A
+</body>
+</html>