1 Architecture overview
The revived site keeps the existing compiler shape: parse files into elements, collect and link metadata, then let a theme render the final site.
`code.doc.parse`
Reads Clojure and markdown sources into a shared document model, now with frontmatter, heading ids, fenced code blocks, and embedded directives.
`code.doc.collect.*`
Pulls referenced namespaces, API metadata, and citations into an interim bundle.
`code.doc.link.*`
Assigns numbering, tags, namespace references, cross-links, and generated API tables.
`code.doc.executive` + theme
Merges theme render functions, page metadata, and templates to emit static HTML pages.
2 Extension points
A theme contract in practice
{:render {:article \"render-article\"\n :outline \"render-outline\"\n :top-level \"render-top-level\"\n :page-meta \"render-page-meta\"\n :site-links \"render-site-links\"}}
3 Walkthrough
3.1 Parsing source forms into elements
code.doc.parse turns Clojure and markdown sources into a sequence of elements. Each element is a map with a :type key and the data needed for later stages.
parse a namespace form and a directive
(-> (nav/parse-string "(ns example.core)")
(parse/parse-ns-form))
=> {:type :ns-form
:indentation 0
:meta {}
:ns 'example.core
:code "(ns example.core)"}
(-> (nav/parse-string "[[:chapter {:title \"hello world\"}]]")
(parse/parse-directive))
=> {:type :chapter :title "hello world"}
parse paragraphs and fact blocks
(-> (nav/parse-string "\"this is a paragraph\"")
(parse/parse-paragraph))
=> {:type :paragraph :text "this is a paragraph"}
(-> (nav/parse-string "(fact \"hello world\" (+ 1 1) \n => 2)")
(parse/parse-fact-form))
=> {:type :test :indentation 2 :caption "hello world" :code "(+ 1 1) \n => 2"}
3.2 Parsing markdown content
Markdown files are first-class sources. parse-frontmatter extracts EDN frontmatter, parse-header recognises heading ids, and parse-markdown weaves together directives, fenced code blocks, and paragraphs.
extract frontmatter and heading ids
(parse/parse-frontmatter ["---" "{:title \"Hello\"}" "---" "# Heading"])
=> [{:type :article}
{:type :article :title "Hello"}
["# Heading"]]
(parse/parse-header "## Section {#section-id}")
=> {:type :section :title "Section" :tag "section-id"}
parse embedded directives and fenced code blocks
(parse/parse-markdown-directive "[[:callout {:tone :info :title \"Hello\" :content \"World\"}]]")
=> {:type :callout :tone :info :title "Hello" :content "World"}
(parse/parse-markdown (str "---\n{:title \"Hello\"}\n---\n"
"# Heading {#heading}\n"
"[[:callout {:tone :success :title \"Nice\" :content \"Yep\"}]]\n"
"```clojure\n(+ 1 1)\n```"))
=> [{:type :article :title "Hello"}
{:type :chapter :title "Heading" :tag "heading"}
{:type :callout :tone :success :title "Nice" :content "Yep"}
{:type :block :indentation 0 :lang "clojure" :code "(+ 1 1)"}]
3.3 Collecting article metadata
The collect layer moves special directives out of the element stream and into lookup tables. This makes namespaces, tags, citations, and article/global options available to later link and render stages.
collect namespaces and article metadata
(collect.base/collect-namespaces
{:articles {"example"
{:elements [{:type :ns-form
:ns 'clojure.core}]}}}
"example")
=> '{:articles {"example" {:elements () :meta {}}}
:namespaces {clojure.core {:type :ns-form :ns clojure.core}}}
(collect.base/collect-article-metas
{:articles {"example" {:elements [{:type :article
:options {:color :light}}]}}}
"example")
=> '{:articles {"example" {:elements []
:meta {:options {:color :light}}}}}
collect global options and tags
(collect.base/collect-global-metas
{:articles {"example" {:elements [{:type :global
:options {:color :light}}]}}}
"example")
=> {:articles {"example" {:elements ()}}
:global {:options {:color :light}}}
(get-in (collect.base/collect-tags
{:articles {"example"
{:elements [{:type :chapter :tag "hello"}
{:type :chapter :tag "world"}]}}}
"example")
[:articles "example" :tags])
=> #{"hello" "world"}
3.4 Linking structure
The link layer turns raw elements into a connected document. Titles become url-friendly tags, headings get numbers, and anchors are indexed for cross-page navigation.
normalise titles into url-friendly tags
(link.tag/tag-string "hello2World/this.Rocks")
=> "hello2-world--this-rocks"
(link.tag/create-tag {:title "hello"} (atom #{"hello" "hello-0" "hello-1"}))
=> {:title "hello" :tag "hello-2"}
number chapters and sections
(link.number/increment 1)
=> "A"
(link.number/increment "1")
=> "2"
(->> (link.number/link-numbers-loop
[{:type :chapter}
{:type :section}
{:type :code :numbered true :title "Code"}]
#{:code})
(mapv :number))
=> ["1" "1.1" "1.1"]
build anchor lookups by tag
(-> (link.anchor/link-anchors
{:anchors-lu {"example" {:by-tag {:a 1 :b 2}}}}
"example")
:anchors)
=> {"example" {:a 1 :b 2}}
3.5 Loading a theme
Finally, code.doc.executive loads the theme map that the renderer consumes. A theme exposes render functions for each page region.
load the bolton theme
(let [theme (executive/load-theme "bolton")]
(keys theme))
=> [:engine :resource :copy :render :manifest]
(let [render-fn (-> (executive/load-theme "bolton")
(get-in [:render :article]))]
[(first render-fn) (var? (second render-fn))])
=> [:fn true]