import { remark } from 'remark'; import gfm from 'remark-gfm'; import type { Options as RemarkGithubOptions } from 'remark-github'; import github from 'remark-github'; import { regEx } from './regex.ts'; // Generic replacements/link-breakers export function sanitizeMarkdown(markdown: string): string { let res = markdown; // Put a zero width space after every # followed by a digit res = res.replace( regEx(/(?
\W)#(?\d)/gi), '$ #$', ); // Put a zero width space after every @ symbol to prevent unintended hyperlinking, // but leave URLs, code blocks (triple backticks), and inline code spans untouched res = res .split(regEx(/(? ```[\s\S]*?```|`[^`\n]*?`|https?:\/\/[^\s<]+)/gi)) .map((part) => part.startsWith('`') || regEx(/^https?:\/\//i).test(part) ? part : part.replace(regEx(/@/g), '@'), ) .join(''); res = res.replace(regEx(/(? [a-z]@)/gi), '$'); res = res.replace(regEx(/\/compare\/@/g), '/compare/@'); res = res.replace( regEx(/(?\(https:\/\/[^)]*?)\.\.\.@/g), '$...@', ); res = res.replace( regEx(/(?[\s(])#(?\d+)(? [)\s]?)/g), '$ #$$ ', ); // convert escaped backticks back to ` const backTickRe = regEx(/`(? [^/]*?)`/g); res = res.replace(backTickRe, '`$ `'); res = res.replace(regEx(/`#(? \d+)`/g), '`#$ `'); res = res.replace( regEx(/(? [^\n]\n)(? #.*)/g), '$ \n$ ', ); return res; } /** * * @param content content to process * @param options github options * @returns linkified content */ export async function linkify( content: string, options: RemarkGithubOptions, ): Promise { // https://github.com/syntax-tree/mdast-util-to-markdown#optionsbullet const output = await remark() .use({ settings: { bullet: '-' } }) .use(gfm) .use(github, { mentionStrong: false, ...options }) .process(content); return output.toString(); }