import type { Root, Element, ElementContent, Text } from 'hast' import type { Plugin } from 'unified' import { visit } from 'unist-util-visit' /** * A rehype plugin that transforms paragraphs containing a leading "Term:" into * definition lists. For example, a paragraph like "Term: Definition" will be * transformed into a
element with a
for the term and a
for the * definition. */ export const rehypeDefinition: Plugin<[], Root> = () => (tree: Root) => { visit(tree, 'element', (node: Element, index, parent) => { if (!parent || typeof index !== 'number') return if (node.tagName !== 'p') return const children = node.children let splitIndex = -1 let colonIndex = -1 for (let i = 0; i < children.length; i++) { const child = children[i] if (child.type === 'text') { const idx = child.value.indexOf(':') if (idx !== -1) { splitIndex = i colonIndex = idx break } } } if (splitIndex === -1) return const splitText = children[splitIndex] as Text const beforeColon = splitText.value.slice(0, colonIndex) const afterColon = splitText.value.slice(colonIndex + 1).replace(/^\s+/, '') const termChildren: ElementContent[] = [ ...children.slice(0, splitIndex), ...(beforeColon ? [{ type: 'text', value: beforeColon } as Text] : []), ] const definitionChildren: ElementContent[] = [ ...(afterColon ? [{ type: 'text', value: afterColon } as Text] : []), ...children.slice(splitIndex + 1), ] if (termChildren.length === 0 || definitionChildren.length === 0) return const dlNode: Element = { type: 'element', tagName: 'dl', properties: {}, children: [ { type: 'element', tagName: 'dt', properties: {}, children: termChildren, }, { type: 'element', tagName: 'dd', properties: {}, children: definitionChildren, }, ], } parent.children[index] = dlNode }) }