const MarkdownIt = require('markdown-it'); const markdownItAnchor = require('markdown-it-anchor'); const markdownItFootnote = require('markdown-it-footnote'); const markdownItAttrs = require('markdown-it-attrs'); const Prism = require('prismjs'); require('prismjs/components/prism-bash'); require('prismjs/components/prism-json'); require('prismjs/components/prism-yaml'); require('prismjs/components/prism-python'); require('prismjs/components/prism-go'); require('prismjs/components/prism-docker'); require('prismjs/components/prism-sql'); require('prismjs/components/prism-typescript'); require('prismjs/components/prism-markdown'); function slugifyHeading(s) { return encodeURIComponent(String(s).trim().toLowerCase().replace(/\s+/g, '-')); } function createMarkdownRenderer(pluginManager) { const md = new MarkdownIt({ html: true, linkify: true, typographer: true, highlight: function (str, lang) { if (lang && Prism.languages[lang]) { try { return `
${Prism.highlight(str, Prism.languages[lang], lang)}`;
} catch (__) {}
}
const escaped = md.utils.escapeHtml(str);
return `${escaped}`;
}
});
md.use(markdownItAnchor, {
slugify: slugifyHeading,
permalink: markdownItAnchor.permalink.linkInsideHeader({
symbol: '#',
placement: 'after',
ariaHidden: true
})
});
md.use(markdownItFootnote);
md.use(markdownItAttrs);
// Disable indented code blocks (4 spaces) so indented HTML elements don't get converted to
md.disable('code');
if (pluginManager) {
pluginManager.applyMarkdownHooks(md);
}
return md;
}
function extractToc(markdownContent) {
const headings = [];
const lines = markdownContent.split(/\r?\n/);
let inCodeBlock = false;
for (const line of lines) {
if (line.trim().startsWith('```')) {
inCodeBlock = !inCodeBlock;
continue;
}
if (inCodeBlock) continue;
const match = line.match(/^(#{1,6})\s+(.+)$/);
if (match) {
const level = match[1].length;
// Strip markdown links/formatting from heading text for TOC display
const title = match[2].replace(/\[([^\]]+)\]\([^)]+\)/g, '$1').replace(/[*_`]/g, '').trim();
const id = slugifyHeading(title);
headings.push({ level, title, id });
}
}
// Build hierarchical tree if needed or flat list with levels
return headings;
}
module.exports = {
createMarkdownRenderer,
extractToc,
slugifyHeading
};