Mermaid 配置机制全解析:defaultConfig、siteConfig、Frontmatter 与 Directives 的三层配置体系
本文以 Mermaid 仓库的 Configuration 文档为主线,系统讲解 Mermaid 启动时如何抽取并合并多来源配置,覆盖 frontmatter YAML 配置、initialize 站点级配置、configApi.reset 重置机制等文档核心内容,并结合 config.ts 与 preprocess.ts 等源码,帮助读者掌握"默认配置 → 站点配置 → 图表级配置"的完整合并链路与安全边界(sanitize),能够独立完成站点级主题定制、单图级 frontmatter 覆盖以及渲染前的配置重置。
一、配置来源总览:三层配置与 render config
Mermaid 启动时,会先抽取配置(configuration)来确定渲染某张图所用的最终配置。根据 Configuration 文档,配置共有以下来源:
- 默认配置(defaultConfig):所有图表的底层基础,代码中由 defaultConfig.ts 提供,并在 config.ts 中以
Object.freeze(config)冻结导出,保证默认值不可被运行时意外篡改; - 站点级覆盖(siteConfig):由宿主站点/应用通过
initialize调用设置,作用于该站点内的所有图表; - Frontmatter(v10.5.0+):图表作者可以在图表代码顶部的 YAML frontmatter 中更新选定的配置参数,应用到 render config 上;
- Directives(已被 Frontmatter 取代,Deprecated):图表作者可以在图表代码中通过
%%{init: ...}%%指令直接更新配置,同样应用到 render config。
最终,render config(渲染配置) 是把上述各层配置按优先级合并后、真正用于渲染的配置对象。
从源码结构看,这一"三层叠加"模型在 config.ts 中体现得非常清晰——模块内部维护了四个状态变量:
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let configFromInitialize: MermaidConfig;
let directives: MermaidConfig[] = [];
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
即 siteConfig(站点配置,基于默认配置深合并而来)、configFromInitialize(initialize 传入的原始配置)、directives(累积的指令/frontmatter 配置队列)和 currentConfig(当前实际生效的渲染配置)。所有合并都通过深合并工具 assignWithDepth 完成,保证嵌套配置项(如 themeVariables)按深度逐项覆盖,而不是整体替换。
二、Frontmatter 配置:在图表代码内自包含地覆盖参数
Configuration 文档指出:除安全配置(secure configs)外,整个 mermaid 配置都可以由图表作者在 frontmatter 中覆盖。frontmatter 是位于图表顶部的一个 YAML 块。文档给出的示例如下:
---
title: Hello Title
config:
theme: base
themeVariables:
primaryColor: "#00ff00"
---
flowchart
Hello --> World
这个示例同时演示了三个 frontmatter 能力:title(图表标题)、config.theme(切换主题为 base)和 config.themeVariables.primaryColor(覆盖主题变量主色为 #00ff00)。
2.1 frontmatter 的解析实现
frontmatter 的提取逻辑在 frontmatter.ts 中。关键实现要点:
- 使用正则
frontMatterRegex匹配开头的---块,若无匹配则原样返回,图表保持无 metadata; - 解析前会对 YAML 体做去缩进处理(js-yaml 拒绝 tab 缩进的文档);
- 使用
js-yaml的JSON_SCHEMA模式解析,以支持完整 JSON 类型的配置值; - 仅提取显式支持的三个字段:
title、displayMode(当前用于甘特图的紧凑显示模式)、config,其余字段会被丢弃。
// frontmatter.ts 简化逻辑
let parsed = yaml.load(yamlBody, { schema: yaml.JSON_SCHEMA }) ?? {};
const metadata = {};
if (parsed.displayMode) metadata.displayMode = parsed.displayMode.toString();
if (parsed.title) metadata.title = parsed.title.toString();
if (parsed.config) metadata.config = parsed.config;
2.2 frontmatter 与 directives 的合并链路
frontmatter 解析完成后进入预处理流程 preprocess.ts。preprocessDiagram 函数按固定顺序完成四件事:
cleanupText:统一换行符(CRLF → LF)、把 HTML 属性的双引号统一为单引号;processFrontmatter:提取 frontmatter。注意一个细节——若设置了displayMode,会自动把它映射到config.gantt.displayMode(为兼容旧版 gantt 紧凑模式);processDirectives:检测init指令和wrap指令,并从代码中移除指令文本;cleanAndMerge(frontMatter.config, directive.directive):将 frontmatter 配置与指令配置清洗合并,得到最终的图表级配置。
也就是说,同一张图里 frontmatter 与 directives 并存时,二者会先合并再叠加到站点配置之上——这正是 Configuration 文档所说"两者都作用于 render config"的具体实现。
三、Mermaid 的启动时序与站点级 Initialize
Configuration 文档给出了一张启动时序图,说明站点如何把配置注入 mermaid:
sequenceDiagram
Site->>mermaid: initialize
Site->>mermaid: content loaded
mermaid->>mermaidAPI: init
时序含义:站点先调用 initialize 完成站点级配置,随后内容加载完毕,mermaid 入口再调用 mermaidAPI 完成内部初始化。
3.1 initialize:只应用一次的站点级覆盖
文档强调:initialize 调用只生效一次,由站点集成方调用来在站点层面覆盖默认配置。在 mermaid.ts 中可以看到其实现就是薄薄一层转发:
const initialize = function (config: MermaidConfig) {
mermaidAPI.initialize(config);
};
对应的底层 API 在 mermaidAPI.ts 中,initialize 会调用 config.ts 的 setSiteConfig:
export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
siteConfig = assignWithDepth({}, defaultConfig);
siteConfig = assignWithDepth(siteConfig, conf);
if (conf.theme && theme[conf.theme]) {
siteConfig.themeVariables = theme[conf.theme].getThemeVariables(conf.themeVariables);
}
updateCurrentConfig(siteConfig, directives);
return siteConfig;
};
这段源码印证了文档中"覆盖默认配置"的说法:siteConfig 总是先以冻结的 defaultConfig 打底,再深合并站点传入的 conf;若站点指定了 theme,还会把该主题预置的主题变量与站点自定义的 themeVariables 合并,保证换主题后变量取值完整。另外 config.ts 中的 saveConfigFromInitialize 会把 initialize 的原始配置单独保存,供主题变量回退合并使用。
3.2 updateSiteConfig:initialize 之外的另一种更新方式
除了一次的 initialize,config.ts 还提供 updateSiteConfig,它不做"打底重置",而是在现有 siteConfig 之上增量深合并后重新计算 currentConfig。从源码结构看,适合站点在运行期间(例如用户切换主题面板)追加更新站点配置的场景。生成的 API 参考文档也收录了这两个函数,可参见 setSiteConfig 与 updateSiteConfig。
四、render config 的合并算法:defaultConfig → siteConfig → directives
最终生效的 render config 由 config.ts 的 updateCurrentConfig 计算,合并优先级与文档描述完全一致:
const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => {
// start with config being the siteConfig
let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
// Join directives
let sumOfDirectives: MermaidConfig = {};
for (const d of _directives) {
sanitize(d);
sumOfDirectives = assignWithDepth(sumOfDirectives, d);
}
cfg = assignWithDepth(cfg, sumOfDirectives);
if (sumOfDirectives.theme && sumOfDirectives.theme in theme) {
// 用 initialize 保存的主题变量打底,再叠加指令的主题变量
const themeVariables = assignWithDepth(
configFromInitialize?.themeVariables || {},
sumOfDirectives.themeVariables
);
if (cfg.theme && cfg.theme in theme) {
cfg.themeVariables = theme[cfg.theme].getThemeVariables(themeVariables);
}
}
currentConfig = cfg;
checkConfig(currentConfig);
return currentConfig;
};
可以归纳出四点:
- 基底是 siteConfig:
cfg从siteConfig深拷贝起步,因此 defaultConfig 的每一项都是"最终兜底值"; - directives(含 frontmatter 配置)最后叠加:多条指令按入队顺序依次
sanitize后深合并,再整体覆盖到cfg上; - 主题变量有专门的回退合并:当指令指定了
theme时,themeVariables会以 initialize 时保存的主题变量为底、叠加指令中的主题变量,最后交给对应主题的getThemeVariables生成完整变量集——这解释了为何 frontmatter 里只写primaryColor一个变量,其余主题变量仍能正确取值; - 每次合并后都会执行
checkConfig:目前用于对已废弃配置项(如lazyLoadedDiagrams、loadExternalDiagramsAtStartup)打印一次性警告(见 config.ts)。
此外,getEffectiveHtmlLabels 展示了配置读取端的兼容处理:优先取全局 htmlLabels,回退到已废弃的 flowchart.htmlLabels(并提示用全局 htmlLabels 替代),这属于 v9→v10 配置迁移的典型模式。
五、安全边界:secure 配置与 sanitize
Configuration 文档特别提到 frontmatter "除 secure configs 外"均可覆盖。这条边界在 sanitize 中落地,它在每次指令加入合并前被调用(updateCurrentConfig 循环内的 sanitize(d)):
export const sanitize = (options: any) => {
// 1. 禁止覆盖 siteConfig 声明的 secure 键(始终包含 'secure' 本身)
['secure', ...(siteConfig.secure ?? [])].forEach((key) => {
if (Object.hasOwn(options, key)) {
log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
delete options[key];
}
});
// 2. 删除以 __ 开头的键,防原型污染
// 3. 删除包含 '<'、'>' 或 'url(data:' 的字符串值,防 XSS
// 4. 递归处理对象值
};
三层防护的意图在源码注释中写得很明白:注释特别提醒不要在日志中用模板字符串打印被拦截的值,因为恶意脚本可以利用日志器对值的序列化执行任意代码。因此:
- 站点可以把敏感键(例如
securityLevel相关的覆盖)列入siteConfig.secure,图表作者即使用 frontmatter 或init指令也无法改写; - 指令/frontmatter 中的任何 HTML 片段、
data:URL 都会被静默删除,防止通过配置通道注入脚本。
生成的 API 参考 sanitize 同样对应这段实现。
六、configApi.reset:每张图渲染前的配置归位
Configuration 文档对 configApi.reset 的定义:把某张图的配置重置回站点整体配置(即站点集成方提供的配置),并且在每次渲染图表之前,reset 会在最开始被调用。
对应实现是 config.ts 的 reset:
export const reset = (config = siteConfig): void => {
// Replace current config with siteConfig
directives = [];
updateCurrentConfig(config, directives);
};
两个关键行为:
- 清空
directives队列:上一张图的 frontmatter/init 指令不会泄漏到下一张图——这正是"每张图渲染前调用 reset"的意义所在,保证图表级配置的隔离性; - 以 siteConfig 为新基底重算 currentConfig:站点配置本身不受影响,被重置掉的只是叠加在站点层之上的图表级覆盖。
reset 还支持传入自定义基底配置(默认值为 siteConfig),配合 getSiteConfig(返回 siteConfig 的深拷贝)可以只读地获取站点配置做比对。对应的生成文档见 reset 与 getSiteConfig。
值得一提的是,旧式 setConfig(直接改 currentConfig)已被标记为 @deprecated:文档注释明确说明对 currentConfig 的修改会被下一次 addDirective 或 reset 覆盖——这进一步说明当前推荐的图表级定制路径就是 frontmatter(或 legacy 的 directives),而站点级路径是 initialize。
七、实用建议与小结
结合 Configuration 文档与源码实现,实际集成时可以遵循以下实践:
- 站点级主题与全局参数:在应用启动时调用一次
mermaid.initialize({ theme, themeVariables, flowchart: {...} }),让 setSiteConfig 完成 defaultConfig 打底与主题变量补全; - 单图级定制:优先使用 frontmatter(v10.5.0+,YAML 更直观、支持任意嵌套配置),例如文档示例中的
config.theme+config.themeVariables.primaryColor;%%{init: ...}%%directives 属于被取代的旧方案,新图不建议再写; - 安全敏感场景:在
initialize时通过secure数组声明受保护键,sanitize 会拦截任何来自图表作者的同名覆盖; - 不要手动改 currentConfig:
setConfig已弃用,图表级配置请走 frontmatter,跨图状态靠"渲染前自动 reset"保证隔离。
小结:Mermaid 的配置体系是一个"默认配置冻结打底 → initialize 生成 siteConfig → frontmatter/directives 逐图叠加 → sanitize 安全过滤 → 渲染前 reset 归位"的闭环。理解 config.ts 中 siteConfig / configFromInitialize / directives / currentConfig 四个状态变量的流转,再对照 frontmatter.ts 与 preprocess.ts 的提取顺序,就能准确预测任意一张图最终拿到的 render config,也能在集成方与图表作者两个角色之间划清配置权限的边界。
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