1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# Dompa
[](https://github.com/askonomm/dompa/actions/workflows/tests.yml)
[](https://codescene.io/projects/72504)
A zero-dependency, runtime-agnostic HTML parser (and builder).
Dompa is continuesly tested to run in the following Clojure runtimes:
- Clojure
- ClojureScript
- Babashka
## Installation
You can fetch it straight from GitHub by adding this to your `deps.edn`:
```clojure
{:deps {askonomm/dompa {:git/url "https://github.com/askonomm/dompa"
:git/tag "v1.0.0"
:git/sha "af50c1c03b4a4812de868fd74941801152e85bbf"}}}
```
## Usage
### Parsing HTML
You can use Dompa to parse an HTML string into a vector of nodes like this:
```clojure
(ns my.app
(:require [dompa.html :as html])
(html/->nodes "<div>hello <strong>world</strong></div>")
```
Which would result in a data structure such as:
```clojure
[{:node/name :div
:node/attrs {}
:node/children [{:node/name :dompa/text
:node/value "hello "}
{:node/name :strong
:node/attrs {}
:node/children [{:node/name :dompa/text
:node/value "world"}]}]}]
```
### Creating HTML
You can turn a vector of nodes, such as those above, back into a HTML string as well:
```clojure
(ns my.app
(:require [dompa.nodes :as nodes])
(nodes/->html [...])
```
Which would then result in a HTML string.
### Traversing and modifying nodes
There's a convenience function `dompa.nodes/traverse` which helps you traverse and modify a node tree, like this:
```clojure
(ns my.app
(:require [dompa.nodes :refer [traverse]])
(defn update-text-value [node]
(if (= :dompa/text (:node/name node))
(assoc node :node/value "updated text")
node))
(traverse [...] update-text-value)
```
The above would update all text nodes to have the value of "updated text". If you wish to keep a node unchanged, just return the node as-is, and if you wish to remove a node, return `nil`.
### Create nodes with the `$` helper function
If you want a more convenient way of creating nodes, without having to create the full map manually, you can use the `dompa.nodes/$` helper function.
Creating a node is as simple as:
```clojure
(ns my.app
(:require [dompa.nodes :refer [$]])
($ :button)
```
You can also add attributes to it:
```clojure
(ns my.app
(:require [dompa.nodes :refer [$]])
($ :button {:class "some-btn"})
```
And text nodes can be created by simply omitting the first node name keyword, like so:
```clojure
(ns my.app
(:require [dompa.nodes :refer [$]])
($ "hello world")
```
Which now when you put both together can look like this:
```clojure
(ns my.app
(:require [dompa.nodes :refer [$]])
($ :button {:class "some-btn"}
($ "hello world"))
```
It's important to note that when creating a text node, it cannot have any children nodes, only literal values and/or vars, whereas non-text nodes can have children as either their second argument, if they lack a attribute map, or as their third argument if they have an argument map.
### Create HTML with the `defhtml` helper macro
If you want a more convenient way of creating functions that convert nodes into HTML on their own during compile-time, you can use the `defhtml` macro:
```clojure
(ns my.app
(:require [dompa.nodes :refer [defhtml $]])
(defhtml hello-page [who]
($ :div
($ "hello " who)))
(hello-page "world")
```
And of course making lists and such works the same as you'd imagine:
```clojure
(ns my.app
(:require [dompa.nodes :refer [defhtml $]])
(def names ["john" "mike" "jenna" "bob"])
(defhtml hello-page []
($ :div
(map #($ (str "hello " %)) names)))
(hello-page)
```
Do note that when using the `defhtml` macro in ClojureScript, you have to use `:refer-macros` instead of `:refer`, due to the differences in how ClojureScript deals with macros.
### Other
While the above covers the most common use cases of Dompa, such as turning HTML into a tree of nodes and vice versa, you can actually make use of the underlying lower-level stuff that makes it all work separately as well:
- `dompa.coordinates/compose` - create coordinates (range positions) of HTML nodes from a HTML string
- `dompa.coordinates/unify` - unify coordinates (merges the same block nodes together)
- `dompa.coordinates/->nodes` - transform coordinates into a tree of nodes
- `dompa.html/->coordinates` - transform HTML into coordinates
|