56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
/** Shared Markdown parsing and depth-first traversal for documentation gates. */
|
|
|
|
import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
import { gfmFromMarkdown } from 'mdast-util-gfm'
|
|
import { gfm } from 'micromark-extension-gfm'
|
|
import type { Nodes } from 'mdast'
|
|
|
|
/** One authored Markdown line outside fenced code. */
|
|
export interface MarkdownProseLine {
|
|
/** 1-based source line number. */
|
|
index: number
|
|
/** Source text without normalization. */
|
|
raw: string
|
|
}
|
|
|
|
/** Parse GitHub-flavored Markdown with the repository's standard extensions. */
|
|
export function parseMarkdown(source: string): Nodes {
|
|
return fromMarkdown(source, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] })
|
|
}
|
|
|
|
/**
|
|
* Visit a Markdown tree depth-first; returning false prunes a node's children.
|
|
* @param node - current tree node.
|
|
* @param visitor - callback invoked before each node's children.
|
|
*/
|
|
export function visitMarkdown(node: Nodes, visitor: (node: Nodes) => boolean | void): void {
|
|
if (visitor(node) === false) return
|
|
if ('children' in node) {
|
|
for (const child of node.children) visitMarkdown(child, visitor)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return source lines outside backtick or tilde fences.
|
|
* @param source - Markdown source whose prose should be retained verbatim.
|
|
* @returns unfenced lines with their original 1-based locations.
|
|
*/
|
|
export function markdownProseLines(source: string): MarkdownProseLine[] {
|
|
let fence: { marker: '`' | '~'; length: number } | undefined
|
|
const kept: MarkdownProseLine[] = []
|
|
source.split('\n').forEach((raw, i) => {
|
|
const token = /^ {0,3}(`{3,}|~{3,})/.exec(raw)?.[1]
|
|
if (token !== undefined) {
|
|
const marker = token[0] as '`' | '~'
|
|
if (fence === undefined) {
|
|
fence = { marker, length: token.length }
|
|
} else if (marker === fence.marker && token.length >= fence.length) {
|
|
fence = undefined
|
|
}
|
|
return
|
|
}
|
|
if (fence === undefined) kept.push({ index: i + 1, raw })
|
|
})
|
|
return kept
|
|
}
|