capybara/render.rkt

210 lines
8.4 KiB
Racket
Raw Normal View History

2021-06-07 07:56:53 +00:00
#lang racket/base
2021-06-06 04:59:21 +00:00
2021-06-10 22:48:29 +00:00
(require racket/fasl racket/list racket/match racket/port racket/path racket/set racket/string
2021-06-10 05:02:35 +00:00
racket/runtime-path
2021-06-07 07:56:53 +00:00
markdown markdown/display-xexpr markdown/toc
2021-06-10 05:02:35 +00:00
"compiler.rkt" "defs.rkt" "fetch.rkt"
2021-06-07 09:09:20 +00:00
threading
2021-06-07 03:09:23 +00:00
(prefix-in sass: sass)
2021-06-06 04:59:21 +00:00
(prefix-in mathml: "ext-mathml/main.rkt")
2021-06-07 03:09:23 +00:00
(prefix-in syntax: "ext-syntax/main.rkt")
2021-06-08 07:14:44 +00:00
(prefix-in page: "templates/page.html.rkt"))
2021-06-06 04:59:21 +00:00
2021-06-11 00:44:03 +00:00
(struct input-doc [metadata xref-name text] #:transparent)
(struct ir-doc [metadata xref-name html] #:prefab)
2021-06-07 02:38:03 +00:00
2021-06-10 05:02:35 +00:00
(define-runtime-path *render.scss* "render.scss")
2021-06-11 00:44:03 +00:00
(define *index.md* (build-path "index.md"))
(define (read-input-doc xref-name [port (current-input-port)])
2021-06-07 02:38:03 +00:00
(define metadata (read port))
(unless (metadata? metadata)
(error "post front matter is not valid metadata!"))
(define text (port->string port))
2021-06-11 00:44:03 +00:00
(input-doc metadata xref-name text))
2021-06-07 09:09:20 +00:00
2021-06-07 09:31:39 +00:00
(define *xref-char* "^")
(define *user-char* "@")
(define *hashtag-char* "#")
;; transforms the xexpr into an xexpr that includes links to entities
(define (extract-text-starting-with xexpr start-char tag-name)
(define re (pregexp (string-append start-char "(\\p{L}|\\p{N})+")))
2021-06-07 09:09:20 +00:00
(define (process+ xexpr)
(match xexpr
2021-06-10 05:02:35 +00:00
;; do not descend into other special tags
[(list (or 'code 'math 'tech 'deftech 'masto 'user 'xref) attrs children ...) (list xexpr)]
2021-06-07 09:09:20 +00:00
;; recursive
[(list tag attrs children ...)
(list (cons tag (cons attrs
(apply append
(map (lambda (child) (process+ child)) children)))))]
2021-06-07 09:31:39 +00:00
;; extract and transform the prefixed text
2021-06-07 09:09:20 +00:00
[(? string? str)
2021-06-07 09:31:39 +00:00
(define posns (regexp-match-positions* re str))
2021-06-07 09:09:20 +00:00
(define-values [items last-pos]
(for/fold ([items '()] [last-pos 0]) ([pos (in-list posns)])
2021-06-07 09:31:39 +00:00
(define value (substring str (car pos) (cdr pos)))
(values (cons `(,tag-name ([target ,(substring value 1)]) ,value)
2021-06-07 09:09:20 +00:00
(cons (substring str last-pos (car pos)) items))
(cdr pos))))
(reverse (cons (substring str last-pos) items))]))
2021-06-07 09:31:39 +00:00
(first (process+ xexpr)))
(define (extract-text-starting-with/many xexprs start-char tag-name)
(map (λ (xexpr) (extract-text-starting-with xexpr start-char tag-name)) xexprs))
2021-06-07 09:09:20 +00:00
2021-06-07 09:31:39 +00:00
;; extracts prefixed links and converts them into the <xref> pseudo-tag
(define (extract-prefixed-links xexpr start-char tag-name)
(define (pred? s)
(and (string? s) (> (string-length s) 1) (string=? start-char (substring s 0 1))))
(define (extract-prefixed-links+ xexpr)
(match xexpr
[(list 'a attrs body ...)
(match (assoc 'href attrs)
[(list _ (? pred? value))
(define target (substring value 1))
`(,tag-name ([target ,target]) ,@(map extract-prefixed-links+ body))]
[_ (apply list 'a attrs (map extract-prefixed-links+ body))])]
[(list tag attrs children ...)
(apply list tag attrs (map extract-prefixed-links+ children))]
[(? string? str) str]))
(extract-prefixed-links+ xexpr))
2021-06-11 00:44:03 +00:00
2021-06-07 09:31:39 +00:00
(define (extract-prefixed-links/many xexprs start-char tag-name)
(map (λ (xexpr) (extract-prefixed-links xexpr start-char tag-name)) xexprs))
2021-06-07 02:38:03 +00:00
2021-06-11 00:44:03 +00:00
(define (check-xrefs xexprs xrefs-repo)
(define (check-xrefs-help xexpr)
(match xexpr
[(list 'xref (list (list 'target target)) body ...)
(define target-path (explode-path (build-path "/" target)))
(unless (set-member? xrefs-repo target-path)
(error "invalid xref!" target))
(map check-xrefs-help body)]
[(list tag attrs body ...)
(map check-xrefs-help body)]
[(? string?) (void)]))
(map check-xrefs-help xexprs)
xexprs)
(define (input-doc->ir-doc doc xrefs-repo)
(match-define (input-doc md xref-name text) doc)
2021-06-07 02:38:03 +00:00
(define output-raw (parse-markdown text))
2021-06-07 09:31:39 +00:00
(define output-cooked
(~> output-raw syntax:transform-xexprs mathml:transform-xexprs
(extract-text-starting-with/many *hashtag-char* 'hashtag)
(extract-text-starting-with/many *user-char* 'user)
(extract-prefixed-links/many *xref-char* 'xref)
2021-06-11 00:44:03 +00:00
(extract-prefixed-links/many *user-char* 'user)
(check-xrefs xrefs-repo)))
(ir-doc md xref-name output-cooked))
2021-06-07 02:38:03 +00:00
2021-06-07 09:09:20 +00:00
;; lowers pseudo-tags into their final form
(define (lower-specials xexpr base-url)
(define (lower-specials* xexpr)
(match xexpr
;; hashtags
2021-06-07 09:31:39 +00:00
[(list 'hashtag (list (list 'target target)) body ...)
2021-06-10 05:02:35 +00:00
`(a ([class "hashtag"] [href ,(format "~a/hashtag/~a" base-url target)])
2021-06-07 09:31:39 +00:00
,@(map lower-specials* body))]
2021-06-07 09:09:20 +00:00
;; xrefs
[(list 'xref (list (list 'target target)) body ...)
2021-06-10 05:02:35 +00:00
`(a ([class "xref"] [href ,(format "~a/xref/~a" base-url target)])
2021-06-07 09:09:20 +00:00
,@(map lower-specials* body))]
2021-06-07 09:31:39 +00:00
;; users
[(list 'user (list (list 'target target)) body ...)
2021-06-10 05:02:35 +00:00
`(a ([class "user"] [href ,(format "~a/user/~a" base-url target)])
2021-06-07 09:31:39 +00:00
,@(map lower-specials* body))]
2021-06-10 05:02:35 +00:00
;; deftech and tech
[(list 'deftech (list (list 'key key)) (? string? body))
`(em ([class "deftech"] [id ,(format "tech-~a" key)]) ,body)]
[(list 'tech (list (list 'key key)) (? string? body))
`(a ([class "tech"] [href ,(format "~a/tech/~a" base-url key)]) ,body)]
;; masto
2021-06-10 22:48:29 +00:00
;; TODO make this not janky
2021-06-10 05:02:35 +00:00
[(list 'p _ (list 'masto _ (? string? url)))
(masto-fetch-embed url)]
2021-06-07 09:09:20 +00:00
;; everything else
[(list tag attrs children ...)
(apply list tag attrs (map lower-specials* children))]
[(? string? str) str]))
(lower-specials* xexpr))
(define (ir-doc->page doc)
2021-06-11 00:44:03 +00:00
(match-define (ir-doc md xref-name content) doc)
2021-06-07 09:09:20 +00:00
(let ([content (lower-specials content "https://awoo.systems")])
(define content-toc (toc content))
(define document
2021-06-08 07:14:44 +00:00
(page:execute
2021-06-07 09:09:20 +00:00
(hash 'metadata md
2021-06-11 00:44:03 +00:00
'xref-name xref-name
2021-06-07 09:09:20 +00:00
'content-toc content-toc
'content content)))
(with-output-to-string (λ () (display "<!doctype html>") (display-xexpr document)))))
(define (compile-index-scss index.scss)
2021-06-10 05:02:35 +00:00
(define scss-files (cons *render.scss* (append mathml:scss-files syntax:scss-files)))
2021-06-07 03:09:23 +00:00
(define top-level-style
(string-join
(cons index.scss (map (λ (x) (format "@import \"~a\";" (path->string x))) scss-files))
"\n"))
2021-06-07 09:09:20 +00:00
(sass:compile/string top-level-style #t))
2021-06-06 04:59:21 +00:00
2021-06-10 22:48:29 +00:00
(define (scan-for-rules [output-dir (build-path "target")]
[build-dir (build-path "build")]
[src-dir (build-path "src")])
(define-rule (scss [out (build-path output-dir "index.css")]
[in (build-path src-dir "index.scss")])
(~> (compile-index-scss (port->string in)) (write-string out)))
2021-06-11 00:44:03 +00:00
(struct rule-spec [src intermediate output xref-name] #:transparent)
2021-06-10 22:48:29 +00:00
(define rule-specs
(parameterize ([current-directory src-dir])
(for/list ([md-file (in-directory #f)]
2021-06-11 00:44:03 +00:00
#:when (equal? (path-get-extension md-file) #".md"))
2021-06-10 22:48:29 +00:00
(define fasl-file (path-replace-extension md-file #".fasl"))
(define out-file (path-replace-extension md-file #".html"))
2021-06-11 00:44:03 +00:00
(define xref-name
(let-values ([(base name dir?) (split-path md-file)])
(if (equal? *index.md* name)
(if (eq? 'relative base)
(build-path "/")
(build-path "/" base))
(build-path "/" (path-replace-extension md-file #"")))))
2021-06-10 22:48:29 +00:00
(rule-spec (build-path src-dir md-file)
(build-path build-dir fasl-file)
2021-06-11 00:44:03 +00:00
(build-path output-dir out-file)
xref-name))))
(define xrefs-repo
(for/set ([spec (in-list rule-specs)])
(explode-path (rule-spec-xref-name spec))))
2021-06-10 22:48:29 +00:00
(define intermediate-rules
(for/list ([spec (in-list rule-specs)])
(define-rule (intermediate-rule [out (rule-spec-intermediate spec)]
[in (rule-spec-src spec)])
2021-06-11 00:44:03 +00:00
(~> (read-input-doc (rule-spec-xref-name spec) in)
(input-doc->ir-doc xrefs-repo)
s-exp->fasl (write-bytes out)))
2021-06-10 22:48:29 +00:00
intermediate-rule))
(define output-rules
(for/list ([spec (in-list rule-specs)])
(define-rule (output-rule [out (rule-spec-output spec)]
[in (rule-spec-intermediate spec)])
2021-06-11 00:44:03 +00:00
(~> (port->bytes in) fasl->s-exp ir-doc->page (write-string out)))
2021-06-10 22:48:29 +00:00
output-rule))
(append intermediate-rules output-rules (list scss)))
2021-06-07 02:38:03 +00:00
(module+ main
2021-06-07 07:56:53 +00:00
(require racket/cmdline)
(command-line
#:program "meow"
2021-06-10 22:48:29 +00:00
#:args ()
(generate/execute (scan-for-rules))))