import type { PluginWithOptions } from 'markdown-it' export interface MarkdownItGitHubAlertsOptions { /** * List of markers to match. * @default ['TIP', 'NOTE', 'IMPORTANT', 'WARNING', 'CAUTION'] */ markers?: string[] | '*' /** * If markers case sensitively on matching. * @default false */ matchCaseSensitive?: boolean /** * Custom icons for each marker. The key is the marker name, and the value is the html script represent the icon. * The key is always lowercase. * * @default inline svg icons from GitHub */ icons?: Record /** * Custom titles for each marker. The key is the marker name, and the value is the title. * The key is always lowercase. * * When the title is not specified, the default title is the capitalized marker name. */ titles?: Record /** * Prefix for the class names. * * @default 'markdown-alert' */ classPrefix?: string } const DEFAULT_GITHUB_ICONS = { note: '', tip: '', important: '', warning: '', caution: '', } const MarkdownItGitHubAlerts: PluginWithOptions = (md, options = {}) => { const { markers = ['TIP', 'NOTE', 'IMPORTANT', 'WARNING', 'CAUTION'], icons = DEFAULT_GITHUB_ICONS, matchCaseSensitive = false, titles = {}, classPrefix = 'markdown-alert', } = options const markerNameRE = markers === '*' ? '\\w+' : markers.join('|') const RE = new RegExp(`^\\\\?\\[\\!(${markerNameRE})\\]([^\\n\\r]*)`, matchCaseSensitive ? '' : 'i') md.core.ruler.after('block', 'github-alerts', (state) => { const tokens = state.tokens for (let i = 0; i < tokens.length; i++) { if (tokens[i].type === 'blockquote_open') { const open = tokens[i] const startIndex = i while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length) i += 1 const close = tokens[i] const endIndex = i const firstContent = tokens.slice(startIndex, endIndex + 1).find(token => token.type === 'inline') if (!firstContent) continue const match = firstContent.content.match(RE) if (!match) continue const type = match[1].toLowerCase() as keyof typeof icons const title = match[2].trim() || (titles[type] ?? capitalize(type)) const icon = icons[type] ?? '' firstContent.content = firstContent.content.slice(match[0].length).trimStart() open.type = 'alert_open' open.tag = 'div' open.meta = { title, type, icon, } close.type = 'alert_close' close.tag = 'div' } } }) md.renderer.rules.alert_open = function (tokens, idx) { const { title, type, icon } = tokens[idx].meta return `

${icon}${title}

` } } function capitalize(str: string) { return str.charAt(0).toUpperCase() + str.slice(1) } export default MarkdownItGitHubAlerts