clojure-demos/resources/public/documents/examples-xml-sitemap.org

6.4 KiB

Generating xml to produce a sitemap

Introduction

In this example we generate a sitemap the example uses reader conditionals so the code works in clojure and clojurescript it also has a simple macro to output the file if used in the frontend.

Clojure(script) sitemap generation function

This will work in clojure and clojurescript, the code is slightly different to account for missing feature in the clojurescript version.

We can use a macro to generate a sitemap from a client side app on startup, or just use it directly if generating on a server.

;;#?(:clj (xml/alias-uri 'sitem "http://www.sitemaps.org/schemas/sitemap/0.9"))

(defn pwa-sitemap [domain routes]
  #?(:cljs (xml/emit-str
        {:tag :urlset
         :attrs {#_#_:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"}
         :content
         (mapv (fn [r]
                 {:tag :url
                  :attrs {}
                  :content [{:tag :loc :attrs {} :content [(str domain r)]}
                            {:tag :lastmod :attrs {} :content [(tick/format :iso-zoned-date-time (tick/zoned-date-time))]}]}
                 ) routes)})
     :clj (xml/indent-str
        {:tag :urlset
         :attrs {:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"}
         :content
         (mapv (fn [r]
                 {:tag :url
                  :attrs {}
                  :content [{:tag :loc :attrs {} :content [(str domain r)]}
                            {:tag :lastmod :attrs {} :content [(tick/format :iso-zoned-date-time (tick/zoned-date-time))]}]}
                 ) routes)})))

(defmacro spit-pwa-sitemap
  "Generate the sitemap and outut to the provided path"
  {:arglists '([body] [options & body]), :style/indent 0}
  [path domain urls]
  #?(:clj (spit path (pwa-sitemap domain urls))
     :cljs nil))


(comment
  (pwa-sitemap "https://example.com/" ["/one" "/two"])
  (spit-pwa-sitemap "sitemap.xml" "https://example.com/" ["/one" "/two"]))

Clojurescript usage

This is an example of creating the sitemap inside a frontend application, it call the macro which is evaluated at build and outputs the sitemap.xml

When working with data.xml it is very important to set the xml namespace to avoid an alias being appended to your xml, the namespace should be set in the root xml element and via alias-uri then make sure all element tag value are called via the namespace.

Also note namespacing is not supported in clojurescript so alias-uri is not available Look into why :xmlns breaks sci rendering Adding the k

Because our routes are just data we could add a :sitemap key for the handler maps and filter the routes based on the key or even run custom functions to build up the sitemap dynamically when the contest is generated from a database.

You can reuse the code in the cljc file the sitemap function is replicated here as sci does not work with the reader conditionals in the code.

;;Should be set in clojure but it is not supported in clojurescript currently
;;(xml/alias-uri 'sitemap "http://www.sitemaps.org/schemas/sitemap/0.9")
(def routes
  [["/"
    {:name ::frontpage
     :view prn}]

   ["/about"
    {:name ::about
     :view prn}]

   ["/item/:id"
    {:name ::item
     :view prn
     :parameters {:path {:id int?}}}]])

;; map first over the routes so we only get strings
(->> routes
     (mapv first)
     (pwa-sitemap "https://example.com" ))