` path treats element-rich `` blocks as inline
* text and escapes `<`/`>`/`*`/etc, producing very noisy markdown
* that no longer round-trips as code. The textContent of any
* well-formed token-highlighted block IS the original code.
*
* Applied AFTER JSDOM parse but BEFORE Readability / selector extraction
* so both paths benefit. Both passes are no-ops on HTML that doesn't
* exhibit the pattern, so the cost on simple inputs is one DOM walk.
*/
function preprocessDom(doc: Document): void {
// (1) Flatten
inside table cells.
for (const br of Array.from(doc.querySelectorAll("td br, th br"))) {
br.replaceWith(doc.createTextNode(" "));
}
// (2) Collapse element-rich blocks to textContent
// so Turndown's fenced-code-block rule applies. Pure-text and
// canonical ...
wrappers are left alone;
// Turndown handles both correctly (the latter even with nested
// syntax-highlighting spans inside the , since the rule keys
// on textContent). The bug case is element-rich WITHOUT a
// wrapper (per-token s as direct pre children), which
// Turndown otherwise treats as inline text and escapes every <, >,
// *, etc.
for (const pre of Array.from(doc.querySelectorAll("pre"))) {
if (pre.children.length === 0) continue;
if (pre.children.length === 1 && pre.children[0]?.nodeName === "CODE") continue;
const text = pre.textContent ?? "";
while (pre.firstChild) pre.removeChild(pre.firstChild);
const code = doc.createElement("code");
code.appendChild(doc.createTextNode(text));
pre.appendChild(code);
}
}
function postprocess(text: string, maxChars: number): string {
let out = text
.split("\n")
.map((line) => line.replace(/[ \t]+$/, ""))
.join("\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
if (maxChars > 0 && out.length > maxChars) {
out = `${out.slice(0, maxChars)}\n\n... [truncated from ${text.length} to ${maxChars} chars]`;
}
return out;
}