Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 77 additions & 10 deletions src/dompa/nodes.cljc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns dompa.nodes
(:require [clojure.zip :as zip]))
(:require
[clojure.zip :as zip]))

(def ^:private default-void-nodes
#{:!doctype :!DOCTYPE :area :base :br :col :embed :hr :img :input
Expand Down Expand Up @@ -35,7 +36,9 @@

:else
(let [value (nodes->html-fn (-> node :node/children))]
(str html "<" node-name node-attrs ">" value "</" node-name ">")))))))
(str html "<" node-name node-attrs ">" value "</" node-name ">"))))

:else "")))

(defn traverse
"Recursively traverses given tree of `nodes` with a `traverser-fn`
Expand All @@ -55,13 +58,13 @@
"Creates a zipper for given a given `node`."
[node]
(zip/zipper
(fn branch? [node]
(boolean (seq (:node/children node))))
(fn children [node]
(:node/children node))
(fn make-node [node children]
(assoc node :node/children children))
node))
(fn branch? [node]
(boolean (seq (:node/children node))))
(fn children [node]
(:node/children node))
(fn make-node [node children]
(assoc node :node/children children))
node))

(defn ->html
"Transform a vector of `nodes` into an HTML string.
Expand Down Expand Up @@ -107,7 +110,7 @@
`(defn ~name ~args
(->html (vector ~@elements)))))

(defn $
(defn $$
"Creates a new node

Usage:
Expand All @@ -128,3 +131,67 @@
(cond-> {:node/name name}
attrs? (assoc :node/attrs attrs)
(seq children) (assoc :node/children (flatten children))))))

(defn- list-of-one?
[coll]
(and (sequential? coll)
(= (count coll) 1)))

(defn- list-of-many?
[coll]
(and (sequential? coll)
(> (count coll) 1)))

(defn- nodes-from-opt
[opt]
(cond (map? opt)
opt

(list-of-many? opt)
{:node/name :<>
:node/children opt}

(list-of-one? opt)
(first opt)

:else
{:node/name :dompa/text
:node/value (str opt)}))

(defn- node-from-opts
[opts]
(let [first-opt (first opts)
second-op (second opts)
attrs? (and (map? second-op)
(not (contains? second-op :node/name)))
attrs (if attrs? second-op {})
children-opts (if attrs? (drop 2 opts) (rest opts))
children-nodes (->> children-opts
(map nodes-from-opt)
flatten)]
(cond-> {:node/name first-opt}
attrs? (assoc :node/attrs attrs)
(seq children-nodes) (assoc :node/children children-nodes))))

(defn- nodes-from-opts
[opts]
(let [nodes (->> (remove nil? opts)
(map nodes-from-opt))]
(if (> (count nodes) 1)
{:node/name :<>
:node/children nodes}
(first nodes))))

(defn $
"Creates a new node

Usage:

```clojure
($ :div
($ \"hello world\" ))
```"
[& opts]
(if (keyword? (first opts))
(node-from-opts opts)
(nodes-from-opts opts)))
30 changes: 17 additions & 13 deletions test/dompa/coordinates_test.cljc
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
(ns dompa.coordinates-test
#?(:clj (:require [clojure.test :refer [deftest testing is]]
[dompa.coordinates :as coordinates]))
#?(:cljs (:require [cljs.test :refer-macros [deftest testing is]]
[dompa.coordinates :as coordinates])))
#?(:clj
(:require
[clojure.test :refer [deftest testing is]]
[dompa.coordinates :as coordinates]))
#?(:cljs
(:require
[cljs.test :refer-macros [deftest testing is]]
[dompa.coordinates :as coordinates])))

(deftest compose-test
(testing "Create coordinates"
Expand Down Expand Up @@ -107,24 +111,24 @@
coordinates/compose
coordinates/unify
coordinates/->nodes))))

(testing "Create nodes from self-closing tags"
(is (= [{:node/name :hr
:node/attrs {}}]
(-> "<hr />"
coordinates/compose
coordinates/unify
coordinates/->nodes))))

(testing "Parse attributes with forward slashes in them"
(is (= [{:node/name :meta,
:node/attrs {:name "route-pattern",
:content "/:user_id/:repository",
(is (= [{:node/name :meta,
:node/attrs {:name "route-pattern",
:content "/:user_id/:repository",
:data-turbo-transient true}}]
(-> "<meta name=\"route-pattern\" content=\"/:user_id/:repository\" data-turbo-transient>"
coordinates/compose
coordinates/unify
coordinates/->nodes))))
(-> "<meta name=\"route-pattern\" content=\"/:user_id/:repository\" data-turbo-transient>"
coordinates/compose
coordinates/unify
coordinates/->nodes))))

(testing "Create nodes with attributes"
(is (= [{:node/name :div
Expand Down
12 changes: 8 additions & 4 deletions test/dompa/html_test.cljc
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
(ns dompa.html-test
#?(:clj (:require [clojure.test :refer [deftest is testing]]
[dompa.html :as html]))
#?(:cljs (:require [cljs.test :refer-macros [deftest testing is]]
[dompa.html :as html])))
#?(:clj
(:require
[clojure.test :refer [deftest is testing]]
[dompa.html :as html]))
#?(:cljs
(:require
[cljs.test :refer-macros [deftest testing is]]
[dompa.html :as html])))

(deftest coordinates-test
(testing "HTML to coordinates"
Expand Down
91 changes: 79 additions & 12 deletions test/dompa/nodes_test.cljc
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
(ns dompa.nodes-test
#?(:clj (:require
[clojure.test :refer [deftest is testing]]
[clojure.zip :as zip]
[dompa.html :as html]
[dompa.nodes :as nodes :refer [$ defhtml]]))
#?(:cljs (:require [cljs.test :refer-macros [deftest testing is]]
[clojure.zip :as zip]
[dompa.nodes :as nodes :refer [$] :refer-macros [defhtml]]
[dompa.html :as html])))
#?(:clj
(:require
[clojure.test :refer [deftest is testing]]
[clojure.zip :as zip]
[dompa.html :as html]
[dompa.nodes :as nodes :refer [$ defhtml]]))
#?(:cljs
(:require
[cljs.test :refer-macros [deftest testing is]]
[clojure.zip :as zip]
[dompa.nodes :as nodes :refer [$] :refer-macros [defhtml]]
[dompa.html :as html])))

(defhtml hello [who]
($ :div
Expand All @@ -21,7 +24,7 @@
($ :ul
(->> items
(map (fn [item]
($ :li ($ item))))
($ :li item)))
(into []))))

(deftest list-items-test
Expand All @@ -33,7 +36,7 @@
(is (= {:node/name :div
:node/children [{:node/name :dompa/text
:node/value "hello world"}]}
($ :div ($ "hello world")))))
($ :div "hello world"))))

(testing "a fragment node"
(is (= {:node/name :<>
Expand All @@ -47,7 +50,71 @@
($ :span
($ "hello"))
($ :span
($ "world")))))))
($ "world"))))))

(testing "a nil node"
(is (= nil
($ nil))))

(testing "a string node"
(is (= {:node/name :dompa/text
:node/value "hello world"}
($ "hello world"))))

(testing "a node of multiple sub-nodes"
(is (= {:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "hello"}
{:node/name :dompa/text
:node/value "12345"}
{:node/name :dompa/text
:node/value "123.3"}
{:node/name :dompa/text
:node/value "world"}]}
($ "hello" 12345 nil 123.3 "world")))

(is (= {:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "hello"}
{:node/name :dompa/text
:node/value "12345"}
{:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "w"}
{:node/name :dompa/text
:node/value "o"}
{:node/name :dompa/text
:node/value "r"}
{:node/name :dompa/text
:node/value "l"}
{:node/name :dompa/text
:node/value "d"}]}]}
($ "hello" 12345 (map #($ %) ["w" "o" "r" "l" "d"]))))

(is (= {:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "hello"}
{:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "w"}
{:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "o"}
{:node/name :dompa/text
:node/value "r"}
{:node/name :<>
:node/children [{:node/name :dompa/text
:node/value "l"}
{:node/name :dompa/text
:node/value "d"}]}]}]}]}
($ "hello"
(map (fn [x]
($ x))
["w" (map (fn [x]
($ x))
["o" "r" (map (fn [x]
($ x))
["l" "d"])])]))))))

(deftest traverse-test
(let [traverser-fn (fn [node]
Expand Down
Loading