Was running into some random issues with some YAML syntaxes being parsed weirdly
in markdown-clj. So here's a quick function which parses out the Front Matter
programmatically and parses it with clj-yaml
.
{:deps {markdown-clj/markdown-clj {:mvn/version "1.11.0"}
clj-commons/clj-yaml {:mvn/version "0.7.108"}}}
(ns com.evanlouie.markdown.core
"Markdown helpers."
{:author "Evan Louie"}
(:require
[clojure.string :as string]
[clj-yaml.core :as yaml]
[markdown.core :as md]))
(defn parse-string-seq
"Converts a sequence markdown lines into a map containing:
::frontmatter -> map from parsed YAML
::html -> HTML string"
[markdown-string-seq]
(when-some [lines (seq markdown-string-seq)]
(->> lines
(transduce
(comp
(drop-while (comp empty? string/trim))
(map string/trimr)
(partition-by (partial = "---"))
(map-indexed (fn [idx group] [idx group])))
(fn
;; Completing - run markdown and yaml parsers on markdown and
;; frontmatter strings
([acc]
(let [html (->> (or (::potential-fm acc)
(::markdown acc))
(string/join \newline)
((fn [s] (md/md-to-html-string
s
:heading-anchors true
:reference-links? true
:footnotes? true))))
frontmatter (->> (::frontmatter acc)
(string/join \newline)
(yaml/parse-string)
(into {}))]
{::frontmatter frontmatter
::html html}))
;; Loop over partitions
([acc [idx group]]
(cond
;; first group is just a "---" -> the next group is POTENTIALLY
;; front matter
(and (zero? idx)
(= (first group) "---"))
(assoc acc
::fm-opening-found? true)
;; Front matter starting delimitter found and have not found the
;; closing delimitter -> this group is POTENTIALLY front matter
(and (::fm-opening-found? acc)
(not (::fm-closing-found? acc))
(not (::potential-fm acc)))
(assoc acc
::potential-fm group)
;; Potential FM found, the closing delimitter was not found
;; previously, and this group is a closing delimitter ->
;; The potential FM is for sure FM
(and (::fm-opening-found? acc)
(::potential-fm acc)
(not (::fm-closing-found? acc))
(= 1 (count group))
(= "---" (first group)))
(-> acc
(assoc
::fm-closing-found? true
::frontmatter (::potential-fm acc))
(dissoc ::potential-fm))
;; Skip FM workflow and just add to the markdown body
:else
(assoc acc
::markdown (concat (::markdown acc) group)))))
{}))))
(defn parse-string
"Parse the provided markdown string."
[markdown-string]
(->> (string/split-lines markdown-string)
(parse-string-seq)))
(defn parse
"Parse the provided markdown file."
[file]
(with-open [rdr (io/reader file)]
(when-some [lines (line-seq rdr)]
(parse-string-seq lines))))