@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
@@ -0,0 +1,81 @@
1
+ import { NodeType } from "./node-types.js";
2
+ import { createElement } from "./create-element.js";
3
+ import { createTextNode } from "./create-text-node.js";
4
+ import {
5
+ prepend,
6
+ append,
7
+ appendChild,
8
+ removeChild,
9
+ insertBefore,
10
+ insertAfter,
11
+ replaceChild,
12
+ } from "./index.js";
13
+ import { querySelector, querySelectorAll } from "../selectors/index.js";
14
+
15
+ export const createDocument = (): any => {
16
+ const document: any = {
17
+ nodeType: NodeType.DOCUMENT_NODE,
18
+ nodeName: "#document",
19
+ nodeValue: null,
20
+ textContent: "",
21
+ childNodes: [],
22
+ parentNode: null,
23
+ firstChild: null,
24
+ lastChild: null,
25
+ nextSibling: null,
26
+ previousSibling: null,
27
+ documentElement: null,
28
+ body: null,
29
+ head: null,
30
+
31
+ createElement(tagName: string): any {
32
+ return createElement(tagName, {});
33
+ },
34
+
35
+ createTextNode(data: string): any {
36
+ return createTextNode(data);
37
+ },
38
+
39
+ appendChild(child: any): any {
40
+ appendChild(document, child);
41
+ return child;
42
+ },
43
+
44
+ prepend(...nodes: any[]): void {
45
+ prepend(document, ...nodes);
46
+ },
47
+
48
+ append(...nodes: any[]): void {
49
+ append(document, ...nodes);
50
+ },
51
+
52
+ removeChild(child: any): any {
53
+ return removeChild(document, child);
54
+ },
55
+
56
+ insertBefore(newNode: any, referenceNode: any): any {
57
+ return insertBefore(document, newNode, referenceNode);
58
+ },
59
+
60
+ replaceChild(newChild: any, oldChild: any): any {
61
+ return replaceChild(document, newChild, oldChild);
62
+ },
63
+
64
+ insertAfter(newNode: any, referenceNode: any): any {
65
+ return insertAfter(document, newNode, referenceNode);
66
+ },
67
+
68
+ querySelector(selector: string): any {
69
+ return querySelector(document, selector);
70
+ },
71
+
72
+ querySelectorAll(selector: string): any[] {
73
+ return querySelectorAll(document, selector);
74
+ },
75
+
76
+ getElementById(id: string): any {
77
+ return querySelector(document, `#${id}`);
78
+ },
79
+ };
80
+ return document;
81
+ };
@@ -0,0 +1,195 @@
1
+ import { NodeType, VOID_ELEMENTS } from "./node-types.js";
2
+ import { appendChild } from "./append-child.js";
3
+ import { prepend } from "./index.js";
4
+ import { querySelector, querySelectorAll } from "../selectors/index.js";
5
+ import {
6
+ getTextContent,
7
+ setInnerHTML,
8
+ cloneNode,
9
+ getInnerHTML,
10
+ setTextContent,
11
+ setOuterHTML,
12
+ } from "./index.js";
13
+ import {
14
+ append,
15
+ remove,
16
+ insertAfter,
17
+ replaceChild,
18
+ insertBefore,
19
+ removeChild,
20
+ matches,
21
+ } from "./index.js";
22
+ import { updateElementContent } from "./update-element-content.js";
23
+
24
+ export const createElement = (
25
+ tagName: string,
26
+ attributes: Record<string, string> = {},
27
+ namespaceURI?: string,
28
+ isSelfClosing?: boolean,
29
+ ): any => {
30
+ const innerHTML = "";
31
+ const tagNameLower = tagName.toLowerCase();
32
+ const isVoid = VOID_ELEMENTS.has(tagNameLower);
33
+ const attrsStr = Object.entries(attributes)
34
+ .map(([k, v]) => ` ${k}="${v}"`)
35
+ .join("");
36
+ const initialOuterHTML = isVoid
37
+ ? `<${tagNameLower}${attrsStr}>`
38
+ : `<${tagNameLower}${attrsStr}></${tagNameLower}>`;
39
+ const textContent = "";
40
+
41
+ const element: any = {
42
+ nodeType: NodeType.ELEMENT_NODE,
43
+ nodeName: tagName.toUpperCase(),
44
+ nodeValue: null,
45
+ tagName: tagName.toUpperCase(),
46
+ namespaceURI: namespaceURI || null,
47
+ isSelfClosing: isSelfClosing || isVoid,
48
+ attributes: { ...attributes },
49
+ childNodes: [],
50
+ children: [],
51
+ textContent,
52
+ innerHTML,
53
+ _internalOuterHTML: initialOuterHTML,
54
+ parentNode: null,
55
+ parentElement: null,
56
+ firstChild: null,
57
+ lastChild: null,
58
+ nextSibling: null,
59
+ previousSibling: null,
60
+ firstElementChild: null,
61
+ lastElementChild: null,
62
+ nextElementSibling: null,
63
+ previousElementSibling: null,
64
+
65
+ appendChild(child: any): any {
66
+ appendChild(element, child);
67
+ return child;
68
+ },
69
+
70
+ prepend(...nodes: any[]): void {
71
+ prepend(element, ...nodes);
72
+ },
73
+
74
+ append(...nodes: any[]): void {
75
+ append(element, ...nodes);
76
+ },
77
+
78
+ remove(): void {
79
+ remove(element);
80
+ },
81
+
82
+ removeChild(child: any): any {
83
+ return removeChild(element, child);
84
+ },
85
+
86
+ insertBefore(newNode: any, referenceNode: any): any {
87
+ return insertBefore(element, newNode, referenceNode);
88
+ },
89
+
90
+ replaceChild(newChild: any, oldChild: any): any {
91
+ return replaceChild(element, newChild, oldChild);
92
+ },
93
+
94
+ insertAfter(newNode: any, referenceNode: any): any {
95
+ return insertAfter(element, newNode, referenceNode);
96
+ },
97
+
98
+ setAttribute(name: string, value: string): void {
99
+ element.attributes[name] = value;
100
+ updateElementContent(element);
101
+ },
102
+
103
+ getAttribute(name: string): string | null {
104
+ return element.attributes[name] || null;
105
+ },
106
+
107
+ hasAttribute(name: string): boolean {
108
+ return name in element.attributes;
109
+ },
110
+
111
+ removeAttribute(name: string): void {
112
+ delete element.attributes[name];
113
+ updateElementContent(element);
114
+ },
115
+
116
+ querySelector(selector: string): any {
117
+ return querySelector(element, selector);
118
+ },
119
+
120
+ querySelectorAll(selector: string): any[] {
121
+ return querySelectorAll(element, selector);
122
+ },
123
+
124
+ matches(selector: string): boolean {
125
+ return matches(element, selector);
126
+ },
127
+
128
+ cloneNode(deep: boolean = false): any {
129
+ return cloneNode(element, deep);
130
+ },
131
+ };
132
+
133
+ Object.defineProperty(element, "textContent", {
134
+ get() {
135
+ return (element as any)._internalTextContent || getTextContent(element);
136
+ },
137
+ set(value: string) {
138
+ setTextContent(element, value);
139
+ },
140
+ enumerable: true,
141
+ configurable: true,
142
+ });
143
+
144
+ Object.defineProperty(element, "innerHTML", {
145
+ get() {
146
+ if (!element._internalInnerHTML) {
147
+ updateElementContent(element);
148
+ }
149
+ return element._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
+ if (!element._internalOuterHTML) {
183
+ updateElementContent(element);
184
+ }
185
+ return element._internalOuterHTML || "";
186
+ },
187
+ set(value: string) {
188
+ setOuterHTML(element, value);
189
+ },
190
+ enumerable: true,
191
+ configurable: true,
192
+ });
193
+
194
+ return element;
195
+ };
@@ -0,0 +1,19 @@
1
+ import { NodeType } from "./node-types.js";
2
+
3
+ export const createProcessingInstruction = (content: string): any => {
4
+ const piNode: any = {
5
+ nodeType: NodeType.PROCESSING_INSTRUCTION_NODE,
6
+ nodeName: "#processing-instruction",
7
+ nodeValue: content,
8
+ textContent: content,
9
+ data: content,
10
+ target: content.split(" ")[0] || "",
11
+ childNodes: [],
12
+ parentNode: null,
13
+ firstChild: null,
14
+ lastChild: null,
15
+ nextSibling: null,
16
+ previousSibling: null,
17
+ };
18
+ return piNode;
19
+ };
@@ -0,0 +1,9 @@
1
+ import { createElement } from "./create-element.js";
2
+
3
+ export const createTempParent = (element: any): any => {
4
+ const temp = createElement("div");
5
+ temp.childNodes.push(element);
6
+ temp.children.push(element);
7
+ element._tempParent = temp;
8
+ return temp;
9
+ };
@@ -0,0 +1,23 @@
1
+ import { NodeType } from "./node-types.js";
2
+ import { remove } from "./index.js";
3
+
4
+ export const createTextNode = (content: string): any => {
5
+ const textNode: any = {
6
+ nodeType: NodeType.TEXT_NODE,
7
+ nodeName: "#text",
8
+ nodeValue: content,
9
+ textContent: content,
10
+ data: content,
11
+ childNodes: [],
12
+ parentNode: null,
13
+ firstChild: null,
14
+ lastChild: null,
15
+ nextSibling: null,
16
+ previousSibling: null,
17
+
18
+ remove(): void {
19
+ remove(textNode);
20
+ },
21
+ };
22
+ return textNode;
23
+ };
@@ -0,0 +1,6 @@
1
+ export const escapeTextContent = (text: string): string => {
2
+ return text
3
+ .replace(/&/g, "&amp;")
4
+ .replace(/</g, "&lt;")
5
+ .replace(/>/g, "&gt;");
6
+ };
@@ -0,0 +1,14 @@
1
+ import { NodeType } from "./node-types.js";
2
+
3
+ export const findSpecialElements = (document: any, htmlElement: any): void => {
4
+ for (const child of htmlElement.childNodes) {
5
+ if (child.nodeType === NodeType.ELEMENT_NODE) {
6
+ const element = child;
7
+ if (element.tagName === "BODY") {
8
+ document.body = element;
9
+ } else if (element.tagName === "HEAD") {
10
+ document.head = element;
11
+ }
12
+ }
13
+ }
14
+ };
@@ -0,0 +1,18 @@
1
+ import { NodeType } from "./node-types.js";
2
+
3
+ export const getTextContent = (node: any): string => {
4
+ if (node.nodeType === NodeType.TEXT_NODE) {
5
+ return node.textContent || "";
6
+ }
7
+ if (
8
+ node.nodeType !== NodeType.ELEMENT_NODE &&
9
+ node.nodeType !== NodeType.DOCUMENT_NODE
10
+ ) {
11
+ return "";
12
+ }
13
+ let textContent = "";
14
+ for (const child of node.childNodes) {
15
+ textContent += getTextContent(child);
16
+ }
17
+ return textContent;
18
+ };
@@ -0,0 +1,36 @@
1
+ export { NodeType, VOID_ELEMENTS } from "./node-types.js";
2
+ export { createElement } from "./create-element.js";
3
+ export { createTextNode } from "./create-text-node.js";
4
+ export { createComment } from "./create-comment.js";
5
+ export { createCDATA } from "./create-cdata.js";
6
+ export { createProcessingInstruction } from "./create-processing-instruction.js";
7
+ export { createDoctype } from "./create-doctype.js";
8
+ export { createDocument } from "./create-document.js";
9
+ export { appendChild } from "./append-child.js";
10
+ export { getTextContent } from "./get-text-content.js";
11
+ export { setTextContent } from "./set-text-content.js";
12
+ export {
13
+ getAttribute,
14
+ hasAttribute,
15
+ setAttribute,
16
+ removeAttribute,
17
+ } from "./attributes.js";
18
+ export {
19
+ setInnerHTML,
20
+ setOuterHTML,
21
+ getInnerHTML,
22
+ } from "./inner-outer-html.js";
23
+ export { cloneNode } from "./clone-node.js";
24
+ export { updateElementContent } from "./update-element-content.js";
25
+ export { prepend } from "./prepend.js";
26
+ export { append } from "./append.js";
27
+ export { remove } from "./remove.js";
28
+ export { insertBefore } from "./insert-before.js";
29
+ export { insertAfter } from "./insert-after.js";
30
+ export { replaceChild } from "./replace-child.js";
31
+ export { removeChild } from "./remove-child.js";
32
+ export { createTempParent } from "./create-temp-parent.js";
33
+ export { escapeTextContent } from "./escape-text-content.js";
34
+ export { findSpecialElements } from "./find-special-elements.js";
35
+ export { convertASTNodeToDOM } from "./convert-ast-node-to-dom.js";
36
+ export { matches } from "./matches.js";
@@ -0,0 +1,182 @@
1
+ import { NodeType, VOID_ELEMENTS } from "./node-types.js";
2
+ import { tokenize } from "../tokenizer/index.js";
3
+ import { parse } from "../parser/index.js";
4
+ import { appendChild } from "./append-child.js";
5
+ import { getTextContent } from "./get-text-content.js";
6
+ import { escapeTextContent } from "./escape-text-content.js";
7
+ import { updateElementContent } from "./update-element-content.js";
8
+
9
+ export const setInnerHTML = (element: any, html: string): void => {
10
+ element.childNodes = [];
11
+ element.children = [];
12
+ element.firstChild = null;
13
+ element.lastChild = null;
14
+ element.firstElementChild = null;
15
+ element.lastElementChild = null;
16
+
17
+ if (html.trim()) {
18
+ const wrappedHtml = "<div>" + html + "</div>";
19
+ const tokens = tokenize(wrappedHtml);
20
+ const doc = parse(tokens);
21
+ const div = doc.querySelector("div");
22
+ if (div && div.childNodes) {
23
+ const nodesToMove = [...div.childNodes];
24
+ for (const child of nodesToMove) {
25
+ child.parentNode = null;
26
+ appendChild(element, child);
27
+ }
28
+ }
29
+ }
30
+
31
+ const actualInnerHTML = getInnerHTML(element);
32
+ Object.defineProperty(element, "_internalInnerHTML", {
33
+ value: actualInnerHTML,
34
+ writable: true,
35
+ enumerable: false,
36
+ configurable: true,
37
+ });
38
+
39
+ const textContent = getTextContent(element);
40
+ Object.defineProperty(element, "_internalTextContent", {
41
+ value: textContent,
42
+ writable: true,
43
+ enumerable: false,
44
+ configurable: true,
45
+ });
46
+
47
+ const attrs = Object.entries(element.attributes)
48
+ .map(([k, v]) => ` ${k}="${v}"`)
49
+ .join("");
50
+ const tagNameLower = element.tagName.toLowerCase();
51
+ const isVoid = VOID_ELEMENTS.has(tagNameLower);
52
+
53
+ Object.defineProperty(element, "_internalOuterHTML", {
54
+ value: isVoid
55
+ ? `<${tagNameLower}${attrs}>`
56
+ : `<${tagNameLower}${attrs}>${actualInnerHTML}</${tagNameLower}>`,
57
+ writable: true,
58
+ enumerable: false,
59
+ configurable: true,
60
+ });
61
+ };
62
+
63
+ export const setOuterHTML = (element: any, html: string): void => {
64
+ if (!element.parentNode) {
65
+ throw new Error("Cannot set outerHTML on element without a parent");
66
+ }
67
+
68
+ const parent = element.parentNode;
69
+ const indexInParent = parent.childNodes.indexOf(element);
70
+
71
+ if (indexInParent === -1) {
72
+ throw new Error("Element not found in parent's childNodes");
73
+ }
74
+
75
+ let newNodes: any[] = [];
76
+
77
+ if (html.trim()) {
78
+ const tokens = tokenize(html);
79
+ const doc = parse(tokens);
80
+ const body = doc.body;
81
+ if (body && body.childNodes) {
82
+ for (const child of body.childNodes) {
83
+ child.parentNode = null;
84
+ newNodes.push(child);
85
+ }
86
+ }
87
+ }
88
+
89
+ const previousSibling = element.previousSibling;
90
+ const nextSibling = element.nextSibling;
91
+
92
+ parent.childNodes.splice(indexInParent, 1);
93
+
94
+ if (newNodes.length > 0) {
95
+ parent.childNodes.splice(indexInParent, 0, ...newNodes);
96
+
97
+ for (const newNode of newNodes) {
98
+ newNode.parentNode = parent;
99
+ newNode.parentElement =
100
+ parent.nodeType === NodeType.ELEMENT_NODE ? parent : null;
101
+ }
102
+
103
+ for (let i = 0; i < newNodes.length; i++) {
104
+ const currentNode = newNodes[i];
105
+
106
+ if (i === 0) {
107
+ currentNode.previousSibling = previousSibling;
108
+ if (previousSibling) {
109
+ previousSibling.nextSibling = currentNode;
110
+ }
111
+ } else {
112
+ currentNode.previousSibling = newNodes[i - 1];
113
+ }
114
+
115
+ if (i === newNodes.length - 1) {
116
+ currentNode.nextSibling = nextSibling;
117
+ if (nextSibling) {
118
+ nextSibling.previousSibling = currentNode;
119
+ }
120
+ } else {
121
+ currentNode.nextSibling = newNodes[i + 1];
122
+ }
123
+ }
124
+ } else {
125
+ if (previousSibling) {
126
+ previousSibling.nextSibling = nextSibling;
127
+ }
128
+ if (nextSibling) {
129
+ nextSibling.previousSibling = previousSibling;
130
+ }
131
+ }
132
+
133
+ element.parentNode = null;
134
+ element.parentElement = null;
135
+ element.previousSibling = null;
136
+ element.nextSibling = null;
137
+
138
+ parent.children = parent.childNodes.filter(
139
+ (child: any) => child.nodeType === NodeType.ELEMENT_NODE,
140
+ );
141
+
142
+ parent.firstChild =
143
+ parent.childNodes.length > 0 ? parent.childNodes[0] : null;
144
+ parent.lastChild =
145
+ parent.childNodes.length > 0
146
+ ? parent.childNodes[parent.childNodes.length - 1]
147
+ : null;
148
+
149
+ parent.firstElementChild =
150
+ parent.children.length > 0 ? parent.children[0] : null;
151
+ parent.lastElementChild =
152
+ parent.children.length > 0
153
+ ? parent.children[parent.children.length - 1]
154
+ : null;
155
+
156
+ for (let i = 0; i < parent.children.length; i++) {
157
+ const child = parent.children[i];
158
+ child.previousElementSibling = i > 0 ? parent.children[i - 1] : null;
159
+ child.nextElementSibling =
160
+ i < parent.children.length - 1 ? parent.children[i + 1] : null;
161
+ }
162
+
163
+ updateElementContent(parent);
164
+ };
165
+
166
+ export const getInnerHTML = (element: any): string => {
167
+ if (element.nodeType !== NodeType.ELEMENT_NODE) {
168
+ return "";
169
+ }
170
+
171
+ let innerHTML = "";
172
+ for (const child of element.childNodes) {
173
+ if (child.nodeType === NodeType.ELEMENT_NODE) {
174
+ innerHTML += child.outerHTML;
175
+ } else if (child.nodeType === NodeType.TEXT_NODE) {
176
+ innerHTML += escapeTextContent(child.textContent || "");
177
+ } else if (child.nodeType === NodeType.COMMENT_NODE) {
178
+ innerHTML += `<!--${child.data || ""}-->`;
179
+ }
180
+ }
181
+ return innerHTML;
182
+ };
@@ -0,0 +1,20 @@
1
+ import { insertBefore } from "./insert-before.js";
2
+
3
+ export const insertAfter = (
4
+ parent: any,
5
+ newNode: any,
6
+ referenceNode: any,
7
+ ): any => {
8
+ if (referenceNode === null) {
9
+ insertBefore(parent, newNode, parent.firstChild);
10
+ return newNode;
11
+ }
12
+
13
+ const refIndex = parent.childNodes.indexOf(referenceNode);
14
+ if (refIndex === -1) {
15
+ throw new Error("Reference node is not a child of this node");
16
+ }
17
+
18
+ const nextSibling = referenceNode.nextSibling;
19
+ return insertBefore(parent, newNode, nextSibling);
20
+ };