( ownerDir: string, toc: TocEntry[], setTitle: (t: string) => void, setLead: (l: string) => void )
| 87 | } |
| 88 | |
| 89 | function transformTree( |
| 90 | ownerDir: string, |
| 91 | toc: TocEntry[], |
| 92 | setTitle: (t: string) => void, |
| 93 | setLead: (l: string) => void |
| 94 | ) { |
| 95 | return (tree: HastRoot) => { |
| 96 | pullTitleAndLead(tree, setTitle, setLead) |
| 97 | |
| 98 | // Pass 1 — leaf rewrites. Visits every element including descendants of |
| 99 | // tables, blockquotes and code blocks (pass 2 wraps those and skips, so we |
| 100 | // must rewrite hrefs / img srcs / inline-code class here first). |
| 101 | visit(tree, 'element', (node, _index, parent) => { |
| 102 | if ((node.tagName === 'h2' || node.tagName === 'h3') && getId(node)) { |
| 103 | toc.push({ |
| 104 | id: getId(node)!, |
| 105 | text: stripAnchor(node), |
| 106 | depth: node.tagName === 'h2' ? 2 : 3 |
| 107 | }) |
| 108 | } |
| 109 | if ( |
| 110 | node.tagName === 'code' && |
| 111 | parent?.type === 'element' && |
| 112 | parent.tagName !== 'pre' |
| 113 | ) { |
| 114 | addClass(node, 'inline') |
| 115 | } |
| 116 | if (node.tagName === 'a' && !hasClass(node, 'anchor')) { |
| 117 | // Skip the autolink-headings <a class="anchor">#</a> — it points at |
| 118 | // its own heading's #id and does not need our prose styling. |
| 119 | addClass(node, 'link') |
| 120 | rewriteHref(node, ownerDir) |
| 121 | } |
| 122 | if (node.tagName === 'img') { |
| 123 | rewriteImgSrc(node, ownerDir) |
| 124 | } |
| 125 | }) |
| 126 | |
| 127 | // Pass 2 — structural wraps. Each match replaces the node with a wrapper |
| 128 | // and returns 'skip' so the visitor doesn't recurse into the new wrapper |
| 129 | // (which would re-match infinitely). |
| 130 | visit(tree, 'element', (node, index, parent) => { |
| 131 | if (!parent || typeof index !== 'number') return |
| 132 | if (node.tagName === 'table') { |
| 133 | addClass(node, 'md') |
| 134 | parent.children[index] = { |
| 135 | type: 'element', |
| 136 | tagName: 'div', |
| 137 | properties: { className: ['tbl-wrap'] }, |
| 138 | children: [node] |
| 139 | } |
| 140 | return ['skip', index + 1] |
| 141 | } |
| 142 | if (node.tagName === 'blockquote') { |
| 143 | const callout = transformAlertBlockquote(node) |
| 144 | if (callout) { |
| 145 | parent.children[index] = callout |
| 146 | return ['skip', index + 1] |
no test coverage detected