@mdream/js 0.17.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.
@@ -0,0 +1,1201 @@
1
+ import { _ as TagIdMap, a as LIST_ITEM_SPACING, i as HTML_ENTITIES, l as TABLE_ROW_SPACING, o as NO_SPACING, t as BLOCKQUOTE_SPACING } from "./const.mjs";
2
+ //#region src/tags.ts
3
+ function resolveUrl(url, origin) {
4
+ if (!url) return url;
5
+ if (url.startsWith("//")) return `https:${url}`;
6
+ if (url.startsWith("#")) return url;
7
+ if (origin) {
8
+ if (url.startsWith("/") && origin) return `${origin.endsWith("/") ? origin.slice(0, -1) : origin}${url}`;
9
+ if (url.startsWith("./")) return `${origin}/${url.slice(2)}`;
10
+ if (!url.startsWith("http")) return `${origin}/${url.startsWith("/") ? url.slice(1) : url}`;
11
+ }
12
+ return url;
13
+ }
14
+ function isInsideTableCell(node) {
15
+ return (node.depthMap[32] || 0) > 0;
16
+ }
17
+ function getLanguageFromClass(className) {
18
+ if (!className) return "";
19
+ const langParts = className.split(" ").map((c) => c.split("language-")[1]).filter(Boolean);
20
+ return langParts && langParts.length > 0 ? langParts[0].trim() : "";
21
+ }
22
+ function handleHeading(depth) {
23
+ return {
24
+ enter: ({ node }) => {
25
+ if ((node.depthMap[26] || 0) > 0) return `<h${depth}>`;
26
+ return `${"#".repeat(depth)} `;
27
+ },
28
+ exit: ({ node }) => {
29
+ if ((node.depthMap[26] || 0) > 0) return `</h${depth}>`;
30
+ },
31
+ collapsesInnerWhiteSpace: true
32
+ };
33
+ }
34
+ const Strong = {
35
+ enter: ({ node }) => {
36
+ if ((node?.depthMap?.[15] || 0) > 1) return "";
37
+ return "**";
38
+ },
39
+ exit: ({ node }) => {
40
+ if ((node?.depthMap?.[15] || 0) > 1) return "";
41
+ return "**";
42
+ },
43
+ collapsesInnerWhiteSpace: true,
44
+ spacing: NO_SPACING,
45
+ isInline: true
46
+ };
47
+ const Emphasis = {
48
+ enter: ({ node }) => {
49
+ if ((node?.depthMap?.[17] || 0) > 1) return "";
50
+ return "_";
51
+ },
52
+ exit: ({ node }) => {
53
+ if ((node?.depthMap?.[17] || 0) > 1) return "";
54
+ return "_";
55
+ },
56
+ collapsesInnerWhiteSpace: true,
57
+ spacing: NO_SPACING,
58
+ isInline: true
59
+ };
60
+ const tagHandlers = {
61
+ [1]: {
62
+ spacing: NO_SPACING,
63
+ collapsesInnerWhiteSpace: true
64
+ },
65
+ [2]: {
66
+ enter: () => "<details>",
67
+ exit: () => "</details>\n\n"
68
+ },
69
+ [3]: {
70
+ enter: () => "<summary>",
71
+ exit: () => "</summary>\n\n"
72
+ },
73
+ [4]: {
74
+ collapsesInnerWhiteSpace: true,
75
+ isNonNesting: true,
76
+ spacing: NO_SPACING
77
+ },
78
+ [52]: {
79
+ excludesTextNodes: true,
80
+ isNonNesting: true
81
+ },
82
+ [53]: {
83
+ isNonNesting: true,
84
+ excludesTextNodes: true
85
+ },
86
+ [5]: {
87
+ collapsesInnerWhiteSpace: true,
88
+ isSelfClosing: true,
89
+ spacing: NO_SPACING
90
+ },
91
+ [6]: {
92
+ enter: ({ node }) => {
93
+ return isInsideTableCell(node) ? "<br>" : void 0;
94
+ },
95
+ isSelfClosing: true,
96
+ spacing: NO_SPACING,
97
+ collapsesInnerWhiteSpace: true,
98
+ isInline: true
99
+ },
100
+ [7]: handleHeading(1),
101
+ [8]: handleHeading(2),
102
+ [9]: handleHeading(3),
103
+ [10]: handleHeading(4),
104
+ [11]: handleHeading(5),
105
+ [12]: handleHeading(6),
106
+ [13]: {
107
+ enter: () => "---",
108
+ isSelfClosing: true
109
+ },
110
+ [14]: Strong,
111
+ [15]: Strong,
112
+ [16]: Emphasis,
113
+ [17]: Emphasis,
114
+ [18]: {
115
+ enter: () => "~~",
116
+ exit: () => "~~",
117
+ collapsesInnerWhiteSpace: true,
118
+ spacing: NO_SPACING,
119
+ isInline: true
120
+ },
121
+ [19]: {
122
+ enter: () => "<sub>",
123
+ exit: () => "</sub>",
124
+ collapsesInnerWhiteSpace: true,
125
+ spacing: NO_SPACING,
126
+ isInline: true
127
+ },
128
+ [20]: {
129
+ enter: () => "<sup>",
130
+ exit: () => "</sup>",
131
+ collapsesInnerWhiteSpace: true,
132
+ spacing: NO_SPACING,
133
+ isInline: true
134
+ },
135
+ [21]: {
136
+ enter: () => "<ins>",
137
+ exit: () => "</ins>",
138
+ collapsesInnerWhiteSpace: true,
139
+ spacing: NO_SPACING,
140
+ isInline: true
141
+ },
142
+ [22]: {
143
+ enter: ({ node }) => {
144
+ const depth = node.depthMap[22] || 1;
145
+ let prefix = "> ".repeat(depth);
146
+ const liDepth = node.depthMap[25] || 0;
147
+ if (liDepth > 0) prefix = `\n${" ".repeat(liDepth)}${prefix}`;
148
+ return prefix;
149
+ },
150
+ spacing: BLOCKQUOTE_SPACING
151
+ },
152
+ [23]: {
153
+ enter: ({ node }) => {
154
+ if ((node.depthMap[34] || 0) > 0) return `\`\`\`${getLanguageFromClass(node.attributes?.class)}\n`;
155
+ return "`";
156
+ },
157
+ exit: ({ node }) => {
158
+ return (node.depthMap[34] || 0) > 0 ? `\n\`\`\`` : "`";
159
+ },
160
+ collapsesInnerWhiteSpace: true,
161
+ spacing: NO_SPACING,
162
+ isInline: true
163
+ },
164
+ [24]: {
165
+ enter: ({ node }) => isInsideTableCell(node) ? "<ul>" : void 0,
166
+ exit: ({ node }) => isInsideTableCell(node) ? "</ul>" : void 0
167
+ },
168
+ [33]: {
169
+ enter: ({ node }) => isInsideTableCell(node) ? "<ol>" : void 0,
170
+ exit: ({ node }) => isInsideTableCell(node) ? "</ol>" : void 0
171
+ },
172
+ [25]: {
173
+ enter: ({ node }) => {
174
+ if (isInsideTableCell(node)) return "<li>";
175
+ const depth = (node.depthMap[24] || 0) + (node.depthMap[33] || 0) - 1;
176
+ const isOrdered = node.parent?.tagId === 33;
177
+ return `${" ".repeat(Math.max(0, depth))}${isOrdered ? `${node.index + 1}. ` : "- "}`;
178
+ },
179
+ exit: ({ node }) => isInsideTableCell(node) ? "</li>" : void 0,
180
+ spacing: LIST_ITEM_SPACING
181
+ },
182
+ [26]: {
183
+ enter: ({ node }) => {
184
+ if (node.attributes?.href) return "[";
185
+ },
186
+ exit: ({ node, state }) => {
187
+ if (!node.attributes?.href) return "";
188
+ const href = resolveUrl(node.attributes?.href || "", state.options?.origin);
189
+ let title = node.attributes?.title;
190
+ if (state.lastContentCache === title) title = "";
191
+ return title ? `](${href} "${title}")` : `](${href})`;
192
+ },
193
+ collapsesInnerWhiteSpace: true,
194
+ spacing: NO_SPACING,
195
+ isInline: true
196
+ },
197
+ [27]: {
198
+ enter: ({ node, state }) => {
199
+ return `![${node.attributes?.alt || ""}](${resolveUrl(node.attributes?.src || "", state.options?.origin)})`;
200
+ },
201
+ collapsesInnerWhiteSpace: true,
202
+ isSelfClosing: true,
203
+ spacing: NO_SPACING,
204
+ isInline: true
205
+ },
206
+ [28]: {
207
+ enter: ({ node, state }) => {
208
+ if (isInsideTableCell(node)) return "<table>";
209
+ if ((node.depthMap[28] || 0) <= 1) state.tableRenderedTable = false;
210
+ state.tableColumnAlignments = [];
211
+ },
212
+ exit: ({ node }) => isInsideTableCell(node) ? "</table>" : void 0
213
+ },
214
+ [29]: {
215
+ enter: ({ node }) => {
216
+ if (isInsideTableCell(node)) return "<thead>";
217
+ },
218
+ exit: ({ node }) => isInsideTableCell(node) ? "</thead>" : void 0,
219
+ spacing: TABLE_ROW_SPACING,
220
+ excludesTextNodes: true
221
+ },
222
+ [30]: {
223
+ enter: ({ node, state }) => {
224
+ if (isInsideTableCell(node)) return "<tr>";
225
+ state.tableCurrentRowCells = 0;
226
+ return "| ";
227
+ },
228
+ exit: ({ node, state }) => {
229
+ if (isInsideTableCell(node) || (node.depthMap[28] || 0) > 1) return "</tr>";
230
+ if (!state.tableRenderedTable) {
231
+ state.tableRenderedTable = true;
232
+ const alignments = state.tableColumnAlignments;
233
+ while (alignments.length < state.tableCurrentRowCells) alignments.push("");
234
+ return ` |\n| ${alignments.map((align) => {
235
+ switch (align) {
236
+ case "left": return ":---";
237
+ case "center": return ":---:";
238
+ case "right": return "---:";
239
+ default: return "---";
240
+ }
241
+ }).join(" | ")} |`;
242
+ }
243
+ return " |";
244
+ },
245
+ excludesTextNodes: true,
246
+ spacing: TABLE_ROW_SPACING
247
+ },
248
+ [31]: {
249
+ enter: ({ node, state }) => {
250
+ if ((node.depthMap[28] || 0) > 1) return "<th>";
251
+ const align = node.attributes?.align?.toLowerCase();
252
+ if (align) state.tableColumnAlignments.push(align);
253
+ else if (state.tableColumnAlignments.length <= state.tableCurrentRowCells) state.tableColumnAlignments.push("");
254
+ return node.index === 0 ? "" : " | ";
255
+ },
256
+ exit: ({ node, state }) => {
257
+ if ((node.depthMap[28] || 0) > 1) return "</th>";
258
+ state.tableCurrentRowCells++;
259
+ },
260
+ collapsesInnerWhiteSpace: true,
261
+ spacing: NO_SPACING
262
+ },
263
+ [32]: {
264
+ enter: ({ node }) => {
265
+ if ((node.depthMap[28] || 0) > 1) return "<td>";
266
+ return node.index === 0 ? "" : " | ";
267
+ },
268
+ exit: ({ node, state }) => {
269
+ if ((node.depthMap[28] || 0) > 1) return "</td>";
270
+ state.tableCurrentRowCells++;
271
+ },
272
+ collapsesInnerWhiteSpace: true,
273
+ spacing: NO_SPACING
274
+ },
275
+ [35]: { enter: ({ node, state }) => {
276
+ const bqDepth = node.depthMap[22] || 0;
277
+ if (bqDepth > 0) {
278
+ const lastEntry = state.buffer.at(-1);
279
+ const lastChar = lastEntry?.charAt(lastEntry.length - 1) || "";
280
+ if (lastChar && lastChar !== "\n" && lastChar !== " " && lastChar !== ">") {
281
+ const prefix = "> ".repeat(bqDepth);
282
+ return `\n${prefix.trimEnd()}\n${prefix}`;
283
+ }
284
+ }
285
+ } },
286
+ [36]: {},
287
+ [37]: {
288
+ collapsesInnerWhiteSpace: true,
289
+ spacing: NO_SPACING,
290
+ isInline: true
291
+ },
292
+ [41]: {},
293
+ [42]: {
294
+ collapsesInnerWhiteSpace: true,
295
+ spacing: NO_SPACING,
296
+ isInline: true
297
+ },
298
+ [43]: {
299
+ collapsesInnerWhiteSpace: true,
300
+ isInline: true
301
+ },
302
+ [44]: { spacing: NO_SPACING },
303
+ [45]: {
304
+ enter: ({ node }) => {
305
+ if ((node.depthMap[28] || 0) > 1) return "<center>";
306
+ },
307
+ exit: ({ node }) => {
308
+ if ((node.depthMap[28] || 0) > 1) return "</center>";
309
+ },
310
+ spacing: NO_SPACING
311
+ },
312
+ [38]: {
313
+ spacing: NO_SPACING,
314
+ excludesTextNodes: true
315
+ },
316
+ [39]: {
317
+ spacing: TABLE_ROW_SPACING,
318
+ excludesTextNodes: true
319
+ },
320
+ [46]: {
321
+ enter: () => "`",
322
+ exit: () => "`",
323
+ collapsesInnerWhiteSpace: true,
324
+ spacing: NO_SPACING,
325
+ isInline: true
326
+ },
327
+ [47]: { spacing: NO_SPACING },
328
+ [40]: { spacing: NO_SPACING },
329
+ [54]: {
330
+ isSelfClosing: true,
331
+ spacing: NO_SPACING,
332
+ collapsesInnerWhiteSpace: true,
333
+ isInline: true
334
+ },
335
+ [55]: {
336
+ isSelfClosing: true,
337
+ spacing: NO_SPACING,
338
+ isInline: true,
339
+ collapsesInnerWhiteSpace: true
340
+ },
341
+ [56]: {
342
+ isSelfClosing: true,
343
+ spacing: NO_SPACING,
344
+ isInline: true,
345
+ collapsesInnerWhiteSpace: true
346
+ },
347
+ [57]: {
348
+ isSelfClosing: true,
349
+ spacing: NO_SPACING
350
+ },
351
+ [58]: {
352
+ isSelfClosing: true,
353
+ spacing: NO_SPACING
354
+ },
355
+ [59]: {
356
+ isSelfClosing: true,
357
+ spacing: NO_SPACING,
358
+ isInline: true,
359
+ collapsesInnerWhiteSpace: true
360
+ },
361
+ [60]: {
362
+ isSelfClosing: true,
363
+ spacing: NO_SPACING,
364
+ isInline: true,
365
+ collapsesInnerWhiteSpace: true
366
+ },
367
+ [61]: {
368
+ isSelfClosing: true,
369
+ spacing: NO_SPACING
370
+ },
371
+ [62]: {
372
+ isSelfClosing: true,
373
+ spacing: NO_SPACING
374
+ },
375
+ [63]: {
376
+ isSelfClosing: true,
377
+ spacing: NO_SPACING
378
+ },
379
+ [64]: {
380
+ isSelfClosing: true,
381
+ spacing: NO_SPACING,
382
+ isInline: true,
383
+ collapsesInnerWhiteSpace: true
384
+ },
385
+ [49]: { spacing: NO_SPACING },
386
+ [65]: { spacing: NO_SPACING },
387
+ [66]: {
388
+ isNonNesting: true,
389
+ spacing: NO_SPACING
390
+ },
391
+ [67]: {
392
+ isNonNesting: true,
393
+ spacing: NO_SPACING
394
+ },
395
+ [68]: { spacing: NO_SPACING },
396
+ [69]: { spacing: NO_SPACING },
397
+ [70]: { spacing: NO_SPACING },
398
+ [71]: { spacing: NO_SPACING },
399
+ [72]: { spacing: NO_SPACING },
400
+ [73]: {
401
+ isNonNesting: true,
402
+ spacing: NO_SPACING
403
+ },
404
+ [74]: { spacing: NO_SPACING },
405
+ [75]: { spacing: NO_SPACING },
406
+ [76]: { spacing: NO_SPACING },
407
+ [77]: { spacing: NO_SPACING },
408
+ [78]: { spacing: NO_SPACING },
409
+ [79]: {
410
+ enter: () => "",
411
+ exit: () => "",
412
+ collapsesInnerWhiteSpace: true,
413
+ spacing: NO_SPACING,
414
+ isInline: true
415
+ },
416
+ [80]: {
417
+ enter: () => "<mark>",
418
+ exit: () => "</mark>",
419
+ collapsesInnerWhiteSpace: true,
420
+ spacing: NO_SPACING,
421
+ isInline: true
422
+ },
423
+ [81]: {
424
+ enter: () => "\"",
425
+ exit: () => "\"",
426
+ collapsesInnerWhiteSpace: true,
427
+ spacing: NO_SPACING,
428
+ isInline: true
429
+ },
430
+ [82]: {
431
+ enter: () => "`",
432
+ exit: () => "`",
433
+ collapsesInnerWhiteSpace: true,
434
+ spacing: NO_SPACING,
435
+ isInline: true
436
+ },
437
+ [83]: {
438
+ enter: () => "",
439
+ exit: () => "",
440
+ collapsesInnerWhiteSpace: true,
441
+ spacing: NO_SPACING,
442
+ isInline: true
443
+ },
444
+ [84]: {
445
+ excludesTextNodes: true,
446
+ spacing: NO_SPACING
447
+ },
448
+ [85]: {
449
+ isNonNesting: true,
450
+ spacing: NO_SPACING
451
+ },
452
+ [86]: {
453
+ isNonNesting: true,
454
+ spacing: NO_SPACING
455
+ },
456
+ [87]: {
457
+ isNonNesting: true,
458
+ spacing: NO_SPACING
459
+ },
460
+ [88]: { spacing: NO_SPACING },
461
+ [89]: {
462
+ enter: () => {
463
+ return "<u>";
464
+ },
465
+ exit: () => {
466
+ return "</u>";
467
+ },
468
+ collapsesInnerWhiteSpace: true,
469
+ spacing: NO_SPACING,
470
+ isInline: true
471
+ },
472
+ [90]: {
473
+ enter: () => "*",
474
+ exit: () => "*",
475
+ collapsesInnerWhiteSpace: true,
476
+ spacing: NO_SPACING,
477
+ isInline: true
478
+ },
479
+ [91]: {
480
+ enter: () => "**",
481
+ exit: () => "**",
482
+ collapsesInnerWhiteSpace: true,
483
+ spacing: NO_SPACING,
484
+ isInline: true
485
+ },
486
+ [92]: {
487
+ enter: () => "`",
488
+ exit: () => "`",
489
+ collapsesInnerWhiteSpace: true,
490
+ spacing: NO_SPACING,
491
+ isInline: true
492
+ },
493
+ [93]: {
494
+ enter: () => "",
495
+ exit: () => "",
496
+ collapsesInnerWhiteSpace: true,
497
+ spacing: NO_SPACING,
498
+ isInline: true
499
+ },
500
+ [94]: {
501
+ enter: () => "",
502
+ exit: () => "",
503
+ collapsesInnerWhiteSpace: true,
504
+ spacing: NO_SPACING,
505
+ isInline: true
506
+ },
507
+ [95]: {
508
+ enter: () => "",
509
+ exit: () => "",
510
+ collapsesInnerWhiteSpace: true,
511
+ spacing: NO_SPACING,
512
+ isInline: true
513
+ },
514
+ [96]: {
515
+ enter: () => "",
516
+ exit: () => "",
517
+ collapsesInnerWhiteSpace: true,
518
+ spacing: NO_SPACING,
519
+ isInline: true
520
+ },
521
+ [97]: {
522
+ enter: () => "",
523
+ exit: () => "",
524
+ collapsesInnerWhiteSpace: true,
525
+ spacing: NO_SPACING,
526
+ isInline: true
527
+ },
528
+ [100]: {
529
+ enter: () => "<address>",
530
+ exit: () => "</address>",
531
+ spacing: NO_SPACING,
532
+ collapsesInnerWhiteSpace: true
533
+ },
534
+ [101]: {
535
+ spacing: NO_SPACING,
536
+ enter: () => "<dl>",
537
+ exit: () => "</dl>"
538
+ },
539
+ [99]: {
540
+ enter: () => "<dt>",
541
+ exit: () => "</dt>",
542
+ collapsesInnerWhiteSpace: true,
543
+ spacing: [0, 1]
544
+ },
545
+ [98]: {
546
+ enter: () => "<dd>",
547
+ exit: () => "</dd>",
548
+ spacing: [0, 1]
549
+ },
550
+ [50]: {},
551
+ [51]: {},
552
+ [105]: {},
553
+ [104]: {},
554
+ [102]: {},
555
+ [106]: {
556
+ enter: () => "_",
557
+ exit: () => "_",
558
+ collapsesInnerWhiteSpace: true,
559
+ spacing: NO_SPACING,
560
+ isInline: true
561
+ }
562
+ };
563
+ /**
564
+ * Build a map of tag name → TagHandler from declarative tagOverrides config.
565
+ * For alias (string value): clone the handler for the aliased tag.
566
+ * For override object: overlay fields onto the base handler (if tag is known).
567
+ */
568
+ function buildTagOverrideHandlers(overrides) {
569
+ const result = /* @__PURE__ */ new Map();
570
+ for (const tagName in overrides) {
571
+ const override = overrides[tagName];
572
+ if (!override) continue;
573
+ if (typeof override === "string") {
574
+ const targetId = TagIdMap[override];
575
+ if (targetId !== void 0) {
576
+ const baseHandler = tagHandlers[targetId];
577
+ if (baseHandler) result.set(tagName, { ...baseHandler });
578
+ }
579
+ } else {
580
+ const baseId = TagIdMap[tagName];
581
+ const baseHandler = baseId !== void 0 ? tagHandlers[baseId] : void 0;
582
+ const handler = baseHandler ? { ...baseHandler } : {};
583
+ if (override.enter !== void 0) {
584
+ const enterStr = override.enter;
585
+ handler.enter = () => enterStr;
586
+ }
587
+ if (override.exit !== void 0) {
588
+ const exitStr = override.exit;
589
+ handler.exit = () => exitStr;
590
+ }
591
+ if (override.spacing !== void 0) handler.spacing = override.spacing;
592
+ if (override.isInline !== void 0) handler.isInline = override.isInline;
593
+ if (override.isSelfClosing !== void 0) handler.isSelfClosing = override.isSelfClosing;
594
+ if (override.collapsesInnerWhiteSpace !== void 0) handler.collapsesInnerWhiteSpace = override.collapsesInnerWhiteSpace;
595
+ result.set(tagName, handler);
596
+ }
597
+ }
598
+ return result;
599
+ }
600
+ //#endregion
601
+ //#region src/utils.ts
602
+ /**
603
+ * Decode HTML entities - optimized version with single pass
604
+ */
605
+ function decodeHTMLEntities(text) {
606
+ let result = "";
607
+ let i = 0;
608
+ while (i < text.length) {
609
+ if (text[i] === "&") {
610
+ let match = false;
611
+ for (const [entity, replacement] of Object.entries(HTML_ENTITIES)) if (text.startsWith(entity, i)) {
612
+ result += replacement;
613
+ i += entity.length;
614
+ match = true;
615
+ break;
616
+ }
617
+ if (match) continue;
618
+ if (i + 2 < text.length && text[i + 1] === "#") {
619
+ const start = i;
620
+ i += 2;
621
+ const isHex = text[i] === "x" || text[i] === "X";
622
+ if (isHex) i++;
623
+ const numStart = i;
624
+ while (i < text.length && text[i] !== ";") i++;
625
+ if (i < text.length && text[i] === ";") {
626
+ const numStr = text.substring(numStart, i);
627
+ const base = isHex ? 16 : 10;
628
+ try {
629
+ const codePoint = Number.parseInt(numStr, base);
630
+ if (!Number.isNaN(codePoint)) {
631
+ result += String.fromCodePoint(codePoint);
632
+ i++;
633
+ continue;
634
+ }
635
+ } catch {}
636
+ }
637
+ i = start;
638
+ }
639
+ }
640
+ result += text[i];
641
+ i++;
642
+ }
643
+ return result;
644
+ }
645
+ function traverseUpToFirstBlockNode(node) {
646
+ let firstBlockParent = node;
647
+ const parentsToIncrement = [firstBlockParent];
648
+ while (firstBlockParent.tagHandler?.isInline) {
649
+ if (!firstBlockParent.parent) break;
650
+ firstBlockParent = firstBlockParent.parent;
651
+ parentsToIncrement.push(firstBlockParent);
652
+ }
653
+ return parentsToIncrement;
654
+ }
655
+ //#endregion
656
+ //#region src/parse.ts
657
+ const LT_CHAR = 60;
658
+ const GT_CHAR = 62;
659
+ const SLASH_CHAR = 47;
660
+ const EQUALS_CHAR = 61;
661
+ const QUOTE_CHAR = 34;
662
+ const APOS_CHAR = 39;
663
+ const EXCLAMATION_CHAR = 33;
664
+ const AMPERSAND_CHAR = 38;
665
+ const BACKSLASH_CHAR = 92;
666
+ const DASH_CHAR = 45;
667
+ const SPACE_CHAR = 32;
668
+ const TAB_CHAR = 9;
669
+ const NEWLINE_CHAR = 10;
670
+ const CARRIAGE_RETURN_CHAR = 13;
671
+ const BACKTICK_CHAR = 96;
672
+ const PIPE_CHAR = 124;
673
+ const OPEN_BRACKET_CHAR = 91;
674
+ const CLOSE_BRACKET_CHAR = 93;
675
+ const EMPTY_ATTRIBUTES = Object.freeze({});
676
+ function copyDepthMap(depthMap) {
677
+ return new Uint8Array(depthMap);
678
+ }
679
+ /**
680
+ * Fast whitespace check using direct character code comparison
681
+ */
682
+ function isWhitespace(charCode) {
683
+ return charCode === SPACE_CHAR || charCode === TAB_CHAR || charCode === NEWLINE_CHAR || charCode === CARRIAGE_RETURN_CHAR;
684
+ }
685
+ /**
686
+ * Pure HTML parser that emits DOM events
687
+ * Completely decoupled from markdown generation
688
+ */
689
+ function parseHtml(html, options = {}) {
690
+ const events = [];
691
+ return {
692
+ events,
693
+ remainingHtml: parseHtmlInternal(html, {
694
+ depthMap: new Uint8Array(108),
695
+ depth: 0,
696
+ resolvedPlugins: options.resolvedPlugins || []
697
+ }, (event) => {
698
+ events.push(event);
699
+ })
700
+ };
701
+ }
702
+ /**
703
+ * Streaming HTML parser - calls onEvent for each DOM event
704
+ */
705
+ function parseHtmlStream(html, state, onEvent) {
706
+ return parseHtmlInternal(html, state, onEvent);
707
+ }
708
+ /**
709
+ * Internal parsing function - extracted from original parseHTML
710
+ */
711
+ function parseHtmlInternal(htmlChunk, state, handleEvent) {
712
+ let textBuffer = "";
713
+ state.depthMap ??= new Uint8Array(108);
714
+ state.depth ??= 0;
715
+ state.lastCharWasWhitespace ??= true;
716
+ state.justClosedTag ??= false;
717
+ state.isFirstTextInElement ??= false;
718
+ state.lastCharWasBackslash ??= false;
719
+ let i = 0;
720
+ const chunkLength = htmlChunk.length;
721
+ while (i < chunkLength) {
722
+ const currentCharCode = htmlChunk.charCodeAt(i);
723
+ if (currentCharCode !== LT_CHAR) {
724
+ if (currentCharCode === AMPERSAND_CHAR) state.hasEncodedHtmlEntity = true;
725
+ if (isWhitespace(currentCharCode)) {
726
+ const inPreTag = (state.depthMap[34] || 0) > 0;
727
+ if (state.justClosedTag) {
728
+ state.justClosedTag = false;
729
+ state.lastCharWasWhitespace = false;
730
+ }
731
+ if (!inPreTag && state.lastCharWasWhitespace) {
732
+ i++;
733
+ continue;
734
+ }
735
+ if (inPreTag) textBuffer += htmlChunk[i];
736
+ else if (currentCharCode === SPACE_CHAR || !state.lastCharWasWhitespace) textBuffer += " ";
737
+ state.lastCharWasWhitespace = true;
738
+ state.textBufferContainsWhitespace = true;
739
+ state.lastCharWasBackslash = false;
740
+ } else {
741
+ state.textBufferContainsNonWhitespace = true;
742
+ state.lastCharWasWhitespace = false;
743
+ state.justClosedTag = false;
744
+ if (currentCharCode === PIPE_CHAR && state.depthMap[28]) textBuffer += "\\|";
745
+ else if (currentCharCode === BACKTICK_CHAR && (state.depthMap[23] || state.depthMap[34])) textBuffer += "\\`";
746
+ else if (currentCharCode === OPEN_BRACKET_CHAR && state.depthMap[26]) textBuffer += "\\[";
747
+ else if (currentCharCode === CLOSE_BRACKET_CHAR && state.depthMap[26]) textBuffer += "\\]";
748
+ else if (currentCharCode === GT_CHAR && state.depthMap[22]) textBuffer += "\\>";
749
+ else textBuffer += htmlChunk[i];
750
+ if (state.currentNode?.tagHandler?.isNonNesting) {
751
+ if (!state.lastCharWasBackslash) {
752
+ if (currentCharCode === APOS_CHAR && !state.inDoubleQuote && !state.inBacktick) state.inSingleQuote = !state.inSingleQuote;
753
+ else if (currentCharCode === QUOTE_CHAR && !state.inSingleQuote && !state.inBacktick) state.inDoubleQuote = !state.inDoubleQuote;
754
+ else if (currentCharCode === BACKTICK_CHAR && !state.inSingleQuote && !state.inDoubleQuote) state.inBacktick = !state.inBacktick;
755
+ }
756
+ }
757
+ state.lastCharWasBackslash = currentCharCode === BACKSLASH_CHAR && !state.lastCharWasBackslash;
758
+ }
759
+ i++;
760
+ continue;
761
+ }
762
+ if (i + 1 >= chunkLength) {
763
+ textBuffer += htmlChunk[i];
764
+ break;
765
+ }
766
+ const nextCharCode = htmlChunk.charCodeAt(i + 1);
767
+ if (nextCharCode === EXCLAMATION_CHAR) {
768
+ if (textBuffer.length > 0) {
769
+ processTextBuffer(textBuffer, state, handleEvent);
770
+ textBuffer = "";
771
+ }
772
+ const result = processCommentOrDoctype(htmlChunk, i);
773
+ if (result.complete) i = result.newPosition;
774
+ else {
775
+ textBuffer += result.remainingText;
776
+ break;
777
+ }
778
+ } else if (nextCharCode === SLASH_CHAR) {
779
+ const inQuotes = state.inSingleQuote || state.inDoubleQuote || state.inBacktick;
780
+ if (state.currentNode?.tagHandler?.isNonNesting && inQuotes) {
781
+ textBuffer += htmlChunk[i];
782
+ i++;
783
+ continue;
784
+ }
785
+ if (textBuffer.length > 0) {
786
+ processTextBuffer(textBuffer, state, handleEvent);
787
+ textBuffer = "";
788
+ }
789
+ const result = processClosingTag(htmlChunk, i, state, handleEvent);
790
+ if (result.complete) i = result.newPosition;
791
+ else {
792
+ textBuffer += result.remainingText;
793
+ break;
794
+ }
795
+ } else {
796
+ let i2 = i + 1;
797
+ const tagNameStart = i2;
798
+ let tagNameEnd = -1;
799
+ while (i2 < chunkLength) {
800
+ const c = htmlChunk.charCodeAt(i2);
801
+ if (isWhitespace(c) || c === SLASH_CHAR || c === GT_CHAR) {
802
+ tagNameEnd = i2;
803
+ break;
804
+ }
805
+ i2++;
806
+ }
807
+ if (tagNameEnd === -1) {
808
+ textBuffer += htmlChunk.substring(i);
809
+ break;
810
+ }
811
+ const tagName = htmlChunk.substring(tagNameStart, tagNameEnd).toLowerCase();
812
+ if (!tagName) {
813
+ i = tagNameEnd;
814
+ break;
815
+ }
816
+ const tagId = TagIdMap[tagName] ?? -1;
817
+ i2 = tagNameEnd;
818
+ if (state.currentNode?.tagHandler?.isNonNesting) {
819
+ if (tagId !== state.currentNode?.tagId) {
820
+ textBuffer += htmlChunk[i++];
821
+ continue;
822
+ }
823
+ }
824
+ if (textBuffer.length > 0) {
825
+ processTextBuffer(textBuffer, state, handleEvent);
826
+ textBuffer = "";
827
+ }
828
+ const result = processOpeningTag(tagName, tagId, htmlChunk, i2, state, handleEvent);
829
+ if (result.skip) textBuffer += htmlChunk[i++];
830
+ else if (result.complete) {
831
+ i = result.newPosition;
832
+ if (!result.selfClosing) state.isFirstTextInElement = true;
833
+ } else {
834
+ textBuffer += result.remainingText;
835
+ break;
836
+ }
837
+ }
838
+ }
839
+ return textBuffer;
840
+ }
841
+ /**
842
+ * Process accumulated text buffer and create text node event
843
+ */
844
+ function processTextBuffer(textBuffer, state, handleEvent) {
845
+ const containsNonWhitespace = state.textBufferContainsNonWhitespace;
846
+ const containsWhitespace = state.textBufferContainsWhitespace;
847
+ state.textBufferContainsNonWhitespace = false;
848
+ state.textBufferContainsWhitespace = false;
849
+ if (!state.currentNode) return;
850
+ const excludesTextNodes = state.currentNode?.tagHandler?.excludesTextNodes;
851
+ const inPreTag = (state.depthMap[34] || 0) > 0;
852
+ if (!inPreTag && !containsNonWhitespace && !state.currentNode.childTextNodeIndex) return;
853
+ let text = textBuffer;
854
+ if (text.length === 0) return;
855
+ const parentsToIncrement = traverseUpToFirstBlockNode(state.currentNode);
856
+ const firstBlockParent = parentsToIncrement.at(-1);
857
+ if (containsWhitespace && !firstBlockParent?.childTextNodeIndex) {
858
+ let start = 0;
859
+ while (start < text.length && (inPreTag ? text.charCodeAt(start) === NEWLINE_CHAR || text.charCodeAt(start) === CARRIAGE_RETURN_CHAR : isWhitespace(text.charCodeAt(start)))) start++;
860
+ if (start > 0) text = text.substring(start);
861
+ }
862
+ if (state.hasEncodedHtmlEntity) {
863
+ text = decodeHTMLEntities(String(text));
864
+ state.hasEncodedHtmlEntity = false;
865
+ }
866
+ const textNode = {
867
+ type: 2,
868
+ value: text,
869
+ parent: state.currentNode,
870
+ index: state.currentNode.currentWalkIndex++,
871
+ depth: state.depth,
872
+ containsWhitespace,
873
+ excludedFromMarkdown: excludesTextNodes
874
+ };
875
+ for (const parent of parentsToIncrement) parent.childTextNodeIndex = (parent.childTextNodeIndex || 0) + 1;
876
+ handleEvent({
877
+ type: 0,
878
+ node: textNode
879
+ });
880
+ state.lastTextNode = textNode;
881
+ }
882
+ /**
883
+ * Process HTML closing tag
884
+ */
885
+ function processClosingTag(htmlChunk, position, state, handleEvent) {
886
+ let i = position + 2;
887
+ const tagNameStart = i;
888
+ const chunkLength = htmlChunk.length;
889
+ let foundClose = false;
890
+ while (i < chunkLength) {
891
+ if (htmlChunk.charCodeAt(i) === GT_CHAR) {
892
+ foundClose = true;
893
+ break;
894
+ }
895
+ i++;
896
+ }
897
+ if (!foundClose) return {
898
+ complete: false,
899
+ newPosition: position,
900
+ remainingText: htmlChunk.substring(position)
901
+ };
902
+ const tagId = TagIdMap[htmlChunk.substring(tagNameStart, i).toLowerCase()] ?? -1;
903
+ if (state.currentNode?.tagHandler?.isNonNesting && tagId !== state.currentNode.tagId) return {
904
+ complete: false,
905
+ newPosition: position,
906
+ remainingText: htmlChunk.substring(position)
907
+ };
908
+ let curr = state.currentNode;
909
+ if (curr) {
910
+ let match = curr.tagId !== tagId;
911
+ while (curr && match) {
912
+ closeNode(curr, state, handleEvent);
913
+ curr = curr.parent;
914
+ match = curr?.tagId !== tagId;
915
+ }
916
+ }
917
+ if (curr) closeNode(curr, state, handleEvent);
918
+ state.justClosedTag = true;
919
+ return {
920
+ complete: true,
921
+ newPosition: i + 1,
922
+ remainingText: ""
923
+ };
924
+ }
925
+ /**
926
+ * Close a node and emit exit event
927
+ */
928
+ function closeNode(node, state, handleEvent) {
929
+ if (!node) return;
930
+ if (node.tagId === 26 && !node.childTextNodeIndex) {
931
+ const prefix = node.attributes?.title || node.attributes?.["aria-label"] || "";
932
+ if (prefix) {
933
+ node.childTextNodeIndex = 1;
934
+ handleEvent({
935
+ type: 0,
936
+ node: {
937
+ type: 2,
938
+ value: prefix,
939
+ parent: node,
940
+ index: 0,
941
+ depth: node.depth + 1
942
+ }
943
+ });
944
+ for (const parent of traverseUpToFirstBlockNode(node)) parent.childTextNodeIndex = (parent.childTextNodeIndex || 0) + 1;
945
+ }
946
+ }
947
+ if (node.tagId) state.depthMap[node.tagId] = Math.max(0, (state.depthMap[node.tagId] || 0) - 1);
948
+ if (node.tagHandler?.isNonNesting) {
949
+ state.inSingleQuote = false;
950
+ state.inDoubleQuote = false;
951
+ state.inBacktick = false;
952
+ state.lastCharWasBackslash = false;
953
+ }
954
+ state.depth--;
955
+ handleEvent({
956
+ type: 1,
957
+ node
958
+ });
959
+ state.currentNode = state.currentNode.parent;
960
+ state.hasEncodedHtmlEntity = false;
961
+ state.justClosedTag = true;
962
+ }
963
+ /**
964
+ * Process HTML comment or doctype
965
+ */
966
+ function processCommentOrDoctype(htmlChunk, position) {
967
+ let i = position;
968
+ const chunkLength = htmlChunk.length;
969
+ if (i + 3 < chunkLength && htmlChunk.charCodeAt(i + 2) === DASH_CHAR && htmlChunk.charCodeAt(i + 3) === DASH_CHAR) {
970
+ i += 4;
971
+ while (i < chunkLength - 2) {
972
+ if (htmlChunk.charCodeAt(i) === DASH_CHAR && htmlChunk.charCodeAt(i + 1) === DASH_CHAR && htmlChunk.charCodeAt(i + 2) === GT_CHAR) {
973
+ i += 3;
974
+ return {
975
+ complete: true,
976
+ newPosition: i,
977
+ remainingText: ""
978
+ };
979
+ }
980
+ i++;
981
+ }
982
+ return {
983
+ complete: false,
984
+ newPosition: position,
985
+ remainingText: htmlChunk.substring(position)
986
+ };
987
+ } else {
988
+ i += 2;
989
+ while (i < chunkLength) {
990
+ if (htmlChunk.charCodeAt(i) === GT_CHAR) {
991
+ i++;
992
+ return {
993
+ complete: true,
994
+ newPosition: i,
995
+ remainingText: ""
996
+ };
997
+ }
998
+ i++;
999
+ }
1000
+ return {
1001
+ complete: false,
1002
+ newPosition: i,
1003
+ remainingText: htmlChunk.substring(position, i)
1004
+ };
1005
+ }
1006
+ }
1007
+ /**
1008
+ * Process HTML opening tag
1009
+ */
1010
+ function processOpeningTag(tagName, tagId, htmlChunk, i, state, handleEvent) {
1011
+ if (state.currentNode?.tagHandler?.isNonNesting) closeNode(state.currentNode, state, handleEvent);
1012
+ const tagHandler = state.tagOverrideHandlers?.get(tagName) ?? tagHandlers[tagId];
1013
+ const result = processTagAttributes(htmlChunk, i, tagHandler);
1014
+ if (!result.complete) return {
1015
+ complete: false,
1016
+ newPosition: i,
1017
+ remainingText: `<${tagName}${result.attrBuffer}`,
1018
+ selfClosing: false
1019
+ };
1020
+ const currentTagCount = result.attributes && result.attributes.id ? void 0 : state.depthMap[tagId] || 0;
1021
+ state.depthMap[tagId] = (currentTagCount || 0) + 1;
1022
+ state.depth++;
1023
+ i = result.newPosition;
1024
+ if (state.currentNode) state.currentNode.currentWalkIndex = state.currentNode.currentWalkIndex || 0;
1025
+ const currentWalkIndex = state.currentNode ? state.currentNode.currentWalkIndex++ : 0;
1026
+ const tag = {
1027
+ type: 1,
1028
+ name: tagName,
1029
+ attributes: result.attributes,
1030
+ parent: state.currentNode,
1031
+ depthMap: copyDepthMap(state.depthMap),
1032
+ depth: state.depth,
1033
+ index: currentWalkIndex,
1034
+ tagId,
1035
+ tagHandler
1036
+ };
1037
+ state.lastTextNode = tag;
1038
+ handleEvent({
1039
+ type: 0,
1040
+ node: tag
1041
+ });
1042
+ const parentNode = tag;
1043
+ parentNode.currentWalkIndex = 0;
1044
+ state.currentNode = parentNode;
1045
+ state.hasEncodedHtmlEntity = false;
1046
+ if (tagHandler?.isNonNesting && !result.selfClosing) {
1047
+ state.inSingleQuote = false;
1048
+ state.inDoubleQuote = false;
1049
+ state.inBacktick = false;
1050
+ state.lastCharWasBackslash = false;
1051
+ }
1052
+ if (result.selfClosing) {
1053
+ closeNode(tag, state, handleEvent);
1054
+ state.justClosedTag = true;
1055
+ } else state.justClosedTag = false;
1056
+ return {
1057
+ complete: true,
1058
+ newPosition: i,
1059
+ remainingText: "",
1060
+ selfClosing: result.selfClosing
1061
+ };
1062
+ }
1063
+ /**
1064
+ * Extract and process HTML tag attributes
1065
+ */
1066
+ function processTagAttributes(htmlChunk, position, tagHandler) {
1067
+ let i = position;
1068
+ const chunkLength = htmlChunk.length;
1069
+ const selfClosing = tagHandler?.isSelfClosing || false;
1070
+ const attrStartPos = i;
1071
+ let insideQuote = false;
1072
+ let quoteChar = 0;
1073
+ let prevChar = 0;
1074
+ while (i < chunkLength) {
1075
+ const c = htmlChunk.charCodeAt(i);
1076
+ if (insideQuote) {
1077
+ if (c === quoteChar && prevChar !== BACKSLASH_CHAR) insideQuote = false;
1078
+ i++;
1079
+ continue;
1080
+ } else if (c === QUOTE_CHAR || c === APOS_CHAR) {
1081
+ insideQuote = true;
1082
+ quoteChar = c;
1083
+ } else if (c === SLASH_CHAR && i + 1 < chunkLength && htmlChunk.charCodeAt(i + 1) === GT_CHAR) {
1084
+ const attrStr = htmlChunk.substring(attrStartPos, i).trim();
1085
+ return {
1086
+ complete: true,
1087
+ newPosition: i + 2,
1088
+ attributes: parseAttributes(attrStr),
1089
+ selfClosing: true,
1090
+ attrBuffer: attrStr
1091
+ };
1092
+ } else if (c === GT_CHAR) {
1093
+ const attrStr = htmlChunk.substring(attrStartPos, i).trim();
1094
+ return {
1095
+ complete: true,
1096
+ newPosition: i + 1,
1097
+ attributes: parseAttributes(attrStr),
1098
+ selfClosing,
1099
+ attrBuffer: attrStr
1100
+ };
1101
+ }
1102
+ i++;
1103
+ prevChar = c;
1104
+ }
1105
+ return {
1106
+ complete: false,
1107
+ newPosition: i,
1108
+ attributes: EMPTY_ATTRIBUTES,
1109
+ selfClosing: false,
1110
+ attrBuffer: htmlChunk.substring(attrStartPos, i)
1111
+ };
1112
+ }
1113
+ /**
1114
+ * Parse HTML attributes string into key-value object
1115
+ */
1116
+ function parseAttributes(attrStr) {
1117
+ if (!attrStr) return EMPTY_ATTRIBUTES;
1118
+ const result = {};
1119
+ const len = attrStr.length;
1120
+ let i = 0;
1121
+ const WHITESPACE = 0;
1122
+ const NAME = 1;
1123
+ const AFTER_NAME = 2;
1124
+ const BEFORE_VALUE = 3;
1125
+ const QUOTED_VALUE = 4;
1126
+ const UNQUOTED_VALUE = 5;
1127
+ let state = WHITESPACE;
1128
+ let nameStart = 0;
1129
+ let nameEnd = 0;
1130
+ let valueStart = 0;
1131
+ let quoteChar = 0;
1132
+ let name = "";
1133
+ while (i < len) {
1134
+ const charCode = attrStr.charCodeAt(i);
1135
+ const isSpace = isWhitespace(charCode);
1136
+ switch (state) {
1137
+ case WHITESPACE:
1138
+ if (!isSpace) {
1139
+ state = NAME;
1140
+ nameStart = i;
1141
+ nameEnd = 0;
1142
+ }
1143
+ break;
1144
+ case NAME:
1145
+ if (charCode === EQUALS_CHAR || isSpace) {
1146
+ nameEnd = i;
1147
+ name = attrStr.substring(nameStart, nameEnd).toLowerCase();
1148
+ state = charCode === EQUALS_CHAR ? BEFORE_VALUE : AFTER_NAME;
1149
+ }
1150
+ break;
1151
+ case AFTER_NAME:
1152
+ if (charCode === EQUALS_CHAR) state = BEFORE_VALUE;
1153
+ else if (!isSpace) {
1154
+ result[name] = "";
1155
+ state = NAME;
1156
+ nameStart = i;
1157
+ nameEnd = 0;
1158
+ }
1159
+ break;
1160
+ case BEFORE_VALUE:
1161
+ if (charCode === QUOTE_CHAR || charCode === APOS_CHAR) {
1162
+ quoteChar = charCode;
1163
+ state = QUOTED_VALUE;
1164
+ valueStart = i + 1;
1165
+ } else if (!isSpace) {
1166
+ state = UNQUOTED_VALUE;
1167
+ valueStart = i;
1168
+ }
1169
+ break;
1170
+ case QUOTED_VALUE:
1171
+ if (charCode === BACKSLASH_CHAR && i + 1 < len) i++;
1172
+ else if (charCode === quoteChar) {
1173
+ const raw = attrStr.substring(valueStart, i);
1174
+ result[name] = raw.includes("&") ? decodeHTMLEntities(raw) : raw;
1175
+ state = WHITESPACE;
1176
+ }
1177
+ break;
1178
+ case UNQUOTED_VALUE:
1179
+ if (isSpace || charCode === GT_CHAR) {
1180
+ const raw = attrStr.substring(valueStart, i);
1181
+ result[name] = raw.includes("&") ? decodeHTMLEntities(raw) : raw;
1182
+ state = WHITESPACE;
1183
+ }
1184
+ break;
1185
+ }
1186
+ i++;
1187
+ }
1188
+ if (state === QUOTED_VALUE || state === UNQUOTED_VALUE) {
1189
+ if (name) {
1190
+ const raw = attrStr.substring(valueStart, i);
1191
+ result[name] = raw.includes("&") ? decodeHTMLEntities(raw) : raw;
1192
+ }
1193
+ } else if (state === NAME || state === AFTER_NAME || state === BEFORE_VALUE) {
1194
+ nameEnd = nameEnd || i;
1195
+ const currentName = attrStr.substring(nameStart, nameEnd).toLowerCase();
1196
+ if (currentName) result[currentName] = "";
1197
+ }
1198
+ return result;
1199
+ }
1200
+ //#endregion
1201
+ export { buildTagOverrideHandlers as i, parseHtml as n, parseHtmlStream as r, parseAttributes as t };