dependabot/npm_and_yarn/astro-5.16.4
copilot/add-i18n-support-for-english
dependabot/npm_and_yarn/astro-5.16.4
dependabot/npm_and_yarn/katex-0.16.27
dependabot/npm_and_yarn/prettier-3.7.4
dependabot/npm_and_yarn/tailwindcss-4.1.16
dependabot/npm_and_yarn/tailwindcss/vite-4.1.16
dependabot/npm_and_yarn/typescript-eslint-8.46.2
dependabot/npm_and_yarn/typescript-eslint/parser-8.46.2
development
main
| 1 | import { visit } from 'unist-util-visit' |
| 2 | |
| 3 | export default function remarkTOC() { |
| 4 | return function (tree, file) { |
| 5 | const headings = [] |
| 6 | let headingIndex = 0 |
| 7 | const usedSlugs = new Set() |
| 8 | |
| 9 | // Extract headings from AST |
| 10 | visit(tree, 'heading', (node) => { |
| 11 | const level = node.depth |
| 12 | |
| 13 | // Only process h1, h2, h3 |
| 14 | if (level > 3) return |
| 15 | |
| 16 | // Skip the first h1 |
| 17 | if (level === 1 && headingIndex === 0) { |
| 18 | headingIndex++ |
| 19 | return |
| 20 | } |
| 21 | |
| 22 | const text = extractTextContent(node) |
| 23 | if (!text) return |
| 24 | |
| 25 | // Generate unique slug from text |
| 26 | const slug = generateUniqueSlug(text, usedSlugs) |
| 27 | const id = slug |
| 28 | |
| 29 | if (!node.data) node.data = {} |
| 30 | if (!node.data.hProperties) node.data.hProperties = {} |
| 31 | node.data.hProperties.id = id |
| 32 | |
| 33 | headings.push({ |
| 34 | level, |
| 35 | text, |
| 36 | id, |
| 37 | index: headingIndex |
| 38 | }) |
| 39 | |
| 40 | headingIndex++ |
| 41 | }) |
| 42 | |
| 43 | // Store TOC data in file.data.astro.frontmatter |
| 44 | if (!file.data.astro) file.data.astro = {} |
| 45 | if (!file.data.astro.frontmatter) file.data.astro.frontmatter = {} |
| 46 | file.data.astro.frontmatter.toc = headings |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | function extractTextContent(node) { |
| 51 | let text = '' |
| 52 | |
| 53 | visit(node, 'text', (textNode) => { |
| 54 | text += textNode.value |
| 55 | }) |
| 56 | |
| 57 | return text.trim() |
| 58 | } |
| 59 | |
| 60 | // Generate a slug from text |
| 61 | function generateSlug(text) { |
| 62 | return ( |
| 63 | text |
| 64 | .toLowerCase() |
| 65 | // Keep Chinese characters, English letters, numbers, spaces and hyphens |
| 66 | .replace(/[^\u4e00-\u9fa5a-z0-9\s-]/g, '') |
| 67 | // Replace spaces with hyphens |
| 68 | .replace(/\s+/g, '-') |
| 69 | // Replace multiple hyphens with single hyphen |
| 70 | .replace(/-+/g, '-') |
| 71 | // Remove leading and trailing hyphens |
| 72 | .replace(/^-|-$/g, '') |
| 73 | ) |
| 74 | } |
| 75 | |
| 76 | // Generate a unique slug from text |
| 77 | function generateUniqueSlug(text, usedSlugs) { |
| 78 | let slug = generateSlug(text) |
| 79 | let counter = 1 |
| 80 | let uniqueSlug = slug |
| 81 | |
| 82 | while (usedSlugs.has(uniqueSlug)) { |
| 83 | uniqueSlug = `${slug}-${counter}` |
| 84 | counter++ |
| 85 | } |
| 86 | |
| 87 | usedSlugs.add(uniqueSlug) |
| 88 | return uniqueSlug |
| 89 | } |