@gjsify/domparser 0.3.13 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/esm/index.js +232 -230
- package/lib/esm/register.js +5 -1
- package/package.json +3 -3
package/lib/esm/index.js
CHANGED
|
@@ -1,238 +1,240 @@
|
|
|
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
|
-
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
var DOMNode = class {
|
|
3
|
+
nodeType;
|
|
4
|
+
nodeName;
|
|
5
|
+
nodeValue;
|
|
6
|
+
parentNode = null;
|
|
7
|
+
childNodes = [];
|
|
8
|
+
constructor(nodeType, nodeName, nodeValue = null) {
|
|
9
|
+
this.nodeType = nodeType;
|
|
10
|
+
this.nodeName = nodeName;
|
|
11
|
+
this.nodeValue = nodeValue;
|
|
12
|
+
}
|
|
13
|
+
get textContent() {
|
|
14
|
+
if (this.nodeType === 3 || this.nodeType === 4) return this.nodeValue ?? "";
|
|
15
|
+
return this.childNodes.map((c) => c.textContent ?? "").join("");
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var DOMElement = class extends DOMNode {
|
|
19
|
+
tagName;
|
|
20
|
+
localName;
|
|
21
|
+
_attrs = new Map();
|
|
22
|
+
constructor(tagName) {
|
|
23
|
+
super(1, tagName.toUpperCase());
|
|
24
|
+
this.tagName = tagName.toLowerCase();
|
|
25
|
+
this.localName = this.tagName;
|
|
26
|
+
}
|
|
27
|
+
get children() {
|
|
28
|
+
return this.childNodes.filter((n) => n.nodeType === 1);
|
|
29
|
+
}
|
|
30
|
+
getAttribute(name) {
|
|
31
|
+
return this._attrs.has(name) ? this._attrs.get(name) ?? null : null;
|
|
32
|
+
}
|
|
33
|
+
setAttribute(name, value) {
|
|
34
|
+
this._attrs.set(name, value);
|
|
35
|
+
}
|
|
36
|
+
hasAttribute(name) {
|
|
37
|
+
return this._attrs.has(name);
|
|
38
|
+
}
|
|
39
|
+
get attributes() {
|
|
40
|
+
return Array.from(this._attrs.entries()).map(([name, value]) => ({
|
|
41
|
+
name,
|
|
42
|
+
value
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
get innerHTML() {
|
|
46
|
+
return this.childNodes.map((n) => {
|
|
47
|
+
if (n.nodeType === 1) return n.outerHTML;
|
|
48
|
+
if (n.nodeType === 3) return n.nodeValue ?? "";
|
|
49
|
+
if (n.nodeType === 4) return "<![CDATA[" + (n.nodeValue ?? "") + "]]>";
|
|
50
|
+
return "";
|
|
51
|
+
}).join("");
|
|
52
|
+
}
|
|
53
|
+
get outerHTML() {
|
|
54
|
+
const attrs = Array.from(this._attrs.entries()).map(([k, v]) => " " + k + "=\"" + v.replace(/"/g, """) + "\"").join("");
|
|
55
|
+
if (this.childNodes.length === 0) return "<" + this.tagName + attrs + "/>";
|
|
56
|
+
return "<" + this.tagName + attrs + ">" + this.innerHTML + "</" + this.tagName + ">";
|
|
57
|
+
}
|
|
58
|
+
querySelector(selector) {
|
|
59
|
+
const parts = selector.trim().split(/\s*>\s*/);
|
|
60
|
+
if (parts.length > 1) return this._queryChildChain(parts);
|
|
61
|
+
const parts2 = selector.trim().split(/\s+/);
|
|
62
|
+
if (parts2.length > 1) return this._queryDescendantChain(parts2);
|
|
63
|
+
return this._find(selector.trim().toLowerCase()) ?? null;
|
|
64
|
+
}
|
|
65
|
+
querySelectorAll(selector) {
|
|
66
|
+
const tag = selector.trim().toLowerCase();
|
|
67
|
+
const results = [];
|
|
68
|
+
this._findAll(tag, results);
|
|
69
|
+
return results;
|
|
70
|
+
}
|
|
71
|
+
_find(tag) {
|
|
72
|
+
for (const child of this.children) {
|
|
73
|
+
if (child.tagName === tag) return child;
|
|
74
|
+
const found = child._find(tag);
|
|
75
|
+
if (found) return found;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
_findAll(tag, results) {
|
|
80
|
+
for (const child of this.children) {
|
|
81
|
+
if (child.tagName === tag) results.push(child);
|
|
82
|
+
child._findAll(tag, results);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
_queryChildChain(parts) {
|
|
86
|
+
const [first, ...rest] = parts;
|
|
87
|
+
const matching = this.children.filter((c) => c.tagName === first.trim().toLowerCase());
|
|
88
|
+
if (rest.length === 0) return matching[0] ?? null;
|
|
89
|
+
for (const el of matching) {
|
|
90
|
+
const found = el._queryChildChain(rest);
|
|
91
|
+
if (found) return found;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
_queryDescendantChain(parts) {
|
|
96
|
+
const [first, ...rest] = parts;
|
|
97
|
+
const candidates = [];
|
|
98
|
+
this._findAll(first.trim().toLowerCase(), candidates);
|
|
99
|
+
if (rest.length === 0) return candidates[0] ?? null;
|
|
100
|
+
for (const el of candidates) {
|
|
101
|
+
const found = el._queryDescendantChain(rest);
|
|
102
|
+
if (found) return found;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var DOMDocument = class extends DOMElement {
|
|
108
|
+
documentElement = null;
|
|
109
|
+
constructor() {
|
|
110
|
+
super("#document");
|
|
111
|
+
this.nodeType = 9;
|
|
112
|
+
this.nodeName = "#document";
|
|
113
|
+
}
|
|
114
|
+
querySelector(selector) {
|
|
115
|
+
if (this.documentElement) {
|
|
116
|
+
const tag = selector.trim().toLowerCase();
|
|
117
|
+
if (this.documentElement.tagName === tag) return this.documentElement;
|
|
118
|
+
return this.documentElement.querySelector(selector);
|
|
119
|
+
}
|
|
120
|
+
return super.querySelector(selector);
|
|
121
|
+
}
|
|
122
|
+
querySelectorAll(selector) {
|
|
123
|
+
const tag = selector.trim().toLowerCase();
|
|
124
|
+
const results = [];
|
|
125
|
+
if (this.documentElement) {
|
|
126
|
+
if (this.documentElement.tagName === tag) results.push(this.documentElement);
|
|
127
|
+
this.documentElement._findAll(tag, results);
|
|
128
|
+
}
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
128
132
|
const ATTR_PATTERN = /\s+([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))/g;
|
|
129
133
|
function parseAttributes(attrsStr, el) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
let m;
|
|
135
|
+
ATTR_PATTERN.lastIndex = 0;
|
|
136
|
+
while ((m = ATTR_PATTERN.exec(attrsStr)) !== null) {
|
|
137
|
+
el.setAttribute(m[1], m[2] ?? m[3] ?? m[4] ?? "");
|
|
138
|
+
}
|
|
135
139
|
}
|
|
136
140
|
function parseXml(xml) {
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
141
|
+
const doc = new DOMDocument();
|
|
142
|
+
const stack = [doc];
|
|
143
|
+
let i = 0;
|
|
144
|
+
const len = xml.length;
|
|
145
|
+
while (i < len) {
|
|
146
|
+
const ltIdx = xml.indexOf("<", i);
|
|
147
|
+
if (ltIdx === -1) {
|
|
148
|
+
const text = xml.slice(i);
|
|
149
|
+
if (text.trim()) {
|
|
150
|
+
const tn = new DOMNode(3, "#text", text);
|
|
151
|
+
const top = stack[stack.length - 1];
|
|
152
|
+
tn.parentNode = top;
|
|
153
|
+
top.childNodes.push(tn);
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (ltIdx > i) {
|
|
158
|
+
const text = xml.slice(i, ltIdx);
|
|
159
|
+
if (text.trim()) {
|
|
160
|
+
const tn = new DOMNode(3, "#text", text);
|
|
161
|
+
const top = stack[stack.length - 1];
|
|
162
|
+
tn.parentNode = top;
|
|
163
|
+
top.childNodes.push(tn);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (xml.startsWith("<![CDATA[", ltIdx)) {
|
|
167
|
+
const end = xml.indexOf("]]>", ltIdx);
|
|
168
|
+
if (end === -1) break;
|
|
169
|
+
const cn = new DOMNode(4, "#cdata-section", xml.slice(ltIdx + 9, end));
|
|
170
|
+
const top = stack[stack.length - 1];
|
|
171
|
+
cn.parentNode = top;
|
|
172
|
+
top.childNodes.push(cn);
|
|
173
|
+
i = end + 3;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (xml.startsWith("<!--", ltIdx)) {
|
|
177
|
+
const end = xml.indexOf("-->", ltIdx);
|
|
178
|
+
i = end === -1 ? len : end + 3;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (xml.startsWith("<?", ltIdx) || xml.startsWith("<!", ltIdx)) {
|
|
182
|
+
const end = xml.indexOf(">", ltIdx);
|
|
183
|
+
i = end === -1 ? len : end + 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const gtIdx = findTagEnd(xml, ltIdx + 1);
|
|
187
|
+
if (gtIdx === -1) break;
|
|
188
|
+
const tagContent = xml.slice(ltIdx + 1, gtIdx);
|
|
189
|
+
if (tagContent.startsWith("/")) {
|
|
190
|
+
if (stack.length > 1) stack.pop();
|
|
191
|
+
i = gtIdx + 1;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const selfClosing = tagContent.endsWith("/");
|
|
195
|
+
const inner = selfClosing ? tagContent.slice(0, -1) : tagContent;
|
|
196
|
+
const wsIdx = inner.search(/\s/);
|
|
197
|
+
const tagName = (wsIdx === -1 ? inner : inner.slice(0, wsIdx)).trim();
|
|
198
|
+
if (!tagName) {
|
|
199
|
+
i = gtIdx + 1;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const el = new DOMElement(tagName);
|
|
203
|
+
if (wsIdx !== -1) parseAttributes(inner.slice(wsIdx), el);
|
|
204
|
+
const top = stack[stack.length - 1];
|
|
205
|
+
el.parentNode = top;
|
|
206
|
+
top.childNodes.push(el);
|
|
207
|
+
if (top === doc && !doc.documentElement) {
|
|
208
|
+
doc.documentElement = el;
|
|
209
|
+
}
|
|
210
|
+
if (!selfClosing) stack.push(el);
|
|
211
|
+
i = gtIdx + 1;
|
|
212
|
+
}
|
|
213
|
+
return doc;
|
|
210
214
|
}
|
|
215
|
+
/** Find the index of '>' that closes a tag, skipping '>' inside quoted attribute values. */
|
|
211
216
|
function findTagEnd(xml, start) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
class DOMParser {
|
|
229
|
-
parseFromString(string, _mimeType) {
|
|
230
|
-
return parseXml(string);
|
|
231
|
-
}
|
|
217
|
+
let inSingle = false;
|
|
218
|
+
let inDouble = false;
|
|
219
|
+
for (let i = start; i < xml.length; i++) {
|
|
220
|
+
const ch = xml[i];
|
|
221
|
+
if (ch === "\"" && !inSingle) {
|
|
222
|
+
inDouble = !inDouble;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (ch === "'" && !inDouble) {
|
|
226
|
+
inSingle = !inSingle;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (ch === ">" && !inSingle && !inDouble) return i;
|
|
230
|
+
}
|
|
231
|
+
return -1;
|
|
232
232
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
DOMParser
|
|
233
|
+
var DOMParser = class {
|
|
234
|
+
parseFromString(string, _mimeType) {
|
|
235
|
+
return parseXml(string);
|
|
236
|
+
}
|
|
238
237
|
};
|
|
238
|
+
|
|
239
|
+
//#endregion
|
|
240
|
+
export { DOMDocument, DOMElement, DOMNode, DOMParser };
|
package/lib/esm/register.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/domparser",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "DOMParser for GJS — self-contained XML parser with minimal DOM (querySelector, getAttribute, children)",
|
|
5
5
|
"module": "lib/esm/index.js",
|
|
6
6
|
"types": "lib/types/index.d.ts",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"web-api"
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@gjsify/cli": "^0.3.
|
|
44
|
-
"@gjsify/unit": "^0.3.
|
|
43
|
+
"@gjsify/cli": "^0.3.14",
|
|
44
|
+
"@gjsify/unit": "^0.3.14",
|
|
45
45
|
"@types/node": "^25.6.0",
|
|
46
46
|
"typescript": "^6.0.3"
|
|
47
47
|
}
|