@gjsify/domparser 0.3.13 → 0.3.15

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 CHANGED
@@ -1,238 +1,240 @@
1
- class DOMNode {
2
- nodeType;
3
- nodeName;
4
- nodeValue;
5
- parentNode = null;
6
- childNodes = [];
7
- constructor(nodeType, nodeName, nodeValue = null) {
8
- this.nodeType = nodeType;
9
- this.nodeName = nodeName;
10
- this.nodeValue = nodeValue;
11
- }
12
- get textContent() {
13
- if (this.nodeType === 3 || this.nodeType === 4) return this.nodeValue ?? "";
14
- return this.childNodes.map((c) => c.textContent ?? "").join("");
15
- }
16
- }
17
- class DOMElement extends DOMNode {
18
- tagName;
19
- localName;
20
- _attrs = /* @__PURE__ */ new Map();
21
- constructor(tagName) {
22
- super(1, tagName.toUpperCase());
23
- this.tagName = tagName.toLowerCase();
24
- this.localName = this.tagName;
25
- }
26
- get children() {
27
- return this.childNodes.filter((n) => n.nodeType === 1);
28
- }
29
- getAttribute(name) {
30
- return this._attrs.has(name) ? this._attrs.get(name) ?? null : null;
31
- }
32
- setAttribute(name, value) {
33
- this._attrs.set(name, value);
34
- }
35
- hasAttribute(name) {
36
- return this._attrs.has(name);
37
- }
38
- get attributes() {
39
- return Array.from(this._attrs.entries()).map(([name, value]) => ({ name, value }));
40
- }
41
- get innerHTML() {
42
- return this.childNodes.map((n) => {
43
- if (n.nodeType === 1) return n.outerHTML;
44
- if (n.nodeType === 3) return n.nodeValue ?? "";
45
- if (n.nodeType === 4) return "<![CDATA[" + (n.nodeValue ?? "") + "]]>";
46
- return "";
47
- }).join("");
48
- }
49
- get outerHTML() {
50
- const attrs = Array.from(this._attrs.entries()).map(([k, v]) => " " + k + '="' + v.replace(/"/g, "&quot;") + '"').join("");
51
- if (this.childNodes.length === 0) return "<" + this.tagName + attrs + "/>";
52
- return "<" + this.tagName + attrs + ">" + this.innerHTML + "</" + this.tagName + ">";
53
- }
54
- querySelector(selector) {
55
- const parts = selector.trim().split(/\s*>\s*/);
56
- if (parts.length > 1) return this._queryChildChain(parts);
57
- const parts2 = selector.trim().split(/\s+/);
58
- if (parts2.length > 1) return this._queryDescendantChain(parts2);
59
- return this._find(selector.trim().toLowerCase()) ?? null;
60
- }
61
- querySelectorAll(selector) {
62
- const tag = selector.trim().toLowerCase();
63
- const results = [];
64
- this._findAll(tag, results);
65
- return results;
66
- }
67
- _find(tag) {
68
- for (const child of this.children) {
69
- if (child.tagName === tag) return child;
70
- const found = child._find(tag);
71
- if (found) return found;
72
- }
73
- return void 0;
74
- }
75
- _findAll(tag, results) {
76
- for (const child of this.children) {
77
- if (child.tagName === tag) results.push(child);
78
- child._findAll(tag, results);
79
- }
80
- }
81
- _queryChildChain(parts) {
82
- const [first, ...rest] = parts;
83
- const matching = this.children.filter((c) => c.tagName === first.trim().toLowerCase());
84
- if (rest.length === 0) return matching[0] ?? null;
85
- for (const el of matching) {
86
- const found = el._queryChildChain(rest);
87
- if (found) return found;
88
- }
89
- return null;
90
- }
91
- _queryDescendantChain(parts) {
92
- const [first, ...rest] = parts;
93
- const candidates = [];
94
- this._findAll(first.trim().toLowerCase(), candidates);
95
- if (rest.length === 0) return candidates[0] ?? null;
96
- for (const el of candidates) {
97
- const found = el._queryDescendantChain(rest);
98
- if (found) return found;
99
- }
100
- return null;
101
- }
102
- }
103
- class DOMDocument extends DOMElement {
104
- documentElement = null;
105
- constructor() {
106
- super("#document");
107
- this.nodeType = 9;
108
- this.nodeName = "#document";
109
- }
110
- querySelector(selector) {
111
- if (this.documentElement) {
112
- const tag = selector.trim().toLowerCase();
113
- if (this.documentElement.tagName === tag) return this.documentElement;
114
- return this.documentElement.querySelector(selector);
115
- }
116
- return super.querySelector(selector);
117
- }
118
- querySelectorAll(selector) {
119
- const tag = selector.trim().toLowerCase();
120
- const results = [];
121
- if (this.documentElement) {
122
- if (this.documentElement.tagName === tag) results.push(this.documentElement);
123
- this.documentElement._findAll(tag, results);
124
- }
125
- return results;
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, "&quot;") + "\"").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
- let m;
131
- ATTR_PATTERN.lastIndex = 0;
132
- while ((m = ATTR_PATTERN.exec(attrsStr)) !== null) {
133
- el.setAttribute(m[1], m[2] ?? m[3] ?? m[4] ?? "");
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
- const doc = new DOMDocument();
138
- const stack = [doc];
139
- let i = 0;
140
- const len = xml.length;
141
- while (i < len) {
142
- const ltIdx = xml.indexOf("<", i);
143
- if (ltIdx === -1) {
144
- const text = xml.slice(i);
145
- if (text.trim()) {
146
- const tn = new DOMNode(3, "#text", text);
147
- const top2 = stack[stack.length - 1];
148
- tn.parentNode = top2;
149
- top2.childNodes.push(tn);
150
- }
151
- break;
152
- }
153
- if (ltIdx > i) {
154
- const text = xml.slice(i, ltIdx);
155
- if (text.trim()) {
156
- const tn = new DOMNode(3, "#text", text);
157
- const top2 = stack[stack.length - 1];
158
- tn.parentNode = top2;
159
- top2.childNodes.push(tn);
160
- }
161
- }
162
- if (xml.startsWith("<![CDATA[", ltIdx)) {
163
- const end = xml.indexOf("]]>", ltIdx);
164
- if (end === -1) break;
165
- const cn = new DOMNode(4, "#cdata-section", xml.slice(ltIdx + 9, end));
166
- const top2 = stack[stack.length - 1];
167
- cn.parentNode = top2;
168
- top2.childNodes.push(cn);
169
- i = end + 3;
170
- continue;
171
- }
172
- if (xml.startsWith("<!--", ltIdx)) {
173
- const end = xml.indexOf("-->", ltIdx);
174
- i = end === -1 ? len : end + 3;
175
- continue;
176
- }
177
- if (xml.startsWith("<?", ltIdx) || xml.startsWith("<!", ltIdx)) {
178
- const end = xml.indexOf(">", ltIdx);
179
- i = end === -1 ? len : end + 1;
180
- continue;
181
- }
182
- const gtIdx = findTagEnd(xml, ltIdx + 1);
183
- if (gtIdx === -1) break;
184
- const tagContent = xml.slice(ltIdx + 1, gtIdx);
185
- if (tagContent.startsWith("/")) {
186
- if (stack.length > 1) stack.pop();
187
- i = gtIdx + 1;
188
- continue;
189
- }
190
- const selfClosing = tagContent.endsWith("/");
191
- const inner = selfClosing ? tagContent.slice(0, -1) : tagContent;
192
- const wsIdx = inner.search(/\s/);
193
- const tagName = (wsIdx === -1 ? inner : inner.slice(0, wsIdx)).trim();
194
- if (!tagName) {
195
- i = gtIdx + 1;
196
- continue;
197
- }
198
- const el = new DOMElement(tagName);
199
- if (wsIdx !== -1) parseAttributes(inner.slice(wsIdx), el);
200
- const top = stack[stack.length - 1];
201
- el.parentNode = top;
202
- top.childNodes.push(el);
203
- if (top === doc && !doc.documentElement) {
204
- doc.documentElement = el;
205
- }
206
- if (!selfClosing) stack.push(el);
207
- i = gtIdx + 1;
208
- }
209
- return doc;
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
- let inSingle = false;
213
- let inDouble = false;
214
- for (let i = start; i < xml.length; i++) {
215
- const ch = xml[i];
216
- if (ch === '"' && !inSingle) {
217
- inDouble = !inDouble;
218
- continue;
219
- }
220
- if (ch === "'" && !inDouble) {
221
- inSingle = !inSingle;
222
- continue;
223
- }
224
- if (ch === ">" && !inSingle && !inDouble) return i;
225
- }
226
- return -1;
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
- export {
234
- DOMDocument,
235
- DOMElement,
236
- DOMNode,
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 };
@@ -1,4 +1,8 @@
1
1
  import { DOMParser } from "./index.js";
2
+
3
+ //#region src/register.ts
2
4
  if (typeof globalThis.DOMParser === "undefined") {
3
- globalThis.DOMParser = DOMParser;
5
+ globalThis.DOMParser = DOMParser;
4
6
  }
7
+
8
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/domparser",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
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.13",
44
- "@gjsify/unit": "^0.3.13",
43
+ "@gjsify/cli": "^0.3.15",
44
+ "@gjsify/unit": "^0.3.15",
45
45
  "@types/node": "^25.6.0",
46
46
  "typescript": "^6.0.3"
47
47
  }