Mermaid Directives 深度解析:用 %%{init}%% 指令在图形代码内覆盖主题与图表配置的完整机制
本文以 Mermaid 官方的 Directives 文档 为主体,系统讲解 %%{init: ...}%% 指令的语法结构、init/initialize 的合并规则、通用与图表专属两类配置项的写法(theme、fontFamily、logLevel、flowchart、sequence),并结合当前仓库源码还原指令的完整生命周期:从正则匹配、预处理提取、安全清洗(sanitize)到与站点配置、frontmatter 的合并优先级,帮助你在编写图表时精准控制单图外观,并理解哪些配置项因安全原因不可被指令覆盖。
一、Directives 的定位:写在图里的配置覆盖层
Directives(指令)让图表作者能够在渲染之前、直接以文本形式改变图表外观。它的价值在于:
- 书写时可用:指令就写在图表文本中,和图形定义一起流转,无需额外的 JS 调用;
- 叠加在默认配置之上:指令修改的是全局默认配置和图表专属配置,属于“单图级”(individual level)覆盖;
- 部分配置不可覆盖:出于安全原因,某些配置项不允许通过指令修改;同时你还有权定义“允许图表作者覆盖的配置集合”。
需要注意的是,官方文档开头明确标注了弃用警告:
Directives 从 v10.5.0 起已弃用(deprecated),建议改用 frontmatter 中的
config键来传递配置,详见 Configuration 文档。
也就是说,在当前仓库中 Directives 仍被完整实现和测试(后文会给出源码证据),但新项目中推荐的新写法是 YAML frontmatter:
---
config:
theme: forest
---
graph LR
A-->B
本文仍按原文档脉络完整讲解 Directives 本身,并补充两条机制在源码中如何并存的事实。
二、两类可被指令覆盖的配置
Mermaid 支持两类可被指令覆盖的配置(原文档“Types of Directives options”一节):
1. General/Top Level configurations(通用/顶层配置)
作用于所有图表的通用配置,其中最常通过指令使用的包括:
themefontFamilylogLevelsecurityLevelstartOnLoadsecure
2. Diagram-specific configurations(图表专属配置)
只作用于特定图表类型的配置。例如 mirrorActors 是 SequenceDiagram 专属配置,控制参与者是否镜像显示,因此它只能出现在 sequence 这一层。
原文档提示:并非所有配置项都在文档中列全,完整列表应参考源码中的 packages/mermaid/src/defaultConfig.ts(原文档给出的外部 GitHub 链接在此统一替换为仓库内路径)。
三、指令的声明语法:%% {directive_text} %%
一条指令始终以两个 % 符号开头和结尾,即 %% {directive_text} %%。指令文本的结构是一个以 init 为根的嵌套键值对映射(JSON 对象):
- 顶层:放通用(General)配置;
- 深一层、以图表类型为 key:放该图表的专属配置。
完整结构示例(原文档原样保留):
%%{
init: {
"theme": "dark",
"fontFamily": "monospace",
"logLevel": "info",
"htmlLabels": true,
"flowchart": {
"curve": "linear"
},
"sequence": {
"mirrorActors": true
}
}
}%%
也可以写在一行内:
%%{init: { **insert configuration options here** } }%%
例如:
%%{init: { "sequence": { "mirrorActors":false }}}%%
注意(原文档 Notes):作为参数传入的 JSON 对象必须是合法的键值对,且键要加引号,否则会被忽略;合法的键值对可参考 config 文档。
源码印证:指令如何被识别
在 diagram-api/regexes.ts 中定义了识别指令的正则:
export const directiveRegex =
/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
它匹配 %%{ ... %%} 的成对结构,并捕获形如 init: 或 initialize: 的键名;同文件还定义了 frontMatterRegex(Jekyll 风格 frontmatter)与 anyCommentRegex,这与下文“指令与 frontmatter 合并”的源码路径一一对应。
四、指令的解析与合并规则
init 与 initialize 等价且会被合并
init 和 initialize 都可作为初始化指令的键,且解析后会被归并为同一条指令。原文档示例:
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
%%{initialize: { 'logLevel': 'fatal', "theme":'dark', 'startOnLoad': true } }%%
...
解析后会生成一个单一的 %%init%% JSON 对象,合并两条指令,并对重复的 logLevel 取最后一次出现的值:
{
"logLevel": "fatal",
"theme": "dark",
"startOnLoad": true
}
该对象随后会交给 mermaid.initialize(...) 用于渲染。
最小示例:logLevel 与 theme
%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
graph LR
A-->B
这条指令声明将 logLevel 设为 debug、theme 设为 dark,直接改变渲染结果的外观。
五、典型配置项逐例讲解
以下四个小节完整继承原文档“Directive Examples”中的示例与参数说明。
5.1 通过指令修改 theme
将 theme 改为 forest:
%%{init: { "theme": "forest" } }%%
可选值:default、base、dark、forest、neutral;默认值是 default。
%%{init: { "theme": "forest" } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
subgraph section
B
C
end
源码侧,theme 的取值与内置主题一一对应:config.ts 中的 updateCurrentConfig 会检查 sumOfDirectives.theme in theme,命中后调用 theme[cfg.theme].getThemeVariables(...) 生成该主题的变量集,再合并进指令里自定义的 themeVariables——这就是“指令改主题”在底层的落点。
5.2 通过指令修改 fontFamily
%%{init: { "fontFamily": "Trebuchet MS, Verdana, Arial, Sans-Serif" } }%%
%%{init: { "fontFamily": "Trebuchet MS, Verdana, Arial, Sans-Serif" } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
subgraph section
B
C
end
一个有意思的源码细节:addDirective 中专门处理了 fontFamily——如果指令带了 fontFamily 却没有 themeVariables,它会自动把 fontFamily 复制进 themeVariables.fontFamily(见 config.ts 中 addDirective 函数)。这解释了为什么“顶层写 fontFamily”能真正影响字体渲染,而不必手工写主题变量。
5.3 通过指令修改 logLevel
%%{init: { "logLevel": 2 } }%%
logLevel 的取值(原文档列表完整保留):
1:debug2:info3:warn4:error5:only fatal errors
默认值是 5。
%%{init: { "logLevel": 2 } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
subgraph section
B
C
end
5.4 通过指令修改 flowchart 配置
常用 flowchart 配置(原文档列表):
htmlLabels:已弃用,建议改在根级别设置;- curve:
linear/curve; - diagramPadding:number;
- useMaxWidth:number。
完整列表参见 packages/mermaid/src/defaultConfig.ts。
只覆盖 flowchart 配置(不碰通用配置)的写法:
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
Warning(原文档原文):Deprecated:
flowchart.htmlLabels自 v11.12.3 起弃用。请改用全局htmlLabels配置。例如不要写"flowchart": { "htmlLabels": true },而是把"htmlLabels": true放在顶层。
%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%
graph TD
A(Forest) --> B[/Another/]
A --> C[End]
subgraph section
B
C
end
这个弃用规则在源码中有对应实现:config.ts 的 getEffectiveHtmlLabels 函数按 根级 htmlLabels → flowchart.htmlLabels → 默认 true 的优先级取值,且一旦检测到 config.flowchart.htmlLabels 已设置,就会通过 issueWarning 打印 flowchart.htmlLabels is deprecated. Please use global htmlLabels instead. 的警告。
5.5 通过指令修改 Sequence 图配置
常用 sequence 配置(原文档列表完整保留):
- width:number
- height:number
- messageAlign:
left、center、right - mirrorActors:boolean
- useMaxWidth:boolean
- rightAngles:boolean
- showSequenceNumbers:boolean
- wrap:boolean
先看默认(wrap 默认为 false):
sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Fine, how did your mother like the book I suggested? And did you catch the new book about alien invasion?
Alice->Bob: Good.
Bob->Alice: Cool
启用 wrap(并把 width 设为 300):
%%{init: { "sequence": { "wrap": true} } }%%
%%{init: { "sequence": { "wrap": true, "width":300 } } }%%
sequenceDiagram
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Fine, how did your mother like the book I suggested? And did you catch the new book about alien invasion?
Alice->Bob: Good.
Bob->Alice: Cool
应用该片段后,长消息会在参与者之间换行显示,图表整体宽度被约束在 300px 附近。
六、源码纵深:指令从文本到生效配置的完整链路
结合当前仓库源码,Directives 的完整生命周期如下(文件路径均可在当前仓库中查看):
6.1 预处理阶段:提取、合并、删除
preprocess.ts 中的 processDirectives 是入口:
- 用
utils.detectInit(code)检测%%init%%指令并解析为对象(见 utils.ts 中detectInit与removeDirectives); - 同时检测
wrap类指令(%%{wrap: true}%%),若存在则把initDirective.wrap置为true; - 调用
removeDirectives(code)把指令文本从源码中删除,避免指令内容进入图形解析器; - 最后由
cleanAndMerge(frontmatter.config, directive)将 frontmatter 的config与 directive 结果合并——这正是“v10.5.0 后推荐 frontmatter、但 directives 仍然可用”两条机制在源码层的会合点。
6.2 配置合并阶段:addDirective 与优先级
mermaidAPI.ts 在解析出图表元数据后调用 configApi.addDirective(processed.config ?? {}),注释明确写着 “Important that we do not create the diagram until after the directives have been included”——即先注入指令,再创建图表对象,保证图表拿到的配置已包含指令覆盖。
config.ts 中的 updateCurrentConfig 给出优先级事实(从源码结构看):
defaultConfig < siteConfig(来自 mermaid.initialize) < 按顺序累积的 directives
- 以
siteConfig为基底深拷贝(assignWithDepth); - 逐条
sanitize(d)后依次assignWithDepth进sumOfDirectives——多条指令中后者覆盖前者,与第四节logLevel: fatal覆盖debug的行为一致; - 再把
sumOfDirectives叠加到基底上,形成currentConfig。
reset() 会清空 directives 并把配置重置回 siteConfig,保证渲染下一张图时不会串味。
6.3 安全边界:两层 sanitize
文档说“出于安全原因,部分配置不可用指令修改”,源码里这是两层具体实现:
第一层:secure 键保护(config.ts 的 sanitize)
secure及siteConfig.secure数组中列出的键,若在指令中出现会被直接删除(日志记录 “Denied attempt to modify a secure key”);- 删除所有以
__开头的键(防原型污染); - 删除任何包含
<、>或url(data:的字符串值(防 XSS / base64 内嵌 SVG 脚本)。
第二层:白名单 + 值校验(utils/sanitizeDirective.ts 的 sanitizeDirective)
- 键白名单:不在
defaultConfig.ts导出的configKeys中的键一律删除——这就是“不是所有配置都能被指令改变”的真正原因,可用键集合严格等于默认配置暴露的键; - 危险键模式:
startsWith('__')、含proto、含constr的键被删除; - 字典型配置的值校验:
nodeColors(sankey 的 CSS 颜色)、filenameIcons/extensionIcons(treeView 的 iconify 图标引用)按DICTIONARY_CONFIG_PATTERNS中的正则校验值,不合规的条目被删除; - CSS 相关键(
themeCSS、fontFamily、altFontFamily)会经sanitizeCss做花括号配平检查,不平衡时替换为{ /* ERROR: Unbalanced CSS */ }; - themeVariables 值校验:值需匹配
/^[\d "#%(),.;A-Za-z]+$/,否则清空为''。
6.4 E2E 佐证
仓库的端到端测试套件里存在专门的 conf-and-directives 用例目录,其中 settings-from-initialize-nodes-should-be-green.mmd 验证 %%initialize%% 指令确实能改变渲染结果(节点着色),与文档中“init/initialize 等价”的说明互为印证。
七、实践建议与要点回顾
- 单图覆盖用指令,全局覆盖用
mermaid.initialize:指令的作用域是“当前图”,适合把主题、字体、wrap 等外观决策写在图表文本里随文档流转; - 新代码优先 frontmatter:由于 Directives 自 v10.5.0 起标记弃用,frontmatter 的
config键是官方推荐写法;当前版本中两者并存且最终在cleanAndMerge处汇合; - 重复键取最后一个:多条
init/initialize合并时后值覆盖前值,写多条指令时注意顺序; - 键名必须合法:JSON 键要加引号,否则被忽略;且键必须存在于 defaultConfig.ts 的键集合中,否则被 sanitize 静默删除;
- 安全键改不了:
secure、原型污染模式、含标签或data:URL 的字符串都会被清洗,试图通过这些通道注入样式/脚本是无效的; - 弃用项要迁移:
flowchart.htmlLabels已弃用(v11.12.3+),改用顶层htmlLabels,源码会打印对应警告。
核心事实速查表:
| 主题 | 说明 | 依据 |
|---|---|---|
| 指令语法 | %%{init: {...} }%%,init/initialize 等价 |
docs/config/directives.md、diagram-api/regexes.ts |
| 生效时序 | 预处理提取 → 删除指令文本 → 先注入配置再建图 | preprocess.ts、mermaidAPI.ts |
| 合并优先级 | defaultConfig < siteConfig < directives(后写覆盖) | config.ts updateCurrentConfig |
| 可用键集合 | 以 defaultConfig.ts 的 configKeys 白名单为准 |
utils/sanitizeDirective.ts |
| 安全清洗 | secure 键、__/proto/constr 键、<>/data: 字符串、CSS 配平、themeVariables 值校验 |
config.ts、utils/sanitizeDirective.ts |
| 推荐写法 | frontmatter config 键(directives 自 v10.5.0 弃用) |
docs/config/directives.md、docs/config/configuration.md |
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 StartedRust0624
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