Mermaid setSiteConfig():站点级配置(siteConfig)机制源码解析
【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid
本文基于 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): MermaidConfigSets 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 --> Worldfrontmatter 中"整份配置除 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 thecurrentConfigtositeConfig"。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
【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考