import type { PluginWithOptions } from 'markdown-it'; import type { MarkdownHeader } from '../../../../shared'; import { resolveHeadersFromTokens, slugify as slugifyDefault, } from '../../utils'; import { createRenderHeaders } from './createRenderHeader'; import { createTocBlockRule } from './createTocBlockRule'; export type TocPluginOptions = { /** * The pattern serving as the TOC placeholder in your markdown. * * @default undefined */ pattern?: RegExp; /** * A custom slugify function. * * Should use the same slugify function with `markdown-it-anchor` to ensure the link is matched. * * @default undefined */ slugify?: (str: string) => string; /** * A function for formatting headers. * * @default undefined */ format?: (str: string) => string; /** * Heading level that going to be extracted to the `env`. Should be a subset of * the `markdown-it-anchor` `level` option to ensure the link exists. * * @default [2,3] */ level?: number[]; /** * HTML tag of container. * * @default 'nav' */ containerTag?: string; /** * The class for container. * * @default 'table-of-contents' */ containerClass?: string; /** * HTML tag of list. * * @default 'ul' */ listTag?: 'ul' | 'ol'; /** * The class for list. * * @default '' */ listClass?: string; /** * The class for the `
  • ` tag. * * @default '' */ itemClass?: string; /** * The tag for the link inside `
  • `. * * @default 'a' */ linkTag?: string; /** * The class for the link tag inside `
  • `. * * @default '' */ linkClass?: string; }; /** * Generate table of contents. * * Forked and modified from `markdown-it-toc-done-right`: * * - Allows `html_inline` tags in headings to support custom components. * - Allows custom tags for links. * - Code refactor and optimizations. * * @see https://github.com/nagaozen/markdown-it-toc-done-right */ export const tocPlugin: PluginWithOptions = ( parser, { pattern = /^\[\[toc\]\]$/i, slugify = slugifyDefault, format, level = [2, 3], containerTag = 'nav', containerClass = 'table-of-contents', listTag = 'ul', listClass = '', itemClass = '', linkTag = 'a', linkClass = '', }: TocPluginOptions = {}, ): void => { let headers: MarkdownHeader[]; // Push the rule to the end of the chain, and resolve headers from the parsed tokens. parser.core.ruler.push('resolveTocHeaders', (state) => { headers = resolveHeadersFromTokens(state.tokens, { level, allowHtml: true, escapeText: true, slugify, format, }); return true; }); // Add toc syntax as a block rule. parser.block.ruler.before( 'heading', 'toc', createTocBlockRule({ pattern, containerTag, containerClass, }), { alt: ['paragraph', 'reference', 'blockquote'], }, ); const renderHeaders = createRenderHeaders({ listTag, listClass, itemClass, linkTag, linkClass, }); // Custom toc_body render rule. parser.renderer.rules.toc_body = () => { /* istanbul ignore if */ if (!headers) { return ''; } return renderHeaders(headers); }; };