// Based on https://spec.commonmark.org/0.30/#info-string // Parse out info strings on fenced code blocks, example: // ```javascript lineNumbers:left copy:all annotate // becomes... // node.lang = javascript // node.meta = { lineNumbers: 'left', copy: 'all', annotate: true } // Also parse equals signs, where id=some-id becomes { id: 'some-id' } import { visit } from 'unist-util-visit' interface CodeNode { type: 'code' lang?: string meta?: string | Record value: string } // Note: Using 'any' for node because unist-util-visit's type constraints // don't easily allow for proper code node typing without complex generics const matcher = (node: any): node is CodeNode => node.type === 'code' && node.lang export default function parseInfoString() { // Note: Using 'any' for tree because unified's AST types are complex and // this function works with different tree types depending on the processor return (tree: any) => { visit(tree, matcher, (node: CodeNode) => { node.meta = strToObj(node.meta as string) // Temporary, remove {:copy} to avoid highlight parse error in translations. if (node.lang) { node.lang = node.lang.replace('{:copy}', '') } }) } } function strToObj(str?: string): Record { if (!str) return {} return Object.fromEntries( str .split(/\s+/g) .map((k: string) => k.split(/[:=]/)) // split by colon or equals sign .map(([k, ...v]: string[]) => [k, v.length ? v.join(':') : true]), ) }