UNPKG

@truto/turndown-plugin-gfm

Version:

Enhanced Turndown plugin for GitHub Flavored Markdown with improved table conversion and performance.

212 lines (211 loc) 6.42 kB
var highlightRegExp = /highlight-(?:(?:text|source)-)?([a-z0-9]+)/; function highlightedCodeBlock(turndownService) { turndownService.addRule("highlightedCodeBlock", { filter: function(node) { var firstChild = node.firstChild; return node.nodeName === "DIV" && highlightRegExp.test(node.className) && firstChild && firstChild.nodeName === "PRE"; }, replacement: function(content, node, options) { var className = node.className || ""; var language = (className.match(highlightRegExp) || [null, ""])[1]; return "\n\n" + options.fence + language + "\n" + node.firstChild.textContent + "\n" + options.fence + "\n\n"; } }); } function strikethrough(turndownService) { turndownService.addRule("strikethrough", { filter: ["del", "s", "strike"], replacement: function(content) { return "~~" + content + "~~"; } }); } var rules = {}; function cleanCellContent(content) { if (!content) return " "; let cleaned = content.trim().replace(/\s+/g, " ").replace(/\|/g, "\\|").replace(/\\/g, "\\\\").replace(/\n+/g, " ").replace(/\r+/g, " "); if (!cleaned || cleaned.match(/^\s*$/)) { return " "; } if (cleaned.length < 3) { cleaned += " ".repeat(3 - cleaned.length); } return cleaned; } function cell(content, node, index) { if (index === null && node && node.parentNode) { index = Array.prototype.indexOf.call(node.parentNode.childNodes, node); } if (index === null) index = 0; var prefix = " "; if (index === 0) prefix = "| "; let cellContent = cleanCellContent(content); let colspan = 1; if (node && node.getAttribute) { colspan = parseInt(node.getAttribute("colspan") || "1", 10); if (isNaN(colspan) || colspan < 1) colspan = 1; } let result = prefix + cellContent + " |"; for (let i = 1; i < colspan; i++) { result += " |"; } return result; } function isHeadingRow(tr) { if (!tr || !tr.parentNode) return false; var parentNode = tr.parentNode; if (parentNode.nodeName === "THEAD") return true; if (parentNode.firstChild === tr && (parentNode.nodeName === "TABLE" || parentNode.nodeName === "TBODY")) { var cellNodes = Array.prototype.filter.call(tr.childNodes, function(n) { return n.nodeType === 1; }); if (cellNodes.length === 0) return false; return Array.prototype.every.call(cellNodes, function(n) { return n.nodeName === "TH"; }); } return false; } function getTableColCount(table) { if (!table || !table.rows) return 0; let maxCols = 0; for (let i = 0; i < table.rows.length; i++) { const row = table.rows[i]; if (!row || !row.childNodes) continue; let colCount = 0; for (let j = 0; j < row.childNodes.length; j++) { const cell2 = row.childNodes[j]; if (cell2.nodeType === 1 && (cell2.nodeName === "TD" || cell2.nodeName === "TH")) { const colspan = parseInt(cell2.getAttribute("colspan") || "1", 10); colCount += isNaN(colspan) ? 1 : Math.max(1, colspan); } } if (colCount > maxCols) maxCols = colCount; } return maxCols; } function shouldSkipTable(table) { if (!table) return true; if (!table.rows || table.rows.length === 0) return true; let contentCells = 0; let totalCells = 0; for (let i = 0; i < table.rows.length; i++) { const row = table.rows[i]; if (!row || !row.childNodes) continue; for (let j = 0; j < row.childNodes.length; j++) { const cell2 = row.childNodes[j]; if (cell2.nodeType === 1 && (cell2.nodeName === "TD" || cell2.nodeName === "TH")) { totalCells++; if (cell2.textContent && cell2.textContent.trim()) { contentCells++; } } } } if (totalCells === 0) return true; if (totalCells === 1 && contentCells === 0) return true; return false; } rules.tableCell = { filter: ["th", "td"], replacement: function(content, node) { return cell(content, node, null); } }; rules.tableRow = { filter: "tr", replacement: function(content, node) { if (!content || !content.trim()) return ""; var borderCells = ""; if (isHeadingRow(node)) { const table = node.closest("table"); if (table) { const colCount = getTableColCount(table); if (colCount > 0) { for (var i = 0; i < colCount; i++) { const prefix = i === 0 ? "| " : " "; borderCells += prefix + "--- |"; } } } } return "\n" + content + (borderCells ? "\n" + borderCells : ""); } }; rules.table = { filter: "table", replacement: function(content, node) { if (shouldSkipTable(node)) { return ""; } content = content.replace(/\n+/g, "\n").trim(); if (!content) return ""; const lines = content.split("\n").filter((line) => line.trim()); if (lines.length === 0) return ""; const hasHeaderSeparator = lines.length >= 2 && /\|\s*-+/.test(lines[1]); let result = lines.join("\n"); if (!hasHeaderSeparator && lines.length >= 1) { const firstLine = lines[0]; const colCount = (firstLine.match(/\|/g) || []).length - 1; if (colCount > 0) { let separator = "|"; for (let i = 0; i < colCount; i++) { separator += " --- |"; } const resultLines = [lines[0], separator, ...lines.slice(1)]; result = resultLines.join("\n"); } } return "\n\n" + result + "\n\n"; } }; rules.tableSection = { filter: ["thead", "tbody", "tfoot"], replacement: function(content) { return content; } }; rules.tableCaption = { filter: ["caption"], replacement: function() { return ""; } }; rules.tableColgroup = { filter: ["colgroup", "col"], replacement: function() { return ""; } }; function tables(turndownService) { for (var key in rules) { turndownService.addRule(key, rules[key]); } } function taskListItems(turndownService) { turndownService.addRule("taskListItems", { filter: function(node) { return node.type === "checkbox" && node.parentNode.nodeName === "LI"; }, replacement: function(content, node) { return (node.checked ? "[x]" : "[ ]") + " "; } }); } function gfm(turndownService) { turndownService.use([ highlightedCodeBlock, strikethrough, tables, taskListItems ]); } export { gfm as default, gfm, highlightedCodeBlock, strikethrough, tables, taskListItems }; //# sourceMappingURL=index.js.map