+
${callouts[calloutType]}
+
${title}
+ ${collapse ? toggleIcon : ""}
+
`,
+ }
+
+ const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleHtml]
+ if (remainingText.length > 0) {
+ blockquoteContent.push({
+ type: "paragraph",
+ children: [
+ {
+ type: "text",
+ value: remainingText,
+ },
+ ],
+ })
+ }
+
+ // replace first line of blockquote with title and rest of the paragraph text
+ node.children.splice(0, 1, ...blockquoteContent)
+
+ // add properties to base blockquote
+ node.data = {
+ hProperties: {
+ ...(node.data?.hProperties ?? {}),
+ className: `callout ${collapse ? "is-collapsible" : ""} ${
+ defaultState === "collapsed" ? "is-collapsed" : ""
+ }`,
+ "data-callout": calloutType,
+ "data-callout-fold": collapse,
+ },
+ }
+ }
+ })
+ }
+ })
+ }
+
+ if (opts.mermaid) {
+ plugins.push(() => {
+ return (tree: Root, _file) => {
+ visit(tree, "code", (node: Code) => {
+ if (node.lang === "mermaid") {
+ node.data = {
+ hProperties: {
+ className: ["mermaid"],
+ },
+ }
+ }
+ })
+ }
+ })
+ }
+
+ return plugins
+ },
+ htmlPlugins() {
+ const plugins: PluggableList = [rehypeRaw]
+ if (opts.parseBlockReferences) {
+ plugins.push(() => {
+ const inlineTagTypes = new Set(["p", "li"])
+ const blockTagTypes = new Set(["blockquote"])
+ return (tree, file) => {
+ file.data.blocks = {}
+
+ visit(tree, "element", (node, index, parent) => {
+ if (blockTagTypes.has(node.tagName)) {
+ const nextChild = parent?.children.at(index! + 2) as Element
+ if (nextChild && nextChild.tagName === "p") {
+ const text = nextChild.children.at(0) as Literal
+ if (text && text.value && text.type === "text") {
+ const matches = text.value.match(blockReferenceRegex)
+ if (matches && matches.length >= 1) {
+ parent!.children.splice(index! + 2, 1)
+ const block = matches[0].slice(1)
+
+ if (!Object.keys(file.data.blocks!).includes(block)) {
+ node.properties = {
+ ...node.properties,
+ id: block,
+ }
+ file.data.blocks![block] = node
+ }
+ }
+ }
+ }
+ } else if (inlineTagTypes.has(node.tagName)) {
+ const last = node.children.at(-1) as Literal
+ if (last && last.value && typeof last.value === "string") {
+ const matches = last.value.match(blockReferenceRegex)
+ if (matches && matches.length >= 1) {
+ last.value = last.value.slice(0, -matches[0].length)
+ const block = matches[0].slice(1)
+
+ if (!Object.keys(file.data.blocks!).includes(block)) {
+ node.properties = {
+ ...node.properties,
+ id: block,
+ }
+ file.data.blocks![block] = node
+ }
+ }
+ }
+ }
+ })
+
+ file.data.htmlAst = tree
+ }
+ })
+ }
+
+ return plugins
+ },
+ externalResources() {
+ const js: JSResource[] = []
+
+ if (opts.callouts) {
+ js.push({
+ script: calloutScript,
+ loadTime: "afterDOMReady",
+ contentType: "inline",
+ })
+ }
+
+ if (opts.mermaid) {
+ js.push({
+ script: `
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
+ const darkMode = document.documentElement.getAttribute('saved-theme') === 'dark'
+ mermaid.initialize({
+ startOnLoad: false,
+ securityLevel: 'loose',
+ theme: darkMode ? 'dark' : 'default'
+ });
+ document.addEventListener('nav', async () => {
+ await mermaid.run({
+ querySelector: '.mermaid'
+ })
+ });
+ `,
+ loadTime: "afterDOMReady",
+ moduleType: "module",
+ contentType: "inline",
+ })
+ }
+
+ return { js }
+ },
+ }
+}
+
+declare module "vfile" {
+ interface DataMap {
+ blocks: Record