Add sitemap generation example.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
608ea2f9e8
commit
47402cb1e2
5
deps.edn
5
deps.edn
|
@ -1,8 +1,11 @@
|
|||
{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
|
||||
org.clojure/clojurescript {:mvn/version "1.11.60"}
|
||||
org.clojure/core.async {:mvn/version "1.6.673"}
|
||||
org.clojure/data.xml {:mvn/version "0.2.0-alpha8"}
|
||||
funcool/promesa {:mvn/version "9.0.494"}
|
||||
|
||||
;; simpler date time
|
||||
tick/tick {:mvn/version "0.6.2"}
|
||||
;;ajax requests
|
||||
cljs-ajax/cljs-ajax {:mvn/version "0.8.1"}
|
||||
|
||||
|
@ -25,7 +28,7 @@
|
|||
org.babashka/sci {:mvn/version "0.7.39"}
|
||||
org.babashka/sci.configs {:git/url "https://github.com/babashka/sci.configs"
|
||||
:sha "33bd51e53700b224b4cb5bda59eb21b62f962745"}
|
||||
datascript/datascript {:mvn/version "1.4.2"}
|
||||
datascript/datascript {:mvn/version "1.5.1"}
|
||||
|
||||
|
||||
com.bhauman/figwheel-main {:mvn/version "0.2.18"}}
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
"@codemirror/state": "^6.2.0",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@codemirror/view": "^6.10.1",
|
||||
"@js-joda/core": "3.2.0",
|
||||
"@js-joda/locale_en-us": "3.1.1",
|
||||
"@js-joda/timezone": "2.5.0",
|
||||
"@nextjournal/lang-clojure": "^1.0.0",
|
||||
"axe-core": "4.7.0",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
|
||||
Future Content
|
||||
|
||||
- Dynamic html with macro for frontend generation on build.
|
||||
- Complete frontend backend example wth ring retit reagent
|
||||
|
||||
clj -m figwheel.main --build dev --repl
|
||||
|
||||
https://www.clojuriststogether.org/news/call-for-new-proposals.-june-survey-results./
|
||||
|
|
|
@ -3,13 +3,21 @@
|
|||
|
||||
* Introduction
|
||||
|
||||
Below you will find some simple examples of using map api's inside clojure, you can use the inline eval for some of the example where sci supports a specific library.
|
||||
Install npm dependencies
|
||||
|
||||
|
||||
** Setup for downloaded version
|
||||
Install the npm dependencies for react then launch shadow via the terminal or jack in via your IDE.
|
||||
#+BEGIN_SRC html :results silent :exports none :tangle readme.org
|
||||
Install dependencies with.
|
||||
npm install
|
||||
|
||||
Run the project
|
||||
Run the project with the below command
|
||||
npx shadow-cljs server app
|
||||
|
||||
Alternatively jack into the project from your ide.
|
||||
|
||||
|
||||
* Google Maps
|
||||
|
||||
|
@ -86,6 +94,8 @@ You will need to provide your own api key, the examples are blank and render a w
|
|||
|
||||
|
||||
** Load in the google maps script by adding it to the dom
|
||||
Code pulled from google
|
||||
|
||||
#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
|
||||
(defn load-google-maps-script [api-key]
|
||||
(let [script (.createElement js/document "script")]
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
#+TITLE: 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.
|
||||
|
||||
#+BEGIN_SRC html :results silent :exports none :tangle readme.org
|
||||
Install dependencies with.
|
||||
npm install
|
||||
|
||||
Run the project with the below command
|
||||
npx shadow-cljs server app
|
||||
|
||||
Alternatively jack into the project from your ide.
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC edn :results silent :exports none :tangle deps.edn
|
||||
{:paths ["src" "resources"]
|
||||
:deps
|
||||
{org.clojure/clojure {:mvn/version "1.10.0"}
|
||||
org.clojure/clojurescript {:mvn/version "1.11.60"}
|
||||
org.clojure/data.xml {:mvn/version "0.2.0-alpha8"}
|
||||
|
||||
tick/tick {:mvn/version "0.6.2"}
|
||||
reagent/reagent {:mvn/version "1.2.0"}
|
||||
thheller/shadow-cljs {:mvn/version "2.24.0"}}}
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC edn :results silent :exports none :tangle shadow-cljs.edn
|
||||
{:deps {:aliases [:dev]}
|
||||
:dev-http {8080 ["resources/public/" "classpath:public"]}
|
||||
:source ["src" "../../components"]
|
||||
:builds {:app {:output-dir "resources/public/cljs-out/"
|
||||
:asset-path "/cljs-out"
|
||||
:target :browser
|
||||
:compiler-options {:infer-externs :auto
|
||||
:externs ["datascript/externs.js"]
|
||||
:output-feature-set :es6}
|
||||
:modules {:main_bundle {:init-fn clojure-demo.core/startup!}}
|
||||
:devtools {:after-load app.main/reload!}}}}
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC json :results silent :exports none :tangle package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"shadow-cljs": "^2.23.3",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0"
|
||||
}
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC html :results silent :exports none :tangle resources/public/index.html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Clojure demos</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
App loading here
|
||||
</div>
|
||||
<script src="/cljs-out/main_bundle.js" type="application/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
#+END_SRC
|
||||
|
||||
** 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.
|
||||
#+BEGIN_SRC clojure :exports none :tangle src/clojure_demo/sitemap.cljc
|
||||
(ns clojure-demo.sitemap
|
||||
(:require
|
||||
[clojure.data.xml :as xml]
|
||||
[tick.core :as tick]))
|
||||
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC clojure :results value :tangle src/clojure_demo/sitemap.cljc
|
||||
;;#?(: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"]))
|
||||
|
||||
#+END_SRC
|
||||
|
||||
** 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
|
||||
#+BEGIN_SRC clojurescript :exports none :tangle src/clojure_demo/core.cljs
|
||||
(ns clojure-demo.core
|
||||
(:require
|
||||
[clojure.data.xml :as xml]
|
||||
["react-dom/client" :refer [createRoot]]
|
||||
[reagent.core :as reagent]
|
||||
[clojure-demo.sitemap :refer [pwa-sitemap]])
|
||||
(:require-macros [clojure-demo.sitemap :refer [spit-pwa-sitemap]]))
|
||||
#+END_SRC
|
||||
|
||||
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.
|
||||
#+BEGIN_SRC clojurescript :results value :tangle src/clojure_demo/core.cljs
|
||||
;;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" ))
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC clojure :exports none :tangle src/clojure_demo/core.cljs
|
||||
(defn current-page []
|
||||
[:pre (str (pwa-sitemap "https://example.com" ["/one" "/two"]))])
|
||||
|
||||
(defn mount-root-page []
|
||||
;; this select the main node from the html file and injects your page content
|
||||
(.render
|
||||
(createRoot (.getElementById js/document "app"))
|
||||
(reagent/as-element [current-page])))
|
||||
|
||||
;; call our macro to generate the sitemap file
|
||||
(spit-pwa-sitemap "sitemap.xml" "https://example.com" ["/one" "/two"])
|
||||
|
||||
(def startup! (mount-root-page))
|
||||
#+END_SRC
|
|
@ -8,6 +8,7 @@
|
|||
["@nextjournal/lang-clojure" :refer [clojure]]
|
||||
["react-dom/client" :refer [createRoot]]
|
||||
["tarts" :as tarts]
|
||||
[tick.core :as t]
|
||||
[cljs.core.async :as async]
|
||||
[cljs.core.async.interop :as async-in]
|
||||
[shadow.cljs.modern :refer [js-await]]
|
||||
|
@ -16,6 +17,7 @@
|
|||
[cl-eorg.parser :as o :refer [parse]]
|
||||
[cl-eorg.themes.tachyon :refer [tachyon-theme]]
|
||||
[clojure.string :as str]
|
||||
[clojure.data.xml :as xml]
|
||||
[honey.sql :as sql]
|
||||
[honey.sql.helpers :as sqlh]
|
||||
[reagent.core :as reagent]
|
||||
|
@ -62,6 +64,8 @@
|
|||
(def sql-ns (sci/create-ns 'honey.sql.core nil))
|
||||
(def sqlh-ns (sci/create-ns 'honey.sql.helpers nil))
|
||||
(def shadow-ns (sci/create-ns 'shadow.cljs.modern nil))
|
||||
(def xml-ns (sci/create-ns 'clojure.data.xml nil))
|
||||
(def tick-ns (sci/create-ns 'tick.core nil))
|
||||
|
||||
;; core async does not work with sci
|
||||
(def async-ns (sci/create-ns 'cljs.core.async nil))
|
||||
|
@ -70,6 +74,7 @@
|
|||
(def sci-ctx
|
||||
(sci/init
|
||||
{:classes {'js js/globalThis :allow :all}
|
||||
:features #{:cljs}
|
||||
:namespaces
|
||||
{'reagent sci-reagent/reagent-namespace
|
||||
'promesa sci-promesa/promesa-namespace
|
||||
|
@ -79,6 +84,13 @@
|
|||
'shadow {'js-await (sci/copy-var my-js-await shadow-ns)}
|
||||
;'async-in {'<p! (sci/copy-var async-in/<p! async-in-ns)}
|
||||
'd sci-datascript/core-namespace
|
||||
'tick {'format (sci/copy-var t/format tick-ns)
|
||||
'zoned-date-time (sci/copy-var t/zoned-date-time tick-ns)}
|
||||
'xml {'emit-str (sci/copy-var xml/emit-str xml-ns)
|
||||
;; dont think indent-str & alias uri is supported in clojurescript
|
||||
;;'indent-str (sci/copy-var xml/indent-str xml-ns)
|
||||
;;'alias-uri (sci/copy-var xml/alias-uri xml-ns)
|
||||
}
|
||||
'sql {'format (sci/copy-var sql/format sql-ns)
|
||||
#_#_'raw (sci/copy-var sql/raw sql-ns)}
|
||||
'sqlh {'select (sci/copy-var sqlh/select sqlh-ns)
|
||||
|
@ -139,7 +151,7 @@
|
|||
;(into [:<>] children)
|
||||
))})))
|
||||
(defn code-editor
|
||||
[{:keys [export class results]} content]
|
||||
[{:keys [exports class results]} content]
|
||||
(let [language class
|
||||
editor (atom nil)
|
||||
evaled-result (reagent/atom nil)
|
||||
|
@ -177,7 +189,7 @@
|
|||
:parent @editor})))))
|
||||
:component-will-unmount (fn [_] (.destroy @view))
|
||||
:reagent-render (fn []
|
||||
(if export
|
||||
(if (= exports "none")
|
||||
nil
|
||||
[:div.mb-8
|
||||
[:div.ba.ma-0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}]
|
||||
|
@ -297,8 +309,12 @@
|
|||
:intro "Some example applications"
|
||||
:key ::examples
|
||||
:demos [{:title "Maps"
|
||||
:description ""
|
||||
:description "Some quick examples using frontend map api's like google maps."
|
||||
:page "example-maps"
|
||||
:icon-image "https://avatars.githubusercontent.com/u/2181346?s=200&v=4"}
|
||||
{:title "XML sitemap"
|
||||
:description "Using data.xml to generate a sitemap"
|
||||
:page "example-xml-sitemap"
|
||||
:icon-image "https://avatars.githubusercontent.com/u/2181346?s=200&v=4"}]}
|
||||
:terminology {:title "Terminology"
|
||||
:intro ""
|
||||
|
@ -318,7 +334,9 @@
|
|||
:ci-demo
|
||||
{:file "documents/ci-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
:example-maps
|
||||
{:file "documents/examples-maps.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
{:file "documents/examples-maps.org" :git-link ""}
|
||||
:example-xml-sitemap
|
||||
{:file "documents/examples-xml-sitemap.org" :git-link "https://github.com/atomjuice/dsl-demo"}
|
||||
:containers
|
||||
{:file "documents/containers.org" :git-link "https://github.com/atomjuice/containers"}}})
|
||||
|
||||
|
|
Loading…
Reference in New Issue