@tkeron/html-parser 0.1.5 → 1.0.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 (91) hide show
  1. package/README.md +1 -7
  2. package/bun.lock +8 -3
  3. package/index.ts +4 -0
  4. package/package.json +13 -6
  5. package/src/css-selector.ts +45 -27
  6. package/src/dom-simulator.ts +162 -20
  7. package/src/encoding.ts +39 -0
  8. package/src/index.ts +9 -0
  9. package/src/parser.ts +478 -183
  10. package/src/serializer.ts +450 -0
  11. package/src/tokenizer.ts +59 -139
  12. package/tests/advanced.test.ts +119 -106
  13. package/tests/custom-elements.test.ts +172 -162
  14. package/tests/dom-extended.test.ts +12 -12
  15. package/tests/dom-manipulation.test.ts +637 -0
  16. package/tests/dom.test.ts +32 -27
  17. package/tests/helpers/tokenizer-adapter.test.ts +70 -0
  18. package/tests/helpers/tokenizer-adapter.ts +65 -0
  19. package/tests/helpers/tree-adapter.test.ts +39 -0
  20. package/tests/helpers/tree-adapter.ts +43 -0
  21. package/tests/html5lib-data/tokenizer/namedEntities.test +42422 -0
  22. package/tests/html5lib-data/tokenizer/pendingSpecChanges.test +9 -0
  23. package/tests/html5lib-data/tree-construction/adoption01.dat +354 -0
  24. package/tests/html5lib-data/tree-construction/adoption02.dat +39 -0
  25. package/tests/html5lib-data/tree-construction/domjs-unsafe.dat +0 -0
  26. package/tests/html5lib-data/tree-construction/entities02.dat +309 -0
  27. package/tests/html5lib-data/tree-construction/html5test-com.dat +301 -0
  28. package/tests/html5lib-data/tree-construction/math.dat +104 -0
  29. package/tests/html5lib-data/tree-construction/namespace-sensitivity.dat +22 -0
  30. package/tests/html5lib-data/tree-construction/noscript01.dat +237 -0
  31. package/tests/html5lib-data/tree-construction/ruby.dat +302 -0
  32. package/tests/html5lib-data/tree-construction/scriptdata01.dat +372 -0
  33. package/tests/html5lib-data/tree-construction/svg.dat +104 -0
  34. package/tests/html5lib-data/tree-construction/template.dat +1673 -0
  35. package/tests/html5lib-data/tree-construction/tests10.dat +853 -0
  36. package/tests/html5lib-data/tree-construction/tests11.dat +523 -0
  37. package/tests/html5lib-data/tree-construction/tests20.dat +842 -0
  38. package/tests/html5lib-data/tree-construction/tests21.dat +306 -0
  39. package/tests/html5lib-data/tree-construction/tests23.dat +168 -0
  40. package/tests/html5lib-data/tree-construction/tests24.dat +79 -0
  41. package/tests/html5lib-data/tree-construction/tests5.dat +210 -0
  42. package/tests/html5lib-data/tree-construction/tests6.dat +663 -0
  43. package/tests/html5lib-data/tree-construction/tests_innerHTML_1.dat +844 -0
  44. package/tests/parser.test.ts +172 -193
  45. package/tests/selectors.test.ts +64 -1
  46. package/tests/serializer-core.test.ts +16 -0
  47. package/tests/serializer-data/core.test +125 -0
  48. package/tests/serializer-data/injectmeta.test +66 -0
  49. package/tests/serializer-data/optionaltags.test +965 -0
  50. package/tests/serializer-data/options.test +60 -0
  51. package/tests/serializer-data/whitespace.test +51 -0
  52. package/tests/serializer-injectmeta.test.ts +16 -0
  53. package/tests/serializer-optionaltags.test.ts +16 -0
  54. package/tests/serializer-options.test.ts +16 -0
  55. package/tests/serializer-whitespace.test.ts +16 -0
  56. package/tests/tokenizer-namedEntities.test.ts +20 -0
  57. package/tests/tokenizer-pendingSpecChanges.test.ts +20 -0
  58. package/tests/tokenizer.test.ts +83 -0
  59. package/tests/tree-construction-adoption01.test.ts +37 -0
  60. package/tests/tree-construction-adoption02.test.ts +34 -0
  61. package/tests/tree-construction-domjs-unsafe.test.ts +24 -0
  62. package/tests/tree-construction-entities02.test.ts +33 -0
  63. package/tests/tree-construction-html5test-com.test.ts +24 -0
  64. package/tests/tree-construction-math.test.ts +18 -0
  65. package/tests/tree-construction-namespace-sensitivity.test.ts +18 -0
  66. package/tests/tree-construction-noscript01.test.ts +18 -0
  67. package/tests/tree-construction-ruby.test.ts +21 -0
  68. package/tests/tree-construction-scriptdata01.test.ts +21 -0
  69. package/tests/tree-construction-svg.test.ts +21 -0
  70. package/tests/tree-construction-template.test.ts +21 -0
  71. package/tests/tree-construction-tests10.test.ts +21 -0
  72. package/tests/tree-construction-tests11.test.ts +21 -0
  73. package/tests/tree-construction-tests20.test.ts +18 -0
  74. package/tests/tree-construction-tests21.test.ts +18 -0
  75. package/tests/tree-construction-tests23.test.ts +18 -0
  76. package/tests/tree-construction-tests24.test.ts +18 -0
  77. package/tests/tree-construction-tests5.test.ts +21 -0
  78. package/tests/tree-construction-tests6.test.ts +21 -0
  79. package/tests/tree-construction-tests_innerHTML_1.test.ts +21 -0
  80. package/tests/void-elements.test.ts +471 -0
  81. package/tests/official/README.md +0 -87
  82. package/tests/official/acid/acid-tests.test.ts +0 -309
  83. package/tests/official/final-output/final-output.test.ts +0 -361
  84. package/tests/official/html5lib/tokenizer-utils.ts +0 -192
  85. package/tests/official/html5lib/tokenizer.test.ts +0 -171
  86. package/tests/official/html5lib/tree-construction-utils.ts +0 -194
  87. package/tests/official/html5lib/tree-construction.test.ts +0 -250
  88. package/tests/official/validator/validator-tests.test.ts +0 -237
  89. package/tests/official/validator-nu/validator-nu.test.ts +0 -335
  90. package/tests/official/whatwg/whatwg-tests.test.ts +0 -205
  91. package/tests/official/wpt/wpt-tests.test.ts +0 -409
@@ -1,94 +1,108 @@
1
- import { expect, test, describe } from 'bun:test';
1
+ import { expect, it, describe } from 'bun:test';
2
2
  import { tokenize } from '../src/tokenizer';
3
- import { parse, ASTNodeType, type ASTNode } from '../src/parser';
3
+ import { parse, domToAST, ASTNodeType, type ASTNode } from '../src/parser';
4
+
5
+ function parseToAST(tokens: any[]): any {
6
+ const dom = parse(tokens);
7
+ const ast = domToAST(dom);
8
+
9
+ const htmlEl = ast.children?.find((c: any) => c.tagName === 'html');
10
+ if (htmlEl) {
11
+ const bodyEl = htmlEl.children?.find((c: any) => c.tagName === 'body');
12
+ if (bodyEl && bodyEl.children) {
13
+ return { type: ASTNodeType.Document, children: bodyEl.children };
14
+ }
15
+ }
16
+ return ast;
17
+ }
4
18
 
5
19
  describe('Custom Elements Support', () => {
6
20
 
7
21
  describe('Basic Custom Elements', () => {
8
- test('should parse simple custom element with single hyphen', () => {
22
+ it('should parse simple custom element with single hyphen', () => {
9
23
  const tokens = tokenize('<my-component></my-component>');
10
- const ast = parse(tokens);
24
+ const ast = parseToAST(tokens);
11
25
 
12
- expect(ast.type).toBe(ASTNodeType.DOCUMENT);
26
+ expect(ast.type).toBe(ASTNodeType.Document);
13
27
  expect(ast.children).toHaveLength(1);
14
28
 
15
29
  const element = ast.children![0]!;
16
- expect(element.type).toBe(ASTNodeType.ELEMENT);
30
+ expect(element.type).toBe(ASTNodeType.Element);
17
31
  expect(element.tagName).toBe('my-component');
18
32
  });
19
33
 
20
- test('should parse custom element with numbers', () => {
34
+ it('should parse custom element with numbers', () => {
21
35
  const tokens = tokenize('<my-component-123></my-component-123>');
22
- const ast = parse(tokens);
36
+ const ast = parseToAST(tokens);
23
37
 
24
38
  const element = ast.children![0]!;
25
- expect(element.type).toBe(ASTNodeType.ELEMENT);
39
+ expect(element.type).toBe(ASTNodeType.Element);
26
40
  expect(element.tagName).toBe('my-component-123');
27
41
  });
28
42
 
29
- test('should parse short custom element', () => {
43
+ it('should parse short custom element', () => {
30
44
  const tokens = tokenize('<x-button></x-button>');
31
- const ast = parse(tokens);
45
+ const ast = parseToAST(tokens);
32
46
 
33
47
  const element = ast.children![0]!;
34
- expect(element.type).toBe(ASTNodeType.ELEMENT);
48
+ expect(element.type).toBe(ASTNodeType.Element);
35
49
  expect(element.tagName).toBe('x-button');
36
50
  });
37
51
 
38
- test('should parse custom element with multiple hyphens', () => {
52
+ it('should parse custom element with multiple hyphens', () => {
39
53
  const tokens = tokenize('<app-header-nav></app-header-nav>');
40
- const ast = parse(tokens);
54
+ const ast = parseToAST(tokens);
41
55
 
42
56
  const element = ast.children![0]!;
43
- expect(element.type).toBe(ASTNodeType.ELEMENT);
57
+ expect(element.type).toBe(ASTNodeType.Element);
44
58
  expect(element.tagName).toBe('app-header-nav');
45
59
  });
46
60
 
47
- test('should parse custom element with many hyphens', () => {
61
+ it('should parse custom element with many hyphens', () => {
48
62
  const tokens = tokenize('<my-custom-super-component></my-custom-super-component>');
49
- const ast = parse(tokens);
63
+ const ast = parseToAST(tokens);
50
64
 
51
65
  const element = ast.children![0]!;
52
- expect(element.type).toBe(ASTNodeType.ELEMENT);
66
+ expect(element.type).toBe(ASTNodeType.Element);
53
67
  expect(element.tagName).toBe('my-custom-super-component');
54
68
  });
55
69
 
56
- test('should parse custom element with dots', () => {
70
+ it('should parse custom element with dots', () => {
57
71
  const tokens = tokenize('<my-comp.v2></my-comp.v2>');
58
- const ast = parse(tokens);
72
+ const ast = parseToAST(tokens);
59
73
 
60
74
  const element = ast.children![0]!;
61
- expect(element.type).toBe(ASTNodeType.ELEMENT);
75
+ expect(element.type).toBe(ASTNodeType.Element);
62
76
  expect(element.tagName).toBe('my-comp.v2');
63
77
  });
64
78
 
65
- test('should parse custom element with underscores', () => {
79
+ it('should parse custom element with underscores', () => {
66
80
  const tokens = tokenize('<my-comp_beta></my-comp_beta>');
67
- const ast = parse(tokens);
81
+ const ast = parseToAST(tokens);
68
82
 
69
83
  const element = ast.children![0]!;
70
- expect(element.type).toBe(ASTNodeType.ELEMENT);
84
+ expect(element.type).toBe(ASTNodeType.Element);
71
85
  expect(element.tagName).toBe('my-comp_beta');
72
86
  });
73
87
  });
74
88
 
75
89
  describe('Custom Elements with Attributes', () => {
76
- test('should parse custom element with class attribute', () => {
90
+ it('should parse custom element with class attribute', () => {
77
91
  const tokens = tokenize('<my-comp class="test"></my-comp>');
78
- const ast = parse(tokens);
92
+ const ast = parseToAST(tokens);
79
93
 
80
94
  const element = ast.children![0]!;
81
- expect(element.type).toBe(ASTNodeType.ELEMENT);
95
+ expect(element.type).toBe(ASTNodeType.Element);
82
96
  expect(element.tagName).toBe('my-comp');
83
97
  expect(element.attributes).toEqual({ class: 'test' });
84
98
  });
85
99
 
86
- test('should parse custom element with multiple attributes', () => {
100
+ it('should parse custom element with multiple attributes', () => {
87
101
  const tokens = tokenize('<my-comp class="test" id="main" data-value="123"></my-comp>');
88
- const ast = parse(tokens);
102
+ const ast = parseToAST(tokens);
89
103
 
90
104
  const element = ast.children![0]!;
91
- expect(element.type).toBe(ASTNodeType.ELEMENT);
105
+ expect(element.type).toBe(ASTNodeType.Element);
92
106
  expect(element.tagName).toBe('my-comp');
93
107
  expect(element.attributes).toEqual({
94
108
  class: 'test',
@@ -97,12 +111,12 @@ describe('Custom Elements Support', () => {
97
111
  });
98
112
  });
99
113
 
100
- test('should parse custom element with custom attributes', () => {
114
+ it('should parse custom element with custom attributes', () => {
101
115
  const tokens = tokenize('<user-card name="John" age="30"></user-card>');
102
- const ast = parse(tokens);
116
+ const ast = parseToAST(tokens);
103
117
 
104
118
  const element = ast.children![0]!;
105
- expect(element.type).toBe(ASTNodeType.ELEMENT);
119
+ expect(element.type).toBe(ASTNodeType.Element);
106
120
  expect(element.tagName).toBe('user-card');
107
121
  expect(element.attributes).toEqual({
108
122
  name: 'John',
@@ -112,34 +126,31 @@ describe('Custom Elements Support', () => {
112
126
  });
113
127
 
114
128
  describe('Self-Closing Custom Elements', () => {
115
- test('should parse self-closing custom element with space', () => {
129
+ it('should parse self-closing custom element with space', () => {
116
130
  const tokens = tokenize('<self-closing />');
117
- const ast = parse(tokens);
131
+ const ast = parseToAST(tokens);
118
132
 
119
133
  const element = ast.children![0]!;
120
- expect(element.type).toBe(ASTNodeType.ELEMENT);
134
+ expect(element.type).toBe(ASTNodeType.Element);
121
135
  expect(element.tagName).toBe('self-closing');
122
- expect(element.isSelfClosing).toBe(true);
123
136
  });
124
137
 
125
- test('should parse self-closing custom element without space', () => {
138
+ it('should parse self-closing custom element without space', () => {
126
139
  const tokens = tokenize('<my-comp/>');
127
- const ast = parse(tokens);
140
+ const ast = parseToAST(tokens);
128
141
 
129
142
  const element = ast.children![0]!;
130
- expect(element.type).toBe(ASTNodeType.ELEMENT);
143
+ expect(element.type).toBe(ASTNodeType.Element);
131
144
  expect(element.tagName).toBe('my-comp');
132
- expect(element.isSelfClosing).toBe(true);
133
145
  });
134
146
 
135
- test('should parse self-closing custom element with attributes', () => {
147
+ it('should parse self-closing custom element with attributes', () => {
136
148
  const tokens = tokenize('<icon-button type="primary" size="lg" />');
137
- const ast = parse(tokens);
149
+ const ast = parseToAST(tokens);
138
150
 
139
151
  const element = ast.children![0]!;
140
- expect(element.type).toBe(ASTNodeType.ELEMENT);
152
+ expect(element.type).toBe(ASTNodeType.Element);
141
153
  expect(element.tagName).toBe('icon-button');
142
- expect(element.isSelfClosing).toBe(true);
143
154
  expect(element.attributes).toEqual({
144
155
  type: 'primary',
145
156
  size: 'lg'
@@ -148,28 +159,28 @@ describe('Custom Elements Support', () => {
148
159
  });
149
160
 
150
161
  describe('Nested Custom Elements', () => {
151
- test('should parse nested custom elements', () => {
162
+ it('should parse nested custom elements', () => {
152
163
  const tokens = tokenize('<outer-comp><inner-comp>text</inner-comp></outer-comp>');
153
- const ast = parse(tokens);
164
+ const ast = parseToAST(tokens);
154
165
 
155
166
  const outer = ast.children![0]!;
156
- expect(outer.type).toBe(ASTNodeType.ELEMENT);
167
+ expect(outer.type).toBe(ASTNodeType.Element);
157
168
  expect(outer.tagName).toBe('outer-comp');
158
169
  expect(outer.children).toHaveLength(1);
159
170
 
160
171
  const inner = outer.children![0]!;
161
- expect(inner.type).toBe(ASTNodeType.ELEMENT);
172
+ expect(inner.type).toBe(ASTNodeType.Element);
162
173
  expect(inner.tagName).toBe('inner-comp');
163
174
  expect(inner.children).toHaveLength(1);
164
175
 
165
176
  const text = inner.children![0]!;
166
- expect(text.type).toBe(ASTNodeType.TEXT);
177
+ expect(text.type).toBe(ASTNodeType.Text);
167
178
  expect(text.content).toBe('text');
168
179
  });
169
180
 
170
- test('should parse deeply nested custom elements', () => {
181
+ it('should parse deeply nested custom elements', () => {
171
182
  const tokens = tokenize('<level-1><level-2><level-3>content</level-3></level-2></level-1>');
172
- const ast = parse(tokens);
183
+ const ast = parseToAST(tokens);
173
184
 
174
185
  const level1 = ast.children![0]!;
175
186
  expect(level1.tagName).toBe('level-1');
@@ -181,9 +192,9 @@ describe('Custom Elements Support', () => {
181
192
  expect(level3.tagName).toBe('level-3');
182
193
  });
183
194
 
184
- test('should parse custom elements mixed with standard elements', () => {
195
+ it('should parse custom elements mixed with standard elements', () => {
185
196
  const tokens = tokenize('<div><my-comp><span>text</span></my-comp></div>');
186
- const ast = parse(tokens);
197
+ const ast = parseToAST(tokens);
187
198
 
188
199
  const div = ast.children![0]!;
189
200
  expect(div.tagName).toBe('div');
@@ -197,57 +208,57 @@ describe('Custom Elements Support', () => {
197
208
  });
198
209
 
199
210
  describe('Tag Name Normalization', () => {
200
- test('should normalize custom element tagName to UPPERCASE', () => {
211
+ it('should normalize custom element tagName to UPPERCASE', () => {
201
212
  const tokens = tokenize('<my-comp></my-comp>');
202
- const ast = parse(tokens);
213
+ const ast = parseToAST(tokens);
203
214
 
204
215
  const element = ast.children![0]!;
205
- expect(element.tagName.toUpperCase()).toBe('MY-COMP');
216
+ expect(element.tagName).toBe('my-comp');
206
217
  });
207
218
 
208
- test('should normalize nodeName to UPPERCASE', () => {
219
+ it('should normalize nodeName to UPPERCASE', () => {
209
220
  const tokens = tokenize('<my-comp></my-comp>');
210
- const ast = parse(tokens);
221
+ const ast = parseToAST(tokens);
211
222
 
212
223
  const element = ast.children![0]!;
213
224
 
214
225
  if (element.nodeName) {
215
- expect(element.nodeName.toUpperCase()).toBe('MY-COMP');
226
+ expect(element.nodeName).toBe('my-comp');
216
227
  }
217
228
  });
218
229
  });
219
230
 
220
231
  describe('Regression Tests - Standard Elements', () => {
221
- test('should still parse standard div element', () => {
232
+ it('should still parse standard div element', () => {
222
233
  const tokens = tokenize('<div></div>');
223
- const ast = parse(tokens);
234
+ const ast = parseToAST(tokens);
224
235
 
225
236
  const element = ast.children![0]!;
226
- expect(element.type).toBe(ASTNodeType.ELEMENT);
237
+ expect(element.type).toBe(ASTNodeType.Element);
227
238
  expect(element.tagName).toBe('div');
228
239
  });
229
240
 
230
- test('should still parse standard header element', () => {
241
+ it('should still parse standard header element', () => {
231
242
  const tokens = tokenize('<header></header>');
232
- const ast = parse(tokens);
243
+ const ast = parseToAST(tokens);
233
244
 
234
245
  const element = ast.children![0]!;
235
- expect(element.type).toBe(ASTNodeType.ELEMENT);
246
+ expect(element.type).toBe(ASTNodeType.Element);
236
247
  expect(element.tagName).toBe('header');
237
248
  });
238
249
 
239
- test('should still parse standard section element', () => {
250
+ it('should still parse standard section element', () => {
240
251
  const tokens = tokenize('<section></section>');
241
- const ast = parse(tokens);
252
+ const ast = parseToAST(tokens);
242
253
 
243
254
  const element = ast.children![0]!;
244
- expect(element.type).toBe(ASTNodeType.ELEMENT);
255
+ expect(element.type).toBe(ASTNodeType.Element);
245
256
  expect(element.tagName).toBe('section');
246
257
  });
247
258
 
248
- test('should distinguish between header tag and header-comp custom element', () => {
259
+ it('should distinguish between header tag and header-comp custom element', () => {
249
260
  const tokens = tokenize('<header></header><header-comp></header-comp>');
250
- const ast = parse(tokens);
261
+ const ast = parseToAST(tokens);
251
262
 
252
263
  expect(ast.children).toHaveLength(2);
253
264
 
@@ -260,41 +271,41 @@ describe('Custom Elements Support', () => {
260
271
  });
261
272
 
262
273
  describe('Custom Elements with Different Formats', () => {
263
- test('should parse custom element: my-comp', () => {
274
+ it('should parse custom element: my-comp', () => {
264
275
  const tokens = tokenize('<my-comp></my-comp>');
265
- const ast = parse(tokens);
276
+ const ast = parseToAST(tokens);
266
277
 
267
278
  const element = ast.children![0]!;
268
279
  expect(element.tagName).toBe('my-comp');
269
280
  });
270
281
 
271
- test('should parse custom element: comp-v2', () => {
282
+ it('should parse custom element: comp-v2', () => {
272
283
  const tokens = tokenize('<comp-v2></comp-v2>');
273
- const ast = parse(tokens);
284
+ const ast = parseToAST(tokens);
274
285
 
275
286
  const element = ast.children![0]!;
276
287
  expect(element.tagName).toBe('comp-v2');
277
288
  });
278
289
 
279
- test('should parse custom element: my-comp-123', () => {
290
+ it('should parse custom element: my-comp-123', () => {
280
291
  const tokens = tokenize('<my-comp-123></my-comp-123>');
281
- const ast = parse(tokens);
292
+ const ast = parseToAST(tokens);
282
293
 
283
294
  const element = ast.children![0]!;
284
295
  expect(element.tagName).toBe('my-comp-123');
285
296
  });
286
297
 
287
- test('should parse custom element: x-foo', () => {
298
+ it('should parse custom element: x-foo', () => {
288
299
  const tokens = tokenize('<x-foo></x-foo>');
289
- const ast = parse(tokens);
300
+ const ast = parseToAST(tokens);
290
301
 
291
302
  const element = ast.children![0]!;
292
303
  expect(element.tagName).toBe('x-foo');
293
304
  });
294
305
 
295
- test('should parse custom element with numbers: comp-123-test', () => {
306
+ it('should parse custom element with numbers: comp-123-test', () => {
296
307
  const tokens = tokenize('<comp-123-test></comp-123-test>');
297
- const ast = parse(tokens);
308
+ const ast = parseToAST(tokens);
298
309
 
299
310
  const element = ast.children![0]!;
300
311
  expect(element.tagName).toBe('comp-123-test');
@@ -302,17 +313,17 @@ describe('Custom Elements Support', () => {
302
313
  });
303
314
 
304
315
  describe('Edge Cases', () => {
305
- test('should parse custom element with whitespace before closing bracket', () => {
316
+ it('should parse custom element with whitespace before closing bracket', () => {
306
317
  const tokens = tokenize('<my-comp ></my-comp>');
307
- const ast = parse(tokens);
318
+ const ast = parseToAST(tokens);
308
319
 
309
320
  const element = ast.children![0]!;
310
321
  expect(element.tagName).toBe('my-comp');
311
322
  });
312
323
 
313
- test('should parse multiple custom elements in sequence', () => {
324
+ it('should parse multiple custom elements in sequence', () => {
314
325
  const tokens = tokenize('<first-comp></first-comp><second-comp></second-comp><third-comp></third-comp>');
315
- const ast = parse(tokens);
326
+ const ast = parseToAST(tokens);
316
327
 
317
328
  expect(ast.children).toHaveLength(3);
318
329
  expect(ast.children![0]!.tagName).toBe('first-comp');
@@ -320,20 +331,20 @@ describe('Custom Elements Support', () => {
320
331
  expect(ast.children![2]!.tagName).toBe('third-comp');
321
332
  });
322
333
 
323
- test('should parse custom element with text content', () => {
334
+ it('should parse custom element with text content', () => {
324
335
  const tokens = tokenize('<user-name>John Doe</user-name>');
325
- const ast = parse(tokens);
336
+ const ast = parseToAST(tokens);
326
337
 
327
338
  const element = ast.children![0]!;
328
339
  expect(element.tagName).toBe('user-name');
329
340
  expect(element.children).toHaveLength(1);
330
- expect(element.children![0]!.type).toBe(ASTNodeType.TEXT);
341
+ expect(element.children![0]!.type).toBe(ASTNodeType.Text);
331
342
  expect(element.children![0]!.content).toBe('John Doe');
332
343
  });
333
344
 
334
- test('should parse custom element with child elements and text', () => {
345
+ it('should parse custom element with child elements and text', () => {
335
346
  const tokens = tokenize('<card-header><h1>Title</h1><sub-title>Subtitle</sub-title></card-header>');
336
- const ast = parse(tokens);
347
+ const ast = parseToAST(tokens);
337
348
 
338
349
  const cardHeader = ast.children![0]!;
339
350
  expect(cardHeader.tagName).toBe('card-header');
@@ -343,26 +354,25 @@ describe('Custom Elements Support', () => {
343
354
  expect(cardHeader.children![1]!.tagName).toBe('sub-title');
344
355
  });
345
356
 
346
- test('should handle unclosed custom element gracefully', () => {
357
+ it('should handle unclosed custom element gracefully', () => {
347
358
  const tokens = tokenize('<my-comp>');
348
- const ast = parse(tokens);
359
+ const ast = parseToAST(tokens);
349
360
 
350
361
  const element = ast.children![0]!;
351
362
  expect(element.tagName).toBe('my-comp');
352
363
  });
353
364
 
354
- test('should parse custom element with trailing slash in opening tag', () => {
365
+ it('should parse custom element with trailing slash in opening tag', () => {
355
366
  const tokens = tokenize('<my-comp/>');
356
- const ast = parse(tokens);
367
+ const ast = parseToAST(tokens);
357
368
 
358
369
  const element = ast.children![0]!;
359
370
  expect(element.tagName).toBe('my-comp');
360
- expect(element.isSelfClosing).toBe(true);
361
371
  });
362
372
  });
363
373
 
364
374
  describe('Complex Real-World Scenarios', () => {
365
- test('should parse web component with shadow DOM structure', () => {
375
+ it('should parse web component with shadow DOM structure', () => {
366
376
  const html = `
367
377
  <user-profile>
368
378
  <profile-header>
@@ -380,10 +390,10 @@ describe('Custom Elements Support', () => {
380
390
  `;
381
391
 
382
392
  const tokens = tokenize(html);
383
- const ast = parse(tokens);
393
+ const ast = parseToAST(tokens);
384
394
 
385
395
 
386
- const userProfile = ast.children!.find(node => node.type === ASTNodeType.ELEMENT)!;
396
+ const userProfile = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
387
397
  expect(userProfile.tagName).toBe('user-profile');
388
398
 
389
399
 
@@ -391,7 +401,7 @@ describe('Custom Elements Support', () => {
391
401
  expect(userProfile.children!.length).toBeGreaterThan(0);
392
402
  });
393
403
 
394
- test('should parse framework-style component tree', () => {
404
+ it('should parse framework-style component tree', () => {
395
405
  const html = `
396
406
  <app-root>
397
407
  <app-header>
@@ -410,16 +420,16 @@ describe('Custom Elements Support', () => {
410
420
  `;
411
421
 
412
422
  const tokens = tokenize(html);
413
- const ast = parse(tokens);
423
+ const ast = parseToAST(tokens);
414
424
 
415
425
 
416
- const appRoot = ast.children!.find(node => node.type === ASTNodeType.ELEMENT)!;
426
+ const appRoot = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
417
427
  expect(appRoot.tagName).toBe('app-root');
418
428
  });
419
429
 
420
- test('should parse custom elements with data attributes', () => {
430
+ it('should parse custom elements with data attributes', () => {
421
431
  const tokens = tokenize('<my-widget data-id="123" data-type="primary" data-config=\'{"key":"value"}\'></my-widget>');
422
- const ast = parse(tokens);
432
+ const ast = parseToAST(tokens);
423
433
 
424
434
  const element = ast.children![0]!;
425
435
  expect(element.tagName).toBe('my-widget');
@@ -430,7 +440,7 @@ describe('Custom Elements Support', () => {
430
440
  });
431
441
 
432
442
  describe('Custom Element Name Validation Pattern', () => {
433
- test('valid: starts with lowercase letter, contains hyphen', () => {
443
+ it('valid: starts with lowercase letter, contains hyphen', () => {
434
444
  const validNames = [
435
445
  'a-b',
436
446
  'my-component',
@@ -444,13 +454,13 @@ describe('Custom Elements Support', () => {
444
454
 
445
455
  validNames.forEach(name => {
446
456
  const tokens = tokenize(`<${name}></${name}>`);
447
- const ast = parse(tokens);
457
+ const ast = parseToAST(tokens);
448
458
  const element = ast.children![0]!;
449
459
  expect(element.tagName).toBe(name);
450
460
  });
451
461
  });
452
462
 
453
- test('should handle complex hyphenated names', () => {
463
+ it('should handle complex hyphenated names', () => {
454
464
  const complexNames = [
455
465
  'my-super-long-component-name',
456
466
  'x-1-2-3-4',
@@ -460,7 +470,7 @@ describe('Custom Elements Support', () => {
460
470
 
461
471
  complexNames.forEach(name => {
462
472
  const tokens = tokenize(`<${name}></${name}>`);
463
- const ast = parse(tokens);
473
+ const ast = parseToAST(tokens);
464
474
  const element = ast.children![0]!;
465
475
  expect(element.tagName).toBe(name);
466
476
  });
@@ -468,7 +478,7 @@ describe('Custom Elements Support', () => {
468
478
  });
469
479
 
470
480
  describe('Tokenizer-specific Tests', () => {
471
- test('tokenizer should capture full custom element name', () => {
481
+ it('tokenizer should capture full custom element name', () => {
472
482
  const tokens = tokenize('<my-component-123></my-component-123>');
473
483
 
474
484
 
@@ -482,7 +492,7 @@ describe('Custom Elements Support', () => {
482
492
  expect(closeTag!.value).toBe('my-component-123');
483
493
  });
484
494
 
485
- test('tokenizer should handle custom element with attributes correctly', () => {
495
+ it('tokenizer should handle custom element with attributes correctly', () => {
486
496
  const tokens = tokenize('<my-comp class="test" id="main"></my-comp>');
487
497
 
488
498
  const openTag = tokens.find(t => t.type === 'TAG_OPEN');
@@ -494,7 +504,7 @@ describe('Custom Elements Support', () => {
494
504
  });
495
505
  });
496
506
 
497
- test('tokenizer should handle self-closing custom elements', () => {
507
+ it('tokenizer should handle self-closing custom elements', () => {
498
508
  const tokens = tokenize('<my-comp />');
499
509
 
500
510
  const openTag = tokens.find(t => t.type === 'TAG_OPEN');
@@ -505,19 +515,19 @@ describe('Custom Elements Support', () => {
505
515
  });
506
516
 
507
517
  describe('Customized Built-in Elements (is attribute)', () => {
508
- test('should parse button with is attribute', () => {
518
+ it('should parse button with is attribute', () => {
509
519
  const tokens = tokenize('<button is="plastic-button">Click Me!</button>');
510
- const ast = parse(tokens);
520
+ const ast = parseToAST(tokens);
511
521
 
512
522
  const button = ast.children![0]!;
513
- expect(button.type).toBe(ASTNodeType.ELEMENT);
523
+ expect(button.type).toBe(ASTNodeType.Element);
514
524
  expect(button.tagName).toBe('button');
515
525
  expect(button.attributes).toHaveProperty('is', 'plastic-button');
516
526
  });
517
527
 
518
- test('should parse input with is attribute', () => {
528
+ it('should parse input with is attribute', () => {
519
529
  const tokens = tokenize('<input is="custom-input" type="text" />');
520
- const ast = parse(tokens);
530
+ const ast = parseToAST(tokens);
521
531
 
522
532
  const input = ast.children![0]!;
523
533
  expect(input.tagName).toBe('input');
@@ -525,9 +535,9 @@ describe('Custom Elements Support', () => {
525
535
  expect(input.attributes).toHaveProperty('type', 'text');
526
536
  });
527
537
 
528
- test('should parse div with is attribute', () => {
538
+ it('should parse div with is attribute', () => {
529
539
  const tokens = tokenize('<div is="fancy-div"></div>');
530
- const ast = parse(tokens);
540
+ const ast = parseToAST(tokens);
531
541
 
532
542
  const div = ast.children![0]!;
533
543
  expect(div.tagName).toBe('div');
@@ -536,25 +546,25 @@ describe('Custom Elements Support', () => {
536
546
  });
537
547
 
538
548
  describe('Reserved Custom Element Names', () => {
539
- test('should parse annotation-xml (reserved SVG name)', () => {
549
+ it('should parse annotation-xml (reserved SVG name)', () => {
540
550
  const tokens = tokenize('<annotation-xml></annotation-xml>');
541
- const ast = parse(tokens);
551
+ const ast = parseToAST(tokens);
542
552
 
543
553
  const element = ast.children![0]!;
544
554
  expect(element.tagName).toBe('annotation-xml');
545
555
  });
546
556
 
547
- test('should parse font-face (reserved SVG name)', () => {
557
+ it('should parse font-face (reserved SVG name)', () => {
548
558
  const tokens = tokenize('<font-face></font-face>');
549
- const ast = parse(tokens);
559
+ const ast = parseToAST(tokens);
550
560
 
551
561
  const element = ast.children![0]!;
552
562
  expect(element.tagName).toBe('font-face');
553
563
  });
554
564
 
555
- test('should parse color-profile (reserved SVG name)', () => {
565
+ it('should parse color-profile (reserved SVG name)', () => {
556
566
  const tokens = tokenize('<color-profile></color-profile>');
557
- const ast = parse(tokens);
567
+ const ast = parseToAST(tokens);
558
568
 
559
569
  const element = ast.children![0]!;
560
570
  expect(element.tagName).toBe('color-profile');
@@ -562,33 +572,33 @@ describe('Custom Elements Support', () => {
562
572
  });
563
573
 
564
574
  describe('Unicode Custom Element Names', () => {
565
- test('should parse custom element with Greek letters', () => {
575
+ it('should parse custom element with Greek letters', () => {
566
576
  const tokens = tokenize('<math-α></math-α>');
567
- const ast = parse(tokens);
577
+ const ast = parseToAST(tokens);
568
578
 
569
579
  const element = ast.children![0]!;
570
580
  expect(element.tagName).toBe('math-α');
571
581
  });
572
582
 
573
- test('should parse custom element with emoji', () => {
583
+ it('should parse custom element with emoji', () => {
574
584
  const tokens = tokenize('<emotion-😍></emotion-😍>');
575
- const ast = parse(tokens);
585
+ const ast = parseToAST(tokens);
576
586
 
577
587
  const element = ast.children![0]!;
578
588
  expect(element.tagName).toBe('emotion-😍');
579
589
  });
580
590
 
581
- test('should parse custom element with Chinese characters', () => {
591
+ it('should parse custom element with Chinese characters', () => {
582
592
  const tokens = tokenize('<my-元素></my-元素>');
583
- const ast = parse(tokens);
593
+ const ast = parseToAST(tokens);
584
594
 
585
595
  const element = ast.children![0]!;
586
596
  expect(element.tagName).toBe('my-元素');
587
597
  });
588
598
 
589
- test('should parse custom element with Arabic characters', () => {
599
+ it('should parse custom element with Arabic characters', () => {
590
600
  const tokens = tokenize('<my-عنصر></my-عنصر>');
591
- const ast = parse(tokens);
601
+ const ast = parseToAST(tokens);
592
602
 
593
603
  const element = ast.children![0]!;
594
604
  expect(element.tagName).toBe('my-عنصر');
@@ -596,32 +606,32 @@ describe('Custom Elements Support', () => {
596
606
  });
597
607
 
598
608
  describe('Extreme Edge Cases', () => {
599
- test('should parse very long custom element name', () => {
609
+ it('should parse very long custom element name', () => {
600
610
  const longName = 'my-super-duper-extra-long-custom-component-name-that-keeps-going-and-going';
601
611
  const tokens = tokenize(`<${longName}></${longName}>`);
602
- const ast = parse(tokens);
612
+ const ast = parseToAST(tokens);
603
613
 
604
614
  const element = ast.children![0]!;
605
615
  expect(element.tagName).toBe(longName);
606
616
  });
607
617
 
608
- test('should parse custom element with many consecutive hyphens', () => {
618
+ it('should parse custom element with many consecutive hyphens', () => {
609
619
  const tokens = tokenize('<my---component></my---component>');
610
- const ast = parse(tokens);
620
+ const ast = parseToAST(tokens);
611
621
 
612
622
  const element = ast.children![0]!;
613
623
  expect(element.tagName).toBe('my---component');
614
624
  });
615
625
 
616
- test('should parse custom element starting with x-', () => {
626
+ it('should parse custom element starting with x-', () => {
617
627
  const tokens = tokenize('<x-></x->');
618
- const ast = parse(tokens);
628
+ const ast = parseToAST(tokens);
619
629
 
620
630
  const element = ast.children![0]!;
621
631
  expect(element.tagName).toBe('x-');
622
632
  });
623
633
 
624
- test('should handle custom element with newlines in attributes', () => {
634
+ it('should handle custom element with newlines in attributes', () => {
625
635
  const html = `<my-comp
626
636
  class="test"
627
637
  id="main"
@@ -629,7 +639,7 @@ describe('Custom Elements Support', () => {
629
639
  ></my-comp>`;
630
640
 
631
641
  const tokens = tokenize(html);
632
- const ast = parse(tokens);
642
+ const ast = parseToAST(tokens);
633
643
 
634
644
  const element = ast.children![0]!;
635
645
  expect(element.tagName).toBe('my-comp');
@@ -637,19 +647,19 @@ describe('Custom Elements Support', () => {
637
647
  expect(element.attributes).toHaveProperty('id', 'main');
638
648
  });
639
649
 
640
- test('should parse custom element mixed with comments', () => {
650
+ it('should parse custom element mixed with comments', () => {
641
651
  const html = '<!-- comment --><my-comp>text</my-comp><!-- another comment -->';
642
652
  const tokens = tokenize(html);
643
- const ast = parse(tokens);
653
+ const ast = parseToAST(tokens);
644
654
 
645
655
 
646
- const myComp = ast.children!.find(node => node.type === ASTNodeType.ELEMENT)!;
656
+ const myComp = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
647
657
  expect(myComp.tagName).toBe('my-comp');
648
658
  });
649
659
 
650
- test('should parse empty custom element', () => {
660
+ it('should parse empty custom element', () => {
651
661
  const tokens = tokenize('<my-comp></my-comp>');
652
- const ast = parse(tokens);
662
+ const ast = parseToAST(tokens);
653
663
 
654
664
  const element = ast.children![0]!;
655
665
  expect(element.tagName).toBe('my-comp');
@@ -657,9 +667,9 @@ describe('Custom Elements Support', () => {
657
667
  expect(element.children!.length).toBe(0);
658
668
  });
659
669
 
660
- test('should parse custom element with only whitespace content', () => {
670
+ it('should parse custom element with only whitespace content', () => {
661
671
  const tokens = tokenize('<my-comp> \n\t </my-comp>');
662
- const ast = parse(tokens);
672
+ const ast = parseToAST(tokens);
663
673
 
664
674
  const element = ast.children![0]!;
665
675
  expect(element.tagName).toBe('my-comp');
@@ -669,25 +679,25 @@ describe('Custom Elements Support', () => {
669
679
  });
670
680
 
671
681
  describe('Malformed Custom Elements', () => {
672
- test('should handle mismatched closing tag', () => {
682
+ it('should handle mismatched closing tag', () => {
673
683
  const tokens = tokenize('<my-comp></my-other>');
674
- const ast = parse(tokens);
684
+ const ast = parseToAST(tokens);
675
685
 
676
686
  const element = ast.children![0]!;
677
687
  expect(element.tagName).toBe('my-comp');
678
688
  });
679
689
 
680
- test('should handle multiple unclosed custom elements', () => {
690
+ it('should handle multiple unclosed custom elements', () => {
681
691
  const tokens = tokenize('<my-comp><nested-comp><deep-comp>');
682
- const ast = parse(tokens);
692
+ const ast = parseToAST(tokens);
683
693
 
684
694
  const element = ast.children![0]!;
685
695
  expect(element.tagName).toBe('my-comp');
686
696
  });
687
697
 
688
- test('should handle custom element with malformed attributes', () => {
698
+ it('should handle custom element with malformed attributes', () => {
689
699
  const tokens = tokenize('<my-comp attr-without-value attr="value"></my-comp>');
690
- const ast = parse(tokens);
700
+ const ast = parseToAST(tokens);
691
701
 
692
702
  const element = ast.children![0]!;
693
703
  expect(element.tagName).toBe('my-comp');
@@ -696,26 +706,26 @@ describe('Custom Elements Support', () => {
696
706
  });
697
707
 
698
708
  describe('Custom Elements in Special Contexts', () => {
699
- test('should parse custom element inside table', () => {
709
+ it('should parse custom element inside table', () => {
700
710
  const tokens = tokenize('<table><tr><td><my-cell>content</my-cell></td></tr></table>');
701
- const ast = parse(tokens);
711
+ const ast = parseToAST(tokens);
702
712
 
703
713
 
704
714
  const table = ast.children![0]!;
705
715
  expect(table.tagName).toBe('table');
706
716
  });
707
717
 
708
- test('should parse custom element inside list', () => {
718
+ it('should parse custom element inside list', () => {
709
719
  const tokens = tokenize('<ul><li><list-item></list-item></li></ul>');
710
- const ast = parse(tokens);
720
+ const ast = parseToAST(tokens);
711
721
 
712
722
  const ul = ast.children![0]!;
713
723
  expect(ul.tagName).toBe('ul');
714
724
  });
715
725
 
716
- test('should parse custom element inside form', () => {
726
+ it('should parse custom element inside form', () => {
717
727
  const tokens = tokenize('<form><form-field name="test"></form-field></form>');
718
- const ast = parse(tokens);
728
+ const ast = parseToAST(tokens);
719
729
 
720
730
  const form = ast.children![0]!;
721
731
  expect(form.tagName).toBe('form');
@@ -723,9 +733,9 @@ describe('Custom Elements Support', () => {
723
733
  });
724
734
 
725
735
  describe('ARIA and Accessibility', () => {
726
- test('should parse custom element with ARIA attributes', () => {
736
+ it('should parse custom element with ARIA attributes', () => {
727
737
  const tokens = tokenize('<my-button role="button" aria-label="Click me" aria-disabled="true"></my-button>');
728
- const ast = parse(tokens);
738
+ const ast = parseToAST(tokens);
729
739
 
730
740
  const element = ast.children![0]!;
731
741
  expect(element.tagName).toBe('my-button');
@@ -734,9 +744,9 @@ describe('Custom Elements Support', () => {
734
744
  expect(element.attributes).toHaveProperty('aria-disabled', 'true');
735
745
  });
736
746
 
737
- test('should parse custom element with tabindex', () => {
747
+ it('should parse custom element with tabindex', () => {
738
748
  const tokens = tokenize('<my-comp tabindex="0"></my-comp>');
739
- const ast = parse(tokens);
749
+ const ast = parseToAST(tokens);
740
750
 
741
751
  const element = ast.children![0]!;
742
752
  expect(element.attributes).toHaveProperty('tabindex', '0');