@tkeron/html-parser 1.1.2 → 1.3.0

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.
Files changed (131) hide show
  1. package/.github/workflows/npm_deploy.yml +14 -4
  2. package/README.md +6 -6
  3. package/bun.lock +6 -8
  4. package/check-versions.ts +147 -0
  5. package/index.ts +4 -8
  6. package/package.json +5 -6
  7. package/src/dom-simulator/append-child.ts +130 -0
  8. package/src/dom-simulator/append.ts +18 -0
  9. package/src/dom-simulator/attributes.ts +23 -0
  10. package/src/dom-simulator/clone-node.ts +51 -0
  11. package/src/dom-simulator/convert-ast-node-to-dom.ts +37 -0
  12. package/src/dom-simulator/create-cdata.ts +18 -0
  13. package/src/dom-simulator/create-comment.ts +23 -0
  14. package/src/dom-simulator/create-doctype.ts +24 -0
  15. package/src/dom-simulator/create-document.ts +81 -0
  16. package/src/dom-simulator/create-element.ts +195 -0
  17. package/src/dom-simulator/create-processing-instruction.ts +19 -0
  18. package/src/dom-simulator/create-temp-parent.ts +9 -0
  19. package/src/dom-simulator/create-text-node.ts +23 -0
  20. package/src/dom-simulator/escape-text-content.ts +6 -0
  21. package/src/dom-simulator/find-special-elements.ts +14 -0
  22. package/src/dom-simulator/get-text-content.ts +18 -0
  23. package/src/dom-simulator/index.ts +36 -0
  24. package/src/dom-simulator/inner-outer-html.ts +182 -0
  25. package/src/dom-simulator/insert-after.ts +20 -0
  26. package/src/dom-simulator/insert-before.ts +108 -0
  27. package/src/dom-simulator/matches.ts +26 -0
  28. package/src/dom-simulator/node-types.ts +26 -0
  29. package/src/dom-simulator/prepend.ts +24 -0
  30. package/src/dom-simulator/remove-child.ts +68 -0
  31. package/src/dom-simulator/remove.ts +7 -0
  32. package/src/dom-simulator/replace-child.ts +152 -0
  33. package/src/dom-simulator/set-text-content.ts +33 -0
  34. package/src/dom-simulator/update-element-content.ts +56 -0
  35. package/src/dom-simulator.ts +12 -1126
  36. package/src/encoding/constants.ts +8 -0
  37. package/src/encoding/detect-encoding.ts +21 -0
  38. package/src/encoding/index.ts +1 -0
  39. package/src/encoding/normalize-encoding.ts +6 -0
  40. package/src/html-entities.ts +2127 -0
  41. package/src/index.ts +5 -5
  42. package/src/parser/adoption-agency-helpers.ts +145 -0
  43. package/src/parser/constants.ts +137 -0
  44. package/src/parser/dom-to-ast.ts +79 -0
  45. package/src/parser/index.ts +9 -0
  46. package/src/parser/parse.ts +772 -0
  47. package/src/parser/types.ts +56 -0
  48. package/src/selectors/find-elements-descendant.ts +47 -0
  49. package/src/selectors/index.ts +2 -0
  50. package/src/selectors/matches-selector.ts +12 -0
  51. package/src/selectors/matches-token.ts +27 -0
  52. package/src/selectors/parse-selector.ts +48 -0
  53. package/src/selectors/query-selector-all.ts +43 -0
  54. package/src/selectors/query-selector.ts +6 -0
  55. package/src/selectors/types.ts +10 -0
  56. package/src/serializer/attributes.ts +74 -0
  57. package/src/serializer/escape.ts +13 -0
  58. package/src/serializer/index.ts +1 -0
  59. package/src/serializer/serialize-tokens.ts +511 -0
  60. package/src/tokenizer/calculate-position.ts +10 -0
  61. package/src/tokenizer/constants.ts +11 -0
  62. package/src/tokenizer/decode-entities.ts +64 -0
  63. package/src/tokenizer/index.ts +2 -0
  64. package/src/tokenizer/parse-attributes.ts +74 -0
  65. package/src/tokenizer/tokenize.ts +165 -0
  66. package/src/tokenizer/types.ts +25 -0
  67. package/tests/adoption-agency-helpers.test.ts +304 -0
  68. package/tests/advanced.test.ts +242 -221
  69. package/tests/cloneNode.test.ts +19 -66
  70. package/tests/custom-elements-head.test.ts +54 -55
  71. package/tests/dom-extended.test.ts +77 -64
  72. package/tests/dom-manipulation.test.ts +51 -24
  73. package/tests/dom.test.ts +15 -13
  74. package/tests/encoding/detect-encoding.test.ts +33 -0
  75. package/tests/google-dom.test.ts +2 -2
  76. package/tests/helpers/tokenizer-adapter.test.ts +29 -43
  77. package/tests/helpers/tokenizer-adapter.ts +36 -33
  78. package/tests/helpers/tree-adapter.test.ts +20 -20
  79. package/tests/helpers/tree-adapter.ts +34 -24
  80. package/tests/html-entities-text.test.ts +6 -2
  81. package/tests/innerhtml-void-elements.test.ts +52 -36
  82. package/tests/outerHTML-replacement.test.ts +37 -65
  83. package/tests/parser/dom-to-ast.test.ts +109 -0
  84. package/tests/parser/parse.test.ts +139 -0
  85. package/tests/parser.test.ts +281 -217
  86. package/tests/selectors/query-selector-all.test.ts +39 -0
  87. package/tests/selectors/query-selector.test.ts +42 -0
  88. package/tests/serializer/attributes.test.ts +132 -0
  89. package/tests/serializer/escape.test.ts +51 -0
  90. package/tests/serializer/serialize-tokens.test.ts +80 -0
  91. package/tests/serializer-core.test.ts +6 -6
  92. package/tests/serializer-injectmeta.test.ts +6 -6
  93. package/tests/serializer-optionaltags.test.ts +9 -6
  94. package/tests/serializer-options.test.ts +6 -6
  95. package/tests/serializer-whitespace.test.ts +6 -6
  96. package/tests/tokenizer/calculate-position.test.ts +34 -0
  97. package/tests/tokenizer/decode-entities.test.ts +31 -0
  98. package/tests/tokenizer/parse-attributes.test.ts +44 -0
  99. package/tests/tokenizer/tokenize.test.ts +757 -0
  100. package/tests/tokenizer-namedEntities.test.ts +10 -7
  101. package/tests/tokenizer-pendingSpecChanges.test.ts +10 -7
  102. package/tests/tokenizer.test.ts +268 -256
  103. package/tests/tree-construction-adoption01.test.ts +25 -16
  104. package/tests/tree-construction-adoption02.test.ts +30 -19
  105. package/tests/tree-construction-domjs-unsafe.test.ts +6 -4
  106. package/tests/tree-construction-entities02.test.ts +18 -16
  107. package/tests/tree-construction-html5test-com.test.ts +16 -10
  108. package/tests/tree-construction-math.test.ts +11 -9
  109. package/tests/tree-construction-namespace-sensitivity.test.ts +11 -9
  110. package/tests/tree-construction-noscript01.test.ts +11 -9
  111. package/tests/tree-construction-ruby.test.ts +6 -4
  112. package/tests/tree-construction-scriptdata01.test.ts +6 -4
  113. package/tests/tree-construction-svg.test.ts +6 -4
  114. package/tests/tree-construction-template.test.ts +6 -4
  115. package/tests/tree-construction-tests10.test.ts +6 -4
  116. package/tests/tree-construction-tests11.test.ts +6 -4
  117. package/tests/tree-construction-tests20.test.ts +7 -4
  118. package/tests/tree-construction-tests21.test.ts +7 -4
  119. package/tests/tree-construction-tests23.test.ts +7 -4
  120. package/tests/tree-construction-tests24.test.ts +7 -4
  121. package/tests/tree-construction-tests5.test.ts +6 -5
  122. package/tests/tree-construction-tests6.test.ts +6 -5
  123. package/tests/tree-construction-tests_innerHTML_1.test.ts +6 -5
  124. package/tests/void-elements.test.ts +85 -40
  125. package/tsconfig.json +1 -1
  126. package/src/css-selector.ts +0 -185
  127. package/src/encoding.ts +0 -39
  128. package/src/parser.ts +0 -682
  129. package/src/serializer.ts +0 -450
  130. package/src/tokenizer.ts +0 -325
  131. package/tests/selectors.test.ts +0 -128
@@ -1,326 +1,16 @@
1
- import type { ASTNode, ASTNodeType } from "./parser.js";
2
- import { parse } from "./parser.js";
3
- import { tokenize } from "./tokenizer.js";
1
+ import type { ASTNode } from "./parser/index";
4
2
  import {
5
3
  querySelector as querySelectorFunction,
6
4
  querySelectorAll as querySelectorAllFunction,
7
- } from "./css-selector.js";
8
-
9
- // Escape special HTML characters in text content
10
- function escapeTextContent(text: string): string {
11
- return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
12
- }
13
-
14
- const VOID_ELEMENTS = new Set([
15
- 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
16
- 'link', 'meta', 'param', 'source', 'track', 'wbr'
17
- ]);
18
-
19
- export const enum NodeType {
20
- ELEMENT_NODE = 1,
21
- TEXT_NODE = 3,
22
- COMMENT_NODE = 8,
23
- DOCUMENT_NODE = 9,
24
- DOCUMENT_TYPE_NODE = 10,
25
- PROCESSING_INSTRUCTION_NODE = 7,
26
- CDATA_SECTION_NODE = 4,
27
- }
28
-
29
- export function createElement(
30
- tagName: string,
31
- attributes: Record<string, string> = {},
32
- namespaceURI?: string
33
- ): any {
34
- const innerHTML = "";
35
- const tagNameLower = tagName.toLowerCase();
36
- const isVoid = VOID_ELEMENTS.has(tagNameLower);
37
- const attrsStr = Object.entries(attributes)
38
- .map(([k, v]) => ` ${k}="${v}"`)
39
- .join("");
40
- const initialOuterHTML = isVoid
41
- ? `<${tagNameLower}${attrsStr}>`
42
- : `<${tagNameLower}${attrsStr}></${tagNameLower}>`;
43
- const textContent = "";
44
-
45
- const element: any = {
46
- nodeType: NodeType.ELEMENT_NODE,
47
- nodeName: tagName.toUpperCase(),
48
- nodeValue: null,
49
- tagName: tagName.toUpperCase(),
50
- namespaceURI: namespaceURI || null,
51
- attributes: { ...attributes },
52
- childNodes: [],
53
- children: [],
54
- textContent,
55
- innerHTML,
56
- _internalOuterHTML: initialOuterHTML,
57
- parentNode: null,
58
- parentElement: null,
59
- firstChild: null,
60
- lastChild: null,
61
- nextSibling: null,
62
- previousSibling: null,
63
- firstElementChild: null,
64
- lastElementChild: null,
65
- nextElementSibling: null,
66
- previousElementSibling: null,
67
-
68
- appendChild(child: any): any {
69
- appendChild(element, child);
70
- return child;
71
- },
72
-
73
- prepend(...nodes: any[]): void {
74
- prepend(element, ...nodes);
75
- },
76
-
77
- append(...nodes: any[]): void {
78
- append(element, ...nodes);
79
- },
80
-
81
- remove(): void {
82
- remove(element);
83
- },
84
-
85
- removeChild(child: any): any {
86
- return removeChild(element, child);
87
- },
88
-
89
- insertBefore(newNode: any, referenceNode: any): any {
90
- return insertBefore(element, newNode, referenceNode);
91
- },
92
-
93
- replaceChild(newChild: any, oldChild: any): any {
94
- return replaceChild(element, newChild, oldChild);
95
- },
96
-
97
- insertAfter(newNode: any, referenceNode: any): any {
98
- return insertAfter(element, newNode, referenceNode);
99
- },
100
-
101
- setAttribute(name: string, value: string): void {
102
- element.attributes[name] = value;
103
- updateElementContent(element);
104
- },
105
-
106
- getAttribute(name: string): string | null {
107
- return element.attributes[name] || null;
108
- },
109
-
110
- hasAttribute(name: string): boolean {
111
- return name in element.attributes;
112
- },
113
-
114
- removeAttribute(name: string): void {
115
- delete element.attributes[name];
116
- updateElementContent(element);
117
- },
118
-
119
- querySelector(selector: string): any {
120
- return querySelectorFunction(element, selector);
121
- },
122
-
123
- querySelectorAll(selector: string): any[] {
124
- return querySelectorAllFunction(element, selector);
125
- },
126
-
127
- matches(selector: string): boolean {
128
- return matches(element, selector);
129
- },
130
-
131
- cloneNode(deep: boolean = false): any {
132
- return cloneNode(element, deep);
133
- },
134
- };
135
-
136
- Object.defineProperty(element, "textContent", {
137
- get() {
138
- return (element as any)._internalTextContent || getTextContent(element);
139
- },
140
- set(value: string) {
141
- setTextContent(element, value);
142
- },
143
- enumerable: true,
144
- configurable: true,
145
- });
146
-
147
- Object.defineProperty(element, "innerHTML", {
148
- get() {
149
- return (element as any)._internalInnerHTML || getInnerHTML(element);
150
- },
151
- set(value: string) {
152
- setInnerHTML(element, value);
153
- },
154
- enumerable: true,
155
- configurable: true,
156
- });
157
-
158
- Object.defineProperty(element, "className", {
159
- get() {
160
- return element.attributes.class || "";
161
- },
162
- set(value: string) {
163
- element.attributes.class = value;
164
- },
165
- enumerable: true,
166
- configurable: true,
167
- });
168
-
169
- Object.defineProperty(element, "id", {
170
- get() {
171
- return element.attributes.id || "";
172
- },
173
- set(value: string) {
174
- element.attributes.id = value;
175
- },
176
- enumerable: true,
177
- configurable: true,
178
- });
179
-
180
- Object.defineProperty(element, "outerHTML", {
181
- get() {
182
- return element._internalOuterHTML || "";
183
- },
184
- set(value: string) {
185
- setOuterHTML(element, value);
186
- },
187
- enumerable: true,
188
- configurable: true,
189
- });
190
-
191
- return element;
192
- }
193
-
194
- export function createTextNode(content: string): any {
195
- const textNode: any = {
196
- nodeType: NodeType.TEXT_NODE,
197
- nodeName: "#text",
198
- nodeValue: content,
199
- textContent: content,
200
- data: content,
201
- childNodes: [],
202
- parentNode: null,
203
- firstChild: null,
204
- lastChild: null,
205
- nextSibling: null,
206
- previousSibling: null,
207
-
208
- remove(): void {
209
- remove(textNode);
210
- },
211
- };
212
- return textNode;
213
- }
214
-
215
- export function createComment(content: string): any {
216
- const commentNode: any = {
217
- nodeType: NodeType.COMMENT_NODE,
218
- nodeName: "#comment",
219
- nodeValue: content,
220
- textContent: "",
221
- data: content,
222
- childNodes: [],
223
- parentNode: null,
224
- firstChild: null,
225
- lastChild: null,
226
- nextSibling: null,
227
- previousSibling: null,
228
-
229
- remove(): void {
230
- remove(commentNode);
231
- },
232
- };
233
- return commentNode;
234
- }
235
-
236
- export function createDoctype(name: string = 'html'): any {
237
- const doctypeNode: any = {
238
- nodeType: NodeType.DOCUMENT_TYPE_NODE,
239
- nodeName: name.toUpperCase(),
240
- name: name.toLowerCase(),
241
- nodeValue: null,
242
- textContent: "",
243
- publicId: null,
244
- systemId: null,
245
- childNodes: [],
246
- parentNode: null,
247
- firstChild: null,
248
- lastChild: null,
249
- nextSibling: null,
250
- previousSibling: null,
251
- };
252
- return doctypeNode;
253
- }
254
-
255
- export function createDocument(): any {
256
- const document: any = {
257
- nodeType: NodeType.DOCUMENT_NODE,
258
- nodeName: "#document",
259
- nodeValue: null,
260
- textContent: "",
261
- childNodes: [],
262
- parentNode: null,
263
- firstChild: null,
264
- lastChild: null,
265
- nextSibling: null,
266
- previousSibling: null,
267
- documentElement: null,
268
- body: null,
269
- head: null,
270
-
271
- createElement(tagName: string): any {
272
- return createElement(tagName, {});
273
- },
274
-
275
- createTextNode(data: string): any {
276
- return createTextNode(data);
277
- },
278
-
279
- appendChild(child: any): any {
280
- appendChild(document, child);
281
- return child;
282
- },
283
-
284
- prepend(...nodes: any[]): void {
285
- prepend(document, ...nodes);
286
- },
287
-
288
- append(...nodes: any[]): void {
289
- append(document, ...nodes);
290
- },
291
-
292
- removeChild(child: any): any {
293
- return removeChild(document, child);
294
- },
295
-
296
- insertBefore(newNode: any, referenceNode: any): any {
297
- return insertBefore(document, newNode, referenceNode);
298
- },
299
-
300
- replaceChild(newChild: any, oldChild: any): any {
301
- return replaceChild(document, newChild, oldChild);
302
- },
303
-
304
- insertAfter(newNode: any, referenceNode: any): any {
305
- return insertAfter(document, newNode, referenceNode);
306
- },
307
-
308
- querySelector(selector: string): any {
309
- return querySelectorFunction(document, selector);
310
- },
311
-
312
- querySelectorAll(selector: string): any[] {
313
- return querySelectorAllFunction(document, selector);
314
- },
315
-
316
- getElementById(id: string): any {
317
- return querySelectorFunction(document, `#${id}`);
318
- },
319
- };
320
- return document;
321
- }
322
-
323
- export function astToDOM(ast: ASTNode): Document {
5
+ } from "./selectors";
6
+ import { NodeType } from "./dom-simulator/node-types.js";
7
+ import { createElement } from "./dom-simulator/create-element.js";
8
+ import { createDocument } from "./dom-simulator/create-document.js";
9
+ import { appendChild } from "./dom-simulator/append-child.js";
10
+ import { convertASTNodeToDOM } from "./dom-simulator/convert-ast-node-to-dom.js";
11
+ import { findSpecialElements } from "./dom-simulator/find-special-elements.js";
12
+
13
+ export const astToDOM = (ast: ASTNode): Document => {
324
14
  const document = createDocument();
325
15
  if (ast.children) {
326
16
  for (const child of ast.children) {
@@ -355,810 +45,6 @@ export function astToDOM(ast: ASTNode): Document {
355
45
  }
356
46
 
357
47
  return document;
358
- }
359
-
360
- function findSpecialElements(document: any, htmlElement: any): void {
361
- for (const child of htmlElement.childNodes) {
362
- if (child.nodeType === NodeType.ELEMENT_NODE) {
363
- const element = child;
364
- if (element.tagName === "BODY") {
365
- document.body = element;
366
- } else if (element.tagName === "HEAD") {
367
- document.head = element;
368
- }
369
- }
370
- }
371
- }
372
-
373
- function convertASTNodeToDOM(astNode: ASTNode): any {
374
- switch (astNode.type) {
375
- case "ELEMENT":
376
- const tagName = astNode.tagName || "div";
377
- const element = createElement(tagName, astNode.attributes || {});
378
-
379
- if (astNode.children) {
380
- for (const child of astNode.children) {
381
- const domChild = convertASTNodeToDOM(child);
382
- if (domChild) {
383
- appendChild(element, domChild);
384
- }
385
- }
386
- }
387
-
388
- updateElementContent(element);
389
- return element;
390
- case "TEXT":
391
- return createTextNode(astNode.content || "");
392
- case "COMMENT":
393
- return createComment(astNode.content || "");
394
- case "CDATA":
395
- return createTextNode(astNode.content || "");
396
- case "DOCTYPE":
397
- case "PROCESSING_INSTRUCTION":
398
- return null;
399
- default:
400
- return null;
401
- }
402
- }
403
-
404
- export function appendChild(parent: any, child: any): void {
405
- if (child.nodeType === NodeType.ELEMENT_NODE || child.nodeType === NodeType.DOCUMENT_NODE) {
406
- let ancestor = parent;
407
- while (ancestor) {
408
- if (ancestor === child) {
409
- throw new Error("HierarchyRequestError: Cannot insert a node as a descendant of itself");
410
- }
411
- ancestor = ancestor.parentNode;
412
- }
413
- }
414
-
415
- if (child.parentNode) {
416
- removeChild(child.parentNode, child);
417
- }
418
-
419
- child.parentNode = parent;
420
- parent.childNodes.push(child);
421
-
422
- if (parent.childNodes.length > 1) {
423
- const previousSibling = parent.childNodes[parent.childNodes.length - 2];
424
- if (previousSibling) {
425
- previousSibling.nextSibling = child;
426
- child.previousSibling = previousSibling;
427
- }
428
- }
429
-
430
- if (parent.childNodes.length === 1) {
431
- parent.firstChild = child;
432
- }
433
- parent.lastChild = child;
434
-
435
- if (
436
- parent.nodeType === NodeType.ELEMENT_NODE &&
437
- child.nodeType === NodeType.ELEMENT_NODE
438
- ) {
439
- const parentElement = parent;
440
- const childElement = child;
441
-
442
- childElement.parentElement = parentElement;
443
- parentElement.children.push(childElement);
444
-
445
- if (parentElement.children.length === 1) {
446
- parentElement.firstElementChild = childElement;
447
- }
448
- parentElement.lastElementChild = childElement;
449
-
450
- if (parentElement.children.length > 1) {
451
- const previousElementSibling =
452
- parentElement.children[parentElement.children.length - 2];
453
- if (previousElementSibling) {
454
- previousElementSibling.nextElementSibling = childElement;
455
- childElement.previousElementSibling = previousElementSibling;
456
- }
457
- }
458
- }
459
-
460
- if (parent.nodeType === NodeType.ELEMENT_NODE) {
461
- updateElementContent(parent);
462
- }
463
- }
464
-
465
- function prepend(parent: any, ...nodes: any[]): void {
466
- if (nodes.length === 0) return;
467
-
468
- for (let i = nodes.length - 1; i >= 0; i--) {
469
- const node = nodes[i];
470
- let childNode: any;
471
-
472
- if (typeof node === 'string') {
473
- childNode = createTextNode(node);
474
- } else {
475
- childNode = node;
476
- }
477
-
478
- if (parent.firstChild) {
479
- insertBefore(parent, childNode, parent.firstChild);
480
- } else {
481
- appendChild(parent, childNode);
482
- }
483
- }
484
- }
485
-
486
- function append(parent: any, ...nodes: any[]): void {
487
- if (nodes.length === 0) return;
488
-
489
- for (const node of nodes) {
490
- let childNode: any;
491
-
492
- if (typeof node === 'string') {
493
- childNode = createTextNode(node);
494
- } else {
495
- childNode = node;
496
- }
497
-
498
- appendChild(parent, childNode);
499
- }
500
- }
501
-
502
- function remove(node: any): void {
503
- if (node.parentNode) {
504
- removeChild(node.parentNode, node);
505
- }
506
- }
507
-
508
- function matches(element: any, selector: string): boolean {
509
- if (!selector || element.nodeType !== NodeType.ELEMENT_NODE) {
510
- return false;
511
- }
512
-
513
- try {
514
- // Para selectores complejos con descendientes, necesitamos buscar desde un ancestro
515
- if (selector.includes(' ') || selector.includes('>')) {
516
- // Buscar desde la raíz del documento
517
- let root = element;
518
- while (root.parentNode) {
519
- root = root.parentNode;
520
- }
521
- const results = querySelectorAllFunction(root, selector);
522
- return results.includes(element);
523
- }
524
-
525
- // Para selectores simples, usar el padre o crear uno temporal
526
- const parent = element.parentNode || createTempParent(element);
527
- const results = querySelectorAllFunction(parent, selector);
528
- return results.includes(element);
529
- } catch (error) {
530
- return false;
531
- }
532
- }
533
-
534
- function createTempParent(element: any): any {
535
- const temp = createElement('div');
536
- temp.childNodes.push(element);
537
- temp.children.push(element);
538
- element._tempParent = temp;
539
- return temp;
540
- }
541
-
542
- function removeChild(parent: any, child: any): any {
543
- const index = parent.childNodes.indexOf(child);
544
- if (index === -1) {
545
- throw new Error("Child not found");
546
- }
547
-
548
- parent.childNodes.splice(index, 1);
549
-
550
- if (child.previousSibling) {
551
- child.previousSibling.nextSibling = child.nextSibling;
552
- }
553
- if (child.nextSibling) {
554
- child.nextSibling.previousSibling = child.previousSibling;
555
- }
556
-
557
- if (parent.firstChild === child) {
558
- parent.firstChild = child.nextSibling;
559
- }
560
- if (parent.lastChild === child) {
561
- parent.lastChild = child.previousSibling;
562
- }
563
-
564
- if (parent.nodeType === NodeType.ELEMENT_NODE && child.nodeType === NodeType.ELEMENT_NODE) {
565
- const childElement = child;
566
- const elemIndex = parent.children.indexOf(childElement);
567
- if (elemIndex !== -1) {
568
- parent.children.splice(elemIndex, 1);
569
-
570
- if (childElement.previousElementSibling) {
571
- childElement.previousElementSibling.nextElementSibling =
572
- childElement.nextElementSibling;
573
- }
574
- if (childElement.nextElementSibling) {
575
- childElement.nextElementSibling.previousElementSibling =
576
- childElement.previousElementSibling;
577
- }
578
-
579
- if (parent.firstElementChild === childElement) {
580
- parent.firstElementChild = childElement.nextElementSibling;
581
- }
582
- if (parent.lastElementChild === childElement) {
583
- parent.lastElementChild = childElement.previousElementSibling;
584
- }
585
- }
586
- }
587
-
588
- child.parentNode = null;
589
- if (child.nodeType === NodeType.ELEMENT_NODE) {
590
- child.parentElement = null;
591
- }
592
- child.previousSibling = null;
593
- child.nextSibling = null;
594
- if (child.nodeType === NodeType.ELEMENT_NODE) {
595
- child.previousElementSibling = null;
596
- child.nextElementSibling = null;
597
- }
598
-
599
- if (parent.nodeType === NodeType.ELEMENT_NODE) {
600
- updateElementContent(parent);
601
- }
602
- return child;
603
- }
604
-
605
- function insertBefore(parent: any, newNode: any, referenceNode: any): any {
606
- if (referenceNode === null) {
607
- appendChild(parent, newNode);
608
- return newNode;
609
- }
610
-
611
- const refIndex = parent.childNodes.indexOf(referenceNode);
612
- if (refIndex === -1) {
613
- throw new Error("Reference node is not a child of this node");
614
- }
615
-
616
- if (newNode.nodeType === NodeType.ELEMENT_NODE || newNode.nodeType === NodeType.DOCUMENT_NODE) {
617
- let ancestor = parent;
618
- while (ancestor) {
619
- if (ancestor === newNode) {
620
- throw new Error("HierarchyRequestError: Cannot insert a node as a descendant of itself");
621
- }
622
- ancestor = ancestor.parentNode;
623
- }
624
- }
625
-
626
- if (newNode.parentNode) {
627
- removeChild(newNode.parentNode, newNode);
628
- }
629
-
630
- parent.childNodes.splice(refIndex, 0, newNode);
631
- newNode.parentNode = parent;
632
-
633
- newNode.previousSibling = referenceNode.previousSibling;
634
- newNode.nextSibling = referenceNode;
635
-
636
- if (referenceNode.previousSibling) {
637
- referenceNode.previousSibling.nextSibling = newNode;
638
- }
639
- referenceNode.previousSibling = newNode;
640
-
641
- if (parent.firstChild === referenceNode) {
642
- parent.firstChild = newNode;
643
- }
644
-
645
- if (
646
- parent.nodeType === NodeType.ELEMENT_NODE &&
647
- newNode.nodeType === NodeType.ELEMENT_NODE
648
- ) {
649
- const parentElement = parent;
650
- const newElement = newNode;
651
-
652
- newElement.parentElement = parentElement;
653
-
654
- let refElementIndex = -1;
655
- if (referenceNode.nodeType === NodeType.ELEMENT_NODE) {
656
- refElementIndex = parentElement.children.indexOf(referenceNode);
657
- } else {
658
- let nextElement = referenceNode.nextSibling;
659
- while (nextElement && nextElement.nodeType !== NodeType.ELEMENT_NODE) {
660
- nextElement = nextElement.nextSibling;
661
- }
662
- if (nextElement) {
663
- refElementIndex = parentElement.children.indexOf(nextElement);
664
- }
665
- }
666
-
667
- if (refElementIndex === -1) {
668
- parentElement.children.push(newElement);
669
- } else {
670
- parentElement.children.splice(refElementIndex, 0, newElement);
671
- }
672
-
673
- const newElemIndex = parentElement.children.indexOf(newElement);
674
- newElement.previousElementSibling =
675
- newElemIndex > 0 ? parentElement.children[newElemIndex - 1] : null;
676
- newElement.nextElementSibling =
677
- newElemIndex < parentElement.children.length - 1
678
- ? parentElement.children[newElemIndex + 1]
679
- : null;
680
-
681
- if (newElement.previousElementSibling) {
682
- newElement.previousElementSibling.nextElementSibling = newElement;
683
- }
684
- if (newElement.nextElementSibling) {
685
- newElement.nextElementSibling.previousElementSibling = newElement;
686
- }
687
-
688
- if (newElemIndex === 0) {
689
- parentElement.firstElementChild = newElement;
690
- }
691
- }
692
-
693
- if (parent.nodeType === NodeType.ELEMENT_NODE) {
694
- updateElementContent(parent);
695
- }
696
-
697
- return newNode;
698
- }
699
-
700
- function replaceChild(parent: any, newChild: any, oldChild: any): any {
701
- const oldIndex = parent.childNodes.indexOf(oldChild);
702
- if (oldIndex === -1) {
703
- throw new Error("Old child is not a child of this node");
704
- }
705
-
706
- if (newChild.nodeType === NodeType.ELEMENT_NODE || newChild.nodeType === NodeType.DOCUMENT_NODE) {
707
- let ancestor = parent;
708
- while (ancestor) {
709
- if (ancestor === newChild) {
710
- throw new Error("HierarchyRequestError: Cannot insert a node as a descendant of itself");
711
- }
712
- ancestor = ancestor.parentNode;
713
- }
714
- }
715
-
716
- if (newChild.parentNode) {
717
- removeChild(newChild.parentNode, newChild);
718
- }
719
-
720
- parent.childNodes[oldIndex] = newChild;
721
- newChild.parentNode = parent;
722
-
723
- newChild.previousSibling = oldChild.previousSibling;
724
- newChild.nextSibling = oldChild.nextSibling;
725
-
726
- if (oldChild.previousSibling) {
727
- oldChild.previousSibling.nextSibling = newChild;
728
- }
729
- if (oldChild.nextSibling) {
730
- oldChild.nextSibling.previousSibling = newChild;
731
- }
732
-
733
- if (parent.firstChild === oldChild) {
734
- parent.firstChild = newChild;
735
- }
736
- if (parent.lastChild === oldChild) {
737
- parent.lastChild = newChild;
738
- }
739
-
740
- if (parent.nodeType === NodeType.ELEMENT_NODE) {
741
- const parentElement = parent;
742
-
743
- if (oldChild.nodeType === NodeType.ELEMENT_NODE) {
744
- const oldElemIndex = parentElement.children.indexOf(oldChild);
745
- if (oldElemIndex !== -1) {
746
- if (newChild.nodeType === NodeType.ELEMENT_NODE) {
747
- parentElement.children[oldElemIndex] = newChild;
748
- newChild.parentElement = parentElement;
749
-
750
- newChild.previousElementSibling = oldChild.previousElementSibling;
751
- newChild.nextElementSibling = oldChild.nextElementSibling;
752
-
753
- if (oldChild.previousElementSibling) {
754
- oldChild.previousElementSibling.nextElementSibling = newChild;
755
- }
756
- if (oldChild.nextElementSibling) {
757
- oldChild.nextElementSibling.previousElementSibling = newChild;
758
- }
759
-
760
- if (parentElement.firstElementChild === oldChild) {
761
- parentElement.firstElementChild = newChild;
762
- }
763
- if (parentElement.lastElementChild === oldChild) {
764
- parentElement.lastElementChild = newChild;
765
- }
766
- } else {
767
- parentElement.children.splice(oldElemIndex, 1);
768
-
769
- if (oldChild.previousElementSibling) {
770
- oldChild.previousElementSibling.nextElementSibling =
771
- oldChild.nextElementSibling;
772
- }
773
- if (oldChild.nextElementSibling) {
774
- oldChild.nextElementSibling.previousElementSibling =
775
- oldChild.previousElementSibling;
776
- }
777
-
778
- if (parentElement.firstElementChild === oldChild) {
779
- parentElement.firstElementChild = oldChild.nextElementSibling;
780
- }
781
- if (parentElement.lastElementChild === oldChild) {
782
- parentElement.lastElementChild = oldChild.previousElementSibling;
783
- }
784
- }
785
- }
786
- } else if (newChild.nodeType === NodeType.ELEMENT_NODE) {
787
- const newElement = newChild;
788
- newElement.parentElement = parentElement;
789
-
790
- let insertIndex = 0;
791
- for (let i = 0; i < oldIndex; i++) {
792
- if (parent.childNodes[i].nodeType === NodeType.ELEMENT_NODE) {
793
- insertIndex++;
794
- }
795
- }
796
-
797
- parentElement.children.splice(insertIndex, 0, newElement);
798
-
799
- newElement.previousElementSibling =
800
- insertIndex > 0 ? parentElement.children[insertIndex - 1] : null;
801
- newElement.nextElementSibling =
802
- insertIndex < parentElement.children.length - 1
803
- ? parentElement.children[insertIndex + 1]
804
- : null;
805
-
806
- if (newElement.previousElementSibling) {
807
- newElement.previousElementSibling.nextElementSibling = newElement;
808
- }
809
- if (newElement.nextElementSibling) {
810
- newElement.nextElementSibling.previousElementSibling = newElement;
811
- }
812
-
813
- if (insertIndex === 0) {
814
- parentElement.firstElementChild = newElement;
815
- }
816
- if (insertIndex === parentElement.children.length - 1) {
817
- parentElement.lastElementChild = newElement;
818
- }
819
- }
820
- }
821
-
822
- oldChild.parentNode = null;
823
- if (oldChild.nodeType === NodeType.ELEMENT_NODE) {
824
- oldChild.parentElement = null;
825
- }
826
- oldChild.previousSibling = null;
827
- oldChild.nextSibling = null;
828
- if (oldChild.nodeType === NodeType.ELEMENT_NODE) {
829
- oldChild.previousElementSibling = null;
830
- oldChild.nextElementSibling = null;
831
- }
832
-
833
- if (parent.nodeType === NodeType.ELEMENT_NODE) {
834
- updateElementContent(parent);
835
- }
836
-
837
- return oldChild;
838
- }
839
-
840
- function insertAfter(parent: any, newNode: any, referenceNode: any): any {
841
- if (referenceNode === null) {
842
- insertBefore(parent, newNode, parent.firstChild);
843
- return newNode;
844
- }
845
-
846
- const refIndex = parent.childNodes.indexOf(referenceNode);
847
- if (refIndex === -1) {
848
- throw new Error("Reference node is not a child of this node");
849
- }
850
-
851
- const nextSibling = referenceNode.nextSibling;
852
- return insertBefore(parent, newNode, nextSibling);
853
- }
854
-
855
- function updateElementContent(element: any): void {
856
- const innerHTML = element.childNodes
857
- .map((child: any) => {
858
- if (child.nodeType === NodeType.TEXT_NODE) {
859
- return escapeTextContent(child.textContent || "");
860
- } else if (child.nodeType === NodeType.ELEMENT_NODE) {
861
- return child.outerHTML;
862
- } else if (child.nodeType === NodeType.COMMENT_NODE) {
863
- return `<!--${child.data}-->`;
864
- }
865
- return "";
866
- })
867
- .join("");
868
-
869
- Object.defineProperty(element, "_internalInnerHTML", {
870
- value: innerHTML,
871
- writable: true,
872
- enumerable: false,
873
- configurable: true,
874
- });
875
-
876
- const attrs = Object.entries(element.attributes)
877
- .map(([k, v]) => ` ${k}="${v}"`)
878
- .join("");
879
- const tagNameLower = element.tagName.toLowerCase();
880
- const isVoid = VOID_ELEMENTS.has(tagNameLower);
881
-
882
- Object.defineProperty(element, "_internalOuterHTML", {
883
- value: isVoid ? `<${tagNameLower}${attrs}>` : `<${tagNameLower}${attrs}>${innerHTML}</${tagNameLower}>`,
884
- writable: true,
885
- enumerable: false,
886
- configurable: true,
887
- });
888
-
889
- const computedTextContent = getTextContent(element);
890
- Object.defineProperty(element, "_internalTextContent", {
891
- value: computedTextContent,
892
- writable: true,
893
- enumerable: false,
894
- configurable: true,
895
- });
896
-
897
- if (element.parentElement) {
898
- updateElementContent(element.parentElement);
899
- }
900
- }
901
-
902
- export function getTextContent(node: any): string {
903
- if (node.nodeType === NodeType.TEXT_NODE) {
904
- return node.textContent || "";
905
- }
906
- if (
907
- node.nodeType !== NodeType.ELEMENT_NODE &&
908
- node.nodeType !== NodeType.DOCUMENT_NODE
909
- ) {
910
- return "";
911
- }
912
- let textContent = "";
913
- for (const child of node.childNodes) {
914
- textContent += getTextContent(child);
915
- }
916
- return textContent;
917
- }
918
-
919
- export function getAttribute(element: any, name: string): string | null {
920
- return element.attributes[name] || null;
921
- }
922
-
923
- export function hasAttribute(element: any, name: string): boolean {
924
- return name in element.attributes;
925
- }
926
-
927
- export function setAttribute(element: any, name: string, value: string): void {
928
- element.attributes[name] = value;
929
- updateElementContent(element);
930
- }
931
-
932
- export function removeAttribute(element: any, name: string): void {
933
- delete element.attributes[name];
934
- updateElementContent(element);
935
- }
936
-
937
- export function setInnerHTML(element: any, html: string): void {
938
- element.childNodes = [];
939
- element.children = [];
940
- element.firstChild = null;
941
- element.lastChild = null;
942
- element.firstElementChild = null;
943
- element.lastElementChild = null;
944
-
945
- if (html.trim()) {
946
- const wrappedHtml = '<div>' + html + '</div>';
947
- const tokens = tokenize(wrappedHtml);
948
- const doc = parse(tokens);
949
- const div = doc.querySelector('div');
950
- if (div && div.childNodes) {
951
- const nodesToMove = [...div.childNodes];
952
- for (const child of nodesToMove) {
953
- child.parentNode = null;
954
- appendChild(element, child);
955
- }
956
- }
957
- }
958
-
959
- const actualInnerHTML = getInnerHTML(element);
960
- Object.defineProperty(element, "_internalInnerHTML", {
961
- value: actualInnerHTML,
962
- writable: true,
963
- enumerable: false,
964
- configurable: true,
965
- });
966
-
967
- const textContent = getTextContent(element);
968
- Object.defineProperty(element, "_internalTextContent", {
969
- value: textContent,
970
- writable: true,
971
- enumerable: false,
972
- configurable: true,
973
- });
974
-
975
- const attrs = Object.entries(element.attributes)
976
- .map(([k, v]) => ` ${k}="${v}"`)
977
- .join("");
978
- const tagNameLower = element.tagName.toLowerCase();
979
- const isVoid = VOID_ELEMENTS.has(tagNameLower);
980
-
981
- Object.defineProperty(element, "_internalOuterHTML", {
982
- value: isVoid ? `<${tagNameLower}${attrs}>` : `<${tagNameLower}${attrs}>${actualInnerHTML}</${tagNameLower}>`,
983
- writable: true,
984
- enumerable: false,
985
- configurable: true,
986
- });
987
- }
988
-
989
- export function setOuterHTML(element: any, html: string): void {
990
- if (!element.parentNode) {
991
- throw new Error("Cannot set outerHTML on element without a parent");
992
- }
993
-
994
- const parent = element.parentNode;
995
- const indexInParent = parent.childNodes.indexOf(element);
996
-
997
- if (indexInParent === -1) {
998
- throw new Error("Element not found in parent's childNodes");
999
- }
1000
-
1001
- let newNodes: any[] = [];
1002
-
1003
- if (html.trim()) {
1004
- const tokens = tokenize(html);
1005
- const doc = parse(tokens);
1006
- const body = doc.body;
1007
- if (body && body.childNodes) {
1008
- for (const child of body.childNodes) {
1009
- child.parentNode = null;
1010
- newNodes.push(child);
1011
- }
1012
- }
1013
- }
1014
-
1015
- const previousSibling = element.previousSibling;
1016
- const nextSibling = element.nextSibling;
1017
-
1018
- parent.childNodes.splice(indexInParent, 1);
1019
-
1020
- if (newNodes.length > 0) {
1021
- parent.childNodes.splice(indexInParent, 0, ...newNodes);
1022
-
1023
- for (const newNode of newNodes) {
1024
- newNode.parentNode = parent;
1025
- newNode.parentElement = parent.nodeType === NodeType.ELEMENT_NODE ? parent : null;
1026
- }
1027
-
1028
- for (let i = 0; i < newNodes.length; i++) {
1029
- const currentNode = newNodes[i];
1030
-
1031
- if (i === 0) {
1032
- currentNode.previousSibling = previousSibling;
1033
- if (previousSibling) {
1034
- previousSibling.nextSibling = currentNode;
1035
- }
1036
- } else {
1037
- currentNode.previousSibling = newNodes[i - 1];
1038
- }
1039
-
1040
- if (i === newNodes.length - 1) {
1041
- currentNode.nextSibling = nextSibling;
1042
- if (nextSibling) {
1043
- nextSibling.previousSibling = currentNode;
1044
- }
1045
- } else {
1046
- currentNode.nextSibling = newNodes[i + 1];
1047
- }
1048
- }
1049
- } else {
1050
- if (previousSibling) {
1051
- previousSibling.nextSibling = nextSibling;
1052
- }
1053
- if (nextSibling) {
1054
- nextSibling.previousSibling = previousSibling;
1055
- }
1056
- }
1057
-
1058
- element.parentNode = null;
1059
- element.parentElement = null;
1060
- element.previousSibling = null;
1061
- element.nextSibling = null;
1062
-
1063
- parent.children = parent.childNodes.filter(
1064
- (child: any) => child.nodeType === NodeType.ELEMENT_NODE
1065
- );
1066
-
1067
- parent.firstChild = parent.childNodes.length > 0 ? parent.childNodes[0] : null;
1068
- parent.lastChild = parent.childNodes.length > 0 ? parent.childNodes[parent.childNodes.length - 1] : null;
1069
-
1070
- parent.firstElementChild = parent.children.length > 0 ? parent.children[0] : null;
1071
- parent.lastElementChild = parent.children.length > 0 ? parent.children[parent.children.length - 1] : null;
1072
-
1073
- for (let i = 0; i < parent.children.length; i++) {
1074
- const child = parent.children[i];
1075
- child.previousElementSibling = i > 0 ? parent.children[i - 1] : null;
1076
- child.nextElementSibling = i < parent.children.length - 1 ? parent.children[i + 1] : null;
1077
- }
1078
-
1079
- updateElementContent(parent);
1080
- }
1081
-
1082
- function setTextContent(element: any, text: string): void {
1083
- element.childNodes = [];
1084
- element.children = [];
1085
- element.firstChild = null;
1086
- element.lastChild = null;
1087
- element.firstElementChild = null;
1088
- element.lastElementChild = null;
1089
-
1090
- if (text) {
1091
- const textNode: any = {
1092
- nodeType: NodeType.TEXT_NODE,
1093
- nodeName: "#text",
1094
- nodeValue: text,
1095
- textContent: text,
1096
- data: text,
1097
- childNodes: [],
1098
- parentNode: element,
1099
- firstChild: null,
1100
- lastChild: null,
1101
- nextSibling: null,
1102
- previousSibling: null,
1103
- };
1104
-
1105
- element.childNodes.push(textNode);
1106
- element.firstChild = textNode;
1107
- element.lastChild = textNode;
1108
- }
1109
-
1110
- updateElementContent(element);
1111
- }
1112
-
1113
- export function cloneNode(node: any, deep: boolean = false): any {
1114
- if (node.nodeType === NodeType.ELEMENT_NODE) {
1115
- const element = node;
1116
- const cloned = createElement(element.tagName, element.attributes || {});
1117
-
1118
- if (deep && element.childNodes && element.childNodes.length > 0) {
1119
- for (let i = 0; i < element.childNodes.length; i++) {
1120
- const child = element.childNodes[i];
1121
- const childClone = cloneNode(child, true);
1122
- appendChild(cloned, childClone);
1123
- }
1124
- }
1125
-
1126
- return cloned;
1127
- } else if (node.nodeType === NodeType.TEXT_NODE) {
1128
- return createTextNode(node.textContent || "");
1129
- } else if (node.nodeType === NodeType.COMMENT_NODE) {
1130
- return createComment(node.data || "");
1131
- } else if (node.nodeType === NodeType.DOCUMENT_NODE) {
1132
- const doc = createDocument();
1133
- if (deep && node.childNodes && node.childNodes.length > 0) {
1134
- for (let i = 0; i < node.childNodes.length; i++) {
1135
- const child = node.childNodes[i];
1136
- const childClone = cloneNode(child, true);
1137
- appendChild(doc, childClone);
1138
- }
1139
- }
1140
- return doc;
1141
- }
1142
-
1143
- return createTextNode("");
1144
- }
1145
-
1146
- export function getInnerHTML(element: any): string {
1147
- if (element.nodeType !== NodeType.ELEMENT_NODE) {
1148
- return "";
1149
- }
1150
-
1151
- let innerHTML = "";
1152
- for (const child of element.childNodes) {
1153
- if (child.nodeType === NodeType.ELEMENT_NODE) {
1154
- innerHTML += child.outerHTML;
1155
- } else if (child.nodeType === NodeType.TEXT_NODE) {
1156
- innerHTML += escapeTextContent(child.textContent || "");
1157
- } else if (child.nodeType === NodeType.COMMENT_NODE) {
1158
- innerHTML += `<!--${child.data || ""}-->`;
1159
- }
1160
- }
1161
- return innerHTML;
1162
- }
48
+ };
1163
49
 
1164
- export { querySelector, querySelectorAll } from "./css-selector.js";
50
+ export { querySelector, querySelectorAll } from "./selectors";