Skip to content

Commit 2e3b768

Browse files
committed
Merge branch 'release/v0.17.2'
2 parents 91190ee + df86064 commit 2e3b768

File tree

2 files changed

+108
-95
lines changed

2 files changed

+108
-95
lines changed

README.md

Lines changed: 107 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,62 @@
11
# 🌱 zeed-dom
22

3-
- Lightweight virtual / offline DOM (Document Object Model)
4-
- Great to use in node or exporting to plain strings
5-
- Written in Typescript
6-
- Generates HTML and XML
7-
- Parses HTML
8-
- Supports some CSS selectors and queries
9-
- JSX compatible
10-
- Easy content manipulation (e.g. through `element.handle` helper)
11-
- Pretty print HTML (`tidyDOM`)
3+
> **A modern, lightweight, TypeScript virtual DOM for Node.js, browser, and static content generation.**
4+
5+
---
6+
7+
- ⚡️ **Fast**: Efficient HTML parsing and serialization
8+
- 🧩 **JSX Compatible**: Works seamlessly with JSX/TSX
9+
- 🔍 **CSS Selectors**: Query with a subset of CSS selectors
10+
- 🛠 **Easy Manipulation**: Chainable API, `.handle()` helper, and more
11+
- 📝 **HTML, XML, Markdown, Plaintext**: Serialize to multiple formats
12+
- 🧹 **Pretty Print**: Tidy up HTML with `tidyDOM`
13+
- 🦾 **TypeScript**: Full typings, modern codebase
14+
- 🔒 **Safe HTML**: Output sanitized HTML for user content
15+
16+
**Note:** This project does not aim for full browser DOM completeness, but covers most practical use cases for static content, SSR, and offline DOM manipulation.
1217

13-
**Does not aim for completeness!**
18+
---
1419

15-
## Get started
20+
## 🚀 Get Started
1621

1722
```sh
1823
npm i zeed-dom
1924
```
2025

21-
## Related projects
26+
## 🔗 Related Projects
2227

23-
- [zeed](https://github.com/holtwick/zeed) - Foundation library
24-
- [zerva](https://github.com/holtwick/zerva) - Event driven server
25-
- [hostic](https://github.com/holtwick/hostic) - Static site generator
28+
- [zeed](https://github.com/holtwick/zeed) – Foundation library
29+
- [zerva](https://github.com/holtwick/zerva) – Event-driven server
2630

27-
Used by [TipTap](https://www.tiptap.dev/) in its [html-package](https://github.com/ueberdosis/tiptap/tree/aac0193050228a8b6237d84f1eb587cfc0d08e24/packages/html).
31+
> **Used by [TipTap](https://www.tiptap.dev/) in its [html-package](https://github.com/ueberdosis/tiptap/tree/main/packages/html).**
32+
33+
---
34+
35+
## ✨ Features
36+
37+
- Virtual DOM tree with `VNode`, `VElement`, `VDocument`, etc.
38+
- HTML parsing and serialization
39+
- XML output support
40+
- CSS selector engine (subset)
41+
- JSX/TSX support (see below)
42+
- Safe HTML serialization (`serializeSafeHTML`)
43+
- Markdown and plaintext serialization
44+
- Manipulation helpers: `.handle()`, `.replaceWith()`, `.remove()`, etc.
45+
- Works in Node.js, browser, and serverless
46+
- Pretty print HTML (`tidyDOM`)
47+
- TypeScript-first API
2848

29-
## Utils
49+
---
50+
51+
## 🛠 Usage Examples
3052

3153
### Manipulation
3254

33-
Drop in HTML and query and change it. Returns HTML again. Nice for post processing.
55+
Drop in HTML, query, and change it. Returns HTML again. Great for post-processing:
3456

3557
```ts
58+
import { handleHTML } from 'zeed-dom'
59+
3660
const newHTML = handleHTML(html, (document) => {
3761
const img = document.querySelector('.img-wrapper img')
3862
if (img)
@@ -42,41 +66,37 @@ const newHTML = handleHTML(html, (document) => {
4266

4367
### Serialization
4468

45-
Take any HTML node or document an serialize it so some other format:
69+
Take any HTML node or document and serialize it to another format:
4670

4771
- `serializePlaintext(node)`: Readable and searchable plain text
4872
- `serializeMarkdown(node)`: Simple Markdown
49-
- `serializeSafeHTML(node)` or `safeHTML(htmlString)`: Just allow some basic tags and attributes
73+
- `serializeSafeHTML(node)` or `safeHTML(htmlString)`: Allow only basic tags and attributes
5074

51-
## Example
52-
53-
A simple example without JSX:
75+
### Virtual DOM Example (no JSX)
5476

5577
```js
5678
import { h, xml } from 'zeed-dom'
5779

5880
const dom = h(
5981
'ol',
60-
{
61-
class: 'projects',
62-
},
82+
{ class: 'projects' },
6383
[
6484
h('li', null, 'zeed ', h('img', { src: 'logo.png' })),
6585
h('li', null, 'zeed-dom'),
6686
]
6787
)
6888

6989
console.log(dom.render())
70-
// Output: <ol class="projects"><li>zeed <img src="logo.png"></li><li>zeed-dom</li></ol>
90+
// <ol class="projects"><li>zeed <img src="logo.png"></li><li>zeed-dom</li></ol>
7191

7292
console.log(dom.render(xml))
73-
// Output: <ol class="projects"><li>zeed <img src="logo.png" /></li><li>zeed-dom</li></ol>
93+
// <ol class="projects"><li>zeed <img src="logo.png" /></li><li>zeed-dom</li></ol>
7494
```
7595

76-
And this one with JSX:
96+
### JSX Example
7797

7898
```jsx
79-
import { h } from "zeed-dom"
99+
import { h } from 'zeed-dom'
80100

81101
let dom = (
82102
<ol className="projects">
@@ -85,61 +105,37 @@ let dom = (
85105
</ol>
86106
)
87107

88-
let projects = dom
89-
.querySelectorAll("li")
90-
.map((e) => e.textContent)
91-
.join(", ")
92-
93-
console.log(projects)
94-
// Output: zeed, zeed-dom
95-
96-
dom.handle("li", (e) => {
97-
if (!e.textContent.endsWith("-dom")) {
108+
dom.handle('li', (e) => {
109+
if (!e.textContent.endsWith('-dom')) {
98110
e.remove()
99111
} else {
100-
e.innerHTML = "<b>zeed-dom</b> - great DOM helper for static content"
112+
e.innerHTML = '<b>zeed-dom</b> - great DOM helper for static content'
101113
}
102114
})
103115

104116
console.log(dom.render())
105-
// Output: <ol class="projects"><li><b>zeed-dom</b> - great DOM helper for static content</li></ol>
117+
// <ol class="projects"><li><b>zeed-dom</b> - great DOM helper for static content</li></ol>
106118
```
107119

108-
In the second example you can see the special manipulation helper `.handle(selector, fn)` in action. You can also see HTML parsing works seamlessly. You can also parse directly:
120+
### HTML Parsing & Tidy
109121

110122
```js
111123
import { tidyDOM, vdom } from 'zeed-dom'
112124

113125
const dom = vdom('<div>Hello World</div>')
114126
tidyDOM(dom)
115127
console.log(dom.render())
116-
// Output is pretty printed like: <div>
128+
// Output is pretty printed like:
129+
// <div>
117130
// Hello World
118131
// </div>
119132
```
120133

121-
These examples are available at [/example](/example).
122-
123-
## JSX
124-
125-
Usually JSX is optimized for React i.e. it expects `React.creatElement` to exist and be the factory for generating the nodes. You can of course get the same effect here if you set up a helper like this:
126-
127-
```js
128-
import { html } from 'zeed-dom'
129-
130-
const React = {
131-
createElement: html,
132-
}
133-
```
134-
135-
But more common is the use of `h` as the factory function. Here is how you can set up this behavior for various environments:
134+
---
136135

137-
> In case of error messages on JSX in your Typescript project, try to add `npm install -D @types/react`.
138-
136+
## ⚛️ JSX Setup
139137

140-
### TypeScript
141-
142-
In [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/compiler-options-in-msbuild.html#mappings):
138+
JSX is supported out of the box. For TypeScript, add to your `tsconfig.json`:
143139

144140
```json
145141
{
@@ -150,7 +146,7 @@ In [`tsconfig.json`](https://www.typescriptlang.org/docs/handbook/compiler-optio
150146
}
151147
```
152148

153-
To avoid type checking issues you should add this to you `shims.d.ts`:
149+
Add this to your `shims.d.ts`:
154150

155151
```ts
156152
// https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements
@@ -161,52 +157,69 @@ declare namespace JSX {
161157
}
162158
```
163159

164-
### [ESBuild](https://github.com/evanw/esbuild)
165-
166-
In options:
160+
For ESBuild:
167161

168162
```js
169163
{
170164
jsxFactory: 'h'
171165
}
172166
```
173167

174-
Or alternatively as [command line option](https://github.com/evanw/esbuild#command-line-usage): `--jsx-factory=h`
168+
Or as a CLI option: `--jsx-factory=h`
175169

176-
### Browser DOM
177-
178-
The JSX factory can also be used to directly create HTML DOM nodes in the browser. Just create the `h` function and let it use the browser's `document` object:
170+
For browser DOM:
179171

180172
```js
181173
const { hFactory } = require('zeed-dom')
182-
183174
export const h = hFactory({ document })
184175
```
185176

186-
## Performance
177+
---
178+
179+
## 🧪 API Highlights
180+
181+
- `vdom(htmlString)`: Parse HTML to virtual DOM
182+
- `tidyDOM(node)`: Pretty print/format DOM
183+
- `serializeSafeHTML(node)`: Output safe HTML
184+
- `serializeMarkdown(node)`: Output Markdown
185+
- `serializePlaintext(node)`: Output plain text
186+
- `handleHTML(html, fn)`: Manipulate HTML with a callback
187+
- `VElement`, `VNode`, `VDocument`, etc.: Core classes
188+
- `.handle(selector, fn)`: Manipulate elements by selector
189+
- `.querySelector`, `.querySelectorAll`: CSS selector queries
190+
- `.replaceWith()`, `.remove()`, `.setAttribute()`, etc.: DOM-like methods
187191

188-
The parser isn't doing too bad, according to the benchmarks of [htmlparser-benchmark](https://github.com/AndreasMadsen/htmlparser-benchmark/blob/master/stats.txt) ;)
192+
---
193+
194+
## 🚦 Performance
195+
196+
The parser is fast, as shown in [htmlparser-benchmark](https://github.com/AndreasMadsen/htmlparser-benchmark/blob/master/stats.txt) (2025-07-19):
189197

190198
```
191-
tl : 1.02699 ms/file ± 0.679139
192-
htmlparser2 : 1.98505 ms/file ± 2.94434
193-
node-html-parser : 2.24176 ms/file ± 1.52112
194-
neutron-html5parser: 2.36648 ms/file ± 1.38879
195-
html5parser : 2.39891 ms/file ± 2.83056
196-
htmlparser2-dom : 2.57523 ms/file ± 3.35587
197-
html-dom-parser : 2.84910 ms/file ± 3.61615
198-
libxmljs : 3.81665 ms/file ± 2.79295
199-
zeed-dom : 5.05130 ms/file ± 3.57184
200-
htmljs-parser : 5.58557 ms/file ± 6.47597
201-
parse5 : 9.07862 ms/file ± 6.50856
202-
htmlparser : 21.2274 ms/file ± 150.951
203-
html-parser : 30.9104 ms/file ± 24.3930
204-
saxes : 49.5906 ms/file ± 141.194
205-
html5 : 114.771 ms/file ± 148.345
199+
htmljs-parser : 0.153576 ms/file ± 0.339639
200+
tl : 0.344457 ms/file ± 0.209764
201+
htmlparser2 : 0.543453 ms/file ± 0.438753
202+
html5parser : 0.605998 ms/file ± 0.387632
203+
neutron-html5parser: 0.606776 ms/file ± 0.324339
204+
htmlparser2-dom : 0.713802 ms/file ± 0.551285
205+
node-html-parser : 0.811724 ms/file ± 0.532164
206+
zeed-dom : 1.09377 ms/file ± 0.595654
207+
sax : 1.84714 ms/file ± 1.50359
208+
parse5 : 1.99615 ms/file ± 1.36227
209+
arijs-stream : 4.34379 ms/file ± 2.40653
210+
arijs-tree : 4.68313 ms/file ± 2.57017
211+
html5 : 4.81755 ms/file ± 3.35113
212+
htmlparser : 7.98449 ms/file ± 57.5936
213+
html-parser : 8.33241 ms/file ± 6.56205
214+
saxes : 24.3492 ms/file ± 70.5843
206215
```
207216

208-
## Misc
217+
---
218+
219+
## 📝 Misc
209220

210-
- To set namespace colons in JSX use double underscore i.e. `<xhtml__link />` becomes `<xhtml:link />`
211-
- To allow `CDATA` use the helper function e.g. `<div>{ CDATA(yourRawData) }</div>`
212-
- `style` attributes can handle objects e.g. `<span style={{backgroundColor: 'red'}} />` becomes `<span style="background-color: red" />`
221+
- Use double underscore in JSX for namespaces: `<xhtml__link />``<xhtml:link />`
222+
- Use `CDATA` helper for raw data: `<div>{CDATA(yourRawData)}</div>`
223+
- `style` attributes can be objects: `<span style={{backgroundColor: 'red'}} />``<span style="background-color: red" />`
224+
- Works in Node.js, browser, and serverless
225+
- TypeScript-first, but works with plain JS too

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "zeed-dom",
33
"type": "module",
4-
"version": "0.17.1",
4+
"version": "0.17.2",
55
"description": "🌱 Lightweight offline DOM",
66
"author": {
77
"name": "Dirk Holtwick",

0 commit comments

Comments
 (0)