the personal playground of evan louie; developer, designer, photographer, and breaker of the web.
   (  )   /\   _                 (     
    \ |  (  \ ( \.(               )                      _____
  \  \ \  `  `   ) \             (  ___                 / _   \
 (_`    \+   . x  ( .\            \/   \____-----------/ (o)   \_
- .-               \+  ;          (  O                           \____
                          )        \_____________  `              \  /
(__                +- .( -'.- <. - _  VVVVVVV VV V\                 \/
(_____            ._._: <_ - <- _  (--  _AAAAAAA__A_/                  |
  .    /./.+-  . .- /  +--  - .     \______________//_              \_______
  (__ ' /x  / x _/ (                                  \___'          \     /
 , x / ( '  . / .  /                                      |           \   /
    /  /  _/ /    +                                      /              \/
   '  (__/                                             /                  \

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))))