| import type { Plugin } from "unified"; |
| import type { Root } from "mdast"; |
| import { visit } from "unist-util-visit"; |
|
|
| export const ALERT_TYPES = [ |
| "note", |
| "tip", |
| "important", |
| "warning", |
| "caution", |
| ] as const; |
| export type AlertType = (typeof ALERT_TYPES)[number]; |
|
|
| |
| |
| |
| |
| |
| const ALERT_REGEX = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*\r?\n?/i; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function toClassList(value: unknown): string[] { |
| if (Array.isArray(value)) return value as string[]; |
| if (typeof value === "string") return value.split(/\s+/).filter(Boolean); |
| return []; |
| } |
|
|
| export const remarkGithubAlerts: Plugin<[], Root> = () => (tree) => { |
| |
| |
| |
| |
| visit(tree, "blockquote", (node) => { |
| const block = node; |
| const firstChild = block.children[0]; |
| if (firstChild?.type !== "paragraph") return; |
| const paragraph = firstChild; |
| const firstText = paragraph.children[0]; |
| if (firstText?.type !== "text") return; |
|
|
| const match = firstText.value.match(ALERT_REGEX); |
| if (!match) return; |
|
|
| const type = match[1].toLowerCase() as AlertType; |
| const text = firstText; |
| text.value = text.value.replace(ALERT_REGEX, ""); |
|
|
| if (text.value === "") { |
| paragraph.children.shift(); |
| if (paragraph.children[0]?.type === "break") { |
| paragraph.children.shift(); |
| } |
| if (paragraph.children.length === 0) { |
| block.children.shift(); |
| } |
| } |
|
|
| block.data = block.data ?? {}; |
| const existingHProps = (block.data.hProperties ?? {}) as Record< |
| string, |
| unknown |
| >; |
| const baseClasses = toClassList(existingHProps.className); |
| block.data.hProperties = { |
| ...existingHProps, |
| className: [...baseClasses, "markdown-alert", `markdown-alert-${type}`], |
| }; |
| }); |
| }; |
|
|