OverRainbow

Mermaid And Mdx

☕️ 1 min read

Mermaid

Mermaid是一款用代码绘制UML图的开源库。 依赖少(纯JS),学习曲线平滑,很适合上手。再加上JS编写的跨端优势,更容易集成到其他项目中。

Mermaid Live Editor可以在线使用。

可以简单看一下Mermaid依赖的库。用到了d3作为图形库,dagre-d3用于有向布局,graphlib提供数据结构存储,khroma用于AI智能色彩管理,moment-minilite版本的momentjs,stylisCSS库 可以说看这几个依赖已经感觉很强了。

"dependencies": {
    "@braintree/sanitize-url": "^3.1.0",
    "d3": "^7.0.0",
    "dagre": "^0.8.5",
    "dagre-d3": "^0.6.4",
    "dompurify": "2.3.1",
    "graphlib": "^2.1.8",
    "khroma": "^1.4.1",
    "moment-mini": "^2.24.0",
    "stylis": "^4.0.10"
  },

如何改造mdx-loader支持mdx-mermaid

本文不是讲如何使用Mermaid绘图,而是侧重在将其和MDX进行整合,从而优化写作体验。

下面这幅图就是mdx渲染出来的。本blog的mdx与mermaid自上而下依赖关系如下。

graph TD; react-scripts-mdx -->mdx-loader; mdx-loader-->mdx-mermai;

在实际接入过程中就遇到了挑战。

首先,在mdx-loader增加插件

diff --git a/node_modules/mdx-loader/index.js b/node_modules/mdx-loader/index.js
index 60c954a..bc0c9a6 100644
--- a/node_modules/mdx-loader/index.js
+++ b/node_modules/mdx-loader/index.js
@@ -10,6 +10,7 @@ const mdxExportJSONByDefault = require('mdx-constant')
 const grayMatter = require('gray-matter')
 const typography = require('./typography')
 const rehypePrism = require('./prism')
+const mdxMermaid = require('mdx-mermaid') 
 module.exports = async function(source) {
   let result
@@ -18,6 +19,7 @@ module.exports = async function(source) {
   const options = Object.assign(
     {
       remarkPlugins: [
+        mdxMermaid,         slug,
         images, 
         emoji,

接入完成后,竟然ts报错了,提示mdx-mermaid/Mermaid不能resolve。这就很困惑了。 于是就去看node_modules,他的package.json如下

"name": "mdx-mermaid",
  "version": "1.1.0",
  "description": "Display mermaid diagrams in mdx files.",
  "types": "index.d.ts",
  "main": "lib/mdxast-mermaid.js",
  "exports": {
    ".": "./lib/mdxast-mermaid.js",
    "./Mermaid": "./lib/Mermaid.js"
  },

首先这个mdx-mermaid包在package.json中定义了exports字段,其中将./lib导出成./,./lib/Mermaid.js导出成./Mermaid。 经过一番调查,发现TS还没实现这个功能

那我们就得把引用关系给改一下,接下来定位是什么地方写死的呢。

改造mdx-mermail

mdxast-mermaid很明显是一个babel插件,通过对代码parse再transform输出改造后的代码。

可以看到visit遍历文件,如果引入了Mermai,就不处理,否则,就增加一条import记录。问题就出在这个路径ts不认。

就改对就行了。

diff --git a/node_modules/mdx-mermaid/lib/mdxast-mermaid.js b/node_modules/mdx-mermaid/lib/mdxast-mermaid.js
index dbc38e4..e9872d4 100644
--- a/node_modules/mdx-mermaid/lib/mdxast-mermaid.js
+++ b/node_modules/mdx-mermaid/lib/mdxast-mermaid.js
@@ -33,7 +33,7 @@ function plugin(config) {
         // See if there is already an import for the Mermaid component
         let importFound = false;
         unist_util_visit_1.default(ast, { type: 'import' }, (node) => {
-            if (/\s*import\s*{\s*Mermaid\s*}\s*from\s*'mdx-mermaid\/Mermaid'\s*;?\s*/.test(node.value)) {
+            if (/\s*import\s*{\s*Mermaid\s*}\s*from\s*'mdx-mermaid\/lib\/Mermaid'\s*;?\s*/.test(node.value)) {                 importFound = true;
                 return unist_util_visit_1.default.EXIT;
             }
@@ -42,7 +42,7 @@ function plugin(config) {
         if (!importFound) {
             ast.children.splice(0, 0, {
                 type: 'import',
-                value: 'import { Mermaid } from \'mdx-mermaid/Mermaid\';'
+                value: 'import { Mermaid } from \'mdx-mermaid/lib/Mermaid\';'             });
         }
     }

patch-package的使用

一开始我说clone了两个代码库,本地通过yalc打包发布,调通。并给作者提了issue和PR

但问题是假如PR没有被合入,那如何在其他设备上同步我的“魔改”呢?

下面就要请出主角patch-package,

简单概括一下就是

1、先在node_modules里面直接修改代码

2、通过npx patch-package 包名的方式产生patch文件(根目录的patches文件夹下)

3、确保package.json中的postinstall存在patch应用的代码(如下)

"scripts": {
+  "postinstall": "npx patch-package" }

上面的两处修正我都是直接在node_modules里面改的,并且fork了代码库提交了issue和PR。

感觉这样比用yalc修复bug的效率高多了呢。你觉得呢?