Skip to content

Commit

Permalink
feat: rehypeMermaid
Browse files Browse the repository at this point in the history
  • Loading branch information
taga3s committed Nov 17, 2024
1 parent 5403569 commit 6d33107
Show file tree
Hide file tree
Showing 6 changed files with 1,907 additions and 57 deletions.
6 changes: 6 additions & 0 deletions app/packages/rehype-mermaid/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { unified } from "unified";
import rehypeParse from "rehype-parse";

const parser = unified().use(rehypeParse, { fragment: true });

export { parser };
96 changes: 96 additions & 0 deletions app/packages/rehype-mermaid/rehypeMermaid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import type { Plugin } from "unified";
import type { Text, Element, Parent } from "hast";
import { visit } from "unist-util-visit";
import { renderMermaid } from "@mermaid-js/mermaid-cli";
import puppeteer from "puppeteer";
import { parser } from "./parser";

interface MermaidCodeBlock {
textNode: Text;
index: number;
parent: Parent;
}

const checkIsMermaid = (lang: string): boolean => lang === "mermaid";

const rehypeMermaid: Plugin = () => {
const parseLanguage = (classNames: string[]): string => {
for (const className of classNames) {
if (className.startsWith("language-")) {
return className.replace("language-", "");
}
}
return "";
};

return async (node) => {
const mermaidCodeBlocks: MermaidCodeBlock[] = [];

visit(node, "element", (node: Element, index, parent) => {
// Check if the node is a pre tag with a single child
if (!(node.tagName === "pre" && Array.isArray(node.children) && node.children.length === 1)) {
return;
}

// Check if the child is a code tag
const codeElem = node.children[0] as Element;
if (!(codeElem !== null && typeof codeElem === "object" && codeElem.tagName === "code")) {
return;
}

// Check if the code tag has a className property
const classNames = codeElem.properties?.className;
if (
!(
Array.isArray(classNames) &&
classNames.length > 0 &&
classNames.every((className) => typeof className === "string")
)
) {
return;
}

// Check if the code tag has a text child
const textNode = codeElem.children[0] as Text;
if (typeof textNode.value !== "string") {
return;
}

// Parse the language from the class names and check if it is supported
const lang = parseLanguage(classNames);
const isMermaid = checkIsMermaid(lang);

if (isMermaid && index !== null && parent !== null) {
mermaidCodeBlocks.push({ textNode, index, parent });
}
});

if (mermaidCodeBlocks.length === 0) {
return;
}

const browser = await puppeteer.launch({
headless: true,
});

const decoder = new TextDecoder();

await Promise.all(
mermaidCodeBlocks.map(async ({ textNode, index, parent }, blockIndex) => {
const { data: svgBuffer } = await renderMermaid(browser, textNode.value, "svg");

const svgElem = decoder.decode(svgBuffer).replaceAll("my-svg", `my-svg-${blockIndex}`);
const parsedRoot = parser.parse(svgElem);

const content = parsedRoot.children[0] as Element;
content.properties.style = "width: 100%; background-color: white;";

parent.children[index] = content;
}),
);

await browser.close();
};
};

export default rehypeMermaid;
19 changes: 19 additions & 0 deletions app/routes/posts/md-test.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,22 @@ publishedAt: 2024/09/14
```ts title="sample.ts"
console.log("Hello, TypeScript!");
```

- こちらは、Mermaid のサンプルです。

```mermaid
classDiagram
Class01 <|-- AveryLongClass : Cool
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Class09 --> C2 : Where am i?
Class09 --* C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 <--> C2: Cool label
```
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@hono/vite-dev-server": "^0.14.0",
"@hono/vite-ssg": "^0.1.0",
"@mdx-js/rollup": "^3.0.1",
"@mermaid-js/mermaid-cli": "^11.4.0",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
Expand All @@ -32,6 +33,7 @@
"husky": "^9.1.1",
"lint-staged": "^15.2.7",
"mdast": "^3.0.0",
"puppeteer": "^23.8.0",
"rehype-parse": "^9.0.1",
"remark-breaks": "^4.0.0",
"remark-frontmatter": "^5.0.0",
Expand Down
Loading

0 comments on commit 6d33107

Please sign in to comment.