|
674 | 674 | return id; |
675 | 675 | }); |
676 | 676 |
|
| 677 | + // Tables (GitHub-Flavored Markdown) — must run before lists/inline so cell |
| 678 | + // content isn't mangled by line-based transforms. |
| 679 | + const tables = []; |
| 680 | + const parseTableRow = (line) => { |
| 681 | + let s = line.trim(); |
| 682 | + if (s.startsWith("|")) s = s.slice(1); |
| 683 | + if (s.endsWith("|")) s = s.slice(0, -1); |
| 684 | + return s.split("|").map((c) => c.trim()); |
| 685 | + }; |
| 686 | + const renderInlineMd = (text) => { |
| 687 | + const codes = []; |
| 688 | + let out = text.replace(/`([^`]+)`/g, (_, c) => { |
| 689 | + const id = `__TBLCODE_${codes.length}__`; |
| 690 | + codes.push(`<code>${escapeHtml(c)}</code>`); |
| 691 | + return id; |
| 692 | + }); |
| 693 | + out = out.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>"); |
| 694 | + out = out.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"); |
| 695 | + out = out.replace(/\*(.+?)\*/g, "<em>$1</em>"); |
| 696 | + out = out.replace(/~~(.+?)~~/g, "<del>$1</del>"); |
| 697 | + out = out.replace( |
| 698 | + /\[([^\]]+)\]\(([^)]+)\)/g, |
| 699 | + '<a href="$2" target="_blank" rel="noopener">$1</a>', |
| 700 | + ); |
| 701 | + codes.forEach((c, i) => { |
| 702 | + out = out.replace(`__TBLCODE_${i}__`, c); |
| 703 | + }); |
| 704 | + return out; |
| 705 | + }; |
| 706 | + const sepRe = /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)+\|?\s*$/; |
| 707 | + const tblLines = html.split("\n"); |
| 708 | + const cleanedLines = []; |
| 709 | + let tli = 0; |
| 710 | + while (tli < tblLines.length) { |
| 711 | + const line = tblLines[tli]; |
| 712 | + const next = tblLines[tli + 1]; |
| 713 | + if (next !== undefined && line.includes("|") && sepRe.test(next)) { |
| 714 | + const headerCells = parseTableRow(line); |
| 715 | + const aligns = parseTableRow(next).map((c) => { |
| 716 | + const l = c.startsWith(":"); |
| 717 | + const r = c.endsWith(":"); |
| 718 | + if (l && r) return "center"; |
| 719 | + if (r) return "right"; |
| 720 | + if (l) return "left"; |
| 721 | + return null; |
| 722 | + }); |
| 723 | + const bodyRows = []; |
| 724 | + let bj = tli + 2; |
| 725 | + while ( |
| 726 | + bj < tblLines.length && |
| 727 | + tblLines[bj].includes("|") && |
| 728 | + tblLines[bj].trim() !== "" |
| 729 | + ) { |
| 730 | + bodyRows.push(parseTableRow(tblLines[bj])); |
| 731 | + bj++; |
| 732 | + } |
| 733 | + let tableHtml = '<table class="md-table"><thead><tr>'; |
| 734 | + headerCells.forEach((c, idx) => { |
| 735 | + const a = aligns[idx] ? ` style="text-align:${aligns[idx]}"` : ""; |
| 736 | + tableHtml += `<th${a}>${renderInlineMd(c)}</th>`; |
| 737 | + }); |
| 738 | + tableHtml += "</tr></thead><tbody>"; |
| 739 | + bodyRows.forEach((row) => { |
| 740 | + tableHtml += "<tr>"; |
| 741 | + for (let k = 0; k < headerCells.length; k++) { |
| 742 | + const c = row[k] !== undefined ? row[k] : ""; |
| 743 | + const a = aligns[k] ? ` style="text-align:${aligns[k]}"` : ""; |
| 744 | + tableHtml += `<td${a}>${renderInlineMd(c)}</td>`; |
| 745 | + } |
| 746 | + tableHtml += "</tr>"; |
| 747 | + }); |
| 748 | + tableHtml += "</tbody></table>"; |
| 749 | + |
| 750 | + const id = `__TABLE_${tables.length}__`; |
| 751 | + tables.push(tableHtml); |
| 752 | + cleanedLines.push(id); |
| 753 | + tli = bj; |
| 754 | + } else { |
| 755 | + cleanedLines.push(line); |
| 756 | + tli++; |
| 757 | + } |
| 758 | + } |
| 759 | + html = cleanedLines.join("\n"); |
| 760 | + |
677 | 761 | // Inline code (protect from other processing) |
678 | 762 | const inlineCodes = []; |
679 | 763 | html = html.replace(/`([^`]+)`/g, (_, code) => { |
|
728 | 812 | block.startsWith("<blockquote") || |
729 | 813 | block.startsWith("<hr") || |
730 | 814 | block.startsWith("__CODE_") || |
| 815 | + block.startsWith("__TABLE_") || |
731 | 816 | block.startsWith("<div") || |
732 | 817 | block.startsWith("<li") |
733 | 818 | ) { |
|
747 | 832 | html = html.replace(`__CODE_${i}__`, block); |
748 | 833 | }); |
749 | 834 |
|
| 835 | + // Restore tables |
| 836 | + tables.forEach((tbl, i) => { |
| 837 | + html = html.replace(`__TABLE_${i}__`, tbl); |
| 838 | + }); |
| 839 | + |
750 | 840 | return html; |
751 | 841 | } |
752 | 842 |
|
|
0 commit comments