首页
/ Mermaid setSiteConfig():站点级配置(siteConfig)机制源码解析

Mermaid setSiteConfig():站点级配置(siteConfig)机制源码解析

2026-09-06 23:19:11作者:舒璇辛Bertina

本文基于 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 siteConfig to the desired values. The siteConfig is a protected configuration for repeat use. Calls to reset will reset the currentConfig to siteConfig. Defined in: packages/mermaid/src/config.ts:64 参数 conf(MermaidConfig):The config to use as siteConfig. This will be merged with the defaultConfig. 返回值(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);

siteConfigcurrentConfig 都起始于 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

结合文档语义与源码,有三点必须强调:

  1. 是"重建"而非"叠加":每次调用都从 defaultConfig 出发重新构建 siteConfig,因此 conf 相对默认值是"全量声明",而不是在上一次 siteConfig 上打补丁。需要增量合并时应改用 updateSiteConfig(见第六节)。
  2. 返回值是完整配置对象,可直接用于后续判断或断言,而不是只返回增量部分。
  3. 立即同步渲染配置:函数末尾的 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;
};

逐行解析:

  1. siteConfig = assignWithDepth({}, defaultConfig):深拷贝全局默认值。因为 defaultConfig 是冻结对象,必须拷贝后才能安全修改,且保证多次调用互不污染。
  2. siteConfig = assignWithDepth(siteConfig, conf):把用户传入的 conf 深合并进默认值——这就是官方文档所说 "This will be merged with the defaultConfig"。注意合并语义中"类型不同互不覆盖"(详见第四节)。
  3. 主题解析:若 conf.theme 是已注册的主题名,则调用该主题的 getThemeVariables(conf.themeVariables) 生成完整的 themeVariables 并写回 siteConfig。这就是"只传主题名加少量变量覆盖,其余颜色体系自动补齐"的实现原理。
  4. 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 时,以该主题为基准重算 themeVariablesconfig.ts#L39-L48),因此指令只需声明"主题名 + 要覆盖的变量";
  • 最后执行 checkConfigconfig.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)。

assignWithDepth 与 Object.assign 深合并效果对比示意图

8.6.0 变更文档 中,assignWithDepth 也被列为该版本的关键新特性,并被明确描述为"类似 Object.assign 但带深度的对象合并机制",上述图片即展示了两种合并方式的差异示例。

五、安全边界:secure 数组与 sanitize

siteConfig 是"受保护"的站点级配置,它同时是下层配置的安全边界定义者。sanitizeconfig.ts#L131-L166)在每条 directive 并入 currentConfig 之前被调用,做三类防护:

  1. secure 键保护:遍历 ['secure', ...(siteConfig.secure ?? [])],若指令携带其中任一键,记录日志并就地删除——这正是图作者无法通过 %%{init}%% 指令抬高站点 securityLevel 等原因;
  2. 原型污染防护:删除所有以 __ 开头的键;
  3. XSS 防护:删除包含 <>url(data: 的字符串值,因为 base64 的 data URL 可以内嵌含内联脚本的 SVG。

需要精确区分的是:传入 setSiteConfigconf 来自可信的站点所有者,不会被 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 还包含 suppressErrorRenderingmaxEdges,以当前 Schema 为准。

测试 packages/mermaid/src/config.spec.ts#L19-L38 精确验证了这条边界:setSiteConfigfontSize 追加进 secure 后,再通过 addDirective 尝试修改 fontSizesecurityLevel,断言 getConfig() 返回的仍是站点值,而 fontFamily 这类非 secure 项则正常生效。

六、公共入口:mermaid.initialize()

站点集成方通常不直接调用 setSiteConfig,而是经由 initializepackages/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
  • 主题解析在 initializesetSiteConfig 中各有一次(逻辑一致),最后设置日志级别并注册内置图表。

一个典型的站点级用法:

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 }) 注入序列图配置。

八、实践要点清单

  1. 站点配置只在应用初始化时设置一次(通过 initialize);setSiteConfig 每调用一次都是"回到默认值再重新合并",反复调用不会累积。
  2. 需要增量微调站点配置时用 updateSiteConfig,避免重复传递全量配置。
  3. 读取配置:getSiteConfig() 取站点基线,getConfig() 取当前生效配置(均为深拷贝)。getConfig 的 JSDoc 建议避免反复调用,应将结果存入变量并向下传递(config.ts#L113-L122)。
  4. 站点所有者要把自定义项也受保护起来,应在 initializesecure 数组中追加声明;图作者侧传入 secure 键、__ 前缀键或含 < / > / url(data: 的字符串都会被 sanitize 就地移除。
  5. 注意弃用项:setConfig 已废弃;flowchart.htmlLabels 应改用全局 htmlLabelslazyLoadedDiagrams / loadExternalDiagramsAtStartup 应改用 registerExternalDiagrams,这些都会由 checkConfig / 相关工具函数发出弃用警告。

九、延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐