Mermaid setSiteConfig():站点级配置(siteConfig)机制源码解析
本文基于 Mermaid 官方 API 文档 setSiteConfig.md,结合 packages/mermaid/src/config.ts 的源码实现,完整讲解 Mermaid 的 defaultConfig → siteConfig → currentConfig 三级配置模型、setSiteConfig 的深合并与主题解析逻辑、secure 安全边界,以及 initialize / reset / updateSiteConfig 等配套 API 的协作方式。读完你可以正确完成站点级全局配置、理解为什么图作者通过指令无法改写某些配置项,并在自定义渲染行为时定位到具体源码。
一、setSiteConfig() 在 Mermaid 配置体系中的位置
官方文档对 setSiteConfig 的定义如下(该页为 TypeDoc 自动生成的 API 参考,源文件位于仓库内 packages/mermaid/src/docs/config/setup/config/functions/setSiteConfig.md,文档中标注 "DO NOT EDIT"):
setSiteConfig(conf): MermaidConfig Sets the
siteConfigto the desired values. ThesiteConfigis a protected configuration for repeat use. Calls to reset will reset thecurrentConfigtositeConfig. Defined in: packages/mermaid/src/config.ts:64 参数 conf(MermaidConfig):The config to use assiteConfig. This will be merged with thedefaultConfig. 返回值(MermaidConfig):The new siteConfig。
要理解这个函数,先要看清 Mermaid 的配置分层。Configuration 文档 说明 Mermaid 启动时从以下来源提取配置:
- defaultConfig:内置默认值;
- siteConfig:由站点集成方通过
initialize调用设置的站点级覆盖,作用于该站点/应用中的所有图表; - Frontmatter(v10.5.0+):图作者可以在图文件顶部的 YAML 块中覆盖选定的配置参数(除 secure 配置外);
- Directives(已被 Frontmatter 取代):图作者通过图代码中的指令直接更新选定配置参数。
最终用于渲染的配置称为 render config,即上述各层合并后的结果。8.6.0 变更文档 则给出了经典的三级模型表述:
| 配置层级 | 说明 |
|---|---|
| Global Configuration | Mermaid 的默认配置 |
| Site Configuration | 由站点所有者(site owner)设置 |
| Current Configuration | 由实现者/图作者(implementor)设置 |
源码中的对应模块状态见 config.ts#L19-L22:
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let configFromInitialize: MermaidConfig;
let directives: MermaidConfig[] = [];
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
即 siteConfig 与 currentConfig 都起始于 defaultConfig 的深拷贝(defaultConfig 本身被 Object.freeze 冻结,见 config.ts#L8),而 siteConfig 每次变更都会触发 currentConfig 的刷新——这正是 setSiteConfig 的核心职责。
二、setSiteConfig() 的签名、参数与返回值
| 项目 | 内容 |
|---|---|
| 签名 | setSiteConfig(conf: MermaidConfig): MermaidConfig |
参数 conf |
要用作 siteConfig 的配置,会与 defaultConfig 做深合并 |
| 返回值 | 新的 siteConfig 完整对象(默认值 + conf + 解析后的主题变量) |
| 定义位置 | packages/mermaid/src/config.ts#L64 |
| 文档入口 | docs/config/setup/config/functions/setSiteConfig.md,配置函数索引见 docs/config/setup/config/README.md |
结合文档语义与源码,有三点必须强调:
- 是"重建"而非"叠加":每次调用都从
defaultConfig出发重新构建siteConfig,因此conf相对默认值是"全量声明",而不是在上一次siteConfig上打补丁。需要增量合并时应改用updateSiteConfig(见第六节)。 - 返回值是完整配置对象,可直接用于后续判断或断言,而不是只返回增量部分。
- 立即同步渲染配置:函数末尾的
updateCurrentConfig(siteConfig, directives)保证调用后当前渲染配置即刻生效,无需等待下一张图渲染。
三、setSiteConfig 源码逐步拆解
完整实现见 config.ts#L64-L76:
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 = assignWithDepth({}, defaultConfig):深拷贝全局默认值。因为defaultConfig是冻结对象,必须拷贝后才能安全修改,且保证多次调用互不污染。siteConfig = assignWithDepth(siteConfig, conf):把用户传入的conf深合并进默认值——这就是官方文档所说 "This will be merged with the defaultConfig"。注意合并语义中"类型不同互不覆盖"(详见第四节)。- 主题解析:若
conf.theme是已注册的主题名,则调用该主题的getThemeVariables(conf.themeVariables)生成完整的themeVariables并写回siteConfig。这就是"只传主题名加少量变量覆盖,其余颜色体系自动补齐"的实现原理。 updateCurrentConfig(siteConfig, directives):以新siteConfig叠加当前累积的 directives 刷新currentConfig,最后返回siteConfig。
currentConfig 的刷新管线:updateCurrentConfig
刷新逻辑在 config.ts#L24-L53:
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) {
// ...以 theme 为基准重算 themeVariables
}
currentConfig = cfg;
checkConfig(currentConfig);
return currentConfig;
};
三个关键点:
- 每条 directive 合并前先经过
sanitize(d)处理——这是安全边界的执行点(见第五节); - 当合并结果包含有效
theme时,以该主题为基准重算themeVariables(config.ts#L39-L48),因此指令只需声明"主题名 + 要覆盖的变量"; - 最后执行
checkConfig(config.ts#L217-L225),对弃用选项发出警告,例如设置了lazyLoadedDiagrams/loadExternalDiagramsAtStartup时会提示改用registerExternalDiagrams。
四、setSiteConfig 背后的深合并工具:assignWithDepth
setSiteConfig 的合并行为完全由 assignWithDepth 决定,其实现见 packages/mermaid/src/assignWithDepth.ts,语义如下:
- 任意深度递归合并:对
src中每个键递归执行合并;目标对象缺失该键时先自动初始化为{}再合并,不会因键缺失报错; - 类型不同互不覆盖(dissimilar types will not clobber):例如
dst.foo是{bar: 'bar'}而src.foo是字符串'foo',结果为{bar: 'bar'}。普通Object.assign会直接用'foo'覆盖对象结构,assignWithDepth则保留原对象,避免误传标量把默认配置结构打碎; - 数组语义:
src是数组而dst不是数组时,逐个元素依次合并;两者都是数组时做去重并集; - 支持
depth参数控制递归深度(默认 2)。
在 8.6.0 变更文档 中,assignWithDepth 也被列为该版本的关键新特性,并被明确描述为"类似 Object.assign 但带深度的对象合并机制",上述图片即展示了两种合并方式的差异示例。
五、安全边界:secure 数组与 sanitize
siteConfig 是"受保护"的站点级配置,它同时是下层配置的安全边界定义者。sanitize(config.ts#L131-L166)在每条 directive 并入 currentConfig 之前被调用,做三类防护:
- secure 键保护:遍历
['secure', ...(siteConfig.secure ?? [])],若指令携带其中任一键,记录日志并就地删除——这正是图作者无法通过%%{init}%%指令抬高站点securityLevel等原因; - 原型污染防护:删除所有以
__开头的键; - XSS 防护:删除包含
<、>或url(data:的字符串值,因为 base64 的 data URL 可以内嵌含内联脚本的 SVG。
需要精确区分的是:传入 setSiteConfig 的 conf 来自可信的站点所有者,不会被 sanitize;被清洗的是渲染管线中的每条 directive。
secure 的默认值定义在配置 Schema 中(packages/mermaid/src/schemas/config.schema.yaml#L259-L276):
secure:
description: |
This option controls which currentConfig keys are considered secure and
can only be changed via call to mermaid.initialize.
This prevents malicious graph directives from overriding a site's default security.
default:
- 'secure'
- 'securityLevel'
- 'startOnLoad'
- 'maxTextSize'
- 'suppressErrorRendering'
- 'maxEdges'
8.6.0 变更文档 用"套娃"比喻解释了规则:全局 secure 数组不可变,站点所有者只能通过 initialize 追加(如 initialize({ secure: ['secure', 'securityLevel', 'parameter1'] })),实现者(图作者)则完全不能修改该数组。注意 8.6.0 文档中的默认列表(['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'])是早期快照,当前仓库 Schema 还包含 suppressErrorRendering 与 maxEdges,以当前 Schema 为准。
测试 packages/mermaid/src/config.spec.ts#L19-L38 精确验证了这条边界:setSiteConfig 将 fontSize 追加进 secure 后,再通过 addDirective 尝试修改 fontSize 和 securityLevel,断言 getConfig() 返回的仍是站点值,而 fontFamily 这类非 secure 项则正常生效。
六、公共入口:mermaid.initialize()
站点集成方通常不直接调用 setSiteConfig,而是经由 initialize(packages/mermaid/src/mermaidAPI.ts#L663-L690):
function initialize(userOptions: MermaidConfig = {}) {
const options: MermaidConfig = assignWithDepth({}, userOptions);
// 兼容旧版:顶层 fontFamily 迁移到 themeVariables.fontFamily
if (options?.fontFamily && !options.themeVariables?.fontFamily) {
options.themeVariables = {
...options.themeVariables,
fontFamily: options.fontFamily,
};
}
// 记录 initialize 的原始输入
configApi.saveConfigFromInitialize(options);
if (options?.theme && options.theme in theme) {
options.themeVariables = theme[options.theme].getThemeVariables(options.themeVariables);
} else if (options) {
options.themeVariables = theme.default.getThemeVariables(options.themeVariables);
}
const config =
typeof options === 'object' ? configApi.setSiteConfig(options) : configApi.getSiteConfig();
setLogLevel(config.logLevel);
addDiagrams();
}
要点:
- 官方文档明确 initialize 只应调用一次("The initialize call is applied only once"),它通过
saveConfigFromInitialize先保存用户输入(供 getUserDefinedConfig 后续读取),再调用setSiteConfig(options)建立siteConfig; - 顶层
fontFamily属于旧版配置位置,会被自动迁移进themeVariables.fontFamily; - 主题解析在
initialize与setSiteConfig中各有一次(逻辑一致),最后设置日志级别并注册内置图表。
一个典型的站点级用法:
import mermaid from 'mermaid';
mermaid.initialize({
startOnLoad: false,
theme: 'forest',
themeVariables: { primaryColor: '#00ff00' },
fontFamily: 'monospace',
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'maxEdges', 'suppressErrorRendering'],
flowchart: { curve: 'basis' },
});
与之对照,图作者(implementor)只能覆盖非 secure 项,Configuration 文档 给出的 frontmatter 示例:
---
title: Hello Title
config:
theme: base
themeVariables:
primaryColor: "#00ff00"
---
flowchart
Hello --> World
frontmatter 中"整份配置除 secure 项外均可覆盖"的边界,正是第五节 sanitize 机制在解析层的体现。
七、配套 API 全家福:reset / getSiteConfig / updateSiteConfig / setConfig
config.ts 围绕 siteConfig 暴露的一组 API(官方索引见 docs/config/setup/config/README.md):
| API | 行为 | 源码位置 |
|---|---|---|
setSiteConfig(conf) |
以 默认值 + conf 重建 siteConfig,并刷新 currentConfig | L64-L76 |
updateSiteConfig(conf) |
在当前 siteConfig 上增量合并 conf 后刷新 currentConfig | L82-L87 |
getSiteConfig() |
返回 siteConfig 的深拷贝 | L94-L96 |
reset(config = siteConfig) |
清空 directives,把 currentConfig 重置为给定配置(默认 siteConfig) | L194-L198 |
setConfig(conf) |
已废弃(@deprecated):只更新 currentConfig,且会被下一次 addDirective/reset 覆盖 |
L106-L110 |
reset 的语义就是文档中"protected"承诺的落地:"Calls to reset will reset the currentConfig to siteConfig"。Configuration 文档 进一步说明:每次渲染图表前,Mermaid 都会在最早期调用 reset,回到站点基线,从而保证不同图之间配置互不串扰。config.spec.ts#L89-L101 验证了 setSiteConfig → setConfig → reset → 回到站点值 的完整往返。
此外 packages/mermaid/src/diagram-api/diagramAPI.ts 内部重新导出了 setSiteConfig,供各图表模块测试使用,例如 sequenceDiagram.spec.js#L1885 中用 setSiteConfig({ logLevel: 5, sequence: conf }) 注入序列图配置。
八、实践要点清单
- 站点配置只在应用初始化时设置一次(通过
initialize);setSiteConfig每调用一次都是"回到默认值再重新合并",反复调用不会累积。 - 需要增量微调站点配置时用
updateSiteConfig,避免重复传递全量配置。 - 读取配置:
getSiteConfig()取站点基线,getConfig()取当前生效配置(均为深拷贝)。getConfig的 JSDoc 建议避免反复调用,应将结果存入变量并向下传递(config.ts#L113-L122)。 - 站点所有者要把自定义项也受保护起来,应在
initialize的secure数组中追加声明;图作者侧传入 secure 键、__前缀键或含</>/url(data:的字符串都会被sanitize就地移除。 - 注意弃用项:
setConfig已废弃;flowchart.htmlLabels应改用全局htmlLabels;lazyLoadedDiagrams/loadExternalDiagramsAtStartup应改用registerExternalDiagrams,这些都会由checkConfig/ 相关工具函数发出弃用警告。
九、延伸阅读
- API 文档:setSiteConfig、MermaidConfig 接口
- 配置模型总览:Configuration
- 指令机制与 8.6.0 新 API:8.6.0 变更文档
- 配置核心实现:config.ts
- 默认配置与 Schema:defaultConfig.ts、config.schema.yaml
- 深合并工具:assignWithDepth.ts
- 单元测试:config.spec.ts
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 StartedRust0625
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
