Mermaid 新增图表类型完全指南:从 JISON 文法到渲染管线(历史参考)
本篇基于 mermaid 仓库的社区贡献文档 new-diagram-jison.md,系统讲解向 mermaid 添加一种新图表类型的完整流程:定义 JISON 文法、用 yy 对象存储解析数据、实现渲染器、注册类型检测以及接入主题与无障碍能力。需要特别注意的是,官方文档已明确 JISON 文法处于弃用(Deprecated)状态,基于 JISON 的新图表 PR 将不被接受;但仓库中仍有 18 个图表使用 .jison 文件(如 flow.jison、sequenceDiagram.jison、classDiagram.jison),理解这套历史管线对于维护旧图表、排错以及理解 parser/renderer 分层架构仍然必要。读完后,你将掌握 mermaid 图表“文法解析 → 数据存储 → 类型检测 → 渲染触发”的完整调用链,以及无障碍(Accessibility)与主题(Theming)两套通用基础设施的接入方式。
背景:JISON 已弃用,新图表请走 Langium/Chevrotain
原文明档开头即给出警告:
JISON grammars are deprecated in mermaid. Please use Langium instead. See New Diagram for more information. New diagrams with JISON grammars will not be accepted.
即:JISON 文法已弃用,请使用 Langium 代替,参见新版指南 new-diagram.md。新图表的推荐做法是:
- 使用 Chevrotain 编写文法,与图表自身一同放在
packages/mermaid/src/diagrams/<diagram>/parser/下,用例图(usecase)是参考实现:包含词法器、CstParser与构建图表模型的 CST visitor,并共享 runChevrotainParse.ts 来运行 lexer/parser 组合并带源码位置地报告错误; - 另一批图表(architecture、gitGraph、info、packet、pie、radar、treemap 等)使用 Langium 文法,位于独立包 packages/parser;
- 旧 JISON 图表继续受支持——修 bug 就地在原处修改,而不是重写,但它不是新工作的目标。
因此,下文的 JISON 流程应作为维护既有图表的参考手册来读:它完整展示了 mermaid 图表模块的骨架,而这套骨架(parser + db + renderer + styles + injectUtils)在 Langium/Chevrotain 时代依然保留,只是文法编写语言发生了变化。
Step 1:文法与解析(Grammar & Parsing)
定义识别关键词
为新的图表类型定义 JISON 文法的第一步,是规定一段文本如何被识别为该类型图表——通常是一个起始关键词。原文给出的对照:
- flowchart 以关键词
graph开头; - sequence diagram 以关键词
sequenceDiagram开头。
组织方式上,需要为每种图表在 diagrams 目录下新建一个文件夹,并在其中放一个 parser 子文件夹存放文法文件。仓库内现存 JISON 图表均遵循这一布局,例如 xychart.jison、block.jison。
官方示例包 mermaid-example-diagram 提供了最小完整的参照。其词法部分 exampleDiagram.jison 展示了典型结构:
%lex
%options case-insensitive
%%
"example-diagram" return 'example-diagram' ;
[\s\n\r]+ return 'NL' ;
[\s]+ return 'space';
"showInfo" return 'showInfo';
<<EOF>> return 'EOF' ;
. return 'TXT' ;
/lex
%start start
%% /* language grammar */
start
: example-diagram document 'EOF' { return yy; }
;
document
: /* empty */
| document line
;
line
: statement { }
| 'NL'
;
statement
: showInfo { yy.setInfo(true); }
;
可以看到:词法规则(%lex 与 /lex 之间)负责把关键词 example-diagram 识别出来,语法规则中 %start start 声明起始符号,而 statement 规则里 yy.setInfo(true) 这一行,正是解析动作(action)把解析结果写入数据对象的典型写法。
在解析过程中存储数据
原文“Store data found during parsing”一节指出:解析器在解析过程中遇到的数据会被暂存,供后续渲染器使用。JISON 的机制是——你可以让解析器在解析期间调用由解析器的使用者注入的一个对象,该对象在解析过程中被调用来存储数据。原文给出的语法片段:
statement
: 'participant' actor { $$='actor'; }
| signal { $$='signal'; }
| note_statement { $$='note'; }
| 'title' message { yy.setTitle($2); }
;
在这段文法中,当解析器遇到 title 关键字时,会调用数据对象上的 setTitle 方法——$2 是第二个语法制约(即 title 后的 message),$$ 是该产生式的返回符号。示例包的 exampleDiagramDb.js 就是这样一个“数据对象”:它维护 message、info 两个内部状态,并导出 setMessage/getMessage/setInfo/getInfo/clear 五个函数供解析器调用和渲染器读取,最后以 default export 汇总成 db。
定义 parseError
原文特别强调(Note):务必为解析器定义 parseError 函数并调用 mermaid.parseError,这样就能为最终用户提供统一的解析错误检测方式。yy 对象上应提供:
exports.parseError = function (err, hash) {
mermaid.parseError(err, hash);
};
而 yy 对象在解析时是这样被初始化绑定的:
const parser = exampleParser.parser;
parser.yy = db;
即:先把图表的 db 模块挂到 parser.yy 上,文法中的动作代码(如 yy.setInfo(true))才能在解析期把数据写入该 db。
Step 2:渲染(Rendering)
写一个渲染器:输入是 Step 1 解析阶段收集到的数据,输出是渲染出的图表 SVG。原文明确建议:参考 sequenceRenderer.js 而不是 flowchart 的渲染器,因为 sequence 的渲染器是更通用(generic)的示例。渲染器应放在该图表自己的文件夹内。
示例包中的 exampleDiagramRenderer.js 展示了渲染器入口的典型形态:导出一个 draw(text, id, version) 函数,内部通过 getConfig() 读取配置、处理 securityLevel === 'sandbox' 的 iframe 场景(从 #i + id 的 iframe 的 contentDocument 中取 body)、用 d3 的 select('#' + id) 选中目标 SVG 并追加内容,最后调用 setupGraphViewbox 设置视框。
从源码结构看,图表最终是以一个聚合对象注册的——diagram-definition.ts 把五个部分捆绑在一起:
export const diagram = {
db, // 数据对象(Step 1 的存储目标)
renderer, // 渲染器(本步)
parser, // JISON 生成的解析器
styles, // 主题样式函数(见 Theming 一节)
injectUtils,
};
这个五元组(db / renderer / parser / styles / injectUtils)就是 mermaid 图表模块的标准骨架,新图表无论用 JISON、Langium 还是 Chevrotain 实现文法,都遵循同样的聚合方式。
Step 3:新图表类型的检测(Detection)
原文指出:需要在 diagram-api/detectType.ts 的 detectType 中增加对新图表类型的检测能力,检测命中时应返回一个新图表类型的键(key)。
关于 key 的命名,原文给出了一段与无障碍直接相关的要求:这个 key 会被用作 SVG 元素的 aria-roledescription,因此应当是一个能清晰描述图表类型的词。以 UML 部署图为例:
- 好:"UMLDeploymentDiagram"(读屏软件会念成 "U-M-L Deployment diagram");
- 好:"deploymentDiagram"(念成 "Deployment Diagram");
- 差:"deployment"(不足以描述图表)。
同时原文注明:类型 key 不必与文法中的起始关键词相同,但两者保持一致会更方便。
对照当前仓库的 detectType.ts,实现上已演化为检测器注册表模式:模块维护一个 detectors: Record<string, DetectorRecord> 映射,detectType(text, config) 先剔除 front matter、%%init 指令和注释(frontMatterRegex/directiveRegex/anyCommentRegex),然后按注册顺序逐一调用各图表的 detector(text, config),第一个返回真值的 key 即为检测结果;全部未命中则抛出 UnknownDiagramError。注册通过 addDetector(key, detector, loader) 完成,其中 loader 支持按需懒加载(lazy load)图表模块——只有被检测命中的图表才会加载。registerLazyLoadedDiagrams 的文档注释还特别提示:检测器的顺序很重要,越具体的检测器越要靠前,因为第一个返回 true 的检测器决定最终加载的图表。
Step 4:最后一块拼图——触发渲染
原文说明:到前三步为止,mermaid 能把文本检测为新类型,但真正尝试渲染时却找不到匹配,渲染会失败。修复方式是在 main.js 的 switch 语句中为 init 增加一个新的 case,其值与 Step 3 检测返回的类型一致;该 case 中的代码应调用该图表的渲染器,并把解析器得到的数据作为参数传入。
结合 Step 3 中检测器注册表的现状可以推断:新版实现中“main.js 的 switch”已被 addDetector 时提供的 loader 所取代——检测命中 key 后,通过 loader 异步取回 diagram 对象(即上文五元组),再依次调用 parser、renderer 完成渲染。对贡献者而言,核心契约没有变:检测返回的 key、注册时提供的 loader、以及五元组内部的数据流必须一一对应。
把解析器作为独立模块使用(Usage of the parser as a separate module)
原文给出了解析器脱离 mermaid 主流程独立使用的完整流程,分四段。
Setup(装配)
const graph = require('./graphDb');
const flow = require('./parser/flow');
flow.parser.yy = graph;
Parsing(解析)
flow.parser.parse(text);
Data extraction(数据提取)
graph.getDirection();
graph.getVertices();
graph.getEdges();
通过 mermaid API 获取解析器
const parser = mermaid.getParser();
原文再次强调:parse 需要一个 graph 对象来存储数据(flow.parser.yy = graph;),关于该对象的细节参见 graphDb.js。这也解释了 JISON 方案的核心数据流:解析器是“无状态动作代码”,所有状态都活在外部的 db/graph 对象里——这正是 Step 1 中 yy 机制的意义。
布局(Layout):用 dagre-wrapper 而非 dagre-d3
原文指出:如果你的新图表使用基于 dagre 的布局,请以 flowchart-v2 为模板。这样你就能用上 dagre-wrapper 而不是 dagreD3——官方正在从后者迁移出去。仓库中独立包 mermaid-layout-elk 与 mermaid-layout-tidy-tree 的存在也印证了布局引擎正走向可插拔化:新图表若需要自动布局,优先考虑与现有 wrapper 层集成,而不是直接内嵌 dagre-d3。
图表的通用能力(Common parts of a diagram)
原文列出了各图表类型之间被标准化的四块通用能力,mermaid 力求让所有图表在最终用户视角下行为一致:
- Directives(指令):一种在图代码内部修改图表配置的方式(如
%%{init: ...}%%); - Accessibility(无障碍):让作者为使用读屏软件的用户提供标题、描述等附加信息;
- Themes(主题):修改图表样式的统一方式;
- Comments(注释):应遵循 mermaid 的注释标准。
下面对其中两块给出原文的完整技术细节。
Accessibility:aria-roledescription 与 accTitle/accDescr
mermaid 会为图表 SVG 元素自动添加三类无障碍信息:aria-roledescription、可访问标题(accessible title)、可访问描述(accessible description)。
其中 aria-roledescription 会被自动设置为 Step 3 检测返回的图表类型 key 并插入 SVG 元素(定义参见 W3C 的 ARIA 标准,原文附 w3.org 链接,此处从略)。
可访问标题与描述的图内语法在 accessibility 文档 中有说明。作为设计目标,各图表之间的 JISON 无障碍语法应当保持一致。原文给出的标准文法片段(lexical 部分 + 语法部分):
* lexical grammar */
%lex
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
<acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
statement
: acc_title acc_title_value { $$=$2.trim();yy.setTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
要点:词法器用 %x 声明排他状态(exclusive state),遇到 accTitle: 时 this.begin("acc_title") 进入单行标题捕获态,捕获到行尾 this.popState() 返回;accDescr:{ 进入多行描述态,直到 } 为止。语法部分统一经 yy.setTitle / yy.setAccDescription 写入数据对象。
设置标题与描述的函数由公共模块提供。原文以 flowDb.js 的导入为例:
import {
setAccTitle,
getAccTitle,
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb';
即各图表 db 不重复实现存取逻辑,而是从 commonDb 统一复用,并在自己的 clear 中调用 commonClear 做状态复位。最后,无障碍标题与描述会在 mermaidAPI 的 render 函数中被插入到 SVG 元素中——与 aria-roledescription 的注入点一致。
Theming:主题引擎接入点
mermaid 支持主题并内置主题引擎,用法参见 theming 文档。原文指出,给图表添加主题支持,代码上集中在几个关键位置:
- 样式引擎入口在
src/styles.js。其中的getStyles函数会在 mermaid 应用样式时被调用; - 该入口会调用你的图表应提供的返回 CSS 的函数。图表侧的这个函数(通常也叫
getStyles)放在src/diagrams/<你的图表>/文件夹下,文件名为styles.js。它接收主题选项作为参数,原文示例:
const getStyles = (options) =>
`
.line {
stroke-width: 1;
stroke: ${options.lineColor};
stroke-dasharray: 2;
}
// ...
`;
- 需要把你的函数登记到
src/styles.js的themes对象中(原文以假想的 xyzDiagram 为例):
const themes = {
flowchart,
'flowchart-v2': flowchart,
sequence,
xyzDiagram,
//...
};
- 颜色选项与数值的实际定义在
src/theme/theme-[xyz].js中。只要你把图表所需的选项补充进现有主题文件,主题系统就能平滑工作而不出现意外。
示例包的 styles.js 正好演示了第 2 点的真实形态:它按 options.THEME_COLOR_LIMIT 循环生成 .section-N 的选择器,从 options['cScale' + i]、options['cScalePeer' + i]、options['cScaleInv' + i]、options['cScaleLabel' + i] 读取主题变量拼出 CSS,最后导出 getStyles。而 exampleDiagramRenderer.js 的 draw 函数里读取的 getConfig().themeVariables.THEME_COLOR_LIMIT 与这里的 options.THEME_COLOR_LIMIT 相互印证:主题变量既经由 styles 函数影响 CSS,也经由 themeVariables 进入渲染逻辑本身(例如决定循环绘制多少段)。
关键文件索引
| 关注点 | 文件 |
|---|---|
| 本篇对应源文档 | docs/community/new-diagram-jison.md |
| 新版(Langium/Chevrotain)指南 | docs/community/new-diagram.md |
| 最小示例图表(五元组骨架) | diagram-definition.ts |
| 示例 JISON 文法 | exampleDiagram.jison |
| 示例数据对象 | exampleDiagramDb.js |
| 示例渲染器 | exampleDiagramRenderer.js |
| 示例主题样式 | styles.js |
| 类型检测与检测器注册表 | detectType.ts |
| Chevrotain 公共解析入口 | runChevrotainParse.ts |
| Langium 文法包 | packages/parser |
小结
- 对新增图表:JISON 已弃用且不接受新 PR,请遵循 new-diagram.md 采用 Chevrotain(与图表同目录)或 Langium(
packages/parser); - 对维护既有 JISON 图表:牢记三步数据流——
parser.yy = db装配、文法动作中yy.setXxx(...)写入、渲染器读取 db 后绘制;错误处理必须走mermaid.parseError; - 无论文法技术栈如何演进,图表模块的契约不变:五元组(db/renderer/parser/styles/injectUtils)+ 检测器 key(兼作 aria-roledescription,须语义完整)+ 公共无障碍函数(commonDb)+ 主题文件中的选项定义 + 指令、注释等通用能力保持一致。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00