Skip to content

Commit 1de9d3d

Browse files
committed
feat: align types to lib.dom.d.ts and fix querySelector bugs
1 parent 79e32cd commit 1de9d3d

12 files changed

Lines changed: 1061 additions & 490 deletions

File tree

README.md

Lines changed: 175 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# domparser-rs
22

3-
A super fast Node.js addon for HTML parsing and manipulation, written in Rust. It aims to provide a standard-compliant DOM API for Node.js.
3+
A super fast Node.js addon for HTML parsing and manipulation, written in Rust. It provides a standard-compliant DOM API for Node.js, mirroring the browser's built-in `DOMParser`.
44

55
## Features
66

7-
- **Standard Compliant**: Implements standard DOM APIs including `DOMParser`, `querySelector`, `classList`, and more, making it easy to migrate from browser-based code.
7+
- **Standard Compliant**: Type definitions aligned to `lib.dom.d.ts` — uses `Node`, `Element`, `Document`, `Text`, `Comment`, and other standard DOM interfaces.
88
- High-performance DOM parsing and manipulation
99
- Exposes a simple JavaScript API via NAPI-RS
1010
- Designed for both server-side and CLI HTML processing
@@ -21,159 +21,213 @@ npm install domparser-rs
2121
## Usage
2222

2323
```js
24-
const { parse } = require('domparser-rs');
25-
const root = parse('<div id="foo" class="bar">hello <span>world</span></div>');
24+
const { DOMParser } = require('domparser-rs');
2625

27-
const div = root.select('div');
26+
const parser = new DOMParser();
27+
const doc = parser.parseFromString('<div id="foo" class="bar">hello <span>world</span></div>', 'text/html');
28+
29+
const div = doc.querySelector('div');
2830
console.log(div.getAttribute('id')); // "foo"
29-
console.log(div.text()); // "hello world"
31+
console.log(div.textContent); // "hello world"
3032
div.setAttribute('title', 'my-title');
31-
console.log(div.outerHtml()); // <div id="foo" class="bar" title="my-title">hello <span>world</span></div>
33+
console.log(div.outerHTML); // <div id="foo" class="bar" title="my-title">hello <span>world</span></div>
3234
```
3335

3436
## API Documentation
3537

36-
### `parse(html: string): NodeRepr`
38+
### `DOMParser`
39+
40+
```ts
41+
class DOMParser {
42+
parseFromString(string: string, type: DOMParserSupportedType): Document;
43+
}
44+
```
45+
46+
Parses a string using the specified MIME type (e.g., `"text/html"`) and returns a `Document`.
47+
48+
---
49+
50+
### `Document`
51+
52+
Extends `Node`. Represents the entire HTML document.
53+
54+
#### Properties
3755

38-
Parses an HTML string and returns a `NodeRepr` instance representing the root node.
56+
| Property | Type | Description |
57+
|---|---|---|
58+
| `doctype` | `DocumentType \| null` | The DTD associated with the document |
59+
| `documentElement` | `Element \| null` | The root element (e.g., `<html>`) |
60+
| `head` | `Element \| null` | The `<head>` element |
61+
| `body` | `Element \| null` | The `<body>` element |
62+
| `title` | `string` | The document title |
63+
| `children` | `Element[]` | Child elements |
64+
| `childElementCount` | `number` | Number of child elements |
65+
| `firstElementChild` | `Element \| null` | First child element |
66+
| `lastElementChild` | `Element \| null` | Last child element |
3967

40-
### `DOMParser` Class
68+
#### Factory Methods
4169

42-
#### `parseFromString(string: string, mimeType: string): NodeRepr`
43-
Parses a string using the specified MIME type (e.g., "text/html").
70+
- `createElement(tagName: string): Element`
71+
- `createTextNode(data: string): Text`
72+
- `createComment(data: string): Comment`
73+
- `createDocumentFragment(): DocumentFragment`
74+
- `createProcessingInstruction(target: string, data: string): ProcessingInstruction`
75+
- `importNode<T extends Node>(node: T, deep?: boolean): T`
76+
- `adoptNode<T extends Node>(node: T): T`
77+
78+
#### Query Methods
79+
80+
- `getElementById(elementId: string): Element | null`
81+
- `getElementsByClassName(classNames: string): Element[]`
82+
- `getElementsByTagName(qualifiedName: string): Element[]`
83+
- `querySelector(selectors: string): Element | null`
84+
- `querySelectorAll(selectors: string): Element[]`
85+
- `append(...nodes: (Node | string)[]): void`
86+
- `prepend(...nodes: (Node | string)[]): void`
4487

4588
---
4689

47-
### `NodeRepr` Class
48-
49-
Represents a DOM node and provides various manipulation methods.
50-
51-
#### Properties (Getters/Setters)
52-
53-
- `nodeType`: number
54-
- `nodeName`: string
55-
- `tagName`: string | null
56-
- `namespaceURI`: string | null
57-
- `prefix`: string | null
58-
- `localName`: string | null
59-
- `id`: string
60-
- `className`: string
61-
- `parentNode`: NodeRepr | null
62-
- `parentElement`: NodeRepr | null
63-
- `firstChild`: NodeRepr | null
64-
- `lastChild`: NodeRepr | null
65-
- `previousSibling`: NodeRepr | null
66-
- `nextSibling`: NodeRepr | null
67-
- `firstElementChild`: NodeRepr | null
68-
- `lastElementChild`: NodeRepr | null
69-
- `previousElementSibling`: NodeRepr | null
70-
- `nextElementSibling`: NodeRepr | null
71-
- `children`: NodeRepr[]
72-
- `childElementCount`: number
73-
- `nodeValue`: string | null
74-
- `data`: string | null
75-
- `textContent`: string
76-
- `innerHTML`: string
77-
- `outerHTML`: string
78-
- `ownerDocument`: NodeRepr | null
79-
- `childNodes`: NodeRepr[]
80-
- `isConnected`: boolean
81-
- `doctype`: NodeRepr | null
82-
- `head`: NodeRepr | null
83-
- `body`: NodeRepr | null
84-
- `title`: string
85-
- `documentElement`: NodeRepr | null
86-
87-
#### Manipulation Methods
88-
89-
- `append(newChild: NodeRepr): void`
90-
- `appendChild(newChild: NodeRepr): NodeRepr`
91-
- `prepend(newChild: NodeRepr): void`
92-
- `after(newSibling: NodeRepr): void`
93-
- `before(newSibling: NodeRepr): void`
94-
- `insertBefore(newNode: NodeRepr, refNode?: NodeRepr | null): NodeRepr`
95-
- `replaceChild(newChild: NodeRepr, oldChild: NodeRepr): NodeRepr`
96-
- `replaceWith(newNode: NodeRepr): void`
97-
- `remove(): void`
98-
- `clone(): NodeRepr` (Shallow clone)
99-
- `cloneRecursive(): NodeRepr` (Deep clone)
100-
- `cloneNode(deep?: boolean): NodeRepr`
101-
- `importNode(externalNode: NodeRepr, deep?: boolean): NodeRepr`
102-
- `adoptNode(externalNode: NodeRepr): NodeRepr`
103-
- `normalize(): void`
90+
### `Element`
91+
92+
Extends `Node`. Represents an HTML element.
93+
94+
#### Properties
95+
96+
| Property | Type | Description |
97+
|---|---|---|
98+
| `tagName` | `string` | The tag name |
99+
| `localName` | `string` | The local part of the qualified name |
100+
| `namespaceURI` | `string \| null` | The namespace URI |
101+
| `prefix` | `string \| null` | The namespace prefix |
102+
| `id` | `string` | The `id` attribute |
103+
| `className` | `string` | The `class` attribute |
104+
| `classList` | `DOMTokenList` | Live token list of class names |
105+
| `dataset` | `Record<string, string>` | Data attributes |
106+
| `innerHTML` | `string` | Inner HTML content |
107+
| `outerHTML` | `string` | Outer HTML content |
108+
| `children` | `Element[]` | Child elements |
109+
| `childElementCount` | `number` | Number of child elements |
110+
| `firstElementChild` | `Element \| null` | First child element |
111+
| `lastElementChild` | `Element \| null` | Last child element |
112+
| `previousElementSibling` | `Element \| null` | Previous sibling element |
113+
| `nextElementSibling` | `Element \| null` | Next sibling element |
104114

105115
#### Attribute Methods
106116

107-
- `getAttribute(name: string): string | null`
108-
- `setAttribute(name: string, value: string): void`
109-
- `removeAttribute(name: string): void`
110-
- `toggleAttribute(name: string, force?: boolean): boolean`
111-
- `hasAttribute(name: string): boolean`
117+
- `getAttribute(qualifiedName: string): string | null`
118+
- `setAttribute(qualifiedName: string, value: string): void`
119+
- `removeAttribute(qualifiedName: string): void`
120+
- `toggleAttribute(qualifiedName: string, force?: boolean): boolean`
121+
- `hasAttribute(qualifiedName: string): boolean`
122+
- `hasAttributes(): boolean`
112123
- `getAttributeNames(): string[]`
113-
- `getAttributes(): Record<string, string>`
114124
- `getAttributeNS(namespace: string | null, localName: string): string | null`
115-
- `setAttributeNS(namespace: string | null, name: string, value: string): void`
125+
- `setAttributeNS(namespace: string | null, qualifiedName: string, value: string): void`
116126
- `removeAttributeNS(namespace: string | null, localName: string): void`
117127
- `hasAttributeNS(namespace: string | null, localName: string): boolean`
118128

119129
#### Query & Selection Methods
120130

121-
- `select(selectors: string): NodeRepr | null`
122-
- `selectAll(selectors: string): NodeRepr[]`
123-
- `querySelector(selectors: string): NodeRepr | null`
124-
- `querySelectorAll(selectors: string): NodeRepr[]`
125-
- `getElementById(id: string): NodeRepr | null`
126-
- `getElementsByClassName(classNames: string): NodeRepr[]`
127-
- `getElementsByTagName(tagName: string): NodeRepr[]`
131+
- `querySelector(selectors: string): Element | null`
132+
- `querySelectorAll(selectors: string): Element[]`
133+
- `getElementById(id: string): Element | null`
134+
- `getElementsByClassName(classNames: string): Element[]`
135+
- `getElementsByTagName(qualifiedName: string): Element[]`
136+
- `closest(selectors: string): Element | null`
128137
- `matches(selectors: string): boolean`
129-
- `closest(selectors: string): NodeRepr | null`
130-
- `contains(otherNode: NodeRepr): boolean`
131-
132-
#### ClassList & Dataset
133-
134-
- `classListAdd(className: string): void`
135-
- `classListRemove(className: string): void`
136-
- `classListToggle(className: string, force?: boolean): boolean`
137-
- `classListContains(className: string): boolean`
138-
- `datasetGet(): Record<string, string>`
139-
- `datasetSet(key: string, value: string): void`
140-
- `datasetRemove(key: string): void`
141-
142-
#### Other Methods
143-
144-
- `text(): string`
145-
- `innerHtml(): string`
146-
- `outerHtml(): string`
147-
- `createElement(tagName: string): NodeRepr`
148-
- `createTextNode(data: string): NodeRepr`
149-
- `createComment(data: string): NodeRepr`
150-
- `createDocumentFragment(): NodeRepr`
151-
- `createProcessingInstruction(target: string, data: string): NodeRepr`
152-
- `isSameNode(otherNode: NodeRepr): boolean`
153-
- `isEqualNode(otherNode: NodeRepr): boolean`
154-
- `compareDocumentPosition(other: NodeRepr): number`
155-
- `lookupPrefix(namespace?: string): string | null`
156-
- `lookupNamespaceURI(prefix?: string): string | null`
157-
- `isDefaultNamespace(namespace?: string): boolean`
158-
- `getRootNode(): NodeRepr`
138+
139+
#### Mutation Methods
140+
141+
- `append(...nodes: (Node | string)[]): void`
142+
- `prepend(...nodes: (Node | string)[]): void`
143+
- `before(...nodes: (Node | string)[]): void`
144+
- `after(...nodes: (Node | string)[]): void`
145+
- `remove(): void`
146+
- `replaceWith(...nodes: (Node | string)[]): void`
147+
- `insertAdjacentHTML(position: InsertPosition, html: string): void`
148+
- `insertAdjacentText(position: InsertPosition, text: string): void`
149+
- `insertAdjacentElement(position: InsertPosition, element: Element): Element | null`
150+
151+
---
152+
153+
### `Node`
154+
155+
Base interface for all DOM nodes.
156+
157+
#### Properties
158+
159+
| Property | Type | Description |
160+
|---|---|---|
161+
| `nodeType` | `number` | The type of the node |
162+
| `nodeName` | `string` | The name of the node |
163+
| `nodeValue` | `string \| null` | The value of the node |
164+
| `textContent` | `string \| null` | The text content |
165+
| `parentNode` | `Node \| null` | The parent node |
166+
| `parentElement` | `Element \| null` | The parent element |
167+
| `firstChild` | `Node \| null` | The first child |
168+
| `lastChild` | `Node \| null` | The last child |
169+
| `previousSibling` | `Node \| null` | The previous sibling |
170+
| `nextSibling` | `Node \| null` | The next sibling |
171+
| `childNodes` | `Node[]` | All child nodes |
172+
| `ownerDocument` | `Document \| null` | The owner document |
173+
174+
#### Methods
175+
176+
- `appendChild<T extends Node>(node: T): T`
177+
- `removeChild<T extends Node>(child: T): T`
178+
- `insertBefore<T extends Node>(node: T, child: Node | null): T`
179+
- `replaceChild<T extends Node>(node: Node, child: T): T`
180+
- `cloneNode(deep?: boolean): Node`
181+
- `contains(other: Node | null): boolean`
159182
- `hasChildNodes(): boolean`
160-
- `hasAttributes(): boolean`
183+
- `getRootNode(): Node`
184+
- `normalize(): void`
185+
- `isSameNode(otherNode: Node | null): boolean`
186+
- `isEqualNode(otherNode: Node | null): boolean`
187+
- `compareDocumentPosition(other: Node): number`
188+
- `lookupNamespaceURI(prefix: string | null): string | null`
189+
- `lookupPrefix(namespace: string | null): string | null`
190+
- `isDefaultNamespace(namespace: string | null): boolean`
191+
192+
---
193+
194+
### `CharacterData`
195+
196+
Extends `Node`. Base interface for `Text`, `Comment`, and `ProcessingInstruction`.
161197

162-
#### Text Node Methods
198+
#### Properties & Methods
163199

164-
- `splitText(offset: number): NodeRepr`
200+
- `data: string`
201+
- `readonly length: number`
165202
- `substringData(offset: number, count: number): string`
166203
- `appendData(data: string): void`
167204
- `insertData(offset: number, data: string): void`
168205
- `deleteData(offset: number, count: number): void`
169206
- `replaceData(offset: number, count: number, data: string): void`
170207

171-
#### Insertion Methods
208+
### `Text` extends `CharacterData`
172209

173-
- `insertAdjacentElement(position: string, element: NodeRepr): NodeRepr | null`
174-
- `insertAdjacentText(position: string, data: string): void`
175-
- `insertAdjacentHTML(position: string, html: string): void`
210+
- `splitText(offset: number): Text`
176211

212+
### `Comment` extends `CharacterData`
213+
214+
### `ProcessingInstruction` extends `CharacterData`
215+
216+
- `readonly target: string`
217+
218+
### `DocumentType` extends `Node`
219+
220+
- `readonly name: string`
221+
- `readonly publicId: string`
222+
- `readonly systemId: string`
223+
224+
### `DocumentFragment` extends `Node`
225+
226+
- `getElementById(elementId: string): Element | null`
227+
- `querySelector(selectors: string): Element | null`
228+
- `querySelectorAll(selectors: string): Element[]`
229+
230+
---
177231

178232
## Contributing
179233

@@ -191,4 +245,4 @@ npm run benchmark
191245

192246
---
193247

194-
For more usage examples and advanced API, see the source code and benchmarks in the repository.
248+
For more usage examples and advanced API, see the source code and tests in the repository.

0 commit comments

Comments
 (0)